diff --git a/CMakeLists.txt b/CMakeLists.txt index e289a79348..b9fd5614bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,5 @@ cmake_minimum_required(VERSION 2.8.12) -# Use ccache if possible -FIND_PROGRAM(CCACHE_PROGRAM ccache) -IF(CCACHE_PROGRAM) - MESSAGE(STATUS "Found ccache ${CCACHE_PROGRAM}") -ENDIF() - if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) @@ -98,10 +92,15 @@ set(ZIG_SHARED_LLVM off CACHE BOOL "Prefer linking against shared LLVM libraries set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") set(ZIG_STATIC_ZLIB off CACHE BOOL "Prefer linking against static zlib") set(ZIG_STATIC_ZSTD off CACHE BOOL "Prefer linking against static zstd") -set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available") +set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache") -if(CCACHE_PROGRAM AND ZIG_USE_CCACHE) - SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") +if(ZIG_USE_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") + else() + message(SEND_ERROR "ccache requested but not found") + endif() endif() if(ZIG_STATIC) @@ -118,19 +117,9 @@ string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_ string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_STATIC_LIB_DIR_ESCAPED "${ZIG_LIBC_STATIC_LIB_DIR}") string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_INCLUDE_DIR_ESCAPED "${ZIG_LIBC_INCLUDE_DIR}") -option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) - set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for") set(ZIG_TARGET_MCPU "native" CACHE STRING "-mcpu parameter to output binaries for") -set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") -set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1") - -if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(ZIG_ENABLE_LOGGING ON CACHE BOOL "enable logging") -else() - set(ZIG_ENABLE_LOGGING OFF CACHE BOOL "enable logging") -endif() if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries") @@ -179,190 +168,6 @@ include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLD_INCLUDE_DIRS}) include_directories(${CLANG_INCLUDE_DIRS}) -# No patches have been applied to SoftFloat-3e -set(EMBEDDED_SOFTFLOAT_SOURCES - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_eq.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_eq_signaling.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_le.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_le_quiet.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_lt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_lt_quiet.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_mul.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_mulAdd.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_rem.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_roundToInt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_sqrt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_sub.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_f16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_f32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_f64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_extF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_i32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_i64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_add.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_div.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_eq.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_le.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_lt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_mul.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_rem.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_roundToInt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_sqrt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_sub.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_to_f16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_to_f32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_to_f64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/extF80M_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_isSignalingNaN.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_extF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_extF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_extF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/i32_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addMagsF16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addMagsF32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addMagsF64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_approxRecip32_1.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_approxRecip_1Ks.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_compare128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_compare96M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_countLeadingZeros16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_countLeadingZeros32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_countLeadingZeros64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_countLeadingZeros8.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_eq128.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_invalidF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_invalidExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_isNaNF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_le128.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_lt128.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mul128MTo256M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mul64To128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mulAddF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mulAddF16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mulAddF32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_mulAddF64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_negXM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normExtF80SigM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normRoundPackToF16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normRoundPackToF32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normRoundPackToF64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_remStepMBy32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundMToI64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundMToUI64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundPackMToF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundPackToF16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundPackToF32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundPackToF64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundToI32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundToI64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundToUI32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_roundToUI64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftLeftM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftNormSigF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftRightJam256M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftRightJam32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftRightJam64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftRightJamM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shiftRightM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftLeftM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftRightJam64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftRightJamM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_shortShiftRightM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_sub1XM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_sub256M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subM.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF16.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF32.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF64.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mulAdd.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_mulAdd.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/softfloat_state.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_f128M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_extF80M.c" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_extF80M.c" -) -add_library(embedded_softfloat STATIC ${EMBEDDED_SOFTFLOAT_SOURCES}) -if(MSVC) - set(SOFTFLOAT_CFLAGS "/w") - - if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(SOFTFLOAT_CFLAGS "${SOFTFLOAT_CFLAGS} /O2") - endif() - - set_target_properties(embedded_softfloat PROPERTIES - COMPILE_FLAGS ${SOFTFLOAT_CFLAGS} - ) -else() - set_target_properties(embedded_softfloat PROPERTIES - COMPILE_FLAGS "-std=c99 -O3" - ) -endif() -target_include_directories(embedded_softfloat PUBLIC - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e-prebuilt" - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086" -) -include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include") -set(SOFTFLOAT_LIBRARIES embedded_softfloat) - find_package(Threads) set(ZIG_LIB_DIR "lib/zig") @@ -374,35 +179,8 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std") set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h") set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig") -# This is our shim which will be replaced by stage1.zig. -set(ZIG1_SOURCES - "${CMAKE_SOURCE_DIR}/src/stage1/zig0.cpp" -) - set(STAGE1_SOURCES - "${CMAKE_SOURCE_DIR}/src/stage1/analyze.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/astgen.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/bigfloat.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/codegen.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/errmsg.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/error.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/heap.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/ir.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/ir_print.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/mem.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/os.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/parser.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/range_set.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/stage1.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/target.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/tokenizer.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/util.cpp" -) -set(OPTIMIZED_C_SOURCES - "${CMAKE_SOURCE_DIR}/src/stage1/parse_f128.c" + "${CMAKE_SOURCE_DIR}/stage1/zig1.c" ) set(ZIG_CPP_SOURCES # These are planned to stay even when we are self-hosted. @@ -416,11 +194,7 @@ set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) # Needed because we use cmake, not the zig build system, to build zig2.o. -# This list is generated by building zig and then clearing the zig-cache directory, -# then manually running the build-obj command (see BUILD_ZIG2_ARGS), and then looking -# in the zig-cache directory for the compiler-generated list of zig file dependencies. set(ZIG_STAGE2_SOURCES - "${ZIG_CONFIG_ZIG_OUT}" "${CMAKE_SOURCE_DIR}/lib/std/array_hash_map.zig" "${CMAKE_SOURCE_DIR}/lib/std/array_list.zig" "${CMAKE_SOURCE_DIR}/lib/std/ascii.zig" @@ -828,7 +602,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/print_targets.zig" "${CMAKE_SOURCE_DIR}/src/print_zir.zig" "${CMAKE_SOURCE_DIR}/src/register_manager.zig" - "${CMAKE_SOURCE_DIR}/src/stage1.zig" "${CMAKE_SOURCE_DIR}/src/target.zig" "${CMAKE_SOURCE_DIR}/src/tracy.zig" "${CMAKE_SOURCE_DIR}/src/translate_c.zig" @@ -847,24 +620,12 @@ if(MSVC) endif() endif() -if(ZIG_OMIT_STAGE2) - set(ZIG_OMIT_STAGE2_BOOL "true") -else() - set(ZIG_OMIT_STAGE2_BOOL "false") -endif() - -if(ZIG_ENABLE_LOGGING) - set(ZIG_ENABLE_LOGGING_BOOL "true") -else() - set(ZIG_ENABLE_LOGGING_BOOL "false") -endif() - configure_file ( - "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" + "${CMAKE_SOURCE_DIR}/stage1/config.h.in" "${ZIG_CONFIG_H_OUT}" ) configure_file ( - "${CMAKE_SOURCE_DIR}/src/config.zig.in" + "${CMAKE_SOURCE_DIR}/stage1/config.zig.in" "${ZIG_CONFIG_ZIG_OUT}" ) @@ -872,55 +633,40 @@ include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/src" - "${CMAKE_SOURCE_DIR}/src/stage1" ) # These have to go before the -Wno- flags if(MSVC) - set(EXE_CFLAGS "/std:c++14") + set(EXE_CXX_FLAGS "/std:c++14") else(MSVC) - set(EXE_CFLAGS "-std=c++14") + set(EXE_CXX_FLAGS "-std=c++14") endif(MSVC) -if(ZIG_STATIC) - set(EXE_CFLAGS "${EXE_CFLAGS} -DZIG_LINK_MODE=Static") -else() - set(EXE_CFLAGS "${EXE_CFLAGS} -DZIG_LINK_MODE=Dynamic") -endif() - if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") if(MSVC) - set(EXE_CFLAGS "${EXE_CFLAGS} /w") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} /w") else() - set(EXE_CFLAGS "${EXE_CFLAGS} -Werror -Wall") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror -Wall") # fallthrough support was added in GCC 7.0 if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0) - set(EXE_CFLAGS "${EXE_CFLAGS} -Werror=implicit-fallthrough") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror=implicit-fallthrough") endif() # GCC 9.2 and older are unable to detect valid variable initialization in some cases if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 9.2) - set(EXE_CFLAGS "${EXE_CFLAGS} -Wno-maybe-uninitialized") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-maybe-uninitialized") endif() endif() endif() if(MSVC) - set(EXE_CFLAGS "${EXE_CFLAGS}") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS}") else() - set(EXE_CFLAGS "${EXE_CFLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment") if(MINGW) - set(EXE_CFLAGS "${EXE_CFLAGS} -Wno-format") + set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format") endif() endif() -if(MSVC) - if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(OPTIMIZED_C_FLAGS "/O2") - endif() -else(MSVC) - set(OPTIMIZED_C_FLAGS "-std=c99 -O3") -endif(MSVC) - set(EXE_LDFLAGS " ") if(MSVC) set(EXE_LDFLAGS "${EXE_LDFLAGS} /STACK:16777216") @@ -939,21 +685,10 @@ if(ZIG_STATIC) elseif(NOT MSVC) set(EXE_LDFLAGS "${EXE_LDFLAGS} -static") endif() -else() - if(MINGW) - set(EXE_LDFLAGS "${EXE_LDFLAGS}") - endif() -endif() - -if(ZIG_TEST_COVERAGE) - set(EXE_CFLAGS "${EXE_CFLAGS} -fprofile-arcs -ftest-coverage") - set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") endif() add_library(zigcpp STATIC ${ZIG_CPP_SOURCES}) -set_target_properties(zigcpp PROPERTIES - COMPILE_FLAGS ${EXE_CFLAGS} -) +set_target_properties(zigcpp PROPERTIES COMPILE_FLAGS ${EXE_CXX_FLAGS}) target_link_libraries(zigcpp LINK_PUBLIC ${CLANG_LIBRARIES} @@ -962,146 +697,73 @@ target_link_libraries(zigcpp LINK_PUBLIC ${CMAKE_THREAD_LIBS_INIT} ) -add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) -set_target_properties(opt_c_util PROPERTIES - COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" -) -target_include_directories(opt_c_util PRIVATE - "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e-prebuilt" -) - -add_library(zigstage1 STATIC ${STAGE1_SOURCES}) -set_target_properties(zigstage1 PROPERTIES - COMPILE_FLAGS ${EXE_CFLAGS} - LINK_FLAGS ${EXE_LDFLAGS} -) -target_link_libraries(zigstage1 LINK_PUBLIC - opt_c_util - ${SOFTFLOAT_LIBRARIES} - zigcpp -) -if(NOT MSVC) - target_link_libraries(zigstage1 LINK_PUBLIC ${LIBXML2}) -endif() - -if(ZIG_DIA_GUIDS_LIB) - target_link_libraries(zigstage1 LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) -endif() - -if(MSVC OR MINGW) - target_link_libraries(zigstage1 LINK_PUBLIC version) -endif() - -if("${ZIG_EXECUTABLE}" STREQUAL "") - add_executable(zig1 ${ZIG1_SOURCES}) - set_target_properties(zig1 PROPERTIES - COMPILE_FLAGS ${EXE_CFLAGS} - LINK_FLAGS ${EXE_LDFLAGS} - ) - target_link_libraries(zig1 zigstage1) -endif() - if(MSVC) - set(ZIG2_OBJECT "${CMAKE_BINARY_DIR}/zig2.obj") + set(ZIG1_COMPILE_FLAGS "/std:c99") + set(ZIG2_COMPILE_FLAGS "/std:c99") + set(ZIG2_LINK_FLAGS "/STACK:16777216") else() - set(ZIG2_OBJECT "${CMAKE_BINARY_DIR}/zig2.o") -endif() -if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(ZIG_RELEASE_ARG "") -elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") - set(ZIG_RELEASE_ARG -Drelease) -else() - set(ZIG_RELEASE_ARG -Drelease -Dstrip) -endif() -if(ZIG_NO_LIB) - set(ZIG_NO_LIB_ARG "-Dno-lib") -else() - set(ZIG_NO_LIB_ARG "") -endif() -if(ZIG_SINGLE_THREADED) - set(ZIG_SINGLE_THREADED_ARG "-fsingle-threaded") -else() - set(ZIG_SINGLE_THREADED_ARG "") -endif() -if(ZIG_STATIC) - set(ZIG_STATIC_ARG "-Duse-zig-libcxx") -else() - set(ZIG_STATIC_ARG "") + #set(ZIG1_COMPILE_FLAGS "-std=c99 -O2 -march=native") + set(ZIG1_COMPILE_FLAGS "-std=c99 -march=native") + set(ZIG2_COMPILE_FLAGS "-std=c99 -O2 -march=native") + set(ZIG2_LINK_FLAGS "-Wl,-z,stack-size=0x10000000") endif() +add_executable(zig1 ${STAGE1_SOURCES}) +set_target_properties(zig1 PROPERTIES COMPILE_FLAGS ${ZIG1_COMPILE_FLAGS}) +#target_include_directories(zig1 PUBLIC "${CMAKE_SOURCE_DIR}/lib") +target_link_libraries(zig1 LINK_PUBLIC m) + + +set(ZIG2_C_SOURCE "${CMAKE_BINARY_DIR}/zig2.c") set(BUILD_ZIG2_ARGS - "src/stage1.zig" - --name zig2 - --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" - "-femit-bin=${ZIG2_OBJECT}" - -fcompiler-rt - ${ZIG_SINGLE_THREADED_ARG} - -target native - -mcpu native - -lc - --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" - --pkg-end + "${CMAKE_SOURCE_DIR}/lib" + "${CMAKE_BINARY_DIR}/zig1-cache" + "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm" + build-exe src/main.zig -ofmt=c -lc + --name zig2 + --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" + --pkg-end + -target x86_64-linux-musl # TODO: autodetect in zig1.c + --color on # TODO: autodetect in zig1.c + -OReleaseFast +) + +add_custom_command( + OUTPUT "${ZIG2_C_SOURCE}" + COMMAND zig1 ${BUILD_ZIG2_ARGS} + DEPENDS zig1 "${ZIG_STAGE2_SOURCES}" + COMMENT STATUS "Interpreting zig1.wasm to produce ${ZIG2_C_SOURCE}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) -if("${ZIG_EXECUTABLE}" STREQUAL "") - add_custom_command( - OUTPUT "${ZIG2_OBJECT}" - COMMAND zig1 ${BUILD_ZIG2_ARGS} - DEPENDS zig1 "${ZIG_STAGE2_SOURCES}" - COMMENT STATUS "Building stage2 object ${ZIG2_OBJECT}" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - ) - if (WIN32) - set(ZIG_EXECUTABLE "${CMAKE_BINARY_DIR}/zig2.exe") - else() - set(ZIG_EXECUTABLE "${CMAKE_BINARY_DIR}/zig2") - endif() -else() - add_custom_command( - OUTPUT "${ZIG2_OBJECT}" - COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG2_ARGS} - DEPENDS ${ZIG_STAGE2_SOURCES} - COMMENT STATUS "Building stage2 component ${ZIG2_OBJECT}" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - ) -endif() +set(ZIG_COMPILER_RT_C_SOURCE "${CMAKE_BINARY_DIR}/compiler_rt.c") +set(BUILD_COMPILER_RT_ARGS + "${CMAKE_SOURCE_DIR}/lib" + "${CMAKE_BINARY_DIR}/zig1-cache" + "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm" + build-obj lib/compiler_rt.zig -ofmt=c + --name compiler_rt + -target x86_64-linux-musl # TODO: autodetect in zig1.c + --color on # TODO: autodetect in zig1.c + -OReleaseFast +) + +add_custom_command( + OUTPUT "${ZIG_COMPILER_RT_C_SOURCE}" + COMMAND zig1 ${BUILD_COMPILER_RT_ARGS} + DEPENDS zig1 "${ZIG_STAGE2_SOURCES}" + COMMENT STATUS "Interpreting zig1.wasm to produce ${ZIG_COMPILER_RT_C_SOURCE}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +) -# cmake won't let us configure an executable without C sources. -add_executable(zig2 "${CMAKE_SOURCE_DIR}/src/stage1/empty.cpp" "${ZIG2_OBJECT}") +add_executable(zig2 ${ZIG2_C_SOURCE}) set_target_properties(zig2 PROPERTIES - COMPILE_FLAGS ${EXE_CFLAGS} - LINK_FLAGS ${EXE_LDFLAGS} -) -target_link_libraries(zig2 zigstage1) -if(MSVC) - target_link_libraries(zig2 ntdll.lib) -elseif(MINGW) - target_link_libraries(zig2 ntdll) -endif() - -set(ZIG_BUILD_ARGS - --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" - "-Dconfig_h=${ZIG_CONFIG_H_OUT}" - "-Denable-llvm" - ${ZIG_RELEASE_ARG} - ${ZIG_STATIC_ARG} - ${ZIG_NO_LIB_ARG} - ${ZIG_SINGLE_THREADED_ARG} - "-Dtarget=${ZIG_TARGET_TRIPLE}" - "-Dcpu=${ZIG_TARGET_MCPU}" - "-Dversion-string=${RESOLVED_ZIG_VERSION}" + COMPILE_FLAGS ${ZIG2_COMPILE_FLAGS} + LINK_FLAGS ${ZIG2_LINK_FLAGS} ) -add_custom_target(stage3 ALL - COMMAND zig2 build compile ${ZIG_BUILD_ARGS} - DEPENDS zig2 - COMMENT STATUS "Building stage3" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" -) -install(CODE "set(ZIG_EXECUTABLE \"${ZIG_EXECUTABLE}\")") -install(CODE "set(ZIG_BUILD_ARGS \"${ZIG_BUILD_ARGS}\")") -install(CODE "set(CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\")") -install(CODE "set(CMAKE_SOURCE_DIR \"${CMAKE_SOURCE_DIR}\")") -install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/install.cmake") + + + diff --git a/stage1/config.h.in b/stage1/config.h.in new file mode 100644 index 0000000000..2d4fff6df2 --- /dev/null +++ b/stage1/config.h.in @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_CONFIG_H +#define ZIG_CONFIG_H + +// Used by zig0.cpp +#define ZIG_VERSION_MAJOR @ZIG_VERSION_MAJOR@ +#define ZIG_VERSION_MINOR @ZIG_VERSION_MINOR@ +#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ +#define ZIG_VERSION_STRING "@ZIG_VERSION@" + +// Used by build.zig for communicating build information to self hosted build. +#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" +#define ZIG_LLVM_LINK_MODE "@LLVM_LINK_MODE@" +#define ZIG_CMAKE_PREFIX_PATH "@ZIG_CMAKE_PREFIX_PATH@" +#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" +#define ZIG_LLD_INCLUDE_PATH "@LLD_INCLUDE_DIRS@" +#define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@" +#define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" +#define ZIG_LLVM_INCLUDE_PATH "@LLVM_INCLUDE_DIRS@" +#define ZIG_LLVM_LIB_PATH "@LLVM_LIBDIRS@" +#define ZIG_LLVM_LIBRARIES "@LLVM_LIBRARIES@" +#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" + +#endif diff --git a/src/config.zig.in b/stage1/config.zig.in similarity index 72% rename from src/config.zig.in rename to stage1/config.zig.in index 2a0d45c010..48d1f75adb 100644 --- a/src/config.zig.in +++ b/stage1/config.zig.in @@ -1,13 +1,13 @@ -pub const have_llvm = true; +pub const have_llvm = false; pub const llvm_has_m68k = false; pub const llvm_has_csky = false; pub const llvm_has_arc = false; pub const version: [:0]const u8 = "@RESOLVED_ZIG_VERSION@"; pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable; -pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@; +pub const enable_logging: bool = false; pub const enable_link_snapshots: bool = false; pub const enable_tracy = false; pub const value_tracing = false; -pub const have_stage1 = true; +pub const have_stage1 = false; pub const skip_non_native = false; -pub const only_c = false; +pub const only_c = true; diff --git a/stage1/zig1.c b/stage1/zig1.c new file mode 100755 index 0000000000..1cdac5a52b --- /dev/null +++ b/stage1/zig1.c @@ -0,0 +1,4220 @@ +// TODO get rid of _GNU_SOURCE +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +enum wasi_errno_t { + WASI_ESUCCESS = 0, + WASI_E2BIG = 1, + WASI_EACCES = 2, + WASI_EADDRINUSE = 3, + WASI_EADDRNOTAVAIL = 4, + WASI_EAFNOSUPPORT = 5, + WASI_EAGAIN = 6, + WASI_EALREADY = 7, + WASI_EBADF = 8, + WASI_EBADMSG = 9, + WASI_EBUSY = 10, + WASI_ECANCELED = 11, + WASI_ECHILD = 12, + WASI_ECONNABORTED = 13, + WASI_ECONNREFUSED = 14, + WASI_ECONNRESET = 15, + WASI_EDEADLK = 16, + WASI_EDESTADDRREQ = 17, + WASI_EDOM = 18, + WASI_EDQUOT = 19, + WASI_EEXIST = 20, + WASI_EFAULT = 21, + WASI_EFBIG = 22, + WASI_EHOSTUNREACH = 23, + WASI_EIDRM = 24, + WASI_EILSEQ = 25, + WASI_EINPROGRESS = 26, + WASI_EINTR = 27, + WASI_EINVAL = 28, + WASI_EIO = 29, + WASI_EISCONN = 30, + WASI_EISDIR = 31, + WASI_ELOOP = 32, + WASI_EMFILE = 33, + WASI_EMLINK = 34, + WASI_EMSGSIZE = 35, + WASI_EMULTIHOP = 36, + WASI_ENAMETOOLONG = 37, + WASI_ENETDOWN = 38, + WASI_ENETRESET = 39, + WASI_ENETUNREACH = 40, + WASI_ENFILE = 41, + WASI_ENOBUFS = 42, + WASI_ENODEV = 43, + WASI_ENOENT = 44, + WASI_ENOEXEC = 45, + WASI_ENOLCK = 46, + WASI_ENOLINK = 47, + WASI_ENOMEM = 48, + WASI_ENOMSG = 49, + WASI_ENOPROTOOPT = 50, + WASI_ENOSPC = 51, + WASI_ENOSYS = 52, + WASI_ENOTCONN = 53, + WASI_ENOTDIR = 54, + WASI_ENOTEMPTY = 55, + WASI_ENOTRECOVERABLE = 56, + WASI_ENOTSOCK = 57, + WASI_EOPNOTSUPP = 58, + WASI_ENOTTY = 59, + WASI_ENXIO = 60, + WASI_EOVERFLOW = 61, + WASI_EOWNERDEAD = 62, + WASI_EPERM = 63, + WASI_EPIPE = 64, + WASI_EPROTO = 65, + WASI_EPROTONOSUPPORT = 66, + WASI_EPROTOTYPE = 67, + WASI_ERANGE = 68, + WASI_EROFS = 69, + WASI_ESPIPE = 70, + WASI_ESRCH = 71, + WASI_ESTALE = 72, + WASI_ETIMEDOUT = 73, + WASI_ETXTBSY = 74, + WASI_EXDEV = 75, + WASI_ENOTCAPABLE = 76, +}; + +static void panic(const char *msg) { + fprintf(stderr, "%s\n", msg); + abort(); +} + +static uint32_t min_u32(uint32_t a, uint32_t b) { + return (a < b) ? a : b; +} + +static uint32_t rotl32(uint32_t n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 31; + return (n << c) | (n >> ((-c) & mask)); +} + +static uint32_t rotr32(uint32_t n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 31; + return (n >> c) | (n << ((-c) & mask)); +} + +static uint64_t rotl64(uint64_t n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 63; + return (n << c) | (n >> ((-c) & mask)); +} + +static uint64_t rotr64(uint64_t n, unsigned c) { + const unsigned mask = CHAR_BIT * sizeof(n) - 1; + c &= mask & 63; + return (n >> c) | (n << ((-c) & mask)); +} + +static void *arena_alloc(size_t n) { + void *ptr = malloc(n); + if (!ptr) panic("out of memory"); + return ptr; +} + +static void *arena_realloc(void *ptr, size_t new_n) { + void *new_ptr = realloc(ptr, new_n); + if (!new_ptr) panic("out of memory"); + return new_ptr; +} + +static int err_wrap(const char *prefix, int rc) { + if (rc == -1) { + perror(prefix); + abort(); + } + return rc; +} + +static bool bs_isSet(const uint32_t *bitset, uint32_t index) { + return (bitset[index >> 5] >> (index & 0x1f)) & 1; +} +static void bs_set(uint32_t *bitset, uint32_t index) { + bitset[index >> 5] |= ((uint32_t)1 << (index & 0x1f)); +} +static void bs_unset(uint32_t *bitset, uint32_t index) { + bitset[index >> 5] &= ~((uint32_t)1 << (index & 0x1f)); +} +static void bs_setValue(uint32_t *bitset, uint32_t index, bool value) { + if (value) bs_set(bitset, index); else bs_unset(bitset, index); +} + +struct ByteSlice { + char *ptr; + size_t len; +}; + +static struct ByteSlice read_file_alloc(const char *file_path) { + FILE *f = fopen(file_path, "rb"); + if (!f) { + fprintf(stderr, "failed to read %s: ", file_path); + perror(""); + abort(); + } + if (fseek(f, 0L, SEEK_END) == -1) panic("failed to seek"); + struct ByteSlice res; + res.len = ftell(f); + res.ptr = malloc(res.len); + rewind(f); + size_t amt_read = fread(res.ptr, 1, res.len, f); + if (amt_read != res.len) panic("short read"); + fclose(f); + return res; +} + + +struct Preopen { + int wasi_fd; + int host_fd; + const char *name; + size_t name_len; +}; + +static struct Preopen preopens_buffer[10]; +static size_t preopens_len = 0; + +static void add_preopen(int wasi_fd, const char *name, int host_fd) { + preopens_buffer[preopens_len].wasi_fd = wasi_fd; + preopens_buffer[preopens_len].host_fd = host_fd; + preopens_buffer[preopens_len].name = name; + preopens_buffer[preopens_len].name_len = strlen(name); + preopens_len += 1; +} + +static const struct Preopen *find_preopen(int32_t wasi_fd) { + for (size_t i = 0; i < preopens_len; i += 1) { + const struct Preopen *preopen = &preopens_buffer[i]; + if (preopen->wasi_fd == wasi_fd) { + return preopen; + } + } + return NULL; +} + +static const size_t max_memory = 2ul * 1024ul * 1024ul * 1024ul; // 2 GiB + +static uint16_t read_u16_le(const char *ptr) { + const uint8_t *u8_ptr = (const uint8_t *)ptr; + return + (((uint64_t)u8_ptr[0]) << 0x00) | + (((uint64_t)u8_ptr[1]) << 0x08); +} + +static int16_t read_i16_le(const char *ptr) { + return read_u16_le(ptr); +} + +static uint32_t read_u32_le(const char *ptr) { + const uint8_t *u8_ptr = (const uint8_t *)ptr; + return + (((uint64_t)u8_ptr[0]) << 0x00) | + (((uint64_t)u8_ptr[1]) << 0x08) | + (((uint64_t)u8_ptr[2]) << 0x10) | + (((uint64_t)u8_ptr[3]) << 0x18); +} + +static uint32_t read_i32_le(const char *ptr) { + return read_u32_le(ptr); +} + +static uint64_t read_u64_le(const char *ptr) { + const uint8_t *u8_ptr = (const uint8_t *)ptr; + return + (((uint64_t)u8_ptr[0]) << 0x00) | + (((uint64_t)u8_ptr[1]) << 0x08) | + (((uint64_t)u8_ptr[2]) << 0x10) | + (((uint64_t)u8_ptr[3]) << 0x18) | + (((uint64_t)u8_ptr[4]) << 0x20) | + (((uint64_t)u8_ptr[5]) << 0x28) | + (((uint64_t)u8_ptr[6]) << 0x30) | + (((uint64_t)u8_ptr[7]) << 0x38); +} + +static void write_u16_le(char *ptr, uint16_t x) { + uint8_t *u8_ptr = (uint8_t*)ptr; + u8_ptr[0] = (x >> 0x00); + u8_ptr[1] = (x >> 0x08); +} + +static void write_u32_le(char *ptr, uint32_t x) { + uint8_t *u8_ptr = (uint8_t*)ptr; + u8_ptr[0] = (x >> 0x00); + u8_ptr[1] = (x >> 0x08); + u8_ptr[2] = (x >> 0x10); + u8_ptr[3] = (x >> 0x18); +} + +static void write_u64_le(char *ptr, uint64_t x) { + uint8_t *u8_ptr = (uint8_t*)ptr; + u8_ptr[0] = (x >> 0x00); + u8_ptr[1] = (x >> 0x08); + u8_ptr[2] = (x >> 0x10); + u8_ptr[3] = (x >> 0x18); + u8_ptr[4] = (x >> 0x20); + u8_ptr[5] = (x >> 0x28); + u8_ptr[6] = (x >> 0x30); + u8_ptr[7] = (x >> 0x38); +} + +static uint32_t read32_uleb128(const char *ptr, uint32_t *i) { + uint32_t result = 0; + uint32_t shift = 0; + + for (;;) { + uint32_t byte = ptr[*i]; + *i += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) return result; + if (shift >= 32) panic("read32_uleb128 failed"); + } +} + +static int64_t read64_ileb128(const char *ptr, uint32_t *i) { + int64_t result = 0; + uint32_t shift = 0; + + for (;;) { + uint64_t byte = ptr[*i]; + *i += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + if ((byte & 0x40) && (shift < 64)) { + uint64_t extend = 0; + result |= (~extend << shift); + } + return result; + } + if (shift >= 64) panic("read64_ileb128 failed"); + } +} + +static int32_t read32_ileb128(const char *ptr, uint32_t *i) { + return read64_ileb128(ptr, i); +} + +static struct ByteSlice read_name(char *ptr, uint32_t *i) { + uint32_t len = read32_uleb128(ptr, i); + struct ByteSlice res; + res.ptr = ptr + *i; + res.len = len; + *i += len; + return res; +} + +enum Section { + Section_custom, + Section_type, + Section_import, + Section_function, + Section_table, + Section_memory, + Section_global, + Section_export, + Section_start, + Section_element, + Section_code, + Section_data, + Section_data_count, +}; + +enum Op { + Op_unreachable, + Op_br_void, + Op_br_32, + Op_br_64, + Op_br_if_nez_void, + Op_br_if_nez_32, + Op_br_if_nez_64, + Op_br_if_eqz_void, + Op_br_if_eqz_32, + Op_br_if_eqz_64, + Op_br_table_void, + Op_br_table_32, + Op_br_table_64, + Op_return_void, + Op_return_32, + Op_return_64, + Op_call, + Op_drop_32, + Op_drop_64, + Op_select_32, + Op_select_64, + Op_local_get_32, + Op_local_get_64, + Op_local_set_32, + Op_local_set_64, + Op_local_tee_32, + Op_local_tee_64, + Op_global_get_0_32, + Op_global_get_32, + Op_global_set_0_32, + Op_global_set_32, + Op_const_32, + Op_const_64, + Op_add_32, + Op_and_32, + Op_wasm, + Op_wasm_prefixed, +}; + +enum WasmOp { + WasmOp_unreachable = 0x00, + WasmOp_nop = 0x01, + WasmOp_block = 0x02, + WasmOp_loop = 0x03, + WasmOp_if = 0x04, + WasmOp_else = 0x05, + WasmOp_end = 0x0B, + WasmOp_br = 0x0C, + WasmOp_br_if = 0x0D, + WasmOp_br_table = 0x0E, + WasmOp_return = 0x0F, + WasmOp_call = 0x10, + WasmOp_call_indirect = 0x11, + WasmOp_drop = 0x1A, + WasmOp_select = 0x1B, + WasmOp_local_get = 0x20, + WasmOp_local_set = 0x21, + WasmOp_local_tee = 0x22, + WasmOp_global_get = 0x23, + WasmOp_global_set = 0x24, + WasmOp_i32_load = 0x28, + WasmOp_i64_load = 0x29, + WasmOp_f32_load = 0x2A, + WasmOp_f64_load = 0x2B, + WasmOp_i32_load8_s = 0x2C, + WasmOp_i32_load8_u = 0x2D, + WasmOp_i32_load16_s = 0x2E, + WasmOp_i32_load16_u = 0x2F, + WasmOp_i64_load8_s = 0x30, + WasmOp_i64_load8_u = 0x31, + WasmOp_i64_load16_s = 0x32, + WasmOp_i64_load16_u = 0x33, + WasmOp_i64_load32_s = 0x34, + WasmOp_i64_load32_u = 0x35, + WasmOp_i32_store = 0x36, + WasmOp_i64_store = 0x37, + WasmOp_f32_store = 0x38, + WasmOp_f64_store = 0x39, + WasmOp_i32_store8 = 0x3A, + WasmOp_i32_store16 = 0x3B, + WasmOp_i64_store8 = 0x3C, + WasmOp_i64_store16 = 0x3D, + WasmOp_i64_store32 = 0x3E, + WasmOp_memory_size = 0x3F, + WasmOp_memory_grow = 0x40, + WasmOp_i32_const = 0x41, + WasmOp_i64_const = 0x42, + WasmOp_f32_const = 0x43, + WasmOp_f64_const = 0x44, + WasmOp_i32_eqz = 0x45, + WasmOp_i32_eq = 0x46, + WasmOp_i32_ne = 0x47, + WasmOp_i32_lt_s = 0x48, + WasmOp_i32_lt_u = 0x49, + WasmOp_i32_gt_s = 0x4A, + WasmOp_i32_gt_u = 0x4B, + WasmOp_i32_le_s = 0x4C, + WasmOp_i32_le_u = 0x4D, + WasmOp_i32_ge_s = 0x4E, + WasmOp_i32_ge_u = 0x4F, + WasmOp_i64_eqz = 0x50, + WasmOp_i64_eq = 0x51, + WasmOp_i64_ne = 0x52, + WasmOp_i64_lt_s = 0x53, + WasmOp_i64_lt_u = 0x54, + WasmOp_i64_gt_s = 0x55, + WasmOp_i64_gt_u = 0x56, + WasmOp_i64_le_s = 0x57, + WasmOp_i64_le_u = 0x58, + WasmOp_i64_ge_s = 0x59, + WasmOp_i64_ge_u = 0x5A, + WasmOp_f32_eq = 0x5B, + WasmOp_f32_ne = 0x5C, + WasmOp_f32_lt = 0x5D, + WasmOp_f32_gt = 0x5E, + WasmOp_f32_le = 0x5F, + WasmOp_f32_ge = 0x60, + WasmOp_f64_eq = 0x61, + WasmOp_f64_ne = 0x62, + WasmOp_f64_lt = 0x63, + WasmOp_f64_gt = 0x64, + WasmOp_f64_le = 0x65, + WasmOp_f64_ge = 0x66, + WasmOp_i32_clz = 0x67, + WasmOp_i32_ctz = 0x68, + WasmOp_i32_popcnt = 0x69, + WasmOp_i32_add = 0x6A, + WasmOp_i32_sub = 0x6B, + WasmOp_i32_mul = 0x6C, + WasmOp_i32_div_s = 0x6D, + WasmOp_i32_div_u = 0x6E, + WasmOp_i32_rem_s = 0x6F, + WasmOp_i32_rem_u = 0x70, + WasmOp_i32_and = 0x71, + WasmOp_i32_or = 0x72, + WasmOp_i32_xor = 0x73, + WasmOp_i32_shl = 0x74, + WasmOp_i32_shr_s = 0x75, + WasmOp_i32_shr_u = 0x76, + WasmOp_i32_rotl = 0x77, + WasmOp_i32_rotr = 0x78, + WasmOp_i64_clz = 0x79, + WasmOp_i64_ctz = 0x7A, + WasmOp_i64_popcnt = 0x7B, + WasmOp_i64_add = 0x7C, + WasmOp_i64_sub = 0x7D, + WasmOp_i64_mul = 0x7E, + WasmOp_i64_div_s = 0x7F, + WasmOp_i64_div_u = 0x80, + WasmOp_i64_rem_s = 0x81, + WasmOp_i64_rem_u = 0x82, + WasmOp_i64_and = 0x83, + WasmOp_i64_or = 0x84, + WasmOp_i64_xor = 0x85, + WasmOp_i64_shl = 0x86, + WasmOp_i64_shr_s = 0x87, + WasmOp_i64_shr_u = 0x88, + WasmOp_i64_rotl = 0x89, + WasmOp_i64_rotr = 0x8A, + WasmOp_f32_abs = 0x8B, + WasmOp_f32_neg = 0x8C, + WasmOp_f32_ceil = 0x8D, + WasmOp_f32_floor = 0x8E, + WasmOp_f32_trunc = 0x8F, + WasmOp_f32_nearest = 0x90, + WasmOp_f32_sqrt = 0x91, + WasmOp_f32_add = 0x92, + WasmOp_f32_sub = 0x93, + WasmOp_f32_mul = 0x94, + WasmOp_f32_div = 0x95, + WasmOp_f32_min = 0x96, + WasmOp_f32_max = 0x97, + WasmOp_f32_copysign = 0x98, + WasmOp_f64_abs = 0x99, + WasmOp_f64_neg = 0x9A, + WasmOp_f64_ceil = 0x9B, + WasmOp_f64_floor = 0x9C, + WasmOp_f64_trunc = 0x9D, + WasmOp_f64_nearest = 0x9E, + WasmOp_f64_sqrt = 0x9F, + WasmOp_f64_add = 0xA0, + WasmOp_f64_sub = 0xA1, + WasmOp_f64_mul = 0xA2, + WasmOp_f64_div = 0xA3, + WasmOp_f64_min = 0xA4, + WasmOp_f64_max = 0xA5, + WasmOp_f64_copysign = 0xA6, + WasmOp_i32_wrap_i64 = 0xA7, + WasmOp_i32_trunc_f32_s = 0xA8, + WasmOp_i32_trunc_f32_u = 0xA9, + WasmOp_i32_trunc_f64_s = 0xAA, + WasmOp_i32_trunc_f64_u = 0xAB, + WasmOp_i64_extend_i32_s = 0xAC, + WasmOp_i64_extend_i32_u = 0xAD, + WasmOp_i64_trunc_f32_s = 0xAE, + WasmOp_i64_trunc_f32_u = 0xAF, + WasmOp_i64_trunc_f64_s = 0xB0, + WasmOp_i64_trunc_f64_u = 0xB1, + WasmOp_f32_convert_i32_s = 0xB2, + WasmOp_f32_convert_i32_u = 0xB3, + WasmOp_f32_convert_i64_s = 0xB4, + WasmOp_f32_convert_i64_u = 0xB5, + WasmOp_f32_demote_f64 = 0xB6, + WasmOp_f64_convert_i32_s = 0xB7, + WasmOp_f64_convert_i32_u = 0xB8, + WasmOp_f64_convert_i64_s = 0xB9, + WasmOp_f64_convert_i64_u = 0xBA, + WasmOp_f64_promote_f32 = 0xBB, + WasmOp_i32_reinterpret_f32 = 0xBC, + WasmOp_i64_reinterpret_f64 = 0xBD, + WasmOp_f32_reinterpret_i32 = 0xBE, + WasmOp_f64_reinterpret_i64 = 0xBF, + WasmOp_i32_extend8_s = 0xC0, + WasmOp_i32_extend16_s = 0xC1, + WasmOp_i64_extend8_s = 0xC2, + WasmOp_i64_extend16_s = 0xC3, + WasmOp_i64_extend32_s = 0xC4, + + WasmOp_prefixed = 0xFC, +}; + +enum WasmPrefixedOp { + WasmPrefixedOp_i32_trunc_sat_f32_s = 0x00, + WasmPrefixedOp_i32_trunc_sat_f32_u = 0x01, + WasmPrefixedOp_i32_trunc_sat_f64_s = 0x02, + WasmPrefixedOp_i32_trunc_sat_f64_u = 0x03, + WasmPrefixedOp_i64_trunc_sat_f32_s = 0x04, + WasmPrefixedOp_i64_trunc_sat_f32_u = 0x05, + WasmPrefixedOp_i64_trunc_sat_f64_s = 0x06, + WasmPrefixedOp_i64_trunc_sat_f64_u = 0x07, + WasmPrefixedOp_memory_init = 0x08, + WasmPrefixedOp_data_drop = 0x09, + WasmPrefixedOp_memory_copy = 0x0A, + WasmPrefixedOp_memory_fill = 0x0B, + WasmPrefixedOp_table_init = 0x0C, + WasmPrefixedOp_elem_drop = 0x0D, + WasmPrefixedOp_table_copy = 0x0E, + WasmPrefixedOp_table_grow = 0x0F, + WasmPrefixedOp_table_size = 0x10, + WasmPrefixedOp_table_fill = 0x11, +}; + +static const uint32_t wasm_page_size = 64 * 1024; + +struct ProgramCounter { + uint32_t opcode; + uint32_t operand; +}; + +struct TypeInfo { + uint32_t param_count; + // bitset with param_count bits, indexed from lsb, 0 -> 32-bit, 1 -> 64-bit + uint32_t param_types; + uint32_t result_count; + // bitset with result_count bits, indexed from lsb, 0 -> 32-bit, 1 -> 64-bit + uint32_t result_types; +}; + +struct Function { + // Index to start of code in opcodes/operands. + struct ProgramCounter entry_pc; + uint32_t type_idx; + uint32_t locals_count; + // multi-word bitset with vm->types[type_idx].param_count + locals_count bits + // indexed from lsb of the first element, 0 -> 32-bit, 1 -> 64-bit + uint32_t *local_types; +}; + +enum ImpMod { + ImpMod_wasi_snapshot_preview1, +}; + +enum ImpName { + ImpName_args_get, + ImpName_args_sizes_get, + ImpName_clock_time_get, + ImpName_debug, + ImpName_debug_slice, + ImpName_environ_get, + ImpName_environ_sizes_get, + ImpName_fd_close, + ImpName_fd_fdstat_get, + ImpName_fd_filestat_get, + ImpName_fd_filestat_set_size, + ImpName_fd_filestat_set_times, + ImpName_fd_pread, + ImpName_fd_prestat_dir_name, + ImpName_fd_prestat_get, + ImpName_fd_pwrite, + ImpName_fd_read, + ImpName_fd_readdir, + ImpName_fd_write, + ImpName_path_create_directory, + ImpName_path_filestat_get, + ImpName_path_open, + ImpName_path_remove_directory, + ImpName_path_rename, + ImpName_path_unlink_file, + ImpName_proc_exit, + ImpName_random_get, +}; + +struct Import { + enum ImpMod mod; + enum ImpName name; + uint32_t type_idx; +}; + +struct VirtualMachine { + uint64_t *stack; + /// Points to one after the last stack item. + uint32_t stack_top; + struct ProgramCounter pc; + uint32_t memory_len; + const char *mod_ptr; + uint8_t *opcodes; + uint32_t *operands; + struct Function *functions; + /// Type index to start of type in module_bytes. + struct TypeInfo *types; + uint64_t *globals; + char *memory; + struct Import *imports; + uint32_t imports_len; + char **args; + uint32_t *table; +}; + +static int to_host_fd(int32_t wasi_fd) { + const struct Preopen *preopen = find_preopen(wasi_fd); + if (!preopen) return wasi_fd; + return preopen->host_fd; +} + +static enum wasi_errno_t to_wasi_err(int err) { + switch (err) { + case EACCES: return WASI_EACCES; + case EDQUOT: return WASI_EDQUOT; + case EIO: return WASI_EIO; + case EFBIG: return WASI_EFBIG; + case ENOSPC: return WASI_ENOSPC; + case EPIPE: return WASI_EPIPE; + case EBADF: return WASI_EBADF; + case ENOMEM: return WASI_ENOMEM; + case ENOENT: return WASI_ENOENT; + case EEXIST: return WASI_EEXIST; + default: + fprintf(stderr, "unexpected errno: %s\n", strerror(err)); + abort(); + }; +} + +/// fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; +static enum wasi_errno_t wasi_args_sizes_get(struct VirtualMachine *vm, + uint32_t argc, uint32_t argv_buf_size) +{ + uint32_t args_len = 0; + size_t buf_size = 0; + while (vm->args[args_len]) { + buf_size += strlen(vm->args[args_len]) + 1; + args_len += 1; + } + write_u32_le(vm->memory + argc, args_len); + write_u32_le(vm->memory + argv_buf_size, buf_size); + return WASI_ESUCCESS; +} + +/// extern fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t; +static enum wasi_errno_t wasi_args_get(struct VirtualMachine *vm, + uint32_t argv, uint32_t argv_buf) +{ + panic("TODO implement wasi_args_get"); + //var argv_buf_i: usize = 0; + //for (vm->args) |arg, arg_i| { + // // Write the arg to the buffer. + // const argv_ptr = argv_buf + argv_buf_i; + // const arg_len = mem.span(arg).len + 1; + // mem.copy(u8, vm->memory[argv_buf + argv_buf_i ..], arg[0..arg_len]); + // argv_buf_i += arg_len; + + // write_u32_le(vm->memory[argv + 4 * arg_i ..][0..4], @intCast(u32, argv_ptr)); + //} + return WASI_ESUCCESS; +} + +/// extern fn random_get(buf: [*]u8, buf_len: usize) errno_t; +static enum wasi_errno_t wasi_random_get(struct VirtualMachine *vm, + uint32_t buf, uint32_t buf_len) +{ + panic("TODO implement wasi_random_get"); + //const host_buf = vm->memory[buf..][0..buf_len]; + //std.crypto.random.bytes(host_buf); + return WASI_ESUCCESS; +} + +/// fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; +/// const prestat_t = extern struct { +/// pr_type: u8, +/// u: usize, +/// }; +static enum wasi_errno_t wasi_fd_prestat_get(struct VirtualMachine *vm, + int32_t fd, uint32_t buf) +{ + panic("TODO implement wasi_fd_prestat_get"); + //const preopen = findPreopen(fd) orelse return .BADF; + //write_u32_le(vm->memory[buf + 0 ..][0..4], 0); + //write_u32_le(vm->memory[buf + 4 ..][0..4], @intCast(u32, preopen.name.len)); + return WASI_ESUCCESS; +} + +/// fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; +static enum wasi_errno_t wasi_fd_prestat_dir_name(struct VirtualMachine *vm, + int32_t fd, uint32_t path, uint32_t path_len) +{ + panic("TODO implement wasi_fd_prestat_dir_name"); + //const preopen = findPreopen(fd) orelse return .BADF; + //assert(path_len == preopen.name.len); + //mem.copy(u8, vm->memory[path..], preopen.name); + return WASI_ESUCCESS; +} + +/// extern fn fd_close(fd: fd_t) errno_t; +static enum wasi_errno_t wasi_fd_close(struct VirtualMachine *vm, int32_t fd) { + panic("TODO implement wasi_fd_close"); + //_ = vm; + //const host_fd = toHostFd(fd); + //os.close(host_fd); + return WASI_ESUCCESS; +} + +static enum wasi_errno_t wasi_fd_read( + struct VirtualMachine *vm, + int32_t fd, + uint32_t iovs, // [*]const iovec_t + uint32_t iovs_len, // usize + uint32_t nread // *usize +) { + panic("TODO implement wasi_fd_read"); + //const host_fd = toHostFd(fd); + //var i: u32 = 0; + //var total_read: usize = 0; + //while (i < iovs_len) : (i += 1) { + // uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); + // uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); + // const buf = vm->memory[ptr..][0..len]; + // const read = os.read(host_fd, buf) catch |err| return toWasiError(err); + // trace_log.debug("read {d} bytes out of {d}", .{ read, buf.len }); + // total_read += read; + // if (read != buf.len) break; + //} + //write_u32_le(vm->memory[nread..][0..4], @intCast(u32, total_read)); + return WASI_ESUCCESS; +} + +/// extern fn fd_write(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; +/// const ciovec_t = extern struct { +/// base: [*]const u8, +/// len: usize, +/// }; +static enum wasi_errno_t wasi_fd_write(struct VirtualMachine *vm, + int32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t nwritten) +{ + int host_fd = to_host_fd(fd); + size_t total_written = 0; + for (uint32_t i = 0; i < iovs_len; i += 1) { + uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); + uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); + ssize_t written = write(host_fd, vm->memory + ptr, len); + if (written < 0) return to_wasi_err(errno); + total_written += written; + if (written != len) break; + } + write_u32_le(vm->memory + nwritten, total_written); + return WASI_ESUCCESS; +} + +static enum wasi_errno_t wasi_fd_pwrite( + struct VirtualMachine *vm, + int32_t fd, + uint32_t iovs, // [*]const ciovec_t + uint32_t iovs_len, // usize + uint64_t offset, // wasi.filesize_t, + uint32_t written_ptr // *usize +) { + panic("TODO implement wasi_fd_pwrite"); + //const host_fd = toHostFd(fd); + //var i: u32 = 0; + //var written: usize = 0; + //while (i < iovs_len) : (i += 1) { + // uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); + // uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); + // const buf = vm->memory[ptr..][0..len]; + // const w = os.pwrite(host_fd, buf, offset + written) catch |err| return toWasiError(err); + // written += w; + // if (w != buf.len) break; + //} + //write_u32_le(vm->memory[written_ptr..][0..4], @intCast(u32, written)); + return WASI_ESUCCESS; +} + +///extern fn path_open( +/// dirfd: fd_t, +/// dirflags: lookupflags_t, +/// path: [*]const u8, +/// path_len: usize, +/// oflags: oflags_t, +/// fs_rights_base: rights_t, +/// fs_rights_inheriting: rights_t, +/// fs_flags: fdflags_t, +/// fd: *fd_t, +///) errno_t; +static enum wasi_errno_t wasi_path_open( + struct VirtualMachine *vm, + int32_t dirfd, + uint32_t dirflags, // wasi.lookupflags_t, + uint32_t path, + uint32_t path_len, + uint16_t oflags, // wasi.oflags_t, + uint64_t fs_rights_base, // wasi.rights_t, + uint64_t fs_rights_inheriting, // wasi.rights_t, + uint16_t fs_flags, // wasi.fdflags_t, + uint32_t fd +) { + panic("TODO implement wasi_path_open"); + //const sub_path = vm->memory[path..][0..path_len]; + //const host_fd = toHostFd(dirfd); + //var flags: u32 = @as(u32, if (oflags & wasi.O.CREAT != 0) os.O.CREAT else 0) | + // @as(u32, if (oflags & wasi.O.DIRECTORY != 0) os.O.DIRECTORY else 0) | + // @as(u32, if (oflags & wasi.O.EXCL != 0) os.O.EXCL else 0) | + // @as(u32, if (oflags & wasi.O.TRUNC != 0) os.O.TRUNC else 0) | + // @as(u32, if (fs_flags & wasi.FDFLAG.APPEND != 0) os.O.APPEND else 0) | + // @as(u32, if (fs_flags & wasi.FDFLAG.DSYNC != 0) os.O.DSYNC else 0) | + // @as(u32, if (fs_flags & wasi.FDFLAG.NONBLOCK != 0) os.O.NONBLOCK else 0) | + // @as(u32, if (fs_flags & wasi.FDFLAG.SYNC != 0) os.O.SYNC else 0); + //if ((fs_rights_base & wasi.RIGHT.FD_READ != 0) and + // (fs_rights_base & wasi.RIGHT.FD_WRITE != 0)) + //{ + // flags |= os.O.RDWR; + //} else if (fs_rights_base & wasi.RIGHT.FD_WRITE != 0) { + // flags |= os.O.WRONLY; + //} else if (fs_rights_base & wasi.RIGHT.FD_READ != 0) { + // flags |= os.O.RDONLY; // no-op because O_RDONLY is 0 + //} + //const mode = 0o644; + //const res_fd = os.openat(host_fd, sub_path, flags, mode) catch |err| return toWasiError(err); + //mem.writeIntLittle(i32, vm->memory[fd..][0..4], res_fd); + return WASI_ESUCCESS; +} + +static enum wasi_errno_t wasi_path_filestat_get( + struct VirtualMachine *vm, + int32_t fd, + uint32_t flags, // wasi.lookupflags_t, + uint32_t path, // [*]const u8 + uint32_t path_len, // usize + uint32_t buf // *filestat_t +) { + panic("TODO implement wasi_path_filestat_get"); + //const sub_path = vm->memory[path..][0..path_len]; + //const host_fd = toHostFd(fd); + //const dir: fs.Dir = .{ .fd = host_fd }; + //const stat = dir.statFile(sub_path) catch |err| return toWasiError(err); + //return finishWasiStat(vm, buf, stat); + return WASI_ESUCCESS; +} + +/// extern fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; +static enum wasi_errno_t wasi_path_create_directory(struct VirtualMachine *vm, int32_t fd, uint32_t path, uint32_t path_len) { + panic("TODO implement wasi_path_create_directory"); + //const sub_path = vm->memory[path..][0..path_len]; + //trace_log.debug("wasi_path_create_directory fd={d} path={s}", .{ fd, sub_path }); + //const host_fd = toHostFd(fd); + //const dir: fs.Dir = .{ .fd = host_fd }; + //dir.makeDir(sub_path) catch |err| return toWasiError(err); + return WASI_ESUCCESS; +} + +static enum wasi_errno_t wasi_path_rename( + struct VirtualMachine *vm, + int32_t old_fd, + uint32_t old_path_ptr, // [*]const u8 + uint32_t old_path_len, // usize + int32_t new_fd, + uint32_t new_path_ptr, // [*]const u8 + uint32_t new_path_len // usize +) { + panic("TODO implement wasi_path_rename"); + //const old_path = vm->memory[old_path_ptr..][0..old_path_len]; + //const new_path = vm->memory[new_path_ptr..][0..new_path_len]; + //trace_log.debug("wasi_path_rename old_fd={d} old_path={s} new_fd={d} new_path={s}", .{ + // old_fd, old_path, new_fd, new_path, + //}); + //const old_host_fd = toHostFd(old_fd); + //const new_host_fd = toHostFd(new_fd); + //os.renameat(old_host_fd, old_path, new_host_fd, new_path) catch |err| return toWasiError(err); + return WASI_ESUCCESS; +} + +/// extern fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; +static enum wasi_errno_t wasi_fd_filestat_get(struct VirtualMachine *vm, int32_t fd, uint32_t buf) { + panic("TODO implement wasi_fd_filestat_get"); + //const host_fd = toHostFd(fd); + //const file = fs.File{ .handle = host_fd }; + //const stat = file.stat() catch |err| return toWasiError(err); + //return finishWasiStat(vm, buf, stat); + return WASI_ESUCCESS; +} + +static enum wasi_errno_t wasi_fd_filestat_set_size( struct VirtualMachine *vm, + int32_t fd, uint64_t size) +{ + panic("TODO implement wasi_fd_filestat_set_size"); + //_ = vm; + //const host_fd = toHostFd(fd); + //os.ftruncate(host_fd, size) catch |err| return toWasiError(err); + return WASI_ESUCCESS; +} + +/// pub extern "wasi_snapshot_preview1" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; +/// pub const fdstat_t = extern struct { +/// fs_filetype: filetype_t, u8 +/// fs_flags: fdflags_t, u16 +/// fs_rights_base: rights_t, u64 +/// fs_rights_inheriting: rights_t, u64 +/// }; +static enum wasi_errno_t wasi_fd_fdstat_get(struct VirtualMachine *vm, int32_t fd, uint32_t buf) { + panic("TODO implement wasi_fd_fdstat_get"); + //const host_fd = toHostFd(fd); + //const file = fs.File{ .handle = host_fd }; + //const stat = file.stat() catch |err| return toWasiError(err); + //mem.writeIntLittle(u16, vm->memory[buf + 0x00 ..][0..2], @enumToInt(toWasiFileType(stat.kind))); + //mem.writeIntLittle(u16, vm->memory[buf + 0x02 ..][0..2], 0); // flags + //mem.writeIntLittle(u64, vm->memory[buf + 0x08 ..][0..8], math.maxInt(u64)); // rights_base + //mem.writeIntLittle(u64, vm->memory[buf + 0x10 ..][0..8], math.maxInt(u64)); // rights_inheriting + return WASI_ESUCCESS; +} + +/// extern fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; +static enum wasi_errno_t wasi_clock_time_get(struct VirtualMachine *vm, + uint32_t clock_id, uint64_t precision, uint32_t timestamp) +{ + panic("TODO implement wasi_clock_time_get"); + ////const host_clock_id = toHostClockId(clock_id); + //_ = precision; + //_ = clock_id; + //const wasi_ts = toWasiTimestamp(std.time.nanoTimestamp()); + //mem.writeIntLittle(u64, vm->memory[timestamp..][0..8], wasi_ts); + return WASI_ESUCCESS; +} + +///pub extern "wasi_snapshot_preview1" fn debug(string: [*:0]const u8, x: u64) void; +void wasi_debug(struct VirtualMachine *vm, uint32_t text, uint64_t n) { + panic("TODO implement wasi_debug"); + //const s = mem.sliceTo(vm->memory[text..], 0); + //trace_log.debug("wasi_debug: '{s}' number={d} {x}", .{ s, n, n }); +} + +/// pub extern "wasi_snapshot_preview1" fn debug_slice(ptr: [*]const u8, len: usize) void; +void wasi_debug_slice(struct VirtualMachine *vm, uint32_t ptr, uint32_t len) { + panic("TODO implement wasi_debug_slice"); + //const s = vm->memory[ptr..][0..len]; + //trace_log.debug("wasi_debug_slice: '{s}'", .{s}); +} + + + +struct Label { + enum WasmOp opcode; + uint32_t stack_depth; + struct TypeInfo type_info; + // this is a UINT32_MAX terminated linked list that is stored in the operands array + uint32_t ref_list; + union { + struct ProgramCounter loop_pc; + uint32_t else_ref; + } extra; +}; + +static uint32_t Label_operandCount(const struct Label *label) { + if (label->opcode == WasmOp_loop) { + return label->type_info.param_count; + } else { + return label->type_info.result_count; + } +} + +static bool Label_operandType(const struct Label *label, uint32_t index) { + if (label->opcode == WasmOp_loop) { + return bs_isSet(&label->type_info.param_types, index); + } else { + return bs_isSet(&label->type_info.result_types, index); + } +} + +static void vm_decodeCode(struct VirtualMachine *vm, struct Function *func, uint32_t *code_i, + struct ProgramCounter *pc) +{ + const char *mod_ptr = vm->mod_ptr; + uint8_t *opcodes = vm->opcodes; + uint32_t *operands = vm->operands; + struct TypeInfo *func_type_info = &vm->types[func->type_idx]; + + uint32_t unreachable_depth = 0; + uint32_t stack_depth = func_type_info->param_count + func->locals_count + 2; + static uint32_t stack_types[1 << (12 - 3)]; + + static struct Label labels[1 << 9]; + uint32_t label_i = 0; + labels[label_i].opcode = WasmOp_block; + labels[label_i].stack_depth = stack_depth; + labels[label_i].type_info = vm->types[func->type_idx]; + labels[label_i].ref_list = UINT32_MAX; + + for (;;) { + enum WasmOp opcode = (uint8_t)mod_ptr[*code_i]; + *code_i += 1; + enum WasmPrefixedOp prefixed_opcode; + if (opcode == WasmOp_prefixed) prefixed_opcode = read32_uleb128(mod_ptr, code_i); + + uint32_t initial_stack_depth = stack_depth; + if (unreachable_depth == 0) { + switch (opcode) { + case WasmOp_unreachable: + case WasmOp_nop: + case WasmOp_block: + case WasmOp_loop: + case WasmOp_else: + case WasmOp_end: + case WasmOp_br: + case WasmOp_call: + case WasmOp_return: + break; + + case WasmOp_if: + case WasmOp_br_if: + case WasmOp_br_table: + case WasmOp_call_indirect: + case WasmOp_drop: + case WasmOp_local_set: + case WasmOp_global_set: + stack_depth -= 1; + break; + + case WasmOp_select: + stack_depth -= 2; + break; + + case WasmOp_local_get: + case WasmOp_global_get: + case WasmOp_memory_size: + case WasmOp_i32_const: + case WasmOp_i64_const: + case WasmOp_f32_const: + case WasmOp_f64_const: + stack_depth += 1; + break; + + case WasmOp_local_tee: + case WasmOp_i32_load: + case WasmOp_i64_load: + case WasmOp_f32_load: + case WasmOp_f64_load: + case WasmOp_i32_load8_s: + case WasmOp_i32_load8_u: + case WasmOp_i32_load16_s: + case WasmOp_i32_load16_u: + case WasmOp_i64_load8_s: + case WasmOp_i64_load8_u: + case WasmOp_i64_load16_s: + case WasmOp_i64_load16_u: + case WasmOp_i64_load32_s: + case WasmOp_i64_load32_u: + case WasmOp_memory_grow: + case WasmOp_i32_eqz: + case WasmOp_i32_clz: + case WasmOp_i32_ctz: + case WasmOp_i32_popcnt: + case WasmOp_i64_eqz: + case WasmOp_i64_clz: + case WasmOp_i64_ctz: + case WasmOp_i64_popcnt: + case WasmOp_f32_abs: + case WasmOp_f32_neg: + case WasmOp_f32_ceil: + case WasmOp_f32_floor: + case WasmOp_f32_trunc: + case WasmOp_f32_nearest: + case WasmOp_f32_sqrt: + case WasmOp_f64_abs: + case WasmOp_f64_neg: + case WasmOp_f64_ceil: + case WasmOp_f64_floor: + case WasmOp_f64_trunc: + case WasmOp_f64_nearest: + case WasmOp_f64_sqrt: + case WasmOp_i32_wrap_i64: + case WasmOp_i32_trunc_f32_s: + case WasmOp_i32_trunc_f32_u: + case WasmOp_i32_trunc_f64_s: + case WasmOp_i32_trunc_f64_u: + case WasmOp_i64_extend_i32_s: + case WasmOp_i64_extend_i32_u: + case WasmOp_i64_trunc_f32_s: + case WasmOp_i64_trunc_f32_u: + case WasmOp_i64_trunc_f64_s: + case WasmOp_i64_trunc_f64_u: + case WasmOp_f32_convert_i32_s: + case WasmOp_f32_convert_i32_u: + case WasmOp_f32_convert_i64_s: + case WasmOp_f32_convert_i64_u: + case WasmOp_f32_demote_f64: + case WasmOp_f64_convert_i32_s: + case WasmOp_f64_convert_i32_u: + case WasmOp_f64_convert_i64_s: + case WasmOp_f64_convert_i64_u: + case WasmOp_f64_promote_f32: + case WasmOp_i32_reinterpret_f32: + case WasmOp_i64_reinterpret_f64: + case WasmOp_f32_reinterpret_i32: + case WasmOp_f64_reinterpret_i64: + case WasmOp_i32_extend8_s: + case WasmOp_i32_extend16_s: + case WasmOp_i64_extend8_s: + case WasmOp_i64_extend16_s: + case WasmOp_i64_extend32_s: + break; + + case WasmOp_i32_store: + case WasmOp_i64_store: + case WasmOp_f32_store: + case WasmOp_f64_store: + case WasmOp_i32_store8: + case WasmOp_i32_store16: + case WasmOp_i64_store8: + case WasmOp_i64_store16: + case WasmOp_i64_store32: + stack_depth -= 2; + break; + + case WasmOp_i32_eq: + case WasmOp_i32_ne: + case WasmOp_i32_lt_s: + case WasmOp_i32_lt_u: + case WasmOp_i32_gt_s: + case WasmOp_i32_gt_u: + case WasmOp_i32_le_s: + case WasmOp_i32_le_u: + case WasmOp_i32_ge_s: + case WasmOp_i32_ge_u: + case WasmOp_i64_eq: + case WasmOp_i64_ne: + case WasmOp_i64_lt_s: + case WasmOp_i64_lt_u: + case WasmOp_i64_gt_s: + case WasmOp_i64_gt_u: + case WasmOp_i64_le_s: + case WasmOp_i64_le_u: + case WasmOp_i64_ge_s: + case WasmOp_i64_ge_u: + case WasmOp_f32_eq: + case WasmOp_f32_ne: + case WasmOp_f32_lt: + case WasmOp_f32_gt: + case WasmOp_f32_le: + case WasmOp_f32_ge: + case WasmOp_f64_eq: + case WasmOp_f64_ne: + case WasmOp_f64_lt: + case WasmOp_f64_gt: + case WasmOp_f64_le: + case WasmOp_f64_ge: + case WasmOp_i32_add: + case WasmOp_i32_sub: + case WasmOp_i32_mul: + case WasmOp_i32_div_s: + case WasmOp_i32_div_u: + case WasmOp_i32_rem_s: + case WasmOp_i32_rem_u: + case WasmOp_i32_and: + case WasmOp_i32_or: + case WasmOp_i32_xor: + case WasmOp_i32_shl: + case WasmOp_i32_shr_s: + case WasmOp_i32_shr_u: + case WasmOp_i32_rotl: + case WasmOp_i32_rotr: + case WasmOp_i64_add: + case WasmOp_i64_sub: + case WasmOp_i64_mul: + case WasmOp_i64_div_s: + case WasmOp_i64_div_u: + case WasmOp_i64_rem_s: + case WasmOp_i64_rem_u: + case WasmOp_i64_and: + case WasmOp_i64_or: + case WasmOp_i64_xor: + case WasmOp_i64_shl: + case WasmOp_i64_shr_s: + case WasmOp_i64_shr_u: + case WasmOp_i64_rotl: + case WasmOp_i64_rotr: + case WasmOp_f32_add: + case WasmOp_f32_sub: + case WasmOp_f32_mul: + case WasmOp_f32_div: + case WasmOp_f32_min: + case WasmOp_f32_max: + case WasmOp_f32_copysign: + case WasmOp_f64_add: + case WasmOp_f64_sub: + case WasmOp_f64_mul: + case WasmOp_f64_div: + case WasmOp_f64_min: + case WasmOp_f64_max: + case WasmOp_f64_copysign: + stack_depth -= 1; + break; + + case WasmOp_prefixed: + switch (prefixed_opcode) { + case WasmPrefixedOp_i32_trunc_sat_f32_s: + case WasmPrefixedOp_i32_trunc_sat_f32_u: + case WasmPrefixedOp_i32_trunc_sat_f64_s: + case WasmPrefixedOp_i32_trunc_sat_f64_u: + case WasmPrefixedOp_i64_trunc_sat_f32_s: + case WasmPrefixedOp_i64_trunc_sat_f32_u: + case WasmPrefixedOp_i64_trunc_sat_f64_s: + case WasmPrefixedOp_i64_trunc_sat_f64_u: + break; + + case WasmPrefixedOp_memory_init: + case WasmPrefixedOp_memory_copy: + case WasmPrefixedOp_memory_fill: + case WasmPrefixedOp_table_init: + case WasmPrefixedOp_table_copy: + case WasmPrefixedOp_table_fill: + stack_depth -= 3; + break; + + case WasmPrefixedOp_data_drop: + case WasmPrefixedOp_elem_drop: + break; + + case WasmPrefixedOp_table_grow: + stack_depth -= 1; + break; + + case WasmPrefixedOp_table_size: + stack_depth += 1; + break; + + default: panic("unexpected prefixed opcode"); + } + break; + + default: panic("unexpected opcode"); + } + switch (opcode) { + case WasmOp_unreachable: + case WasmOp_nop: + case WasmOp_block: + case WasmOp_loop: + case WasmOp_else: + case WasmOp_end: + case WasmOp_br: + case WasmOp_call: + case WasmOp_return: + case WasmOp_if: + case WasmOp_br_if: + case WasmOp_br_table: + case WasmOp_call_indirect: + case WasmOp_drop: + case WasmOp_select: + case WasmOp_local_set: + case WasmOp_local_get: + case WasmOp_local_tee: + case WasmOp_global_set: + case WasmOp_global_get: + case WasmOp_i32_store: + case WasmOp_i64_store: + case WasmOp_f32_store: + case WasmOp_f64_store: + case WasmOp_i32_store8: + case WasmOp_i32_store16: + case WasmOp_i64_store8: + case WasmOp_i64_store16: + case WasmOp_i64_store32: + break; + + case WasmOp_i32_const: + case WasmOp_f32_const: + case WasmOp_memory_size: + case WasmOp_i32_load: + case WasmOp_f32_load: + case WasmOp_i32_load8_s: + case WasmOp_i32_load8_u: + case WasmOp_i32_load16_s: + case WasmOp_i32_load16_u: + case WasmOp_memory_grow: + case WasmOp_i32_eqz: + case WasmOp_i32_clz: + case WasmOp_i32_ctz: + case WasmOp_i32_popcnt: + case WasmOp_i64_eqz: + case WasmOp_f32_abs: + case WasmOp_f32_neg: + case WasmOp_f32_ceil: + case WasmOp_f32_floor: + case WasmOp_f32_trunc: + case WasmOp_f32_nearest: + case WasmOp_f32_sqrt: + case WasmOp_i32_wrap_i64: + case WasmOp_i32_trunc_f32_s: + case WasmOp_i32_trunc_f32_u: + case WasmOp_i32_trunc_f64_s: + case WasmOp_i32_trunc_f64_u: + case WasmOp_f32_convert_i32_s: + case WasmOp_f32_convert_i32_u: + case WasmOp_f32_convert_i64_s: + case WasmOp_f32_convert_i64_u: + case WasmOp_f32_demote_f64: + case WasmOp_i32_reinterpret_f32: + case WasmOp_f32_reinterpret_i32: + case WasmOp_i32_extend8_s: + case WasmOp_i32_extend16_s: + case WasmOp_i32_eq: + case WasmOp_i32_ne: + case WasmOp_i32_lt_s: + case WasmOp_i32_lt_u: + case WasmOp_i32_gt_s: + case WasmOp_i32_gt_u: + case WasmOp_i32_le_s: + case WasmOp_i32_le_u: + case WasmOp_i32_ge_s: + case WasmOp_i32_ge_u: + case WasmOp_i64_eq: + case WasmOp_i64_ne: + case WasmOp_i64_lt_s: + case WasmOp_i64_lt_u: + case WasmOp_i64_gt_s: + case WasmOp_i64_gt_u: + case WasmOp_i64_le_s: + case WasmOp_i64_le_u: + case WasmOp_i64_ge_s: + case WasmOp_i64_ge_u: + case WasmOp_f32_eq: + case WasmOp_f32_ne: + case WasmOp_f32_lt: + case WasmOp_f32_gt: + case WasmOp_f32_le: + case WasmOp_f32_ge: + case WasmOp_f64_eq: + case WasmOp_f64_ne: + case WasmOp_f64_lt: + case WasmOp_f64_gt: + case WasmOp_f64_le: + case WasmOp_f64_ge: + case WasmOp_i32_add: + case WasmOp_i32_sub: + case WasmOp_i32_mul: + case WasmOp_i32_div_s: + case WasmOp_i32_div_u: + case WasmOp_i32_rem_s: + case WasmOp_i32_rem_u: + case WasmOp_i32_and: + case WasmOp_i32_or: + case WasmOp_i32_xor: + case WasmOp_i32_shl: + case WasmOp_i32_shr_s: + case WasmOp_i32_shr_u: + case WasmOp_i32_rotl: + case WasmOp_i32_rotr: + case WasmOp_f32_add: + case WasmOp_f32_sub: + case WasmOp_f32_mul: + case WasmOp_f32_div: + case WasmOp_f32_min: + case WasmOp_f32_max: + case WasmOp_f32_copysign: + bs_unset(stack_types, stack_depth - 1); + break; + + case WasmOp_i64_const: + case WasmOp_f64_const: + case WasmOp_i64_load: + case WasmOp_f64_load: + case WasmOp_i64_load8_s: + case WasmOp_i64_load8_u: + case WasmOp_i64_load16_s: + case WasmOp_i64_load16_u: + case WasmOp_i64_load32_s: + case WasmOp_i64_load32_u: + case WasmOp_i64_clz: + case WasmOp_i64_ctz: + case WasmOp_i64_popcnt: + case WasmOp_f64_abs: + case WasmOp_f64_neg: + case WasmOp_f64_ceil: + case WasmOp_f64_floor: + case WasmOp_f64_trunc: + case WasmOp_f64_nearest: + case WasmOp_f64_sqrt: + case WasmOp_i64_extend_i32_s: + case WasmOp_i64_extend_i32_u: + case WasmOp_i64_trunc_f32_s: + case WasmOp_i64_trunc_f32_u: + case WasmOp_i64_trunc_f64_s: + case WasmOp_i64_trunc_f64_u: + case WasmOp_f64_convert_i32_s: + case WasmOp_f64_convert_i32_u: + case WasmOp_f64_convert_i64_s: + case WasmOp_f64_convert_i64_u: + case WasmOp_f64_promote_f32: + case WasmOp_i64_reinterpret_f64: + case WasmOp_f64_reinterpret_i64: + case WasmOp_i64_extend8_s: + case WasmOp_i64_extend16_s: + case WasmOp_i64_extend32_s: + case WasmOp_i64_add: + case WasmOp_i64_sub: + case WasmOp_i64_mul: + case WasmOp_i64_div_s: + case WasmOp_i64_div_u: + case WasmOp_i64_rem_s: + case WasmOp_i64_rem_u: + case WasmOp_i64_and: + case WasmOp_i64_or: + case WasmOp_i64_xor: + case WasmOp_i64_shl: + case WasmOp_i64_shr_s: + case WasmOp_i64_shr_u: + case WasmOp_i64_rotl: + case WasmOp_i64_rotr: + case WasmOp_f64_add: + case WasmOp_f64_sub: + case WasmOp_f64_mul: + case WasmOp_f64_div: + case WasmOp_f64_min: + case WasmOp_f64_max: + case WasmOp_f64_copysign: + bs_set(stack_types, stack_depth - 1); + break; + + case WasmOp_prefixed: + switch (prefixed_opcode) { + case WasmPrefixedOp_memory_init: + case WasmPrefixedOp_memory_copy: + case WasmPrefixedOp_memory_fill: + case WasmPrefixedOp_table_init: + case WasmPrefixedOp_table_copy: + case WasmPrefixedOp_table_fill: + case WasmPrefixedOp_data_drop: + case WasmPrefixedOp_elem_drop: + break; + + case WasmPrefixedOp_i32_trunc_sat_f32_s: + case WasmPrefixedOp_i32_trunc_sat_f32_u: + case WasmPrefixedOp_i32_trunc_sat_f64_s: + case WasmPrefixedOp_i32_trunc_sat_f64_u: + case WasmPrefixedOp_table_grow: + case WasmPrefixedOp_table_size: + bs_unset(stack_types, stack_depth - 1); + break; + + case WasmPrefixedOp_i64_trunc_sat_f32_s: + case WasmPrefixedOp_i64_trunc_sat_f32_u: + case WasmPrefixedOp_i64_trunc_sat_f64_s: + case WasmPrefixedOp_i64_trunc_sat_f64_u: + bs_set(stack_types, stack_depth - 1); + break; + + default: panic("unexpected prefixed opcode"); + } + break; + + default: panic("unexpected opcode"); + } + } + + switch (opcode) { + case WasmOp_unreachable: + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_unreachable; + pc->opcode += 1; + } + break; + + case WasmOp_nop: + case WasmOp_i32_reinterpret_f32: + case WasmOp_i64_reinterpret_f64: + case WasmOp_f32_reinterpret_i32: + case WasmOp_f64_reinterpret_i64: + break; + + case WasmOp_block: + case WasmOp_loop: + case WasmOp_if: + { + int64_t block_type = read64_ileb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + label_i += 1; + struct Label *label = &labels[label_i]; + label->opcode = opcode; + if (block_type < 0) { + label->type_info.param_count = 0; + label->type_info.param_types = 0; + label->type_info.result_count = block_type != -0x40; + label->type_info.result_types = 0; + switch (block_type) { + case -0x40: break; + case -1: case -3: bs_unset(&label->type_info.param_types, 0); break; + case -2: case -4: bs_set(&label->type_info.param_types, 0); break; + default: panic("unexpected param type"); + } + } else { + label->type_info = vm->types[block_type]; + } + label->stack_depth = stack_depth - label->type_info.param_count; + label->ref_list = UINT32_MAX; + switch (opcode) { + case WasmOp_block: + break; + + case WasmOp_loop: + label->extra.loop_pc = *pc; + break; + + case WasmOp_if: + opcodes[pc->opcode] = Op_br_if_eqz_void; + pc->opcode += 1; + operands[pc->operand] = 0; + label->extra.else_ref = pc->operand + 1; + pc->operand += 3; + break; + + default: panic("unexpected label opcode"); + } + } + } + break; + + case WasmOp_else: + { + struct Label *label = &labels[label_i]; + assert(label->opcode == WasmOp_if); + label->opcode = WasmOp_else; + if (unreachable_depth == 0) { + uint32_t operand_count = Label_operandCount(label); + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_br_void; + break; + + case 1: + switch ((int)Label_operandType(label, 0)) { + case false: opcodes[pc->opcode] = Op_br_32; break; + case true: opcodes[pc->opcode] = Op_br_64; break; + } + break; + + default: panic("unexpected operand count"); + } + pc->opcode += 1; + operands[pc->operand + 0] = stack_depth - operand_count - label->stack_depth; + operands[pc->operand + 1] = label->ref_list; + label->ref_list = pc->operand + 1; + pc->operand += 3; + assert(stack_depth - label->type_info.result_count == label->stack_depth); + } else unreachable_depth = 0; + operands[label->extra.else_ref + 0] = pc->opcode; + operands[label->extra.else_ref + 1] = pc->operand; + stack_depth = label->stack_depth + label->type_info.param_count; + }; + break; + + case WasmOp_end: + if (unreachable_depth <= 1) { + unreachable_depth = 0; + struct Label *label = &labels[label_i]; + struct ProgramCounter *target_pc = (label->opcode == WasmOp_loop) ? &label->extra.loop_pc : pc; + if (label->opcode == WasmOp_if) { + operands[label->extra.else_ref + 0] = target_pc->opcode; + operands[label->extra.else_ref + 1] = target_pc->operand; + } + uint32_t ref = label->ref_list; + while (ref != UINT32_MAX) { + uint32_t next_ref = operands[ref]; + operands[ref + 0] = target_pc->opcode; + operands[ref + 1] = target_pc->operand; + ref = next_ref; + } + stack_depth = label->stack_depth + label->type_info.result_count; + + if (label_i == 0) { + uint32_t operand_count = Label_operandCount(&labels[0]); + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_return_void; + break; + + case 1: + switch ((int)Label_operandType(&labels[0], 0)) { + case false: opcodes[pc->opcode] = Op_return_32; break; + case true: opcodes[pc->opcode] = Op_return_64; break; + } + break; + + default: panic("unexpected operand count"); + } + pc->opcode += 1; + operands[pc->operand + 0] = 2 + operand_count; + stack_depth -= operand_count; + assert(stack_depth == labels[0].stack_depth); + operands[pc->operand + 1] = stack_depth; + pc->operand += 2; + return; + } + label_i -= 1; + } else unreachable_depth -= 1; + break; + + case WasmOp_br: + case WasmOp_br_if: + { + uint32_t label_idx = read32_uleb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + struct Label *label = &labels[label_i - label_idx]; + uint32_t operand_count = Label_operandCount(label); + switch (opcode) { + case WasmOp_br: + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_br_void; + break; + + case 1: + switch ((int)Label_operandType(label, 0)) { + case false: opcodes[pc->opcode] = Op_br_32; break; + case true: opcodes[pc->opcode] = Op_br_64; break; + } + break; + + default: panic("unexpected operand count"); + } + break; + + case WasmOp_br_if: + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_br_if_nez_void; + break; + + case 1: + switch ((int)Label_operandType(label, 0)) { + case false: opcodes[pc->opcode] = Op_br_if_nez_32; break; + case true: opcodes[pc->opcode] = Op_br_if_nez_64; break; + } + break; + + default: panic("unexpected operand count"); + } + break; + + default: panic("unexpected opcode"); + } + pc->opcode += 1; + operands[pc->operand + 0] = stack_depth - operand_count - label->stack_depth; + operands[pc->operand + 1] = label->ref_list; + label->ref_list = pc->operand + 1; + pc->operand += 3; + } + } + break; + + case WasmOp_br_table: + { + uint32_t labels_len = read32_uleb128(mod_ptr, code_i); + for (uint32_t i = 0; i <= labels_len; i += 1) { + uint32_t label_idx = read32_uleb128(mod_ptr, code_i); + if (unreachable_depth != 0) continue; + struct Label *label = &labels[label_i - label_idx]; + uint32_t operand_count = Label_operandCount(label); + if (i == 0) { + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_br_table_void; + break; + + case 1: + switch ((int)Label_operandType(label, 0)) { + case false: opcodes[pc->opcode] = Op_br_table_32; break; + case true: opcodes[pc->opcode] = Op_br_table_64; break; + } + break; + + default: panic("unexpected operand count"); + } + pc->opcode += 1; + operands[pc->operand] = labels_len; + pc->operand += 1; + } + operands[pc->operand + 0] = stack_depth - operand_count - label->stack_depth; + operands[pc->operand + 1] = label->ref_list; + label->ref_list = pc->operand + 1; + pc->operand += 3; + } + + opcodes[pc->opcode] = opcode; + pc->opcode += 1; + operands[pc->operand] = labels_len; + pc->operand += 1; + } + break; + + case WasmOp_call: + { + uint32_t fn_id = read32_uleb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_call; + pc->opcode += 1; + operands[pc->operand] = fn_id; + pc->operand += 1; + uint32_t type_idx = (fn_id < vm->imports_len) ? + vm->imports[fn_id].type_idx : + vm->functions[fn_id - vm->imports_len].type_idx; + struct TypeInfo *type_info = &vm->types[type_idx]; + stack_depth -= type_info->param_count; + for (uint32_t result_i = 0; result_i < type_info->result_count; result_i += 1) + bs_setValue(stack_types, stack_depth + result_i, + bs_isSet(&type_info->result_types, result_i)); + stack_depth += type_info->result_count; + } + } + break; + + case WasmOp_call_indirect: + { + uint32_t type_idx = read32_uleb128(mod_ptr, code_i); + if (read32_uleb128(mod_ptr, code_i) != 0) panic("unexpected table index"); + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm; + opcodes[pc->opcode + 1] = opcode; + pc->opcode += 2; + struct TypeInfo *type_info = &vm->types[type_idx]; + stack_depth -= type_info->param_count; + for (uint32_t result_i = 0; result_i < type_info->result_count; result_i += 1) + bs_setValue(stack_types, stack_depth + result_i, + bs_isSet(&type_info->result_types, result_i)); + stack_depth += type_info->result_count; + } + } + break; + + case WasmOp_return: + { + uint32_t operand_count = Label_operandCount(&labels[0]); + switch (operand_count) { + case 0: + opcodes[pc->opcode] = Op_return_void; + break; + + case 1: + switch ((int)Label_operandType(&labels[0], 0)) { + case false: opcodes[pc->opcode] = Op_return_32; break; + case true: opcodes[pc->opcode] = Op_return_64; break; + } + break; + + default: panic("unexpected operand count"); + } + pc->opcode += 1; + operands[pc->operand + 0] = 2 + stack_depth - labels[0].stack_depth; + stack_depth -= operand_count; + operands[pc->operand + 1] = stack_depth; + pc->operand += 2; + } + break; + + case WasmOp_select: + case WasmOp_drop: + if (unreachable_depth == 0) { + switch ((int)bs_isSet(stack_types, stack_depth)) { + case false: + switch (opcode) { + case WasmOp_select: + opcodes[pc->opcode] = Op_select_32; + break; + + case WasmOp_drop: + opcodes[pc->opcode] = Op_drop_32; + break; + + default: panic("unexpected opcode"); + } + break; + + case true: + switch (opcode) { + case WasmOp_select: + opcodes[pc->opcode] = Op_select_64; + break; + + case WasmOp_drop: + opcodes[pc->opcode] = Op_drop_64; + break; + + default: panic("unexpected opcode"); + } + break; + } + pc->opcode += 1; + } + break; + + case WasmOp_local_get: + case WasmOp_local_set: + case WasmOp_local_tee: + { + uint32_t local_idx = read32_uleb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + bool local_type = bs_isSet(func->local_types, local_idx); + switch ((int)local_type) { + case false: + switch (opcode) { + case WasmOp_local_get: + opcodes[pc->opcode] = Op_local_get_32; + break; + + case WasmOp_local_set: + opcodes[pc->opcode] = Op_local_set_32; + break; + + case WasmOp_local_tee: + opcodes[pc->opcode] = Op_local_tee_32; + break; + + default: panic("unexpected opcode"); + } + break; + + case true: + switch (opcode) { + case WasmOp_local_get: + opcodes[pc->opcode] = Op_local_get_64; + break; + + case WasmOp_local_set: + opcodes[pc->opcode] = Op_local_set_64; + break; + + case WasmOp_local_tee: + opcodes[pc->opcode] = Op_local_tee_64; + break; + + default: panic("unexpected opcode"); + } + break; + } + pc->opcode += 1; + operands[pc->operand] = initial_stack_depth - local_idx; + pc->operand += 1; + if (opcode == WasmOp_local_get) bs_setValue(stack_types, stack_depth - 1, local_type); + } + } + break; + + case WasmOp_global_get: + case WasmOp_global_set: + { + uint32_t global_idx = read32_uleb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + switch (global_idx) { + case 0: + switch (opcode) { + case WasmOp_global_get: + opcodes[pc->opcode] = Op_global_get_0_32; + break; + + case WasmOp_global_set: + opcodes[pc->opcode] = Op_global_set_0_32; + break; + + default: panic("unexpected opcode"); + } + break; + + default: + switch (opcode) { + case WasmOp_global_get: + opcodes[pc->opcode] = Op_global_get_32; + break; + + case WasmOp_global_set: + opcodes[pc->opcode] = Op_global_set_32; + break; + + default: panic("unexpected opcode"); + } + break; + } + pc->opcode += 1; + if (global_idx != 0) { + operands[pc->operand] = global_idx; + pc->operand += 1; + } + } + } + break; + + case WasmOp_i32_load: + case WasmOp_i64_load: + case WasmOp_f32_load: + case WasmOp_f64_load: + case WasmOp_i32_load8_s: + case WasmOp_i32_load8_u: + case WasmOp_i32_load16_s: + case WasmOp_i32_load16_u: + case WasmOp_i64_load8_s: + case WasmOp_i64_load8_u: + case WasmOp_i64_load16_s: + case WasmOp_i64_load16_u: + case WasmOp_i64_load32_s: + case WasmOp_i64_load32_u: + case WasmOp_i32_store: + case WasmOp_i64_store: + case WasmOp_f32_store: + case WasmOp_f64_store: + case WasmOp_i32_store8: + case WasmOp_i32_store16: + case WasmOp_i64_store8: + case WasmOp_i64_store16: + case WasmOp_i64_store32: + { + uint32_t alignment = read32_uleb128(mod_ptr, code_i); + uint32_t offset = read32_uleb128(mod_ptr, code_i); + (void)alignment; + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm; + opcodes[pc->opcode + 1] = opcode; + pc->opcode += 2; + operands[pc->operand] = offset; + pc->operand += 1; + } + } + break; + + case WasmOp_memory_size: + case WasmOp_memory_grow: + { + if (mod_ptr[*code_i] != 0) panic("unexpected memory index"); + *code_i += 1; + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm; + opcodes[pc->opcode + 1] = opcode; + pc->opcode += 2; + } + } + break; + + case WasmOp_i32_const: + { + uint32_t x = read32_ileb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_const_32; + pc->opcode += 1; + operands[pc->operand] = x; + pc->operand += 1; + } + } + break; + + case WasmOp_i64_const: + { + uint64_t x = read64_ileb128(mod_ptr, code_i); + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_const_64; + pc->opcode += 1; + operands[pc->operand + 0] = x & UINT32_MAX; + operands[pc->operand + 1] = (x >> 32) & UINT32_MAX; + pc->operand += 2; + } + } + break; + + case WasmOp_f32_const: + { + uint32_t x; + memcpy(&x, mod_ptr + *code_i, 4); + *code_i += 4; + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_const_32; + pc->opcode += 1; + operands[pc->operand] = x; + pc->operand += 1; + } + } + break; + + case WasmOp_f64_const: + { + uint64_t x; + memcpy(&x, mod_ptr + *code_i, 8); + *code_i += 8; + if (unreachable_depth == 0) { + opcodes[pc->opcode] = Op_const_64; + pc->opcode += 1; + operands[pc->operand + 0] = x & UINT32_MAX; + operands[pc->operand + 1] = (x >> 32) & UINT32_MAX; + pc->operand += 2; + } + } + break; + + case WasmOp_i32_add: + opcodes[pc->opcode] = Op_add_32; + pc->opcode += 1; + break; + + case WasmOp_i32_and: + opcodes[pc->opcode] = Op_and_32; + pc->opcode += 1; + break; + + default: + opcodes[pc->opcode + 0] = Op_wasm; + opcodes[pc->opcode + 1] = opcode; + pc->opcode += 2; + break; + + case WasmOp_prefixed: + switch (prefixed_opcode) { + case WasmPrefixedOp_i32_trunc_sat_f32_s: + case WasmPrefixedOp_i32_trunc_sat_f32_u: + case WasmPrefixedOp_i32_trunc_sat_f64_s: + case WasmPrefixedOp_i32_trunc_sat_f64_u: + case WasmPrefixedOp_i64_trunc_sat_f32_s: + case WasmPrefixedOp_i64_trunc_sat_f32_u: + case WasmPrefixedOp_i64_trunc_sat_f64_s: + case WasmPrefixedOp_i64_trunc_sat_f64_u: + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm_prefixed; + opcodes[pc->opcode + 1] = prefixed_opcode; + pc->opcode += 2; + } + break; + + case WasmPrefixedOp_memory_copy: + if (mod_ptr[*code_i + 0] != 0 || mod_ptr[*code_i + 1] != 0) + panic("unexpected memory index"); + *code_i += 2; + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm_prefixed; + opcodes[pc->opcode + 1] = prefixed_opcode; + pc->opcode += 2; + } + break; + + case WasmPrefixedOp_memory_fill: + if (mod_ptr[*code_i] != 0) panic("unexpected memory index"); + *code_i += 1; + if (unreachable_depth == 0) { + opcodes[pc->opcode + 0] = Op_wasm_prefixed; + opcodes[pc->opcode + 1] = prefixed_opcode; + pc->opcode += 2; + } + break; + + default: panic("unreachable"); + } + break; + } + + switch (opcode) { + case WasmOp_unreachable: + case WasmOp_return: + case WasmOp_br: + case WasmOp_br_table: + if (unreachable_depth == 0) unreachable_depth = 1; + break; + + default: + break; + } + } +} + +static void vm_push_u32(struct VirtualMachine *vm, uint32_t value) { + vm->stack[vm->stack_top] = value; + vm->stack_top += 1; +} + +static void vm_push_i32(struct VirtualMachine *vm, int32_t value) { + return vm_push_u32(vm, value); +} + +static void vm_push_u64(struct VirtualMachine *vm, uint64_t value) { + vm->stack[vm->stack_top] = value; + vm->stack_top += 1; +} + +static void vm_push_i64(struct VirtualMachine *vm, int64_t value) { + return vm_push_u64(vm, value); +} + +static void vm_push_f32(struct VirtualMachine *vm, float value) { + uint32_t integer; + memcpy(&integer, &value, 4); + return vm_push_u32(vm, integer); +} + +static void vm_push_f64(struct VirtualMachine *vm, double value) { + uint64_t integer; + memcpy(&integer, &value, 8); + return vm_push_u64(vm, integer); +} + +static uint32_t vm_pop_u32(struct VirtualMachine *vm) { + vm->stack_top -= 1; + return vm->stack[vm->stack_top]; +} + +static int32_t vm_pop_i32(struct VirtualMachine *vm) { + return vm_pop_u32(vm); +} + +static uint64_t vm_pop_u64(struct VirtualMachine *vm) { + vm->stack_top -= 1; + return vm->stack[vm->stack_top]; +} + +static int64_t vm_pop_i64(struct VirtualMachine *vm) { + return vm_pop_u64(vm); +} + +static float vm_pop_f32(struct VirtualMachine *vm) { + uint32_t integer = vm_pop_u32(vm); + float result; + memcpy(&result, &integer, 4); + return result; +} + +static double vm_pop_f64(struct VirtualMachine *vm) { + uint32_t integer = vm_pop_u64(vm); + double result; + memcpy(&result, &integer, 8); + return result; +} + +static void vm_callImport(struct VirtualMachine *vm, struct Import import) { + switch (import.mod) { + case ImpMod_wasi_snapshot_preview1: switch (import.name) { + case ImpName_fd_prestat_get: + { + uint32_t buf = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_prestat_get(vm, fd, buf)); + } + break; + case ImpName_fd_prestat_dir_name: + { + uint32_t path_len = vm_pop_u32(vm); + uint32_t path = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_prestat_dir_name(vm, fd, path, path_len)); + } + break; + case ImpName_fd_close: + { + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_close(vm, fd)); + } + break; + case ImpName_fd_read: + { + uint32_t nread = vm_pop_u32(vm); + uint32_t iovs_len = vm_pop_u32(vm); + uint32_t iovs = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_read(vm, fd, iovs, iovs_len, nread)); + } + break; + case ImpName_fd_filestat_get: + { + uint32_t buf = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_filestat_get(vm, fd, buf)); + } + break; + case ImpName_fd_filestat_set_size: + { + uint64_t size = vm_pop_u64(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_filestat_set_size(vm, fd, size)); + } + break; + case ImpName_fd_filestat_set_times: + { + panic("unexpected call to fd_filestat_set_times"); + } + break; + case ImpName_fd_fdstat_get: + { + uint32_t buf = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_fdstat_get(vm, fd, buf)); + } + break; + case ImpName_fd_readdir: + { + panic("TODO implement fd_readdir"); + } + break; + case ImpName_fd_write: + { + uint32_t nwritten = vm_pop_u32(vm); + uint32_t iovs_len = vm_pop_u32(vm); + uint32_t iovs = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_write(vm, fd, iovs, iovs_len, nwritten)); + } + break; + case ImpName_fd_pwrite: + { + uint32_t nwritten = vm_pop_u32(vm); + uint64_t offset = vm_pop_u64(vm); + uint32_t iovs_len = vm_pop_u32(vm); + uint32_t iovs = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_fd_pwrite(vm, fd, iovs, iovs_len, offset, nwritten)); + } + break; + case ImpName_proc_exit: + { + uint32_t code = vm_pop_u32(vm); + exit(code); + } + break; + case ImpName_args_sizes_get: + { + uint32_t argv_buf_size = vm_pop_u32(vm); + uint32_t argc = vm_pop_u32(vm); + vm_push_u32(vm, wasi_args_sizes_get(vm, argc, argv_buf_size)); + } + break; + case ImpName_args_get: + { + uint32_t argv_buf = vm_pop_u32(vm); + uint32_t argv = vm_pop_u32(vm); + vm_push_u32(vm, wasi_args_get(vm, argv, argv_buf)); + } + break; + case ImpName_random_get: + { + uint32_t buf_len = vm_pop_u32(vm); + uint32_t buf = vm_pop_u32(vm); + vm_push_u32(vm, wasi_random_get(vm, buf, buf_len)); + } + break; + case ImpName_environ_sizes_get: + { + panic("unexpected call to environ_sizes_get"); + } + break; + case ImpName_environ_get: + { + panic("unexpected call to environ_get"); + } + break; + case ImpName_path_filestat_get: + { + uint32_t buf = vm_pop_u32(vm); + uint32_t path_len = vm_pop_u32(vm); + uint32_t path = vm_pop_u32(vm); + uint32_t flags = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_path_filestat_get(vm, fd, flags, path, path_len, buf)); + } + break; + case ImpName_path_create_directory: + { + uint32_t path_len = vm_pop_u32(vm); + uint32_t path = vm_pop_u32(vm); + int32_t fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_path_create_directory(vm, fd, path, path_len)); + } + break; + case ImpName_path_rename: + { + uint32_t new_path_len = vm_pop_u32(vm); + uint32_t new_path = vm_pop_u32(vm); + int32_t new_fd = vm_pop_i32(vm); + uint32_t old_path_len = vm_pop_u32(vm); + uint32_t old_path = vm_pop_u32(vm); + int32_t old_fd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_path_rename( + vm, + old_fd, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len + )); + } + break; + case ImpName_path_open: + { + uint32_t fd = vm_pop_u32(vm); + uint32_t fs_flags = vm_pop_u32(vm); + uint64_t fs_rights_inheriting = vm_pop_u64(vm); + uint64_t fs_rights_base = vm_pop_u64(vm); + uint32_t oflags = vm_pop_u32(vm); + uint32_t path_len = vm_pop_u32(vm); + uint32_t path = vm_pop_u32(vm); + uint32_t dirflags = vm_pop_u32(vm); + int32_t dirfd = vm_pop_i32(vm); + vm_push_u32(vm, wasi_path_open( + vm, + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + )); + } + break; + case ImpName_path_remove_directory: + { + panic("unexpected call to path_remove_directory"); + } + break; + case ImpName_path_unlink_file: + { + panic("unexpected call to path_unlink_file"); + } + break; + case ImpName_clock_time_get: + { + uint32_t timestamp = vm_pop_u32(vm); + uint64_t precision = vm_pop_u64(vm); + uint32_t clock_id = vm_pop_u32(vm); + vm_push_u32(vm, wasi_clock_time_get(vm, clock_id, precision, timestamp)); + } + break; + case ImpName_fd_pread: + { + panic("unexpected call to fd_pread"); + } + break; + case ImpName_debug: + { + uint64_t number = vm_pop_u64(vm); + uint32_t text = vm_pop_u32(vm); + wasi_debug(vm, text, number); + } + break; + case ImpName_debug_slice: + { + uint32_t len = vm_pop_u32(vm); + uint32_t ptr = vm_pop_u32(vm); + wasi_debug_slice(vm, ptr, len); + } + break; + } + break; + } +} + +static void vm_call(struct VirtualMachine *vm, uint32_t fn_id) { + if (fn_id < vm->imports_len) { + struct Import imp = vm->imports[fn_id]; + return vm_callImport(vm, imp); + } + uint32_t fn_idx = fn_id - vm->imports_len; + struct Function *func = &vm->functions[fn_idx]; + + // Push zeroed locals to stack + memset(&vm->stack[vm->stack_top], 0, func->locals_count * sizeof(uint64_t)); + vm->stack_top += func->locals_count; + + vm_push_u32(vm, vm->pc.opcode); + vm_push_u32(vm, vm->pc.operand); + + vm->pc = func->entry_pc; +} + +static void vm_br_void(struct VirtualMachine *vm) { + uint32_t stack_adjust = vm->operands[vm->pc.operand]; + + vm->stack_top -= stack_adjust; + + vm->pc.opcode = vm->operands[vm->pc.operand + 1]; + vm->pc.operand = vm->operands[vm->pc.operand + 2]; +} + +static void vm_br_u32(struct VirtualMachine *vm) { + uint32_t stack_adjust = vm->operands[vm->pc.operand]; + + uint32_t result = vm_pop_u32(vm); + vm->stack_top -= stack_adjust; + vm_push_u32(vm, result); + + vm->pc.opcode = vm->operands[vm->pc.operand + 1]; + vm->pc.operand = vm->operands[vm->pc.operand + 2]; +} + +static void vm_br_u64(struct VirtualMachine *vm) { + uint32_t stack_adjust = vm->operands[vm->pc.operand]; + + uint64_t result = vm_pop_u64(vm); + vm->stack_top -= stack_adjust; + vm_push_u64(vm, result); + + vm->pc.opcode = vm->operands[vm->pc.operand + 1]; + vm->pc.operand = vm->operands[vm->pc.operand + 2]; +} + +static void vm_return_void(struct VirtualMachine *vm) { + uint32_t ret_pc_offset = vm->operands[vm->pc.operand + 0]; + uint32_t stack_adjust = vm->operands[vm->pc.operand + 1]; + + vm->pc.opcode = vm->stack[vm->stack_top - ret_pc_offset]; + vm->pc.operand = vm->stack[vm->stack_top - ret_pc_offset + 1]; + + vm->stack_top -= stack_adjust; +} + +static void vm_return_u32(struct VirtualMachine *vm) { + uint32_t ret_pc_offset = vm->operands[vm->pc.operand + 0]; + uint32_t stack_adjust = vm->operands[vm->pc.operand + 1]; + + vm->pc.opcode = vm->stack[vm->stack_top - ret_pc_offset]; + vm->pc.operand = vm->stack[vm->stack_top - ret_pc_offset + 1]; + + uint32_t result = vm_pop_u32(vm); + vm->stack_top -= stack_adjust; + vm_push_u32(vm, result); +} + +static void vm_return_u64(struct VirtualMachine *vm) { + uint32_t ret_pc_offset = vm->operands[vm->pc.operand + 0]; + uint32_t stack_adjust = vm->operands[vm->pc.operand + 1]; + + vm->pc.opcode = vm->stack[vm->stack_top - ret_pc_offset]; + vm->pc.operand = vm->stack[vm->stack_top - ret_pc_offset + 1]; + + uint64_t result = vm_pop_u64(vm); + vm->stack_top -= stack_adjust; + vm_push_u64(vm, result); +} + +static void vm_run(struct VirtualMachine *vm) { + uint8_t *opcodes = vm->opcodes; + uint32_t *operands = vm->operands; + struct ProgramCounter *pc = &vm->pc; + for (;;) { + enum Op op = opcodes[pc->opcode]; + pc->opcode += 1; + switch (op) { + case Op_unreachable: + panic("unreachable reached"); + + case Op_br_void: + vm_br_void(vm); + break; + + case Op_br_32: + vm_br_u32(vm); + break; + + case Op_br_64: + vm_br_u64(vm); + break; + + case Op_br_if_nez_void: + if (vm_pop_u32(vm) != 0) { + vm_br_void(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_if_nez_32: + if (vm_pop_u32(vm) != 0) { + vm_br_u32(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_if_nez_64: + if (vm_pop_u32(vm) != 0) { + vm_br_u64(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_if_eqz_void: + if (vm_pop_u32(vm) == 0) { + vm_br_void(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_if_eqz_32: + if (vm_pop_u32(vm) == 0) { + vm_br_u32(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_if_eqz_64: + if (vm_pop_u32(vm) == 0) { + vm_br_u64(vm); + } else { + pc->operand += 3; + } + break; + + case Op_br_table_void: + { + uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); + pc->operand += 1 + index * 3; + vm_br_void(vm); + } + break; + + case Op_br_table_32: + { + uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); + pc->operand += 1 + index * 3; + vm_br_u32(vm); + } + break; + + case Op_br_table_64: + { + uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); + pc->operand += 1 + index * 3; + vm_br_u64(vm); + } + break; + + case Op_return_void: + vm_return_void(vm); + break; + + case Op_return_32: + vm_return_u32(vm); + break; + + case Op_return_64: + vm_return_u64(vm); + break; + + case Op_call: + { + uint32_t fn_id = operands[pc->operand]; + pc->operand += 1; + vm_call(vm, fn_id); + } + break; + + case Op_drop_32: + case Op_drop_64: + vm->stack_top -= 1; + break; + + case Op_select_32: + { + uint32_t c = vm_pop_u32(vm); + uint32_t b = vm_pop_u32(vm); + uint32_t a = vm_pop_u32(vm); + uint32_t result = (c != 0) ? a : b; + vm_push_u32(vm, result); + } + break; + + case Op_select_64: + { + uint32_t c = vm_pop_u32(vm); + uint64_t b = vm_pop_u64(vm); + uint64_t a = vm_pop_u64(vm); + uint64_t result = (c != 0) ? a : b; + vm_push_u64(vm, result); + } + break; + + case Op_local_get_32: + { + uint64_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; + pc->operand += 1; + vm_push_u32(vm, *local); + } + break; + + case Op_local_get_64: + { + uint64_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; + pc->operand += 1; + vm_push_u64(vm, *local); + } + break; + + case Op_local_set_32: + { + uint64_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; + pc->operand += 1; + *local = vm_pop_u32(vm); + } + break; + + case Op_local_set_64: + { + uint64_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; + pc->operand += 1; + *local = vm_pop_u64(vm); + } + break; + + case Op_local_tee_32: + case Op_local_tee_64: + { + uint64_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; + pc->operand += 1; + *local = vm->stack[vm->stack_top - 1]; + } + break; + + case Op_global_get_0_32: + vm_push_u32(vm, vm->globals[0]); + break; + + case Op_global_get_32: + { + uint32_t idx = operands[pc->operand]; + pc->operand += 1; + vm_push_u32(vm, vm->globals[idx]); + } + break; + + case Op_global_set_0_32: + vm->globals[0] = vm_pop_u32(vm); + break; + + case Op_global_set_32: + { + uint32_t idx = operands[pc->operand]; + pc->operand += 1; + vm->globals[idx] = vm_pop_u32(vm); + } + break; + + case Op_const_32: + { + uint32_t x = operands[pc->operand]; + pc->operand += 1; + vm_push_i32(vm, x); + } + break; + + case Op_const_64: + { + uint64_t x = ((uint64_t)operands[pc->operand]) | + (((uint64_t)operands[pc->operand + 1]) << 32); + pc->operand += 2; + vm_push_i64(vm, x); + } + break; + + case Op_add_32: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs + rhs); + } + break; + + case Op_and_32: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs & rhs); + } + break; + + case Op_wasm: + { + enum WasmOp wasm_op = opcodes[pc->opcode]; + pc->opcode += 1; + switch (wasm_op) { + case WasmOp_unreachable: + case WasmOp_nop: + case WasmOp_block: + case WasmOp_loop: + case WasmOp_if: + case WasmOp_else: + case WasmOp_end: + case WasmOp_br: + case WasmOp_br_if: + case WasmOp_br_table: + case WasmOp_return: + case WasmOp_call: + case WasmOp_drop: + case WasmOp_select: + case WasmOp_local_get: + case WasmOp_local_set: + case WasmOp_local_tee: + case WasmOp_global_get: + case WasmOp_global_set: + case WasmOp_i32_const: + case WasmOp_i64_const: + case WasmOp_f32_const: + case WasmOp_f64_const: + case WasmOp_i32_add: + case WasmOp_i32_and: + case WasmOp_i32_reinterpret_f32: + case WasmOp_i64_reinterpret_f64: + case WasmOp_f32_reinterpret_i32: + case WasmOp_f64_reinterpret_i64: + case WasmOp_prefixed: + panic("not produced by decodeCode"); + break; + + case WasmOp_call_indirect: + { + uint32_t fn_id = vm->table[vm_pop_u32(vm)]; + vm_call(vm, fn_id); + } + break; + case WasmOp_i32_load: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_u32(vm, read_u32_le(vm->memory + offset)); + } + break; + case WasmOp_i64_load: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_u64(vm, read_u64_le(vm->memory + offset)); + } + break; + case WasmOp_f32_load: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + uint32_t integer = read_u32_le(vm->memory + offset); + vm_push_u32(vm, integer); + } + break; + case WasmOp_f64_load: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + uint64_t integer = read_u64_le(vm->memory + offset); + vm_push_u64(vm, integer); + } + break; + case WasmOp_i32_load8_s: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_i32(vm, (int8_t)vm->memory[offset]); + } + break; + case WasmOp_i32_load8_u: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_u32(vm, vm->memory[offset]); + } + break; + case WasmOp_i32_load16_s: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + int16_t integer = read_i16_le(vm->memory + offset); + vm_push_i32(vm, integer); + } + break; + case WasmOp_i32_load16_u: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + uint16_t integer = read_u16_le(vm->memory + offset); + vm_push_u32(vm, integer); + } + break; + case WasmOp_i64_load8_s: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_i64(vm, (int8_t)vm->memory[offset]); + } + break; + case WasmOp_i64_load8_u: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm_push_u64(vm, vm->memory[offset]); + } + break; + case WasmOp_i64_load16_s: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + int16_t integer = read_i16_le(vm->memory + offset); + vm_push_i64(vm, integer); + } + break; + case WasmOp_i64_load16_u: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + uint16_t integer = read_u16_le(vm->memory + offset); + vm_push_u64(vm, integer); + } + break; + case WasmOp_i64_load32_s: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + int32_t integer = read_i32_le(vm->memory + offset); + vm_push_i64(vm, integer); + } + break; + case WasmOp_i64_load32_u: + { + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + uint32_t integer = read_u32_le(vm->memory + offset); + vm_push_u64(vm, integer); + } + break; + case WasmOp_i32_store: + { + uint32_t operand = vm_pop_u32(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u32_le(vm->memory + offset, operand); + } + break; + case WasmOp_i64_store: + { + uint64_t operand = vm_pop_u64(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u64_le(vm->memory + offset, operand); + } + break; + case WasmOp_f32_store: + { + uint32_t integer = vm_pop_u32(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u32_le(vm->memory + offset, integer); + } + break; + case WasmOp_f64_store: + { + uint64_t integer = vm_pop_u64(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u64_le(vm->memory + offset, integer); + } + break; + case WasmOp_i32_store8: + { + uint8_t small = vm_pop_u32(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm->memory[offset] = small; + } + break; + case WasmOp_i32_store16: + { + uint16_t small = vm_pop_u32(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u16_le(vm->memory + offset, small); + } + break; + case WasmOp_i64_store8: + { + uint8_t operand = vm_pop_u64(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + vm->memory[offset] = operand; + } + break; + case WasmOp_i64_store16: + { + uint16_t small = vm_pop_u64(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u16_le(vm->memory + offset, small); + } + break; + case WasmOp_i64_store32: + { + uint32_t small = vm_pop_u64(vm); + uint32_t offset = operands[pc->operand] + vm_pop_u32(vm); + pc->operand += 1; + write_u32_le(vm->memory + offset, small); + } + break; + case WasmOp_memory_size: + { + uint32_t page_count = vm->memory_len / wasm_page_size; + vm_push_u32(vm, page_count); + } + break; + case WasmOp_memory_grow: + { + uint32_t page_count = vm_pop_u32(vm); + uint32_t old_page_count = vm->memory_len / wasm_page_size; + uint32_t new_len = vm->memory_len + page_count * wasm_page_size; + if (new_len > vm->memory_len) { + vm_push_i32(vm, -1); + } else { + vm->memory_len = new_len; + vm_push_u32(vm, old_page_count); + } + } + break; + case WasmOp_i32_eqz: + { + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs == 0); + } + break; + case WasmOp_i32_eq: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs == rhs); + } + break; + case WasmOp_i32_ne: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs != rhs); + } + break; + case WasmOp_i32_lt_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_u32(vm, lhs < rhs); + } + break; + case WasmOp_i32_lt_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs < rhs); + } + break; + case WasmOp_i32_gt_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_i32_gt_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_i32_le_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_i32_le_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_i32_ge_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + case WasmOp_i32_ge_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + case WasmOp_i64_eqz: + { + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs == 0); + } + break; + case WasmOp_i64_eq: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs == rhs); + } + break; + case WasmOp_i64_ne: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs != rhs); + } + break; + case WasmOp_i64_lt_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_u32(vm, lhs < rhs); + } + break; + case WasmOp_i64_lt_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs < rhs); + } + break; + case WasmOp_i64_gt_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_i64_gt_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_i64_le_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_i64_le_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_i64_ge_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + case WasmOp_i64_ge_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + case WasmOp_f32_eq: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs == rhs); + } + break; + case WasmOp_f32_ne: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs != rhs); + } + break; + case WasmOp_f32_lt: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs < rhs); + } + break; + case WasmOp_f32_gt: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_f32_le: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_f32_ge: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + case WasmOp_f64_eq: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs == rhs); + } + break; + case WasmOp_f64_ne: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs != rhs); + } + break; + case WasmOp_f64_lt: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_f64_gt: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs > rhs); + } + break; + case WasmOp_f64_le: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs <= rhs); + } + break; + case WasmOp_f64_ge: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_u32(vm, lhs >= rhs); + } + break; + + case WasmOp_i32_clz: + { + uint32_t operand = vm_pop_u32(vm); + uint32_t result = (operand == 0) ? 32 : __builtin_clz(operand); + vm_push_u32(vm, result); + } + break; + case WasmOp_i32_ctz: + { + uint32_t operand = vm_pop_u32(vm); + uint32_t result = (operand == 0) ? 32 : __builtin_ctz(operand); + vm_push_u32(vm, result); + } + break; + case WasmOp_i32_popcnt: + { + uint32_t operand = vm_pop_u32(vm); + uint32_t result = __builtin_popcount(operand); + vm_push_u32(vm, result); + } + break; + case WasmOp_i32_sub: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs - rhs); + } + break; + case WasmOp_i32_mul: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs * rhs); + } + break; + case WasmOp_i32_div_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_i32(vm, lhs / rhs); + } + break; + case WasmOp_i32_div_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs / rhs); + } + break; + case WasmOp_i32_rem_s: + { + int32_t rhs = vm_pop_i32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_i32(vm, lhs % rhs); + } + break; + case WasmOp_i32_rem_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs % rhs); + } + break; + case WasmOp_i32_or: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs | rhs); + } + break; + case WasmOp_i32_xor: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs ^ rhs); + } + break; + case WasmOp_i32_shl: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs << rhs); + } + break; + case WasmOp_i32_shr_s: + { + uint32_t rhs = vm_pop_u32(vm); + int32_t lhs = vm_pop_i32(vm); + vm_push_i32(vm, lhs >> rhs); + } + break; + case WasmOp_i32_shr_u: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, lhs >> rhs); + } + break; + case WasmOp_i32_rotl: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, rotl32(lhs, rhs)); + } + break; + case WasmOp_i32_rotr: + { + uint32_t rhs = vm_pop_u32(vm); + uint32_t lhs = vm_pop_u32(vm); + vm_push_u32(vm, rotr32(lhs, rhs )); + } + break; + + case WasmOp_i64_clz: + { + uint64_t operand = vm_pop_u64(vm); + uint64_t result = (operand == 0) ? 64 : __builtin_clzll(operand); + vm_push_u64(vm, result); + } + break; + case WasmOp_i64_ctz: + { + uint64_t operand = vm_pop_u64(vm); + uint64_t result = (operand == 0) ? 64 : __builtin_ctzll(operand); + vm_push_u64(vm, result); + } + break; + case WasmOp_i64_popcnt: + { + uint64_t operand = vm_pop_u64(vm); + uint64_t result = __builtin_popcountll(operand); + vm_push_u64(vm, result); + } + break; + case WasmOp_i64_add: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs + rhs); + } + break; + case WasmOp_i64_sub: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs - rhs); + } + break; + case WasmOp_i64_mul: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs * rhs); + } + break; + case WasmOp_i64_div_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_i64(vm, lhs / rhs); + } + break; + case WasmOp_i64_div_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs / rhs); + } + break; + case WasmOp_i64_rem_s: + { + int64_t rhs = vm_pop_i64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_i64(vm, lhs % rhs); + } + break; + case WasmOp_i64_rem_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs % rhs); + } + break; + case WasmOp_i64_and: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs & rhs); + } + break; + case WasmOp_i64_or: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs | rhs); + } + break; + case WasmOp_i64_xor: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs ^ rhs); + } + break; + case WasmOp_i64_shl: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs << rhs); + } + break; + case WasmOp_i64_shr_s: + { + uint64_t rhs = vm_pop_u64(vm); + int64_t lhs = vm_pop_i64(vm); + vm_push_i64(vm, lhs >> rhs); + } + break; + case WasmOp_i64_shr_u: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, lhs >> rhs); + } + break; + case WasmOp_i64_rotl: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, rotl64(lhs, rhs )); + } + break; + case WasmOp_i64_rotr: + { + uint64_t rhs = vm_pop_u64(vm); + uint64_t lhs = vm_pop_u64(vm); + vm_push_u64(vm, rotr64(lhs, rhs )); + } + break; + + case WasmOp_f32_abs: + { + vm_push_f32(vm, fabsf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_neg: + { + vm_push_f32(vm, -vm_pop_f32(vm)); + } + break; + case WasmOp_f32_ceil: + { + vm_push_f32(vm, ceilf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_floor: + { + vm_push_f32(vm, floorf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_trunc: + { + vm_push_f32(vm, truncf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_nearest: + { + vm_push_f32(vm, roundf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_sqrt: + { + vm_push_f32(vm, sqrtf(vm_pop_f32(vm))); + } + break; + case WasmOp_f32_add: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, lhs + rhs); + } + break; + case WasmOp_f32_sub: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, lhs - rhs); + } + break; + case WasmOp_f32_mul: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, lhs * rhs); + } + break; + case WasmOp_f32_div: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, lhs / rhs); + } + break; + case WasmOp_f32_min: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, (lhs < rhs) ? lhs : rhs); + } + break; + case WasmOp_f32_max: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, (lhs > rhs) ? lhs : rhs); + } + break; + case WasmOp_f32_copysign: + { + float rhs = vm_pop_f32(vm); + float lhs = vm_pop_f32(vm); + vm_push_f32(vm, copysignf(lhs, rhs)); + } + break; + case WasmOp_f64_abs: + { + vm_push_f64(vm, fabs(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_neg: + { + vm_push_f64(vm, -vm_pop_f64(vm)); + } + break; + case WasmOp_f64_ceil: + { + vm_push_f64(vm, ceil(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_floor: + { + vm_push_f64(vm, floor(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_trunc: + { + vm_push_f64(vm, trunc(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_nearest: + { + vm_push_f64(vm, round(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_sqrt: + { + vm_push_f64(vm, sqrt(vm_pop_f64(vm))); + } + break; + case WasmOp_f64_add: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, lhs + rhs); + } + break; + case WasmOp_f64_sub: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, lhs - rhs); + } + break; + case WasmOp_f64_mul: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, lhs * rhs); + } + break; + case WasmOp_f64_div: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, lhs / rhs); + } + break; + case WasmOp_f64_min: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, (lhs < rhs) ? lhs : rhs); + } + break; + case WasmOp_f64_max: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, (lhs > rhs) ? lhs : rhs); + } + break; + case WasmOp_f64_copysign: + { + double rhs = vm_pop_f64(vm); + double lhs = vm_pop_f64(vm); + vm_push_f64(vm, copysign(lhs, rhs)); + } + break; + + case WasmOp_i32_wrap_i64: + { + uint64_t operand = vm_pop_u64(vm); + vm_push_u32(vm, operand); + } + break; + case WasmOp_i32_trunc_f32_s: + { + float operand = vm_pop_f32(vm); + vm_push_i32(vm, truncf(operand)); + } + break; + case WasmOp_i32_trunc_f32_u: + { + float operand = vm_pop_f32(vm); + vm_push_u32(vm, truncf(operand)); + } + break; + case WasmOp_i32_trunc_f64_s: + { + double operand = vm_pop_f64(vm); + vm_push_i32(vm, trunc(operand)); + } + break; + case WasmOp_i32_trunc_f64_u: + { + double operand = vm_pop_f64(vm); + vm_push_u32(vm, trunc(operand)); + } + break; + case WasmOp_i64_extend_i32_s: + { + int32_t operand = vm_pop_i32(vm); + vm_push_i64(vm, operand); + } + break; + case WasmOp_i64_extend_i32_u: + { + uint64_t operand = vm_pop_u64(vm); + vm_push_u64(vm, operand); + } + break; + case WasmOp_i64_trunc_f32_s: + { + float operand = vm_pop_f32(vm); + vm_push_i64(vm, truncf(operand)); + } + break; + case WasmOp_i64_trunc_f32_u: + { + float operand = vm_pop_f32(vm); + vm_push_u64(vm, truncf(operand)); + } + break; + case WasmOp_i64_trunc_f64_s: + { + double operand = vm_pop_f64(vm); + vm_push_i64(vm, trunc(operand)); + } + break; + case WasmOp_i64_trunc_f64_u: + { + double operand = vm_pop_f64(vm); + vm_push_u64(vm, trunc(operand)); + } + break; + case WasmOp_f32_convert_i32_s: + { + vm_push_f32(vm, vm_pop_i32(vm)); + } + break; + case WasmOp_f32_convert_i32_u: + { + vm_push_f32(vm, vm_pop_u32(vm)); + } + break; + case WasmOp_f32_convert_i64_s: + { + vm_push_f32(vm, vm_pop_i64(vm)); + } + break; + case WasmOp_f32_convert_i64_u: + { + vm_push_f32(vm, vm_pop_u64(vm)); + } + break; + case WasmOp_f32_demote_f64: + { + vm_push_f32(vm, vm_pop_f64(vm)); + } + break; + case WasmOp_f64_convert_i32_s: + { + vm_push_f64(vm, vm_pop_i32(vm)); + } + break; + case WasmOp_f64_convert_i32_u: + { + vm_push_f64(vm, vm_pop_u32(vm)); + } + break; + case WasmOp_f64_convert_i64_s: + { + vm_push_f64(vm, vm_pop_i64(vm)); + } + break; + case WasmOp_f64_convert_i64_u: + { + vm_push_f64(vm, vm_pop_u64(vm)); + } + break; + case WasmOp_f64_promote_f32: + { + vm_push_f64(vm, vm_pop_f32(vm)); + } + break; + + case WasmOp_i32_extend8_s: + { + int8_t operand = vm_pop_i32(vm); + vm_push_i32(vm, operand); + } + break; + case WasmOp_i32_extend16_s: + { + int16_t operand = vm_pop_i32(vm); + vm_push_i32(vm, operand); + } + break; + case WasmOp_i64_extend8_s: + { + int8_t operand = vm_pop_i64(vm); + vm_push_i64(vm, operand); + } + break; + case WasmOp_i64_extend16_s: + { + int16_t operand = vm_pop_i64(vm); + vm_push_i64(vm, operand); + } + break; + case WasmOp_i64_extend32_s: + { + int32_t operand = vm_pop_i64(vm); + vm_push_i64(vm, operand); + } + break; + + default: + panic("unreachable"); + } + } + break; + + case Op_wasm_prefixed: + { + enum WasmPrefixedOp wasm_prefixed_op = opcodes[pc->opcode]; + pc->opcode += 1; + switch (wasm_prefixed_op) { + case WasmPrefixedOp_i32_trunc_sat_f32_s: + panic("unreachable"); + case WasmPrefixedOp_i32_trunc_sat_f32_u: + panic("unreachable"); + case WasmPrefixedOp_i32_trunc_sat_f64_s: + panic("unreachable"); + case WasmPrefixedOp_i32_trunc_sat_f64_u: + panic("unreachable"); + case WasmPrefixedOp_i64_trunc_sat_f32_s: + panic("unreachable"); + case WasmPrefixedOp_i64_trunc_sat_f32_u: + panic("unreachable"); + case WasmPrefixedOp_i64_trunc_sat_f64_s: + panic("unreachable"); + case WasmPrefixedOp_i64_trunc_sat_f64_u: + panic("unreachable"); + case WasmPrefixedOp_memory_init: + panic("unreachable"); + case WasmPrefixedOp_data_drop: + panic("unreachable"); + + case WasmPrefixedOp_memory_copy: + { + uint32_t n = vm_pop_u32(vm); + uint32_t src = vm_pop_u32(vm); + uint32_t dest = vm_pop_u32(vm); + assert(dest + n <= vm->memory_len); + assert(src + n <= vm->memory_len); + assert(src + n <= dest || dest + n <= src); // overlapping + memcpy(vm->memory + dest, vm->memory + src, n); + } + break; + + case WasmPrefixedOp_memory_fill: + { + uint32_t n = vm_pop_u32(vm); + uint8_t value = vm_pop_u32(vm); + uint32_t dest = vm_pop_u32(vm); + assert(dest + n <= vm->memory_len); + memset(vm->memory + dest, value, n); + } + break; + + case WasmPrefixedOp_table_init: panic("unreachable"); + case WasmPrefixedOp_elem_drop: panic("unreachable"); + case WasmPrefixedOp_table_copy: panic("unreachable"); + case WasmPrefixedOp_table_grow: panic("unreachable"); + case WasmPrefixedOp_table_size: panic("unreachable"); + case WasmPrefixedOp_table_fill: panic("unreachable"); + default: panic("unreachable"); + } + } + break; + + } + } +} + +int main(int argc, char **argv) { + char *memory = mmap( NULL, max_memory, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + + const char *zig_lib_dir_path = argv[1]; + const char *zig_cache_dir_path = argv[2]; + const size_t vm_argv_start = 3; + const char *wasm_file = argv[vm_argv_start]; + + const struct ByteSlice mod = read_file_alloc(wasm_file); + + int cwd = err_wrap("opening cwd", open(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH)); + mkdir(zig_cache_dir_path, 0666); + int cache_dir = err_wrap("opening cache dir", open(zig_cache_dir_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH)); + int zig_lib_dir = err_wrap("opening zig lib dir", open(zig_lib_dir_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH)); + + add_preopen(0, "stdin", STDIN_FILENO); + add_preopen(1, "stdout", STDOUT_FILENO); + add_preopen(2, "stderr", STDERR_FILENO); + add_preopen(3, ".", cwd); + add_preopen(4, "/cache", cache_dir); + add_preopen(5, "/lib", zig_lib_dir); + + uint32_t i = 0; + + if (mod.ptr[0] != 0 || mod.ptr[1] != 'a' || mod.ptr[2] != 's' || mod.ptr[3] != 'm') { + panic("bad magic"); + } + i += 4; + + uint32_t version = read_u32_le(mod.ptr + i); + i += 4; + if (version != 1) panic("bad wasm version"); + + uint32_t section_starts[13]; + memset(§ion_starts, 0, 4 * 13); + + while (i < mod.len) { + uint8_t section_id = mod.ptr[i]; + i += 1; + uint32_t section_len = read32_uleb128(mod.ptr, &i); + section_starts[section_id] = i; + i += section_len; + } + + // Map type indexes to offsets into the module. + struct TypeInfo *types; + { + i = section_starts[Section_type]; + uint32_t types_len = read32_uleb128(mod.ptr, &i); + types = arena_alloc(sizeof(struct TypeInfo) * types_len); + for (size_t type_i = 0; type_i < types_len; type_i += 1) { + struct TypeInfo *info = &types[type_i]; + if (mod.ptr[i] != 0x60) panic("bad type byte"); + i += 1; + + info->param_count = read32_uleb128(mod.ptr, &i); + info->param_types = 0; + for (uint32_t param_i = 0; param_i < info->param_count; param_i += 1) { + int64_t param_type = read64_ileb128(mod.ptr, &i); + switch (param_type) { + case -1: case -3: bs_unset(&info->param_types, param_i); break; + case -2: case -4: bs_set(&info->param_types, param_i); break; + default: panic("unexpected param type"); + } + } + + info->result_count = read32_uleb128(mod.ptr, &i); + info->result_types = 0; + for (uint32_t result_i = 0; result_i < info->result_count; result_i += 1) { + int64_t result_type = read64_ileb128(mod.ptr, &i); + switch (result_type) { + case -1: case -3: bs_unset(&info->result_types, result_i); break; + case -2: case -4: bs_set(&info->result_types, result_i); break; + default: panic("unexpected result type"); + } + } + } + } + + // Count the imported functions so we can correct function references. + struct Import *imports; + uint32_t imports_len; + { + i = section_starts[Section_import]; + imports_len = read32_uleb128(mod.ptr, &i); + imports = arena_alloc(sizeof(struct Import) * imports_len); + for (size_t imp_i = 0; imp_i < imports_len; imp_i += 1) { + struct Import *imp = &imports[imp_i]; + + struct ByteSlice mod_name = read_name(mod.ptr, &i); + if (mod_name.len == strlen("wasi_snapshot_preview1") && + memcmp(mod_name.ptr, "wasi_snapshot_preview1", mod_name.len) == 0) { + imp->mod = ImpMod_wasi_snapshot_preview1; + } else panic("unknown import module"); + + struct ByteSlice sym_name = read_name(mod.ptr, &i); + if (sym_name.len == strlen("args_get") && + memcmp(sym_name.ptr, "args_get", sym_name.len) == 0) { + imp->name = ImpName_args_get; + } else if (sym_name.len == strlen("args_sizes_get") && + memcmp(sym_name.ptr, "args_sizes_get", sym_name.len) == 0) { + imp->name = ImpName_args_sizes_get; + } else if (sym_name.len == strlen("clock_time_get") && + memcmp(sym_name.ptr, "clock_time_get", sym_name.len) == 0) { + imp->name = ImpName_clock_time_get; + } else if (sym_name.len == strlen("debug") && + memcmp(sym_name.ptr, "debug", sym_name.len) == 0) { + imp->name = ImpName_debug; + } else if (sym_name.len == strlen("debug_slice") && + memcmp(sym_name.ptr, "debug_slice", sym_name.len) == 0) { + imp->name = ImpName_debug_slice; + } else if (sym_name.len == strlen("environ_get") && + memcmp(sym_name.ptr, "environ_get", sym_name.len) == 0) { + imp->name = ImpName_environ_get; + } else if (sym_name.len == strlen("environ_sizes_get") && + memcmp(sym_name.ptr, "environ_sizes_get", sym_name.len) == 0) { + imp->name = ImpName_environ_sizes_get; + } else if (sym_name.len == strlen("fd_close") && + memcmp(sym_name.ptr, "fd_close", sym_name.len) == 0) { + imp->name = ImpName_fd_close; + } else if (sym_name.len == strlen("fd_fdstat_get") && + memcmp(sym_name.ptr, "fd_fdstat_get", sym_name.len) == 0) { + imp->name = ImpName_fd_fdstat_get; + } else if (sym_name.len == strlen("fd_filestat_get") && + memcmp(sym_name.ptr, "fd_filestat_get", sym_name.len) == 0) { + imp->name = ImpName_fd_filestat_get; + } else if (sym_name.len == strlen("fd_filestat_set_size") && + memcmp(sym_name.ptr, "fd_filestat_set_size", sym_name.len) == 0) { + imp->name = ImpName_fd_filestat_set_size; + } else if (sym_name.len == strlen("fd_filestat_set_times") && + memcmp(sym_name.ptr, "fd_filestat_set_times", sym_name.len) == 0) { + imp->name = ImpName_fd_filestat_set_times; + } else if (sym_name.len == strlen("fd_pread") && + memcmp(sym_name.ptr, "fd_pread", sym_name.len) == 0) { + imp->name = ImpName_fd_pread; + } else if (sym_name.len == strlen("fd_prestat_dir_name") && + memcmp(sym_name.ptr, "fd_prestat_dir_name", sym_name.len) == 0) { + imp->name = ImpName_fd_prestat_dir_name; + } else if (sym_name.len == strlen("fd_prestat_get") && + memcmp(sym_name.ptr, "fd_prestat_get", sym_name.len) == 0) { + imp->name = ImpName_fd_prestat_get; + } else if (sym_name.len == strlen("fd_pwrite") && + memcmp(sym_name.ptr, "fd_pwrite", sym_name.len) == 0) { + imp->name = ImpName_fd_pwrite; + } else if (sym_name.len == strlen("fd_read") && + memcmp(sym_name.ptr, "fd_read", sym_name.len) == 0) { + imp->name = ImpName_fd_read; + } else if (sym_name.len == strlen("fd_readdir") && + memcmp(sym_name.ptr, "fd_readdir", sym_name.len) == 0) { + imp->name = ImpName_fd_readdir; + } else if (sym_name.len == strlen("fd_write") && + memcmp(sym_name.ptr, "fd_write", sym_name.len) == 0) { + imp->name = ImpName_fd_write; + } else if (sym_name.len == strlen("path_create_directory") && + memcmp(sym_name.ptr, "path_create_directory", sym_name.len) == 0) { + imp->name = ImpName_path_create_directory; + } else if (sym_name.len == strlen("path_filestat_get") && + memcmp(sym_name.ptr, "path_filestat_get", sym_name.len) == 0) { + imp->name = ImpName_path_filestat_get; + } else if (sym_name.len == strlen("path_open") && + memcmp(sym_name.ptr, "path_open", sym_name.len) == 0) { + imp->name = ImpName_path_open; + } else if (sym_name.len == strlen("path_remove_directory") && + memcmp(sym_name.ptr, "path_remove_directory", sym_name.len) == 0) { + imp->name = ImpName_path_remove_directory; + } else if (sym_name.len == strlen("path_rename") && + memcmp(sym_name.ptr, "path_rename", sym_name.len) == 0) { + imp->name = ImpName_path_rename; + } else if (sym_name.len == strlen("path_unlink_file") && + memcmp(sym_name.ptr, "path_unlink_file", sym_name.len) == 0) { + imp->name = ImpName_path_unlink_file; + } else if (sym_name.len == strlen("proc_exit") && + memcmp(sym_name.ptr, "proc_exit", sym_name.len) == 0) { + imp->name = ImpName_proc_exit; + } else if (sym_name.len == strlen("random_get") && + memcmp(sym_name.ptr, "random_get", sym_name.len) == 0) { + imp->name = ImpName_random_get; + } else panic("unknown import name"); + + uint32_t desc = read32_uleb128(mod.ptr, &i); + if (desc != 0) panic("external kind not function"); + imp->type_idx = read32_uleb128(mod.ptr, &i); + } + } + + // Find _start in the exports + uint32_t start_fn_idx; + { + i = section_starts[Section_export]; + uint32_t count = read32_uleb128(mod.ptr, &i); + for (; count > 0; count -= 1) { + struct ByteSlice name = read_name(mod.ptr, &i); + uint32_t desc = read32_uleb128(mod.ptr, &i); + start_fn_idx = read32_uleb128(mod.ptr, &i); + if (desc == 0 && name.len == strlen("_start") && + memcmp(name.ptr, "_start", name.len) == 0) + { + break; + } + } + if (count == 0) panic("_start symbol not found"); + } + + // Map function indexes to offsets into the module and type index. + struct Function *functions; + uint32_t functions_len; + { + i = section_starts[Section_function]; + functions_len = read32_uleb128(mod.ptr, &i); + functions = arena_alloc(sizeof(struct Function) * functions_len); + for (size_t func_i = 0; func_i < functions_len; func_i += 1) { + struct Function *func = &functions[func_i]; + func->type_idx = read32_uleb128(mod.ptr, &i); + } + } + + // Allocate and initialize globals. + uint64_t *globals; + { + i = section_starts[Section_global]; + uint32_t globals_len = read32_uleb128(mod.ptr, &i); + globals = arena_alloc(sizeof(uint64_t) * globals_len); + for (size_t glob_i = 0; glob_i < globals_len; glob_i += 1) { + uint64_t *global = &globals[glob_i]; + uint32_t content_type = read32_uleb128(mod.ptr, &i); + uint32_t mutability = read32_uleb128(mod.ptr, &i); + if (mutability != 1) panic("expected mutable global"); + if (content_type != 0x7f) panic("unexpected content type"); + uint8_t opcode = mod.ptr[i]; + i += 1; + if (opcode != WasmOp_i32_const) panic("expected i32_const op"); + uint32_t init = read32_ileb128(mod.ptr, &i); + *global = (uint32_t)init; + } + } + + // Allocate and initialize memory. + uint32_t memory_len; + { + i = section_starts[Section_memory]; + uint32_t memories_len = read32_uleb128(mod.ptr, &i); + if (memories_len != 1) panic("unexpected memory count"); + uint32_t flags = read32_uleb128(mod.ptr, &i); + (void)flags; + memory_len = read32_uleb128(mod.ptr, &i) * wasm_page_size; + + i = section_starts[Section_data]; + uint32_t datas_count = read32_uleb128(mod.ptr, &i); + for (; datas_count > 0; datas_count -= 1) { + uint32_t mode = read32_uleb128(mod.ptr, &i); + if (mode != 0) panic("expected mode 0"); + enum WasmOp opcode = mod.ptr[i]; + i += 1; + if (opcode != WasmOp_i32_const) panic("expected opcode i32_const"); + uint32_t offset = read32_uleb128(mod.ptr, &i); + enum WasmOp end = mod.ptr[i]; + if (end != WasmOp_end) panic("expected end opcode"); + i += 1; + uint32_t bytes_len = read32_uleb128(mod.ptr, &i); + memcpy(memory + offset, mod.ptr + i, bytes_len); + i += bytes_len; + } + } + + uint32_t *table = NULL; + { + i = section_starts[Section_table]; + uint32_t table_count = read32_uleb128(mod.ptr, &i); + if (table_count > 1) { + panic("expected only one table section"); + } else if (table_count == 1) { + uint32_t element_type = read32_uleb128(mod.ptr, &i); + (void)element_type; + uint32_t has_max = read32_uleb128(mod.ptr, &i); + if (has_max != 1) panic("expected has_max==1"); + uint32_t initial = read32_uleb128(mod.ptr, &i); + (void)initial; + uint32_t maximum = read32_uleb128(mod.ptr, &i); + + i = section_starts[Section_element]; + uint32_t element_section_count = read32_uleb128(mod.ptr, &i); + if (element_section_count != 1) panic("expected one element section"); + uint32_t flags = read32_uleb128(mod.ptr, &i); + (void)flags; + enum WasmOp opcode = mod.ptr[i]; + i += 1; + if (opcode != WasmOp_i32_const) panic("expected op i32_const"); + uint32_t offset = read32_uleb128(mod.ptr, &i); + enum WasmOp end = mod.ptr[i]; + if (end != WasmOp_end) panic("expected op end"); + i += 1; + uint32_t elem_count = read32_uleb128(mod.ptr, &i); + + table = arena_alloc(sizeof(uint32_t) * maximum); + memset(table, 0, maximum); + + for (uint32_t elem_i = 0; elem_i < elem_count; elem_i += 1) { + table[elem_i + offset] = read32_uleb128(mod.ptr, &i); + } + } + } + + struct VirtualMachine vm; + vm.stack = arena_alloc(sizeof(uint64_t) * 10000000), + vm.mod_ptr = mod.ptr; + vm.opcodes = arena_alloc(2000000); + vm.operands = arena_alloc(sizeof(uint32_t) * 2000000); + vm.stack_top = 0; + vm.functions = functions; + vm.types = types; + vm.globals = globals; + vm.memory = memory; + vm.memory_len = memory_len; + vm.imports = imports; + vm.imports_len = imports_len; + vm.args = argv + vm_argv_start; + vm.table = table; + + { + uint32_t code_i = section_starts[Section_code]; + uint32_t codes_len = read32_uleb128(mod.ptr, &code_i); + if (codes_len != functions_len) panic("code/function length mismatch"); + struct ProgramCounter pc; + pc.opcode = 0; + pc.operand = 0; + for (uint32_t func_i = 0; func_i < functions_len; func_i += 1) { + struct Function *func = &functions[func_i]; + uint32_t size = read32_uleb128(mod.ptr, &code_i); + uint32_t code_begin = code_i; + + struct TypeInfo *type_info = &vm.types[func->type_idx]; + func->locals_count = 0; + func->local_types = arena_alloc(sizeof(uint32_t) * ((type_info->param_count + func->locals_count + 31) / 32)); + func->local_types[0] = type_info->param_types; + + for (uint32_t local_sets_count = read32_uleb128(mod.ptr, &code_i); + local_sets_count > 0; local_sets_count -= 1) { + uint32_t set_count = read32_uleb128(mod.ptr, &code_i); + int64_t local_type = read64_ileb128(mod.ptr, &code_i); + + uint32_t i = type_info->param_count + func->locals_count; + func->locals_count += set_count; + if ((type_info->param_count + func->locals_count + 31) / 32 > (i + 31) / 32) + func->local_types = arena_realloc(func->local_types, sizeof(uint32_t) * ((type_info->param_count + func->locals_count + 31) / 32)); + for (; i < type_info->param_count + func->locals_count; i += 1) + switch (local_type) { + case -1: case -3: bs_unset(func->local_types, i); break; + case -2: case -4: bs_set(func->local_types, i); break; + default: panic("unexpected local type"); + } + } + + func->entry_pc = pc; + vm_decodeCode(&vm, func, &code_i, &pc); + if (code_i != code_begin + size) panic("bad code size"); + } + + uint64_t opcode_counts[0x100]; + memset(opcode_counts, 0, 0x100); + uint64_t prefixed_opcode_counts[0x100]; + memset(prefixed_opcode_counts, 0, 0x100); + bool is_prefixed = false; + for (uint32_t opcode_i = 0; opcode_i < pc.opcode; opcode_i += 1) { + uint8_t opcode = vm.opcodes[opcode_i]; + if (!is_prefixed) { + opcode_counts[opcode] += 1; + is_prefixed = opcode == WasmOp_prefixed; + } else { + prefixed_opcode_counts[opcode] += 1; + is_prefixed = false; + } + } + } + + vm_call(&vm, start_fn_idx); + vm_run(&vm); + + return 0; +}