├── .gitignore ├── LICENSE ├── README.md ├── src ├── CMakeLists.txt ├── compiler_bridges.h ├── lib │ ├── BuiltinFunctions.h │ ├── CMakeLists.txt │ ├── Exceptions.h │ ├── ExecutionControlBlock.h │ ├── ExpressionService.cpp │ ├── ExpressionService.h │ ├── FunctionEntry.h │ ├── JitWriter.cpp │ ├── JitWriter.h │ ├── WasmContext.cpp │ ├── WasmContext.h │ ├── layer │ │ ├── layer.h │ │ ├── posix │ │ │ ├── optemplates.asm │ │ │ └── pagealloc.cpp │ │ └── win32 │ │ │ ├── optemplates.asm │ │ │ └── pagealloc.cpp │ ├── numeric_cast.h │ ├── rt_callbacks.cpp │ ├── safe_access.cpp │ ├── safe_access.h │ ├── sal_stubs.h │ ├── stdafx.h │ ├── targetver.h │ └── wasm_types.h ├── runtime │ ├── CMakeLists.txt │ ├── main.cpp │ └── stdafx.h └── testhost │ ├── CMakeLists.txt │ └── testhost.cpp └── test ├── scripts └── compile_tests.py └── spec_tests ├── address.wast ├── align.wast ├── binary.wast ├── block.wast ├── br.wast ├── br_if.wast ├── br_table.wast ├── break-drop.wast ├── call.wast ├── call_indirect.wast ├── comments.wast ├── const.wast ├── conversions.wast ├── custom_section.wast ├── elem.wast ├── endianness.wast ├── exports.wast ├── f32.wast ├── f32_bitwise.wast ├── f32_cmp.wast ├── f64.wast ├── f64_bitwise.wast ├── f64_cmp.wast ├── fac.wast ├── float_exprs.wast ├── float_literals.wast ├── float_memory.wast ├── float_misc.wast ├── forward.wast ├── func.wast ├── func_ptrs.wast ├── get_local.wast ├── globals.wast ├── i32.wast ├── i64.wast ├── if.wast ├── imports.wast ├── inline-module.wast ├── int_exprs.wast ├── int_literals.wast ├── labels.wast ├── left-to-right.wast ├── linking.wast ├── loop.wast ├── memory.wast ├── memory_redundancy.wast ├── memory_trap.wast ├── names.wast ├── nop.wast ├── resizing.wast ├── return.wast ├── run.py ├── select.wast ├── set_local.wast ├── skip-stack-guard-page.wast ├── stack.wast ├── start.wast ├── store_retval.wast ├── switch.wast ├── tee_local.wast ├── token.wast ├── traps.wast ├── type.wast ├── typecheck.wast ├── unreachable.wast ├── unreached-invalid.wast ├── unwind.wast ├── utf8-custom-section-id.wast ├── utf8-import-field.wast ├── utf8-import-module.wast └── utf8-invalid-encoding.wast /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | Testing 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | *.o 11 | 12 | ## Ignore Visual Studio temporary files, build results, and 13 | ## files generated by popular Visual Studio add-ons. 14 | ## 15 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 16 | 17 | # User-specific files 18 | *.suo 19 | *.user 20 | *.userosscache 21 | *.sln.docstates 22 | 23 | # User-specific files (MonoDevelop/Xamarin Studio) 24 | *.userprefs 25 | 26 | # Build results 27 | [Dd]ebug/ 28 | [Dd]ebugPublic/ 29 | [Rr]elease/ 30 | [Rr]eleases/ 31 | x64/ 32 | x86/ 33 | bld/ 34 | [Bb]in/ 35 | [Oo]bj/ 36 | [Ll]og/ 37 | 38 | # Visual Studio 2015 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUNIT 48 | *.VisualState.xml 49 | TestResult.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | **/Properties/launchSettings.json 64 | 65 | *_i.c 66 | *_p.c 67 | *_i.h 68 | *.ilk 69 | *.meta 70 | *.obj 71 | *.pch 72 | *.pdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *.log 83 | *.vspscc 84 | *.vssscc 85 | .builds 86 | *.pidb 87 | *.svclog 88 | *.scc 89 | 90 | # Chutzpah Test files 91 | _Chutzpah* 92 | 93 | # Visual C++ cache files 94 | ipch/ 95 | *.aps 96 | *.ncb 97 | *.opendb 98 | *.opensdf 99 | *.sdf 100 | *.cachefile 101 | *.VC.db 102 | *.VC.VC.opendb 103 | 104 | # Visual Studio profiler 105 | *.psess 106 | *.vsp 107 | *.vspx 108 | *.sap 109 | 110 | # Visual Studio Trace Files 111 | *.e2e 112 | 113 | # TFS 2012 Local Workspace 114 | $tf/ 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | *.DotSettings.user 123 | 124 | # JustCode is a .NET coding add-in 125 | .JustCode 126 | 127 | # TeamCity is a build add-in 128 | _TeamCity* 129 | 130 | # DotCover is a Code Coverage Tool 131 | *.dotCover 132 | 133 | # AxoCover is a Code Coverage Tool 134 | .axoCover/* 135 | !.axoCover/settings.json 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/[Pp]ackages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/[Pp]ackages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/[Pp]ackages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Since there are multiple workflows, uncomment next line to ignore bower_components 227 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 228 | #bower_components/ 229 | 230 | # RIA/Silverlight projects 231 | Generated_Code/ 232 | 233 | # Backup & report files from converting an old project file 234 | # to a newer Visual Studio version. Backup files are not needed, 235 | # because we have git ;-) 236 | _UpgradeReport_Files/ 237 | Backup*/ 238 | UpgradeLog*.XML 239 | UpgradeLog*.htm 240 | 241 | # SQL Server files 242 | *.mdf 243 | *.ldf 244 | *.ndf 245 | 246 | # Business Intelligence projects 247 | *.rdl.data 248 | *.bim.layout 249 | *.bim_*.settings 250 | 251 | # Microsoft Fakes 252 | FakesAssemblies/ 253 | 254 | # GhostDoc plugin setting file 255 | *.GhostDoc.xml 256 | 257 | # Node.js Tools for Visual Studio 258 | .ntvs_analysis.dat 259 | node_modules/ 260 | 261 | # Typescript v1 declaration files 262 | typings/ 263 | 264 | # Visual Studio 6 build log 265 | *.plg 266 | 267 | # Visual Studio 6 workspace options file 268 | *.opt 269 | 270 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 271 | *.vbw 272 | 273 | # Visual Studio LightSwitch build output 274 | **/*.HTMLClient/GeneratedArtifacts 275 | **/*.DesktopClient/GeneratedArtifacts 276 | **/*.DesktopClient/ModelManifest.xml 277 | **/*.Server/GeneratedArtifacts 278 | **/*.Server/ModelManifest.xml 279 | _Pvt_Extensions 280 | 281 | # Paket dependency manager 282 | .paket/paket.exe 283 | paket-files/ 284 | 285 | # FAKE - F# Make 286 | .fake/ 287 | 288 | # JetBrains Rider 289 | .idea/ 290 | *.sln.iml 291 | 292 | # CodeRush 293 | .cr/ 294 | 295 | # Python Tools for Visual Studio (PTVS) 296 | __pycache__/ 297 | *.pyc 298 | 299 | # Cake - Uncomment if you are using it 300 | # tools/** 301 | # !tools/packages.config 302 | 303 | # Tabs Studio 304 | *.tss 305 | 306 | # Telerik's JustMock configuration file 307 | *.jmconfig 308 | 309 | # BizTalk build output 310 | *.btp.cs 311 | *.btm.cs 312 | *.odx.cs 313 | *.xsd.cs 314 | 315 | # OpenCover UI analysis results 316 | OpenCover/ 317 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libwasm 2 | libwasm is a cross platform Web Assembly JIT engine with no external dependencies and good performance. It was created to allow non-web programs to easily support web assembly. In addition to providing an interpreter libwasm also specifies a stable ABI and libc allowing cross platform execution outside of the web. 3 | 4 | # WasmRT 5 | WasmRT - The web assembly runtime builds upon libwasm and is intended to allow applications to ship in webassembly without requiring recompilation on different hosts. WasmRT provides a consistent and cross platform ABI intended to work well with command line applications. 6 | 7 | # Why 8 | Many programs embed language runtimes for extensibility such as Lua or Javascript. While great for most purposes traditional embedded programmability forces a specific language on the user. Wasm allows extensibility to be added in a language neutral way. 9 | 10 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.7 FATAL_ERROR) 2 | project(libwasm VERSION 0.1 LANGUAGES CXX) 3 | set(CMAKE_GENERATOR_PLATFORM x64) 4 | IF( CMAKE_SIZEOF_VOID_P EQUAL 8 ) 5 | ELSE() 6 | message( FATAL_ERROR "libwasm supports X64 only" ) 7 | ENDIF() 8 | 9 | if(WIN32) 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX") 11 | ELSE() 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-switch") 13 | ENDIF() 14 | 15 | set(CMAKE_CXX_STANDARD 14) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | set(CMAKE_CXX_EXTENSIONS OFF) 18 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 19 | add_subdirectory(lib) 20 | add_subdirectory(runtime) 21 | add_subdirectory(testhost) 22 | -------------------------------------------------------------------------------- /src/compiler_bridges.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _MSC_VER 3 | template 4 | constexpr std::size_t _countof(T const (&)[N]) noexcept 5 | { 6 | return N; 7 | } 8 | #define UNREFERENCED_PARAMETER(param) do { (void)(param); } while(0) 9 | #endif 10 | -------------------------------------------------------------------------------- /src/lib/BuiltinFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wasm_types.h" 4 | #include 5 | 6 | typedef uint64_t(*FnBuiltinPtr)(uint64_t *pvArgs, uint8_t *pvMemBase); 7 | struct BuiltinExport 8 | { 9 | const char *szName; 10 | value_type retT; 11 | std::vector vecarg; 12 | FnBuiltinPtr pfn; 13 | }; 14 | 15 | extern BuiltinExport BuiltinMap[]; 16 | 17 | bool FEqualProto(const BuiltinExport &builtinexport, const FunctionTypeEntry &fnt); 18 | int IBuiltinFromName(const std::string &strName); -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(libwasm VERSION 0.1) 2 | file(GLOB GENERIC_SOURCES *.cpp) 3 | 4 | IF (WIN32) 5 | enable_language(ASM_MASM) 6 | file(GLOB ASM_SOURCES layer/win32/*.asm) 7 | file(GLOB PLAT_SOURCES layer/win32/*.cpp) 8 | ELSE() 9 | enable_language(ASM_NASM) 10 | file(GLOB ASM_SOURCES layer/posix/*.asm) 11 | set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -f elf64 -g -F dwarf") 12 | 13 | set_property(SOURCE ${ASM_SOURCES} PROPERTY LANGUAGE ASM_NASM) 14 | 15 | file(GLOB PLAT_SOURCES layer/posix/*.cpp) 16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") # Hack! 17 | ENDIF() 18 | 19 | add_library(wasm STATIC SHARED ${GENERIC_SOURCES} ${ASM_SOURCES} ${PLAT_SOURCES}) 20 | -------------------------------------------------------------------------------- /src/lib/Exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | struct Exception 4 | { 5 | Exception(const std::string &strErr) 6 | : strErr(strErr) 7 | {} 8 | std::string strErr; 9 | }; 10 | 11 | struct RuntimeException : public Exception 12 | { 13 | RuntimeException(const std::string &strErr) 14 | : Exception(strErr) 15 | {} 16 | }; 17 | 18 | static void Verify(bool fVerify, const char *sz = "Validation Failure") 19 | { 20 | if (!fVerify) 21 | throw Exception(sz); 22 | } -------------------------------------------------------------------------------- /src/lib/ExecutionControlBlock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ExecutionControlBlock 4 | { 5 | class JitWriter *pjitWriter; 6 | void *pfnEntry; 7 | void *operandStack; 8 | void *localsStack; 9 | uint64_t cbHeap; 10 | void *memoryBase; 11 | 12 | uint64_t cFnIndirect; 13 | uint32_t *rgfnIndirect; 14 | uint32_t *rgFnTypeIndicies; 15 | uint64_t cFnTypeIndicies; 16 | void *rgFnPtrs; 17 | uint64_t cFnPtrs; 18 | 19 | // Values set by the executing code 20 | void *stackrestore; 21 | uint64_t retvalue; 22 | }; -------------------------------------------------------------------------------- /src/lib/ExpressionService.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ExpressionService.h" 3 | #include "safe_access.h" 4 | #include "Exceptions.h" 5 | 6 | size_t ExpressionService::CbEatExpression(const uint8_t *rgb, size_t cb, _Out_ Variant *pvariantOut) 7 | { 8 | const size_t cbStart = cb; 9 | opcode op = safe_read_buffer(&rgb, &cb); 10 | switch (op) 11 | { 12 | case opcode::i32_const: 13 | pvariantOut->type = value_type::i32; 14 | pvariantOut->val = safe_read_buffer(&rgb, &cb); 15 | break; 16 | case opcode::i64_const: 17 | pvariantOut->type = value_type::i64; 18 | pvariantOut->val = safe_read_buffer(&rgb, &cb); 19 | break; 20 | 21 | case opcode::f32_const: 22 | pvariantOut->type = value_type::f32; 23 | pvariantOut->val = 0; 24 | *reinterpret_cast(&pvariantOut->val) = safe_read_buffer(&rgb, &cb); 25 | break; 26 | 27 | case opcode::f64_const: 28 | pvariantOut->type = value_type::f64; 29 | *reinterpret_cast(&pvariantOut->val) = safe_read_buffer(&rgb, &cb); 30 | break; 31 | 32 | default: 33 | Verify(false); 34 | } 35 | 36 | op = safe_read_buffer(&rgb, &cb); 37 | Verify(op == opcode::end); 38 | 39 | return cbStart - cb; 40 | } 41 | 42 | value_type ParseTypeString(const std::string &strType) 43 | { 44 | if (strType == "i32") 45 | return value_type::i32; 46 | if (strType == "i64") 47 | return value_type::i64; 48 | if (strType == "f32") 49 | return value_type::f32; 50 | if (strType == "f64") 51 | return value_type::f64; 52 | Verify(false); 53 | return value_type::none; 54 | } 55 | 56 | size_t ExpressionService::CchEatExpression(const char *sz, size_t cch, _Out_ Variant *pvalOut) 57 | { 58 | // eat expressions such as (i32.const 123) 59 | const char *pchCur = sz; 60 | const char *pchMax = sz + cch; 61 | 62 | // OpenParen -> type -> ".const" -> val -> CloseParen 63 | int mode = 0; 64 | std::string strType; 65 | std::string strConst; 66 | std::string strVal; 67 | while (pchCur < pchMax) 68 | { 69 | if (isspace(*pchCur)) 70 | { 71 | Verify(mode != 1 || strType.size() == 0); 72 | if (mode == 2 && strConst.size() > 0) 73 | { 74 | Verify(strConst == "const"); 75 | ++mode; 76 | } 77 | Verify(mode != 3 || strVal.size() == 0); 78 | ++pchCur; 79 | continue; 80 | } 81 | 82 | switch (mode) 83 | { 84 | default: 85 | Verify(false); 86 | break; 87 | 88 | case 0: 89 | Verify(*pchCur == '('); 90 | ++mode; 91 | break; 92 | 93 | case 1: 94 | if (*pchCur == '.') 95 | { 96 | Verify(strType.size() > 0); 97 | pvalOut->type = ParseTypeString(strType); 98 | ++mode; 99 | } 100 | else 101 | { 102 | strType.append(pchCur, pchCur + 1); 103 | } 104 | break; 105 | 106 | case 2: 107 | strConst.append(pchCur, pchCur + 1); 108 | break; 109 | 110 | case 3: 111 | if (*pchCur == ')') 112 | { 113 | Verify(strVal.size() > 0); 114 | switch (pvalOut->type) 115 | { 116 | case value_type::i32: 117 | case value_type::i64: 118 | { 119 | int base = 10; 120 | if (strVal.size() > 2 && strVal[0] == '0' && strVal[1] == 'x') 121 | { 122 | // Hex 123 | base = 16; 124 | strVal = std::string(strVal.begin() + 2, strVal.end()); 125 | } 126 | pvalOut->val = static_cast(std::stoull(strVal, nullptr, base)); 127 | if (pvalOut->type == value_type::i32) 128 | { 129 | pvalOut->val = (uint32_t)pvalOut->val; 130 | } 131 | break; 132 | } 133 | case value_type::f32: 134 | { 135 | float valT = std::stof(strVal); 136 | pvalOut->val = *reinterpret_cast(&valT); 137 | break; 138 | } 139 | case value_type::f64: 140 | { 141 | double valT = std::stod(strVal); 142 | pvalOut->val = *reinterpret_cast(&valT); 143 | break; 144 | } 145 | default: 146 | Verify(false); 147 | } 148 | ++mode; 149 | continue; // don't increment pch 150 | } 151 | strVal.append(pchCur, pchCur + 1); 152 | break; 153 | 154 | case 4: 155 | Verify(*pchCur == ')'); 156 | return (pchCur + 1) - sz; 157 | } 158 | 159 | ++pchCur; 160 | } 161 | Verify(false); 162 | abort(); 163 | } -------------------------------------------------------------------------------- /src/lib/ExpressionService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "wasm_types.h" 4 | 5 | // The expression service executes 1-off expressions as required by the loader. This is not intended to supplement behavior of the JIT engine 6 | class ExpressionService 7 | { 8 | public: 9 | struct EXPORT Variant 10 | { 11 | bool operator==(const Variant &other) 12 | { 13 | return (type == other.type) && (val == other.val); 14 | } 15 | uint64_t val = 0; 16 | value_type type = value_type::none; 17 | }; 18 | 19 | static EXPORT size_t CbEatExpression(const uint8_t *rgb, size_t cb, Variant *pvalOut); // consume an expression from a byte buffer and inform the caller how long it was 20 | static EXPORT size_t CchEatExpression(const char *sz, size_t cch, Variant *pvalOut); 21 | }; 22 | -------------------------------------------------------------------------------- /src/lib/FunctionEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /src/lib/JitWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wasm_types.h" 4 | #include "Exceptions.h" 5 | #include "numeric_cast.h" 6 | #include "ExpressionService.h" 7 | 8 | extern "C" void CompileFn(struct ExecutionControlBlock *pectl, uint32_t ifn); 9 | class JitWriter 10 | { 11 | friend void CompileFn(ExecutionControlBlock *pectl, uint32_t ifn); 12 | public: 13 | JitWriter(class WasmContext *pctxt, size_t cfn, size_t cglbls); 14 | ~JitWriter(); 15 | 16 | void CompileFn(uint32_t ifn); 17 | 18 | ExpressionService::Variant ExternCallFn(uint32_t ifn, void *pvAddrMem, ExpressionService::Variant *rgargs, uint32_t cargs); 19 | 20 | // Psuedo private callbacks from ASM 21 | uint64_t CReentryFn(int ifn, uint64_t *pvArgs, uint8_t *pvMemBase, ExecutionControlBlock *pecb); 22 | uint32_t GrowMemory(ExecutionControlBlock *pectl, uint32_t cpages); 23 | private: 24 | void SafePushCode(const void *pv, size_t cb); 25 | template 26 | size_t GetArrLength(T(&)[size]) { return size; } 27 | 28 | template 29 | void SafePushCode(T &rgcode, std::true_type) 30 | { 31 | SafePushCode(rgcode, GetArrLength(rgcode) * sizeof(*rgcode)); 32 | } 33 | 34 | template 35 | void SafePushCode(T &val, std::false_type) 36 | { 37 | SafePushCode(&val, sizeof(val)); 38 | } 39 | 40 | template 41 | void SafePushCode(T &&val) 42 | { 43 | SafePushCode(val, std::is_array()); 44 | } 45 | 46 | enum class CompareType 47 | { 48 | LessThan, 49 | LessThanEqual, 50 | Equal, 51 | NotEqual, 52 | GreaterThanEqual, 53 | GreaterThan, 54 | }; 55 | enum class LogicOperation 56 | { 57 | And, 58 | Or, 59 | Xor, 60 | ShiftLeft, 61 | ShiftRight, 62 | ShiftRightUnsigned, 63 | RotateLeft, 64 | RotateRight, 65 | }; 66 | enum class ArithmeticOperation 67 | { 68 | Add, 69 | Sub, 70 | Multiply, 71 | Divide, 72 | }; 73 | 74 | int32_t RelAddrPfnVector(uint32_t ifn, uint32_t opSize) const 75 | { 76 | return numeric_cast((m_pexecPlane + (sizeof(void*)*ifn)) - (m_pexecPlaneCur + opSize)); 77 | } 78 | 79 | // Common code sequences (does not leave machine in valid state) 80 | void _PushExpandStack(); 81 | void _PopContractStack(); 82 | void _PopSecondParam(bool fSwapParams = false); 83 | void _SetDbgReg(uint32_t opcode); 84 | 85 | // common operations (does leave machine in valid state) 86 | void LoadMem(uint32_t offset, bool f64Dst /* else 32 */, uint32_t cbSrc, bool fSignExtend); 87 | void StoreMem(uint32_t offset, uint32_t cbDst); 88 | 89 | void Sub32(); 90 | void Add32(); 91 | void Mul32(); 92 | void Add64(); 93 | void Sub64(); 94 | void Mul64(); 95 | void Div(bool fSigned, bool fModulo, bool f64); 96 | void Popcnt32(); 97 | void Popcnt64(); 98 | void PushC32(uint32_t c); 99 | void PushC64(uint64_t c); 100 | void PushF32(float c); 101 | void PushF64(double c); 102 | void Convert(value_type typeDst, value_type typeSrc, bool fSigned); 103 | void SetLocal(uint32_t idx, bool fPop); 104 | void GetLocal(uint32_t idx); 105 | void GetGlobal(uint32_t idx); 106 | void SetGlobal(uint32_t idx); 107 | void CountTrailingZeros(bool f64); 108 | void CountLeadingZeros(bool f64); 109 | void Select(); 110 | void Compare(CompareType type, bool fSigned, bool f64); 111 | void FloatCompare(CompareType type); 112 | void DoubleCompare(CompareType type); 113 | void Eqz32(); 114 | void Eqz64(); 115 | void CallAsmOp(void **pfn); 116 | void LogicOp(LogicOperation op); 117 | void LogicOp64(LogicOperation op); 118 | void FloatArithmetic(ArithmeticOperation op, bool fDouble); 119 | int32_t *JumpNIf(void *addr); // returns a pointer to the offset encoded in the instruction for later adjustment 120 | int32_t *Jump(void *addr); 121 | void CallIfn(uint32_t ifn, uint32_t clocalsCaller, uint32_t cargsCallee, bool fReturnValue, bool fIndirect); 122 | void FnEpilogue(bool fRetVal); 123 | void FnPrologue(uint32_t clocals, uint32_t cargs); 124 | void BranchTableParse(const uint8_t **ppoperand, size_t *pcbOperand, const std::vector> &stackBlockTypeAddr, std::vector> &stackVecFixups, std::vector> &stackVecFixupsAbsolute); 125 | void ExtendSigned32_64(); 126 | void FloatNeg(bool fDouble); 127 | 128 | void Ud2(); 129 | 130 | void EnterBlock(); 131 | int32_t *EnterIF(); 132 | void LeaveBlock(bool fHasReturn); 133 | 134 | void ProtectForRuntime(); 135 | void UnprotectRuntime(); 136 | 137 | class WasmContext *m_pctxt = nullptr; // Parent 138 | uint8_t *m_pexecPlane = nullptr; 139 | uint8_t *m_pcodeStart = nullptr; 140 | uint8_t *m_pexecPlaneCur = nullptr; 141 | uint8_t *m_pexecPlaneMax = nullptr; 142 | void **m_pfnCallIndirectShim = nullptr; 143 | void **m_pfnBranchTable = nullptr; 144 | void **m_pfnU64ToF32 = nullptr; 145 | void **m_pfnU64ToF64 = nullptr; 146 | void **m_pfnGrowMemoryOp = nullptr; 147 | void **m_pfnF32ToU64Trunc = nullptr; 148 | void **m_pfnF64ToU64Trunc = nullptr; 149 | uint64_t *m_pGlobalsStart = nullptr; 150 | void *m_pheap = nullptr; 151 | size_t m_cfn; 152 | 153 | std::vector m_vecoperand; 154 | std::vector m_veclocals; 155 | std::unique_ptr m_spapbExecPlane; 156 | std::unique_ptr m_spapbHeap; 157 | }; 158 | -------------------------------------------------------------------------------- /src/lib/WasmContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "wasm_types.h" 3 | #include "ExpressionService.h" 4 | 5 | class WasmContext 6 | { 7 | friend class JitWriter; 8 | 9 | public: 10 | EXPORT WasmContext(); 11 | EXPORT ~WasmContext(); 12 | 13 | EXPORT ExpressionService::Variant CallFunction(const char *szName, ExpressionService::Variant *rgargs = nullptr, uint32_t cargs = 0); 14 | EXPORT void LoadModule(FILE *pfModule); 15 | 16 | protected: 17 | // File Load Helpers 18 | void load_fn_type(const uint8_t **prgbPayload, size_t *pcbData); 19 | void load_fn_types(const uint8_t *rgbPayload, size_t cbData); 20 | void load_fn_decls(const uint8_t *rgbPayload, size_t cbData); 21 | void load_tables(const uint8_t *rgbPayload, size_t cbData); 22 | void load_memory(const uint8_t *rgbPayload, size_t cbData); 23 | void load_globals(const uint8_t *rgbPayload, size_t cbData); 24 | void load_exports(const uint8_t *rgbPayload, size_t cbData); 25 | void load_code(const uint8_t *rgbPayload, size_t cbData); 26 | void load_imports(const uint8_t *rgbPayload, size_t cbData); 27 | void load_elements(const uint8_t *rgbPayload, size_t cbData); 28 | void load_data(const uint8_t *rgbPayload, size_t cbData); 29 | void load_start(const uint8_t *rgbPayload, size_t cbData); 30 | bool load_section(FILE *pf); 31 | 32 | void InitializeMemory(); 33 | void LinkImports(); 34 | 35 | uint32_t ITypeCanonicalFromIType(uint32_t idx); 36 | 37 | struct GlobalVar 38 | { 39 | uint64_t val; 40 | value_type type; 41 | bool fMutable; 42 | }; 43 | std::vector m_vecglbls; 44 | std::vector m_vecfn_types; 45 | std::vector m_vecfn_entries; 46 | std::vector m_vectbl; 47 | std::vector m_vecmem_types; 48 | std::vector m_vecimports; 49 | std::vector m_vecIndirectFnTable; 50 | std::vector m_vecimportFnNames; 51 | std::vector m_vecexports; 52 | std::vector m_vecfn_code; 53 | std::vector m_vecmem; 54 | 55 | bool m_fStartFn = false; 56 | uint32_t m_ifnStart = 0; 57 | 58 | std::unique_ptr m_spjitwriter; 59 | }; 60 | -------------------------------------------------------------------------------- /src/lib/layer/layer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace layer 4 | { 5 | 6 | /* Platform Abstraction Layer defines */ 7 | class AllocatedPageBlock 8 | { 9 | public: 10 | virtual ~AllocatedPageBlock() {} 11 | AllocatedPageBlock(const AllocatedPageBlock &other) = delete; 12 | AllocatedPageBlock(AllocatedPageBlock &&other) = delete; 13 | 14 | void *PvBaseAddr() const { return m_pv; } 15 | size_t Cb() const { return m_cb; } 16 | protected: 17 | AllocatedPageBlock() = default; 18 | 19 | void *m_pv = nullptr; 20 | size_t m_cb = 0; 21 | }; 22 | 23 | enum class PAGE_PROTECTION 24 | { 25 | ReadOnly, 26 | ReadWrite, 27 | ReadExecute, 28 | Unallocated, 29 | }; 30 | 31 | // ReservePages reserves a range but does not commit it 32 | std::unique_ptr ReservePages(const void *pvBaseRequested, size_t cb); 33 | 34 | void ProtectRange(AllocatedPageBlock &block, void *pvAddrStart, size_t cbRange, PAGE_PROTECTION prot); 35 | }; 36 | -------------------------------------------------------------------------------- /src/lib/layer/posix/optemplates.asm: -------------------------------------------------------------------------------- 1 | BITS 64 2 | section .note.GNU-stack noalloc noexec nowrite progbits 3 | 4 | %define arg0 rdi 5 | %define arg1 rsi 6 | %define arg2 rdx 7 | 8 | section .data 9 | f32_2p63: 10 | dd 5f000000h 11 | f64_2p63: 12 | dq 43E0000000000000h 13 | 14 | section .code 15 | STRUC ExecutionControlBlock 16 | .JitWriter resq 1 17 | 18 | .pfnEntry resq 1 19 | .operandStack resq 1 20 | .localsStack resq 1 21 | .cbHeap resq 1 22 | .memoryBase resq 1 23 | 24 | .cFnIndirect resq 1 25 | .rgFnIndirect resq 1 26 | .rgFnTypeIndicies resq 1 27 | .cFnTypeIndicies resq 1 28 | .rgFnPtrs resq 1 29 | .cFnPtrs resq 1 30 | 31 | ; Outputs and Temps 32 | .stackrestore resq 1 33 | .retvalue resq 1 34 | ENDSTRUC 35 | 36 | %macro CallCFn 1 37 | ; call the C function passed in, assuming the stack is unbalanced 38 | 39 | ; balance the stack 40 | push r13 41 | mov r13, rsp 42 | sub rsp, 8 43 | and rsp, ~15 44 | ;Do the actual call 45 | sub rsp, 32 46 | call %1 wrt ..plt 47 | ; restore stack and go home 48 | mov rsp, r13 49 | pop r13 50 | %endmacro 51 | 52 | %macro BackupVMState 0 53 | push rdi 54 | push rsi 55 | %endmacro 56 | %macro RestoreVMState 0 57 | pop rsi 58 | pop rdi 59 | %endmacro 60 | 61 | extern CReentryFn 62 | extern GrowMemory 63 | 64 | 65 | ; REGISTERS: 66 | ; rax - top of param stack 67 | ; rdi - param stack - 1 (volatile C calls) 68 | ; rsi - memory base (volatile C calls) 69 | ; rbx - parameter base 70 | ; rbp - pointer to the execution control block 71 | ; temps: rcx, rdx, r11 72 | 73 | global ExternCallFnASM 74 | ExternCallFnASM: 75 | push rbx 76 | push rbp 77 | push r12 78 | push r13 79 | push r14 80 | push r15 81 | 82 | mov rsi, [arg0 + ExecutionControlBlock.memoryBase] 83 | mov rbx, [arg0 + ExecutionControlBlock.localsStack] 84 | mov [arg0 + ExecutionControlBlock.stackrestore], rsp 85 | mov rax, [arg0 + ExecutionControlBlock.pfnEntry] 86 | mov rbp, arg0 87 | mov rdi, [arg0 + ExecutionControlBlock.operandStack] ; rdi == arg0 88 | call rax 89 | mov [rbp + ExecutionControlBlock.retvalue], rax 90 | mov [rbp + ExecutionControlBlock.operandStack], rdi 91 | mov [rbp + ExecutionControlBlock.localsStack], rbx 92 | 93 | mov eax, 1 94 | .LDone: 95 | pop r15 96 | pop r14 97 | pop r13 98 | pop r12 99 | pop rbp 100 | pop rbx 101 | ret 102 | LTrapRet: 103 | xor eax, eax ; return 0 for failed execution 104 | jmp ExternCallFnASM.LDone 105 | 106 | global BranchTable 107 | BranchTable: 108 | ; jump to an argument in the table or default 109 | ; note: this is a candidate for more optimization but for now we will always perform a jump table 110 | ; 111 | ; rax - table index 112 | ; rcx - table pointer 113 | ; edx - temp 114 | ; 115 | ; Table Format: 116 | ; dword count 117 | ; dword fReturnVal 118 | ; --default_target-- 119 | ; qword distance (how many blocks we're jumping) 120 | ; qword target_addr 121 | ; --table_targets--- 122 | ; .... * count 123 | 124 | mov edx, eax ; backup the table index 125 | mov rax, [rdi] ; store the block return value (or garbage) 126 | 127 | cmp edx, [rcx] 128 | jb .LNotDefault 129 | xor edx, edx 130 | jmp .LDefault 131 | 132 | .LNotDefault: 133 | ;else use table 134 | add edx, 1 ; skip the default 135 | shl edx, 4 ; convert table offset to a byte offset 136 | .LDefault: 137 | ; at this point rdx is a byte offset into the vector table 138 | ; Lets adjust the stack to deal with the blocks we're leaving 139 | mov r11d, [rcx+8+rdx] ; r11d = table[idx].distance 140 | lea rsp, [rsp+r11*8] ; adjust the stack for the blocks 141 | mov rax, [rdi-8] ; get the potential block return value 142 | pop rdi ; Restore the param stack to the right location 143 | ; Put the top of the param stack in rax if we don't have a return value 144 | mov r11d, [rcx+4] 145 | test r11d, r11d 146 | jnz .LHasRetValue 147 | ; Doesn't have a return value if we get here, so put the top param back in rax 148 | sub rdi, 8 149 | mov rax, [rdi] 150 | .LHasRetValue: 151 | ; The universe should be setup correctly for the target block so we just need to jump 152 | jmp [rcx+16+rdx] 153 | 154 | Trap: 155 | mov rsp, [rbp + ExecutionControlBlock.stackrestore] 156 | jmp LTrapRet 157 | 158 | global WasmToC 159 | WasmToC: 160 | ; Translates a wasm function call into a C function call for internal use 161 | ; we expect the internal function # in rcx already 162 | ; we will put the parameter base address in rdx 163 | ; the normal JIT registers are preserved by the calling convention 164 | 165 | BackupVMState 166 | mov rdi, rcx ; first arg function # 167 | mov rdx, rsi ; third arg 168 | mov rsi, rbx ; put the parameter base address in rsi (second arg) 169 | mov rcx, rbp 170 | CallCFn CReentryFn 171 | RestoreVMState 172 | ret 173 | 174 | extern CompileFn 175 | global CallIndirectShim 176 | CallIndirectShim: 177 | ; ecx contains the function index 178 | ; eax contains the type 179 | 180 | ; Convert the indirect index to the function index 181 | ; First Bounds Check 182 | cmp rcx, [rbp + ExecutionControlBlock.cFnIndirect] 183 | jae .LDoTrap 184 | ; Now do the conversion 185 | mov rdx, [rbp + ExecutionControlBlock.rgFnIndirect] 186 | mov ecx, [rdx + rcx * 4] 187 | 188 | ; Now bounds check the type index 189 | mov rdx, [rbp + ExecutionControlBlock.rgFnTypeIndicies] 190 | cmp rcx, [rbp + ExecutionControlBlock.cFnTypeIndicies] 191 | jae .LDoTrap 192 | 193 | mov edx, [rdx + rcx*4] ; get the callee's type 194 | cmp edx, eax ; check if its equal to the expected type 195 | jne .LDoTrap ; Trap if not 196 | 197 | ; Type is validated now lets do the function call 198 | cmp rcx, [rbp + ExecutionControlBlock.cFnPtrs] 199 | jae .LDoTrap 200 | mov rdx, [rbp + ExecutionControlBlock.rgFnPtrs] 201 | mov rax, [rdx + rcx*8] 202 | test rax, rax 203 | jz .LCompileFn 204 | jmp rax 205 | ud2 206 | 207 | .LDoTrap: 208 | jmp Trap ; this is just for a chance to break before going 209 | 210 | .LCompileFn: 211 | lea rax, [rdx + rcx*8] 212 | push rax 213 | BackupVMState 214 | mov esi, ecx ; second param is the function index 215 | mov rdi, rbp ; first param the control block 216 | CallCFn CompileFn 217 | RestoreVMState 218 | pop rax 219 | jmp [rax] 220 | ud2 221 | 222 | global U64ToF32 223 | U64ToF32: 224 | test rax, rax 225 | js .LSpecialCase 226 | cvtsi2ss xmm0, rax 227 | movd eax, xmm0 228 | ret 229 | .LSpecialCase: 230 | mov rdx, rax 231 | and edx, 1 ; edx store the least significant bit of our input 232 | shr rax, 1 ; divide the input by 2 233 | or rax, rdx ; round to odd 234 | cvtsi2ss xmm0, rax ; convert input/2 -> double 235 | addss xmm0, xmm0 ; multiply by 2 236 | movq rax, xmm0 ; save the result in rax 237 | ret 238 | 239 | global U64ToF64 240 | U64ToF64: 241 | test rax, rax 242 | js .LSpecialCase 243 | cvtsi2sd xmm0, rax 244 | movq rax, xmm0 245 | ret 246 | .LSpecialCase: 247 | mov rdx, rax 248 | and edx, 1 ; edx store the least significant bit of our input 249 | shr rax, 1 ; divide the input by 2 250 | or rax, rdx ; round to odd 251 | cvtsi2sd xmm0, rax ; convert input/2 -> double 252 | addsd xmm0, xmm0 ; multiply by 2 253 | movq rax, xmm0 ; save the result in rax 254 | ret 255 | 256 | global F32ToU64Trunc 257 | F32ToU64Trunc: 258 | movd xmm0, eax 259 | ucomiss xmm0, [rel f32_2p63] ; compare with 2^63 260 | jae .LSpecialCase 261 | cvttss2si rax, xmm0 262 | ret 263 | .LSpecialCase: 264 | subss xmm0, [rel f32_2p63] ; take out the 2^63 (should reduce output by one bit) 265 | xor rcx, rcx 266 | add rcx, 1 ; set lsb of rcx 267 | ror rcx, 1 ; set msb of rcx (clear lsb) at this point rcx == 2^63 268 | cvttss2si rax, xmm0 ; convert to integer 269 | add rax, rcx ; add in the 2^63 we took out 270 | ret 271 | 272 | global F64ToU64Trunc 273 | F64ToU64Trunc: 274 | movq xmm0, rax 275 | ucomisd xmm0, [rel f64_2p63] 276 | jae .LSpecialCase 277 | cvttsd2si rax, xmm0 278 | ret 279 | .LSpecialCase: 280 | subsd xmm0, [rel f64_2p63] ; take out the 2^63 (should reduce output by one bit) 281 | xor rcx, rcx 282 | add rcx, 1 ; set lsb of rcx 283 | ror rcx, 1 ; set msb of rcx (clear lsb) rcx == 2^63 284 | cvttsd2si rax, xmm0 ; convert to integer 285 | add rax, rcx ; add back the 2^63 we removed 286 | ret 287 | 288 | global GrowMemoryOp 289 | GrowMemoryOp: 290 | BackupVMState 291 | mov rdi, rbp 292 | mov rsi, rax 293 | ; Grow Memory eats an operand and returns an operand 294 | CallCFn GrowMemory 295 | RestoreVMState 296 | ret 297 | 298 | -------------------------------------------------------------------------------- /src/lib/layer/posix/pagealloc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../layer.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace layer 11 | { 12 | 13 | class AllocatedPageBlockUnix : public AllocatedPageBlock 14 | { 15 | public: 16 | AllocatedPageBlockUnix(void *pv, size_t cb) 17 | { 18 | assert(pv != nullptr && cb > 0); 19 | m_pv = pv; 20 | m_cb = cb; 21 | } 22 | ~AllocatedPageBlockUnix() 23 | { 24 | munmap(m_pv, m_cb); 25 | } 26 | 27 | }; 28 | 29 | std::unique_ptr ReservePages(const void *pvBaseRequested, size_t cb) 30 | { 31 | void *pv = mmap((void*)pvBaseRequested, cb, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 32 | if (pv == MAP_FAILED) 33 | throw std::bad_alloc(); 34 | return std::unique_ptr(new AllocatedPageBlockUnix(pv, cb)); 35 | } 36 | 37 | void ProtectRange(AllocatedPageBlock &block, void *pvAddrStart, size_t cbRange, PAGE_PROTECTION prot) 38 | { 39 | int unixprot = PROT_NONE; 40 | switch (prot) 41 | { 42 | case PAGE_PROTECTION::ReadOnly: 43 | unixprot = PROT_READ; 44 | break; 45 | case PAGE_PROTECTION::ReadWrite: 46 | unixprot = PROT_READ | PROT_WRITE; 47 | break; 48 | case PAGE_PROTECTION::ReadExecute: 49 | unixprot = PROT_READ | PROT_EXEC; 50 | break; 51 | case PAGE_PROTECTION::Unallocated: 52 | unixprot = PROT_NONE; 53 | break; 54 | } 55 | if (pvAddrStart == nullptr) 56 | pvAddrStart = block.PvBaseAddr(); 57 | int ret = mprotect(pvAddrStart, cbRange, unixprot); 58 | if (ret != 0) 59 | throw std::bad_alloc(); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /src/lib/layer/win32/optemplates.asm: -------------------------------------------------------------------------------- 1 | _DATA SEGMENT 2 | f32_2p63: 3 | dd 5f000000h 4 | f64_2p63: 5 | dq 43E0000000000000h 6 | _DATA ENDS 7 | _TEXT SEGMENT 8 | 9 | ExecutionControlBlock STRUCT 10 | JitWriter dq ? 11 | 12 | pfnEntry dq ? 13 | operandStack dq ? 14 | localsStack dq ? 15 | cbHeap dq ? 16 | memoryBase dq ? 17 | 18 | cFnIndirect dq ? 19 | rgfnIndirect dq ? 20 | rgFnTypeIndicies dq ? 21 | cFnTypeIndicies dq ? 22 | rgFnPtrs dq ? 23 | cFnPtrs dq ? 24 | 25 | ; Outputs and Temps 26 | stackrestore dq ? 27 | retvalue dq ? 28 | ExecutionControlBlock ENDS 29 | 30 | CallCFn MACRO fn 31 | ; call the C function passed in, assuming the stack is unbalanced 32 | 33 | ; balance the stack 34 | push r13 35 | mov r13, rsp 36 | sub rsp, 8 37 | and rsp, not 15 38 | ;Do the actual call 39 | sub rsp, 32 40 | call fn 41 | ; restore stack and go home 42 | mov rsp, r13 43 | pop r13 44 | ENDM 45 | 46 | CReentryFn PROTO 47 | GrowMemory PROTO 48 | 49 | 50 | ; REGISTERS: 51 | ; rax - top of param stack 52 | ; rdi - param stack - 1 53 | ; rsi - memory base 54 | ; rbx - parameter base 55 | ; rbp - pointer to the execution control block 56 | ; temps: rcx, rdx, r11 57 | 58 | ExternCallFnASM PROC 59 | push rdi 60 | push rsi 61 | push rbx 62 | push rbp 63 | 64 | mov rdi, (ExecutionControlBlock PTR [rcx]).operandStack 65 | mov rsi, (ExecutionControlBlock PTR [rcx]).memoryBase 66 | mov rbx, (ExecutionControlBlock PTR [rcx]).localsStack 67 | mov (ExecutionControlBlock PTR [rcx]).stackrestore, rsp 68 | mov rbp, rcx 69 | mov rax, (ExecutionControlBlock PTR [rcx]).pfnEntry 70 | call rax 71 | mov (ExecutionControlBlock PTR [rbp]).retvalue, rax 72 | mov (ExecutionControlBlock PTR [rbp]).operandStack, rdi 73 | mov (ExecutionControlBlock PTR [rbp]).localsStack, rbx 74 | 75 | mov eax, 1 76 | LDone: 77 | pop rbp 78 | pop rbx 79 | pop rsi 80 | pop rdi 81 | ret 82 | LTrapRet:: 83 | xor eax, eax ; return 0 for failed execution 84 | jmp LDone 85 | ExternCallFnASM ENDP 86 | 87 | BranchTable PROC 88 | ; jump to an argument in the table or default 89 | ; note: this is a candidate for more optimization but for now we will always perform a jump table 90 | ; 91 | ; rax - table index 92 | ; rcx - table pointer 93 | ; edx - temp 94 | ; 95 | ; Table Format: 96 | ; dword count 97 | ; dword fReturnVal 98 | ; --default_target-- 99 | ; qword distance (how many blocks we're jumping) 100 | ; qword target_addr 101 | ; --table_targets--- 102 | ; .... * count 103 | 104 | mov edx, eax ; backup the table index 105 | mov rax, [rdi] ; store the block return value (or garbage) 106 | 107 | cmp edx, [rcx] 108 | jb LNotDefault 109 | xor edx, edx 110 | jmp LDefault 111 | 112 | LNotDefault: 113 | ;else use table 114 | add edx, 1 ; skip the default 115 | shl edx, 4 ; convert table offset to a byte offset 116 | LDefault: 117 | ; at this point rdx is a byte offset into the vector table 118 | ; Lets adjust the stack to deal with the blocks we're leaving 119 | mov r11d, dword ptr [rcx+8+rdx] ; r11d = table[idx].distance 120 | lea rsp, [rsp+r11*8] ; adjust the stack for the blocks 121 | mov rax, [rdi-8] ; get the potential block return value 122 | pop rdi ; Restore the param stack to the right location 123 | ; Put the top of the param stack in rax if we don't have a return value 124 | mov r11d, dword ptr [rcx+4] 125 | test r11d, r11d 126 | jnz LHasRetValue 127 | ; Doesn't have a return value if we get here, so put the top param back in rax 128 | sub rdi, 8 129 | mov rax, [rdi] 130 | LHasRetValue: 131 | ; The universe should be setup correctly for the target block so we just need to jump 132 | jmp [rcx+16+rdx] 133 | BranchTable ENDP 134 | 135 | Trap PROC 136 | mov rsp, (ExecutionControlBlock PTR [rbp]).stackrestore 137 | jmp LTrapRet 138 | Trap ENDP 139 | 140 | WasmToC PROC 141 | ; Translates a wasm function call into a C function call for internal use 142 | ; we expect the internal function # in rcx already 143 | ; we will put the parameter base address in rdx 144 | ; the normal JIT registers are preserved by the calling convention 145 | 146 | mov rdx, rbx ; put the parameter base address in rdx (second arg) 147 | mov r8, rsi 148 | mov r9, rbp 149 | ; reserve space on stack 150 | 151 | sub rsp, 32 152 | call CReentryFn 153 | add rsp, 32 154 | ret 155 | WasmToC ENDP 156 | 157 | CompileFn PROTO 158 | CallIndirectShim PROC 159 | ; ecx contains the function index 160 | ; eax contains the type 161 | 162 | ; Convert the indirect index to the function index 163 | ; First Bounds Check 164 | cmp rcx, (ExecutionControlBlock PTR [rbp]).cFnIndirect 165 | jae LDoTrap 166 | ; Now do the conversion 167 | mov rdx, (ExecutionControlBlock PTR [rbp]).rgFnIndirect 168 | mov ecx, [rdx + rcx * 4] 169 | 170 | ; Now bounds check the type index 171 | mov rdx, (ExecutionControlBlock PTR [rbp]).rgFnTypeIndicies 172 | cmp rcx, (ExecutionControlBlock PTR [rbp]).cFnTypeIndicies 173 | jae LDoTrap 174 | 175 | mov edx, [rdx + rcx*4] ; get the callee's type 176 | cmp edx, eax ; check if its equal to the expected type 177 | jne LDoTrap ; Trap if not 178 | 179 | ; Type is validated now lets do the function call 180 | cmp rcx, (ExecutionControlBlock PTR [rbp]).cFnPtrs 181 | jae LDoTrap 182 | mov rdx, (ExecutionControlBlock PTR [rbp]).rgFnPtrs 183 | mov rax, [rdx + rcx*8] 184 | test rax, rax 185 | jz LCompileFn 186 | jmp rax 187 | ud2 188 | 189 | LDoTrap: 190 | jmp Trap ; this is just for a chance to break before going 191 | 192 | LCompileFn: 193 | lea rax, [rdx + rcx*8] 194 | push rax 195 | mov edx, ecx ; second param is the function index 196 | mov rcx, rbp ; first param the control block 197 | CallCFn CompileFn 198 | pop rax 199 | jmp qword ptr [rax] 200 | ud2 201 | CallIndirectShim ENDP 202 | 203 | U64ToF32 PROC 204 | test rax, rax 205 | js LSpecialCase 206 | cvtsi2ss xmm0, rax 207 | movd rax, xmm0 208 | ret 209 | LSpecialCase: 210 | mov rdx, rax 211 | and edx, 1 ; edx store the least significant bit of our input 212 | shr rax, 1 ; divide the input by 2 213 | or rax, rdx ; round to odd 214 | cvtsi2ss xmm0, rax ; convert input/2 -> double 215 | addss xmm0, xmm0 ; multiply by 2 216 | movq rax, xmm0 ; save the result in rax 217 | ret 218 | U64ToF32 ENDP 219 | 220 | U64ToF64 PROC 221 | test rax, rax 222 | js LSpecialCase 223 | cvtsi2sd xmm0, rax 224 | movq rax, xmm0 225 | ret 226 | LSpecialCase: 227 | mov rdx, rax 228 | and edx, 1 ; edx store the least significant bit of our input 229 | shr rax, 1 ; divide the input by 2 230 | or rax, rdx ; round to odd 231 | cvtsi2sd xmm0, rax ; convert input/2 -> double 232 | addsd xmm0, xmm0 ; multiply by 2 233 | movq rax, xmm0 ; save the result in rax 234 | ret 235 | U64ToF64 ENDP 236 | 237 | F32ToU64Trunc PROC 238 | movd xmm0, eax 239 | ucomiss xmm0, dword ptr [f32_2p63] ; compare with 2^63 240 | jae LSpecialCase 241 | cvttss2si rax, xmm0 242 | ret 243 | LSpecialCase: 244 | subss xmm0, dword ptr [f32_2p63] ; take out the 2^63 (should reduce output by one bit) 245 | xor rcx, rcx 246 | add rcx, 1 ; set lsb of rcx 247 | ror rcx, 1 ; set msb of rcx (clear lsb) at this point rcx == 2^63 248 | cvttss2si rax, xmm0 ; convert to integer 249 | add rax, rcx ; add in the 2^63 we took out 250 | ret 251 | F32ToU64Trunc ENDP 252 | 253 | F64ToU64Trunc PROC 254 | movq xmm0, rax 255 | ucomisd xmm0, qword ptr [f64_2p63] 256 | jae LSpecialCase 257 | cvttsd2si rax, xmm0 258 | ret 259 | LSpecialCase: 260 | subsd xmm0, qword ptr [f64_2p63] ; take out the 2^63 (should reduce output by one bit) 261 | xor rcx, rcx 262 | add rcx, 1 ; set lsb of rcx 263 | ror rcx, 1 ; set msb of rcx (clear lsb) rcx == 2^63 264 | cvttsd2si rax, xmm0 ; convert to integer 265 | add rax, rcx ; add back the 2^63 we removed 266 | ret 267 | F64ToU64Trunc ENDP 268 | 269 | GrowMemoryOp PROC 270 | mov rcx, rbp 271 | mov rdx, rax 272 | ; Grow Memory eats an operand and returns an operand 273 | CallCFn GrowMemory 274 | ret 275 | GrowMemoryOp ENDP 276 | 277 | _TEXT ENDS 278 | 279 | END -------------------------------------------------------------------------------- /src/lib/layer/win32/pagealloc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../layer.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace layer 10 | { 11 | 12 | class AllocatedPageBlockWindows : public AllocatedPageBlock 13 | { 14 | public: 15 | AllocatedPageBlockWindows(void *pv, size_t cb) 16 | { 17 | assert(pv != nullptr && cb > 0); 18 | m_pv = pv; 19 | m_cb = cb; 20 | } 21 | ~AllocatedPageBlockWindows() 22 | { 23 | VirtualFree(m_pv, 0, MEM_RELEASE); 24 | } 25 | 26 | }; 27 | 28 | std::unique_ptr ReservePages(const void *pvBaseRequested, size_t cb) 29 | { 30 | void *pv = VirtualAlloc((void*)pvBaseRequested, cb, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); 31 | if (pv == nullptr) 32 | throw std::bad_alloc(); 33 | return std::make_unique(pv, cb); 34 | } 35 | 36 | void ProtectRange(AllocatedPageBlock &block, void *pvAddrStart, size_t cbRange, PAGE_PROTECTION prot) 37 | { 38 | int winprot = PAGE_NOACCESS; 39 | switch (prot) 40 | { 41 | case PAGE_PROTECTION::ReadOnly: 42 | winprot = PAGE_READONLY; 43 | break; 44 | case PAGE_PROTECTION::ReadWrite: 45 | winprot = PAGE_READWRITE; 46 | break; 47 | case PAGE_PROTECTION::ReadExecute: 48 | winprot = PAGE_EXECUTE_READ; 49 | break; 50 | case PAGE_PROTECTION::Unallocated: 51 | winprot = PAGE_NOACCESS; 52 | break; 53 | } 54 | if (pvAddrStart == nullptr) 55 | pvAddrStart = block.PvBaseAddr(); 56 | DWORD dwT; 57 | bool ret = VirtualProtect(pvAddrStart, cbRange, winprot, &dwT); 58 | if (!ret) 59 | throw std::bad_alloc(); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /src/lib/numeric_cast.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class numeric_cast_exception 5 | {}; 6 | 7 | template 8 | T_DST numeric_cast_base(T_SRC src) 9 | { 10 | T_DST dst = static_cast(src); 11 | if (dst != src) 12 | throw 0; 13 | return dst; 14 | } 15 | 16 | template 17 | T_DST numeric_cast_dispatch(T_SRC src, std::true_type) 18 | { 19 | static_assert(!std::is_same::value, "Tautological cast"); 20 | return numeric_cast_base(src); 21 | } 22 | 23 | template 24 | T_DST numeric_cast_dispatch(T_SRC src, std::false_type) 25 | { 26 | return numeric_cast_base(src); 27 | } 28 | 29 | template 30 | T_DST numeric_cast(T_SRC &&src) 31 | { 32 | return numeric_cast_dispatch(src, std::integral_constant()); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/lib/rt_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Exceptions.h" 3 | #include "BuiltinFunctions.h" 4 | #include "ExecutionControlBlock.h" 5 | #include "JitWriter.h" 6 | #include "WasmContext.h" 7 | #ifdef _MSC_VER 8 | #include 9 | #else 10 | #include 11 | #define _write write 12 | #endif 13 | 14 | bool FEqualProto(const BuiltinExport &builtinexport, const FunctionTypeEntry &fnt) 15 | { 16 | // check return 17 | switch (builtinexport.retT) 18 | { 19 | case value_type::none: 20 | if (fnt.fHasReturnValue) 21 | return false; 22 | break; 23 | 24 | default: 25 | if (builtinexport.retT != fnt.return_type) 26 | return false; 27 | } 28 | 29 | auto itrParamT = builtinexport.vecarg.begin(); 30 | for (size_t iparam = 0; iparam < fnt.cparams; ++iparam) 31 | { 32 | if (itrParamT == builtinexport.vecarg.end()) 33 | return false; 34 | if (*itrParamT != fnt.rgparam_type[iparam]) 35 | return false; 36 | ++itrParamT; 37 | } 38 | return (itrParamT == builtinexport.vecarg.end()); 39 | } 40 | 41 | uint64_t wasm_close_fd(uint64_t *pvArgs, uint8_t *pvMemBase) 42 | { 43 | return 0; 44 | } 45 | 46 | // size_t wasm_write_fd(int fd, void *pv, size_t cb) 47 | uint64_t wasm_write_fd(uint64_t *rgArgs, uint8_t *pvMemBase) 48 | { 49 | int fd = (int)rgArgs[0]; 50 | const uint8_t *pv = pvMemBase + rgArgs[1]; 51 | uint32_t cb = static_cast(rgArgs[2]); 52 | 53 | return _write(fd, pv, cb); 54 | } 55 | 56 | // long long wasm_llseek_fd(int fd, long long offset, int whence) 57 | uint64_t wasm_llseek_fd(uint64_t *pvArgs, uint8_t *pvMemBase) 58 | { 59 | return 0; 60 | } 61 | 62 | BuiltinExport BuiltinMap[] = { 63 | { "wasm_close_fd", value_type::i32, { value_type::i32 }, wasm_close_fd }, 64 | { "wasm_write_fd", value_type::i32, { value_type::i32, value_type::i32, value_type::i32}, wasm_write_fd }, 65 | { "wasm_llseek_fd", value_type::i32, { value_type::i32, value_type::i64, value_type::i32 }, wasm_llseek_fd } 66 | }; 67 | 68 | int IBuiltinFromName(const std::string &strName) 69 | { 70 | for (size_t ifn = 0; ifn < _countof(BuiltinMap); ++ifn) 71 | { 72 | if (strName == BuiltinMap[ifn].szName) 73 | return ifn; 74 | } 75 | Verify(false); 76 | abort(); 77 | } 78 | 79 | extern "C" uint64_t CReentryFn(int ifn, uint64_t *pvArgs, uint8_t *pvMemBase, ExecutionControlBlock *pecb) 80 | { 81 | return pecb->pjitWriter->CReentryFn(ifn, pvArgs, pvMemBase, pecb); 82 | } 83 | uint64_t JitWriter::CReentryFn(int ifn, uint64_t *pvArgs, uint8_t *pvMemBase, ExecutionControlBlock *pecb) 84 | { 85 | Verify(ifn >= 0 && (size_t)ifn < m_pctxt->m_vecimports.size()); 86 | ifn = m_pctxt->m_vecimports[ifn]; 87 | Verify(ifn >= 0 && (size_t)ifn < _countof(BuiltinMap)); 88 | BuiltinMap[ifn].pfn(pvArgs, pvMemBase); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /src/lib/safe_access.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "safe_access.h" 3 | #include "Exceptions.h" 4 | 5 | template<> 6 | void fread_struct(varuint32 *dst, FILE *pf, size_t cstructs) 7 | { 8 | for (size_t istruct = 0; istruct < cstructs; ++istruct) 9 | { 10 | uint8_t val; 11 | varuint32 &dstCur = dst[istruct]; 12 | dstCur = 0; 13 | uint32_t shift = 0; 14 | do 15 | { 16 | fread_struct(&val, pf); 17 | dstCur |= (val & 0x7F) << shift; 18 | shift += 7; 19 | } while (val & 0x80); 20 | } 21 | } 22 | 23 | template<> 24 | varuint32 safe_read_buffer(const uint8_t **prgb, size_t *pcb) 25 | { 26 | uint32_t ret = 0; 27 | uint32_t shift = 0; 28 | for (int ib = 0; ib < 5 && *pcb > 0; ++ib) 29 | { 30 | ret |= (**prgb & 0x7F) << shift; 31 | shift += 7; 32 | bool fContinue = !!(**prgb & 0x80); 33 | (*prgb)++; 34 | (*pcb)--; 35 | if (!fContinue) 36 | break; 37 | } 38 | return ret; 39 | } 40 | 41 | template<> 42 | varuint64 safe_read_buffer(const uint8_t **prgb, size_t *pcb) 43 | { 44 | uint64_t ret = 0; 45 | uint32_t shift = 0; 46 | for (int ib = 0; ib < 10 && *pcb > 0; ++ib) 47 | { 48 | ret |= static_cast(**prgb & 0x7F) << shift; 49 | shift += 7; 50 | bool fContinue = !!(**prgb & 0x80); 51 | (*prgb)++; 52 | (*pcb)--; 53 | if (!fContinue) 54 | break; 55 | } 56 | return ret; 57 | } 58 | 59 | template<> 60 | varint32 safe_read_buffer(const uint8_t **prgb, size_t *pcb) 61 | { 62 | int32_t ret = 0; 63 | uint32_t shift = 0; 64 | uint8_t byteLast = 0; 65 | for (int ib = 0; ib < 5 && *pcb > 0; ++ib) 66 | { 67 | byteLast = **prgb; 68 | ret |= (byteLast & 0x7F) << shift; 69 | shift += 7; 70 | bool fContinue = !!(**prgb & 0x80); 71 | (*prgb)++; 72 | (*pcb)--; 73 | if (!fContinue) 74 | break; 75 | } 76 | 77 | /* sign bit of byte is second high order bit (0x40) */ 78 | if ((shift < 32) && (byteLast & 0x40)) 79 | ret |= (~0 << shift); // sign extend 80 | return ret; 81 | } 82 | 83 | template<> 84 | varint64 safe_read_buffer(const uint8_t **prgb, size_t *pcb) 85 | { 86 | int64_t ret = 0; 87 | uint64_t shift = 0; 88 | uint8_t byteLast = 0; 89 | for (int ib = 0; ib < 10 && *pcb > 0; ++ib) 90 | { 91 | byteLast = **prgb; 92 | ret |= static_cast(byteLast & 0x7F) << shift; 93 | shift += 7; 94 | bool fContinue = !!(**prgb & 0x80); 95 | (*prgb)++; 96 | (*pcb)--; 97 | if (!fContinue) 98 | break; 99 | } 100 | 101 | /* sign bit of byte is second high order bit (0x40) */ 102 | if ((shift < 64) && (byteLast & 0x40)) 103 | ret |= (~0 << shift); // sign extend 104 | return ret; 105 | } 106 | 107 | 108 | template<> 109 | std::string safe_read_buffer(const uint8_t **prgb, size_t *pcb) 110 | { 111 | varuint32 cch = safe_read_buffer(prgb, pcb); 112 | std::string str; 113 | if (*pcb < cch) 114 | throw 0; 115 | str.assign(*prgb, *prgb + cch); 116 | Verify(strlen(str.c_str()) <= cch); 117 | *prgb += cch; 118 | *pcb -= cch; 119 | return str; 120 | } -------------------------------------------------------------------------------- /src/lib/safe_access.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "wasm_types.h" 3 | 4 | template 5 | void fread_struct(T *dst, FILE *pf, size_t cstructs = 1) 6 | { 7 | if (cstructs == 0) 8 | return; 9 | size_t celem = 0; 10 | do 11 | { 12 | size_t creadT = fread(dst + celem, sizeof(T), cstructs, pf); 13 | if (creadT == 0) 14 | throw 0; 15 | celem += creadT; 16 | } while (celem < cstructs); 17 | } 18 | 19 | template<> 20 | void fread_struct(varuint32 *dst, FILE *pf, size_t cstructs); 21 | 22 | template 23 | T safe_read_buffer(const uint8_t ** prgb, size_t *pcb) 24 | { 25 | if (*pcb < sizeof(T)) 26 | throw 0; 27 | const T *tr = reinterpret_cast(*prgb); 28 | *prgb += sizeof(T); 29 | *pcb -= sizeof(T); 30 | return *tr; 31 | } 32 | 33 | template<> 34 | varuint32 safe_read_buffer(const uint8_t **prgb, size_t *pcb); 35 | 36 | template<> 37 | varuint64 safe_read_buffer(const uint8_t **prgb, size_t *pcb); 38 | 39 | template<> 40 | varint32 safe_read_buffer(const uint8_t **prgb, size_t *pcb); 41 | 42 | template<> 43 | varint64 safe_read_buffer(const uint8_t **prgb, size_t *pcb); 44 | 45 | template<> 46 | std::string safe_read_buffer(const uint8_t **prgb, size_t *pcb); 47 | 48 | template void safe_copy_buffer(T *rgdst, size_t celem, const uint8_t **prgb, size_t *pcb) 49 | { 50 | if (*pcb < (sizeof(T)*celem)) 51 | throw 0; 52 | memcpy(rgdst, *prgb, sizeof(T)*celem); 53 | *prgb += sizeof(T)*celem; 54 | *pcb -= sizeof(T)*celem; 55 | } -------------------------------------------------------------------------------- /src/lib/sal_stubs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _Out_ 4 | -------------------------------------------------------------------------------- /src/lib/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #define _CRT_SECURE_NO_WARNINGS 1 9 | #define NOMINMAX 1 10 | #ifdef _MSC_VER 11 | #include "targetver.h" 12 | #include 13 | #else 14 | #include "sal_stubs.h" 15 | #endif 16 | 17 | #ifdef _MSC_VER 18 | #define EXPORT __declspec(dllexport) 19 | #else 20 | #define EXPORT 21 | #endif 22 | 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "layer/layer.h" 36 | #include "../compiler_bridges.h" 37 | -------------------------------------------------------------------------------- /src/lib/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /src/lib/wasm_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static const uint32_t WASM_PAGE_SIZE = 64 * 1024; 4 | 5 | template 6 | struct varuintT 7 | { 8 | varuintT() = default; 9 | varuintT(T val) 10 | : val(val) 11 | {} 12 | 13 | operator T() const 14 | { 15 | return val; 16 | } 17 | 18 | varuintT operator|=(T other) 19 | { 20 | val |= other; 21 | return *this; 22 | } 23 | 24 | template 25 | varuintT &operator-=(TT other) 26 | { 27 | val -= other; 28 | return *this; 29 | } 30 | 31 | T val; 32 | }; 33 | 34 | using varuint32 = varuintT; 35 | using varuint64 = varuintT; 36 | 37 | template 38 | struct varintT 39 | { 40 | varintT() = default; 41 | varintT(T val) 42 | : val(val) 43 | {} 44 | 45 | operator T() const 46 | { 47 | return val; 48 | } 49 | 50 | varintT &operator|=(T other) 51 | { 52 | val |= other; 53 | return *this; 54 | } 55 | 56 | template 57 | varintT &operator-=(TT other) 58 | { 59 | val -= other; 60 | return *this; 61 | } 62 | 63 | T val; 64 | }; 65 | 66 | using varint32 = varintT; 67 | using varint64 = varintT; 68 | 69 | typedef uint8_t block_type; 70 | 71 | struct wasm_file_header 72 | { 73 | uint32_t magic; 74 | uint32_t version; 75 | }; 76 | 77 | enum class value_type : uint8_t 78 | { 79 | none = 0, 80 | i32 = 0x7f, 81 | i64 = 0x7e, 82 | f32 = 0x7d, 83 | f64 = 0x7c, 84 | anyfunc = 0x70, 85 | func = 0x60, 86 | empty_block = 0x40 87 | }; 88 | 89 | enum class section_types : uint8_t 90 | { 91 | Custom = 0, 92 | Type = 1, 93 | Import = 2, 94 | Function = 3, 95 | Table = 4, 96 | Memory = 5, 97 | Global = 6, 98 | Export = 7, 99 | Start = 8, 100 | Element = 9, 101 | Code = 10, 102 | Data = 11, 103 | }; 104 | 105 | enum class external_kind : uint8_t 106 | { 107 | Function = 0, 108 | Table = 1, 109 | Memory = 2, 110 | Global = 3, 111 | }; 112 | 113 | enum class elem_type : uint8_t 114 | { 115 | anyfunc = 0x70, 116 | }; 117 | 118 | enum class opcode : uint8_t 119 | { 120 | unreachable = 0x00, 121 | nop = 0x01, 122 | 123 | block = 0x02, 124 | loop = 0x03, 125 | IF = 0x04, 126 | ELSE = 0x05, 127 | 128 | br = 0x0c, 129 | br_if = 0x0d, 130 | br_table = 0x0e, 131 | 132 | ret = 0x0f, 133 | call = 0x10, 134 | call_indirect = 0x11, 135 | 136 | drop = 0x1a, 137 | select = 0x1b, 138 | 139 | get_local = 0x20, 140 | set_local = 0x21, 141 | tee_local = 0x22, 142 | get_global = 0x23, 143 | set_global = 0x24, 144 | 145 | i32_load = 0x28, 146 | i64_load = 0x29, 147 | f32_load = 0x2a, 148 | f64_load = 0x2b, 149 | i32_load8_s = 0x2c, 150 | i32_load8_u = 0x2d, 151 | i32_load16_s = 0x2e, 152 | i32_load16_u = 0x2f, 153 | i64_load8_s = 0x30, 154 | i64_load8_u = 0x31, 155 | i64_load16_s = 0x32, 156 | i64_load16_u = 0x33, 157 | i64_load32_s = 0x34, 158 | i64_load32_u = 0x35, 159 | 160 | i32_store = 0x36, 161 | i64_store = 0x37, 162 | f32_store = 0x38, 163 | f64_store = 0x39, 164 | i32_store8 = 0x3a, 165 | i32_store16 = 0x3b, 166 | i64_store8 = 0x3c, 167 | i64_store16 = 0x3d, 168 | i64_store32 = 0x3e, 169 | 170 | current_memory = 0x3f, 171 | grow_memory = 0x40, 172 | 173 | i32_const = 0x41, 174 | i64_const = 0x42, 175 | f32_const = 0x43, 176 | f64_const = 0x44, 177 | 178 | i32_eqz = 0x45, 179 | i32_eq = 0x46, 180 | i32_ne = 0x47, 181 | i32_lt_s = 0x48, 182 | i32_lt_u = 0x49, 183 | i32_gt_s = 0x4a, 184 | i32_gt_u = 0x4b, 185 | i32_le_s = 0x4c, 186 | i32_le_u = 0x4d, 187 | i32_ge_s = 0x4e, // signed >= 188 | i32_ge_u = 0x4f, 189 | 190 | i64_eqz = 0x50, 191 | i64_eq = 0x51, 192 | i64_ne = 0x52, 193 | i64_lt_s = 0x53, 194 | i64_lt_u = 0x54, 195 | i64_gt_s = 0x55, 196 | i64_gt_u = 0x56, 197 | i64_le_s = 0x57, 198 | i64_le_u = 0x58, 199 | i64_ge_s = 0x59, 200 | i64_ge_u = 0x5a, 201 | 202 | f32_eq = 0x5b, 203 | f32_ne = 0x5c, 204 | f32_lt = 0x5d, 205 | f32_gt = 0x5e, 206 | f32_le = 0x5f, 207 | f32_ge = 0x60, 208 | 209 | f64_eq = 0x61, 210 | f64_ne = 0x62, 211 | f64_lt = 0x63, 212 | f64_gt = 0x64, 213 | f64_le = 0x65, 214 | f64_ge = 0x66, 215 | 216 | i32_clz = 0x67, 217 | i32_ctz = 0x68, 218 | i32_popcnt = 0x69, 219 | i32_add = 0x6a, 220 | i32_sub = 0x6b, 221 | i32_mul = 0x6c, 222 | i32_div_s = 0x6d, 223 | i32_div_u = 0x6e, 224 | i32_rem_s = 0x6f, 225 | i32_rem_u = 0x70, 226 | i32_and = 0x71, 227 | i32_or = 0x72, 228 | i32_xor = 0x73, 229 | i32_shl = 0x74, 230 | i32_shr_s = 0x75, 231 | i32_shr_u = 0x76, 232 | i32_rotl = 0x77, 233 | i32_rotr = 0x78, 234 | 235 | i64_clz = 0x79, 236 | i64_ctz = 0x7a, 237 | i64_popcnt = 0x7b, 238 | i64_add = 0x7c, 239 | i64_sub = 0x7d, 240 | i64_mul = 0x7e, 241 | i64_div_s = 0x7f, 242 | i64_div_u = 0x80, 243 | i64_rem_s = 0x81, 244 | i64_rem_u = 0x82, 245 | i64_and = 0x83, 246 | i64_or = 0x84, 247 | i64_xor = 0x85, 248 | i64_shl = 0x86, 249 | i64_shr_s = 0x87, 250 | i64_shr_u = 0x88, 251 | i64_rotl = 0x89, 252 | i64_rotr = 0x8a, 253 | 254 | f32_abs = 0x8b, 255 | f32_neg = 0x8c, 256 | f32_ceil = 0x8d, 257 | f32_floor = 0x8e, 258 | f32_trunc = 0x8f, 259 | f32_nearest = 0x90, 260 | f32_sqrt = 0x91, 261 | f32_add = 0x92, 262 | f32_sub = 0x93, 263 | f32_mul = 0x94, 264 | f32_div = 0x95, 265 | f32_min = 0x96, 266 | f32_max = 0x97, 267 | f32_copysign = 0x98, 268 | 269 | f64_abs = 0x99, 270 | f64_neg = 0x9a, 271 | f64_ceil = 0x9b, 272 | f64_floor = 0x9c, 273 | f64_trunc = 0x9d, 274 | f64_nearest = 0x9e, 275 | f64_sqrt = 0x9f, 276 | f64_add = 0xa0, 277 | f64_sub = 0xa1, 278 | f64_mul = 0xa2, 279 | f64_div = 0xa3, 280 | f64_min = 0xa4, 281 | f64_max = 0xa5, 282 | f64_copysign = 0xa6, 283 | 284 | i32_wrap_i64 = 0xa7, 285 | i32_trunc_s_f32 = 0xa8, 286 | i32_trunc_u_f32 = 0xa9, 287 | i32_trunc_s_f64 = 0xaa, 288 | i32_trunc_u_f64 = 0xab, 289 | i64_extend_s_i32 = 0xac, 290 | i64_extend_u_i32 = 0xad, 291 | i64_trunc_s_f32 = 0xae, 292 | i64_trunc_u_f32 = 0xaf, 293 | i64_trunc_s_f64 = 0xb0, 294 | i64_trunc_u_f64 = 0xb1, 295 | f32_convert_s_i32 = 0xb2, 296 | f32_convert_u_i32 = 0xb3, 297 | f32_convert_s_i64 = 0xb4, 298 | f32_convert_u_i64 = 0xb5, 299 | f32_demote_f64 = 0xb6, 300 | f64_convert_s_i32 = 0xb7, 301 | f64_convert_u_i32 = 0xb8, 302 | f64_convert_s_i64 = 0xb9, 303 | f64_convert_u_i64 = 0xba, 304 | f64_promote_f32 = 0xbb, 305 | 306 | i32_reinterpret_f32 = 0xbc, 307 | i64_reinterpret_f64 = 0xbd, 308 | f32_reinterpret_i32 = 0xbe, 309 | f64_reinterpret_i64 = 0xbf, 310 | 311 | end = 0x0b, 312 | }; 313 | 314 | 315 | struct section_header 316 | { 317 | section_types id; 318 | varuint32 payload_len; 319 | }; 320 | 321 | struct resizable_limits 322 | { 323 | bool fMaxSet; 324 | uint32_t initial_size; 325 | uint32_t maximum_size; 326 | }; 327 | 328 | struct table_type 329 | { 330 | elem_type type; 331 | resizable_limits limits; 332 | }; 333 | 334 | struct export_entry 335 | { 336 | std::string strName; 337 | external_kind kind; 338 | uint32_t index; 339 | }; 340 | 341 | struct local_entry 342 | { 343 | uint32_t count; 344 | value_type type; 345 | }; 346 | 347 | 348 | 349 | template 350 | struct free_delete 351 | { 352 | void operator()(T* pT) 353 | { 354 | pT->~T(); 355 | free(pT); 356 | } 357 | }; 358 | 359 | struct FunctionTypeEntry 360 | { 361 | private: 362 | FunctionTypeEntry() = default; 363 | 364 | public: 365 | using unique_pfne_ptr = std::unique_ptr >; 366 | static unique_pfne_ptr CreateFunctionEntry(uint32_t cparams) 367 | { 368 | FunctionTypeEntry *pfne = (FunctionTypeEntry*)malloc(sizeof(FunctionTypeEntry) + (sizeof(value_type) * cparams)); // this will allocate 1 extra value_type... who cares 369 | pfne->cparams = cparams; 370 | return unique_pfne_ptr(pfne); 371 | } 372 | 373 | bool operator==(const FunctionTypeEntry &other) 374 | { 375 | if (fHasReturnValue != other.fHasReturnValue) 376 | return false; 377 | if (fHasReturnValue) 378 | { 379 | if (return_type != other.return_type) 380 | return false; 381 | } 382 | if (cparams != other.cparams) 383 | return false; 384 | for (uint32_t iparam = 0; iparam < cparams; ++iparam) 385 | { 386 | if (rgparam_type[iparam] != other.rgparam_type[iparam]) 387 | return false; 388 | } 389 | return true; 390 | } 391 | 392 | bool fHasReturnValue; 393 | value_type return_type; 394 | uint32_t cparams; 395 | value_type rgparam_type[1]; 396 | }; 397 | 398 | struct FunctionCodeEntry 399 | { 400 | private: 401 | FunctionCodeEntry() = default; 402 | 403 | public: 404 | using unique_pfne_ptr = std::unique_ptr>; 405 | static unique_pfne_ptr CreateFunctionCodeEntry(uint32_t clocals) 406 | { 407 | FunctionCodeEntry *pfnce = (FunctionCodeEntry*)malloc(sizeof(FunctionCodeEntry) + (sizeof(local_entry) * clocals)); // this will allocate 1 extra local_entry... who cares 408 | new (pfnce) FunctionCodeEntry(); // ensure the vec is initialized 409 | pfnce->clocalVars = clocals; 410 | return unique_pfne_ptr(pfnce); 411 | } 412 | 413 | uint32_t clocalVars; 414 | std::vector vecbytecode; 415 | local_entry rglocals[1]; 416 | }; 417 | 418 | -------------------------------------------------------------------------------- /src/runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(wasmrt VERSION 0.1) 2 | file(GLOB GENERIC_SOURCES *.cpp) 3 | 4 | include_directories("../lib/") 5 | add_executable(wasmrt ${GENERIC_SOURCES}) 6 | target_link_libraries(wasmrt wasm) 7 | -------------------------------------------------------------------------------- /src/runtime/main.cpp: -------------------------------------------------------------------------------- 1 | // webasmRT.cpp : Defines the entry point for the console application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | if (argc < 2) 11 | { 12 | fprintf(stderr, "Usage: module.wasm\n"); 13 | return EXIT_FAILURE; 14 | } 15 | FILE *pf = fopen(argv[1], "rb"); 16 | if (pf == nullptr) 17 | { 18 | perror("Could not open wasm module"); 19 | return EXIT_FAILURE; 20 | } 21 | 22 | WasmContext ctxt; 23 | ctxt.LoadModule(pf); 24 | fclose(pf); 25 | 26 | ctxt.CallFunction("main"); 27 | 28 | return EXIT_SUCCESS; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/runtime/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #define _CRT_SECURE_NO_WARNINGS 1 9 | #define NOMINMAX 1 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "../compiler_bridges.h" 23 | -------------------------------------------------------------------------------- /src/testhost/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(wasm_test VERSION 0.1) 2 | file(GLOB GENERIC_SOURCES *.cpp) 3 | 4 | include_directories("../lib/") 5 | add_executable(testhost ${GENERIC_SOURCES}) 6 | target_link_libraries(testhost wasm) 7 | -------------------------------------------------------------------------------- /test/scripts/compile_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | def ProcessTestFile(strFile): 5 | f = open(strFile) 6 | 7 | 8 | for file in os.listdir("../spec_tests"): 9 | if (not file.endswith(".wast")): 10 | continue 11 | ProcessTestFile("../spec_tests" + file) 12 | -------------------------------------------------------------------------------- /test/spec_tests/address.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 1) 3 | (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz") 4 | 5 | (func (export "good1") (param $i i32) (result i32) 6 | (i32.load8_u offset=0 (get_local $i)) ;; 97 'a' 7 | ) 8 | (func (export "good2") (param $i i32) (result i32) 9 | (i32.load8_u offset=1 (get_local $i)) ;; 98 'b' 10 | ) 11 | (func (export "good3") (param $i i32) (result i32) 12 | (i32.load8_u offset=2 (get_local $i)) ;; 99 'c' 13 | ) 14 | (func (export "good4") (param $i i32) (result i32) 15 | (i32.load8_u offset=25 (get_local $i)) ;; 122 'z' 16 | ) 17 | 18 | (func (export "good5") (param $i i32) (result i32) 19 | (i32.load16_u offset=0 (get_local $i)) ;; 25185 'ab' 20 | ) 21 | (func (export "good6") (param $i i32) (result i32) 22 | (i32.load16_u align=1 (get_local $i)) ;; 25185 'ab' 23 | ) 24 | (func (export "good7") (param $i i32) (result i32) 25 | (i32.load16_u offset=1 align=1 (get_local $i)) ;; 25442 'bc' 26 | ) 27 | (func (export "good8") (param $i i32) (result i32) 28 | (i32.load16_u offset=2 (get_local $i)) ;; 25699 'cd' 29 | ) 30 | (func (export "good9") (param $i i32) (result i32) 31 | (i32.load16_u offset=25 align=1 (get_local $i)) ;; 122 'z\0' 32 | ) 33 | 34 | (func (export "good10") (param $i i32) (result i32) 35 | (i32.load offset=0 (get_local $i)) ;; 1684234849 'abcd' 36 | ) 37 | (func (export "good11") (param $i i32) (result i32) 38 | (i32.load offset=1 align=1 (get_local $i)) ;; 1701077858 'bcde' 39 | ) 40 | (func (export "good12") (param $i i32) (result i32) 41 | (i32.load offset=2 align=2 (get_local $i)) ;; 1717920867 'cdef' 42 | ) 43 | (func (export "good13") (param $i i32) (result i32) 44 | (i32.load offset=25 align=1 (get_local $i)) ;; 122 'z\0\0\0' 45 | ) 46 | 47 | (func (export "bad") (param $i i32) 48 | (drop (i32.load offset=4294967295 (get_local $i))) 49 | ) 50 | ) 51 | 52 | (assert_return (invoke "good1" (i32.const 0)) (i32.const 97)) 53 | (assert_return (invoke "good2" (i32.const 0)) (i32.const 98)) 54 | (assert_return (invoke "good3" (i32.const 0)) (i32.const 99)) 55 | (assert_return (invoke "good4" (i32.const 0)) (i32.const 122)) 56 | (assert_return (invoke "good5" (i32.const 0)) (i32.const 25185)) 57 | (assert_return (invoke "good6" (i32.const 0)) (i32.const 25185)) 58 | (assert_return (invoke "good7" (i32.const 0)) (i32.const 25442)) 59 | (assert_return (invoke "good8" (i32.const 0)) (i32.const 25699)) 60 | (assert_return (invoke "good9" (i32.const 0)) (i32.const 122)) 61 | (assert_return (invoke "good10" (i32.const 0)) (i32.const 1684234849)) 62 | (assert_return (invoke "good11" (i32.const 0)) (i32.const 1701077858)) 63 | (assert_return (invoke "good12" (i32.const 0)) (i32.const 1717920867)) 64 | (assert_return (invoke "good13" (i32.const 0)) (i32.const 122)) 65 | 66 | (assert_return (invoke "good1" (i32.const 65507)) (i32.const 0)) 67 | (assert_return (invoke "good2" (i32.const 65507)) (i32.const 0)) 68 | (assert_return (invoke "good3" (i32.const 65507)) (i32.const 0)) 69 | (assert_return (invoke "good4" (i32.const 65507)) (i32.const 0)) 70 | (assert_return (invoke "good5" (i32.const 65507)) (i32.const 0)) 71 | (assert_return (invoke "good6" (i32.const 65507)) (i32.const 0)) 72 | (assert_return (invoke "good7" (i32.const 65507)) (i32.const 0)) 73 | (assert_return (invoke "good8" (i32.const 65507)) (i32.const 0)) 74 | (assert_return (invoke "good9" (i32.const 65507)) (i32.const 0)) 75 | (assert_return (invoke "good10" (i32.const 65507)) (i32.const 0)) 76 | (assert_return (invoke "good11" (i32.const 65507)) (i32.const 0)) 77 | (assert_return (invoke "good12" (i32.const 65507)) (i32.const 0)) 78 | (assert_return (invoke "good13" (i32.const 65507)) (i32.const 0)) 79 | 80 | (assert_return (invoke "good1" (i32.const 65508)) (i32.const 0)) 81 | (assert_return (invoke "good2" (i32.const 65508)) (i32.const 0)) 82 | (assert_return (invoke "good3" (i32.const 65508)) (i32.const 0)) 83 | (assert_return (invoke "good4" (i32.const 65508)) (i32.const 0)) 84 | (assert_return (invoke "good5" (i32.const 65508)) (i32.const 0)) 85 | (assert_return (invoke "good6" (i32.const 65508)) (i32.const 0)) 86 | (assert_return (invoke "good7" (i32.const 65508)) (i32.const 0)) 87 | (assert_return (invoke "good8" (i32.const 65508)) (i32.const 0)) 88 | (assert_return (invoke "good9" (i32.const 65508)) (i32.const 0)) 89 | (assert_return (invoke "good10" (i32.const 65508)) (i32.const 0)) 90 | (assert_return (invoke "good11" (i32.const 65508)) (i32.const 0)) 91 | (assert_return (invoke "good12" (i32.const 65508)) (i32.const 0)) 92 | (assert_trap (invoke "good13" (i32.const 65508)) "out of bounds memory access") 93 | 94 | (assert_trap (invoke "bad" (i32.const 0)) "out of bounds memory access") 95 | (assert_trap (invoke "bad" (i32.const 1)) "out of bounds memory access") 96 | 97 | (assert_malformed 98 | (module quote 99 | "(memory 1)" 100 | "(func (drop (i32.load offset=4294967296 (i32.const 0))))" 101 | ) 102 | "i32 constant" 103 | ) 104 | -------------------------------------------------------------------------------- /test/spec_tests/align.wast: -------------------------------------------------------------------------------- 1 | (assert_malformed 2 | (module quote 3 | "(module (memory 0) (func (drop (i64.load align=0 (i32.const 0)))))" 4 | ) 5 | "alignment" 6 | ) 7 | (assert_malformed 8 | (module quote 9 | "(module (memory 0) (func (drop (i64.load align=7 (i32.const 0)))))" 10 | ) 11 | "alignment" 12 | ) 13 | (assert_invalid 14 | (module (memory 0) (func (drop (i64.load align=16 (i32.const 0))))) 15 | "alignment" 16 | ) 17 | 18 | (assert_malformed 19 | (module quote 20 | "(module (memory 0) (func (i64.store align=0 (i32.const 0) (i64.const 0))))" 21 | ) 22 | "alignment" 23 | ) 24 | (assert_malformed 25 | (module quote 26 | "(module (memory 0) (func (i64.store align=5 (i32.const 0) (i64.const 0))))" 27 | ) 28 | "alignment" 29 | ) 30 | (assert_invalid 31 | (module (memory 0) (func (i64.store align=16 (i32.const 0) (i64.const 0)))) 32 | "alignment" 33 | ) 34 | -------------------------------------------------------------------------------- /test/spec_tests/binary.wast: -------------------------------------------------------------------------------- 1 | (module binary "\00asm\01\00\00\00") 2 | (module binary "\00asm" "\01\00\00\00") 3 | (module $M1 binary "\00asm\01\00\00\00") 4 | (module $M2 binary "\00asm" "\01\00\00\00") 5 | 6 | (assert_malformed (module binary "") "unexpected end") 7 | (assert_malformed (module binary "\01") "unexpected end") 8 | (assert_malformed (module binary "\00as") "unexpected end") 9 | (assert_malformed (module binary "asm\00") "magic header not detected") 10 | (assert_malformed (module binary "msa\00") "magic header not detected") 11 | (assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") 12 | (assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") 13 | (assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") 14 | (assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") 15 | (assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") 16 | (assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") 17 | (assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") 18 | (assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") 19 | 20 | ;; 8-byte endian-reversed. 21 | (assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") 22 | 23 | ;; Middle-endian byte orderings. 24 | (assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") 25 | (assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") 26 | 27 | ;; Upper-cased. 28 | (assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") 29 | 30 | ;; EBCDIC-encoded magic. 31 | (assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") 32 | 33 | ;; Leading UTF-8 BOM. 34 | (assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") 35 | 36 | (assert_malformed (module binary "\00asm") "unexpected end") 37 | (assert_malformed (module binary "\00asm\01") "unexpected end") 38 | (assert_malformed (module binary "\00asm\01\00\00") "unexpected end") 39 | (assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") 40 | (assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") 41 | (assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") 42 | (assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") 43 | (assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") 44 | (assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") 45 | -------------------------------------------------------------------------------- /test/spec_tests/block.wast: -------------------------------------------------------------------------------- 1 | ;; Test `block` operator 2 | 3 | (module 4 | ;; Auxiliary definition 5 | (func $dummy) 6 | 7 | (func (export "empty") 8 | (block) 9 | (block $l) 10 | ) 11 | 12 | (func (export "singular") (result i32) 13 | (block (nop)) 14 | (block (result i32) (i32.const 7)) 15 | ) 16 | 17 | (func (export "multi") (result i32) 18 | (block (call $dummy) (call $dummy) (call $dummy) (call $dummy)) 19 | (block (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8)) 20 | ) 21 | 22 | (func (export "nested") (result i32) 23 | (block (result i32) 24 | (block (call $dummy) (block) (nop)) 25 | (block (result i32) (call $dummy) (i32.const 9)) 26 | ) 27 | ) 28 | 29 | (func (export "deep") (result i32) 30 | (block (result i32) (block (result i32) 31 | (block (result i32) (block (result i32) 32 | (block (result i32) (block (result i32) 33 | (block (result i32) (block (result i32) 34 | (block (result i32) (block (result i32) 35 | (block (result i32) (block (result i32) 36 | (block (result i32) (block (result i32) 37 | (block (result i32) (block (result i32) 38 | (block (result i32) (block (result i32) 39 | (block (result i32) (block (result i32) 40 | (block (result i32) (block (result i32) 41 | (block (result i32) (block (result i32) 42 | (block (result i32) (block (result i32) 43 | (block (result i32) (block (result i32) 44 | (block (result i32) (block (result i32) 45 | (block (result i32) (block (result i32) 46 | (block (result i32) (block (result i32) 47 | (block (result i32) (block (result i32) 48 | (block (result i32) (block (result i32) 49 | (call $dummy) (i32.const 150) 50 | )) 51 | )) 52 | )) 53 | )) 54 | )) 55 | )) 56 | )) 57 | )) 58 | )) 59 | )) 60 | )) 61 | )) 62 | )) 63 | )) 64 | )) 65 | )) 66 | )) 67 | )) 68 | )) 69 | ) 70 | 71 | (func (export "as-unary-operand") (result i32) 72 | (i32.ctz (block (result i32) (call $dummy) (i32.const 13))) 73 | ) 74 | (func (export "as-binary-operand") (result i32) 75 | (i32.mul 76 | (block (result i32) (call $dummy) (i32.const 3)) 77 | (block (result i32) (call $dummy) (i32.const 4)) 78 | ) 79 | ) 80 | (func (export "as-test-operand") (result i32) 81 | (i32.eqz (block (result i32) (call $dummy) (i32.const 13))) 82 | ) 83 | (func (export "as-compare-operand") (result i32) 84 | (f32.gt 85 | (block (result f32) (call $dummy) (f32.const 3)) 86 | (block (result f32) (call $dummy) (f32.const 3)) 87 | ) 88 | ) 89 | 90 | (func (export "break-bare") (result i32) 91 | (block (br 0) (unreachable)) 92 | (block (br_if 0 (i32.const 1)) (unreachable)) 93 | (block (br_table 0 (i32.const 0)) (unreachable)) 94 | (block (br_table 0 0 0 (i32.const 1)) (unreachable)) 95 | (i32.const 19) 96 | ) 97 | (func (export "break-value") (result i32) 98 | (block (result i32) (br 0 (i32.const 18)) (i32.const 19)) 99 | ) 100 | (func (export "break-repeated") (result i32) 101 | (block (result i32) 102 | (br 0 (i32.const 18)) 103 | (br 0 (i32.const 19)) 104 | (drop (br_if 0 (i32.const 20) (i32.const 0))) 105 | (drop (br_if 0 (i32.const 20) (i32.const 1))) 106 | (br 0 (i32.const 21)) 107 | (br_table 0 (i32.const 22) (i32.const 4)) 108 | (br_table 0 0 0 (i32.const 23) (i32.const 1)) 109 | (i32.const 21) 110 | ) 111 | ) 112 | (func (export "break-inner") (result i32) 113 | (local i32) 114 | (set_local 0 (i32.const 0)) 115 | (set_local 0 (i32.add (get_local 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1)))))) 116 | (set_local 0 (i32.add (get_local 0) (block (result i32) (block (br 0)) (i32.const 0x2)))) 117 | (set_local 0 118 | (i32.add (get_local 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4))))) 119 | ) 120 | (set_local 0 121 | (i32.add (get_local 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8)))))) 122 | ) 123 | (get_local 0) 124 | ) 125 | 126 | (func (export "effects") (result i32) 127 | (local i32) 128 | (block 129 | (set_local 0 (i32.const 1)) 130 | (set_local 0 (i32.mul (get_local 0) (i32.const 3))) 131 | (set_local 0 (i32.sub (get_local 0) (i32.const 5))) 132 | (set_local 0 (i32.mul (get_local 0) (i32.const 7))) 133 | (br 0) 134 | (set_local 0 (i32.mul (get_local 0) (i32.const 100))) 135 | ) 136 | (i32.eq (get_local 0) (i32.const -14)) 137 | ) 138 | ) 139 | 140 | (assert_return (invoke "empty")) 141 | (assert_return (invoke "singular") (i32.const 7)) 142 | (assert_return (invoke "multi") (i32.const 8)) 143 | (assert_return (invoke "nested") (i32.const 9)) 144 | (assert_return (invoke "deep") (i32.const 150)) 145 | 146 | (assert_return (invoke "as-unary-operand") (i32.const 0)) 147 | (assert_return (invoke "as-binary-operand") (i32.const 12)) 148 | (assert_return (invoke "as-test-operand") (i32.const 0)) 149 | (assert_return (invoke "as-compare-operand") (i32.const 0)) 150 | 151 | (assert_return (invoke "break-bare") (i32.const 19)) 152 | (assert_return (invoke "break-value") (i32.const 18)) 153 | (assert_return (invoke "break-repeated") (i32.const 18)) 154 | (assert_return (invoke "break-inner") (i32.const 0xf)) 155 | 156 | (assert_return (invoke "effects") (i32.const 1)) 157 | 158 | (assert_invalid 159 | (module (func $type-empty-i32 (result i32) (block))) 160 | "type mismatch" 161 | ) 162 | (assert_invalid 163 | (module (func $type-empty-i64 (result i64) (block))) 164 | "type mismatch" 165 | ) 166 | (assert_invalid 167 | (module (func $type-empty-f32 (result f32) (block))) 168 | "type mismatch" 169 | ) 170 | (assert_invalid 171 | (module (func $type-empty-f64 (result f64) (block))) 172 | "type mismatch" 173 | ) 174 | 175 | (assert_invalid 176 | (module (func $type-value-num-vs-void 177 | (block (i32.const 1)) 178 | )) 179 | "type mismatch" 180 | ) 181 | (assert_invalid 182 | (module (func $type-value-empty-vs-num (result i32) 183 | (block (result i32)) 184 | )) 185 | "type mismatch" 186 | ) 187 | (assert_invalid 188 | (module (func $type-value-void-vs-num (result i32) 189 | (block (result i32) (nop)) 190 | )) 191 | "type mismatch" 192 | ) 193 | (assert_invalid 194 | (module (func $type-value-num-vs-num (result i32) 195 | (block (result i32) (f32.const 0)) 196 | )) 197 | "type mismatch" 198 | ) 199 | (assert_invalid 200 | (module (func $type-value-unreached-select (result i32) 201 | (block (result i64) (select (unreachable) (unreachable) (unreachable))) 202 | )) 203 | "type mismatch" 204 | ) 205 | 206 | (assert_invalid 207 | (module (func $type-break-last-void-vs-num (result i32) 208 | (block (result i32) (br 0)) 209 | )) 210 | "type mismatch" 211 | ) 212 | (assert_invalid 213 | (module (func $type-break-empty-vs-num (result i32) 214 | (block (result i32) (br 0) (i32.const 1)) 215 | )) 216 | "type mismatch" 217 | ) 218 | 219 | (assert_invalid 220 | (module (func $type-break-void-vs-num (result i32) 221 | (block (result i32) (br 0 (nop)) (i32.const 1)) 222 | )) 223 | "type mismatch" 224 | ) 225 | (assert_invalid 226 | (module (func $type-break-num-vs-num (result i32) 227 | (block (result i32) (br 0 (i64.const 1)) (i32.const 1)) 228 | )) 229 | "type mismatch" 230 | ) 231 | (assert_invalid 232 | (module (func $type-break-first-void-vs-num (result i32) 233 | (block (result i32) (br 0 (nop)) (br 0 (i32.const 1))) 234 | )) 235 | "type mismatch" 236 | ) 237 | (assert_invalid 238 | (module (func $type-break-first-num-vs-num (result i32) 239 | (block (result i32) (br 0 (i64.const 1)) (br 0 (i32.const 1))) 240 | )) 241 | "type mismatch" 242 | ) 243 | 244 | (assert_invalid 245 | (module (func $type-break-nested-num-vs-void 246 | (block (result i32) (block (result i32) (br 1 (i32.const 1))) (br 0)) 247 | )) 248 | "type mismatch" 249 | ) 250 | (assert_invalid 251 | (module (func $type-break-nested-empty-vs-num (result i32) 252 | (block (result i32) (block (br 1)) (br 0 (i32.const 1))) 253 | )) 254 | "type mismatch" 255 | ) 256 | 257 | (assert_invalid 258 | (module (func $type-break-nested-void-vs-num (result i32) 259 | (block (result i32) (block (result i32) (br 1 (nop))) (br 0 (i32.const 1))) 260 | )) 261 | "type mismatch" 262 | ) 263 | (assert_invalid 264 | (module (func $type-break-nested-num-vs-num (result i32) 265 | (block (result i32) 266 | (block (result i32) (br 1 (i64.const 1))) (br 0 (i32.const 1)) 267 | ) 268 | )) 269 | "type mismatch" 270 | ) 271 | 272 | (assert_invalid 273 | (module (func $type-break-operand-empty-vs-num (result i32) 274 | (i32.ctz (block (br 0))) 275 | )) 276 | "type mismatch" 277 | ) 278 | (assert_invalid 279 | (module (func $type-break-operand-void-vs-num (result i32) 280 | (i64.ctz (block (br 0 (nop)))) 281 | )) 282 | "type mismatch" 283 | ) 284 | (assert_invalid 285 | (module (func $type-break-operand-num-vs-num (result i32) 286 | (i64.ctz (block (br 0 (i64.const 9)))) 287 | )) 288 | "type mismatch" 289 | ) 290 | 291 | 292 | (assert_malformed 293 | (module quote "(func block end $l)") 294 | "mismatching label" 295 | ) 296 | (assert_malformed 297 | (module quote "(func block $a end $l)") 298 | "mismatching label" 299 | ) 300 | -------------------------------------------------------------------------------- /test/spec_tests/break-drop.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "br") (block (br 0))) 3 | (func (export "br_if") (block (br_if 0 (i32.const 1)))) 4 | (func (export "br_table") (block (br_table 0 (i32.const 0)))) 5 | ) 6 | 7 | (assert_return (invoke "br")) 8 | (assert_return (invoke "br_if")) 9 | (assert_return (invoke "br_table")) 10 | -------------------------------------------------------------------------------- /test/spec_tests/call.wast: -------------------------------------------------------------------------------- 1 | ;; Test `call` operator 2 | 3 | (module 4 | ;; Auxiliary definitions 5 | (func $const-i32 (result i32) (i32.const 0x132)) 6 | (func $const-i64 (result i64) (i64.const 0x164)) 7 | (func $const-f32 (result f32) (f32.const 0xf32)) 8 | (func $const-f64 (result f64) (f64.const 0xf64)) 9 | 10 | (func $id-i32 (param i32) (result i32) (get_local 0)) 11 | (func $id-i64 (param i64) (result i64) (get_local 0)) 12 | (func $id-f32 (param f32) (result f32) (get_local 0)) 13 | (func $id-f64 (param f64) (result f64) (get_local 0)) 14 | 15 | (func $f32-i32 (param f32 i32) (result i32) (get_local 1)) 16 | (func $i32-i64 (param i32 i64) (result i64) (get_local 1)) 17 | (func $f64-f32 (param f64 f32) (result f32) (get_local 1)) 18 | (func $i64-f64 (param i64 f64) (result f64) (get_local 1)) 19 | 20 | ;; Typing 21 | 22 | (func (export "type-i32") (result i32) (call $const-i32)) 23 | (func (export "type-i64") (result i64) (call $const-i64)) 24 | (func (export "type-f32") (result f32) (call $const-f32)) 25 | (func (export "type-f64") (result f64) (call $const-f64)) 26 | 27 | (func (export "type-first-i32") (result i32) (call $id-i32 (i32.const 32))) 28 | (func (export "type-first-i64") (result i64) (call $id-i64 (i64.const 64))) 29 | (func (export "type-first-f32") (result f32) (call $id-f32 (f32.const 1.32))) 30 | (func (export "type-first-f64") (result f64) (call $id-f64 (f64.const 1.64))) 31 | 32 | (func (export "type-second-i32") (result i32) 33 | (call $f32-i32 (f32.const 32.1) (i32.const 32)) 34 | ) 35 | (func (export "type-second-i64") (result i64) 36 | (call $i32-i64 (i32.const 32) (i64.const 64)) 37 | ) 38 | (func (export "type-second-f32") (result f32) 39 | (call $f64-f32 (f64.const 64) (f32.const 32)) 40 | ) 41 | (func (export "type-second-f64") (result f64) 42 | (call $i64-f64 (i64.const 64) (f64.const 64.1)) 43 | ) 44 | 45 | ;; Recursion 46 | 47 | (func $fac (export "fac") (param i64) (result i64) 48 | (if (result i64) (i64.eqz (get_local 0)) 49 | (then (i64.const 1)) 50 | (else 51 | (i64.mul 52 | (get_local 0) 53 | (call $fac (i64.sub (get_local 0) (i64.const 1))) 54 | ) 55 | ) 56 | ) 57 | ) 58 | 59 | (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) 60 | (if (result i64) (i64.eqz (get_local 0)) 61 | (then (get_local 1)) 62 | (else 63 | (call $fac-acc 64 | (i64.sub (get_local 0) (i64.const 1)) 65 | (i64.mul (get_local 0) (get_local 1)) 66 | ) 67 | ) 68 | ) 69 | ) 70 | 71 | (func $fib (export "fib") (param i64) (result i64) 72 | (if (result i64) (i64.le_u (get_local 0) (i64.const 1)) 73 | (then (i64.const 1)) 74 | (else 75 | (i64.add 76 | (call $fib (i64.sub (get_local 0) (i64.const 2))) 77 | (call $fib (i64.sub (get_local 0) (i64.const 1))) 78 | ) 79 | ) 80 | ) 81 | ) 82 | 83 | (func $even (export "even") (param i64) (result i32) 84 | (if (result i32) (i64.eqz (get_local 0)) 85 | (then (i32.const 44)) 86 | (else (call $odd (i64.sub (get_local 0) (i64.const 1)))) 87 | ) 88 | ) 89 | (func $odd (export "odd") (param i64) (result i32) 90 | (if (result i32) (i64.eqz (get_local 0)) 91 | (then (i32.const 99)) 92 | (else (call $even (i64.sub (get_local 0) (i64.const 1)))) 93 | ) 94 | ) 95 | 96 | ;; Stack exhaustion 97 | 98 | ;; Implementations are required to have every call consume some abstract 99 | ;; resource towards exhausting some abstract finite limit, such that 100 | ;; infinitely recursive test cases reliably trap in finite time. This is 101 | ;; because otherwise applications could come to depend on it on those 102 | ;; implementations and be incompatible with implementations that don't do 103 | ;; it (or don't do it under the same circumstances). 104 | 105 | (func $runaway (export "runaway") (call $runaway)) 106 | 107 | (func $mutual-runaway1 (export "mutual-runaway") (call $mutual-runaway2)) 108 | (func $mutual-runaway2 (call $mutual-runaway1)) 109 | ) 110 | 111 | (assert_return (invoke "type-i32") (i32.const 0x132)) 112 | (assert_return (invoke "type-i64") (i64.const 0x164)) 113 | (assert_return (invoke "type-f32") (f32.const 0xf32)) 114 | (assert_return (invoke "type-f64") (f64.const 0xf64)) 115 | 116 | (assert_return (invoke "type-first-i32") (i32.const 32)) 117 | (assert_return (invoke "type-first-i64") (i64.const 64)) 118 | (assert_return (invoke "type-first-f32") (f32.const 1.32)) 119 | (assert_return (invoke "type-first-f64") (f64.const 1.64)) 120 | 121 | (assert_return (invoke "type-second-i32") (i32.const 32)) 122 | (assert_return (invoke "type-second-i64") (i64.const 64)) 123 | (assert_return (invoke "type-second-f32") (f32.const 32)) 124 | (assert_return (invoke "type-second-f64") (f64.const 64.1)) 125 | 126 | (assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) 127 | (assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) 128 | (assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) 129 | (assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) 130 | (assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) 131 | (assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) 132 | (assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) 133 | (assert_return 134 | (invoke "fac-acc" (i64.const 25) (i64.const 1)) 135 | (i64.const 7034535277573963776) 136 | ) 137 | 138 | (assert_return (invoke "fib" (i64.const 0)) (i64.const 1)) 139 | (assert_return (invoke "fib" (i64.const 1)) (i64.const 1)) 140 | (assert_return (invoke "fib" (i64.const 2)) (i64.const 2)) 141 | (assert_return (invoke "fib" (i64.const 5)) (i64.const 8)) 142 | (assert_return (invoke "fib" (i64.const 20)) (i64.const 10946)) 143 | 144 | (assert_return (invoke "even" (i64.const 0)) (i32.const 44)) 145 | (assert_return (invoke "even" (i64.const 1)) (i32.const 99)) 146 | (assert_return (invoke "even" (i64.const 100)) (i32.const 44)) 147 | (assert_return (invoke "even" (i64.const 77)) (i32.const 99)) 148 | (assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) 149 | (assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) 150 | (assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) 151 | (assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) 152 | 153 | (assert_exhaustion (invoke "runaway") "call stack exhausted") 154 | (assert_exhaustion (invoke "mutual-runaway") "call stack exhausted") 155 | 156 | 157 | ;; Invalid typing 158 | 159 | (assert_invalid 160 | (module 161 | (func $type-void-vs-num (i32.eqz (call 1))) 162 | (func) 163 | ) 164 | "type mismatch" 165 | ) 166 | (assert_invalid 167 | (module 168 | (func $type-num-vs-num (i32.eqz (call 1))) 169 | (func (result i64) (i64.const 1)) 170 | ) 171 | "type mismatch" 172 | ) 173 | 174 | (assert_invalid 175 | (module 176 | (func $arity-0-vs-1 (call 1)) 177 | (func (param i32)) 178 | ) 179 | "type mismatch" 180 | ) 181 | (assert_invalid 182 | (module 183 | (func $arity-0-vs-2 (call 1)) 184 | (func (param f64 i32)) 185 | ) 186 | "type mismatch" 187 | ) 188 | (assert_invalid 189 | (module 190 | (func $arity-1-vs-0 (call 1 (i32.const 1))) 191 | (func) 192 | ) 193 | "type mismatch" 194 | ) 195 | (assert_invalid 196 | (module 197 | (func $arity-2-vs-0 (call 1 (f64.const 2) (i32.const 1))) 198 | (func) 199 | ) 200 | "type mismatch" 201 | ) 202 | 203 | (assert_invalid 204 | (module 205 | (func $type-first-void-vs-num (call 1 (nop) (i32.const 1))) 206 | (func (param i32 i32)) 207 | ) 208 | "type mismatch" 209 | ) 210 | (assert_invalid 211 | (module 212 | (func $type-second-void-vs-num (call 1 (i32.const 1) (nop))) 213 | (func (param i32 i32)) 214 | ) 215 | "type mismatch" 216 | ) 217 | (assert_invalid 218 | (module 219 | (func $type-first-num-vs-num (call 1 (f64.const 1) (i32.const 1))) 220 | (func (param i32 f64)) 221 | ) 222 | "type mismatch" 223 | ) 224 | (assert_invalid 225 | (module 226 | (func $type-second-num-vs-num (call 1 (i32.const 1) (f64.const 1))) 227 | (func (param f64 i32)) 228 | ) 229 | "type mismatch" 230 | ) 231 | 232 | 233 | ;; Unbound function 234 | 235 | (assert_invalid 236 | (module (func $unbound-func (call 1))) 237 | "unknown function" 238 | ) 239 | (assert_invalid 240 | (module (func $large-func (call 1012321300))) 241 | "unknown function" 242 | ) 243 | -------------------------------------------------------------------------------- /test/spec_tests/comments.wast: -------------------------------------------------------------------------------- 1 | ;; Test comment syntax 2 | 3 | ;;comment 4 | 5 | ;;;;;;;;;;; 6 | 7 | ;;comment 8 | 9 | ( ;;comment 10 | module;;comment 11 | );;comment 12 | 13 | ;;) 14 | ;;;) 15 | ;; ;) 16 | ;; (; 17 | 18 | (;;) 19 | 20 | (;comment;) 21 | 22 | (;;comment;) 23 | 24 | (;;;comment;) 25 | 26 | (;;;;;;;;;;;;;;) 27 | 28 | (;(((((((((( ;) 29 | 30 | (;)))))))))));) 31 | 32 | (;comment";) 33 | 34 | (;comment"";) 35 | 36 | (;comment""";) 37 | 38 | ;; ASCII 00-1F, 7F 39 | (; 40 | ;) 41 | 42 | (;Heiße Würstchen;) 43 | 44 | (;;) 45 | 46 | (;comment 47 | comment;) 48 | 49 | (;comment;) 50 | 51 | (;comment;)((;comment;) 52 | (;comment;)module(;comment;) 53 | (;comment;))(;comment;) 54 | 55 | (;comment(;nested;)comment;) 56 | 57 | (;comment 58 | (;nested 59 | ;)comment 60 | ;) 61 | 62 | (module 63 | (;comment(;nested(;further;)nested;)comment;) 64 | ) 65 | 66 | (;comment;;comment;) 67 | 68 | (;comment;;comment 69 | ;) 70 | 71 | (module 72 | (;comment;;comment(;nested;)comment;) 73 | ) -------------------------------------------------------------------------------- /test/spec_tests/const.wast: -------------------------------------------------------------------------------- 1 | ;; Test t.const instructions 2 | 3 | ;; Syntax error 4 | 5 | (module (func (i32.const 0xffffffff) drop)) 6 | (module (func (i32.const -0x80000000) drop)) 7 | (assert_malformed 8 | (module quote "(func (i32.const 0x100000000) drop)") 9 | "constant out of range" 10 | ) 11 | (assert_malformed 12 | (module quote "(func (i32.const -0x80000001) drop)") 13 | "constant out of range" 14 | ) 15 | 16 | (module (func (i32.const 4294967295) drop)) 17 | (module (func (i32.const -2147483648) drop)) 18 | (assert_malformed 19 | (module quote "(func (i32.const 4294967296) drop)") 20 | "constant out of range" 21 | ) 22 | (assert_malformed 23 | (module quote "(func (i32.const -2147483649) drop)") 24 | "constant out of range" 25 | ) 26 | 27 | (module (func (i64.const 0xffffffffffffffff) drop)) 28 | (module (func (i64.const -0x8000000000000000) drop)) 29 | (assert_malformed 30 | (module quote "(func (i64.const 0x10000000000000000) drop)") 31 | "constant out of range" 32 | ) 33 | (assert_malformed 34 | (module quote "(func (i64.const -0x8000000000000001) drop)") 35 | "constant out of range" 36 | ) 37 | 38 | (module (func (i64.const 18446744073709551615) drop)) 39 | (module (func (i64.const -9223372036854775808) drop)) 40 | (assert_malformed 41 | (module quote "(func (i64.const 18446744073709551616) drop)") 42 | "constant out of range" 43 | ) 44 | (assert_malformed 45 | (module quote "(func (i64.const -9223372036854775809) drop)") 46 | "constant out of range" 47 | ) 48 | 49 | (module (func (f32.const 0x1p127) drop)) 50 | (module (func (f32.const -0x1p127) drop)) 51 | (module (func (f32.const 0x1.fffffep127) drop)) 52 | (module (func (f32.const -0x1.fffffep127) drop)) 53 | (module (func (f32.const 0x1.fffffe7p127) drop)) 54 | (module (func (f32.const -0x1.fffffe7p127) drop)) 55 | (assert_malformed 56 | (module quote "(func (f32.const 0x1p128) drop)") 57 | "constant out of range" 58 | ) 59 | (assert_malformed 60 | (module quote "(func (f32.const -0x1p128) drop)") 61 | "constant out of range" 62 | ) 63 | (assert_malformed 64 | (module quote "(func (f32.const 0x1.ffffffp127) drop)") 65 | "constant out of range" 66 | ) 67 | (assert_malformed 68 | (module quote "(func (f32.const -0x1.ffffffp127) drop)") 69 | "constant out of range" 70 | ) 71 | 72 | (module (func (f32.const 1e38) drop)) 73 | (module (func (f32.const -1e38) drop)) 74 | (assert_malformed 75 | (module quote "(func (f32.const 1e39) drop)") 76 | "constant out of range" 77 | ) 78 | (assert_malformed 79 | (module quote "(func (f32.const -1e39) drop)") 80 | "constant out of range" 81 | ) 82 | 83 | (module (func (f32.const 340282356779733623858607532500980858880) drop)) 84 | (module (func (f32.const -340282356779733623858607532500980858880) drop)) 85 | (assert_malformed 86 | (module quote "(func (f32.const 340282356779733661637539395458142568448) drop)") 87 | "constant out of range" 88 | ) 89 | (assert_malformed 90 | (module quote "(func (f32.const -340282356779733661637539395458142568448) drop)") 91 | "constant out of range" 92 | ) 93 | 94 | (module (func (f64.const 0x1p1023) drop)) 95 | (module (func (f64.const -0x1p1023) drop)) 96 | (module (func (f64.const 0x1.fffffffffffffp1023) drop)) 97 | (module (func (f64.const -0x1.fffffffffffffp1023) drop)) 98 | (module (func (f64.const 0x1.fffffffffffff7p1023) drop)) 99 | (module (func (f64.const -0x1.fffffffffffff7p1023) drop)) 100 | (assert_malformed 101 | (module quote "(func (f64.const 0x1p1024) drop)") 102 | "constant out of range" 103 | ) 104 | (assert_malformed 105 | (module quote "(func (f64.const -0x1p1024) drop)") 106 | "constant out of range" 107 | ) 108 | (assert_malformed 109 | (module quote "(func (f64.const 0x1.fffffffffffff8p1023) drop)") 110 | "constant out of range" 111 | ) 112 | (assert_malformed 113 | (module quote "(func (f64.const -0x1.fffffffffffff8p1023) drop)") 114 | "constant out of range" 115 | ) 116 | 117 | (module (func (f64.const 1e308) drop)) 118 | (module (func (f64.const -1e308) drop)) 119 | (assert_malformed 120 | (module quote "(func (f64.const 1e309) drop)") 121 | "constant out of range" 122 | ) 123 | (assert_malformed 124 | (module quote "(func (f64.const -1e309) drop)") 125 | "constant out of range" 126 | ) 127 | 128 | (module (func (f64.const 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) 129 | (module (func (f64.const -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) 130 | (assert_malformed 131 | (module quote "(func (f64.const 269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") 132 | "constant out of range" 133 | ) 134 | (assert_malformed 135 | (module quote "(func (f64.const -269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") 136 | "constant out of range" 137 | ) 138 | -------------------------------------------------------------------------------- /test/spec_tests/custom_section.wast: -------------------------------------------------------------------------------- 1 | (module binary 2 | "\00asm" "\01\00\00\00" 3 | "\00\24\10" "a custom section" "this is the payload" 4 | "\00\20\10" "a custom section" "this is payload" 5 | "\00\11\10" "a custom section" "" 6 | "\00\10\00" "" "this is payload" 7 | "\00\01\00" "" "" 8 | "\00\24\10" "\00\00custom sectio\00" "this is the payload" 9 | "\00\24\10" "\ef\bb\bfa custom sect" "this is the payload" 10 | "\00\24\10" "a custom sect\e2\8c\a3" "this is the payload" 11 | "\00\1f\16" "module within a module" "\00asm" "\01\00\00\00" 12 | ) 13 | 14 | (module binary 15 | "\00asm" "\01\00\00\00" 16 | "\00\0e\06" "custom" "payload" 17 | "\00\0e\06" "custom" "payload" 18 | "\01\01\00" ;; type section 19 | "\00\0e\06" "custom" "payload" 20 | "\00\0e\06" "custom" "payload" 21 | "\02\01\00" ;; import section 22 | "\00\0e\06" "custom" "payload" 23 | "\00\0e\06" "custom" "payload" 24 | "\03\01\00" ;; function section 25 | "\00\0e\06" "custom" "payload" 26 | "\00\0e\06" "custom" "payload" 27 | "\04\01\00" ;; table section 28 | "\00\0e\06" "custom" "payload" 29 | "\00\0e\06" "custom" "payload" 30 | "\05\01\00" ;; memory section 31 | "\00\0e\06" "custom" "payload" 32 | "\00\0e\06" "custom" "payload" 33 | "\06\01\00" ;; global section 34 | "\00\0e\06" "custom" "payload" 35 | "\00\0e\06" "custom" "payload" 36 | "\07\01\00" ;; export section 37 | "\00\0e\06" "custom" "payload" 38 | "\00\0e\06" "custom" "payload" 39 | "\09\01\00" ;; element section 40 | "\00\0e\06" "custom" "payload" 41 | "\00\0e\06" "custom" "payload" 42 | "\0a\01\00" ;; code section 43 | "\00\0e\06" "custom" "payload" 44 | "\00\0e\06" "custom" "payload" 45 | "\0b\01\00" ;; data section 46 | "\00\0e\06" "custom" "payload" 47 | "\00\0e\06" "custom" "payload" 48 | ) 49 | 50 | (module binary 51 | "\00asm" "\01\00\00\00" 52 | "\01\07\01\60\02\7f\7f\01\7f" ;; type section 53 | "\00\1a\06" "custom" "this is the payload" ;; custom section 54 | "\03\02\01\00" ;; function section 55 | "\07\0a\01\06\61\64\64\54\77\6f\00\00" ;; export section 56 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 57 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 58 | ) 59 | 60 | (assert_malformed 61 | (module binary 62 | "\00asm" "\01\00\00\00" 63 | "\00" 64 | ) 65 | "unexpected end" 66 | ) 67 | 68 | (assert_malformed 69 | (module binary 70 | "\00asm" "\01\00\00\00" 71 | "\00\00" 72 | ) 73 | "unexpected end" 74 | ) 75 | 76 | (assert_malformed 77 | (module binary 78 | "\00asm" "\01\00\00\00" 79 | "\00\26\10" "a custom section" "this is the payload" 80 | ) 81 | "unexpected end" 82 | ) 83 | 84 | (assert_malformed 85 | (module binary 86 | "\00asm" "\01\00\00\00" 87 | "\00\25\10" "a custom section" "this is the payload" 88 | "\00\24\10" "a custom section" "this is the payload" 89 | ) 90 | "invalid section id" 91 | ) 92 | 93 | (assert_malformed 94 | (module binary 95 | "\00asm" "\01\00\00\00" 96 | "\01\07\01\60\02\7f\7f\01\7f" ;; type section 97 | "\00\25\10" "a custom section" "this is the payload" ;; invalid length! 98 | "\03\02\01\00" ;; function section 99 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 100 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 101 | ) 102 | "function and code section have inconsistent lengths" 103 | ) 104 | 105 | ;; Test concatenated modules. 106 | (assert_malformed 107 | (module binary 108 | "\00asm\01\00\00\00" 109 | "\00asm\01\00\00\00" 110 | ) 111 | "length out of bounds" 112 | ) 113 | -------------------------------------------------------------------------------- /test/spec_tests/elem.wast: -------------------------------------------------------------------------------- 1 | ;; Test the element section 2 | 3 | (module 4 | (table 10 anyfunc) 5 | (elem (i32.const 0) $f) 6 | (func $f) 7 | ) 8 | 9 | (module 10 | (table 10 anyfunc) 11 | (elem (i32.const 9) $f) 12 | (func $f) 13 | ) 14 | 15 | (module 16 | (type $out-i32 (func (result i32))) 17 | (table 10 anyfunc) 18 | (elem (i32.const 7) $const-i32-a) 19 | (elem (i32.const 9) $const-i32-b) 20 | (func $const-i32-a (type $out-i32) (i32.const 65)) 21 | (func $const-i32-b (type $out-i32) (i32.const 66)) 22 | (func (export "call-7") (type $out-i32) 23 | (call_indirect $out-i32 (i32.const 7)) 24 | ) 25 | (func (export "call-9") (type $out-i32) 26 | (call_indirect $out-i32 (i32.const 9)) 27 | ) 28 | ) 29 | 30 | (assert_return (invoke "call-7") (i32.const 65)) 31 | (assert_return (invoke "call-9") (i32.const 66)) 32 | 33 | ;; Two elements target the same slot 34 | 35 | (module 36 | (type $out-i32 (func (result i32))) 37 | (table 10 anyfunc) 38 | (elem (i32.const 9) $const-i32-a) 39 | (elem (i32.const 9) $const-i32-b) 40 | (func $const-i32-a (type $out-i32) (i32.const 65)) 41 | (func $const-i32-b (type $out-i32) (i32.const 66)) 42 | (func (export "call-overwritten-element") (type $out-i32) 43 | (call_indirect $out-i32 (i32.const 9)) 44 | ) 45 | ) 46 | 47 | (assert_return (invoke "call-overwritten-element") (i32.const 66)) 48 | 49 | ;; Invalid bounds for elements 50 | 51 | (assert_unlinkable 52 | (module 53 | (table 10 anyfunc) 54 | (elem (i32.const 10) $f) 55 | (func $f) 56 | ) 57 | "elements segment does not fit" 58 | ) 59 | 60 | (assert_unlinkable 61 | (module 62 | (table 10 20 anyfunc) 63 | (elem (i32.const 10) $f) 64 | (func $f) 65 | ) 66 | "elements segment does not fit" 67 | ) 68 | 69 | (assert_unlinkable 70 | (module 71 | (table 10 anyfunc) 72 | (elem (i32.const -1) $f) 73 | (func $f) 74 | ) 75 | "elements segment does not fit" 76 | ) 77 | 78 | (assert_unlinkable 79 | (module 80 | (table 10 anyfunc) 81 | (elem (i32.const -10) $f) 82 | (func $f) 83 | ) 84 | "elements segment does not fit" 85 | ) 86 | 87 | ;; Tests with an imported table 88 | 89 | (module 90 | (import "spectest" "table" (table 10 anyfunc)) 91 | (elem (i32.const 0) $f) 92 | (func $f) 93 | ) 94 | 95 | (module 96 | (import "spectest" "table" (table 10 anyfunc)) 97 | (elem (i32.const 9) $f) 98 | (func $f) 99 | ) 100 | 101 | ;; Two elements target the same slot 102 | 103 | (module 104 | (type $out-i32 (func (result i32))) 105 | (import "spectest" "table" (table 10 anyfunc)) 106 | (elem (i32.const 9) $const-i32-a) 107 | (elem (i32.const 9) $const-i32-b) 108 | (func $const-i32-a (type $out-i32) (i32.const 65)) 109 | (func $const-i32-b (type $out-i32) (i32.const 66)) 110 | (func (export "call-overwritten-element") (type $out-i32) 111 | (call_indirect $out-i32 (i32.const 9)) 112 | ) 113 | ) 114 | 115 | (assert_return (invoke "call-overwritten-element") (i32.const 66)) 116 | 117 | ;; Invalid bounds for elements 118 | 119 | (assert_unlinkable 120 | (module 121 | (import "spectest" "table" (table 10 anyfunc)) 122 | (elem (i32.const 10) $f) 123 | (func $f) 124 | ) 125 | "elements segment does not fit" 126 | ) 127 | 128 | (assert_unlinkable 129 | (module 130 | (import "spectest" "table" (table 10 20 anyfunc)) 131 | (elem (i32.const 10) $f) 132 | (func $f) 133 | ) 134 | "elements segment does not fit" 135 | ) 136 | 137 | (assert_unlinkable 138 | (module 139 | (import "spectest" "table" (table 10 anyfunc)) 140 | (elem (i32.const -1) $f) 141 | (func $f) 142 | ) 143 | "elements segment does not fit" 144 | ) 145 | 146 | (assert_unlinkable 147 | (module 148 | (import "spectest" "table" (table 10 anyfunc)) 149 | (elem (i32.const -10) $f) 150 | (func $f) 151 | ) 152 | "elements segment does not fit" 153 | ) 154 | 155 | ;; Element without table 156 | 157 | (assert_invalid 158 | (module 159 | (elem (i32.const 0) $f) 160 | (func $f) 161 | ) 162 | "unknown table 0" 163 | ) 164 | 165 | ;; Test element sections across multiple modules change the same table 166 | 167 | (module $module1 168 | (type $out-i32 (func (result i32))) 169 | (table (export "shared-table") 10 anyfunc) 170 | (elem (i32.const 8) $const-i32-a) 171 | (elem (i32.const 9) $const-i32-b) 172 | (func $const-i32-a (type $out-i32) (i32.const 65)) 173 | (func $const-i32-b (type $out-i32) (i32.const 66)) 174 | (func (export "call-7") (type $out-i32) 175 | (call_indirect $out-i32 (i32.const 7)) 176 | ) 177 | (func (export "call-8") (type $out-i32) 178 | (call_indirect $out-i32 (i32.const 8)) 179 | ) 180 | (func (export "call-9") (type $out-i32) 181 | (call_indirect $out-i32 (i32.const 9)) 182 | ) 183 | ) 184 | 185 | (register "module1" $module1) 186 | 187 | (assert_trap (invoke $module1 "call-7") "uninitialized element 7") 188 | (assert_return (invoke $module1 "call-8") (i32.const 65)) 189 | (assert_return (invoke $module1 "call-9") (i32.const 66)) 190 | 191 | (module $module2 192 | (type $out-i32 (func (result i32))) 193 | (import "module1" "shared-table" (table 10 anyfunc)) 194 | (elem (i32.const 7) $const-i32-c) 195 | (elem (i32.const 8) $const-i32-d) 196 | (func $const-i32-c (type $out-i32) (i32.const 67)) 197 | (func $const-i32-d (type $out-i32) (i32.const 68)) 198 | ) 199 | 200 | (assert_return (invoke $module1 "call-7") (i32.const 67)) 201 | (assert_return (invoke $module1 "call-8") (i32.const 68)) 202 | (assert_return (invoke $module1 "call-9") (i32.const 66)) 203 | 204 | (module $module3 205 | (type $out-i32 (func (result i32))) 206 | (import "module1" "shared-table" (table 10 anyfunc)) 207 | (elem (i32.const 8) $const-i32-e) 208 | (elem (i32.const 9) $const-i32-f) 209 | (func $const-i32-e (type $out-i32) (i32.const 69)) 210 | (func $const-i32-f (type $out-i32) (i32.const 70)) 211 | ) 212 | 213 | (assert_return (invoke $module1 "call-7") (i32.const 67)) 214 | (assert_return (invoke $module1 "call-8") (i32.const 69)) 215 | (assert_return (invoke $module1 "call-9") (i32.const 70)) 216 | -------------------------------------------------------------------------------- /test/spec_tests/exports.wast: -------------------------------------------------------------------------------- 1 | ;; Functions 2 | 3 | (module (func) (export "a" (func 0))) 4 | (module (func) (export "a" (func 0)) (export "b" (func 0))) 5 | (module (func) (func) (export "a" (func 0)) (export "b" (func 1))) 6 | 7 | (module (func (export "a"))) 8 | (module (func (export "a") (export "b") (export "c"))) 9 | (module (func (export "a") (export "b") (param i32))) 10 | (module (func) (export "a" (func 0))) 11 | (module (func $a (export "a"))) 12 | (module (func $a) (export "a" (func $a))) 13 | (module (export "a" (func 0)) (func)) 14 | (module (export "a" (func $a)) (func $a)) 15 | 16 | (module $Func 17 | (export "e" (func $f)) 18 | (func $f (param $n i32) (result i32) 19 | (return (i32.add (get_local $n) (i32.const 1))) 20 | ) 21 | ) 22 | (assert_return (invoke "e" (i32.const 42)) (i32.const 43)) 23 | (assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) 24 | (module) 25 | (module $Other1) 26 | (assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) 27 | 28 | (assert_invalid 29 | (module (func) (export "a" (func 1))) 30 | "unknown function" 31 | ) 32 | (assert_invalid 33 | (module (func) (export "a" (func 0)) (export "a" (func 0))) 34 | "duplicate export name" 35 | ) 36 | (assert_invalid 37 | (module (func) (func) (export "a" (func 0)) (export "a" (func 1))) 38 | "duplicate export name" 39 | ) 40 | (assert_invalid 41 | (module (func) (global i32 (i32.const 0)) (export "a" (func 0)) (export "a" (global 0))) 42 | "duplicate export name" 43 | ) 44 | (assert_invalid 45 | (module (func) (table 0 anyfunc) (export "a" (func 0)) (export "a" (table 0))) 46 | "duplicate export name" 47 | ) 48 | (assert_invalid 49 | (module (func) (memory 0) (export "a" (func 0)) (export "a" (memory 0))) 50 | "duplicate export name" 51 | ) 52 | 53 | 54 | ;; Globals 55 | 56 | (module (global i32 (i32.const 0)) (export "a" (global 0))) 57 | (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 0))) 58 | (module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 1))) 59 | 60 | (module (global (export "a") i32 (i32.const 0))) 61 | (module (global i32 (i32.const 0)) (export "a" (global 0))) 62 | (module (global $a (export "a") i32 (i32.const 0))) 63 | (module (global $a i32 (i32.const 0)) (export "a" (global $a))) 64 | (module (export "a" (global 0)) (global i32 (i32.const 0))) 65 | (module (export "a" (global $a)) (global $a i32 (i32.const 0))) 66 | 67 | (module $Global 68 | (export "e" (global $g)) 69 | (global $g i32 (i32.const 42)) 70 | ) 71 | (assert_return (get "e") (i32.const 42)) 72 | (assert_return (get $Global "e") (i32.const 42)) 73 | (module) 74 | (module $Other2) 75 | (assert_return (get $Global "e") (i32.const 42)) 76 | 77 | (assert_invalid 78 | (module (global i32 (i32.const 0)) (export "a" (global 1))) 79 | "unknown global" 80 | ) 81 | (assert_invalid 82 | (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 0))) 83 | "duplicate export name" 84 | ) 85 | (assert_invalid 86 | (module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 1))) 87 | "duplicate export name" 88 | ) 89 | (assert_invalid 90 | (module (global i32 (i32.const 0)) (func) (export "a" (global 0)) (export "a" (func 0))) 91 | "duplicate export name" 92 | ) 93 | (assert_invalid 94 | (module (global i32 (i32.const 0)) (table 0 anyfunc) (export "a" (global 0)) (export "a" (table 0))) 95 | "duplicate export name" 96 | ) 97 | (assert_invalid 98 | (module (global i32 (i32.const 0)) (memory 0) (export "a" (global 0)) (export "a" (memory 0))) 99 | "duplicate export name" 100 | ) 101 | 102 | 103 | ;; Tables 104 | 105 | (module (table 0 anyfunc) (export "a" (table 0))) 106 | (module (table 0 anyfunc) (export "a" (table 0)) (export "b" (table 0))) 107 | ;; No multiple tables yet. 108 | ;; (module (table 0 anyfunc) (table 0 anyfunc) (export "a" (table 0)) (export "b" (table 1))) 109 | 110 | (module (table (export "a") 0 anyfunc)) 111 | (module (table (export "a") 0 1 anyfunc)) 112 | (module (table 0 anyfunc) (export "a" (table 0))) 113 | (module (table 0 1 anyfunc) (export "a" (table 0))) 114 | (module (table $a (export "a") 0 anyfunc)) 115 | (module (table $a (export "a") 0 1 anyfunc)) 116 | (module (table $a 0 anyfunc) (export "a" (table $a))) 117 | (module (table $a 0 1 anyfunc) (export "a" (table $a))) 118 | (module (export "a" (table 0)) (table 0 anyfunc)) 119 | (module (export "a" (table 0)) (table 0 1 anyfunc)) 120 | (module (export "a" (table $a)) (table $a 0 anyfunc)) 121 | (module (export "a" (table $a)) (table $a 0 1 anyfunc)) 122 | 123 | (; TODO: access table ;) 124 | 125 | (assert_invalid 126 | (module (table 0 anyfunc) (export "a" (table 1))) 127 | "unknown table" 128 | ) 129 | (assert_invalid 130 | (module (table 0 anyfunc) (export "a" (table 0)) (export "a" (table 0))) 131 | "duplicate export name" 132 | ) 133 | ;; No multiple tables yet. 134 | ;; (assert_invalid 135 | ;; (module (table 0 anyfunc) (table 0 anyfunc) (export "a" (table 0)) (export "a" (table 1))) 136 | ;; "duplicate export name" 137 | ;; ) 138 | (assert_invalid 139 | (module (table 0 anyfunc) (func) (export "a" (table 0)) (export "a" (func 0))) 140 | "duplicate export name" 141 | ) 142 | (assert_invalid 143 | (module (table 0 anyfunc) (global i32 (i32.const 0)) (export "a" (table 0)) (export "a" (global 0))) 144 | "duplicate export name" 145 | ) 146 | (assert_invalid 147 | (module (table 0 anyfunc) (memory 0) (export "a" (table 0)) (export "a" (memory 0))) 148 | "duplicate export name" 149 | ) 150 | 151 | 152 | ;; Memories 153 | 154 | (module (memory 0) (export "a" (memory 0))) 155 | (module (memory 0) (export "a" (memory 0)) (export "b" (memory 0))) 156 | ;; No multiple memories yet. 157 | ;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "b" (memory 1))) 158 | 159 | (module (memory (export "a") 0)) 160 | (module (memory (export "a") 0 1)) 161 | (module (memory 0) (export "a" (memory 0))) 162 | (module (memory 0 1) (export "a" (memory 0))) 163 | (module (memory $a (export "a") 0)) 164 | (module (memory $a (export "a") 0 1)) 165 | (module (memory $a 0) (export "a" (memory $a))) 166 | (module (memory $a 0 1) (export "a" (memory $a))) 167 | (module (export "a" (memory 0)) (memory 0)) 168 | (module (export "a" (memory 0)) (memory 0 1)) 169 | (module (export "a" (memory $a)) (memory $a 0)) 170 | (module (export "a" (memory $a)) (memory $a 0 1)) 171 | 172 | (; TODO: access memory ;) 173 | 174 | (assert_invalid 175 | (module (memory 0) (export "a" (memory 1))) 176 | "unknown memory" 177 | ) 178 | (assert_invalid 179 | (module (memory 0) (export "a" (memory 0)) (export "a" (memory 0))) 180 | "duplicate export name" 181 | ) 182 | ;; No multiple memories yet. 183 | ;; (assert_invalid 184 | ;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "a" (memory 1))) 185 | ;; "duplicate export name" 186 | ;; ) 187 | (assert_invalid 188 | (module (memory 0) (func) (export "a" (memory 0)) (export "a" (func 0))) 189 | "duplicate export name" 190 | ) 191 | (assert_invalid 192 | (module (memory 0) (global i32 (i32.const 0)) (export "a" (memory 0)) (export "a" (global 0))) 193 | "duplicate export name" 194 | ) 195 | (assert_invalid 196 | (module (memory 0) (table 0 anyfunc) (export "a" (memory 0)) (export "a" (table 0))) 197 | "duplicate export name" 198 | ) 199 | -------------------------------------------------------------------------------- /test/spec_tests/fac.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Recursive factorial 3 | (func (export "fac-rec") (param i64) (result i64) 4 | (if (result i64) (i64.eq (get_local 0) (i64.const 0)) 5 | (then (i64.const 1)) 6 | (else 7 | (i64.mul (get_local 0) (call 0 (i64.sub (get_local 0) (i64.const 1)))) 8 | ) 9 | ) 10 | ) 11 | 12 | ;; Recursive factorial named 13 | (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) 14 | (if (result i64) (i64.eq (get_local $n) (i64.const 0)) 15 | (then (i64.const 1)) 16 | (else 17 | (i64.mul 18 | (get_local $n) 19 | (call $fac-rec-named (i64.sub (get_local $n) (i64.const 1))) 20 | ) 21 | ) 22 | ) 23 | ) 24 | 25 | ;; Iterative factorial 26 | (func (export "fac-iter") (param i64) (result i64) 27 | (local i64 i64) 28 | (set_local 1 (get_local 0)) 29 | (set_local 2 (i64.const 1)) 30 | (block 31 | (loop 32 | (if 33 | (i64.eq (get_local 1) (i64.const 0)) 34 | (then (br 2)) 35 | (else 36 | (set_local 2 (i64.mul (get_local 1) (get_local 2))) 37 | (set_local 1 (i64.sub (get_local 1) (i64.const 1))) 38 | ) 39 | ) 40 | (br 0) 41 | ) 42 | ) 43 | (get_local 2) 44 | ) 45 | 46 | ;; Iterative factorial named 47 | (func (export "fac-iter-named") (param $n i64) (result i64) 48 | (local $i i64) 49 | (local $res i64) 50 | (set_local $i (get_local $n)) 51 | (set_local $res (i64.const 1)) 52 | (block $done 53 | (loop $loop 54 | (if 55 | (i64.eq (get_local $i) (i64.const 0)) 56 | (then (br $done)) 57 | (else 58 | (set_local $res (i64.mul (get_local $i) (get_local $res))) 59 | (set_local $i (i64.sub (get_local $i) (i64.const 1))) 60 | ) 61 | ) 62 | (br $loop) 63 | ) 64 | ) 65 | (get_local $res) 66 | ) 67 | 68 | ;; Optimized factorial. 69 | (func (export "fac-opt") (param i64) (result i64) 70 | (local i64) 71 | (set_local 1 (i64.const 1)) 72 | (block 73 | (br_if 0 (i64.lt_s (get_local 0) (i64.const 2))) 74 | (loop 75 | (set_local 1 (i64.mul (get_local 1) (get_local 0))) 76 | (set_local 0 (i64.add (get_local 0) (i64.const -1))) 77 | (br_if 0 (i64.gt_s (get_local 0) (i64.const 1))) 78 | ) 79 | ) 80 | (get_local 1) 81 | ) 82 | ) 83 | 84 | (assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) 85 | (assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) 86 | (assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) 87 | (assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) 88 | (assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) 89 | (assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") 90 | -------------------------------------------------------------------------------- /test/spec_tests/float_memory.wast: -------------------------------------------------------------------------------- 1 | ;; Test that floating-point load and store are bit-preserving. 2 | 3 | ;; Test that load and store do not canonicalize NaNs as x87 does. 4 | 5 | (module 6 | (memory (data "\00\00\a0\7f")) 7 | 8 | (func (export "f32.load") (result f32) (f32.load (i32.const 0))) 9 | (func (export "i32.load") (result i32) (i32.load (i32.const 0))) 10 | (func (export "f32.store") (f32.store (i32.const 0) (f32.const nan:0x200000))) 11 | (func (export "i32.store") (i32.store (i32.const 0) (i32.const 0x7fa00000))) 12 | (func (export "reset") (i32.store (i32.const 0) (i32.const 0))) 13 | ) 14 | 15 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 16 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 17 | (invoke "reset") 18 | (assert_return (invoke "i32.load") (i32.const 0x0)) 19 | (assert_return (invoke "f32.load") (f32.const 0.0)) 20 | (invoke "f32.store") 21 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 22 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 23 | (invoke "reset") 24 | (assert_return (invoke "i32.load") (i32.const 0x0)) 25 | (assert_return (invoke "f32.load") (f32.const 0.0)) 26 | (invoke "i32.store") 27 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 28 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 29 | 30 | (module 31 | (memory (data "\00\00\00\00\00\00\f4\7f")) 32 | 33 | (func (export "f64.load") (result f64) (f64.load (i32.const 0))) 34 | (func (export "i64.load") (result i64) (i64.load (i32.const 0))) 35 | (func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0x4000000000000))) 36 | (func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ff4000000000000))) 37 | (func (export "reset") (i64.store (i32.const 0) (i64.const 0))) 38 | ) 39 | 40 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 41 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 42 | (invoke "reset") 43 | (assert_return (invoke "i64.load") (i64.const 0x0)) 44 | (assert_return (invoke "f64.load") (f64.const 0.0)) 45 | (invoke "f64.store") 46 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 47 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 48 | (invoke "reset") 49 | (assert_return (invoke "i64.load") (i64.const 0x0)) 50 | (assert_return (invoke "f64.load") (f64.const 0.0)) 51 | (invoke "i64.store") 52 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 53 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 54 | 55 | ;; Test that unaligned load and store do not canonicalize NaNs. 56 | 57 | (module 58 | (memory (data "\00\00\00\a0\7f")) 59 | 60 | (func (export "f32.load") (result f32) (f32.load (i32.const 1))) 61 | (func (export "i32.load") (result i32) (i32.load (i32.const 1))) 62 | (func (export "f32.store") (f32.store (i32.const 1) (f32.const nan:0x200000))) 63 | (func (export "i32.store") (i32.store (i32.const 1) (i32.const 0x7fa00000))) 64 | (func (export "reset") (i32.store (i32.const 1) (i32.const 0))) 65 | ) 66 | 67 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 68 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 69 | (invoke "reset") 70 | (assert_return (invoke "i32.load") (i32.const 0x0)) 71 | (assert_return (invoke "f32.load") (f32.const 0.0)) 72 | (invoke "f32.store") 73 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 74 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 75 | (invoke "reset") 76 | (assert_return (invoke "i32.load") (i32.const 0x0)) 77 | (assert_return (invoke "f32.load") (f32.const 0.0)) 78 | (invoke "i32.store") 79 | (assert_return (invoke "i32.load") (i32.const 0x7fa00000)) 80 | (assert_return (invoke "f32.load") (f32.const nan:0x200000)) 81 | 82 | (module 83 | (memory (data "\00\00\00\00\00\00\00\f4\7f")) 84 | 85 | (func (export "f64.load") (result f64) (f64.load (i32.const 1))) 86 | (func (export "i64.load") (result i64) (i64.load (i32.const 1))) 87 | (func (export "f64.store") (f64.store (i32.const 1) (f64.const nan:0x4000000000000))) 88 | (func (export "i64.store") (i64.store (i32.const 1) (i64.const 0x7ff4000000000000))) 89 | (func (export "reset") (i64.store (i32.const 1) (i64.const 0))) 90 | ) 91 | 92 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 93 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 94 | (invoke "reset") 95 | (assert_return (invoke "i64.load") (i64.const 0x0)) 96 | (assert_return (invoke "f64.load") (f64.const 0.0)) 97 | (invoke "f64.store") 98 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 99 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 100 | (invoke "reset") 101 | (assert_return (invoke "i64.load") (i64.const 0x0)) 102 | (assert_return (invoke "f64.load") (f64.const 0.0)) 103 | (invoke "i64.store") 104 | (assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) 105 | (assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) 106 | 107 | ;; Test that load and store do not canonicalize NaNs as some JS engines do. 108 | 109 | (module 110 | (memory (data "\01\00\d0\7f")) 111 | 112 | (func (export "f32.load") (result f32) (f32.load (i32.const 0))) 113 | (func (export "i32.load") (result i32) (i32.load (i32.const 0))) 114 | (func (export "f32.store") (f32.store (i32.const 0) (f32.const nan:0x500001))) 115 | (func (export "i32.store") (i32.store (i32.const 0) (i32.const 0x7fd00001))) 116 | (func (export "reset") (i32.store (i32.const 0) (i32.const 0))) 117 | ) 118 | 119 | (assert_return (invoke "i32.load") (i32.const 0x7fd00001)) 120 | (assert_return (invoke "f32.load") (f32.const nan:0x500001)) 121 | (invoke "reset") 122 | (assert_return (invoke "i32.load") (i32.const 0x0)) 123 | (assert_return (invoke "f32.load") (f32.const 0.0)) 124 | (invoke "f32.store") 125 | (assert_return (invoke "i32.load") (i32.const 0x7fd00001)) 126 | (assert_return (invoke "f32.load") (f32.const nan:0x500001)) 127 | (invoke "reset") 128 | (assert_return (invoke "i32.load") (i32.const 0x0)) 129 | (assert_return (invoke "f32.load") (f32.const 0.0)) 130 | (invoke "i32.store") 131 | (assert_return (invoke "i32.load") (i32.const 0x7fd00001)) 132 | (assert_return (invoke "f32.load") (f32.const nan:0x500001)) 133 | 134 | (module 135 | (memory (data "\01\00\00\00\00\00\fc\7f")) 136 | 137 | (func (export "f64.load") (result f64) (f64.load (i32.const 0))) 138 | (func (export "i64.load") (result i64) (i64.load (i32.const 0))) 139 | (func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0xc000000000001))) 140 | (func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ffc000000000001))) 141 | (func (export "reset") (i64.store (i32.const 0) (i64.const 0))) 142 | ) 143 | 144 | (assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) 145 | (assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) 146 | (invoke "reset") 147 | (assert_return (invoke "i64.load") (i64.const 0x0)) 148 | (assert_return (invoke "f64.load") (f64.const 0.0)) 149 | (invoke "f64.store") 150 | (assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) 151 | (assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) 152 | (invoke "reset") 153 | (assert_return (invoke "i64.load") (i64.const 0x0)) 154 | (assert_return (invoke "f64.load") (f64.const 0.0)) 155 | (invoke "i64.store") 156 | (assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) 157 | (assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) 158 | -------------------------------------------------------------------------------- /test/spec_tests/forward.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $even (export "even") (param $n i32) (result i32) 3 | (if (result i32) (i32.eq (get_local $n) (i32.const 0)) 4 | (then (i32.const 1)) 5 | (else (call $odd (i32.sub (get_local $n) (i32.const 1)))) 6 | ) 7 | ) 8 | 9 | (func $odd (export "odd") (param $n i32) (result i32) 10 | (if (result i32) (i32.eq (get_local $n) (i32.const 0)) 11 | (then (i32.const 0)) 12 | (else (call $even (i32.sub (get_local $n) (i32.const 1)))) 13 | ) 14 | ) 15 | ) 16 | 17 | (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) 18 | (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) 19 | (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) 20 | (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) 21 | -------------------------------------------------------------------------------- /test/spec_tests/func_ptrs.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type (func)) ;; 0: void -> void 3 | (type $S (func)) ;; 1: void -> void 4 | (type (func (param))) ;; 2: void -> void 5 | (type (func (result i32))) ;; 3: void -> i32 6 | (type (func (param) (result i32))) ;; 4: void -> i32 7 | (type $T (func (param i32) (result i32))) ;; 5: i32 -> i32 8 | (type $U (func (param i32))) ;; 6: i32 -> void 9 | 10 | ;; (func $print (import "spectest" "print") (type 6)) 11 | (func $print (type 6)) 12 | 13 | (func (type 0)) 14 | (func (type $S)) 15 | 16 | (func (export "one") (type 4) (i32.const 13)) 17 | (func (export "two") (type $T) (i32.add (get_local 0) (i32.const 1))) 18 | 19 | ;; Both signature and parameters are allowed (and required to match) 20 | ;; since this allows the naming of parameters. 21 | (func (export "three") (type $T) (param $a i32) (result i32) 22 | (i32.sub (get_local 0) (i32.const 2)) 23 | ) 24 | 25 | (func (export "four") (type $U) (call $print (get_local 0))) 26 | ) 27 | 28 | (assert_return (invoke "one") (i32.const 13)) 29 | (assert_return (invoke "two" (i32.const 13)) (i32.const 14)) 30 | (assert_return (invoke "three" (i32.const 13)) (i32.const 11)) 31 | (invoke "four" (i32.const 83)) 32 | 33 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 34 | (assert_invalid (module (elem (i32.const 0) 0) (func)) "unknown table") 35 | 36 | (assert_invalid 37 | (module (table 1 anyfunc) (elem (i64.const 0))) 38 | "type mismatch" 39 | ) 40 | (assert_invalid 41 | (module (table 1 anyfunc) (elem (i32.ctz (i32.const 0)))) 42 | "constant expression required" 43 | ) 44 | (assert_invalid 45 | (module (table 1 anyfunc) (elem (nop))) 46 | "constant expression required" 47 | ) 48 | 49 | (assert_invalid (module (func (type 42))) "unknown type") 50 | (assert_invalid (module (import "spectest" "print" (func (type 43)))) "unknown type") 51 | 52 | (module 53 | (type $T (func (param) (result i32))) 54 | (type $U (func (param) (result i32))) 55 | (table anyfunc (elem $t1 $t2 $t3 $u1 $u2 $t1 $t3)) 56 | 57 | (func $t1 (type $T) (i32.const 1)) 58 | (func $t2 (type $T) (i32.const 2)) 59 | (func $t3 (type $T) (i32.const 3)) 60 | (func $u1 (type $U) (i32.const 4)) 61 | (func $u2 (type $U) (i32.const 5)) 62 | 63 | (func (export "callt") (param $i i32) (result i32) 64 | (call_indirect $T (get_local $i)) 65 | ) 66 | 67 | (func (export "callu") (param $i i32) (result i32) 68 | (call_indirect $U (get_local $i)) 69 | ) 70 | ) 71 | 72 | (assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) 73 | (assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) 74 | (assert_return (invoke "callt" (i32.const 2)) (i32.const 3)) 75 | (assert_return (invoke "callt" (i32.const 3)) (i32.const 4)) 76 | (assert_return (invoke "callt" (i32.const 4)) (i32.const 5)) 77 | (assert_return (invoke "callt" (i32.const 5)) (i32.const 1)) 78 | (assert_return (invoke "callt" (i32.const 6)) (i32.const 3)) 79 | (assert_trap (invoke "callt" (i32.const 7)) "undefined element") 80 | (assert_trap (invoke "callt" (i32.const 100)) "undefined element") 81 | (assert_trap (invoke "callt" (i32.const -1)) "undefined element") 82 | 83 | (assert_return (invoke "callu" (i32.const 0)) (i32.const 1)) 84 | (assert_return (invoke "callu" (i32.const 1)) (i32.const 2)) 85 | (assert_return (invoke "callu" (i32.const 2)) (i32.const 3)) 86 | (assert_return (invoke "callu" (i32.const 3)) (i32.const 4)) 87 | (assert_return (invoke "callu" (i32.const 4)) (i32.const 5)) 88 | (assert_return (invoke "callu" (i32.const 5)) (i32.const 1)) 89 | (assert_return (invoke "callu" (i32.const 6)) (i32.const 3)) 90 | (assert_trap (invoke "callu" (i32.const 7)) "undefined element") 91 | (assert_trap (invoke "callu" (i32.const 100)) "undefined element") 92 | (assert_trap (invoke "callu" (i32.const -1)) "undefined element") 93 | 94 | (module 95 | (type $T (func (result i32))) 96 | (table anyfunc (elem 0 1)) 97 | 98 | (func $t1 (type $T) (i32.const 1)) 99 | (func $t2 (type $T) (i32.const 2)) 100 | 101 | (func (export "callt") (param $i i32) (result i32) 102 | (call_indirect $T (get_local $i)) 103 | ) 104 | ) 105 | 106 | (assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) 107 | (assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) 108 | -------------------------------------------------------------------------------- /test/spec_tests/get_local.wast: -------------------------------------------------------------------------------- 1 | ;; Test `get_local` operator 2 | 3 | (module 4 | ;; Typing 5 | 6 | (func (export "type-local-i32") (result i32) (local i32) (get_local 0)) 7 | (func (export "type-local-i64") (result i64) (local i64) (get_local 0)) 8 | (func (export "type-local-f32") (result f32) (local f32) (get_local 0)) 9 | (func (export "type-local-f64") (result f64) (local f64) (get_local 0)) 10 | 11 | (func (export "type-param-i32") (param i32) (result i32) (get_local 0)) 12 | (func (export "type-param-i64") (param i64) (result i64) (get_local 0)) 13 | (func (export "type-param-f32") (param f32) (result f32) (get_local 0)) 14 | (func (export "type-param-f64") (param f64) (result f64) (get_local 0)) 15 | 16 | (func (export "type-mixed") (param i64 f32 f64 i32 i32) 17 | (local f32 i64 i64 f64) 18 | (drop (i64.eqz (get_local 0))) 19 | (drop (f32.neg (get_local 1))) 20 | (drop (f64.neg (get_local 2))) 21 | (drop (i32.eqz (get_local 3))) 22 | (drop (i32.eqz (get_local 4))) 23 | (drop (f32.neg (get_local 5))) 24 | (drop (i64.eqz (get_local 6))) 25 | (drop (i64.eqz (get_local 7))) 26 | (drop (f64.neg (get_local 8))) 27 | ) 28 | 29 | ;; Reading 30 | 31 | (func (export "read") (param i64 f32 f64 i32 i32) (result f64) 32 | (local f32 i64 i64 f64) 33 | (set_local 5 (f32.const 5.5)) 34 | (set_local 6 (i64.const 6)) 35 | (set_local 8 (f64.const 8)) 36 | (f64.add 37 | (f64.convert_u/i64 (get_local 0)) 38 | (f64.add 39 | (f64.promote/f32 (get_local 1)) 40 | (f64.add 41 | (get_local 2) 42 | (f64.add 43 | (f64.convert_u/i32 (get_local 3)) 44 | (f64.add 45 | (f64.convert_s/i32 (get_local 4)) 46 | (f64.add 47 | (f64.promote/f32 (get_local 5)) 48 | (f64.add 49 | (f64.convert_u/i64 (get_local 6)) 50 | (f64.add 51 | (f64.convert_u/i64 (get_local 7)) 52 | (get_local 8) 53 | ) 54 | ) 55 | ) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | 64 | (assert_return (invoke "type-local-i32") (i32.const 0)) 65 | (assert_return (invoke "type-local-i64") (i64.const 0)) 66 | (assert_return (invoke "type-local-f32") (f32.const 0)) 67 | (assert_return (invoke "type-local-f64") (f64.const 0)) 68 | 69 | (assert_return (invoke "type-param-i32" (i32.const 2)) (i32.const 2)) 70 | (assert_return (invoke "type-param-i64" (i64.const 3)) (i64.const 3)) 71 | (assert_return (invoke "type-param-f32" (f32.const 4.4)) (f32.const 4.4)) 72 | (assert_return (invoke "type-param-f64" (f64.const 5.5)) (f64.const 5.5)) 73 | 74 | (assert_return 75 | (invoke "type-mixed" 76 | (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5) 77 | ) 78 | ) 79 | 80 | (assert_return 81 | (invoke "read" 82 | (i64.const 1) (f32.const 2) (f64.const 3.3) (i32.const 4) (i32.const 5) 83 | ) 84 | (f64.const 34.8) 85 | ) 86 | 87 | 88 | ;; Invalid typing of access to locals 89 | 90 | (assert_invalid 91 | (module (func $type-local-num-vs-num (result i64) (local i32) (get_local 0))) 92 | "type mismatch" 93 | ) 94 | (assert_invalid 95 | (module (func $type-local-num-vs-num (local f32) (i32.eqz (get_local 0)))) 96 | "type mismatch" 97 | ) 98 | (assert_invalid 99 | (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (get_local 1)))) 100 | "type mismatch" 101 | ) 102 | 103 | 104 | ;; Invalid typing of access to parameters 105 | 106 | (assert_invalid 107 | (module (func $type-param-num-vs-num (param i32) (result i64) (get_local 0))) 108 | "type mismatch" 109 | ) 110 | (assert_invalid 111 | (module (func $type-param-num-vs-num (param f32) (i32.eqz (get_local 0)))) 112 | "type mismatch" 113 | ) 114 | (assert_invalid 115 | (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (get_local 1)))) 116 | "type mismatch" 117 | ) 118 | 119 | 120 | ;; Invalid local index 121 | 122 | (assert_invalid 123 | (module (func $unbound-local (local i32 i64) (get_local 3))) 124 | "unknown local" 125 | ) 126 | (assert_invalid 127 | (module (func $large-local (local i32 i64) (get_local 14324343))) 128 | "unknown local" 129 | ) 130 | 131 | (assert_invalid 132 | (module (func $unbound-param (param i32 i64) (get_local 2))) 133 | "unknown local" 134 | ) 135 | (assert_invalid 136 | (module (func $large-param (local i32 i64) (get_local 714324343))) 137 | "unknown local" 138 | ) 139 | 140 | (assert_invalid 141 | (module (func $unbound-mixed (param i32) (local i32 i64) (get_local 3))) 142 | "unknown local" 143 | ) 144 | (assert_invalid 145 | (module (func $large-mixed (param i64) (local i32 i64) (get_local 214324343))) 146 | "unknown local" 147 | ) 148 | 149 | -------------------------------------------------------------------------------- /test/spec_tests/globals.wast: -------------------------------------------------------------------------------- 1 | ;; Test globals 2 | 3 | (module 4 | (global $a i32 (i32.const -2)) 5 | (global (;1;) f32 (f32.const -3)) 6 | (global (;2;) f64 (f64.const -4)) 7 | (global $b i64 (i64.const -5)) 8 | 9 | (global $x (mut i32) (i32.const -12)) 10 | (global (;5;) (mut f32) (f32.const -13)) 11 | (global (;6;) (mut f64) (f64.const -14)) 12 | (global $y (mut i64) (i64.const -15)) 13 | 14 | (func (export "get-a") (result i32) (get_global $a)) 15 | (func (export "get-b") (result i64) (get_global $b)) 16 | (func (export "get-x") (result i32) (get_global $x)) 17 | (func (export "get-y") (result i64) (get_global $y)) 18 | (func (export "set-x") (param i32) (set_global $x (get_local 0))) 19 | (func (export "set-y") (param i64) (set_global $y (get_local 0))) 20 | 21 | (func (export "get-1") (result f32) (get_global 1)) 22 | (func (export "get-2") (result f64) (get_global 2)) 23 | (func (export "get-5") (result f32) (get_global 5)) 24 | (func (export "get-6") (result f64) (get_global 6)) 25 | (func (export "set-5") (param f32) (set_global 5 (get_local 0))) 26 | (func (export "set-6") (param f64) (set_global 6 (get_local 0))) 27 | ) 28 | 29 | (assert_return (invoke "get-a") (i32.const -2)) 30 | (assert_return (invoke "get-b") (i64.const -5)) 31 | (assert_return (invoke "get-x") (i32.const -12)) 32 | (assert_return (invoke "get-y") (i64.const -15)) 33 | 34 | (assert_return (invoke "get-1") (f32.const -3)) 35 | (assert_return (invoke "get-2") (f64.const -4)) 36 | (assert_return (invoke "get-5") (f32.const -13)) 37 | (assert_return (invoke "get-6") (f64.const -14)) 38 | 39 | (assert_return (invoke "set-x" (i32.const 6))) 40 | (assert_return (invoke "set-y" (i64.const 7))) 41 | (assert_return (invoke "set-5" (f32.const 8))) 42 | (assert_return (invoke "set-6" (f64.const 9))) 43 | 44 | (assert_return (invoke "get-x") (i32.const 6)) 45 | (assert_return (invoke "get-y") (i64.const 7)) 46 | (assert_return (invoke "get-5") (f32.const 8)) 47 | (assert_return (invoke "get-6") (f64.const 9)) 48 | 49 | (assert_invalid 50 | (module (global f32 (f32.const 0)) (func (set_global 0 (i32.const 1)))) 51 | "global is immutable" 52 | ) 53 | 54 | (assert_invalid 55 | (module (import "m" "a" (global (mut i32)))) 56 | "mutable globals cannot be imported" 57 | ) 58 | 59 | (assert_invalid 60 | (module (global (import "m" "a") (mut i32))) 61 | "mutable globals cannot be imported" 62 | ) 63 | 64 | (assert_invalid 65 | (module (global (mut f32) (f32.const 0)) (export "a" (global 0))) 66 | "mutable globals cannot be exported" 67 | ) 68 | 69 | (assert_invalid 70 | (module (global (export "a") (mut f32) (f32.const 0))) 71 | "mutable globals cannot be exported" 72 | ) 73 | 74 | (assert_invalid 75 | (module (global f32 (f32.neg (f32.const 0)))) 76 | "constant expression required" 77 | ) 78 | 79 | (assert_invalid 80 | (module (global f32 (get_local 0))) 81 | "constant expression required" 82 | ) 83 | 84 | (assert_invalid 85 | (module (global f32 (f32.neg (f32.const 1)))) 86 | "constant expression required" 87 | ) 88 | 89 | (assert_invalid 90 | (module (global i32 (i32.const 0) (nop))) 91 | "constant expression required" 92 | ) 93 | 94 | (assert_invalid 95 | (module (global i32 (nop))) 96 | "constant expression required" 97 | ) 98 | 99 | (assert_invalid 100 | (module (global i32 (f32.const 0))) 101 | "type mismatch" 102 | ) 103 | 104 | (assert_invalid 105 | (module (global i32 (i32.const 0) (i32.const 0))) 106 | "type mismatch" 107 | ) 108 | 109 | (assert_invalid 110 | (module (global i32 (;empty instruction sequence;))) 111 | "type mismatch" 112 | ) 113 | 114 | (assert_invalid 115 | (module (global i32 (get_global 0))) 116 | "unknown global" 117 | ) 118 | 119 | (assert_invalid 120 | (module (global i32 (get_global 1)) (global i32 (i32.const 0))) 121 | "unknown global" 122 | ) 123 | 124 | (module 125 | (import "spectest" "global" (global i32)) 126 | ) 127 | (assert_malformed 128 | (module binary 129 | "\00asm" "\01\00\00\00" 130 | "\02\94\80\80\80\00" ;; import section 131 | "\01" ;; length 1 132 | "\08\73\70\65\63\74\65\73\74" ;; "spectest" 133 | "\06\67\6c\6f\62\61\6c" ;; "global" 134 | "\03" ;; GlobalImport 135 | "\7f" ;; i32 136 | "\02" ;; invalid mutability 137 | ) 138 | "invalid mutability" 139 | ) 140 | (assert_malformed 141 | (module binary 142 | "\00asm" "\01\00\00\00" 143 | "\02\94\80\80\80\00" ;; import section 144 | "\01" ;; length 1 145 | "\08\73\70\65\63\74\65\73\74" ;; "spectest" 146 | "\06\67\6c\6f\62\61\6c" ;; "global" 147 | "\03" ;; GlobalImport 148 | "\7f" ;; i32 149 | "\ff" ;; invalid mutability 150 | ) 151 | "invalid mutability" 152 | ) 153 | 154 | (module 155 | (global i32 (i32.const 0)) 156 | ) 157 | (assert_malformed 158 | (module binary 159 | "\00asm" "\01\00\00\00" 160 | "\06\86\80\80\80\00" ;; global section 161 | "\01" ;; length 1 162 | "\7f" ;; i32 163 | "\02" ;; invalid mutability 164 | "\41\00" ;; i32.const 0 165 | "\0b" ;; end 166 | ) 167 | "invalid mutability" 168 | ) 169 | (assert_malformed 170 | (module binary 171 | "\00asm" "\01\00\00\00" 172 | "\06\86\80\80\80\00" ;; global section 173 | "\01" ;; length 1 174 | "\7f" ;; i32 175 | "\ff" ;; invalid mutability 176 | "\41\00" ;; i32.const 0 177 | "\0b" ;; end 178 | ) 179 | "invalid mutability" 180 | ) 181 | -------------------------------------------------------------------------------- /test/spec_tests/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /test/spec_tests/int_literals.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "i32.test") (result i32) (return (i32.const 0x0bAdD00D))) 3 | (func (export "i32.umax") (result i32) (return (i32.const 0xffffffff))) 4 | (func (export "i32.smax") (result i32) (return (i32.const 0x7fffffff))) 5 | (func (export "i32.neg_smax") (result i32) (return (i32.const -0x7fffffff))) 6 | (func (export "i32.smin") (result i32) (return (i32.const -0x80000000))) 7 | (func (export "i32.alt_smin") (result i32) (return (i32.const 0x80000000))) 8 | (func (export "i32.inc_smin") (result i32) (return (i32.add (i32.const -0x80000000) (i32.const 1)))) 9 | (func (export "i32.neg_zero") (result i32) (return (i32.const -0x0))) 10 | (func (export "i32.not_octal") (result i32) (return (i32.const 010))) 11 | (func (export "i32.unsigned_decimal") (result i32) (return (i32.const 4294967295))) 12 | (func (export "i32.plus_sign") (result i32) (return (i32.const +42))) 13 | 14 | (func (export "i64.test") (result i64) (return (i64.const 0x0CABBA6E0ba66a6e))) 15 | (func (export "i64.umax") (result i64) (return (i64.const 0xffffffffffffffff))) 16 | (func (export "i64.smax") (result i64) (return (i64.const 0x7fffffffffffffff))) 17 | (func (export "i64.neg_smax") (result i64) (return (i64.const -0x7fffffffffffffff))) 18 | (func (export "i64.smin") (result i64) (return (i64.const -0x8000000000000000))) 19 | (func (export "i64.alt_smin") (result i64) (return (i64.const 0x8000000000000000))) 20 | (func (export "i64.inc_smin") (result i64) (return (i64.add (i64.const -0x8000000000000000) (i64.const 1)))) 21 | (func (export "i64.neg_zero") (result i64) (return (i64.const -0x0))) 22 | (func (export "i64.not_octal") (result i64) (return (i64.const 010))) 23 | (func (export "i64.unsigned_decimal") (result i64) (return (i64.const 18446744073709551615))) 24 | (func (export "i64.plus_sign") (result i64) (return (i64.const +42))) 25 | 26 | (func (export "i32-dec-sep1") (result i32) (i32.const 1_000_000)) 27 | (func (export "i32-dec-sep2") (result i32) (i32.const 1_0_0_0)) 28 | (func (export "i32-hex-sep1") (result i32) (i32.const 0xa_0f_00_99)) 29 | (func (export "i32-hex-sep2") (result i32) (i32.const 0x1_a_A_0_f)) 30 | 31 | (func (export "i64-dec-sep1") (result i64) (i64.const 1_000_000)) 32 | (func (export "i64-dec-sep2") (result i64) (i64.const 1_0_0_0)) 33 | (func (export "i64-hex-sep1") (result i64) (i64.const 0xa_f00f_0000_9999)) 34 | (func (export "i64-hex-sep2") (result i64) (i64.const 0x1_a_A_0_f)) 35 | ) 36 | 37 | (assert_return (invoke "i32.test") (i32.const 195940365)) 38 | (assert_return (invoke "i32.umax") (i32.const -1)) 39 | (assert_return (invoke "i32.smax") (i32.const 2147483647)) 40 | (assert_return (invoke "i32.neg_smax") (i32.const -2147483647)) 41 | (assert_return (invoke "i32.smin") (i32.const -2147483648)) 42 | (assert_return (invoke "i32.alt_smin") (i32.const -2147483648)) 43 | (assert_return (invoke "i32.inc_smin") (i32.const -2147483647)) 44 | (assert_return (invoke "i32.neg_zero") (i32.const 0)) 45 | (assert_return (invoke "i32.not_octal") (i32.const 10)) 46 | (assert_return (invoke "i32.unsigned_decimal") (i32.const -1)) 47 | (assert_return (invoke "i32.plus_sign") (i32.const 42)) 48 | 49 | (assert_return (invoke "i64.test") (i64.const 913028331277281902)) 50 | (assert_return (invoke "i64.umax") (i64.const -1)) 51 | (assert_return (invoke "i64.smax") (i64.const 9223372036854775807)) 52 | (assert_return (invoke "i64.neg_smax") (i64.const -9223372036854775807)) 53 | (assert_return (invoke "i64.smin") (i64.const -9223372036854775808)) 54 | (assert_return (invoke "i64.alt_smin") (i64.const -9223372036854775808)) 55 | (assert_return (invoke "i64.inc_smin") (i64.const -9223372036854775807)) 56 | (assert_return (invoke "i64.neg_zero") (i64.const 0)) 57 | (assert_return (invoke "i64.not_octal") (i64.const 10)) 58 | (assert_return (invoke "i64.unsigned_decimal") (i64.const -1)) 59 | (assert_return (invoke "i64.plus_sign") (i64.const 42)) 60 | 61 | (assert_return (invoke "i32-dec-sep1") (i32.const 1000000)) 62 | (assert_return (invoke "i32-dec-sep2") (i32.const 1000)) 63 | (assert_return (invoke "i32-hex-sep1") (i32.const 0xa0f0099)) 64 | (assert_return (invoke "i32-hex-sep2") (i32.const 0x1aa0f)) 65 | 66 | (assert_return (invoke "i64-dec-sep1") (i64.const 1000000)) 67 | (assert_return (invoke "i64-dec-sep2") (i64.const 1000)) 68 | (assert_return (invoke "i64-hex-sep1") (i64.const 0xaf00f00009999)) 69 | (assert_return (invoke "i64-hex-sep2") (i64.const 0x1aa0f)) 70 | 71 | (assert_malformed 72 | (module quote "(global i32 (i32.const _100))") 73 | "unknown operator" 74 | ) 75 | (assert_malformed 76 | (module quote "(global i32 (i32.const +_100))") 77 | "unknown operator" 78 | ) 79 | (assert_malformed 80 | (module quote "(global i32 (i32.const -_100))") 81 | "unknown operator" 82 | ) 83 | (assert_malformed 84 | (module quote "(global i32 (i32.const 99_))") 85 | "unknown operator" 86 | ) 87 | (assert_malformed 88 | (module quote "(global i32 (i32.const 1__000))") 89 | "unknown operator" 90 | ) 91 | (assert_malformed 92 | (module quote "(global i32 (i32.const _0x100))") 93 | "unknown operator" 94 | ) 95 | (assert_malformed 96 | (module quote "(global i32 (i32.const 0_x100))") 97 | "unknown operator" 98 | ) 99 | (assert_malformed 100 | (module quote "(global i32 (i32.const 0x_100))") 101 | "unknown operator" 102 | ) 103 | (assert_malformed 104 | (module quote "(global i32 (i32.const 0x00_))") 105 | "unknown operator" 106 | ) 107 | (assert_malformed 108 | (module quote "(global i32 (i32.const 0xff__ffff))") 109 | "unknown operator" 110 | ) 111 | 112 | (assert_malformed 113 | (module quote "(global i64 (i64.const _100))") 114 | "unknown operator" 115 | ) 116 | (assert_malformed 117 | (module quote "(global i64 (i64.const +_100))") 118 | "unknown operator" 119 | ) 120 | (assert_malformed 121 | (module quote "(global i64 (i64.const -_100))") 122 | "unknown operator" 123 | ) 124 | (assert_malformed 125 | (module quote "(global i64 (i64.const 99_))") 126 | "unknown operator" 127 | ) 128 | (assert_malformed 129 | (module quote "(global i64 (i64.const 1__000))") 130 | "unknown operator" 131 | ) 132 | (assert_malformed 133 | (module quote "(global i64 (i64.const _0x100))") 134 | "unknown operator" 135 | ) 136 | (assert_malformed 137 | (module quote "(global i64 (i64.const 0_x100))") 138 | "unknown operator" 139 | ) 140 | (assert_malformed 141 | (module quote "(global i64 (i64.const 0x_100))") 142 | "unknown operator" 143 | ) 144 | (assert_malformed 145 | (module quote "(global i64 (i64.const 0x00_))") 146 | "unknown operator" 147 | ) 148 | (assert_malformed 149 | (module quote "(global i64 (i64.const 0xff__ffff))") 150 | "unknown operator" 151 | ) 152 | -------------------------------------------------------------------------------- /test/spec_tests/labels.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "block") (result i32) 3 | (block $exit (result i32) 4 | (br $exit (i32.const 1)) 5 | (i32.const 0) 6 | ) 7 | ) 8 | 9 | (func (export "loop1") (result i32) 10 | (local $i i32) 11 | (set_local $i (i32.const 0)) 12 | (block $exit (result i32) 13 | (loop $cont (result i32) 14 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 15 | (if (i32.eq (get_local $i) (i32.const 5)) 16 | (then (br $exit (get_local $i))) 17 | ) 18 | (br $cont) 19 | ) 20 | ) 21 | ) 22 | 23 | (func (export "loop2") (result i32) 24 | (local $i i32) 25 | (set_local $i (i32.const 0)) 26 | (block $exit (result i32) 27 | (loop $cont (result i32) 28 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 29 | (if (i32.eq (get_local $i) (i32.const 5)) 30 | (then (br $cont)) 31 | ) 32 | (if (i32.eq (get_local $i) (i32.const 8)) 33 | (then (br $exit (get_local $i))) 34 | ) 35 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 36 | (br $cont) 37 | ) 38 | ) 39 | ) 40 | 41 | (func (export "loop3") (result i32) 42 | (local $i i32) 43 | (set_local $i (i32.const 0)) 44 | (block $exit (result i32) 45 | (loop $cont (result i32) 46 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 47 | (if (i32.eq (get_local $i) (i32.const 5)) 48 | (then (br $exit (get_local $i))) 49 | ) 50 | (get_local $i) 51 | ) 52 | ) 53 | ) 54 | 55 | (func (export "loop4") (param $max i32) (result i32) 56 | (local $i i32) 57 | (set_local $i (i32.const 1)) 58 | (block $exit (result i32) 59 | (loop $cont (result i32) 60 | (set_local $i (i32.add (get_local $i) (get_local $i))) 61 | (if (i32.gt_u (get_local $i) (get_local $max)) 62 | (then (br $exit (get_local $i))) 63 | ) 64 | (br $cont) 65 | ) 66 | ) 67 | ) 68 | 69 | (func (export "loop5") (result i32) 70 | (i32.add 71 | (loop $l (result i32) (i32.const 1)) 72 | (i32.const 1) 73 | ) 74 | ) 75 | 76 | (func (export "if") (result i32) 77 | (local $i i32) 78 | (set_local $i (i32.const 0)) 79 | (block 80 | (if $l 81 | (i32.const 1) 82 | (then (br $l) (set_local $i (i32.const 666))) 83 | ) 84 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 85 | (if $l 86 | (i32.const 1) 87 | (then (br $l) (set_local $i (i32.const 666))) 88 | (else (set_local $i (i32.const 888))) 89 | ) 90 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 91 | (if $l 92 | (i32.const 1) 93 | (then (br $l) (set_local $i (i32.const 666))) 94 | (else (set_local $i (i32.const 888))) 95 | ) 96 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 97 | (if $l 98 | (i32.const 0) 99 | (then (set_local $i (i32.const 888))) 100 | (else (br $l) (set_local $i (i32.const 666))) 101 | ) 102 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 103 | (if $l 104 | (i32.const 0) 105 | (then (set_local $i (i32.const 888))) 106 | (else (br $l) (set_local $i (i32.const 666))) 107 | ) 108 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 109 | ) 110 | (get_local $i) 111 | ) 112 | 113 | (func (export "if2") (result i32) 114 | (local $i i32) 115 | (set_local $i (i32.const 0)) 116 | (block 117 | (if 118 | (i32.const 1) 119 | (then (br 0) (set_local $i (i32.const 666))) 120 | ) 121 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 122 | (if 123 | (i32.const 1) 124 | (then (br 0) (set_local $i (i32.const 666))) 125 | (else (set_local $i (i32.const 888))) 126 | ) 127 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 128 | (if 129 | (i32.const 1) 130 | (then (br 0) (set_local $i (i32.const 666))) 131 | (else (set_local $i (i32.const 888))) 132 | ) 133 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 134 | (if 135 | (i32.const 0) 136 | (then (set_local $i (i32.const 888))) 137 | (else (br 0) (set_local $i (i32.const 666))) 138 | ) 139 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 140 | (if 141 | (i32.const 0) 142 | (then (set_local $i (i32.const 888))) 143 | (else (br 0) (set_local $i (i32.const 666))) 144 | ) 145 | (set_local $i (i32.add (get_local $i) (i32.const 1))) 146 | ) 147 | (get_local $i) 148 | ) 149 | 150 | (func (export "switch") (param i32) (result i32) 151 | (block $ret (result i32) 152 | (i32.mul (i32.const 10) 153 | (block $exit (result i32) 154 | (block $0 155 | (block $default 156 | (block $3 157 | (block $2 158 | (block $1 159 | (br_table $0 $1 $2 $3 $default (get_local 0)) 160 | ) ;; 1 161 | ) ;; 2 162 | (br $exit (i32.const 2)) 163 | ) ;; 3 164 | (br $ret (i32.const 3)) 165 | ) ;; default 166 | ) ;; 0 167 | (i32.const 5) 168 | ) 169 | ) 170 | ) 171 | ) 172 | 173 | (func (export "return") (param i32) (result i32) 174 | (block $default 175 | (block $1 176 | (block $0 177 | (br_table $0 $1 (get_local 0)) 178 | (br $default) 179 | ) ;; 0 180 | (return (i32.const 0)) 181 | ) ;; 1 182 | ) ;; default 183 | (i32.const 2) 184 | ) 185 | 186 | (func (export "br_if0") (result i32) 187 | (local $i i32) 188 | (set_local $i (i32.const 0)) 189 | (block $outer (result i32) 190 | (block $inner 191 | (br_if $inner (i32.const 0)) 192 | (set_local $i (i32.or (get_local $i) (i32.const 0x1))) 193 | (br_if $inner (i32.const 1)) 194 | (set_local $i (i32.or (get_local $i) (i32.const 0x2))) 195 | ) 196 | (drop (br_if $outer 197 | (block (result i32) 198 | (set_local $i (i32.or (get_local $i) (i32.const 0x4))) 199 | (get_local $i) 200 | ) 201 | (i32.const 0) 202 | )) 203 | (set_local $i (i32.or (get_local $i) (i32.const 0x8))) 204 | (drop (br_if $outer 205 | (block (result i32) 206 | (set_local $i (i32.or (get_local $i) (i32.const 0x10))) 207 | (get_local $i) 208 | ) 209 | (i32.const 1) 210 | )) 211 | (set_local $i (i32.or (get_local $i) (i32.const 0x20))) (get_local $i) 212 | ) 213 | ) 214 | 215 | (func (export "br_if1") (result i32) 216 | (block $l0 (result i32) 217 | (drop 218 | (br_if $l0 219 | (block $l1 (result i32) (br $l1 (i32.const 1))) 220 | (i32.const 1) 221 | ) 222 | ) 223 | (i32.const 1) 224 | ) 225 | ) 226 | 227 | (func (export "br_if2") (result i32) 228 | (block $l0 (result i32) 229 | (if (i32.const 1) 230 | (then (br $l0 (block $l1 (result i32) (br $l1 (i32.const 1))))) 231 | ) 232 | (i32.const 1) 233 | ) 234 | ) 235 | 236 | (func (export "br_if3") (result i32) 237 | (local $i1 i32) 238 | (drop 239 | (i32.add 240 | (block $l0 (result i32) 241 | (drop (br_if $l0 242 | (block (result i32) (set_local $i1 (i32.const 1)) (get_local $i1)) 243 | (block (result i32) (set_local $i1 (i32.const 2)) (get_local $i1)) 244 | )) 245 | (i32.const 0) 246 | ) 247 | (i32.const 0) 248 | ) 249 | ) 250 | (get_local $i1) 251 | ) 252 | 253 | (func (export "br") (result i32) 254 | (block $l0 (result i32) 255 | (if (i32.const 1) 256 | (then (br $l0 (block $l1 (result i32) (br $l1 (i32.const 1))))) 257 | (else (block (drop (block $l1 (result i32) (br $l1 (i32.const 1)))))) 258 | ) 259 | (i32.const 1) 260 | ) 261 | ) 262 | 263 | (func (export "shadowing") (result i32) 264 | (block $l1 (result i32) (i32.xor (br $l1 (i32.const 1)) (i32.const 2))) 265 | ) 266 | 267 | (func (export "redefinition") (result i32) 268 | (block $l1 (result i32) 269 | (i32.add 270 | (block $l1 (result i32) (i32.const 2)) 271 | (block $l1 (result i32) (br $l1 (i32.const 3))) 272 | ) 273 | ) 274 | ) 275 | ) 276 | 277 | (assert_return (invoke "block") (i32.const 1)) 278 | (assert_return (invoke "loop1") (i32.const 5)) 279 | (assert_return (invoke "loop2") (i32.const 8)) 280 | (assert_return (invoke "loop3") (i32.const 1)) 281 | (assert_return (invoke "loop4" (i32.const 8)) (i32.const 16)) 282 | (assert_return (invoke "loop5") (i32.const 2)) 283 | (assert_return (invoke "if") (i32.const 5)) 284 | (assert_return (invoke "if2") (i32.const 5)) 285 | (assert_return (invoke "switch" (i32.const 0)) (i32.const 50)) 286 | (assert_return (invoke "switch" (i32.const 1)) (i32.const 20)) 287 | (assert_return (invoke "switch" (i32.const 2)) (i32.const 20)) 288 | (assert_return (invoke "switch" (i32.const 3)) (i32.const 3)) 289 | (assert_return (invoke "switch" (i32.const 4)) (i32.const 50)) 290 | (assert_return (invoke "switch" (i32.const 5)) (i32.const 50)) 291 | (assert_return (invoke "return" (i32.const 0)) (i32.const 0)) 292 | (assert_return (invoke "return" (i32.const 1)) (i32.const 2)) 293 | (assert_return (invoke "return" (i32.const 2)) (i32.const 2)) 294 | (assert_return (invoke "br_if0") (i32.const 0x1d)) 295 | (assert_return (invoke "br_if1") (i32.const 1)) 296 | (assert_return (invoke "br_if2") (i32.const 1)) 297 | (assert_return (invoke "br_if3") (i32.const 2)) 298 | (assert_return (invoke "br") (i32.const 1)) 299 | (assert_return (invoke "shadowing") (i32.const 1)) 300 | (assert_return (invoke "redefinition") (i32.const 5)) 301 | 302 | (assert_invalid 303 | (module (func (block $l (f32.neg (br_if $l (i32.const 1))) (nop)))) 304 | "type mismatch" 305 | ) 306 | (assert_invalid 307 | (module (func (block $l (br_if $l (f32.const 0) (i32.const 1))))) 308 | "type mismatch" 309 | ) 310 | (assert_invalid 311 | (module (func (block $l (br_if $l (f32.const 0) (i32.const 1))))) 312 | "type mismatch" 313 | ) 314 | -------------------------------------------------------------------------------- /test/spec_tests/memory_redundancy.wast: -------------------------------------------------------------------------------- 1 | ;; Test that optimizers don't do redundant-load, store-to-load, or dead-store 2 | ;; optimizations when there are interfering stores, even of different types 3 | ;; and to non-identical addresses. 4 | 5 | (module 6 | (memory 1 1) 7 | 8 | (func (export "zero_everything") 9 | (i32.store (i32.const 0) (i32.const 0)) 10 | (i32.store (i32.const 4) (i32.const 0)) 11 | (i32.store (i32.const 8) (i32.const 0)) 12 | (i32.store (i32.const 12) (i32.const 0)) 13 | ) 14 | 15 | (func (export "test_store_to_load") (result i32) 16 | (i32.store (i32.const 8) (i32.const 0)) 17 | (f32.store (i32.const 5) (f32.const -0.0)) 18 | (i32.load (i32.const 8)) 19 | ) 20 | 21 | (func (export "test_redundant_load") (result i32) 22 | (local $t i32) 23 | (local $s i32) 24 | (set_local $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (set_local $s (i32.load (i32.const 8))) 27 | (i32.add (get_local $t) (get_local $s)) 28 | ) 29 | 30 | (func (export "test_dead_store") (result f32) 31 | (local $t f32) 32 | (i32.store (i32.const 8) (i32.const 0x23232323)) 33 | (set_local $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (get_local $t) 36 | ) 37 | 38 | ;; A function named "malloc" which implementations nonetheless shouldn't 39 | ;; assume behaves like C malloc. 40 | (func $malloc (export "malloc") 41 | (param $size i32) 42 | (result i32) 43 | (i32.const 16) 44 | ) 45 | 46 | ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. 47 | (func (export "malloc_aliasing") 48 | (result i32) 49 | (local $x i32) 50 | (local $y i32) 51 | (set_local $x (call $malloc (i32.const 4))) 52 | (set_local $y (call $malloc (i32.const 4))) 53 | (i32.store (get_local $x) (i32.const 42)) 54 | (i32.store (get_local $y) (i32.const 43)) 55 | (i32.load (get_local $x)) 56 | ) 57 | ) 58 | 59 | (assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) 60 | (invoke "zero_everything") 61 | (assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) 62 | (invoke "zero_everything") 63 | (assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) 64 | (invoke "zero_everything") 65 | (assert_return (invoke "malloc_aliasing") (i32.const 43)) 66 | -------------------------------------------------------------------------------- /test/spec_tests/resizing.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 0) 3 | 4 | (func (export "load_at_zero") (result i32) (i32.load (i32.const 0))) 5 | (func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2))) 6 | 7 | (func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000))) 8 | (func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3))) 9 | 10 | (func (export "grow") (param $sz i32) (result i32) (grow_memory (get_local $sz))) 11 | (func (export "size") (result i32) (current_memory)) 12 | ) 13 | 14 | (assert_return (invoke "size") (i32.const 0)) 15 | (assert_trap (invoke "store_at_zero") "out of bounds memory access") 16 | (assert_trap (invoke "load_at_zero") "out of bounds memory access") 17 | (assert_trap (invoke "store_at_page_size") "out of bounds memory access") 18 | (assert_trap (invoke "load_at_page_size") "out of bounds memory access") 19 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 20 | (assert_return (invoke "size") (i32.const 1)) 21 | (assert_return (invoke "load_at_zero") (i32.const 0)) 22 | (assert_return (invoke "store_at_zero")) 23 | (assert_return (invoke "load_at_zero") (i32.const 2)) 24 | (assert_trap (invoke "store_at_page_size") "out of bounds memory access") 25 | (assert_trap (invoke "load_at_page_size") "out of bounds memory access") 26 | (assert_return (invoke "grow" (i32.const 4)) (i32.const 1)) 27 | (assert_return (invoke "size") (i32.const 5)) 28 | (assert_return (invoke "load_at_zero") (i32.const 2)) 29 | (assert_return (invoke "store_at_zero")) 30 | (assert_return (invoke "load_at_zero") (i32.const 2)) 31 | (assert_return (invoke "load_at_page_size") (i32.const 0)) 32 | (assert_return (invoke "store_at_page_size")) 33 | (assert_return (invoke "load_at_page_size") (i32.const 3)) 34 | 35 | 36 | (module 37 | (memory 0) 38 | (func (export "grow") (param i32) (result i32) (grow_memory (get_local 0))) 39 | ) 40 | 41 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) 42 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 43 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) 44 | (assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) 45 | (assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) 46 | (assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) 47 | 48 | (module 49 | (memory 0 10) 50 | (func (export "grow") (param i32) (result i32) (grow_memory (get_local 0))) 51 | ) 52 | 53 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) 54 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 55 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) 56 | (assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) 57 | (assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) 58 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) 59 | (assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) 60 | (assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) 61 | -------------------------------------------------------------------------------- /test/spec_tests/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | import argparse 5 | import os 6 | import os.path 7 | import unittest 8 | import subprocess 9 | import glob 10 | import sys 11 | 12 | 13 | ownDir = os.path.dirname(os.path.abspath(sys.argv[0])) 14 | inputDir = ownDir 15 | outputDir = os.path.join(inputDir, "_output") 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("--wasm", metavar="", default=os.path.join(os.getcwd(), "wasm")) 19 | parser.add_argument("--js", metavar="") 20 | parser.add_argument("--out", metavar="", default=outputDir) 21 | parser.add_argument("file", nargs='*') 22 | arguments = parser.parse_args() 23 | sys.argv = sys.argv[:1] 24 | 25 | wasmCommand = arguments.wasm 26 | jsCommand = arguments.js 27 | outputDir = arguments.out 28 | inputFiles = arguments.file if arguments.file else glob.glob(os.path.join(inputDir, "*.wast")) 29 | 30 | 31 | class RunTests(unittest.TestCase): 32 | def _runCommand(self, command, logPath, expectedExitCode = 0): 33 | with open(logPath, 'w+') as out: 34 | exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) 35 | self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) 36 | 37 | def _auxFile(self, path): 38 | if os.path.exists(path): 39 | os.remove(path) 40 | return path 41 | 42 | def _compareFile(self, expectFile, actualFile): 43 | if os.path.exists(expectFile): 44 | with open(expectFile) as expect: 45 | with open(actualFile) as actual: 46 | expectText = expect.read() 47 | actualText = actual.read() 48 | self.assertEqual(expectText, actualText) 49 | 50 | def _runTestFile(self, inputPath): 51 | dir, inputFile = os.path.split(inputPath) 52 | outputPath = os.path.join(outputDir, inputFile) 53 | 54 | # Run original file 55 | expectedExitCode = 1 if ".fail." in inputFile else 0 56 | logPath = self._auxFile(outputPath + ".log") 57 | self._runCommand(('%s "%s"') % (wasmCommand, inputPath), logPath, expectedExitCode) 58 | 59 | if expectedExitCode != 0: 60 | return 61 | 62 | return 63 | # Convert to binary and validate again 64 | wasmPath = self._auxFile(outputPath + ".bin.wast") 65 | logPath = self._auxFile(wasmPath + ".log") 66 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath) 67 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasmPath), logPath) 68 | 69 | # Convert back to text and validate again 70 | wastPath = self._auxFile(wasmPath + ".wast") 71 | logPath = self._auxFile(wastPath + ".log") 72 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath) 73 | self._runCommand(('%s -d "%s" ') % (wasmCommand, wastPath), logPath) 74 | 75 | # Convert back to binary once more and compare 76 | wasm2Path = self._auxFile(wastPath + ".bin.wast") 77 | logPath = self._auxFile(wasm2Path + ".log") 78 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath) 79 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasm2Path), logPath) 80 | # TODO: The binary should stay the same, but OCaml's float-string conversions are inaccurate. 81 | # Once we upgrade to OCaml 4.03, use sprintf "%s" for printing floats. 82 | # self._compareFile(wasmPath, wasm2Path) 83 | 84 | # Convert to JavaScript 85 | jsPath = self._auxFile(outputPath.replace(".wast", ".js")) 86 | logPath = self._auxFile(jsPath + ".log") 87 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath) 88 | if jsCommand != None: 89 | self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath) 90 | 91 | if __name__ == "__main__": 92 | if not os.path.exists(outputDir): 93 | os.makedirs(outputDir) 94 | for fileName in inputFiles: 95 | testName = 'test ' + os.path.basename(fileName) 96 | setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) 97 | unittest.main() 98 | -------------------------------------------------------------------------------- /test/spec_tests/select.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "select_i32") (param $lhs i32) (param $rhs i32) (param $cond i32) (result i32) 3 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 4 | 5 | (func (export "select_i64") (param $lhs i64) (param $rhs i64) (param $cond i32) (result i64) 6 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 7 | 8 | (func (export "select_f32") (param $lhs f32) (param $rhs f32) (param $cond i32) (result f32) 9 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 10 | 11 | (func (export "select_f64") (param $lhs f64) (param $rhs f64) (param $cond i32) (result f64) 12 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 13 | 14 | ;; Check that both sides of the select are evaluated 15 | (func (export "select_trap_l") (param $cond i32) (result i32) 16 | (select (unreachable) (i32.const 0) (get_local $cond)) 17 | ) 18 | (func (export "select_trap_r") (param $cond i32) (result i32) 19 | (select (i32.const 0) (unreachable) (get_local $cond)) 20 | ) 21 | 22 | (func (export "select_unreached") 23 | (unreachable) (select) 24 | (unreachable) (i32.const 0) (select) 25 | (unreachable) (i32.const 0) (i32.const 0) (select) 26 | (unreachable) (f32.const 0) (i32.const 0) (select) 27 | (unreachable) 28 | ) 29 | ) 30 | 31 | (assert_return (invoke "select_i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) 32 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) 33 | (assert_return (invoke "select_f32" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) 34 | (assert_return (invoke "select_f64" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) 35 | 36 | (assert_return (invoke "select_i32" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) 37 | (assert_return (invoke "select_i32" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) 38 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) 39 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) 40 | 41 | (assert_return (invoke "select_f32" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) 42 | (assert_return (invoke "select_f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) 43 | (assert_return (invoke "select_f32" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) 44 | (assert_return (invoke "select_f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) 45 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) 46 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) 47 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) 48 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) 49 | 50 | (assert_return (invoke "select_f64" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) 51 | (assert_return (invoke "select_f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) 52 | (assert_return (invoke "select_f64" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) 53 | (assert_return (invoke "select_f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) 54 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) 55 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) 56 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) 57 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) 58 | 59 | (assert_trap (invoke "select_trap_l" (i32.const 1)) "unreachable executed") 60 | (assert_trap (invoke "select_trap_l" (i32.const 0)) "unreachable executed") 61 | (assert_trap (invoke "select_trap_r" (i32.const 1)) "unreachable executed") 62 | (assert_trap (invoke "select_trap_r" (i32.const 0)) "unreachable executed") 63 | 64 | (assert_invalid 65 | (module (func $arity-0 (select (nop) (nop) (i32.const 1)))) 66 | "type mismatch" 67 | ) 68 | -------------------------------------------------------------------------------- /test/spec_tests/set_local.wast: -------------------------------------------------------------------------------- 1 | ;; Test `set_local` operator 2 | 3 | (module 4 | ;; Typing 5 | 6 | (func (export "type-local-i32") (local i32) (set_local 0 (i32.const 0))) 7 | (func (export "type-local-i64") (local i64) (set_local 0 (i64.const 0))) 8 | (func (export "type-local-f32") (local f32) (set_local 0 (f32.const 0))) 9 | (func (export "type-local-f64") (local f64) (set_local 0 (f64.const 0))) 10 | 11 | (func (export "type-param-i32") (param i32) (set_local 0 (i32.const 10))) 12 | (func (export "type-param-i64") (param i64) (set_local 0 (i64.const 11))) 13 | (func (export "type-param-f32") (param f32) (set_local 0 (f32.const 11.1))) 14 | (func (export "type-param-f64") (param f64) (set_local 0 (f64.const 12.2))) 15 | 16 | (func (export "type-mixed") (param i64 f32 f64 i32 i32) (local f32 i64 i64 f64) 17 | (set_local 0 (i64.const 0)) 18 | (set_local 1 (f32.const 0)) 19 | (set_local 2 (f64.const 0)) 20 | (set_local 3 (i32.const 0)) 21 | (set_local 4 (i32.const 0)) 22 | (set_local 5 (f32.const 0)) 23 | (set_local 6 (i64.const 0)) 24 | (set_local 7 (i64.const 0)) 25 | (set_local 8 (f64.const 0)) 26 | ) 27 | 28 | ;; Writing 29 | 30 | (func (export "write") (param i64 f32 f64 i32 i32) (result i64) 31 | (local f32 i64 i64 f64) 32 | (set_local 1 (f32.const -0.3)) 33 | (set_local 3 (i32.const 40)) 34 | (set_local 4 (i32.const -7)) 35 | (set_local 5 (f32.const 5.5)) 36 | (set_local 6 (i64.const 6)) 37 | (set_local 8 (f64.const 8)) 38 | (i64.trunc_s/f64 39 | (f64.add 40 | (f64.convert_u/i64 (get_local 0)) 41 | (f64.add 42 | (f64.promote/f32 (get_local 1)) 43 | (f64.add 44 | (get_local 2) 45 | (f64.add 46 | (f64.convert_u/i32 (get_local 3)) 47 | (f64.add 48 | (f64.convert_s/i32 (get_local 4)) 49 | (f64.add 50 | (f64.promote/f32 (get_local 5)) 51 | (f64.add 52 | (f64.convert_u/i64 (get_local 6)) 53 | (f64.add 54 | (f64.convert_u/i64 (get_local 7)) 55 | (get_local 8) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | ) 64 | ) 65 | ) 66 | ) 67 | 68 | (assert_return (invoke "type-local-i32")) 69 | (assert_return (invoke "type-local-i64")) 70 | (assert_return (invoke "type-local-f32")) 71 | (assert_return (invoke "type-local-f64")) 72 | 73 | (assert_return (invoke "type-param-i32" (i32.const 2))) 74 | (assert_return (invoke "type-param-i64" (i64.const 3))) 75 | (assert_return (invoke "type-param-f32" (f32.const 4.4))) 76 | (assert_return (invoke "type-param-f64" (f64.const 5.5))) 77 | 78 | (assert_return 79 | (invoke "type-mixed" 80 | (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5) 81 | ) 82 | ) 83 | 84 | (assert_return 85 | (invoke "write" 86 | (i64.const 1) (f32.const 2) (f64.const 3.3) (i32.const 4) (i32.const 5) 87 | ) 88 | (i64.const 56) 89 | ) 90 | 91 | 92 | ;; Invalid typing of access to locals 93 | 94 | (assert_invalid 95 | (module (func $type-local-num-vs-num (result i64) (local i32) 96 | (set_local 0 (i32.const 0)) 97 | )) 98 | "type mismatch" 99 | ) 100 | (assert_invalid 101 | (module (func $type-local-num-vs-num (local f32) 102 | (i32.eqz (set_local 0 (f32.const 0))) 103 | )) 104 | "type mismatch" 105 | ) 106 | (assert_invalid 107 | (module (func $type-local-num-vs-num (local f64 i64) 108 | (f64.neg (set_local 1 (i64.const 0))) 109 | )) 110 | "type mismatch" 111 | ) 112 | 113 | (assert_invalid 114 | (module (func $type-local-arg-void-vs-num (local i32) (set_local 0 (nop)))) 115 | "type mismatch" 116 | ) 117 | (assert_invalid 118 | (module (func $type-local-arg-num-vs-num (local i32) (set_local 0 (f32.const 0)))) 119 | "type mismatch" 120 | ) 121 | (assert_invalid 122 | (module (func $type-local-arg-num-vs-num (local f32) (set_local 0 (f64.const 0)))) 123 | "type mismatch" 124 | ) 125 | (assert_invalid 126 | (module (func $type-local-arg-num-vs-num (local f64 i64) (set_local 1 (f64.const 0)))) 127 | "type mismatch" 128 | ) 129 | 130 | 131 | ;; Invalid typing of access to parameters 132 | 133 | (assert_invalid 134 | (module (func $type-param-num-vs-num (param i32) (result i64) (get_local 0))) 135 | "type mismatch" 136 | ) 137 | (assert_invalid 138 | (module (func $type-param-num-vs-num (param f32) (i32.eqz (get_local 0)))) 139 | "type mismatch" 140 | ) 141 | (assert_invalid 142 | (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (get_local 1)))) 143 | "type mismatch" 144 | ) 145 | 146 | (assert_invalid 147 | (module (func $type-param-arg-void-vs-num (param i32) (set_local 0 (nop)))) 148 | "type mismatch" 149 | ) 150 | (assert_invalid 151 | (module (func $type-param-arg-num-vs-num (param i32) (set_local 0 (f32.const 0)))) 152 | "type mismatch" 153 | ) 154 | (assert_invalid 155 | (module (func $type-param-arg-num-vs-num (param f32) (set_local 0 (f64.const 0)))) 156 | "type mismatch" 157 | ) 158 | (assert_invalid 159 | (module (func $type-param-arg-num-vs-num (param f64 i64) (set_local 1 (f64.const 0)))) 160 | "type mismatch" 161 | ) 162 | 163 | 164 | ;; Invalid local index 165 | 166 | (assert_invalid 167 | (module (func $unbound-local (local i32 i64) (get_local 3))) 168 | "unknown local" 169 | ) 170 | (assert_invalid 171 | (module (func $large-local (local i32 i64) (get_local 14324343))) 172 | "unknown local" 173 | ) 174 | 175 | (assert_invalid 176 | (module (func $unbound-param (param i32 i64) (get_local 2))) 177 | "unknown local" 178 | ) 179 | (assert_invalid 180 | (module (func $large-param (local i32 i64) (get_local 714324343))) 181 | "unknown local" 182 | ) 183 | 184 | (assert_invalid 185 | (module (func $unbound-mixed (param i32) (local i32 i64) (get_local 3))) 186 | "unknown local" 187 | ) 188 | (assert_invalid 189 | (module (func $large-mixed (param i64) (local i32 i64) (get_local 214324343))) 190 | "unknown local" 191 | ) 192 | 193 | (assert_invalid 194 | (module (func $type-mixed-arg-num-vs-num (param f32) (local i32) (set_local 1 (f32.const 0)))) 195 | "type mismatch" 196 | ) 197 | (assert_invalid 198 | (module (func $type-mixed-arg-num-vs-num (param i64 i32) (local f32) (set_local 1 (f32.const 0)))) 199 | "type mismatch" 200 | ) 201 | (assert_invalid 202 | (module (func $type-mixed-arg-num-vs-num (param i64) (local f64 i64) (set_local 1 (i64.const 0)))) 203 | "type mismatch" 204 | ) 205 | 206 | -------------------------------------------------------------------------------- /test/spec_tests/stack.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "fac-expr") (param $n i64) (result i64) 3 | (local $i i64) 4 | (local $res i64) 5 | (set_local $i (get_local $n)) 6 | (set_local $res (i64.const 1)) 7 | (block $done 8 | (loop $loop 9 | (if 10 | (i64.eq (get_local $i) (i64.const 0)) 11 | (then (br $done)) 12 | (else 13 | (set_local $res (i64.mul (get_local $i) (get_local $res))) 14 | (set_local $i (i64.sub (get_local $i) (i64.const 1))) 15 | ) 16 | ) 17 | (br $loop) 18 | ) 19 | ) 20 | (get_local $res) 21 | ) 22 | 23 | (func (export "fac-stack") (param $n i64) (result i64) 24 | (local $i i64) 25 | (local $res i64) 26 | (get_local $n) 27 | (set_local $i) 28 | (i64.const 1) 29 | (set_local $res) 30 | (block $done 31 | (loop $loop 32 | (get_local $i) 33 | (i64.const 0) 34 | (i64.eq) 35 | (if 36 | (then (br $done)) 37 | (else 38 | (get_local $i) 39 | (get_local $res) 40 | (i64.mul) 41 | (set_local $res) 42 | (get_local $i) 43 | (i64.const 1) 44 | (i64.sub) 45 | (set_local $i) 46 | ) 47 | ) 48 | (br $loop) 49 | ) 50 | ) 51 | (get_local $res) 52 | ) 53 | 54 | (func (export "fac-stack-raw") (param $n i64) (result i64) 55 | (local $i i64) 56 | (local $res i64) 57 | get_local $n 58 | set_local $i 59 | i64.const 1 60 | set_local $res 61 | block $done 62 | loop $loop 63 | get_local $i 64 | i64.const 0 65 | i64.eq 66 | if $body 67 | br $done 68 | else $body 69 | get_local $i 70 | get_local $res 71 | i64.mul 72 | set_local $res 73 | get_local $i 74 | i64.const 1 75 | i64.sub 76 | set_local $i 77 | end $body 78 | br $loop 79 | end $loop 80 | end $done 81 | get_local $res 82 | ) 83 | 84 | (func (export "fac-mixed") (param $n i64) (result i64) 85 | (local $i i64) 86 | (local $res i64) 87 | (set_local $i (get_local $n)) 88 | (set_local $res (i64.const 1)) 89 | (block $done 90 | (loop $loop 91 | (i64.eq (get_local $i) (i64.const 0)) 92 | (if 93 | (then (br $done)) 94 | (else 95 | (i64.mul (get_local $i) (get_local $res)) 96 | (set_local $res) 97 | (i64.sub (get_local $i) (i64.const 1)) 98 | (set_local $i) 99 | ) 100 | ) 101 | (br $loop) 102 | ) 103 | ) 104 | (get_local $res) 105 | ) 106 | 107 | (func (export "fac-mixed-raw") (param $n i64) (result i64) 108 | (local $i i64) 109 | (local $res i64) 110 | (set_local $i (get_local $n)) 111 | (set_local $res (i64.const 1)) 112 | block $done 113 | loop $loop 114 | (i64.eq (get_local $i) (i64.const 0)) 115 | if 116 | br $done 117 | else 118 | (i64.mul (get_local $i) (get_local $res)) 119 | set_local $res 120 | (i64.sub (get_local $i) (i64.const 1)) 121 | set_local $i 122 | end 123 | br $loop 124 | end 125 | end 126 | get_local $res 127 | ) 128 | ) 129 | 130 | (assert_return (invoke "fac-expr" (i64.const 25)) (i64.const 7034535277573963776)) 131 | (assert_return (invoke "fac-stack" (i64.const 25)) (i64.const 7034535277573963776)) 132 | (assert_return (invoke "fac-mixed" (i64.const 25)) (i64.const 7034535277573963776)) 133 | -------------------------------------------------------------------------------- /test/spec_tests/start.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module (func) (start 1)) 3 | "unknown function" 4 | ) 5 | 6 | (assert_invalid 7 | (module 8 | (func $main (result i32) (return (i32.const 0))) 9 | (start $main) 10 | ) 11 | "start function" 12 | ) 13 | (assert_invalid 14 | (module 15 | (func $main (param $a i32)) 16 | (start $main) 17 | ) 18 | "start function" 19 | ) 20 | 21 | (module 22 | (memory (data "A")) 23 | (func $inc 24 | (i32.store8 25 | (i32.const 0) 26 | (i32.add 27 | (i32.load8_u (i32.const 0)) 28 | (i32.const 1) 29 | ) 30 | ) 31 | ) 32 | (func $get (result i32) 33 | (return (i32.load8_u (i32.const 0))) 34 | ) 35 | (func $main 36 | (call $inc) 37 | (call $inc) 38 | (call $inc) 39 | ) 40 | 41 | (start $main) 42 | (export "inc" (func $inc)) 43 | (export "get" (func $get)) 44 | ) 45 | (assert_return (invoke "get") (i32.const 68)) 46 | (invoke "inc") 47 | (assert_return (invoke "get") (i32.const 69)) 48 | (invoke "inc") 49 | (assert_return (invoke "get") (i32.const 70)) 50 | 51 | (module 52 | (memory (data "A")) 53 | (func $inc 54 | (i32.store8 55 | (i32.const 0) 56 | (i32.add 57 | (i32.load8_u (i32.const 0)) 58 | (i32.const 1) 59 | ) 60 | ) 61 | ) 62 | (func $get (result i32) 63 | (return (i32.load8_u (i32.const 0))) 64 | ) 65 | (func $main 66 | (call $inc) 67 | (call $inc) 68 | (call $inc) 69 | ) 70 | (start 2) 71 | (export "inc" (func $inc)) 72 | (export "get" (func $get)) 73 | ) 74 | (assert_return (invoke "get") (i32.const 68)) 75 | (invoke "inc") 76 | (assert_return (invoke "get") (i32.const 69)) 77 | (invoke "inc") 78 | (assert_return (invoke "get") (i32.const 70)) 79 | 80 | (assert_trap 81 | (module (func $main (unreachable)) (start $main)) 82 | "unreachable" 83 | ) 84 | -------------------------------------------------------------------------------- /test/spec_tests/store_retval.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module (func (param i32) (result i32) (set_local 0 (i32.const 1)))) 3 | "type mismatch" 4 | ) 5 | (assert_invalid 6 | (module (func (param i64) (result i64) (set_local 0 (i64.const 1)))) 7 | "type mismatch" 8 | ) 9 | (assert_invalid 10 | (module (func (param f32) (result f32) (set_local 0 (f32.const 1)))) 11 | "type mismatch" 12 | ) 13 | (assert_invalid 14 | (module (func (param f64) (result f64) (set_local 0 (f64.const 1)))) 15 | "type mismatch" 16 | ) 17 | 18 | (assert_invalid 19 | (module (memory 1) (func (param i32) (result i32) (i32.store (i32.const 0) (i32.const 1)))) 20 | "type mismatch" 21 | ) 22 | (assert_invalid 23 | (module (memory 1) (func (param i64) (result i64) (i64.store (i32.const 0) (i64.const 1)))) 24 | "type mismatch" 25 | ) 26 | (assert_invalid 27 | (module (memory 1) (func (param f32) (result f32) (f32.store (i32.const 0) (f32.const 1)))) 28 | "type mismatch" 29 | ) 30 | (assert_invalid 31 | (module (memory 1) (func (param f64) (result f64) (f64.store (i32.const 0) (f64.const 1)))) 32 | "type mismatch" 33 | ) 34 | 35 | (assert_invalid 36 | (module (memory 1) (func (param i32) (result i32) (i32.store8 (i32.const 0) (i32.const 1)))) 37 | "type mismatch" 38 | ) 39 | (assert_invalid 40 | (module (memory 1) (func (param i32) (result i32) (i32.store16 (i32.const 0) (i32.const 1)))) 41 | "type mismatch" 42 | ) 43 | (assert_invalid 44 | (module (memory 1) (func (param i64) (result i64) (i64.store8 (i32.const 0) (i64.const 1)))) 45 | "type mismatch" 46 | ) 47 | (assert_invalid 48 | (module (memory 1) (func (param i64) (result i64) (i64.store16 (i32.const 0) (i64.const 1)))) 49 | "type mismatch" 50 | ) 51 | (assert_invalid 52 | (module (memory 1) (func (param i64) (result i64) (i64.store32 (i32.const 0) (i64.const 1)))) 53 | "type mismatch" 54 | ) 55 | 56 | -------------------------------------------------------------------------------- /test/spec_tests/switch.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Statement switch 3 | (func (export "stmt") (param $i i32) (result i32) 4 | (local $j i32) 5 | (set_local $j (i32.const 100)) 6 | (block $switch 7 | (block $7 8 | (block $default 9 | (block $6 10 | (block $5 11 | (block $4 12 | (block $3 13 | (block $2 14 | (block $1 15 | (block $0 16 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 17 | (get_local $i) 18 | ) 19 | ) ;; 0 20 | (return (get_local $i)) 21 | ) ;; 1 22 | (nop) 23 | ;; fallthrough 24 | ) ;; 2 25 | ;; fallthrough 26 | ) ;; 3 27 | (set_local $j (i32.sub (i32.const 0) (get_local $i))) 28 | (br $switch) 29 | ) ;; 4 30 | (br $switch) 31 | ) ;; 5 32 | (set_local $j (i32.const 101)) 33 | (br $switch) 34 | ) ;; 6 35 | (set_local $j (i32.const 101)) 36 | ;; fallthrough 37 | ) ;; default 38 | (set_local $j (i32.const 102)) 39 | ) ;; 7 40 | ;; fallthrough 41 | ) 42 | (return (get_local $j)) 43 | ) 44 | 45 | ;; Expression switch 46 | (func (export "expr") (param $i i64) (result i64) 47 | (local $j i64) 48 | (set_local $j (i64.const 100)) 49 | (return 50 | (block $switch (result i64) 51 | (block $7 52 | (block $default 53 | (block $4 54 | (block $5 55 | (block $6 56 | (block $3 57 | (block $2 58 | (block $1 59 | (block $0 60 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 61 | (i32.wrap/i64 (get_local $i)) 62 | ) 63 | ) ;; 0 64 | (return (get_local $i)) 65 | ) ;; 1 66 | (nop) 67 | ;; fallthrough 68 | ) ;; 2 69 | ;; fallthrough 70 | ) ;; 3 71 | (br $switch (i64.sub (i64.const 0) (get_local $i))) 72 | ) ;; 6 73 | (set_local $j (i64.const 101)) 74 | ;; fallthrough 75 | ) ;; 4 76 | ;; fallthrough 77 | ) ;; 5 78 | ;; fallthrough 79 | ) ;; default 80 | (br $switch (get_local $j)) 81 | ) ;; 7 82 | (i64.const -5) 83 | ) 84 | ) 85 | ) 86 | 87 | ;; Argument switch 88 | (func (export "arg") (param $i i32) (result i32) 89 | (return 90 | (block $2 (result i32) 91 | (i32.add (i32.const 10) 92 | (block $1 (result i32) 93 | (i32.add (i32.const 100) 94 | (block $0 (result i32) 95 | (i32.add (i32.const 1000) 96 | (block $default (result i32) 97 | (br_table $0 $1 $2 $default 98 | (i32.mul (i32.const 2) (get_local $i)) 99 | (i32.and (i32.const 3) (get_local $i)) 100 | ) 101 | ) 102 | ) 103 | ) 104 | ) 105 | ) 106 | ) 107 | ) 108 | ) 109 | ) 110 | 111 | ;; Corner cases 112 | (func (export "corner") (result i32) 113 | (block 114 | (br_table 0 (i32.const 0)) 115 | ) 116 | (i32.const 1) 117 | ) 118 | ) 119 | 120 | (assert_return (invoke "stmt" (i32.const 0)) (i32.const 0)) 121 | (assert_return (invoke "stmt" (i32.const 1)) (i32.const -1)) 122 | (assert_return (invoke "stmt" (i32.const 2)) (i32.const -2)) 123 | (assert_return (invoke "stmt" (i32.const 3)) (i32.const -3)) 124 | (assert_return (invoke "stmt" (i32.const 4)) (i32.const 100)) 125 | (assert_return (invoke "stmt" (i32.const 5)) (i32.const 101)) 126 | (assert_return (invoke "stmt" (i32.const 6)) (i32.const 102)) 127 | (assert_return (invoke "stmt" (i32.const 7)) (i32.const 100)) 128 | (assert_return (invoke "stmt" (i32.const -10)) (i32.const 102)) 129 | 130 | (assert_return (invoke "expr" (i64.const 0)) (i64.const 0)) 131 | (assert_return (invoke "expr" (i64.const 1)) (i64.const -1)) 132 | (assert_return (invoke "expr" (i64.const 2)) (i64.const -2)) 133 | (assert_return (invoke "expr" (i64.const 3)) (i64.const -3)) 134 | (assert_return (invoke "expr" (i64.const 6)) (i64.const 101)) 135 | (assert_return (invoke "expr" (i64.const 7)) (i64.const -5)) 136 | (assert_return (invoke "expr" (i64.const -10)) (i64.const 100)) 137 | 138 | (assert_return (invoke "arg" (i32.const 0)) (i32.const 110)) 139 | (assert_return (invoke "arg" (i32.const 1)) (i32.const 12)) 140 | (assert_return (invoke "arg" (i32.const 2)) (i32.const 4)) 141 | (assert_return (invoke "arg" (i32.const 3)) (i32.const 1116)) 142 | (assert_return (invoke "arg" (i32.const 4)) (i32.const 118)) 143 | (assert_return (invoke "arg" (i32.const 5)) (i32.const 20)) 144 | (assert_return (invoke "arg" (i32.const 6)) (i32.const 12)) 145 | (assert_return (invoke "arg" (i32.const 7)) (i32.const 1124)) 146 | (assert_return (invoke "arg" (i32.const 8)) (i32.const 126)) 147 | 148 | (assert_return (invoke "corner") (i32.const 1)) 149 | 150 | (assert_invalid (module (func (br_table 3 (i32.const 0)))) "unknown label") 151 | -------------------------------------------------------------------------------- /test/spec_tests/tee_local.wast: -------------------------------------------------------------------------------- 1 | ;; Test `tee_local` operator 2 | 3 | (module 4 | ;; Typing 5 | 6 | (func (export "type-local-i32") (result i32) (local i32) (tee_local 0 (i32.const 0))) 7 | (func (export "type-local-i64") (result i64) (local i64) (tee_local 0 (i64.const 0))) 8 | (func (export "type-local-f32") (result f32) (local f32) (tee_local 0 (f32.const 0))) 9 | (func (export "type-local-f64") (result f64) (local f64) (tee_local 0 (f64.const 0))) 10 | 11 | (func (export "type-param-i32") (param i32) (result i32) (tee_local 0 (i32.const 10))) 12 | (func (export "type-param-i64") (param i64) (result i64) (tee_local 0 (i64.const 11))) 13 | (func (export "type-param-f32") (param f32) (result f32) (tee_local 0 (f32.const 11.1))) 14 | (func (export "type-param-f64") (param f64) (result f64) (tee_local 0 (f64.const 12.2))) 15 | 16 | (func (export "type-mixed") (param i64 f32 f64 i32 i32) (local f32 i64 i64 f64) 17 | (drop (i64.eqz (tee_local 0 (i64.const 0)))) 18 | (drop (f32.neg (tee_local 1 (f32.const 0)))) 19 | (drop (f64.neg (tee_local 2 (f64.const 0)))) 20 | (drop (i32.eqz (tee_local 3 (i32.const 0)))) 21 | (drop (i32.eqz (tee_local 4 (i32.const 0)))) 22 | (drop (f32.neg (tee_local 5 (f32.const 0)))) 23 | (drop (i64.eqz (tee_local 6 (i64.const 0)))) 24 | (drop (i64.eqz (tee_local 7 (i64.const 0)))) 25 | (drop (f64.neg (tee_local 8 (f64.const 0)))) 26 | ) 27 | 28 | ;; Writing 29 | 30 | (func (export "write") (param i64 f32 f64 i32 i32) (result i64) (local f32 i64 i64 f64) 31 | (drop (tee_local 1 (f32.const -0.3))) 32 | (drop (tee_local 3 (i32.const 40))) 33 | (drop (tee_local 4 (i32.const -7))) 34 | (drop (tee_local 5 (f32.const 5.5))) 35 | (drop (tee_local 6 (i64.const 6))) 36 | (drop (tee_local 8 (f64.const 8))) 37 | (i64.trunc_s/f64 38 | (f64.add 39 | (f64.convert_u/i64 (get_local 0)) 40 | (f64.add 41 | (f64.promote/f32 (get_local 1)) 42 | (f64.add 43 | (get_local 2) 44 | (f64.add 45 | (f64.convert_u/i32 (get_local 3)) 46 | (f64.add 47 | (f64.convert_s/i32 (get_local 4)) 48 | (f64.add 49 | (f64.promote/f32 (get_local 5)) 50 | (f64.add 51 | (f64.convert_u/i64 (get_local 6)) 52 | (f64.add 53 | (f64.convert_u/i64 (get_local 7)) 54 | (get_local 8) 55 | ) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | ) 64 | ) 65 | 66 | ;; Result 67 | 68 | (func (export "result") (param i64 f32 f64 i32 i32) (result f64) 69 | (local f32 i64 i64 f64) 70 | (f64.add 71 | (f64.convert_u/i64 (tee_local 0 (i64.const 1))) 72 | (f64.add 73 | (f64.promote/f32 (tee_local 1 (f32.const 2))) 74 | (f64.add 75 | (tee_local 2 (f64.const 3.3)) 76 | (f64.add 77 | (f64.convert_u/i32 (tee_local 3 (i32.const 4))) 78 | (f64.add 79 | (f64.convert_s/i32 (tee_local 4 (i32.const 5))) 80 | (f64.add 81 | (f64.promote/f32 (tee_local 5 (f32.const 5.5))) 82 | (f64.add 83 | (f64.convert_u/i64 (tee_local 6 (i64.const 6))) 84 | (f64.add 85 | (f64.convert_u/i64 (tee_local 7 (i64.const 0))) 86 | (tee_local 8 (f64.const 8)) 87 | ) 88 | ) 89 | ) 90 | ) 91 | ) 92 | ) 93 | ) 94 | ) 95 | ) 96 | ) 97 | 98 | (assert_return (invoke "type-local-i32") (i32.const 0)) 99 | (assert_return (invoke "type-local-i64") (i64.const 0)) 100 | (assert_return (invoke "type-local-f32") (f32.const 0)) 101 | (assert_return (invoke "type-local-f64") (f64.const 0)) 102 | 103 | (assert_return (invoke "type-param-i32" (i32.const 2)) (i32.const 10)) 104 | (assert_return (invoke "type-param-i64" (i64.const 3)) (i64.const 11)) 105 | (assert_return (invoke "type-param-f32" (f32.const 4.4)) (f32.const 11.1)) 106 | (assert_return (invoke "type-param-f64" (f64.const 5.5)) (f64.const 12.2)) 107 | 108 | (assert_return 109 | (invoke "type-mixed" 110 | (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5) 111 | ) 112 | ) 113 | 114 | (assert_return 115 | (invoke "write" 116 | (i64.const 1) (f32.const 2) (f64.const 3.3) (i32.const 4) (i32.const 5) 117 | ) 118 | (i64.const 56) 119 | ) 120 | 121 | (assert_return 122 | (invoke "result" 123 | (i64.const -1) (f32.const -2) (f64.const -3.3) (i32.const -4) (i32.const -5) 124 | ) 125 | (f64.const 34.8) 126 | ) 127 | 128 | 129 | ;; Invalid typing of access to locals 130 | 131 | (assert_invalid 132 | (module (func $type-local-num-vs-num (result i64) (local i32) (tee_local 0 (i32.const 0)))) 133 | "type mismatch" 134 | ) 135 | (assert_invalid 136 | (module (func $type-local-num-vs-num (local f32) (i32.eqz (tee_local 0 (f32.const 0))))) 137 | "type mismatch" 138 | ) 139 | (assert_invalid 140 | (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (tee_local 1 (i64.const 0))))) 141 | "type mismatch" 142 | ) 143 | 144 | (assert_invalid 145 | (module (func $type-local-arg-void-vs-num (local i32) (tee_local 0 (nop)))) 146 | "type mismatch" 147 | ) 148 | (assert_invalid 149 | (module (func $type-local-arg-num-vs-num (local i32) (tee_local 0 (f32.const 0)))) 150 | "type mismatch" 151 | ) 152 | (assert_invalid 153 | (module (func $type-local-arg-num-vs-num (local f32) (tee_local 0 (f64.const 0)))) 154 | "type mismatch" 155 | ) 156 | (assert_invalid 157 | (module (func $type-local-arg-num-vs-num (local f64 i64) (tee_local 1 (f64.const 0)))) 158 | "type mismatch" 159 | ) 160 | 161 | 162 | ;; Invalid typing of access to parameters 163 | 164 | (assert_invalid 165 | (module (func $type-param-num-vs-num (param i32) (result i64) (get_local 0))) 166 | "type mismatch" 167 | ) 168 | (assert_invalid 169 | (module (func $type-param-num-vs-num (param f32) (i32.eqz (get_local 0)))) 170 | "type mismatch" 171 | ) 172 | (assert_invalid 173 | (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (get_local 1)))) 174 | "type mismatch" 175 | ) 176 | 177 | (assert_invalid 178 | (module (func $type-param-arg-void-vs-num (param i32) (tee_local 0 (nop)))) 179 | "type mismatch" 180 | ) 181 | (assert_invalid 182 | (module (func $type-param-arg-num-vs-num (param i32) (tee_local 0 (f32.const 0)))) 183 | "type mismatch" 184 | ) 185 | (assert_invalid 186 | (module (func $type-param-arg-num-vs-num (param f32) (tee_local 0 (f64.const 0)))) 187 | "type mismatch" 188 | ) 189 | (assert_invalid 190 | (module (func $type-param-arg-num-vs-num (param f64 i64) (tee_local 1 (f64.const 0)))) 191 | "type mismatch" 192 | ) 193 | 194 | 195 | ;; Invalid local index 196 | 197 | (assert_invalid 198 | (module (func $unbound-local (local i32 i64) (get_local 3))) 199 | "unknown local" 200 | ) 201 | (assert_invalid 202 | (module (func $large-local (local i32 i64) (get_local 14324343))) 203 | "unknown local" 204 | ) 205 | 206 | (assert_invalid 207 | (module (func $unbound-param (param i32 i64) (get_local 2))) 208 | "unknown local" 209 | ) 210 | (assert_invalid 211 | (module (func $large-param (local i32 i64) (get_local 714324343))) 212 | "unknown local" 213 | ) 214 | 215 | (assert_invalid 216 | (module (func $unbound-mixed (param i32) (local i32 i64) (get_local 3))) 217 | "unknown local" 218 | ) 219 | (assert_invalid 220 | (module (func $large-mixed (param i64) (local i32 i64) (get_local 214324343))) 221 | "unknown local" 222 | ) 223 | 224 | (assert_invalid 225 | (module (func $type-mixed-arg-num-vs-num (param f32) (local i32) (tee_local 1 (f32.const 0)))) 226 | "type mismatch" 227 | ) 228 | (assert_invalid 229 | (module (func $type-mixed-arg-num-vs-num (param i64 i32) (local f32) (tee_local 1 (f32.const 0)))) 230 | "type mismatch" 231 | ) 232 | (assert_invalid 233 | (module (func $type-mixed-arg-num-vs-num (param i64) (local f64 i64) (tee_local 1 (i64.const 0)))) 234 | "type mismatch" 235 | ) 236 | 237 | -------------------------------------------------------------------------------- /test/spec_tests/token.wast: -------------------------------------------------------------------------------- 1 | ;; Test tokenization 2 | 3 | (assert_malformed 4 | (module quote "(func (drop (i32.const0)))") 5 | "unknown operator" 6 | ) 7 | (assert_malformed 8 | (module quote "(func br 0drop)") 9 | "unknown operator" 10 | ) 11 | -------------------------------------------------------------------------------- /test/spec_tests/traps.wast: -------------------------------------------------------------------------------- 1 | ;; Test that traps are preserved even in instructions which might otherwise 2 | ;; be dead-code-eliminated. These functions all perform an operation and 3 | ;; discard its return value. 4 | 5 | (module 6 | (func (export "no_dce.i32.div_s") (param $x i32) (param $y i32) 7 | (drop (i32.div_s (get_local $x) (get_local $y)))) 8 | (func (export "no_dce.i32.div_u") (param $x i32) (param $y i32) 9 | (drop (i32.div_u (get_local $x) (get_local $y)))) 10 | (func (export "no_dce.i64.div_s") (param $x i64) (param $y i64) 11 | (drop (i64.div_s (get_local $x) (get_local $y)))) 12 | (func (export "no_dce.i64.div_u") (param $x i64) (param $y i64) 13 | (drop (i64.div_u (get_local $x) (get_local $y)))) 14 | ) 15 | 16 | (assert_trap (invoke "no_dce.i32.div_s" (i32.const 1) (i32.const 0)) "integer divide by zero") 17 | (assert_trap (invoke "no_dce.i32.div_u" (i32.const 1) (i32.const 0)) "integer divide by zero") 18 | (assert_trap (invoke "no_dce.i64.div_s" (i64.const 1) (i64.const 0)) "integer divide by zero") 19 | (assert_trap (invoke "no_dce.i64.div_u" (i64.const 1) (i64.const 0)) "integer divide by zero") 20 | (assert_trap (invoke "no_dce.i32.div_s" (i32.const 0x80000000) (i32.const -1)) "integer overflow") 21 | (assert_trap (invoke "no_dce.i64.div_s" (i64.const 0x8000000000000000) (i64.const -1)) "integer overflow") 22 | 23 | (module 24 | (func (export "no_dce.i32.rem_s") (param $x i32) (param $y i32) 25 | (drop (i32.rem_s (get_local $x) (get_local $y)))) 26 | (func (export "no_dce.i32.rem_u") (param $x i32) (param $y i32) 27 | (drop (i32.rem_u (get_local $x) (get_local $y)))) 28 | (func (export "no_dce.i64.rem_s") (param $x i64) (param $y i64) 29 | (drop (i64.rem_s (get_local $x) (get_local $y)))) 30 | (func (export "no_dce.i64.rem_u") (param $x i64) (param $y i64) 31 | (drop (i64.rem_u (get_local $x) (get_local $y)))) 32 | ) 33 | 34 | (assert_trap (invoke "no_dce.i32.rem_s" (i32.const 1) (i32.const 0)) "integer divide by zero") 35 | (assert_trap (invoke "no_dce.i32.rem_u" (i32.const 1) (i32.const 0)) "integer divide by zero") 36 | (assert_trap (invoke "no_dce.i64.rem_s" (i64.const 1) (i64.const 0)) "integer divide by zero") 37 | (assert_trap (invoke "no_dce.i64.rem_u" (i64.const 1) (i64.const 0)) "integer divide by zero") 38 | 39 | (module 40 | (func (export "no_dce.i32.trunc_s_f32") (param $x f32) (drop (i32.trunc_s/f32 (get_local $x)))) 41 | (func (export "no_dce.i32.trunc_u_f32") (param $x f32) (drop (i32.trunc_u/f32 (get_local $x)))) 42 | (func (export "no_dce.i32.trunc_s_f64") (param $x f64) (drop (i32.trunc_s/f64 (get_local $x)))) 43 | (func (export "no_dce.i32.trunc_u_f64") (param $x f64) (drop (i32.trunc_u/f64 (get_local $x)))) 44 | (func (export "no_dce.i64.trunc_s_f32") (param $x f32) (drop (i64.trunc_s/f32 (get_local $x)))) 45 | (func (export "no_dce.i64.trunc_u_f32") (param $x f32) (drop (i64.trunc_u/f32 (get_local $x)))) 46 | (func (export "no_dce.i64.trunc_s_f64") (param $x f64) (drop (i64.trunc_s/f64 (get_local $x)))) 47 | (func (export "no_dce.i64.trunc_u_f64") (param $x f64) (drop (i64.trunc_u/f64 (get_local $x)))) 48 | ) 49 | 50 | (assert_trap (invoke "no_dce.i32.trunc_s_f32" (f32.const nan)) "invalid conversion to integer") 51 | (assert_trap (invoke "no_dce.i32.trunc_u_f32" (f32.const nan)) "invalid conversion to integer") 52 | (assert_trap (invoke "no_dce.i32.trunc_s_f64" (f64.const nan)) "invalid conversion to integer") 53 | (assert_trap (invoke "no_dce.i32.trunc_u_f64" (f64.const nan)) "invalid conversion to integer") 54 | (assert_trap (invoke "no_dce.i64.trunc_s_f32" (f32.const nan)) "invalid conversion to integer") 55 | (assert_trap (invoke "no_dce.i64.trunc_u_f32" (f32.const nan)) "invalid conversion to integer") 56 | (assert_trap (invoke "no_dce.i64.trunc_s_f64" (f64.const nan)) "invalid conversion to integer") 57 | (assert_trap (invoke "no_dce.i64.trunc_u_f64" (f64.const nan)) "invalid conversion to integer") 58 | 59 | (module 60 | (memory 1) 61 | 62 | (func (export "no_dce.i32.load") (param $i i32) (drop (i32.load (get_local $i)))) 63 | (func (export "no_dce.i32.load16_s") (param $i i32) (drop (i32.load16_s (get_local $i)))) 64 | (func (export "no_dce.i32.load16_u") (param $i i32) (drop (i32.load16_u (get_local $i)))) 65 | (func (export "no_dce.i32.load8_s") (param $i i32) (drop (i32.load8_s (get_local $i)))) 66 | (func (export "no_dce.i32.load8_u") (param $i i32) (drop (i32.load8_u (get_local $i)))) 67 | (func (export "no_dce.i64.load") (param $i i32) (drop (i64.load (get_local $i)))) 68 | (func (export "no_dce.i64.load32_s") (param $i i32) (drop (i64.load32_s (get_local $i)))) 69 | (func (export "no_dce.i64.load32_u") (param $i i32) (drop (i64.load32_u (get_local $i)))) 70 | (func (export "no_dce.i64.load16_s") (param $i i32) (drop (i64.load16_s (get_local $i)))) 71 | (func (export "no_dce.i64.load16_u") (param $i i32) (drop (i64.load16_u (get_local $i)))) 72 | (func (export "no_dce.i64.load8_s") (param $i i32) (drop (i64.load8_s (get_local $i)))) 73 | (func (export "no_dce.i64.load8_u") (param $i i32) (drop (i64.load8_u (get_local $i)))) 74 | (func (export "no_dce.f32.load") (param $i i32) (drop (f32.load (get_local $i)))) 75 | (func (export "no_dce.f64.load") (param $i i32) (drop (f64.load (get_local $i)))) 76 | ) 77 | 78 | (assert_trap (invoke "no_dce.i32.load" (i32.const 65536)) "out of bounds memory access") 79 | (assert_trap (invoke "no_dce.i32.load16_s" (i32.const 65536)) "out of bounds memory access") 80 | (assert_trap (invoke "no_dce.i32.load16_u" (i32.const 65536)) "out of bounds memory access") 81 | (assert_trap (invoke "no_dce.i32.load8_s" (i32.const 65536)) "out of bounds memory access") 82 | (assert_trap (invoke "no_dce.i32.load8_u" (i32.const 65536)) "out of bounds memory access") 83 | (assert_trap (invoke "no_dce.i64.load" (i32.const 65536)) "out of bounds memory access") 84 | (assert_trap (invoke "no_dce.i64.load32_s" (i32.const 65536)) "out of bounds memory access") 85 | (assert_trap (invoke "no_dce.i64.load32_u" (i32.const 65536)) "out of bounds memory access") 86 | (assert_trap (invoke "no_dce.i64.load16_s" (i32.const 65536)) "out of bounds memory access") 87 | (assert_trap (invoke "no_dce.i64.load16_u" (i32.const 65536)) "out of bounds memory access") 88 | (assert_trap (invoke "no_dce.i64.load8_s" (i32.const 65536)) "out of bounds memory access") 89 | (assert_trap (invoke "no_dce.i64.load8_u" (i32.const 65536)) "out of bounds memory access") 90 | (assert_trap (invoke "no_dce.f32.load" (i32.const 65536)) "out of bounds memory access") 91 | (assert_trap (invoke "no_dce.f64.load" (i32.const 65536)) "out of bounds memory access") 92 | -------------------------------------------------------------------------------- /test/spec_tests/type.wast: -------------------------------------------------------------------------------- 1 | ;; Test type definitions 2 | 3 | (module 4 | (type (func)) 5 | (type $t (func)) 6 | 7 | (type (func (param i32))) 8 | (type (func (param $x i32))) 9 | (type (func (result i32))) 10 | (type (func (param i32) (result i32))) 11 | (type (func (param $x i32) (result i32))) 12 | 13 | (type (func (param f32 f64))) 14 | ;; (type (func (result i64 f32))) 15 | ;; (type (func (param i32 i64) (result f32 f64))) 16 | 17 | (type (func (param f32) (param f64))) 18 | (type (func (param $x f32) (param f64))) 19 | (type (func (param f32) (param $y f64))) 20 | (type (func (param $x f32) (param $y f64))) 21 | ;; (type (func (result i64) (result f32))) 22 | ;; (type (func (param i32) (param i64) (result f32) (result f64))) 23 | ;; (type (func (param $x i32) (param $y i64) (result f32) (result f64))) 24 | 25 | (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) 26 | ;; (type (func (result i64 i64 f32) (result f32 i32))) 27 | ;; (type 28 | ;; (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) 29 | ;; ) 30 | 31 | (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) 32 | ;; (type 33 | ;; (func (result) (result) (result i64 i64) (result) (result f32) (result)) 34 | ;; ) 35 | ;; (type 36 | ;; (func 37 | ;; (param i32 i32) (param i64 i32) (param) (param $x i32) (param) 38 | ;; (result) (result f32 f64) (result f64 i32) (result) 39 | ;; ) 40 | ;; ) 41 | ) 42 | 43 | (assert_malformed 44 | (module quote "(type (func (result i32) (param i32)))") 45 | "result before parameter" 46 | ) 47 | (assert_malformed 48 | (module quote "(type (func (result $x i32)))") 49 | "unexpected token" 50 | ) 51 | 52 | (assert_invalid 53 | (module (type (func (result i32 i32)))) 54 | "invalid result arity" 55 | ) 56 | (assert_invalid 57 | (module (type (func (result i32) (result i32)))) 58 | "invalid result arity" 59 | ) 60 | -------------------------------------------------------------------------------- /test/spec_tests/unwind.wast: -------------------------------------------------------------------------------- 1 | ;; Test that control-flow transfer unwinds stack and it can be anything after. 2 | 3 | (module 4 | (func (export "func-unwind-by-unreachable") 5 | (i32.const 3) (i64.const 1) (unreachable) 6 | ) 7 | (func (export "func-unwind-by-br") 8 | (i32.const 3) (i64.const 1) (br 0) 9 | ) 10 | (func (export "func-unwind-by-br-value") (result i32) 11 | (i32.const 3) (i64.const 1) (br 0 (i32.const 9)) 12 | ) 13 | (func (export "func-unwind-by-br_table") 14 | (i32.const 3) (i64.const 1) (br_table 0 (i32.const 0)) 15 | ) 16 | (func (export "func-unwind-by-br_table-value") (result i32) 17 | (i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) 18 | ) 19 | (func (export "func-unwind-by-return") (result i32) 20 | (i32.const 3) (i64.const 1) (return (i32.const 9)) 21 | ) 22 | 23 | (func (export "block-unwind-by-unreachable") 24 | (block (i32.const 3) (i64.const 1) (unreachable)) 25 | ) 26 | (func (export "block-unwind-by-br") (result i32) 27 | (block (i32.const 3) (i64.const 1) (br 0)) (i32.const 9) 28 | ) 29 | (func (export "block-unwind-by-br-value") (result i32) 30 | (block (result i32) (i32.const 3) (i64.const 1) (br 0 (i32.const 9))) 31 | ) 32 | (func (export "block-unwind-by-br_table") (result i32) 33 | (block (i32.const 3) (i64.const 1) (br_table 0 (i32.const 0))) (i32.const 9) 34 | ) 35 | (func (export "block-unwind-by-br_table-value") (result i32) 36 | (block (result i32) 37 | (i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) 38 | ) 39 | ) 40 | (func (export "block-unwind-by-return") (result i32) 41 | (block (result i32) (i32.const 3) (i64.const 1) (return (i32.const 9))) 42 | ) 43 | 44 | (func (export "block-nested-unwind-by-unreachable") (result i32) 45 | (block (result i32) (i32.const 3) (block (i64.const 1) (unreachable))) 46 | ) 47 | (func (export "block-nested-unwind-by-br") (result i32) 48 | (block (i32.const 3) (block (i64.const 1) (br 1)) (drop)) (i32.const 9) 49 | ) 50 | (func (export "block-nested-unwind-by-br-value") (result i32) 51 | (block (result i32) 52 | (i32.const 3) (block (i64.const 1) (br 1 (i32.const 9))) 53 | ) 54 | ) 55 | (func (export "block-nested-unwind-by-br_table") (result i32) 56 | (block 57 | (i32.const 3) (block (i64.const 1) (br_table 1 (i32.const 1))) 58 | (drop) 59 | ) 60 | (i32.const 9) 61 | ) 62 | (func (export "block-nested-unwind-by-br_table-value") (result i32) 63 | (block (result i32) 64 | (i32.const 3) 65 | (block (i64.const 1) (br_table 1 (i32.const 9) (i32.const 1))) 66 | ) 67 | ) 68 | (func (export "block-nested-unwind-by-return") (result i32) 69 | (block (result i32) 70 | (i32.const 3) (block (i64.const 1) (return (i32.const 9))) 71 | ) 72 | ) 73 | 74 | (func (export "unary-after-unreachable") (result i32) 75 | (f32.const 0) (unreachable) (i64.eqz) 76 | ) 77 | (func (export "unary-after-br") (result i32) 78 | (block (result i32) (f32.const 0) (br 0 (i32.const 9)) (i64.eqz)) 79 | ) 80 | (func (export "unary-after-br_table") (result i32) 81 | (block (result i32) 82 | (f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0)) (i64.eqz) 83 | ) 84 | ) 85 | (func (export "unary-after-return") (result i32) 86 | (f32.const 0) (return (i32.const 9)) (i64.eqz) 87 | ) 88 | 89 | (func (export "binary-after-unreachable") (result i32) 90 | (f32.const 0) (f64.const 1) (unreachable) (i64.eq) 91 | ) 92 | (func (export "binary-after-br") (result i32) 93 | (block (result i32) 94 | (f32.const 0) (f64.const 1) (br 0 (i32.const 9)) (i64.eq) 95 | ) 96 | ) 97 | (func (export "binary-after-br_table") (result i32) 98 | (block (result i32) 99 | (f32.const 0) (f64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) 100 | (i64.eq) 101 | ) 102 | ) 103 | (func (export "binary-after-return") (result i32) 104 | (f32.const 0) (f64.const 1) (return (i32.const 9)) (i64.eq) 105 | ) 106 | 107 | (func (export "select-after-unreachable") (result i32) 108 | (f32.const 0) (f64.const 1) (i64.const 0) (unreachable) (select) 109 | ) 110 | (func (export "select-after-br") (result i32) 111 | (block (result i32) 112 | (f32.const 0) (f64.const 1) (i64.const 0) (br 0 (i32.const 9)) (select) 113 | ) 114 | ) 115 | (func (export "select-after-br_table") (result i32) 116 | (block (result i32) 117 | (f32.const 0) (f64.const 1) (i64.const 0) 118 | (br_table 0 (i32.const 9) (i32.const 0)) 119 | (select) 120 | ) 121 | ) 122 | (func (export "select-after-return") (result i32) 123 | (f32.const 0) (f64.const 1) (i64.const 1) (return (i32.const 9)) (select) 124 | ) 125 | 126 | (func (export "block-value-after-unreachable") (result i32) 127 | (block (result i32) (f32.const 0) (unreachable)) 128 | ) 129 | (func (export "block-value-after-br") (result i32) 130 | (block (result i32) (f32.const 0) (br 0 (i32.const 9))) 131 | ) 132 | (func (export "block-value-after-br_table") (result i32) 133 | (block (result i32) 134 | (f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0)) 135 | ) 136 | ) 137 | (func (export "block-value-after-return") (result i32) 138 | (block (result i32) (f32.const 0) (return (i32.const 9))) 139 | ) 140 | 141 | (func (export "loop-value-after-unreachable") (result i32) 142 | (loop (result i32) (f32.const 0) (unreachable)) 143 | ) 144 | (func (export "loop-value-after-br") (result i32) 145 | (block (result i32) (loop (result i32) (f32.const 0) (br 1 (i32.const 9)))) 146 | ) 147 | (func (export "loop-value-after-br_table") (result i32) 148 | (block (result i32) 149 | (loop (result i32) 150 | (f32.const 0) (br_table 1 1 (i32.const 9) (i32.const 0)) 151 | ) 152 | ) 153 | ) 154 | (func (export "loop-value-after-return") (result i32) 155 | (loop (result i32) (f32.const 0) (return (i32.const 9))) 156 | ) 157 | ) 158 | 159 | (assert_trap (invoke "func-unwind-by-unreachable") "unreachable") 160 | (assert_return (invoke "func-unwind-by-br")) 161 | (assert_return (invoke "func-unwind-by-br-value") (i32.const 9)) 162 | (assert_return (invoke "func-unwind-by-br_table")) 163 | (assert_return (invoke "func-unwind-by-br_table-value") (i32.const 9)) 164 | (assert_return (invoke "func-unwind-by-return") (i32.const 9)) 165 | 166 | (assert_trap (invoke "block-unwind-by-unreachable") "unreachable") 167 | (assert_return (invoke "block-unwind-by-br") (i32.const 9)) 168 | (assert_return (invoke "block-unwind-by-br-value") (i32.const 9)) 169 | (assert_return (invoke "block-unwind-by-br_table") (i32.const 9)) 170 | (assert_return (invoke "block-unwind-by-br_table-value") (i32.const 9)) 171 | (assert_return (invoke "block-unwind-by-return") (i32.const 9)) 172 | 173 | (assert_trap (invoke "block-nested-unwind-by-unreachable") "unreachable") 174 | (assert_return (invoke "block-nested-unwind-by-br") (i32.const 9)) 175 | (assert_return (invoke "block-nested-unwind-by-br-value") (i32.const 9)) 176 | (assert_return (invoke "block-nested-unwind-by-br_table") (i32.const 9)) 177 | (assert_return (invoke "block-nested-unwind-by-br_table-value") (i32.const 9)) 178 | (assert_return (invoke "block-nested-unwind-by-return") (i32.const 9)) 179 | 180 | (assert_trap (invoke "unary-after-unreachable") "unreachable") 181 | (assert_return (invoke "unary-after-br") (i32.const 9)) 182 | (assert_return (invoke "unary-after-br_table") (i32.const 9)) 183 | (assert_return (invoke "unary-after-return") (i32.const 9)) 184 | 185 | (assert_trap (invoke "binary-after-unreachable") "unreachable") 186 | (assert_return (invoke "binary-after-br") (i32.const 9)) 187 | (assert_return (invoke "binary-after-br_table") (i32.const 9)) 188 | (assert_return (invoke "binary-after-return") (i32.const 9)) 189 | 190 | (assert_trap (invoke "select-after-unreachable") "unreachable") 191 | (assert_return (invoke "select-after-br") (i32.const 9)) 192 | (assert_return (invoke "select-after-br_table") (i32.const 9)) 193 | (assert_return (invoke "select-after-return") (i32.const 9)) 194 | 195 | (assert_trap (invoke "block-value-after-unreachable") "unreachable") 196 | (assert_return (invoke "block-value-after-br") (i32.const 9)) 197 | (assert_return (invoke "block-value-after-br_table") (i32.const 9)) 198 | (assert_return (invoke "block-value-after-return") (i32.const 9)) 199 | 200 | (assert_trap (invoke "loop-value-after-unreachable") "unreachable") 201 | (assert_return (invoke "loop-value-after-br") (i32.const 9)) 202 | (assert_return (invoke "loop-value-after-br_table") (i32.const 9)) 203 | (assert_return (invoke "loop-value-after-return") (i32.const 9)) 204 | --------------------------------------------------------------------------------