weval 0.4.1

The WebAssembly partial evaluator
/*
 * Wizer interface for Wasm module to be initialized.
 *
 * This header provides several macros that allow a Wasm module written in C/C++
 * to declare an initializer function, and ensure that global constructors (in
 * C++'s case) are run at initialization time rather than on startup of the
 * pre-initialized module.
 */
#ifndef _WIZER_H_
#define _WIZER_H_

#ifdef __cplusplus
#define __WIZER_EXTERN_C extern "C"
#else
#define __WIZER_EXTERN_C extern
#endif

#ifdef __clang_major__
// wasi-sdk-16 was the first wasi-sdk version that shipped with a version of
// wasi-libc that did not include __original_main. However, wasi-sdk-15 shipped
// with clang-14.0.0. To correctly identify the boundary where __original_main
// no longer exists, we check for either clang-15+ or specifically clang-14.0.4.
//
// wasi-sdk-17 ships with clang-15.0.6
// wasi-sdk-16 ships with clang-14.0.4
#if __clang_major__ >= 15 ||                                                   \
    (__clang_major__ == 14 && __clang_patchlevel__ == 4)
#define WIZER_MAIN_VOID __main_void
#else
#define WIZER_MAIN_VOID __original_main
#endif
#endif

// We default to assuming that the compiler is new enough to provide
// __main_void.
#ifndef WIZER_MAIN_VOID
#define WIZER_MAIN_VOID __main_void
#endif

/*
 * This macro inserts the exported functions necessary to allow Wizer to
 * pre-initialize a Wasm module.
 *
 * To use, simply invoke the macro in exactly one compilation unit (C/C++ file)
 * that is compiled into the Wasm module to be pre-initialized:
 *
 *     static void my_init_function() { ... }
 *
 *     WIZER_INIT(my_init_function);
 *
 * (The macro refers to the provided init function, so it must have been defined
 * or must have a forward declaration at the point the macro is used.)
 *
 * The resulting module should be processed by Wizer as follows:
 *
 *     $ wizer -r _start=wizer.resume -o out.wasm in.wasm
 *
 * The result of this will be the following behavior:
 *
 * - If the `in.wasm` (the direct compilation output of a program including this
 *   macro invocation) is run directly according to the WASI ABI (i.e., by
 *   invoking `_start`), then nothing changes: global constructors are run,
 *   `main()` is invoked, then global destructors are run. The initialization
 *   function is *not* run in this case.
 *
 * - During pre-initialization (i.e., during this `wizer` invocation), global
 *   constructors will run, and then the provided initialization function will
 *   run. The module's memory and global-variable state is then snapshotted and
 *   saved into `out.wasm`.
 *
 *   All other Wizer restrictions apply (see Wizer documentation for details):
 *   for example, WASI hostcalls may be blocked, depending on options, and
 *   invoking any other imported function will result in an immediate trap
 *   and failure of the Wizer run.
 *
 * - If the resulting `out.wasm` is then run using the WASI ABI, the program's
 *   global constructors are *not* re-run. Instead, execution starts directly at
 *   `main()`, using the heap and global-variable state left by the global
 *   constructor and initialization function execution during the Wizer
 *   invocation.
 *
 * If no initialization function is needed (i.e., only C++ global constructors
 * should be run), use `WIZER_DEFAULT_INIT()` instead.
 */
#define WIZER_INIT(init_func)                                                  \
  __WIZER_EXTERN_C void __wasm_call_ctors();                                   \
  __WIZER_EXTERN_C void __wasm_call_dtors();                                   \
  __WIZER_EXTERN_C void __wasi_proc_exit(int);                                 \
  __WIZER_EXTERN_C int WIZER_MAIN_VOID();                                      \
  /* This function's export name `wizer-initialize` is specially          */   \
  /* recognized by Wizer. It is the direct entry point for pre-init.      */   \
  __attribute__((export_name("wizer-initialize"))) void __wizer_initialize() { \
    /* `__wasm_call_ctors()` is generated by `wasm-ld` and invokes all  */     \
    /* of the global constructors. It is safe (and in fact necessary)   */     \
    /* to manually invoke it here because `wizer-initialize` is the     */     \
    /* direct entry point, and no libc startup (crt1.o or equivalent)   */     \
    /* is executed before this code does. */                                   \
    __wasm_call_ctors();                                                       \
    /* We now invoke the provided init function before returning.       */     \
    init_func();                                                               \
  }                                                                            \
  /* This function replaces `_start` (the WASI-specified entry point) in  */   \
  /* the pre-initialized Wasm module.                                     */   \
  __attribute__((export_name("wizer.resume"))) void __wizer_resume() {         \
    /* `__main_void()` is defined by the WASI SDK toolchain due to      */     \
    /* special semantics in C/C++ for the `main()` function, i.e., ito  */     \
    /* can either take argc/argv or not. It collects arguments using    */     \
    /* the appropriate WASI calls and then invokes the user program's   */     \
    /* `main()`. This may change in the future; when it does, we will   */     \
    /* coordinate with the WASI-SDK toolchain to implement this entry   */     \
    /* point in an alternate way. */                                           \
    int r = WIZER_MAIN_VOID();                                                 \
    /* Because we are replacing `_start()`, we need to manually invoke  */     \
    /* destructors as well.                                             */     \
    __wasm_call_dtors();                                                       \
    /* If main returned non-zero code, call `__wasi_proc_exit`. */             \
    if (r != 0) {                                                              \
      __wasi_proc_exit(r);                                                     \
    }                                                                          \
  }

/*
 * This macro is like `WIZER_INIT()`, but takes no initialization function.
 * Instead, the pre-initialization phase only executes C++ global constructors
 * before snapshotting the module state.
 *
 * See documentation for `WIZER_INIT()` for more details and usage instructions.
 */
#define WIZER_DEFAULT_INIT()                                                   \
  static void __empty_init() {}                                                \
  WIZER_INIT(__empty_init)

#endif // _WIZER_H_