├── .gitignore ├── LICENSE ├── README.md ├── external ├── LICENSE.txt ├── m3_api_libc.c ├── m3_api_libc.h ├── m3_api_meta_wasi.c ├── m3_api_tracer.c ├── m3_api_tracer.h ├── m3_api_uvwasi.c ├── m3_api_wasi.c ├── m3_api_wasi.h ├── m3_bind.c ├── m3_bind.h ├── m3_code.c ├── m3_code.h ├── m3_compile.c ├── m3_compile.h ├── m3_config.h ├── m3_config_platforms.h ├── m3_core.c ├── m3_core.h ├── m3_env.c ├── m3_env.h ├── m3_exception.h ├── m3_exec.c ├── m3_exec.h ├── m3_exec_defs.h ├── m3_function.c ├── m3_function.h ├── m3_info.c ├── m3_info.h ├── m3_math_utils.h ├── m3_module.c ├── m3_parse.c ├── wasm3.h └── wasm3_defs.h ├── genie.lua └── src ├── editor └── visual_script_plugins.cpp ├── m3_lumix.c ├── m3_lumix.h ├── script.cpp └── script.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | tmp/ 32 | projects/tmp 33 | !projects/genie.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mikulas Florek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visual scripting plugin for [Lumix Engine](https://github.com/nem0/LumixEngine) 2 | 3 | 4 | [![Discord Chat](https://img.shields.io/discord/480318777943392266.svg)](https://discord.gg/RgFybs6) 5 | [![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) 6 | [![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/mikulasflorek) 7 | 8 | Work in progress, very early stage. 9 | 10 | Using [WASM3](https://github.com/wasm3/wasm3) as runtime. 11 | 12 | ![Screenshot](https://user-images.githubusercontent.com/153526/204159230-75e10b12-c2e5-4339-a592-1630bfe789b6.png) 13 | 14 | -------------------------------------------------------------------------------- /external/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Steven Massey, Volodymyr Shymanskyy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /external/m3_api_libc.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_libc.c 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #define _POSIX_C_SOURCE 200809L 9 | 10 | #include "m3_api_libc.h" 11 | 12 | #include "m3_env.h" 13 | #include "m3_exception.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | typedef uint32_t wasm_ptr_t; 20 | typedef uint32_t wasm_size_t; 21 | 22 | m3ApiRawFunction(m3_libc_abort) 23 | { 24 | m3ApiTrap(m3Err_trapAbort); 25 | } 26 | 27 | m3ApiRawFunction(m3_libc_exit) 28 | { 29 | m3ApiGetArg (int32_t, code) 30 | 31 | m3ApiTrap(m3Err_trapExit); 32 | } 33 | 34 | 35 | m3ApiRawFunction(m3_libc_memset) 36 | { 37 | m3ApiReturnType (int32_t) 38 | 39 | m3ApiGetArgMem (void*, i_ptr) 40 | m3ApiGetArg (int32_t, i_value) 41 | m3ApiGetArg (wasm_size_t, i_size) 42 | 43 | m3ApiCheckMem(i_ptr, i_size); 44 | 45 | u32 result = m3ApiPtrToOffset(memset (i_ptr, i_value, i_size)); 46 | m3ApiReturn(result); 47 | } 48 | 49 | m3ApiRawFunction(m3_libc_memmove) 50 | { 51 | m3ApiReturnType (int32_t) 52 | 53 | m3ApiGetArgMem (void*, o_dst) 54 | m3ApiGetArgMem (void*, i_src) 55 | m3ApiGetArg (wasm_size_t, i_size) 56 | 57 | m3ApiCheckMem(o_dst, i_size); 58 | m3ApiCheckMem(i_src, i_size); 59 | 60 | u32 result = m3ApiPtrToOffset(memmove (o_dst, i_src, i_size)); 61 | m3ApiReturn(result); 62 | } 63 | 64 | m3ApiRawFunction(m3_libc_print) 65 | { 66 | m3ApiReturnType (uint32_t) 67 | 68 | m3ApiGetArgMem (void*, i_ptr) 69 | m3ApiGetArg (wasm_size_t, i_size) 70 | 71 | m3ApiCheckMem(i_ptr, i_size); 72 | 73 | fwrite(i_ptr, i_size, 1, stdout); 74 | fflush(stdout); 75 | 76 | m3ApiReturn(i_size); 77 | } 78 | 79 | static 80 | void internal_itoa(int n, char s[], int radix) 81 | { 82 | static char const HEXDIGITS[0x10] = { 83 | '0', '1', '2', '3', '4', '5', '6', '7', 84 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 85 | }; 86 | 87 | int i, j, sign; 88 | char c; 89 | 90 | if ((sign = n) < 0) { n = -n; } 91 | i = 0; 92 | do { 93 | s[i++] = HEXDIGITS[n % radix]; 94 | } while ((n /= radix) > 0); 95 | 96 | if (sign < 0) { s[i++] = '-'; } 97 | s[i] = '\0'; 98 | 99 | // reverse 100 | for (i = 0, j = strlen(s)-1; i d_m3MaxSaneFunctionArgRetCount); 47 | 48 | _ (AllocFuncType (& funcType, (u32) maxNumTypes)); 49 | 50 | u8 * typelist = funcType->types; 51 | 52 | bool parsingRets = true; 53 | while (* sig) 54 | { 55 | char typeChar = * sig++; 56 | 57 | if (typeChar == '(') 58 | { 59 | parsingRets = false; 60 | continue; 61 | } 62 | else if ( typeChar == ' ') 63 | continue; 64 | else if (typeChar == ')') 65 | break; 66 | 67 | u8 type = ConvertTypeCharToTypeId (typeChar); 68 | 69 | _throwif ("unknown argument type char", c_m3Type_unknown == type); 70 | 71 | if (type == c_m3Type_none) 72 | continue; 73 | 74 | if (parsingRets) 75 | { 76 | _throwif ("malformed signature; return count overflow", funcType->numRets >= maxNumTypes); 77 | funcType->numRets++; 78 | *typelist++ = type; 79 | } 80 | else 81 | { 82 | _throwif ("malformed signature; arg count overflow", (u32)(funcType->numRets) + funcType->numArgs >= maxNumTypes); 83 | funcType->numArgs++; 84 | *typelist++ = type; 85 | } 86 | } 87 | 88 | } _catch: 89 | 90 | if (result) 91 | m3_Free (funcType); 92 | 93 | * o_functionType = funcType; 94 | 95 | return result; 96 | } 97 | 98 | 99 | static 100 | M3Result ValidateSignature (IM3Function i_function, ccstr_t i_linkingSignature) 101 | { 102 | M3Result result = m3Err_none; 103 | 104 | IM3FuncType ftype = NULL; 105 | _ (SignatureToFuncType (& ftype, i_linkingSignature)); 106 | 107 | if (not AreFuncTypesEqual (ftype, i_function->funcType)) 108 | { 109 | m3log (module, "expected: %s", SPrintFuncTypeSignature (ftype)); 110 | m3log (module, " found: %s", SPrintFuncTypeSignature (i_function->funcType)); 111 | 112 | _throw ("function signature mismatch"); 113 | } 114 | 115 | _catch: 116 | 117 | m3_Free (ftype); 118 | 119 | return result; 120 | } 121 | 122 | 123 | M3Result FindAndLinkFunction (IM3Module io_module, 124 | ccstr_t i_moduleName, 125 | ccstr_t i_functionName, 126 | ccstr_t i_signature, 127 | voidptr_t i_function, 128 | voidptr_t i_userdata) 129 | { 130 | _try { 131 | _throwif(m3Err_moduleNotLinked, !io_module->runtime); 132 | 133 | const bool wildcardModule = (strcmp (i_moduleName, "*") == 0); 134 | 135 | result = m3Err_functionLookupFailed; 136 | 137 | for (u32 i = 0; i < io_module->numFunctions; ++i) 138 | { 139 | const IM3Function f = & io_module->functions [i]; 140 | 141 | if (f->import.moduleUtf8 and f->import.fieldUtf8) 142 | { 143 | if (strcmp (f->import.fieldUtf8, i_functionName) == 0 and 144 | (wildcardModule or strcmp (f->import.moduleUtf8, i_moduleName) == 0)) 145 | { 146 | if (i_signature) { 147 | _ (ValidateSignature (f, i_signature)); 148 | } 149 | _ (CompileRawFunction (io_module, f, i_function, i_userdata)); 150 | } 151 | } 152 | } 153 | } _catch: 154 | return result; 155 | } 156 | 157 | M3Result m3_LinkRawFunctionEx (IM3Module io_module, 158 | const char * const i_moduleName, 159 | const char * const i_functionName, 160 | const char * const i_signature, 161 | M3RawCall i_function, 162 | const void * i_userdata) 163 | { 164 | return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, i_userdata); 165 | } 166 | 167 | M3Result m3_LinkRawFunction (IM3Module io_module, 168 | const char * const i_moduleName, 169 | const char * const i_functionName, 170 | const char * const i_signature, 171 | M3RawCall i_function) 172 | { 173 | return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, NULL); 174 | } 175 | 176 | -------------------------------------------------------------------------------- /external/m3_bind.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_bind.h 3 | // 4 | // Created by Steven Massey on 2/27/20. 5 | // Copyright © 2020 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_bind_h 9 | #define m3_bind_h 10 | 11 | #include "m3_env.h" 12 | 13 | d_m3BeginExternC 14 | 15 | u8 ConvertTypeCharToTypeId (char i_code); 16 | M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature); 17 | 18 | d_m3EndExternC 19 | 20 | #endif /* m3_bind_h */ 21 | -------------------------------------------------------------------------------- /external/m3_code.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.c 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include 9 | #include "m3_code.h" 10 | #include "m3_env.h" 11 | 12 | //--------------------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines) 16 | { 17 | IM3CodePage page; 18 | 19 | // check multiplication overflow 20 | if (i_minNumLines > UINT_MAX / sizeof (code_t)) { 21 | return NULL; 22 | } 23 | u32 pageSize = sizeof (M3CodePageHeader) + sizeof (code_t) * i_minNumLines; 24 | 25 | // check addition overflow 26 | if (pageSize < sizeof (M3CodePageHeader)) { 27 | return NULL; 28 | } 29 | 30 | pageSize = (pageSize + (d_m3CodePageAlignSize-1)) & ~(d_m3CodePageAlignSize-1); // align 31 | // check alignment overflow 32 | if (pageSize == 0) { 33 | return NULL; 34 | } 35 | 36 | page = (IM3CodePage)m3_Malloc ("M3CodePage", pageSize); 37 | 38 | if (page) 39 | { 40 | page->info.sequence = ++i_runtime->newCodePageSequence; 41 | page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); 42 | 43 | #if d_m3RecordBacktraces 44 | u32 pageSizeBt = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * page->info.numLines; 45 | page->info.mapping = (M3CodeMappingPage *)m3_Malloc ("M3CodeMappingPage", pageSizeBt); 46 | 47 | if (page->info.mapping) 48 | { 49 | page->info.mapping->size = 0; 50 | page->info.mapping->capacity = page->info.numLines; 51 | } 52 | else 53 | { 54 | m3_Free (page); 55 | return NULL; 56 | } 57 | page->info.mapping->basePC = GetPageStartPC(page); 58 | #endif // d_m3RecordBacktraces 59 | 60 | m3log (runtime, "new page: %p; seq: %d; bytes: %d; lines: %d", GetPagePC (page), page->info.sequence, pageSize, page->info.numLines); 61 | } 62 | 63 | return page; 64 | } 65 | 66 | 67 | void FreeCodePages (IM3CodePage * io_list) 68 | { 69 | IM3CodePage page = * io_list; 70 | 71 | while (page) 72 | { 73 | m3log (code, "free page: %d; %p; util: %3.1f%%", page->info.sequence, page, 100. * page->info.lineIndex / page->info.numLines); 74 | 75 | IM3CodePage next = page->info.next; 76 | #if d_m3RecordBacktraces 77 | m3_Free (page->info.mapping); 78 | #endif // d_m3RecordBacktraces 79 | m3_Free (page); 80 | page = next; 81 | } 82 | 83 | * io_list = NULL; 84 | } 85 | 86 | 87 | u32 NumFreeLines (IM3CodePage i_page) 88 | { 89 | d_m3Assert (i_page->info.lineIndex <= i_page->info.numLines); 90 | 91 | return i_page->info.numLines - i_page->info.lineIndex; 92 | } 93 | 94 | 95 | void EmitWord_impl (IM3CodePage i_page, void * i_word) 96 | { d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 97 | i_page->code [i_page->info.lineIndex++] = i_word; 98 | } 99 | 100 | void EmitWord32 (IM3CodePage i_page, const u32 i_word) 101 | { d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 102 | memcpy (& i_page->code[i_page->info.lineIndex++], & i_word, sizeof(i_word)); 103 | } 104 | 105 | void EmitWord64 (IM3CodePage i_page, const u64 i_word) 106 | { 107 | #if M3_SIZEOF_PTR == 4 108 | d_m3Assert (i_page->info.lineIndex+2 <= i_page->info.numLines); 109 | memcpy (& i_page->code[i_page->info.lineIndex], & i_word, sizeof(i_word)); 110 | i_page->info.lineIndex += 2; 111 | #else 112 | d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 113 | memcpy (& i_page->code[i_page->info.lineIndex], & i_word, sizeof(i_word)); 114 | i_page->info.lineIndex += 1; 115 | #endif 116 | } 117 | 118 | 119 | #if d_m3RecordBacktraces 120 | void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset) 121 | { 122 | M3CodeMappingPage * page = i_page->info.mapping; 123 | d_m3Assert (page->size < page->capacity); 124 | 125 | M3CodeMapEntry * entry = & page->entries[page->size++]; 126 | pc_t pc = GetPagePC (i_page); 127 | 128 | entry->pcOffset = pc - page->basePC; 129 | entry->moduleOffset = i_moduleOffset; 130 | } 131 | #endif // d_m3RecordBacktraces 132 | 133 | pc_t GetPageStartPC (IM3CodePage i_page) 134 | { 135 | return & i_page->code [0]; 136 | } 137 | 138 | 139 | pc_t GetPagePC (IM3CodePage i_page) 140 | { 141 | if (i_page) 142 | return & i_page->code [i_page->info.lineIndex]; 143 | else 144 | return NULL; 145 | } 146 | 147 | 148 | void PushCodePage (IM3CodePage * i_list, IM3CodePage i_codePage) 149 | { 150 | IM3CodePage next = * i_list; 151 | i_codePage->info.next = next; 152 | * i_list = i_codePage; 153 | } 154 | 155 | 156 | IM3CodePage PopCodePage (IM3CodePage * i_list) 157 | { 158 | IM3CodePage page = * i_list; 159 | * i_list = page->info.next; 160 | page->info.next = NULL; 161 | 162 | return page; 163 | } 164 | 165 | 166 | 167 | u32 FindCodePageEnd (IM3CodePage i_list, IM3CodePage * o_end) 168 | { 169 | u32 numPages = 0; 170 | * o_end = NULL; 171 | 172 | while (i_list) 173 | { 174 | * o_end = i_list; 175 | ++numPages; 176 | i_list = i_list->info.next; 177 | } 178 | 179 | return numPages; 180 | } 181 | 182 | 183 | u32 CountCodePages (IM3CodePage i_list) 184 | { 185 | IM3CodePage unused; 186 | return FindCodePageEnd (i_list, & unused); 187 | } 188 | 189 | 190 | IM3CodePage GetEndCodePage (IM3CodePage i_list) 191 | { 192 | IM3CodePage end; 193 | FindCodePageEnd (i_list, & end); 194 | 195 | return end; 196 | } 197 | 198 | #if d_m3RecordBacktraces 199 | bool ContainsPC (IM3CodePage i_page, pc_t i_pc) 200 | { 201 | return GetPageStartPC (i_page) <= i_pc && i_pc < GetPagePC (i_page); 202 | } 203 | 204 | 205 | bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) 206 | { 207 | M3CodeMappingPage * mapping = i_page->info.mapping; 208 | 209 | u32 pcOffset = i_pc - mapping->basePC; 210 | 211 | u32 left = 0; 212 | u32 right = mapping->size; 213 | 214 | while (left < right) 215 | { 216 | u32 mid = left + (right - left) / 2; 217 | 218 | if (mapping->entries[mid].pcOffset < pcOffset) 219 | { 220 | left = mid + 1; 221 | } 222 | else if (mapping->entries[mid].pcOffset > pcOffset) 223 | { 224 | right = mid; 225 | } 226 | else 227 | { 228 | *o_moduleOffset = mapping->entries[mid].moduleOffset; 229 | return true; 230 | } 231 | } 232 | 233 | // Getting here means left is now one more than the element we want. 234 | if (left > 0) 235 | { 236 | left--; 237 | *o_moduleOffset = mapping->entries[left].moduleOffset; 238 | return true; 239 | } 240 | else return false; 241 | } 242 | #endif // d_m3RecordBacktraces 243 | 244 | //--------------------------------------------------------------------------------------------------------------------------------- 245 | 246 | 247 | -------------------------------------------------------------------------------- /external/m3_code.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_code_h 9 | #define m3_code_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | typedef struct M3CodePage 16 | { 17 | M3CodePageHeader info; 18 | code_t code [1]; 19 | } 20 | M3CodePage; 21 | 22 | typedef M3CodePage * IM3CodePage; 23 | 24 | 25 | IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines); 26 | 27 | void FreeCodePages (IM3CodePage * io_list); 28 | 29 | u32 NumFreeLines (IM3CodePage i_page); 30 | pc_t GetPageStartPC (IM3CodePage i_page); 31 | pc_t GetPagePC (IM3CodePage i_page); 32 | void EmitWord_impl (IM3CodePage i_page, void* i_word); 33 | void EmitWord32 (IM3CodePage i_page, u32 i_word); 34 | void EmitWord64 (IM3CodePage i_page, u64 i_word); 35 | # if d_m3RecordBacktraces 36 | void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); 37 | # endif // d_m3RecordBacktraces 38 | 39 | void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); 40 | IM3CodePage PopCodePage (IM3CodePage * io_list); 41 | 42 | IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid 43 | u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid 44 | 45 | # if d_m3RecordBacktraces 46 | bool ContainsPC (IM3CodePage i_page, pc_t i_pc); 47 | bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); 48 | # endif // d_m3RecordBacktraces 49 | 50 | # ifdef DEBUG 51 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); 52 | # endif 53 | 54 | #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) 55 | 56 | //--------------------------------------------------------------------------------------------------------------------------------- 57 | 58 | # if d_m3RecordBacktraces 59 | 60 | typedef struct M3CodeMapEntry 61 | { 62 | u32 pcOffset; 63 | u32 moduleOffset; 64 | } 65 | M3CodeMapEntry; 66 | 67 | typedef struct M3CodeMappingPage 68 | { 69 | pc_t basePC; 70 | u32 size; 71 | u32 capacity; 72 | M3CodeMapEntry entries []; 73 | } 74 | M3CodeMappingPage; 75 | 76 | # endif // d_m3RecordBacktraces 77 | 78 | d_m3EndExternC 79 | 80 | #endif // m3_code_h 81 | -------------------------------------------------------------------------------- /external/m3_compile.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_compile.h 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_compile_h 9 | #define m3_compile_h 10 | 11 | #include "m3_code.h" 12 | #include "m3_exec_defs.h" 13 | #include "m3_function.h" 14 | 15 | d_m3BeginExternC 16 | 17 | enum 18 | { 19 | c_waOp_block = 0x02, 20 | c_waOp_loop = 0x03, 21 | c_waOp_if = 0x04, 22 | c_waOp_else = 0x05, 23 | c_waOp_end = 0x0b, 24 | c_waOp_branch = 0x0c, 25 | c_waOp_branchTable = 0x0e, 26 | c_waOp_branchIf = 0x0d, 27 | c_waOp_call = 0x10, 28 | c_waOp_getLocal = 0x20, 29 | c_waOp_setLocal = 0x21, 30 | c_waOp_teeLocal = 0x22, 31 | 32 | c_waOp_getGlobal = 0x23, 33 | 34 | c_waOp_store_f32 = 0x38, 35 | c_waOp_store_f64 = 0x39, 36 | 37 | c_waOp_i32_const = 0x41, 38 | c_waOp_i64_const = 0x42, 39 | c_waOp_f32_const = 0x43, 40 | c_waOp_f64_const = 0x44, 41 | 42 | c_waOp_extended = 0xfc, 43 | 44 | c_waOp_memoryCopy = 0xfc0a, 45 | c_waOp_memoryFill = 0xfc0b 46 | }; 47 | 48 | 49 | #define d_FuncRetType(ftype,i) ((ftype)->types[(i)]) 50 | #define d_FuncArgType(ftype,i) ((ftype)->types[(ftype)->numRets + (i)]) 51 | 52 | //----------------------------------------------------------------------------------------------------------------------------------- 53 | 54 | typedef struct M3CompilationScope 55 | { 56 | struct M3CompilationScope * outer; 57 | 58 | pc_t pc; // used by ContinueLoop's 59 | pc_t patches; 60 | i32 depth; 61 | u16 exitStackIndex; 62 | u16 blockStackIndex; 63 | // u16 topSlot; 64 | IM3FuncType type; 65 | m3opcode_t opcode; 66 | bool isPolymorphic; 67 | } 68 | M3CompilationScope; 69 | 70 | typedef M3CompilationScope * IM3CompilationScope; 71 | 72 | typedef struct 73 | { 74 | IM3Runtime runtime; 75 | IM3Module module; 76 | 77 | bytes_t wasm; 78 | bytes_t wasmEnd; 79 | bytes_t lastOpcodeStart; 80 | 81 | M3CompilationScope block; 82 | 83 | IM3Function function; 84 | 85 | IM3CodePage page; 86 | 87 | #ifdef DEBUG 88 | u32 numEmits; 89 | u32 numOpcodes; 90 | #endif 91 | 92 | u16 stackFirstDynamicIndex; // args and locals are pushed to the stack so that their slot locations can be tracked. the wasm model itself doesn't 93 | // treat these values as being on the stack, so stackFirstDynamicIndex marks the start of the real Wasm stack 94 | u16 stackIndex; // current stack top 95 | 96 | u16 slotFirstConstIndex; 97 | u16 slotMaxConstIndex; // as const's are encountered during compilation this tracks their location in the "real" stack 98 | 99 | u16 slotFirstLocalIndex; 100 | u16 slotFirstDynamicIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. 101 | 102 | u16 maxStackSlots; 103 | 104 | m3slot_t constants [d_m3MaxConstantTableSize]; 105 | 106 | // 'wasmStack' holds slot locations 107 | u16 wasmStack [d_m3MaxFunctionStackHeight]; 108 | u8 typeStack [d_m3MaxFunctionStackHeight]; 109 | 110 | // 'm3Slots' contains allocation usage counts 111 | u8 m3Slots [d_m3MaxFunctionSlots]; 112 | 113 | u16 slotMaxAllocatedIndexPlusOne; 114 | 115 | u16 regStackIndexPlusOne [2]; 116 | 117 | m3opcode_t previousOpcode; 118 | } 119 | M3Compilation; 120 | 121 | typedef M3Compilation * IM3Compilation; 122 | 123 | typedef M3Result (* M3Compiler) (IM3Compilation, m3opcode_t); 124 | 125 | 126 | //----------------------------------------------------------------------------------------------------------------------------------- 127 | 128 | 129 | typedef struct M3OpInfo 130 | { 131 | #ifdef DEBUG 132 | const char * const name; 133 | #endif 134 | 135 | i8 stackOffset; 136 | u8 type; 137 | 138 | // for most operations: 139 | // [0]= top operand in register, [1]= top operand in stack, [2]= both operands in stack 140 | IM3Operation operations [4]; 141 | 142 | M3Compiler compiler; 143 | } 144 | M3OpInfo; 145 | 146 | typedef const M3OpInfo * IM3OpInfo; 147 | 148 | IM3OpInfo GetOpInfo (m3opcode_t opcode); 149 | 150 | // TODO: This helper should be removed, when MultiValue is implemented 151 | static inline 152 | u8 GetSingleRetType(IM3FuncType ftype) { 153 | return (ftype && ftype->numRets) ? ftype->types[0] : (u8)c_m3Type_none; 154 | } 155 | 156 | static const u16 c_m3RegisterUnallocated = 0; 157 | static const u16 c_slotUnused = 0xffff; 158 | 159 | static inline 160 | bool IsRegisterAllocated (IM3Compilation o, u32 i_register) 161 | { 162 | return (o->regStackIndexPlusOne [i_register] != c_m3RegisterUnallocated); 163 | } 164 | 165 | static inline 166 | bool IsStackPolymorphic (IM3Compilation o) 167 | { 168 | return o->block.isPolymorphic; 169 | } 170 | 171 | static inline bool IsRegisterSlotAlias (u16 i_slot) { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); } 172 | static inline bool IsFpRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Fp0SlotAlias); } 173 | static inline bool IsIntRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Reg0SlotAlias); } 174 | 175 | 176 | #ifdef DEBUG 177 | #define M3OP(...) { __VA_ARGS__ } 178 | #define M3OP_RESERVED { "reserved" } 179 | #else 180 | // Strip-off name 181 | #define M3OP(name, ...) { __VA_ARGS__ } 182 | #define M3OP_RESERVED { 0 } 183 | #endif 184 | 185 | #if d_m3HasFloat 186 | #define M3OP_F M3OP 187 | #elif d_m3NoFloatDynamic 188 | #define M3OP_F(n,o,t,op,...) M3OP(n, o, t, { op_Unsupported, op_Unsupported, op_Unsupported, op_Unsupported }, __VA_ARGS__) 189 | #else 190 | #define M3OP_F(...) { 0 } 191 | #endif 192 | 193 | //----------------------------------------------------------------------------------------------------------------------------------- 194 | 195 | u16 GetMaxUsedSlotPlusOne (IM3Compilation o); 196 | 197 | M3Result CompileBlock (IM3Compilation io, IM3FuncType i_blockType, m3opcode_t i_blockOpcode); 198 | 199 | M3Result CompileBlockStatements (IM3Compilation io); 200 | M3Result CompileFunction (IM3Function io_function); 201 | 202 | M3Result CompileRawFunction (IM3Module io_module, IM3Function io_function, const void * i_function, const void * i_userdata); 203 | 204 | d_m3EndExternC 205 | 206 | #endif // m3_compile_h 207 | -------------------------------------------------------------------------------- /external/m3_config.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config.h 3 | // 4 | // Created by Steven Massey on 5/4/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_h 9 | #define m3_config_h 10 | 11 | #include "m3_config_platforms.h" 12 | 13 | #pragma warning(disable : 4002) 14 | #pragma warning(disable : 4003) 15 | #pragma warning(disable : 4146) 16 | #pragma warning(disable : 4244) 17 | #pragma warning(disable : 4267) 18 | #pragma warning(disable : 4616) 19 | 20 | 21 | // general -------------------------------------------------------------------- 22 | 23 | # ifndef d_m3CodePageAlignSize 24 | # define d_m3CodePageAlignSize 32*1024 25 | # endif 26 | 27 | # ifndef d_m3MaxFunctionStackHeight 28 | # define d_m3MaxFunctionStackHeight 2000 // max: 32768 29 | # endif 30 | 31 | # ifndef d_m3MaxLinearMemoryPages 32 | # define d_m3MaxLinearMemoryPages 65536 33 | # endif 34 | 35 | # ifndef d_m3MaxFunctionSlots 36 | # define d_m3MaxFunctionSlots ((d_m3MaxFunctionStackHeight)*2) 37 | # endif 38 | 39 | # ifndef d_m3MaxConstantTableSize 40 | # define d_m3MaxConstantTableSize 120 41 | # endif 42 | 43 | # ifndef d_m3MaxDuplicateFunctionImpl 44 | # define d_m3MaxDuplicateFunctionImpl 3 45 | # endif 46 | 47 | # ifndef d_m3CascadedOpcodes // Cascaded opcodes are slightly faster at the expense of some memory 48 | # define d_m3CascadedOpcodes 1 // Adds ~3Kb to operations table in m3_compile.c 49 | # endif 50 | 51 | # ifndef d_m3VerboseErrorMessages 52 | # define d_m3VerboseErrorMessages 1 53 | # endif 54 | 55 | # ifndef d_m3FixedHeap 56 | # define d_m3FixedHeap false 57 | //# define d_m3FixedHeap (32*1024) 58 | # endif 59 | 60 | # ifndef d_m3FixedHeapAlign 61 | # define d_m3FixedHeapAlign 16 62 | # endif 63 | 64 | # ifndef d_m3Use32BitSlots 65 | # define d_m3Use32BitSlots 1 66 | # endif 67 | 68 | # ifndef d_m3ProfilerSlotMask 69 | # define d_m3ProfilerSlotMask 0xFFFF 70 | # endif 71 | 72 | # ifndef d_m3RecordBacktraces 73 | # define d_m3RecordBacktraces 0 74 | # endif 75 | 76 | # ifndef d_m3EnableExceptionBreakpoint 77 | # define d_m3EnableExceptionBreakpoint 0 // see m3_exception.h 78 | # endif 79 | 80 | 81 | // profiling and tracing ------------------------------------------------------ 82 | 83 | # ifndef d_m3EnableOpProfiling 84 | # define d_m3EnableOpProfiling 0 // opcode usage counters 85 | # endif 86 | 87 | # ifndef d_m3EnableOpTracing 88 | # define d_m3EnableOpTracing 0 // only works with DEBUG 89 | # endif 90 | 91 | # ifndef d_m3EnableWasiTracing 92 | # define d_m3EnableWasiTracing 0 93 | # endif 94 | 95 | # ifndef d_m3EnableStrace 96 | # define d_m3EnableStrace 0 // 1 - trace exported function calls 97 | // 2 - trace all calls (structured) 98 | // 3 - all calls + loops + memory operations 99 | # endif 100 | 101 | 102 | // logging -------------------------------------------------------------------- 103 | 104 | # ifndef d_m3LogParse 105 | # define d_m3LogParse 0 // .wasm binary decoding info 106 | # endif 107 | 108 | # ifndef d_m3LogModule 109 | # define d_m3LogModule 0 // wasm module info 110 | # endif 111 | 112 | # ifndef d_m3LogCompile 113 | # define d_m3LogCompile 0 // wasm -> metacode generation phase 114 | # endif 115 | 116 | # ifndef d_m3LogWasmStack 117 | # define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped 118 | # endif 119 | 120 | # ifndef d_m3LogEmit 121 | # define d_m3LogEmit 0 // metacode generation info 122 | # endif 123 | 124 | # ifndef d_m3LogCodePages 125 | # define d_m3LogCodePages 0 // dump metacode pages when released 126 | # endif 127 | 128 | # ifndef d_m3LogRuntime 129 | # define d_m3LogRuntime 0 // higher-level runtime information 130 | # endif 131 | 132 | # ifndef d_m3LogNativeStack 133 | # define d_m3LogNativeStack 0 // track the memory usage of the C-stack 134 | # endif 135 | 136 | # ifndef d_m3LogHeapOps 137 | # define d_m3LogHeapOps 0 // track heap usage 138 | # endif 139 | 140 | # ifndef d_m3LogTimestamps 141 | # define d_m3LogTimestamps 0 // track timestamps on heap logs 142 | # endif 143 | 144 | // other ---------------------------------------------------------------------- 145 | 146 | # ifndef d_m3HasFloat 147 | # define d_m3HasFloat 1 // implement floating point ops 148 | # endif 149 | 150 | #if !d_m3HasFloat && !defined(d_m3NoFloatDynamic) 151 | # define d_m3NoFloatDynamic 1 // if no floats, do not fail until flops are actually executed 152 | #endif 153 | 154 | # ifndef d_m3SkipStackCheck 155 | # define d_m3SkipStackCheck 0 // skip stack overrun checks 156 | # endif 157 | 158 | # ifndef d_m3SkipMemoryBoundsCheck 159 | # define d_m3SkipMemoryBoundsCheck 0 // skip memory bounds checks 160 | # endif 161 | 162 | #define d_m3EnableCodePageRefCounting 0 // not supported currently 163 | 164 | #endif // m3_config_h 165 | -------------------------------------------------------------------------------- /external/m3_config_platforms.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config_platforms.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_platforms_h 9 | #define m3_config_platforms_h 10 | 11 | #include "wasm3_defs.h" 12 | 13 | /* 14 | * Internal helpers 15 | */ 16 | 17 | # if !defined(__cplusplus) || defined(_MSC_VER) 18 | # define not ! 19 | # define and && 20 | # define or || 21 | # endif 22 | 23 | /* 24 | * Detect/define features 25 | */ 26 | 27 | # if defined(M3_COMPILER_MSVC) 28 | # include 29 | # if UINTPTR_MAX == 0xFFFFFFFF 30 | # define M3_SIZEOF_PTR 4 31 | # elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu 32 | # define M3_SIZEOF_PTR 8 33 | # else 34 | # error "Pointer size not supported" 35 | # endif 36 | # elif defined(__SIZEOF_POINTER__) 37 | # define M3_SIZEOF_PTR __SIZEOF_POINTER__ 38 | #else 39 | # error "Pointer size not detected" 40 | # endif 41 | 42 | # if defined(M3_BIG_ENDIAN) 43 | # define M3_BSWAP_u8(X) {} 44 | # define M3_BSWAP_u16(X) { (X)=m3_bswap16((X)); } 45 | # define M3_BSWAP_u32(X) { (X)=m3_bswap32((X)); } 46 | # define M3_BSWAP_u64(X) { (X)=m3_bswap64((X)); } 47 | # define M3_BSWAP_i8(X) {} 48 | # define M3_BSWAP_i16(X) M3_BSWAP_u16(X) 49 | # define M3_BSWAP_i32(X) M3_BSWAP_u32(X) 50 | # define M3_BSWAP_i64(X) M3_BSWAP_u64(X) 51 | # define M3_BSWAP_f32(X) { union { f32 f; u32 i; } u; u.f = (X); M3_BSWAP_u32(u.i); (X) = u.f; } 52 | # define M3_BSWAP_f64(X) { union { f64 f; u64 i; } u; u.f = (X); M3_BSWAP_u64(u.i); (X) = u.f; } 53 | # else 54 | # define M3_BSWAP_u8(X) {} 55 | # define M3_BSWAP_u16(x) {} 56 | # define M3_BSWAP_u32(x) {} 57 | # define M3_BSWAP_u64(x) {} 58 | # define M3_BSWAP_i8(X) {} 59 | # define M3_BSWAP_i16(X) {} 60 | # define M3_BSWAP_i32(X) {} 61 | # define M3_BSWAP_i64(X) {} 62 | # define M3_BSWAP_f32(X) {} 63 | # define M3_BSWAP_f64(X) {} 64 | # endif 65 | 66 | # if defined(M3_COMPILER_MSVC) 67 | # define M3_WEAK //__declspec(selectany) 68 | # define M3_NO_UBSAN 69 | # define M3_NOINLINE 70 | # elif defined(__MINGW32__) || defined(__CYGWIN__) 71 | # define M3_WEAK //__attribute__((selectany)) 72 | # define M3_NO_UBSAN 73 | # define M3_NOINLINE __attribute__((noinline)) 74 | # else 75 | # define M3_WEAK __attribute__((weak)) 76 | # define M3_NO_UBSAN //__attribute__((no_sanitize("undefined"))) 77 | // Workaround for Cosmopolitan noinline conflict: https://github.com/jart/cosmopolitan/issues/310 78 | # if defined(noinline) 79 | # define M3_NOINLINE noinline 80 | # else 81 | # define M3_NOINLINE __attribute__((noinline)) 82 | # endif 83 | # endif 84 | 85 | # if M3_COMPILER_HAS_ATTRIBUTE(musttail) 86 | # define M3_MUSTTAIL __attribute__((musttail)) 87 | # else 88 | # define M3_MUSTTAIL 89 | # endif 90 | 91 | # ifndef M3_MIN 92 | # define M3_MIN(A,B) (((A) < (B)) ? (A) : (B)) 93 | # endif 94 | # ifndef M3_MAX 95 | # define M3_MAX(A,B) (((A) > (B)) ? (A) : (B)) 96 | # endif 97 | 98 | #define M3_INIT(field) memset(&field, 0, sizeof(field)) 99 | 100 | #define M3_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 101 | 102 | #if defined(__AVR__) 103 | 104 | #include 105 | 106 | # define PRIu64 "llu" 107 | # define PRIi64 "lli" 108 | 109 | # define d_m3ShortTypesDefined 110 | typedef double f64; 111 | typedef float f32; 112 | typedef uint64_t u64; 113 | typedef int64_t i64; 114 | typedef uint32_t u32; 115 | typedef int32_t i32; 116 | typedef short unsigned u16; 117 | typedef short i16; 118 | typedef uint8_t u8; 119 | typedef int8_t i8; 120 | 121 | #endif 122 | 123 | /* 124 | * Apply settings 125 | */ 126 | 127 | # if defined (M3_COMPILER_MSVC) 128 | # define vectorcall // For MSVC, better not to specify any call convention 129 | # elif defined(__x86_64__) 130 | # define vectorcall 131 | //# elif defined(__riscv) && (__riscv_xlen == 64) 132 | //# define vectorcall 133 | # elif defined(__MINGW32__) 134 | # define vectorcall 135 | # elif defined(WIN32) 136 | # define vectorcall __vectorcall 137 | # elif defined (ESP8266) 138 | # include 139 | # define vectorcall //ICACHE_FLASH_ATTR 140 | # elif defined (ESP32) 141 | # if defined(M3_IN_IRAM) // the interpreter is in IRAM, attribute not needed 142 | # define vectorcall 143 | # else 144 | # include "esp_system.h" 145 | # define vectorcall IRAM_ATTR 146 | # endif 147 | # elif defined (FOMU) 148 | # define vectorcall __attribute__((section(".ramtext"))) 149 | # endif 150 | 151 | #ifndef vectorcall 152 | #define vectorcall 153 | #endif 154 | 155 | 156 | /* 157 | * Device-specific defaults 158 | */ 159 | 160 | # ifndef d_m3MaxFunctionStackHeight 161 | # if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_AMEBA) || defined(TEENSYDUINO) 162 | # define d_m3MaxFunctionStackHeight 256 163 | # endif 164 | # endif 165 | 166 | # ifndef d_m3FixedHeap 167 | # if defined(ARDUINO_AMEBA) 168 | # define d_m3FixedHeap (128*1024) 169 | # elif defined(BLUE_PILL) || defined(FOMU) 170 | # define d_m3FixedHeap (12*1024) 171 | # elif defined(ARDUINO_ARCH_ARC32) // Arduino 101 172 | # define d_m3FixedHeap (10*1024) 173 | # endif 174 | # endif 175 | 176 | /* 177 | * Platform-specific defaults 178 | */ 179 | 180 | # if defined(ARDUINO) || defined(PARTICLE) || defined(PLATFORMIO) || defined(__MBED__) || \ 181 | defined(ESP8266) || defined(ESP32) || defined(BLUE_PILL) || defined(WM_W600) || defined(FOMU) 182 | # ifndef d_m3CascadedOpcodes 183 | # define d_m3CascadedOpcodes 0 184 | # endif 185 | # ifndef d_m3VerboseErrorMessages 186 | # define d_m3VerboseErrorMessages 0 187 | # endif 188 | # ifndef d_m3MaxConstantTableSize 189 | # define d_m3MaxConstantTableSize 64 190 | # endif 191 | # ifndef d_m3MaxFunctionStackHeight 192 | # define d_m3MaxFunctionStackHeight 128 193 | # endif 194 | # ifndef d_m3CodePageAlignSize 195 | # define d_m3CodePageAlignSize 1024 196 | # endif 197 | # endif 198 | 199 | /* 200 | * Arch-specific defaults 201 | */ 202 | #if defined(__riscv) && (__riscv_xlen == 64) 203 | # ifndef d_m3Use32BitSlots 204 | # define d_m3Use32BitSlots 0 205 | # endif 206 | #endif 207 | 208 | #endif // m3_config_platforms_h 209 | -------------------------------------------------------------------------------- /external/m3_core.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_core.c 3 | // 4 | // Created by Steven Massey on 4/15/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #define M3_IMPLEMENT_ERROR_STRINGS 9 | #include "m3_config.h" 10 | #include "wasm3.h" 11 | 12 | #include "m3_core.h" 13 | #include "m3_env.h" 14 | 15 | void m3_Abort(const char* message) { 16 | #ifdef DEBUG 17 | fprintf(stderr, "Error: %s\n", message); 18 | #endif 19 | abort(); 20 | } 21 | 22 | M3_WEAK 23 | M3Result m3_Yield () 24 | { 25 | return m3Err_none; 26 | } 27 | 28 | #if d_m3LogTimestamps 29 | 30 | #include 31 | 32 | #define SEC_TO_US(sec) ((sec)*1000000) 33 | #define NS_TO_US(ns) ((ns)/1000) 34 | 35 | static uint64_t initial_ts = -1; 36 | 37 | uint64_t m3_GetTimestamp() 38 | { 39 | if (initial_ts == -1) { 40 | initial_ts = 0; 41 | initial_ts = m3_GetTimestamp(); 42 | } 43 | struct timespec ts; 44 | timespec_get(&ts, TIME_UTC); 45 | uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); 46 | return us - initial_ts; 47 | } 48 | 49 | #endif 50 | 51 | #if d_m3FixedHeap 52 | 53 | static u8 fixedHeap[d_m3FixedHeap]; 54 | static u8* fixedHeapPtr = fixedHeap; 55 | static u8* const fixedHeapEnd = fixedHeap + d_m3FixedHeap; 56 | static u8* fixedHeapLast = NULL; 57 | 58 | #if d_m3FixedHeapAlign > 1 59 | # define HEAP_ALIGN_PTR(P) P = (u8*)(((size_t)(P)+(d_m3FixedHeapAlign-1)) & ~ (d_m3FixedHeapAlign-1)); 60 | #else 61 | # define HEAP_ALIGN_PTR(P) 62 | #endif 63 | 64 | void * m3_Malloc_Impl (size_t i_size) 65 | { 66 | u8 * ptr = fixedHeapPtr; 67 | 68 | fixedHeapPtr += i_size; 69 | HEAP_ALIGN_PTR(fixedHeapPtr); 70 | 71 | if (fixedHeapPtr >= fixedHeapEnd) 72 | { 73 | return NULL; 74 | } 75 | 76 | memset (ptr, 0x0, i_size); 77 | fixedHeapLast = ptr; 78 | 79 | return ptr; 80 | } 81 | 82 | void m3_Free_Impl (void * i_ptr) 83 | { 84 | // Handle the last chunk 85 | if (i_ptr && i_ptr == fixedHeapLast) { 86 | fixedHeapPtr = fixedHeapLast; 87 | fixedHeapLast = NULL; 88 | } else { 89 | //printf("== free %p [failed]\n", io_ptr); 90 | } 91 | } 92 | 93 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) 94 | { 95 | if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; 96 | 97 | void * newPtr; 98 | 99 | // Handle the last chunk 100 | if (i_ptr && i_ptr == fixedHeapLast) { 101 | fixedHeapPtr = fixedHeapLast + i_newSize; 102 | HEAP_ALIGN_PTR(fixedHeapPtr); 103 | if (fixedHeapPtr >= fixedHeapEnd) 104 | { 105 | return NULL; 106 | } 107 | newPtr = i_ptr; 108 | } else { 109 | newPtr = m3_Malloc_Impl(i_newSize); 110 | if (!newPtr) { 111 | return NULL; 112 | } 113 | if (i_ptr) { 114 | memcpy(newPtr, i_ptr, i_oldSize); 115 | } 116 | } 117 | 118 | if (i_newSize > i_oldSize) { 119 | memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); 120 | } 121 | 122 | return newPtr; 123 | } 124 | 125 | #else 126 | 127 | void * m3_Malloc_Impl (size_t i_size) 128 | { 129 | return calloc (i_size, 1); 130 | } 131 | 132 | void m3_Free_Impl (void * io_ptr) 133 | { 134 | free (io_ptr); 135 | } 136 | 137 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) 138 | { 139 | if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; 140 | 141 | void * newPtr = realloc (i_ptr, i_newSize); 142 | 143 | if (M3_LIKELY(newPtr)) 144 | { 145 | if (i_newSize > i_oldSize) { 146 | memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); 147 | } 148 | return newPtr; 149 | } 150 | return NULL; 151 | } 152 | 153 | #endif 154 | 155 | void * m3_CopyMem (const void * i_from, size_t i_size) 156 | { 157 | void * ptr = m3_Malloc("CopyMem", i_size); 158 | if (ptr) { 159 | memcpy (ptr, i_from, i_size); 160 | } 161 | return ptr; 162 | } 163 | 164 | //-------------------------------------------------------------------------------------------- 165 | 166 | #if d_m3LogNativeStack 167 | 168 | static size_t stack_start; 169 | static size_t stack_end; 170 | 171 | void m3StackCheckInit () 172 | { 173 | char stack; 174 | stack_end = stack_start = (size_t)&stack; 175 | } 176 | 177 | void m3StackCheck () 178 | { 179 | char stack; 180 | size_t addr = (size_t)&stack; 181 | 182 | size_t stackEnd = stack_end; 183 | stack_end = M3_MIN (stack_end, addr); 184 | 185 | // if (stackEnd != stack_end) 186 | // printf ("maxStack: %ld\n", m3StackGetMax ()); 187 | } 188 | 189 | int m3StackGetMax () 190 | { 191 | return stack_start - stack_end; 192 | } 193 | 194 | #endif 195 | 196 | //-------------------------------------------------------------------------------------------- 197 | 198 | M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType) 199 | { 200 | M3Result result = m3Err_none; 201 | 202 | u8 type = -i_convolutedWasmType; 203 | 204 | if (type == 0x40) 205 | type = c_m3Type_none; 206 | else if (type < c_m3Type_i32 or type > c_m3Type_f64) 207 | result = m3Err_invalidTypeId; 208 | 209 | * o_type = type; 210 | 211 | return result; 212 | } 213 | 214 | 215 | bool IsFpType (u8 i_m3Type) 216 | { 217 | return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64); 218 | } 219 | 220 | 221 | bool IsIntType (u8 i_m3Type) 222 | { 223 | return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64); 224 | } 225 | 226 | 227 | bool Is64BitType (u8 i_m3Type) 228 | { 229 | if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64) 230 | return true; 231 | else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none) 232 | return false; 233 | else 234 | return (sizeof (voidptr_t) == 8); // all other cases are pointers 235 | } 236 | 237 | u32 SizeOfType (u8 i_m3Type) 238 | { 239 | if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32) 240 | return sizeof (i32); 241 | 242 | return sizeof (i64); 243 | } 244 | 245 | 246 | //-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------ 247 | 248 | 249 | M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 250 | { 251 | const u8 * ptr = * io_bytes; 252 | ptr += sizeof (u64); 253 | 254 | if (ptr <= i_end) 255 | { 256 | memcpy(o_value, * io_bytes, sizeof(u64)); 257 | M3_BSWAP_u64(*o_value); 258 | * io_bytes = ptr; 259 | return m3Err_none; 260 | } 261 | else return m3Err_wasmUnderrun; 262 | } 263 | 264 | 265 | M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 266 | { 267 | const u8 * ptr = * io_bytes; 268 | ptr += sizeof (u32); 269 | 270 | if (ptr <= i_end) 271 | { 272 | memcpy(o_value, * io_bytes, sizeof(u32)); 273 | M3_BSWAP_u32(*o_value); 274 | * io_bytes = ptr; 275 | return m3Err_none; 276 | } 277 | else return m3Err_wasmUnderrun; 278 | } 279 | 280 | #if d_m3ImplementFloat 281 | 282 | M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 283 | { 284 | const u8 * ptr = * io_bytes; 285 | ptr += sizeof (f64); 286 | 287 | if (ptr <= i_end) 288 | { 289 | memcpy(o_value, * io_bytes, sizeof(f64)); 290 | M3_BSWAP_f64(*o_value); 291 | * io_bytes = ptr; 292 | return m3Err_none; 293 | } 294 | else return m3Err_wasmUnderrun; 295 | } 296 | 297 | 298 | M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 299 | { 300 | const u8 * ptr = * io_bytes; 301 | ptr += sizeof (f32); 302 | 303 | if (ptr <= i_end) 304 | { 305 | memcpy(o_value, * io_bytes, sizeof(f32)); 306 | M3_BSWAP_f32(*o_value); 307 | * io_bytes = ptr; 308 | return m3Err_none; 309 | } 310 | else return m3Err_wasmUnderrun; 311 | } 312 | 313 | #endif 314 | 315 | M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 316 | { 317 | const u8 * ptr = * io_bytes; 318 | 319 | if (ptr < i_end) 320 | { 321 | * o_value = * ptr; 322 | * io_bytes = ptr + 1; 323 | 324 | return m3Err_none; 325 | } 326 | else return m3Err_wasmUnderrun; 327 | } 328 | 329 | M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end) 330 | { 331 | const u8 * ptr = * io_bytes; 332 | 333 | if (ptr < i_end) 334 | { 335 | m3opcode_t opcode = * ptr++; 336 | 337 | #if d_m3CascadedOpcodes == 0 338 | if (M3_UNLIKELY(opcode == c_waOp_extended)) 339 | { 340 | if (ptr < i_end) 341 | { 342 | opcode = (opcode << 8) | (* ptr++); 343 | } 344 | else return m3Err_wasmUnderrun; 345 | } 346 | #endif 347 | * o_value = opcode; 348 | * io_bytes = ptr; 349 | 350 | return m3Err_none; 351 | } 352 | else return m3Err_wasmUnderrun; 353 | } 354 | 355 | 356 | M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) 357 | { 358 | M3Result result = m3Err_wasmUnderrun; 359 | 360 | u64 value = 0; 361 | 362 | u32 shift = 0; 363 | const u8 * ptr = * io_bytes; 364 | 365 | while (ptr < i_end) 366 | { 367 | u64 byte = * (ptr++); 368 | 369 | value |= ((byte & 0x7f) << shift); 370 | shift += 7; 371 | 372 | if ((byte & 0x80) == 0) 373 | { 374 | result = m3Err_none; 375 | break; 376 | } 377 | 378 | if (shift >= i_maxNumBits) 379 | { 380 | result = m3Err_lebOverflow; 381 | break; 382 | } 383 | } 384 | 385 | * o_value = value; 386 | * io_bytes = ptr; 387 | 388 | return result; 389 | } 390 | 391 | 392 | M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) 393 | { 394 | M3Result result = m3Err_wasmUnderrun; 395 | 396 | i64 value = 0; 397 | 398 | u32 shift = 0; 399 | const u8 * ptr = * io_bytes; 400 | 401 | while (ptr < i_end) 402 | { 403 | u64 byte = * (ptr++); 404 | 405 | value |= ((byte & 0x7f) << shift); 406 | shift += 7; 407 | 408 | if ((byte & 0x80) == 0) 409 | { 410 | result = m3Err_none; 411 | 412 | if ((byte & 0x40) and (shift < 64)) // do sign extension 413 | { 414 | u64 extend = 0; 415 | value |= (~extend << shift); 416 | } 417 | 418 | break; 419 | } 420 | 421 | if (shift >= i_maxNumBits) 422 | { 423 | result = m3Err_lebOverflow; 424 | break; 425 | } 426 | } 427 | 428 | * o_value = value; 429 | * io_bytes = ptr; 430 | 431 | return result; 432 | } 433 | 434 | 435 | M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 436 | { 437 | u64 value; 438 | M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end); 439 | * o_value = (u32) value; 440 | 441 | return result; 442 | } 443 | 444 | 445 | M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 446 | { 447 | u64 value; 448 | M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end); 449 | * o_value = (u8) value; 450 | 451 | return result; 452 | } 453 | 454 | 455 | M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 456 | { 457 | i64 value; 458 | M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end); 459 | * o_value = (i8) value; 460 | 461 | return result; 462 | } 463 | 464 | 465 | M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 466 | { 467 | i64 value; 468 | M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end); 469 | * o_value = (i32) value; 470 | 471 | return result; 472 | } 473 | 474 | 475 | M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 476 | { 477 | i64 value; 478 | M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end); 479 | * o_value = value; 480 | 481 | return result; 482 | } 483 | 484 | 485 | M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) 486 | { 487 | *o_utf8 = NULL; 488 | 489 | u32 utf8Length; 490 | M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end); 491 | 492 | if (not result) 493 | { 494 | if (utf8Length <= d_m3MaxSaneUtf8Length) 495 | { 496 | const u8 * ptr = * io_bytes; 497 | const u8 * end = ptr + utf8Length; 498 | 499 | if (end <= i_end) 500 | { 501 | char * utf8 = (char *)m3_Malloc ("UTF8", utf8Length + 1); 502 | 503 | if (utf8) 504 | { 505 | memcpy (utf8, ptr, utf8Length); 506 | utf8 [utf8Length] = 0; 507 | * o_utf8 = utf8; 508 | } 509 | 510 | * io_bytes = end; 511 | } 512 | else result = m3Err_wasmUnderrun; 513 | } 514 | else result = m3Err_missingUTF8; 515 | } 516 | 517 | return result; 518 | } 519 | 520 | #if d_m3RecordBacktraces 521 | u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) 522 | { 523 | // walk the code pages 524 | IM3CodePage curr = i_runtime->pagesOpen; 525 | bool pageFound = false; 526 | 527 | while (curr) 528 | { 529 | if (ContainsPC (curr, i_pc)) 530 | { 531 | pageFound = true; 532 | break; 533 | } 534 | curr = curr->info.next; 535 | } 536 | 537 | if (!pageFound) 538 | { 539 | curr = i_runtime->pagesFull; 540 | while (curr) 541 | { 542 | if (ContainsPC (curr, i_pc)) 543 | { 544 | pageFound = true; 545 | break; 546 | } 547 | curr = curr->info.next; 548 | } 549 | } 550 | 551 | if (pageFound) 552 | { 553 | u32 result = 0; 554 | 555 | bool pcFound = MapPCToOffset (curr, i_pc, & result); 556 | d_m3Assert (pcFound); 557 | 558 | return result; 559 | } 560 | else return 0; 561 | } 562 | 563 | 564 | void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) 565 | { 566 | // don't try to push any more frames if we've already had an alloc failure 567 | if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) 568 | return; 569 | 570 | M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame); 571 | 572 | if (!newFrame) 573 | { 574 | io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED; 575 | return; 576 | } 577 | 578 | newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); 579 | 580 | if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) 581 | io_runtime->backtrace.frames = newFrame; 582 | else 583 | io_runtime->backtrace.lastFrame->next = newFrame; 584 | io_runtime->backtrace.lastFrame = newFrame; 585 | } 586 | 587 | 588 | void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) 589 | { 590 | // If we've had an alloc failure then the last frame doesn't refer to the 591 | // frame we want to fill in the function info for. 592 | if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) 593 | return; 594 | 595 | if (!io_runtime->backtrace.lastFrame) 596 | return; 597 | 598 | io_runtime->backtrace.lastFrame->function = i_function; 599 | } 600 | 601 | 602 | void ClearBacktrace (IM3Runtime io_runtime) 603 | { 604 | M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; 605 | while (currentFrame) 606 | { 607 | M3BacktraceFrame * nextFrame = currentFrame->next; 608 | m3_Free (currentFrame); 609 | currentFrame = nextFrame; 610 | } 611 | 612 | io_runtime->backtrace.frames = NULL; 613 | io_runtime->backtrace.lastFrame = NULL; 614 | } 615 | #endif // d_m3RecordBacktraces 616 | -------------------------------------------------------------------------------- /external/m3_core.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_core.h 3 | // 4 | // Created by Steven Massey on 4/15/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_core_h 9 | #define m3_core_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "wasm3.h" 18 | #include "m3_config.h" 19 | 20 | # if defined(__cplusplus) 21 | # define d_m3BeginExternC extern "C" { 22 | # define d_m3EndExternC } 23 | # else 24 | # define d_m3BeginExternC 25 | # define d_m3EndExternC 26 | # endif 27 | 28 | d_m3BeginExternC 29 | 30 | #define d_m3ImplementFloat (d_m3HasFloat || d_m3NoFloatDynamic) 31 | 32 | #if !defined(d_m3ShortTypesDefined) 33 | 34 | typedef uint64_t u64; 35 | typedef int64_t i64; 36 | typedef uint32_t u32; 37 | typedef int32_t i32; 38 | typedef uint16_t u16; 39 | typedef int16_t i16; 40 | typedef uint8_t u8; 41 | typedef int8_t i8; 42 | 43 | #if d_m3ImplementFloat 44 | typedef double f64; 45 | typedef float f32; 46 | #endif 47 | 48 | #endif // d_m3ShortTypesDefined 49 | 50 | #define PRIf32 "f" 51 | #define PRIf64 "lf" 52 | 53 | typedef const void * m3ret_t; 54 | typedef const void * voidptr_t; 55 | typedef const char * cstr_t; 56 | typedef const char * const ccstr_t; 57 | typedef const u8 * bytes_t; 58 | typedef const u8 * const cbytes_t; 59 | 60 | typedef u16 m3opcode_t; 61 | 62 | typedef i64 m3reg_t; 63 | 64 | # if d_m3Use32BitSlots 65 | typedef u32 m3slot_t; 66 | # else 67 | typedef u64 m3slot_t; 68 | # endif 69 | 70 | typedef m3slot_t * m3stack_t; 71 | 72 | typedef 73 | const void * const cvptr_t; 74 | 75 | # if defined (DEBUG) 76 | 77 | # define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__); 78 | 79 | # if d_m3LogParse 80 | # define m3log_parse(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 81 | # else 82 | # define m3log_parse(...) {} 83 | # endif 84 | 85 | # if d_m3LogCompile 86 | # define m3log_compile(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 87 | # else 88 | # define m3log_compile(...) {} 89 | # endif 90 | 91 | # if d_m3LogEmit 92 | # define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 93 | # else 94 | # define m3log_emit(...) {} 95 | # endif 96 | 97 | # if d_m3LogCodePages 98 | # define m3log_code(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 99 | # else 100 | # define m3log_code(...) {} 101 | # endif 102 | 103 | # if d_m3LogModule 104 | # define m3log_module(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 105 | # else 106 | # define m3log_module(...) {} 107 | # endif 108 | 109 | # if d_m3LogRuntime 110 | # define m3log_runtime(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 111 | # else 112 | # define m3log_runtime(...) {} 113 | # endif 114 | 115 | # define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__) 116 | # else 117 | # define d_m3Log(CATEGORY, FMT, ...) {} 118 | # define m3log(CATEGORY, FMT, ...) {} 119 | # endif 120 | 121 | 122 | # if defined(ASSERTS) || (defined(DEBUG) && !defined(NASSERTS)) 123 | # define d_m3Assert(ASS) if (!(ASS)) { printf("Assertion failed at %s:%d : %s\n", __FILE__, __LINE__, #ASS); abort(); } 124 | # else 125 | # define d_m3Assert(ASS) 126 | # endif 127 | 128 | typedef void /*const*/ * code_t; 129 | typedef code_t const * /*__restrict__*/ pc_t; 130 | 131 | 132 | typedef struct M3MemoryHeader 133 | { 134 | IM3Runtime runtime; 135 | void * maxStack; 136 | size_t length; 137 | } 138 | M3MemoryHeader; 139 | 140 | struct M3CodeMappingPage; 141 | 142 | typedef struct M3CodePageHeader 143 | { 144 | struct M3CodePage * next; 145 | 146 | u32 lineIndex; 147 | u32 numLines; 148 | u32 sequence; // this is just used for debugging; could be removed 149 | u32 usageCount; 150 | 151 | # if d_m3RecordBacktraces 152 | struct M3CodeMappingPage * mapping; 153 | # endif // d_m3RecordBacktraces 154 | } 155 | M3CodePageHeader; 156 | 157 | 158 | #define d_m3CodePageFreeLinesThreshold 4+2 // max is: select _sss & CallIndirect + 2 for bridge 159 | 160 | #define d_m3MemPageSize 65536 161 | 162 | #define d_m3Reg0SlotAlias 60000 163 | #define d_m3Fp0SlotAlias (d_m3Reg0SlotAlias + 2) 164 | 165 | #define d_m3MaxSaneTypesCount 1000000 166 | #define d_m3MaxSaneFunctionsCount 1000000 167 | #define d_m3MaxSaneImportsCount 100000 168 | #define d_m3MaxSaneExportsCount 100000 169 | #define d_m3MaxSaneGlobalsCount 1000000 170 | #define d_m3MaxSaneElementSegments 10000000 171 | #define d_m3MaxSaneDataSegments 100000 172 | #define d_m3MaxSaneTableSize 10000000 173 | #define d_m3MaxSaneUtf8Length 10000 174 | #define d_m3MaxSaneFunctionArgRetCount 1000 // still insane, but whatever 175 | 176 | #define d_externalKind_function 0 177 | #define d_externalKind_table 1 178 | #define d_externalKind_memory 2 179 | #define d_externalKind_global 3 180 | 181 | static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", "f64", "unknown" }; 182 | static const char * const c_waCompactTypes [] = { "_", "i", "I", "f", "F", "?" }; 183 | 184 | 185 | # if d_m3VerboseErrorMessages 186 | 187 | M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, 188 | const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); 189 | 190 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) \ 191 | m3Error (RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ##__VA_ARGS__) 192 | 193 | # else 194 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) (RESULT) 195 | # endif 196 | 197 | #define ErrorRuntime(RESULT, RUNTIME, FORMAT, ...) _m3Error (RESULT, RUNTIME, NULL, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 198 | #define ErrorModule(RESULT, MOD, FORMAT, ...) _m3Error (RESULT, MOD->runtime, MOD, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 199 | #define ErrorCompile(RESULT, COMP, FORMAT, ...) _m3Error (RESULT, COMP->runtime, COMP->module, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 200 | 201 | #if d_m3LogNativeStack 202 | void m3StackCheckInit (); 203 | void m3StackCheck (); 204 | int m3StackGetMax (); 205 | #else 206 | #define m3StackCheckInit() 207 | #define m3StackCheck() 208 | #define m3StackGetMax() 0 209 | #endif 210 | 211 | #if d_m3LogTimestamps 212 | #define PRIts "%llu" 213 | uint64_t m3_GetTimestamp (); 214 | #else 215 | #define PRIts "%s" 216 | #define m3_GetTimestamp() "" 217 | #endif 218 | 219 | void m3_Abort (const char* message); 220 | void * m3_Malloc_Impl (size_t i_size); 221 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize); 222 | void m3_Free_Impl (void * i_ptr); 223 | void * m3_CopyMem (const void * i_from, size_t i_size); 224 | 225 | #if d_m3LogHeapOps 226 | 227 | // Tracing format: timestamp;heap:OpCode;name;size(bytes);new items;new ptr;old items;old ptr 228 | 229 | static inline void * m3_AllocStruct_Impl(ccstr_t name, size_t i_size) { 230 | void * result = m3_Malloc_Impl(i_size); 231 | fprintf(stderr, PRIts ";heap:AllocStruct;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); 232 | return result; 233 | } 234 | 235 | static inline void * m3_AllocArray_Impl(ccstr_t name, size_t i_num, size_t i_size) { 236 | void * result = m3_Malloc_Impl(i_size * i_num); 237 | fprintf(stderr, PRIts ";heap:AllocArr;%s;%zu;%zu;%p;;\n", m3_GetTimestamp(), name, i_size, i_num, result); 238 | return result; 239 | } 240 | 241 | static inline void * m3_ReallocArray_Impl(ccstr_t name, void * i_ptr_old, size_t i_num_new, size_t i_num_old, size_t i_size) { 242 | void * result = m3_Realloc_Impl (i_ptr_old, i_size * i_num_new, i_size * i_num_old); 243 | fprintf(stderr, PRIts ";heap:ReallocArr;%s;%zu;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_size, i_num_new, result, i_num_old, i_ptr_old); 244 | return result; 245 | } 246 | 247 | static inline void * m3_Malloc (ccstr_t name, size_t i_size) { 248 | void * result = m3_Malloc_Impl (i_size); 249 | fprintf(stderr, PRIts ";heap:AllocMem;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); 250 | return result; 251 | } 252 | static inline void * m3_Realloc (ccstr_t name, void * i_ptr, size_t i_newSize, size_t i_oldSize) { 253 | void * result = m3_Realloc_Impl (i_ptr, i_newSize, i_oldSize); 254 | fprintf(stderr, PRIts ";heap:ReallocMem;%s;;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_newSize, result, i_oldSize, i_ptr); 255 | return result; 256 | } 257 | 258 | #define m3_AllocStruct(STRUCT) (STRUCT *)m3_AllocStruct_Impl (#STRUCT, sizeof (STRUCT)) 259 | #define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_AllocArray_Impl (#STRUCT, NUM, sizeof (STRUCT)) 260 | #define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_ReallocArray_Impl (#STRUCT, (void *)(PTR), (NEW), (OLD), sizeof (STRUCT)) 261 | #define m3_Free(P) do { void* p = (void*)(P); \ 262 | if (p) { fprintf(stderr, PRIts ";heap:FreeMem;;;;%p;\n", m3_GetTimestamp(), p); } \ 263 | m3_Free_Impl (p); (P) = NULL; } while(0) 264 | #else 265 | #define m3_Malloc(NAME, SIZE) m3_Malloc_Impl(SIZE) 266 | #define m3_Realloc(NAME, PTR, NEW, OLD) m3_Realloc_Impl(PTR, NEW, OLD) 267 | #define m3_AllocStruct(STRUCT) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT)) 268 | #define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT) * (NUM)) 269 | #define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_Realloc_Impl ((void *)(PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) 270 | #define m3_Free(P) do { m3_Free_Impl ((void*)(P)); (P) = NULL; } while(0) 271 | #endif 272 | 273 | M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType); 274 | 275 | bool IsIntType (u8 i_wasmType); 276 | bool IsFpType (u8 i_wasmType); 277 | bool Is64BitType (u8 i_m3Type); 278 | u32 SizeOfType (u8 i_m3Type); 279 | 280 | M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 281 | M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 282 | #if d_m3ImplementFloat 283 | M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 284 | M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 285 | #endif 286 | M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 287 | M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end); 288 | 289 | M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 290 | M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 291 | M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 292 | M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 293 | M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 294 | M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 295 | M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 296 | M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end); 297 | 298 | cstr_t SPrintValue (void * i_value, u8 i_type); 299 | size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type); 300 | 301 | void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); 302 | 303 | # if d_m3RecordBacktraces 304 | void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc); 305 | void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function); 306 | void ClearBacktrace (IM3Runtime io_runtime); 307 | # endif 308 | 309 | d_m3EndExternC 310 | 311 | #endif // m3_core_h 312 | -------------------------------------------------------------------------------- /external/m3_env.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_env.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_env_h 9 | #define m3_env_h 10 | 11 | #include "wasm3.h" 12 | #include "m3_code.h" 13 | #include "m3_compile.h" 14 | 15 | d_m3BeginExternC 16 | 17 | 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | typedef struct M3MemoryInfo 21 | { 22 | u32 initPages; 23 | u32 maxPages; 24 | } 25 | M3MemoryInfo; 26 | 27 | 28 | typedef struct M3Memory 29 | { 30 | M3MemoryHeader * mallocated; 31 | 32 | u32 numPages; 33 | u32 maxPages; 34 | } 35 | M3Memory; 36 | 37 | typedef M3Memory * IM3Memory; 38 | 39 | 40 | //--------------------------------------------------------------------------------------------------------------------------------- 41 | 42 | typedef struct M3DataSegment 43 | { 44 | const u8 * initExpr; // wasm code 45 | const u8 * data; 46 | 47 | u32 initExprSize; 48 | u32 memoryRegion; 49 | u32 size; 50 | } 51 | M3DataSegment; 52 | 53 | //--------------------------------------------------------------------------------------------------------------------------------- 54 | 55 | typedef struct M3Global 56 | { 57 | M3ImportInfo import; 58 | 59 | union 60 | { 61 | i32 i32Value; 62 | i64 i64Value; 63 | #if d_m3HasFloat 64 | f64 f64Value; 65 | f32 f32Value; 66 | #endif 67 | }; 68 | 69 | cstr_t name; 70 | bytes_t initExpr; // wasm code 71 | u32 initExprSize; 72 | u8 type; 73 | bool imported; 74 | bool isMutable; 75 | } 76 | M3Global; 77 | 78 | 79 | //--------------------------------------------------------------------------------------------------------------------------------- 80 | typedef struct M3Module 81 | { 82 | struct M3Runtime * runtime; 83 | struct M3Environment * environment; 84 | 85 | bytes_t wasmStart; 86 | bytes_t wasmEnd; 87 | 88 | cstr_t name; 89 | 90 | u32 numFuncTypes; 91 | IM3FuncType * funcTypes; // array of pointers to list of FuncTypes 92 | 93 | u32 numFuncImports; 94 | u32 numFunctions; 95 | u32 allFunctions; // allocated functions count 96 | M3Function * functions; 97 | 98 | i32 startFunction; 99 | 100 | u32 numDataSegments; 101 | M3DataSegment * dataSegments; 102 | 103 | //u32 importedGlobals; 104 | u32 numGlobals; 105 | M3Global * globals; 106 | 107 | u32 numElementSegments; 108 | bytes_t elementSection; 109 | bytes_t elementSectionEnd; 110 | 111 | IM3Function * table0; 112 | u32 table0Size; 113 | 114 | M3MemoryInfo memoryInfo; 115 | bool memoryImported; 116 | 117 | //bool hasWasmCodeCopy; 118 | 119 | struct M3Module * next; 120 | } 121 | M3Module; 122 | 123 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported); 124 | 125 | M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions); 126 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */); 127 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex); 128 | 129 | void Module_GenerateNames (IM3Module i_module); 130 | 131 | void FreeImportInfo (M3ImportInfo * i_info); 132 | 133 | //--------------------------------------------------------------------------------------------------------------------------------- 134 | 135 | typedef struct M3Environment 136 | { 137 | // struct M3Runtime * runtimes; 138 | 139 | IM3FuncType funcTypes; // linked list of unique M3FuncType structs that can be compared using pointer-equivalence 140 | 141 | IM3FuncType retFuncTypes [c_m3Type_unknown]; // these 'point' to elements in the linked list above. 142 | // the number of elements must match the basic types as per M3ValueType 143 | M3CodePage * pagesReleased; 144 | 145 | M3SectionHandler customSectionHandler; 146 | } 147 | M3Environment; 148 | 149 | void Environment_Release (IM3Environment i_environment); 150 | 151 | // takes ownership of io_funcType and returns a pointer to the persistent version (could be same or different) 152 | void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType); 153 | 154 | //--------------------------------------------------------------------------------------------------------------------------------- 155 | 156 | typedef struct M3Runtime 157 | { 158 | M3Compilation compilation; 159 | 160 | IM3Environment environment; 161 | 162 | M3CodePage * pagesOpen; // linked list of code pages with writable space on them 163 | M3CodePage * pagesFull; // linked list of at-capacity pages 164 | 165 | u32 numCodePages; 166 | u32 numActiveCodePages; 167 | 168 | IM3Module modules; // linked list of imported modules 169 | 170 | void * stack; 171 | u32 stackSize; 172 | u32 numStackSlots; 173 | IM3Function lastCalled; // last function that successfully executed 174 | 175 | void * userdata; 176 | 177 | M3Memory memory; 178 | u32 memoryLimit; 179 | 180 | #if d_m3EnableStrace >= 2 181 | u32 callDepth; 182 | #endif 183 | 184 | M3ErrorInfo error; 185 | #if d_m3VerboseErrorMessages 186 | char error_message[256]; // the actual buffer. M3ErrorInfo can point to this 187 | #endif 188 | 189 | #if d_m3RecordBacktraces 190 | M3BacktraceInfo backtrace; 191 | #endif 192 | 193 | u32 newCodePageSequence; 194 | } 195 | M3Runtime; 196 | 197 | void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes); 198 | void Runtime_Release (IM3Runtime io_runtime); 199 | 200 | M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages); 201 | 202 | typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info); 203 | void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info); 204 | 205 | void * v_FindFunction (IM3Module i_module, const char * const i_name); 206 | 207 | IM3CodePage AcquireCodePage (IM3Runtime io_runtime); 208 | IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_lineCount); 209 | void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage); 210 | 211 | d_m3EndExternC 212 | 213 | #endif // m3_env_h 214 | -------------------------------------------------------------------------------- /external/m3_exception.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exception.h 3 | // 4 | // Created by Steven Massey on 7/5/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | // some macros to emulate try/catch 8 | 9 | #ifndef m3_exception_h 10 | #define m3_exception_h 11 | 12 | #include "m3_config.h" 13 | 14 | # if d_m3EnableExceptionBreakpoint 15 | 16 | // declared in m3_info.c 17 | void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message); 18 | 19 | # define EXCEPTION_PRINT(ERROR) ExceptionBreakpoint (ERROR, (__FILE__ ":" M3_STR(__LINE__))) 20 | 21 | # else 22 | # define EXCEPTION_PRINT(...) 23 | # endif 24 | 25 | 26 | #define _try M3Result result = m3Err_none; 27 | #define _(TRY) { result = TRY; if (M3_UNLIKELY(result)) { EXCEPTION_PRINT (result); goto _catch; } } 28 | #define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } 29 | #define _throwif(ERROR, COND) if (M3_UNLIKELY(COND)) { _throw(ERROR); } 30 | 31 | #define _throwifnull(PTR) _throwif (m3Err_mallocFailed, !(PTR)) 32 | 33 | #endif // m3_exception_h 34 | -------------------------------------------------------------------------------- /external/m3_exec.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec.c 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | // EMPTY FOR NOW 9 | -------------------------------------------------------------------------------- /external/m3_exec_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec_defs.h 3 | // 4 | // Created by Steven Massey on 5/1/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_exec_defs_h 9 | #define m3_exec_defs_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | # define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) 16 | # define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) 17 | # define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) 18 | 19 | # define d_m3BaseOpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 20 | # define d_m3BaseOpArgs _sp, _mem, _r0 21 | # define d_m3BaseOpAllArgs _pc, _sp, _mem, _r0 22 | # define d_m3BaseOpDefaultArgs 0 23 | # define d_m3BaseClearRegisters _r0 = 0; 24 | 25 | # define d_m3ExpOpSig(...) d_m3BaseOpSig, __VA_ARGS__ 26 | # define d_m3ExpOpArgs(...) d_m3BaseOpArgs, __VA_ARGS__ 27 | # define d_m3ExpOpAllArgs(...) d_m3BaseOpAllArgs, __VA_ARGS__ 28 | # define d_m3ExpOpDefaultArgs(...) d_m3BaseOpDefaultArgs, __VA_ARGS__ 29 | # define d_m3ExpClearRegisters(...) d_m3BaseClearRegisters; __VA_ARGS__ 30 | 31 | # if d_m3HasFloat 32 | # define d_m3OpSig d_m3ExpOpSig (f64 _fp0) 33 | # define d_m3OpArgs d_m3ExpOpArgs (_fp0) 34 | # define d_m3OpAllArgs d_m3ExpOpAllArgs (_fp0) 35 | # define d_m3OpDefaultArgs d_m3ExpOpDefaultArgs (0.) 36 | # define d_m3ClearRegisters d_m3ExpClearRegisters (_fp0 = 0.;) 37 | # else 38 | # define d_m3OpSig d_m3BaseOpSig 39 | # define d_m3OpArgs d_m3BaseOpArgs 40 | # define d_m3OpAllArgs d_m3BaseOpAllArgs 41 | # define d_m3OpDefaultArgs d_m3BaseOpDefaultArgs 42 | # define d_m3ClearRegisters d_m3BaseClearRegisters 43 | # endif 44 | 45 | typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); 46 | 47 | #define d_m3RetSig static inline m3ret_t vectorcall 48 | #define d_m3Op(NAME) M3_NO_UBSAN d_m3RetSig op_##NAME (d_m3OpSig) 49 | 50 | #define nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) 51 | #define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) 52 | 53 | #define nextOpDirect() M3_MUSTTAIL return nextOpImpl() 54 | #define jumpOpDirect(PC) M3_MUSTTAIL return jumpOpImpl((pc_t)(PC)) 55 | 56 | d_m3RetSig RunCode (d_m3OpSig) 57 | { 58 | nextOpDirect(); 59 | } 60 | 61 | d_m3EndExternC 62 | 63 | #endif // m3_exec_defs_h 64 | -------------------------------------------------------------------------------- /external/m3_function.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_function.c 3 | // 4 | // Created by Steven Massey on 4/7/21. 5 | // Copyright © 2021 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_function.h" 9 | #include "m3_env.h" 10 | 11 | 12 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes) 13 | { 14 | *o_functionType = (IM3FuncType) m3_Malloc ("M3FuncType", sizeof (M3FuncType) + i_numTypes); 15 | return (*o_functionType) ? m3Err_none : m3Err_mallocFailed; 16 | } 17 | 18 | 19 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB) 20 | { 21 | if (i_typeA->numRets == i_typeB->numRets && i_typeA->numArgs == i_typeB->numArgs) 22 | { 23 | return (memcmp (i_typeA->types, i_typeB->types, i_typeA->numRets + i_typeA->numArgs) == 0); 24 | } 25 | 26 | return false; 27 | } 28 | 29 | u16 GetFuncTypeNumParams (const IM3FuncType i_funcType) 30 | { 31 | return i_funcType ? i_funcType->numArgs : 0; 32 | } 33 | 34 | 35 | u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index) 36 | { 37 | u8 type = c_m3Type_unknown; 38 | 39 | if (i_funcType) 40 | { 41 | if (i_index < i_funcType->numArgs) 42 | { 43 | type = i_funcType->types [i_funcType->numRets + i_index]; 44 | } 45 | } 46 | 47 | return type; 48 | } 49 | 50 | 51 | 52 | u16 GetFuncTypeNumResults (const IM3FuncType i_funcType) 53 | { 54 | return i_funcType ? i_funcType->numRets : 0; 55 | } 56 | 57 | 58 | u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index) 59 | { 60 | u8 type = c_m3Type_unknown; 61 | 62 | if (i_funcType) 63 | { 64 | if (i_index < i_funcType->numRets) 65 | { 66 | type = i_funcType->types [i_index]; 67 | } 68 | } 69 | 70 | return type; 71 | } 72 | 73 | 74 | //--------------------------------------------------------------------------------------------------------------- 75 | 76 | 77 | void FreeImportInfo (M3ImportInfo * i_info) 78 | { 79 | m3_Free (i_info->moduleUtf8); 80 | m3_Free (i_info->fieldUtf8); 81 | } 82 | 83 | 84 | void Function_Release (IM3Function i_function) 85 | { 86 | m3_Free (i_function->constants); 87 | 88 | for (int i = 0; i < i_function->numNames; i++) 89 | { 90 | // name can be an alias of fieldUtf8 91 | if (i_function->names[i] != i_function->import.fieldUtf8) 92 | { 93 | m3_Free (i_function->names[i]); 94 | } 95 | } 96 | 97 | FreeImportInfo (& i_function->import); 98 | 99 | if (i_function->ownsWasmCode) 100 | m3_Free (i_function->wasm); 101 | 102 | // Function_FreeCompiledCode (func); 103 | 104 | # if (d_m3EnableCodePageRefCounting) 105 | { 106 | m3_Free (i_function->codePageRefs); 107 | i_function->numCodePageRefs = 0; 108 | } 109 | # endif 110 | } 111 | 112 | 113 | void Function_FreeCompiledCode (IM3Function i_function) 114 | { 115 | # if (d_m3EnableCodePageRefCounting) 116 | { 117 | i_function->compiled = NULL; 118 | 119 | while (i_function->numCodePageRefs--) 120 | { 121 | IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs]; 122 | 123 | if (--(page->info.usageCount) == 0) 124 | { 125 | // printf ("free %p\n", page); 126 | } 127 | } 128 | 129 | m3_Free (i_function->codePageRefs); 130 | 131 | Runtime_ReleaseCodePages (i_function->module->runtime); 132 | } 133 | # endif 134 | } 135 | 136 | 137 | cstr_t m3_GetFunctionName (IM3Function i_function) 138 | { 139 | u16 numNames = 0; 140 | cstr_t *names = GetFunctionNames(i_function, &numNames); 141 | if (numNames > 0) 142 | return names[0]; 143 | else 144 | return ""; 145 | } 146 | 147 | 148 | IM3Module m3_GetFunctionModule (IM3Function i_function) 149 | { 150 | return i_function ? i_function->module : NULL; 151 | } 152 | 153 | 154 | cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames) 155 | { 156 | if (!i_function || !o_numNames) 157 | return NULL; 158 | 159 | if (i_function->import.fieldUtf8) 160 | { 161 | *o_numNames = 1; 162 | return &i_function->import.fieldUtf8; 163 | } 164 | else 165 | { 166 | *o_numNames = i_function->numNames; 167 | return i_function->names; 168 | } 169 | } 170 | 171 | 172 | cstr_t GetFunctionImportModuleName (IM3Function i_function) 173 | { 174 | return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : ""; 175 | } 176 | 177 | 178 | u16 GetFunctionNumArgs (IM3Function i_function) 179 | { 180 | u16 numArgs = 0; 181 | 182 | if (i_function) 183 | { 184 | if (i_function->funcType) 185 | numArgs = i_function->funcType->numArgs; 186 | } 187 | 188 | return numArgs; 189 | } 190 | 191 | u8 GetFunctionArgType (IM3Function i_function, u32 i_index) 192 | { 193 | u8 type = c_m3Type_none; 194 | 195 | if (i_index < GetFunctionNumArgs (i_function)) 196 | { 197 | u32 numReturns = i_function->funcType->numRets; 198 | 199 | type = i_function->funcType->types [numReturns + i_index]; 200 | } 201 | 202 | return type; 203 | } 204 | 205 | 206 | u16 GetFunctionNumReturns (IM3Function i_function) 207 | { 208 | u16 numReturns = 0; 209 | 210 | if (i_function) 211 | { 212 | if (i_function->funcType) 213 | numReturns = i_function->funcType->numRets; 214 | } 215 | 216 | return numReturns; 217 | } 218 | 219 | 220 | u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index) 221 | { 222 | return i_function ? GetFuncTypeResultType (i_function->funcType, i_index) : c_m3Type_unknown; 223 | } 224 | 225 | 226 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function) 227 | { 228 | if (i_function) 229 | return i_function->numLocals + GetFunctionNumArgs (i_function); 230 | else 231 | return 0; 232 | } 233 | 234 | -------------------------------------------------------------------------------- /external/m3_function.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_function.h 3 | // 4 | // Created by Steven Massey on 4/7/21. 5 | // Copyright © 2021 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_function_h 9 | #define m3_function_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | //--------------------------------------------------------------------------------------------------------------------------------- 16 | 17 | typedef struct M3FuncType 18 | { 19 | struct M3FuncType * next; 20 | 21 | u16 numRets; 22 | u16 numArgs; 23 | u8 types []; // returns, then args 24 | } 25 | M3FuncType; 26 | 27 | typedef M3FuncType * IM3FuncType; 28 | 29 | 30 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); 31 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); 32 | 33 | u16 GetFuncTypeNumParams (const IM3FuncType i_funcType); 34 | u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index); 35 | 36 | u16 GetFuncTypeNumResults (const IM3FuncType i_funcType); 37 | u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index); 38 | 39 | //--------------------------------------------------------------------------------------------------------------------------------- 40 | 41 | typedef struct M3Function 42 | { 43 | struct M3Module * module; 44 | 45 | M3ImportInfo import; 46 | 47 | bytes_t wasm; 48 | bytes_t wasmEnd; 49 | 50 | cstr_t names[d_m3MaxDuplicateFunctionImpl]; 51 | cstr_t export_name; // should be a part of "names" 52 | u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl 53 | 54 | IM3FuncType funcType; 55 | 56 | pc_t compiled; 57 | 58 | # if (d_m3EnableCodePageRefCounting) 59 | IM3CodePage * codePageRefs; // array of all pages used 60 | u32 numCodePageRefs; 61 | # endif 62 | 63 | # if defined (DEBUG) 64 | u32 hits; 65 | u32 index; 66 | # endif 67 | 68 | u16 maxStackSlots; 69 | 70 | u16 numRetSlots; 71 | u16 numRetAndArgSlots; 72 | 73 | u16 numLocals; // not including args 74 | u16 numLocalBytes; 75 | 76 | bool ownsWasmCode; 77 | 78 | u16 numConstantBytes; 79 | void * constants; 80 | } 81 | M3Function; 82 | 83 | void Function_Release (IM3Function i_function); 84 | void Function_FreeCompiledCode (IM3Function i_function); 85 | 86 | cstr_t GetFunctionImportModuleName (IM3Function i_function); 87 | cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); 88 | u16 GetFunctionNumArgs (IM3Function i_function); 89 | u8 GetFunctionArgType (IM3Function i_function, u32 i_index); 90 | 91 | u16 GetFunctionNumReturns (IM3Function i_function); 92 | u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index); 93 | 94 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function); 95 | 96 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); 97 | 98 | //--------------------------------------------------------------------------------------------------------------------------------- 99 | 100 | 101 | d_m3EndExternC 102 | 103 | #endif /* m3_function_h */ 104 | -------------------------------------------------------------------------------- /external/m3_info.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.c 3 | // 4 | // Created by Steven Massey on 4/27/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_info.h" 10 | #include "m3_compile.h" 11 | 12 | #if defined(DEBUG) || (d_m3EnableStrace >= 2) 13 | 14 | size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type) 15 | { 16 | int len = 0; 17 | 18 | * o_string = 0; 19 | 20 | if (i_type == c_m3Type_i32) 21 | len = snprintf (o_string, i_stringBufferSize, "%" PRIi32, * (i32 *) i_sp); 22 | else if (i_type == c_m3Type_i64) 23 | len = snprintf (o_string, i_stringBufferSize, "%" PRIi64, * (i64 *) i_sp); 24 | #if d_m3HasFloat 25 | else if (i_type == c_m3Type_f32) 26 | len = snprintf (o_string, i_stringBufferSize, "%" PRIf32, * (f32 *) i_sp); 27 | else if (i_type == c_m3Type_f64) 28 | len = snprintf (o_string, i_stringBufferSize, "%" PRIf64, * (f64 *) i_sp); 29 | #endif 30 | 31 | len = M3_MAX (0, len); 32 | 33 | return len; 34 | } 35 | 36 | 37 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) 38 | { 39 | int ret; 40 | static char string [256]; 41 | 42 | char * s = string; 43 | ccstr_t e = string + sizeof(string) - 1; 44 | 45 | ret = snprintf (s, e-s, "("); 46 | s += M3_MAX (0, ret); 47 | 48 | u64 * argSp = (u64 *) i_sp; 49 | 50 | IM3FuncType funcType = i_function->funcType; 51 | if (funcType) 52 | { 53 | u32 numArgs = funcType->numArgs; 54 | 55 | for (u32 i = 0; i < numArgs; ++i) 56 | { 57 | u8 type = d_FuncArgType(funcType, i); 58 | 59 | ret = snprintf (s, e-s, "%s: ", c_waTypes [type]); 60 | s += M3_MAX (0, ret); 61 | 62 | s += SPrintArg (s, e-s, argSp + i, type); 63 | 64 | if (i != numArgs - 1) { 65 | ret = snprintf (s, e-s, ", "); 66 | s += M3_MAX (0, ret); 67 | } 68 | } 69 | } 70 | else printf ("null signature"); 71 | 72 | ret = snprintf (s, e-s, ")"); 73 | s += M3_MAX (0, ret); 74 | 75 | return string; 76 | } 77 | 78 | #endif 79 | 80 | #ifdef DEBUG 81 | 82 | // a central function you can be breakpoint: 83 | void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message) 84 | { 85 | printf ("\nexception: '%s' @ %s\n", i_exception, i_message); 86 | return; 87 | } 88 | 89 | 90 | typedef struct OpInfo 91 | { 92 | IM3OpInfo info; 93 | m3opcode_t opcode; 94 | } 95 | OpInfo; 96 | 97 | void m3_PrintM3Info () 98 | { 99 | printf ("\n-- m3 configuration --------------------------------------------\n"); 100 | // printf (" sizeof M3CodePage : %zu bytes (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots); 101 | printf (" sizeof M3MemPage : %u bytes \n", d_m3MemPageSize); 102 | printf (" sizeof M3Compilation : %zu bytes \n", sizeof (M3Compilation)); 103 | printf (" sizeof M3Function : %zu bytes \n", sizeof (M3Function)); 104 | printf ("----------------------------------------------------------------\n\n"); 105 | } 106 | 107 | 108 | void * v_PrintEnvModuleInfo (IM3Module i_module, u32 * io_index) 109 | { 110 | printf (" module [%u] name: '%s'; funcs: %d \n", * io_index++, i_module->name, i_module->numFunctions); 111 | 112 | return NULL; 113 | } 114 | 115 | 116 | void m3_PrintRuntimeInfo (IM3Runtime i_runtime) 117 | { 118 | printf ("\n-- m3 runtime -------------------------------------------------\n"); 119 | 120 | printf (" stack-size: %zu \n\n", i_runtime->numStackSlots * sizeof (m3slot_t)); 121 | 122 | u32 moduleIndex = 0; 123 | ForEachModule (i_runtime, (ModuleVisitor) v_PrintEnvModuleInfo, & moduleIndex); 124 | 125 | printf ("----------------------------------------------------------------\n\n"); 126 | } 127 | 128 | 129 | cstr_t GetTypeName (u8 i_m3Type) 130 | { 131 | if (i_m3Type < 5) 132 | return c_waTypes [i_m3Type]; 133 | else 134 | return "?"; 135 | } 136 | 137 | 138 | // TODO: these 'static char string []' aren't thread-friendly. though these functions are 139 | // mainly for simple diagnostics during development, it'd be nice if they were fully reliable. 140 | 141 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) 142 | { 143 | static char string [256]; 144 | 145 | sprintf (string, "("); 146 | 147 | for (u32 i = 0; i < i_funcType->numArgs; ++i) 148 | { 149 | if (i != 0) 150 | strcat (string, ", "); 151 | 152 | strcat (string, GetTypeName (d_FuncArgType(i_funcType, i))); 153 | } 154 | 155 | strcat (string, ") -> "); 156 | 157 | for (u32 i = 0; i < i_funcType->numRets; ++i) 158 | { 159 | if (i != 0) 160 | strcat (string, ", "); 161 | 162 | strcat (string, GetTypeName (d_FuncRetType(i_funcType, i))); 163 | } 164 | 165 | return string; 166 | } 167 | 168 | 169 | cstr_t SPrintValue (void * i_value, u8 i_type) 170 | { 171 | static char string [100]; 172 | SPrintArg (string, 100, (m3stack_t) i_value, i_type); 173 | return string; 174 | } 175 | 176 | static 177 | OpInfo find_operation_info (IM3Operation i_operation) 178 | { 179 | OpInfo opInfo = { NULL, 0 }; 180 | 181 | if (!i_operation) return opInfo; 182 | 183 | // TODO: find also extended opcodes 184 | for (u32 i = 0; i <= 0xff; ++i) 185 | { 186 | IM3OpInfo oi = GetOpInfo (i); 187 | 188 | if (oi->type != c_m3Type_unknown) 189 | { 190 | for (u32 o = 0; o < 4; ++o) 191 | { 192 | if (oi->operations [o] == i_operation) 193 | { 194 | opInfo.info = oi; 195 | opInfo.opcode = i; 196 | break; 197 | } 198 | } 199 | } 200 | else break; 201 | } 202 | 203 | return opInfo; 204 | } 205 | 206 | 207 | #undef fetch 208 | #define fetch(TYPE) (* (TYPE *) ((*o_pc)++)) 209 | 210 | #define d_m3Decoder(FUNC) void Decode_##FUNC (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) 211 | 212 | d_m3Decoder (Call) 213 | { 214 | void * function = fetch (void *); 215 | i32 stackOffset = fetch (i32); 216 | 217 | sprintf (o_string, "%p; stack-offset: %d", function, stackOffset); 218 | } 219 | 220 | 221 | d_m3Decoder (Entry) 222 | { 223 | IM3Function function = fetch (IM3Function); 224 | 225 | // only prints out the first registered name for the function 226 | sprintf (o_string, "%s", m3_GetFunctionName(function)); 227 | } 228 | 229 | 230 | d_m3Decoder (f64_Store) 231 | { 232 | if (i_operation == i_opInfo->operations [0]) 233 | { 234 | u32 operand = fetch (u32); 235 | u32 offset = fetch (u32); 236 | 237 | sprintf (o_string, "offset= slot:%d + immediate:%d", operand, offset); 238 | } 239 | 240 | // sprintf (o_string, "%s", function->name); 241 | } 242 | 243 | 244 | d_m3Decoder (Branch) 245 | { 246 | void * target = fetch (void *); 247 | sprintf (o_string, "%p", target); 248 | } 249 | 250 | d_m3Decoder (BranchTable) 251 | { 252 | u32 slot = fetch (u32); 253 | 254 | o_string += sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot); 255 | 256 | // IM3Function function = fetch2 (IM3Function); 257 | 258 | i32 targets = fetch (i32); 259 | 260 | for (i32 i = 0; i < targets; ++i) 261 | { 262 | pc_t addr = fetch (pc_t); 263 | o_string += sprintf (o_string, "%" PRIi32 "=%p, ", i, addr); 264 | } 265 | 266 | pc_t addr = fetch (pc_t); 267 | sprintf (o_string, "def=%p ", addr); 268 | } 269 | 270 | 271 | d_m3Decoder (Const) 272 | { 273 | u64 value = fetch (u64); i32 offset = fetch (i32); 274 | sprintf (o_string, " slot [%d] = %" PRIu64, offset, value); 275 | } 276 | 277 | 278 | #undef fetch 279 | 280 | void DecodeOperation (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) 281 | { 282 | #define d_m3Decode(OPCODE, FUNC) case OPCODE: Decode_##FUNC (o_string, i_opcode, i_operation, i_opInfo, o_pc); break; 283 | 284 | switch (i_opcode) 285 | { 286 | // d_m3Decode (0xc0, Const) 287 | d_m3Decode (0xc5, Entry) 288 | d_m3Decode (c_waOp_call, Call) 289 | d_m3Decode (c_waOp_branch, Branch) 290 | d_m3Decode (c_waOp_branchTable, BranchTable) 291 | d_m3Decode (0x39, f64_Store) 292 | } 293 | } 294 | 295 | // WARNING/TODO: this isn't fully implemented. it blindly assumes each word is a Operation pointer 296 | // and, if an operation happens to missing from the c_operations table it won't be recognized here 297 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) 298 | { 299 | m3log (code, "code page seq: %d", i_codePage->info.sequence); 300 | 301 | pc_t pc = i_startPC ? i_startPC : GetPageStartPC (i_codePage); 302 | pc_t end = GetPagePC (i_codePage); 303 | 304 | m3log (code, "---------------------------------------------------------------------------------------"); 305 | 306 | while (pc < end) 307 | { 308 | pc_t operationPC = pc; 309 | IM3Operation op = (IM3Operation) (* pc++); 310 | 311 | OpInfo i = find_operation_info (op); 312 | 313 | if (i.info) 314 | { 315 | char infoString [8*1024] = { 0 }; 316 | 317 | DecodeOperation (infoString, i.opcode, op, i.info, & pc); 318 | 319 | m3log (code, "%p | %20s %s", operationPC, i.info->name, infoString); 320 | } 321 | else 322 | m3log (code, "%p | %p", operationPC, op); 323 | 324 | } 325 | 326 | m3log (code, "---------------------------------------------------------------------------------------"); 327 | 328 | m3log (code, "free-lines: %d", i_codePage->info.numLines - i_codePage->info.lineIndex); 329 | } 330 | 331 | 332 | void dump_type_stack (IM3Compilation o) 333 | { 334 | /* Reminders about how the stack works! :) 335 | -- args & locals remain on the type stack for duration of the function. Denoted with a constant 'A' and 'L' in this dump. 336 | -- the initial stack dumps originate from the CompileLocals () function, so these identifiers won't/can't be 337 | applied until this compilation stage is finished 338 | -- constants are not statically represented in the type stack (like args & constants) since they don't have/need 339 | write counts 340 | 341 | -- the number shown for static args and locals (value in wasmStack [i]) represents the write count for the variable 342 | 343 | -- (does Wasm ever write to an arg? I dunno/don't remember.) 344 | -- the number for the dynamic stack values represents the slot number. 345 | -- if the slot index points to arg, local or constant it's denoted with a lowercase 'a', 'l' or 'c' 346 | 347 | */ 348 | 349 | // for the assert at end of dump: 350 | i32 regAllocated [2] = { (i32) IsRegisterAllocated (o, 0), (i32) IsRegisterAllocated (o, 1) }; 351 | 352 | // display whether r0 or fp0 is allocated. these should then also be reflected somewhere in the stack too. 353 | d_m3Log(stack, "\n"); 354 | d_m3Log(stack, " "); 355 | printf ("%s %s ", regAllocated [0] ? "(r0)" : " ", regAllocated [1] ? "(fp0)" : " "); 356 | printf("\n"); 357 | 358 | for (u32 p = 1; p <= 2; ++p) 359 | { 360 | d_m3Log(stack, " "); 361 | 362 | for (u16 i = 0; i < o->stackIndex; ++i) 363 | { 364 | if (i > 0 and i == o->stackFirstDynamicIndex) 365 | printf ("#"); 366 | 367 | if (i == o->block.blockStackIndex) 368 | printf (">"); 369 | 370 | const char * type = c_waCompactTypes [o->typeStack [i]]; 371 | 372 | const char * location = ""; 373 | 374 | i32 slot = o->wasmStack [i]; 375 | 376 | if (IsRegisterSlotAlias (slot)) 377 | { 378 | bool isFp = IsFpRegisterSlotAlias (slot); 379 | location = isFp ? "/f" : "/r"; 380 | 381 | regAllocated [isFp]--; 382 | slot = -1; 383 | } 384 | else 385 | { 386 | if (slot < o->slotFirstDynamicIndex) 387 | { 388 | if (slot >= o->slotFirstConstIndex) 389 | location = "c"; 390 | else if (slot >= o->function->numRetAndArgSlots) 391 | location = "L"; 392 | else 393 | location = "a"; 394 | } 395 | } 396 | 397 | char item [100]; 398 | 399 | if (slot >= 0) 400 | sprintf (item, "%s%s%d", type, location, slot); 401 | else 402 | sprintf (item, "%s%s", type, location); 403 | 404 | if (p == 1) 405 | { 406 | size_t s = strlen (item); 407 | 408 | sprintf (item, "%d", i); 409 | 410 | while (strlen (item) < s) 411 | strcat (item, " "); 412 | } 413 | 414 | printf ("|%s ", item); 415 | 416 | } 417 | printf ("\n"); 418 | } 419 | 420 | // for (u32 r = 0; r < 2; ++r) 421 | // d_m3Assert (regAllocated [r] == 0); // reg allocation & stack out of sync 422 | 423 | u16 maxSlot = GetMaxUsedSlotPlusOne (o); 424 | 425 | if (maxSlot > o->slotFirstDynamicIndex) 426 | { 427 | d_m3Log (stack, " -"); 428 | 429 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 430 | printf ("----"); 431 | 432 | printf ("\n"); 433 | 434 | d_m3Log (stack, " slot |"); 435 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 436 | printf ("%3d|", i); 437 | 438 | printf ("\n"); 439 | d_m3Log (stack, " alloc |"); 440 | 441 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 442 | { 443 | printf ("%3d|", o->m3Slots [i]); 444 | } 445 | 446 | printf ("\n"); 447 | } 448 | d_m3Log(stack, "\n"); 449 | } 450 | 451 | 452 | static const char * GetOpcodeIndentionString (i32 blockDepth) 453 | { 454 | blockDepth += 1; 455 | 456 | if (blockDepth < 0) 457 | blockDepth = 0; 458 | 459 | static const char * s_spaces = "......................................................................................."; 460 | const char * indent = s_spaces + strlen (s_spaces); 461 | indent -= (blockDepth * 2); 462 | if (indent < s_spaces) 463 | indent = s_spaces; 464 | 465 | return indent; 466 | } 467 | 468 | 469 | const char * get_indention_string (IM3Compilation o) 470 | { 471 | return GetOpcodeIndentionString (o->block.depth+4); 472 | } 473 | 474 | 475 | void log_opcode (IM3Compilation o, m3opcode_t i_opcode) 476 | { 477 | i32 depth = o->block.depth; 478 | if (i_opcode == c_waOp_end or i_opcode == c_waOp_else) 479 | depth--; 480 | 481 | m3log (compile, "%4d | 0x%02x %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), GetOpInfo(i_opcode)->name); 482 | } 483 | 484 | 485 | void log_emit (IM3Compilation o, IM3Operation i_operation) 486 | { 487 | OpInfo i = find_operation_info (i_operation); 488 | 489 | d_m3Log(emit, ""); 490 | if (i.info) 491 | { 492 | printf ("%p: %s\n", GetPagePC (o->page), i.info->name); 493 | } 494 | else printf ("not found: %p\n", i_operation); 495 | } 496 | 497 | #endif // DEBUG 498 | 499 | 500 | # if d_m3EnableOpProfiling 501 | 502 | typedef struct M3ProfilerSlot 503 | { 504 | cstr_t opName; 505 | u64 hitCount; 506 | } 507 | M3ProfilerSlot; 508 | 509 | static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {}; 510 | 511 | void ProfileHit (cstr_t i_operationName) 512 | { 513 | u64 ptr = (u64) i_operationName; 514 | 515 | M3ProfilerSlot * slot = & s_opProfilerCounts [ptr & d_m3ProfilerSlotMask]; 516 | 517 | if (slot->opName) 518 | { 519 | if (slot->opName != i_operationName) 520 | { 521 | m3_Abort ("profiler slot collision; increase d_m3ProfilerSlotMask"); 522 | } 523 | } 524 | 525 | slot->opName = i_operationName; 526 | slot->hitCount++; 527 | } 528 | 529 | 530 | void m3_PrintProfilerInfo () 531 | { 532 | M3ProfilerSlot dummy; 533 | M3ProfilerSlot * maxSlot = & dummy; 534 | 535 | do 536 | { 537 | maxSlot->hitCount = 0; 538 | 539 | for (u32 i = 0; i <= d_m3ProfilerSlotMask; ++i) 540 | { 541 | M3ProfilerSlot * slot = & s_opProfilerCounts [i]; 542 | 543 | if (slot->opName) 544 | { 545 | if (slot->hitCount > maxSlot->hitCount) 546 | maxSlot = slot; 547 | } 548 | } 549 | 550 | if (maxSlot->opName) 551 | { 552 | fprintf (stderr, "%13llu %s\n", maxSlot->hitCount, maxSlot->opName); 553 | maxSlot->opName = NULL; 554 | } 555 | } 556 | while (maxSlot->hitCount); 557 | } 558 | 559 | # else 560 | 561 | void m3_PrintProfilerInfo () {} 562 | 563 | # endif 564 | 565 | -------------------------------------------------------------------------------- /external/m3_info.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.h 3 | // 4 | // Created by Steven Massey on 12/6/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_info_h 9 | #define m3_info_h 10 | 11 | #include "m3_compile.h" 12 | 13 | d_m3BeginExternC 14 | 15 | void ProfileHit (cstr_t i_operationName); 16 | 17 | #ifdef DEBUG 18 | 19 | void dump_type_stack (IM3Compilation o); 20 | void log_opcode (IM3Compilation o, m3opcode_t i_opcode); 21 | const char * get_indention_string (IM3Compilation o); 22 | void log_emit (IM3Compilation o, IM3Operation i_operation); 23 | 24 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); 25 | 26 | #else // DEBUG 27 | 28 | #define dump_type_stack(...) {} 29 | #define log_opcode(...) {} 30 | #define get_indention_string(...) "" 31 | #define emit_stack_dump(...) {} 32 | #define log_emit(...) {} 33 | 34 | #endif // DEBUG 35 | 36 | d_m3EndExternC 37 | 38 | #endif // m3_info_h 39 | -------------------------------------------------------------------------------- /external/m3_math_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_math_utils.h 3 | // 4 | // Created by Volodymyr Shymanksyy on 8/10/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_math_utils_h 9 | #define m3_math_utils_h 10 | 11 | #include "m3_core.h" 12 | 13 | #include 14 | 15 | #if defined(M3_COMPILER_MSVC) 16 | 17 | #include 18 | 19 | #define __builtin_popcount __popcnt 20 | 21 | static inline 22 | int __builtin_ctz(uint32_t x) { 23 | unsigned long ret; 24 | _BitScanForward(&ret, x); 25 | return (int)ret; 26 | } 27 | 28 | static inline 29 | int __builtin_clz(uint32_t x) { 30 | unsigned long ret; 31 | _BitScanReverse(&ret, x); 32 | return (int)(31 ^ ret); 33 | } 34 | 35 | 36 | 37 | #ifdef _WIN64 38 | 39 | #define __builtin_popcountll __popcnt64 40 | 41 | static inline 42 | int __builtin_ctzll(uint64_t value) { 43 | unsigned long ret; 44 | _BitScanForward64(&ret, value); 45 | return (int)ret; 46 | } 47 | 48 | static inline 49 | int __builtin_clzll(uint64_t value) { 50 | unsigned long ret; 51 | _BitScanReverse64(&ret, value); 52 | return (int)(63 ^ ret); 53 | } 54 | 55 | #else // _WIN64 56 | 57 | #define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32)) 58 | 59 | static inline 60 | int __builtin_ctzll(uint64_t value) { 61 | //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway 62 | uint32_t msh = (uint32_t)(value >> 32); 63 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 64 | if (lsh != 0) return __builtin_ctz(lsh); 65 | return 32 + __builtin_ctz(msh); 66 | } 67 | 68 | static inline 69 | int __builtin_clzll(uint64_t value) { 70 | //if (value == 0) return 64; // Note: clz(0) result is undefined anyway 71 | uint32_t msh = (uint32_t)(value >> 32); 72 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 73 | if (msh != 0) return __builtin_clz(msh); 74 | return 32 + __builtin_clz(lsh); 75 | } 76 | 77 | #endif // _WIN64 78 | 79 | #endif // defined(M3_COMPILER_MSVC) 80 | 81 | 82 | // TODO: not sure why, signbit is actually defined in math.h 83 | #if (defined(ESP8266) || defined(ESP32)) && !defined(signbit) 84 | #define signbit(__x) \ 85 | ((sizeof(__x) == sizeof(float)) ? __signbitf(__x) : __signbitd(__x)) 86 | #endif 87 | 88 | #if defined(__AVR__) 89 | 90 | static inline 91 | float rintf( float arg ) { 92 | union { float f; uint32_t i; } u; 93 | u.f = arg; 94 | uint32_t ux = u.i & 0x7FFFFFFF; 95 | if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) { 96 | return arg; 97 | } 98 | return (float)lrint(arg); 99 | } 100 | 101 | static inline 102 | double rint( double arg ) { 103 | union { double f; uint32_t i[2]; } u; 104 | u.f = arg; 105 | uint32_t ux = u.i[1] & 0x7FFFFFFF; 106 | if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) { 107 | return arg; 108 | } 109 | return (double)lrint(arg); 110 | } 111 | 112 | //TODO 113 | static inline 114 | uint64_t strtoull(const char* str, char** endptr, int base) { 115 | return 0; 116 | } 117 | 118 | #endif 119 | 120 | /* 121 | * Rotr, Rotl 122 | */ 123 | 124 | static inline 125 | u32 rotl32(u32 n, unsigned c) { 126 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 127 | c &= mask & 31; 128 | return (n << c) | (n >> ((-c) & mask)); 129 | } 130 | 131 | static inline 132 | u32 rotr32(u32 n, unsigned c) { 133 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 134 | c &= mask & 31; 135 | return (n >> c) | (n << ((-c) & mask)); 136 | } 137 | 138 | static inline 139 | u64 rotl64(u64 n, unsigned c) { 140 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 141 | c &= mask & 63; 142 | return (n << c) | (n >> ((-c) & mask)); 143 | } 144 | 145 | static inline 146 | u64 rotr64(u64 n, unsigned c) { 147 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 148 | c &= mask & 63; 149 | return (n >> c) | (n << ((-c) & mask)); 150 | } 151 | 152 | /* 153 | * Integer Div, Rem 154 | */ 155 | 156 | #define OP_DIV_U(RES, A, B) \ 157 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 158 | RES = A / B; 159 | 160 | #define OP_REM_U(RES, A, B) \ 161 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 162 | RES = A % B; 163 | 164 | // 2's complement detection 165 | #if (INT_MIN != -INT_MAX) 166 | 167 | #define OP_DIV_S(RES, A, B, TYPE_MIN) \ 168 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 169 | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) { \ 170 | newTrap (m3Err_trapIntegerOverflow); \ 171 | } \ 172 | RES = A / B; 173 | 174 | #define OP_REM_S(RES, A, B, TYPE_MIN) \ 175 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 176 | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ 177 | else RES = A % B; 178 | 179 | #else 180 | 181 | #define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B) 182 | #define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B) 183 | 184 | #endif 185 | 186 | /* 187 | * Trunc 188 | */ 189 | 190 | #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ 191 | if (M3_UNLIKELY(isnan(A))) { \ 192 | newTrap (m3Err_trapIntegerConversion); \ 193 | } \ 194 | if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) { \ 195 | newTrap (m3Err_trapIntegerOverflow); \ 196 | } \ 197 | RES = (TYPE)A; 198 | 199 | 200 | #define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f) 201 | #define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f) 202 | #define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 ) 203 | #define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 ) 204 | 205 | #define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f) 206 | #define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f) 207 | #define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 ) 208 | #define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 ) 209 | 210 | #define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \ 211 | if (M3_UNLIKELY(isnan(A))) { \ 212 | RES = 0; \ 213 | } else if (M3_UNLIKELY(A <= RMIN)) { \ 214 | RES = IMIN; \ 215 | } else if (M3_UNLIKELY(A >= RMAX)) { \ 216 | RES = IMAX; \ 217 | } else { \ 218 | RES = (TYPE)A; \ 219 | } 220 | 221 | #define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX) 222 | #define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX) 223 | #define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX) 224 | #define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX) 225 | 226 | #define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX) 227 | #define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX) 228 | #define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX) 229 | #define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX) 230 | 231 | /* 232 | * Min, Max 233 | */ 234 | 235 | #if d_m3HasFloat 236 | 237 | #include 238 | 239 | static inline 240 | f32 min_f32(f32 a, f32 b) { 241 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 242 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 243 | return a > b ? b : a; 244 | } 245 | 246 | static inline 247 | f32 max_f32(f32 a, f32 b) { 248 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 249 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 250 | return a > b ? a : b; 251 | } 252 | 253 | static inline 254 | f64 min_f64(f64 a, f64 b) { 255 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 256 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 257 | return a > b ? b : a; 258 | } 259 | 260 | static inline 261 | f64 max_f64(f64 a, f64 b) { 262 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 263 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 264 | return a > b ? a : b; 265 | } 266 | #endif 267 | 268 | #endif // m3_math_utils_h 269 | -------------------------------------------------------------------------------- /external/m3_module.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_module.c 3 | // 4 | // Created by Steven Massey on 5/7/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_exception.h" 10 | 11 | 12 | void Module_FreeFunctions (IM3Module i_module) 13 | { 14 | for (u32 i = 0; i < i_module->numFunctions; ++i) 15 | { 16 | IM3Function func = & i_module->functions [i]; 17 | Function_Release (func); 18 | } 19 | } 20 | 21 | 22 | void m3_FreeModule (IM3Module i_module) 23 | { 24 | if (i_module) 25 | { 26 | m3log (module, "freeing module: %s (funcs: %d; segments: %d)", 27 | i_module->name, i_module->numFunctions, i_module->numDataSegments); 28 | 29 | Module_FreeFunctions (i_module); 30 | 31 | m3_Free (i_module->functions); 32 | //m3_Free (i_module->imports); 33 | m3_Free (i_module->funcTypes); 34 | m3_Free (i_module->dataSegments); 35 | m3_Free (i_module->table0); 36 | 37 | for (u32 i = 0; i < i_module->numGlobals; ++i) 38 | { 39 | m3_Free (i_module->globals[i].name); 40 | FreeImportInfo(&(i_module->globals[i].import)); 41 | } 42 | m3_Free (i_module->globals); 43 | 44 | m3_Free (i_module); 45 | } 46 | } 47 | 48 | 49 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported) 50 | { 51 | _try { 52 | u32 index = io_module->numGlobals++; 53 | io_module->globals = m3_ReallocArray (M3Global, io_module->globals, io_module->numGlobals, index); 54 | _throwifnull (io_module->globals); 55 | M3Global * global = & io_module->globals [index]; 56 | 57 | global->type = i_type; 58 | global->imported = i_isImported; 59 | global->isMutable = i_mutable; 60 | 61 | if (o_global) 62 | * o_global = global; 63 | 64 | } _catch: 65 | return result; 66 | } 67 | 68 | M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions) 69 | { 70 | _try { 71 | if (i_totalFunctions > io_module->allFunctions) { 72 | io_module->functions = m3_ReallocArray (M3Function, io_module->functions, i_totalFunctions, io_module->allFunctions); 73 | io_module->allFunctions = i_totalFunctions; 74 | _throwifnull (io_module->functions); 75 | } 76 | } _catch: 77 | return result; 78 | } 79 | 80 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo) 81 | { 82 | _try { 83 | 84 | u32 index = io_module->numFunctions++; 85 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions)); 86 | 87 | _throwif ("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); 88 | 89 | IM3FuncType ft = io_module->funcTypes [i_typeIndex]; 90 | 91 | IM3Function func = Module_GetFunction (io_module, index); 92 | func->funcType = ft; 93 | 94 | # ifdef DEBUG 95 | func->index = index; 96 | # endif 97 | 98 | if (i_importInfo and func->numNames == 0) 99 | { 100 | func->import = * i_importInfo; 101 | func->names[0] = i_importInfo->fieldUtf8; 102 | func->numNames = 1; 103 | } 104 | 105 | m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); 106 | 107 | } _catch: 108 | return result; 109 | } 110 | 111 | #ifdef DEBUG 112 | void Module_GenerateNames (IM3Module i_module) 113 | { 114 | for (u32 i = 0; i < i_module->numFunctions; ++i) 115 | { 116 | IM3Function func = & i_module->functions [i]; 117 | 118 | if (func->numNames == 0) 119 | { 120 | char* buff = m3_AllocArray(char, 16); 121 | snprintf(buff, 16, "$func%d", i); 122 | func->names[0] = buff; 123 | func->numNames = 1; 124 | } 125 | } 126 | for (u32 i = 0; i < i_module->numGlobals; ++i) 127 | { 128 | IM3Global global = & i_module->globals [i]; 129 | 130 | if (global->name == NULL) 131 | { 132 | char* buff = m3_AllocArray(char, 16); 133 | snprintf(buff, 16, "$global%d", i); 134 | global->name = buff; 135 | } 136 | } 137 | } 138 | #endif 139 | 140 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) 141 | { 142 | IM3Function func = NULL; 143 | 144 | if (i_functionIndex < i_module->numFunctions) 145 | { 146 | func = & i_module->functions [i_functionIndex]; 147 | //func->module = i_module; 148 | } 149 | 150 | return func; 151 | } 152 | 153 | 154 | const char* m3_GetModuleName (IM3Module i_module) 155 | { 156 | if (!i_module || !i_module->name) 157 | return ".unnamed"; 158 | 159 | return i_module->name; 160 | } 161 | 162 | void m3_SetModuleName (IM3Module i_module, const char* name) 163 | { 164 | if (i_module) i_module->name = name; 165 | } 166 | 167 | IM3Runtime m3_GetModuleRuntime (IM3Module i_module) 168 | { 169 | return i_module ? i_module->runtime : NULL; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /external/m3_parse.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_parse.c 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_compile.h" 10 | #include "m3_exception.h" 11 | #include "m3_info.h" 12 | 13 | 14 | M3Result ParseType_Table (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 15 | { 16 | M3Result result = m3Err_none; 17 | 18 | return result; 19 | } 20 | 21 | 22 | M3Result ParseType_Memory (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end) 23 | { 24 | M3Result result = m3Err_none; 25 | 26 | u8 flag; 27 | 28 | _ (ReadLEB_u7 (& flag, io_bytes, i_end)); // really a u1 29 | _ (ReadLEB_u32 (& o_memory->initPages, io_bytes, i_end)); 30 | 31 | o_memory->maxPages = 0; 32 | if (flag) 33 | _ (ReadLEB_u32 (& o_memory->maxPages, io_bytes, i_end)); 34 | 35 | _catch: return result; 36 | } 37 | 38 | 39 | M3Result ParseSection_Type (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 40 | { 41 | IM3FuncType ftype = NULL; 42 | 43 | _try { 44 | u32 numTypes; 45 | _ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes); 46 | 47 | _throwif("too many types", numTypes > d_m3MaxSaneTypesCount); 48 | 49 | if (numTypes) 50 | { 51 | // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment) 52 | io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes); 53 | _throwifnull (io_module->funcTypes); 54 | io_module->numFuncTypes = numTypes; 55 | 56 | for (u32 i = 0; i < numTypes; ++i) 57 | { 58 | i8 form; 59 | _ (ReadLEB_i7 (& form, & i_bytes, i_end)); 60 | _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP 61 | 62 | u32 numArgs; 63 | _ (ReadLEB_u32 (& numArgs, & i_bytes, i_end)); 64 | 65 | _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount); 66 | #if defined(M3_COMPILER_MSVC) 67 | u8 argTypes [d_m3MaxSaneFunctionArgRetCount]; 68 | #else 69 | u8 argTypes[numArgs+1]; // make ubsan happy 70 | #endif 71 | for (u32 a = 0; a < numArgs; ++a) 72 | { 73 | i8 wasmType; 74 | u8 argType; 75 | _ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); 76 | _ (NormalizeType (& argType, wasmType)); 77 | 78 | argTypes[a] = argType; 79 | } 80 | 81 | u32 numRets; 82 | _ (ReadLEB_u32 (& numRets, & i_bytes, i_end)); 83 | _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount); 84 | 85 | _ (AllocFuncType (& ftype, numRets + numArgs)); 86 | ftype->numArgs = numArgs; 87 | ftype->numRets = numRets; 88 | 89 | for (u32 r = 0; r < numRets; ++r) 90 | { 91 | i8 wasmType; 92 | u8 retType; 93 | _ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); 94 | _ (NormalizeType (& retType, wasmType)); 95 | 96 | ftype->types[r] = retType; 97 | } 98 | memcpy (ftype->types + numRets, argTypes, numArgs); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); 99 | 100 | Environment_AddFuncType (io_module->environment, & ftype); 101 | io_module->funcTypes [i] = ftype; 102 | ftype = NULL; // ownership transferred to environment 103 | } 104 | } 105 | 106 | } _catch: 107 | 108 | if (result) 109 | { 110 | m3_Free (ftype); 111 | // FIX: M3FuncTypes in the table are leaked 112 | m3_Free (io_module->funcTypes); 113 | io_module->numFuncTypes = 0; 114 | } 115 | 116 | return result; 117 | } 118 | 119 | 120 | M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 121 | { 122 | M3Result result = m3Err_none; 123 | 124 | u32 numFunctions; 125 | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions); 126 | 127 | _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount); 128 | 129 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions)); 130 | 131 | for (u32 i = 0; i < numFunctions; ++i) 132 | { 133 | u32 funcTypeIndex; 134 | _ (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end)); 135 | 136 | _ (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */)); 137 | } 138 | 139 | _catch: return result; 140 | } 141 | 142 | 143 | M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 144 | { 145 | M3Result result = m3Err_none; 146 | 147 | M3ImportInfo import = { NULL, NULL }, clearImport = { NULL, NULL }; 148 | 149 | u32 numImports; 150 | _ (ReadLEB_u32 (& numImports, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numImports); 151 | 152 | _throwif("too many imports", numImports > d_m3MaxSaneImportsCount); 153 | 154 | // Most imports are functions, so we won't waste much space anyway (if any) 155 | _ (Module_PreallocFunctions(io_module, numImports)); 156 | 157 | for (u32 i = 0; i < numImports; ++i) 158 | { 159 | u8 importKind; 160 | 161 | _ (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end)); 162 | _ (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end)); 163 | _ (Read_u8 (& importKind, & i_bytes, i_end)); m3log (parse, " kind: %d '%s.%s' ", 164 | (u32) importKind, import.moduleUtf8, import.fieldUtf8); 165 | switch (importKind) 166 | { 167 | case d_externalKind_function: 168 | { 169 | u32 typeIndex; 170 | _ (ReadLEB_u32 (& typeIndex, & i_bytes, i_end)) 171 | 172 | _ (Module_AddFunction (io_module, typeIndex, & import)) 173 | import = clearImport; 174 | 175 | io_module->numFuncImports++; 176 | } 177 | break; 178 | 179 | case d_externalKind_table: 180 | // result = ParseType_Table (& i_bytes, i_end); 181 | break; 182 | 183 | case d_externalKind_memory: 184 | { 185 | _ (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end)); 186 | io_module->memoryImported = true; 187 | } 188 | break; 189 | 190 | case d_externalKind_global: 191 | { 192 | i8 waType; 193 | u8 type, isMutable; 194 | 195 | _ (ReadLEB_i7 (& waType, & i_bytes, i_end)); 196 | _ (NormalizeType (& type, waType)); 197 | _ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: %s mutable=%d", c_waTypes [type], (u32) isMutable); 198 | 199 | IM3Global global; 200 | _ (Module_AddGlobal (io_module, & global, type, isMutable, true /* isImport */)); 201 | global->import = import; 202 | import = clearImport; 203 | } 204 | break; 205 | 206 | default: 207 | _throw (m3Err_wasmMalformed); 208 | } 209 | 210 | FreeImportInfo (& import); 211 | } 212 | 213 | _catch: 214 | 215 | FreeImportInfo (& import); 216 | 217 | return result; 218 | } 219 | 220 | 221 | M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 222 | { 223 | M3Result result = m3Err_none; 224 | const char * utf8 = NULL; 225 | 226 | u32 numExports; 227 | _ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports); 228 | 229 | _throwif("too many exports", numExports > d_m3MaxSaneExportsCount); 230 | 231 | for (u32 i = 0; i < numExports; ++i) 232 | { 233 | u8 exportKind; 234 | u32 index; 235 | 236 | _ (Read_utf8 (& utf8, & i_bytes, i_end)); 237 | _ (Read_u8 (& exportKind, & i_bytes, i_end)); 238 | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); m3log (parse, " index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8); 239 | 240 | if (exportKind == d_externalKind_function) 241 | { 242 | _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); 243 | IM3Function func = &(io_module->functions [index]); 244 | if (func->numNames < d_m3MaxDuplicateFunctionImpl) 245 | { 246 | func->names[func->numNames++] = utf8; 247 | func->export_name = utf8; 248 | utf8 = NULL; // ownership transferred to M3Function 249 | } 250 | } 251 | else if (exportKind == d_externalKind_global) 252 | { 253 | _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals); 254 | IM3Global global = &(io_module->globals [index]); 255 | m3_Free (global->name); 256 | global->name = utf8; 257 | utf8 = NULL; // ownership transferred to M3Global 258 | } 259 | 260 | m3_Free (utf8); 261 | } 262 | 263 | _catch: 264 | m3_Free (utf8); 265 | return result; 266 | } 267 | 268 | 269 | M3Result ParseSection_Start (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 270 | { 271 | M3Result result = m3Err_none; 272 | 273 | u32 startFuncIndex; 274 | _ (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end)); m3log (parse, "** Start Function: %d", startFuncIndex); 275 | 276 | if (startFuncIndex < io_module->numFunctions) 277 | { 278 | io_module->startFunction = startFuncIndex; 279 | } 280 | else result = "start function index out of bounds"; 281 | 282 | _catch: return result; 283 | } 284 | 285 | 286 | M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end) 287 | { 288 | M3Result result = m3Err_none; 289 | 290 | // this doesn't generate code pages. just walks the wasm bytecode to find the end 291 | 292 | #if defined(d_m3PreferStaticAlloc) 293 | static M3Compilation compilation; 294 | #else 295 | M3Compilation compilation; 296 | #endif 297 | compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end }; 298 | 299 | result = CompileBlockStatements (& compilation); 300 | 301 | * io_bytes = compilation.wasm; 302 | 303 | return result; 304 | } 305 | 306 | 307 | M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) 308 | { 309 | M3Result result = m3Err_none; 310 | 311 | u32 numSegments; 312 | _ (ReadLEB_u32 (& numSegments, & i_bytes, i_end)); m3log (parse, "** Element [%d]", numSegments); 313 | 314 | _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments); 315 | 316 | io_module->elementSection = i_bytes; 317 | io_module->elementSectionEnd = i_end; 318 | io_module->numElementSegments = numSegments; 319 | 320 | _catch: return result; 321 | } 322 | 323 | 324 | M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 325 | { 326 | M3Result result; 327 | 328 | u32 numFunctions; 329 | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions); 330 | 331 | if (numFunctions != io_module->numFunctions - io_module->numFuncImports) 332 | { 333 | _throw ("mismatched function count in code section"); 334 | } 335 | 336 | for (u32 f = 0; f < numFunctions; ++f) 337 | { 338 | const u8 * start = i_bytes; 339 | 340 | u32 size; 341 | _ (ReadLEB_u32 (& size, & i_bytes, i_end)); 342 | 343 | if (size) 344 | { 345 | const u8 * ptr = i_bytes; 346 | i_bytes += size; 347 | 348 | if (i_bytes <= i_end) 349 | { 350 | /* 351 | u32 numLocalBlocks; 352 | _ (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end)); m3log (parse, " code size: %-4d", size); 353 | 354 | u32 numLocals = 0; 355 | 356 | for (u32 l = 0; l < numLocalBlocks; ++l) 357 | { 358 | u32 varCount; 359 | i8 wasmType; 360 | u8 normalType; 361 | 362 | _ (ReadLEB_u32 (& varCount, & ptr, i_end)); 363 | _ (ReadLEB_i7 (& wasmType, & ptr, i_end)); 364 | _ (NormalizeType (& normalType, wasmType)); 365 | 366 | numLocals += varCount; m3log (parse, " %2d locals; type: '%s'", varCount, c_waTypes [normalType]); 367 | } 368 | */ 369 | 370 | IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports); 371 | 372 | func->module = io_module; 373 | func->wasm = start; 374 | func->wasmEnd = i_bytes; 375 | //func->ownsWasmCode = io_module->hasWasmCodeCopy; 376 | // func->numLocals = numLocals; 377 | } 378 | else _throw (m3Err_wasmSectionOverrun); 379 | } 380 | } 381 | 382 | _catch: 383 | 384 | if (not result and i_bytes != i_end) 385 | result = m3Err_wasmSectionUnderrun; 386 | 387 | return result; 388 | } 389 | 390 | 391 | M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 392 | { 393 | M3Result result = m3Err_none; 394 | 395 | u32 numDataSegments; 396 | _ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments); 397 | 398 | _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments); 399 | 400 | io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments); 401 | _throwifnull(io_module->dataSegments); 402 | io_module->numDataSegments = numDataSegments; 403 | 404 | for (u32 i = 0; i < numDataSegments; ++i) 405 | { 406 | M3DataSegment * segment = & io_module->dataSegments [i]; 407 | 408 | _ (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end)); 409 | 410 | segment->initExpr = i_bytes; 411 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); 412 | segment->initExprSize = (u32) (i_bytes - segment->initExpr); 413 | 414 | _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1); 415 | 416 | _ (ReadLEB_u32 (& segment->size, & i_bytes, i_end)); 417 | segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d", 418 | i, segment->memoryRegion, segment->initExprSize, segment->size); 419 | i_bytes += segment->size; 420 | 421 | _throwif("data segment underflow", i_bytes > i_end); 422 | } 423 | 424 | _catch: 425 | 426 | return result; 427 | } 428 | 429 | 430 | M3Result ParseSection_Memory (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 431 | { 432 | M3Result result = m3Err_none; 433 | 434 | // TODO: MVP; assert no memory imported 435 | 436 | u32 numMemories; 437 | _ (ReadLEB_u32 (& numMemories, & i_bytes, i_end)); m3log (parse, "** Memory [%d]", numMemories); 438 | 439 | _throwif (m3Err_tooManyMemorySections, numMemories != 1); 440 | 441 | ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end); 442 | 443 | _catch: return result; 444 | } 445 | 446 | 447 | M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 448 | { 449 | M3Result result = m3Err_none; 450 | 451 | u32 numGlobals; 452 | _ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals); 453 | 454 | _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount); 455 | 456 | for (u32 i = 0; i < numGlobals; ++i) 457 | { 458 | i8 waType; 459 | u8 type, isMutable; 460 | 461 | _ (ReadLEB_i7 (& waType, & i_bytes, i_end)); 462 | _ (NormalizeType (& type, waType)); 463 | _ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: [%d] %s mutable: %d", i, c_waTypes [type], (u32) isMutable); 464 | 465 | IM3Global global; 466 | _ (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */)); 467 | 468 | global->initExpr = i_bytes; 469 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); 470 | global->initExprSize = (u32) (i_bytes - global->initExpr); 471 | 472 | _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1); 473 | } 474 | 475 | _catch: return result; 476 | } 477 | 478 | 479 | M3Result ParseSection_Name (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 480 | { 481 | M3Result result; 482 | 483 | cstr_t name; 484 | 485 | while (i_bytes < i_end) 486 | { 487 | u8 nameType; 488 | u32 payloadLength; 489 | 490 | _ (ReadLEB_u7 (& nameType, & i_bytes, i_end)); 491 | _ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end)); 492 | 493 | bytes_t start = i_bytes; 494 | if (nameType == 1) 495 | { 496 | u32 numNames; 497 | _ (ReadLEB_u32 (& numNames, & i_bytes, i_end)); 498 | 499 | _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount); 500 | 501 | for (u32 i = 0; i < numNames; ++i) 502 | { 503 | u32 index; 504 | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); 505 | _ (Read_utf8 (& name, & i_bytes, i_end)); 506 | 507 | if (index < io_module->numFunctions) 508 | { 509 | IM3Function func = &(io_module->functions [index]); 510 | if (func->numNames == 0) 511 | { 512 | func->names[0] = name; m3log (parse, " naming function%5d: %s", index, name); 513 | func->numNames = 1; 514 | name = NULL; // transfer ownership 515 | } 516 | // else m3log (parse, "prenamed: %s", io_module->functions [index].name); 517 | } 518 | 519 | m3_Free (name); 520 | } 521 | } 522 | 523 | i_bytes = start + payloadLength; 524 | } 525 | 526 | _catch: return result; 527 | } 528 | 529 | 530 | M3Result ParseSection_Custom (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) 531 | { 532 | M3Result result; 533 | 534 | cstr_t name; 535 | _ (Read_utf8 (& name, & i_bytes, i_end)); 536 | m3log (parse, "** Custom: '%s'", name); 537 | if (strcmp (name, "name") == 0) { 538 | _ (ParseSection_Name(io_module, i_bytes, i_end)); 539 | } else if (io_module->environment->customSectionHandler) { 540 | _ (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end)); 541 | } 542 | 543 | m3_Free (name); 544 | 545 | _catch: return result; 546 | } 547 | 548 | 549 | M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes) 550 | { 551 | M3Result result = m3Err_none; 552 | 553 | typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t); 554 | 555 | static M3Parser s_parsers [] = 556 | { 557 | ParseSection_Custom, // 0 558 | ParseSection_Type, // 1 559 | ParseSection_Import, // 2 560 | ParseSection_Function, // 3 561 | NULL, // 4: TODO Table 562 | ParseSection_Memory, // 5 563 | ParseSection_Global, // 6 564 | ParseSection_Export, // 7 565 | ParseSection_Start, // 8 566 | ParseSection_Element, // 9 567 | ParseSection_Code, // 10 568 | ParseSection_Data, // 11 569 | NULL, // 12: TODO DataCount 570 | }; 571 | 572 | M3Parser parser = NULL; 573 | 574 | if (i_sectionType <= 12) 575 | parser = s_parsers [i_sectionType]; 576 | 577 | if (parser) 578 | { 579 | cbytes_t end = i_bytes + i_numBytes; 580 | result = parser (o_module, i_bytes, end); 581 | } 582 | else 583 | { 584 | m3log (parse, " skipped section type: %d", (u32) i_sectionType); 585 | } 586 | 587 | return result; 588 | } 589 | 590 | 591 | M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes) 592 | { 593 | IM3Module module; m3log (parse, "load module: %d bytes", i_numBytes); 594 | _try { 595 | module = m3_AllocStruct (M3Module); 596 | _throwifnull (module); 597 | module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); 598 | module->startFunction = -1; 599 | //module->hasWasmCodeCopy = false; 600 | module->environment = i_environment; 601 | 602 | const u8 * pos = i_bytes; 603 | const u8 * end = pos + i_numBytes; 604 | 605 | module->wasmStart = pos; 606 | module->wasmEnd = end; 607 | 608 | u32 magic, version; 609 | _ (Read_u32 (& magic, & pos, end)); 610 | _ (Read_u32 (& version, & pos, end)); 611 | 612 | _throwif (m3Err_wasmMalformed, magic != 0x6d736100); 613 | _throwif (m3Err_incompatibleWasmVersion, version != 1); 614 | 615 | static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder 616 | u8 expectedSection = 0; 617 | 618 | while (pos < end) 619 | { 620 | u8 section; 621 | _ (ReadLEB_u7 (& section, & pos, end)); 622 | 623 | if (section != 0) { 624 | // Ensure sections appear only once and in order 625 | while (sectionsOrder[expectedSection++] != section) { 626 | _throwif(m3Err_misorderedWasmSection, expectedSection >= 12); 627 | } 628 | } 629 | 630 | u32 sectionLength; 631 | _ (ReadLEB_u32 (& sectionLength, & pos, end)); 632 | _throwif(m3Err_wasmMalformed, pos + sectionLength > end); 633 | 634 | _ (ParseModuleSection (module, section, pos, sectionLength)); 635 | 636 | pos += sectionLength; 637 | } 638 | 639 | } _catch: 640 | 641 | if (result) 642 | { 643 | m3_FreeModule (module); 644 | module = NULL; 645 | } 646 | 647 | * o_module = module; 648 | 649 | return result; 650 | } 651 | -------------------------------------------------------------------------------- /external/wasm3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Wasm3, high performance WebAssembly interpreter 3 | // 4 | // Copyright © 2019 Steven Massey, Volodymyr Shymanskyy. 5 | // All rights reserved. 6 | // 7 | 8 | #ifndef wasm3_h 9 | #define wasm3_h 10 | 11 | #define M3_VERSION_MAJOR 0 12 | #define M3_VERSION_MINOR 5 13 | #define M3_VERSION_REV 0 14 | #define M3_VERSION "0.5.0" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "wasm3_defs.h" 22 | 23 | // Constants 24 | #define M3_BACKTRACE_TRUNCATED (IM3BacktraceFrame)(SIZE_MAX) 25 | 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | typedef const char * M3Result; 31 | 32 | struct M3Environment; typedef struct M3Environment * IM3Environment; 33 | struct M3Runtime; typedef struct M3Runtime * IM3Runtime; 34 | struct M3Module; typedef struct M3Module * IM3Module; 35 | struct M3Function; typedef struct M3Function * IM3Function; 36 | struct M3Global; typedef struct M3Global * IM3Global; 37 | 38 | typedef struct M3ErrorInfo 39 | { 40 | M3Result result; 41 | 42 | IM3Runtime runtime; 43 | IM3Module module; 44 | IM3Function function; 45 | 46 | const char * file; 47 | uint32_t line; 48 | 49 | const char * message; 50 | } M3ErrorInfo; 51 | 52 | typedef struct M3BacktraceFrame 53 | { 54 | uint32_t moduleOffset; 55 | IM3Function function; 56 | 57 | struct M3BacktraceFrame * next; 58 | } 59 | M3BacktraceFrame, * IM3BacktraceFrame; 60 | 61 | typedef struct M3BacktraceInfo 62 | { 63 | IM3BacktraceFrame frames; 64 | IM3BacktraceFrame lastFrame; // can be M3_BACKTRACE_TRUNCATED 65 | } 66 | M3BacktraceInfo, * IM3BacktraceInfo; 67 | 68 | 69 | typedef enum M3ValueType 70 | { 71 | c_m3Type_none = 0, 72 | c_m3Type_i32 = 1, 73 | c_m3Type_i64 = 2, 74 | c_m3Type_f32 = 3, 75 | c_m3Type_f64 = 4, 76 | 77 | c_m3Type_unknown 78 | } M3ValueType; 79 | 80 | typedef struct M3TaggedValue 81 | { 82 | M3ValueType type; 83 | union M3ValueUnion 84 | { 85 | uint32_t i32; 86 | uint64_t i64; 87 | float f32; 88 | double f64; 89 | } value; 90 | } 91 | M3TaggedValue, * IM3TaggedValue; 92 | 93 | typedef struct M3ImportInfo 94 | { 95 | const char * moduleUtf8; 96 | const char * fieldUtf8; 97 | } 98 | M3ImportInfo, * IM3ImportInfo; 99 | 100 | 101 | typedef struct M3ImportContext 102 | { 103 | void * userdata; 104 | IM3Function function; 105 | } 106 | M3ImportContext, * IM3ImportContext; 107 | 108 | // ------------------------------------------------------------------------------------------------------------------------------- 109 | // error codes 110 | // ------------------------------------------------------------------------------------------------------------------------------- 111 | 112 | # if defined(M3_IMPLEMENT_ERROR_STRINGS) 113 | # if defined(__cplusplus) 114 | # define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL = { STRING }; 115 | # else 116 | # define d_m3ErrorConst(LABEL, STRING) const M3Result m3Err_##LABEL = { STRING }; 117 | # endif 118 | # else 119 | # define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL; 120 | # endif 121 | 122 | // ------------------------------------------------------------------------------------------------------------------------------- 123 | 124 | d_m3ErrorConst (none, NULL) 125 | 126 | // general errors 127 | d_m3ErrorConst (mallocFailed, "memory allocation failed") 128 | 129 | // parse errors 130 | d_m3ErrorConst (incompatibleWasmVersion, "incompatible Wasm binary version") 131 | d_m3ErrorConst (wasmMalformed, "malformed Wasm binary") 132 | d_m3ErrorConst (misorderedWasmSection, "out of order Wasm section") 133 | d_m3ErrorConst (wasmUnderrun, "underrun while parsing Wasm binary") 134 | d_m3ErrorConst (wasmOverrun, "overrun while parsing Wasm binary") 135 | d_m3ErrorConst (wasmMissingInitExpr, "missing init_expr in Wasm binary") 136 | d_m3ErrorConst (lebOverflow, "LEB encoded value overflow") 137 | d_m3ErrorConst (missingUTF8, "invalid length UTF-8 string") 138 | d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing Wasm binary") 139 | d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing Wasm binary") 140 | d_m3ErrorConst (invalidTypeId, "unknown value_type") 141 | d_m3ErrorConst (tooManyMemorySections, "only one memory per module is supported") 142 | d_m3ErrorConst (tooManyArgsRets, "too many arguments or return values") 143 | 144 | // link errors 145 | d_m3ErrorConst (moduleNotLinked, "attempting to use module that is not loaded") 146 | d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes") 147 | d_m3ErrorConst (functionLookupFailed, "function lookup failed") 148 | d_m3ErrorConst (functionImportMissing, "missing imported function") 149 | 150 | d_m3ErrorConst (malformedFunctionSignature, "malformed function signature") 151 | 152 | // compilation errors 153 | d_m3ErrorConst (noCompiler, "no compiler found for opcode") 154 | d_m3ErrorConst (unknownOpcode, "unknown opcode") 155 | d_m3ErrorConst (restrictedOpcode, "restricted opcode") 156 | d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit") 157 | d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack") 158 | d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page") 159 | d_m3ErrorConst (settingImmutableGlobal, "attempting to set an immutable global") 160 | d_m3ErrorConst (typeMismatch, "incorrect type on stack") 161 | d_m3ErrorConst (typeCountMismatch, "incorrect value count on stack") 162 | 163 | // runtime errors 164 | d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") 165 | d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory") 166 | d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module") 167 | d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large") 168 | d_m3ErrorConst (argumentCountMismatch, "argument count mismatch") 169 | d_m3ErrorConst (argumentTypeMismatch, "argument type mismatch") 170 | d_m3ErrorConst (globalLookupFailed, "global lookup failed") 171 | d_m3ErrorConst (globalTypeMismatch, "global type mismatch") 172 | d_m3ErrorConst (globalNotMutable, "global is not mutable") 173 | 174 | // traps 175 | d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access") 176 | d_m3ErrorConst (trapDivisionByZero, "[trap] integer divide by zero") 177 | d_m3ErrorConst (trapIntegerOverflow, "[trap] integer overflow") 178 | d_m3ErrorConst (trapIntegerConversion, "[trap] invalid conversion to integer") 179 | d_m3ErrorConst (trapIndirectCallTypeMismatch, "[trap] indirect call type mismatch") 180 | d_m3ErrorConst (trapTableIndexOutOfRange, "[trap] undefined element") 181 | d_m3ErrorConst (trapTableElementIsNull, "[trap] null table element") 182 | d_m3ErrorConst (trapExit, "[trap] program called exit") 183 | d_m3ErrorConst (trapAbort, "[trap] program called abort") 184 | d_m3ErrorConst (trapUnreachable, "[trap] unreachable executed") 185 | d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") 186 | 187 | 188 | //------------------------------------------------------------------------------------------------------------------------------- 189 | // configuration, can be found in m3_config.h, m3_config_platforms.h, m3_core.h) 190 | //------------------------------------------------------------------------------------------------------------------------------- 191 | 192 | //------------------------------------------------------------------------------------------------------------------------------- 193 | // global environment than can host multiple runtimes 194 | //------------------------------------------------------------------------------------------------------------------------------- 195 | IM3Environment m3_NewEnvironment (void); 196 | 197 | void m3_FreeEnvironment (IM3Environment i_environment); 198 | 199 | typedef M3Result (* M3SectionHandler) (IM3Module i_module, const char* name, const uint8_t * start, const uint8_t * end); 200 | 201 | void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler); 202 | 203 | 204 | //------------------------------------------------------------------------------------------------------------------------------- 205 | // execution context 206 | //------------------------------------------------------------------------------------------------------------------------------- 207 | 208 | IM3Runtime m3_NewRuntime (IM3Environment io_environment, 209 | uint32_t i_stackSizeInBytes, 210 | void * i_userdata); 211 | 212 | void m3_FreeRuntime (IM3Runtime i_runtime); 213 | 214 | // Wasm currently only supports one memory region. i_memoryIndex should be zero. 215 | uint8_t * m3_GetMemory (IM3Runtime i_runtime, 216 | uint32_t * o_memorySizeInBytes, 217 | uint32_t i_memoryIndex); 218 | 219 | // This is used internally by Raw Function helpers 220 | uint32_t m3_GetMemorySize (IM3Runtime i_runtime); 221 | 222 | void * m3_GetUserData (IM3Runtime i_runtime); 223 | 224 | 225 | //------------------------------------------------------------------------------------------------------------------------------- 226 | // modules 227 | //------------------------------------------------------------------------------------------------------------------------------- 228 | 229 | // i_wasmBytes data must be persistent during the lifetime of the module 230 | M3Result m3_ParseModule (IM3Environment i_environment, 231 | IM3Module * o_module, 232 | const uint8_t * const i_wasmBytes, 233 | uint32_t i_numWasmBytes); 234 | 235 | // Only modules not loaded into a M3Runtime need to be freed. A module is considered unloaded if 236 | // a. m3_LoadModule has not yet been called on that module. Or, 237 | // b. m3_LoadModule returned a result. 238 | void m3_FreeModule (IM3Module i_module); 239 | 240 | // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully loaded into the runtime 241 | M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module); 242 | 243 | // Optional, compiles all functions in the module 244 | M3Result m3_CompileModule (IM3Module io_module); 245 | 246 | // Calling m3_RunStart is optional 247 | M3Result m3_RunStart (IM3Module i_module); 248 | 249 | // Arguments and return values are passed in and out through the stack pointer _sp. 250 | // Placeholder return value slots are first and arguments after. So, the first argument is at _sp [numReturns] 251 | // Return values should be written into _sp [0] to _sp [num_returns - 1] 252 | typedef const void * (* M3RawCall) (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem); 253 | 254 | M3Result m3_LinkRawFunction (IM3Module io_module, 255 | const char * const i_moduleName, 256 | const char * const i_functionName, 257 | const char * const i_signature, 258 | M3RawCall i_function); 259 | 260 | M3Result m3_LinkRawFunctionEx (IM3Module io_module, 261 | const char * const i_moduleName, 262 | const char * const i_functionName, 263 | const char * const i_signature, 264 | M3RawCall i_function, 265 | const void * i_userdata); 266 | 267 | const char* m3_GetModuleName (IM3Module i_module); 268 | void m3_SetModuleName (IM3Module i_module, const char* name); 269 | IM3Runtime m3_GetModuleRuntime (IM3Module i_module); 270 | 271 | //------------------------------------------------------------------------------------------------------------------------------- 272 | // globals 273 | //------------------------------------------------------------------------------------------------------------------------------- 274 | IM3Global m3_FindGlobal (IM3Module io_module, 275 | const char * const i_globalName); 276 | 277 | M3Result m3_GetGlobal (IM3Global i_global, 278 | IM3TaggedValue o_value); 279 | 280 | M3Result m3_SetGlobal (IM3Global i_global, 281 | const IM3TaggedValue i_value); 282 | 283 | M3ValueType m3_GetGlobalType (IM3Global i_global); 284 | 285 | //------------------------------------------------------------------------------------------------------------------------------- 286 | // functions 287 | //------------------------------------------------------------------------------------------------------------------------------- 288 | M3Result m3_Yield (void); 289 | 290 | // o_function is valid during the lifetime of the originating runtime 291 | M3Result m3_FindFunction (IM3Function * o_function, 292 | IM3Runtime i_runtime, 293 | const char * const i_functionName); 294 | 295 | uint32_t m3_GetArgCount (IM3Function i_function); 296 | uint32_t m3_GetRetCount (IM3Function i_function); 297 | M3ValueType m3_GetArgType (IM3Function i_function, uint32_t i_index); 298 | M3ValueType m3_GetRetType (IM3Function i_function, uint32_t i_index); 299 | 300 | M3Result m3_CallV (IM3Function i_function, ...); 301 | M3Result m3_CallVL (IM3Function i_function, va_list i_args); 302 | M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]); 303 | M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]); 304 | 305 | M3Result m3_GetResultsV (IM3Function i_function, ...); 306 | M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets); 307 | M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]); 308 | 309 | 310 | void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); 311 | void m3_ResetErrorInfo (IM3Runtime i_runtime); 312 | 313 | const char* m3_GetFunctionName (IM3Function i_function); 314 | IM3Module m3_GetFunctionModule (IM3Function i_function); 315 | 316 | //------------------------------------------------------------------------------------------------------------------------------- 317 | // debug info 318 | //------------------------------------------------------------------------------------------------------------------------------- 319 | 320 | void m3_PrintRuntimeInfo (IM3Runtime i_runtime); 321 | void m3_PrintM3Info (void); 322 | void m3_PrintProfilerInfo (void); 323 | 324 | // The runtime owns the backtrace, do not free the backtrace you obtain. Returns NULL if there's no backtrace. 325 | IM3BacktraceInfo m3_GetBacktrace (IM3Runtime i_runtime); 326 | 327 | //------------------------------------------------------------------------------------------------------------------------------- 328 | // raw function definition helpers 329 | //------------------------------------------------------------------------------------------------------------------------------- 330 | 331 | # define m3ApiOffsetToPtr(offset) (void*)((uint8_t*)_mem + (uint32_t)(offset)) 332 | # define m3ApiPtrToOffset(ptr) (uint32_t)((uint8_t*)ptr - (uint8_t*)_mem) 333 | 334 | # define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp++)); 335 | # define m3ApiMultiValueReturnType(TYPE, NAME) TYPE* NAME = ((TYPE*) (_sp++)); 336 | # define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); 337 | # define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((uint32_t *) (_sp++))); 338 | 339 | # define m3ApiIsNullPtr(addr) ((void*)(addr) <= _mem) 340 | # define m3ApiCheckMem(addr, len) { if (M3_UNLIKELY(((void*)(addr) < _mem) || ((uint64_t)(uintptr_t)(addr) + (len)) > ((uint64_t)(uintptr_t)(_mem)+m3_GetMemorySize(runtime)))) m3ApiTrap(m3Err_trapOutOfBoundsMemoryAccess); } 341 | 342 | # define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) 343 | # define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none;} 344 | # define m3ApiMultiValueReturn(NAME, VALUE) { *NAME = (VALUE); } 345 | # define m3ApiTrap(VALUE) { return VALUE; } 346 | # define m3ApiSuccess() { return m3Err_none; } 347 | 348 | # if defined(M3_BIG_ENDIAN) 349 | # define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) 350 | # define m3ApiReadMem16(ptr) m3_bswap16((* (uint16_t *)(ptr))) 351 | # define m3ApiReadMem32(ptr) m3_bswap32((* (uint32_t *)(ptr))) 352 | # define m3ApiReadMem64(ptr) m3_bswap64((* (uint64_t *)(ptr))) 353 | # define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } 354 | # define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = m3_bswap16((val)); } 355 | # define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = m3_bswap32((val)); } 356 | # define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = m3_bswap64((val)); } 357 | # else 358 | # define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) 359 | # define m3ApiReadMem16(ptr) (* (uint16_t *)(ptr)) 360 | # define m3ApiReadMem32(ptr) (* (uint32_t *)(ptr)) 361 | # define m3ApiReadMem64(ptr) (* (uint64_t *)(ptr)) 362 | # define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } 363 | # define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = (val); } 364 | # define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = (val); } 365 | # define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = (val); } 366 | # endif 367 | 368 | #if defined(__cplusplus) 369 | } 370 | #endif 371 | 372 | #endif // wasm3_h 373 | -------------------------------------------------------------------------------- /external/wasm3_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // wasm3_defs.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef wasm3_defs_h 9 | #define wasm3_defs_h 10 | 11 | #define M3_STR__(x) #x 12 | #define M3_STR(x) M3_STR__(x) 13 | 14 | #define M3_CONCAT__(a,b) a##b 15 | #define M3_CONCAT(a,b) M3_CONCAT__(a,b) 16 | 17 | /* 18 | * Detect compiler 19 | */ 20 | 21 | # if defined(__clang__) 22 | # define M3_COMPILER_CLANG 1 23 | # elif defined(__INTEL_COMPILER) 24 | # define M3_COMPILER_ICC 1 25 | # elif defined(__GNUC__) || defined(__GNUG__) 26 | # define M3_COMPILER_GCC 1 27 | # elif defined(_MSC_VER) 28 | # define M3_COMPILER_MSVC 1 29 | # else 30 | # warning "Compiler not detected" 31 | # endif 32 | 33 | # if defined(M3_COMPILER_CLANG) 34 | # if defined(WIN32) 35 | # define M3_COMPILER_VER __VERSION__ " for Windows" 36 | # else 37 | # define M3_COMPILER_VER __VERSION__ 38 | # endif 39 | # elif defined(M3_COMPILER_GCC) 40 | # define M3_COMPILER_VER "GCC " __VERSION__ 41 | # elif defined(M3_COMPILER_ICC) 42 | # define M3_COMPILER_VER __VERSION__ 43 | # elif defined(M3_COMPILER_MSVC) 44 | # define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) 45 | # else 46 | # define M3_COMPILER_VER "unknown" 47 | # endif 48 | 49 | # ifdef __has_feature 50 | # define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) 51 | # else 52 | # define M3_COMPILER_HAS_FEATURE(x) 0 53 | # endif 54 | 55 | # ifdef __has_builtin 56 | # define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) 57 | # else 58 | # define M3_COMPILER_HAS_BUILTIN(x) 0 59 | # endif 60 | 61 | # ifdef __has_attribute 62 | # define M3_COMPILER_HAS_ATTRIBUTE(x) __has_attribute(x) 63 | # else 64 | # define M3_COMPILER_HAS_ATTRIBUTE(x) 0 65 | # endif 66 | 67 | /* 68 | * Detect endianness 69 | */ 70 | 71 | # if defined(M3_COMPILER_MSVC) 72 | # define M3_LITTLE_ENDIAN 73 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 74 | # define M3_LITTLE_ENDIAN 75 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 76 | # define M3_BIG_ENDIAN 77 | # else 78 | # error "Byte order not detected" 79 | # endif 80 | 81 | /* 82 | * Detect platform 83 | */ 84 | 85 | # if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) || defined(M3_COMPILER_ICC) 86 | # if defined(__wasm__) 87 | # define M3_ARCH "wasm" 88 | 89 | # elif defined(__x86_64__) 90 | # define M3_ARCH "x86_64" 91 | 92 | # elif defined(__i386__) 93 | # define M3_ARCH "i386" 94 | 95 | # elif defined(__aarch64__) 96 | # define M3_ARCH "arm64-v8a" 97 | 98 | # elif defined(__arm__) 99 | # if defined(__ARM_ARCH_7A__) 100 | # if defined(__ARM_NEON__) 101 | # if defined(__ARM_PCS_VFP) 102 | # define M3_ARCH "arm-v7a/NEON hard-float" 103 | # else 104 | # define M3_ARCH "arm-v7a/NEON" 105 | # endif 106 | # else 107 | # if defined(__ARM_PCS_VFP) 108 | # define M3_ARCH "arm-v7a hard-float" 109 | # else 110 | # define M3_ARCH "arm-v7a" 111 | # endif 112 | # endif 113 | # else 114 | # define M3_ARCH "arm" 115 | # endif 116 | 117 | # elif defined(__riscv) 118 | # if defined(__riscv_32e) 119 | # define _M3_ARCH_RV "rv32e" 120 | # elif __riscv_xlen == 128 121 | # define _M3_ARCH_RV "rv128i" 122 | # elif __riscv_xlen == 64 123 | # define _M3_ARCH_RV "rv64i" 124 | # elif __riscv_xlen == 32 125 | # define _M3_ARCH_RV "rv32i" 126 | # endif 127 | # if defined(__riscv_muldiv) 128 | # define _M3_ARCH_RV_M _M3_ARCH_RV "m" 129 | # else 130 | # define _M3_ARCH_RV_M _M3_ARCH_RV 131 | # endif 132 | # if defined(__riscv_atomic) 133 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" 134 | # else 135 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M 136 | # endif 137 | # if defined(__riscv_flen) 138 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" 139 | # else 140 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A 141 | # endif 142 | # if defined(__riscv_flen) && __riscv_flen >= 64 143 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" 144 | # else 145 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F 146 | # endif 147 | # if defined(__riscv_compressed) 148 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" 149 | # else 150 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D 151 | # endif 152 | # define M3_ARCH _M3_ARCH_RV_C 153 | 154 | # elif defined(__mips__) 155 | # if defined(__MIPSEB__) && defined(__mips64) 156 | # define M3_ARCH "mips64 " _MIPS_ARCH 157 | # elif defined(__MIPSEL__) && defined(__mips64) 158 | # define M3_ARCH "mips64el " _MIPS_ARCH 159 | # elif defined(__MIPSEB__) 160 | # define M3_ARCH "mips " _MIPS_ARCH 161 | # elif defined(__MIPSEL__) 162 | # define M3_ARCH "mipsel " _MIPS_ARCH 163 | # endif 164 | 165 | # elif defined(__PPC__) 166 | # if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) 167 | # define M3_ARCH "ppc64le" 168 | # elif defined(__PPC64__) 169 | # define M3_ARCH "ppc64" 170 | # else 171 | # define M3_ARCH "ppc" 172 | # endif 173 | 174 | # elif defined(__sparc__) 175 | # if defined(__arch64__) 176 | # define M3_ARCH "sparc64" 177 | # else 178 | # define M3_ARCH "sparc" 179 | # endif 180 | 181 | # elif defined(__s390x__) 182 | # define M3_ARCH "s390x" 183 | 184 | # elif defined(__alpha__) 185 | # define M3_ARCH "alpha" 186 | 187 | # elif defined(__m68k__) 188 | # define M3_ARCH "m68k" 189 | 190 | # elif defined(__xtensa__) 191 | # define M3_ARCH "xtensa" 192 | 193 | # elif defined(__arc__) 194 | # define M3_ARCH "arc32" 195 | 196 | # elif defined(__AVR__) 197 | # define M3_ARCH "avr" 198 | # endif 199 | # endif 200 | 201 | # if defined(M3_COMPILER_MSVC) 202 | # if defined(_M_X64) 203 | # define M3_ARCH "x86_64" 204 | # elif defined(_M_IX86) 205 | # define M3_ARCH "i386" 206 | # elif defined(_M_ARM64) 207 | # define M3_ARCH "arm64" 208 | # elif defined(_M_ARM) 209 | # define M3_ARCH "arm" 210 | # endif 211 | # endif 212 | 213 | # if !defined(M3_ARCH) 214 | # warning "Architecture not detected" 215 | # define M3_ARCH "unknown" 216 | # endif 217 | 218 | /* 219 | * Byte swapping (for Big-Endian systems only) 220 | */ 221 | 222 | # if defined(M3_COMPILER_MSVC) 223 | # define m3_bswap16(x) _byteswap_ushort((x)) 224 | # define m3_bswap32(x) _byteswap_ulong((x)) 225 | # define m3_bswap64(x) _byteswap_uint64((x)) 226 | # elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 227 | // __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 228 | # define m3_bswap16(x) __builtin_bswap16((x)) 229 | # define m3_bswap32(x) __builtin_bswap32((x)) 230 | # define m3_bswap64(x) __builtin_bswap64((x)) 231 | # elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) 232 | # define m3_bswap16(x) __builtin_bswap16((x)) 233 | # define m3_bswap32(x) __builtin_bswap32((x)) 234 | # define m3_bswap64(x) __builtin_bswap64((x)) 235 | # elif defined(M3_COMPILER_ICC) 236 | # define m3_bswap16(x) __builtin_bswap16((x)) 237 | # define m3_bswap32(x) __builtin_bswap32((x)) 238 | # define m3_bswap64(x) __builtin_bswap64((x)) 239 | # else 240 | # ifdef __linux__ 241 | # include 242 | # else 243 | # include 244 | # endif 245 | # if defined(__bswap_16) 246 | # define m3_bswap16(x) __bswap_16((x)) 247 | # define m3_bswap32(x) __bswap_32((x)) 248 | # define m3_bswap64(x) __bswap_64((x)) 249 | # else 250 | # warning "Using naive (probably slow) bswap operations" 251 | static inline 252 | uint16_t m3_bswap16(uint16_t x) { 253 | return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); 254 | } 255 | static inline 256 | uint32_t m3_bswap32(uint32_t x) { 257 | return ((( x & 0xff000000u ) >> 24 ) | 258 | (( x & 0x00ff0000u ) >> 8 ) | 259 | (( x & 0x0000ff00u ) << 8 ) | 260 | (( x & 0x000000ffu ) << 24 )); 261 | } 262 | static inline 263 | uint64_t m3_bswap64(uint64_t x) { 264 | return ((( x & 0xff00000000000000ull ) >> 56 ) | 265 | (( x & 0x00ff000000000000ull ) >> 40 ) | 266 | (( x & 0x0000ff0000000000ull ) >> 24 ) | 267 | (( x & 0x000000ff00000000ull ) >> 8 ) | 268 | (( x & 0x00000000ff000000ull ) << 8 ) | 269 | (( x & 0x0000000000ff0000ull ) << 24 ) | 270 | (( x & 0x000000000000ff00ull ) << 40 ) | 271 | (( x & 0x00000000000000ffull ) << 56 )); 272 | } 273 | # endif 274 | # endif 275 | 276 | /* 277 | * Bit ops 278 | */ 279 | #define m3_isBitSet(val, pos) ((val & (1 << pos)) != 0) 280 | 281 | /* 282 | * Other 283 | */ 284 | 285 | # if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) 286 | # define M3_UNLIKELY(x) __builtin_expect(!!(x), 0) 287 | # define M3_LIKELY(x) __builtin_expect(!!(x), 1) 288 | # else 289 | # define M3_UNLIKELY(x) (x) 290 | # define M3_LIKELY(x) (x) 291 | # endif 292 | 293 | #endif // wasm3_defs_h 294 | -------------------------------------------------------------------------------- /genie.lua: -------------------------------------------------------------------------------- 1 | project "visualscript" 2 | libType() 3 | files { 4 | "src/**.c", 5 | "src/**.cpp", 6 | "src/**.h", 7 | "external/**.c", 8 | "external/**.h", 9 | "genie.lua" 10 | } 11 | defines { "BUILDING_VISUALSCRIPT" } 12 | links { "engine", "core" } 13 | if build_studio then 14 | links { "editor" } 15 | end 16 | defaultConfigurations() 17 | 18 | linkPlugin("visualscript") -------------------------------------------------------------------------------- /src/m3_lumix.c: -------------------------------------------------------------------------------- 1 | #include "m3_lumix.h" 2 | #include "../external/m3_env.h" 3 | 4 | int m3l_getGlobalCount(IM3Module module) { 5 | return module->numGlobals; 6 | } 7 | 8 | const char* m3l_getGlobalName(IM3Module module, int idx) { 9 | return module->globals[idx].name; 10 | } 11 | -------------------------------------------------------------------------------- /src/m3_lumix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../external/wasm3.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | int m3l_getGlobalCount(IM3Module module); 10 | const char* m3l_getGlobalName(IM3Module module, int idx); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif -------------------------------------------------------------------------------- /src/script.cpp: -------------------------------------------------------------------------------- 1 | #include "core/hash_map.h" 2 | #include "core/log.h" 3 | #include "core/profiler.h" 4 | #include "core/stream.h" 5 | #include "engine/engine.h" 6 | #include "engine/input_system.h" 7 | #include "engine/plugin.h" 8 | #include "engine/reflection.h" 9 | #include "engine/resource.h" 10 | #include "engine/resource_manager.h" 11 | #include "engine/world.h" 12 | #include "script.h" 13 | #include "../external/wasm3.h" 14 | 15 | namespace Lumix { 16 | 17 | ResourceType ScriptResource::TYPE("script"); 18 | static const ComponentType SCRIPT_TYPE = reflection::getComponentType("script"); 19 | 20 | void ScriptResource::unload() { 21 | m_bytecode.clear(); 22 | } 23 | 24 | ScriptResource::ScriptResource(const Path& path, ResourceManager& resource_manager, IAllocator& allocator) 25 | : Resource(path, resource_manager, allocator) 26 | , m_bytecode(allocator) 27 | , m_allocator(allocator) 28 | {} 29 | 30 | bool ScriptResource::load(Span mem) { 31 | InputMemoryStream blob(mem); 32 | Header header; 33 | blob.read(header); 34 | if (header.magic != Header::MAGIC) return false; 35 | if (header.version > Version::LAST) return false; 36 | 37 | u32 bytecode_size = u32(blob.remaining()); 38 | m_bytecode.resize(bytecode_size); 39 | blob.read(m_bytecode.getMutableData(), bytecode_size); 40 | return true; 41 | } 42 | 43 | Script::Script(Script&& script) 44 | { 45 | m_runtime = script.m_runtime; 46 | m_module = script.m_module; 47 | m_resource = script.m_resource; 48 | m_init_failed = script.m_init_failed; 49 | 50 | script.m_resource = nullptr; 51 | script.m_runtime = nullptr; 52 | script.m_module = nullptr; 53 | } 54 | 55 | Script::~Script() { 56 | if (m_resource) m_resource->decRefCount(); 57 | ASSERT(!m_runtime); 58 | ASSERT(!m_module); 59 | } 60 | 61 | struct ScriptModuleImpl : ScriptModule { 62 | ScriptModuleImpl(ISystem& system, Engine& engine, World& world, IAllocator& allocator) 63 | : m_system(system) 64 | , m_world(world) 65 | , m_engine(engine) 66 | , m_allocator(allocator) 67 | , m_scripts(allocator) 68 | , m_mouse_move_scripts(allocator) 69 | , m_key_input_scripts(allocator) 70 | {} 71 | 72 | const char* getName() const override { return "script"; } 73 | 74 | void tryCall(EntityRef entity, const char* function_name, ...) { 75 | Script& scr = m_scripts[entity]; 76 | va_list ap; 77 | va_start(ap, function_name); 78 | IM3Function fn; 79 | M3Result find_res = m3_FindFunction(&fn, scr.m_runtime, function_name); 80 | if (find_res == m3Err_none) { 81 | PROFILE_BLOCK("tryCall"); 82 | m3_CallVL(fn, ap); 83 | } 84 | else if (find_res != m3Err_functionLookupFailed) { 85 | logError(scr.m_resource->getPath(), ": ", find_res); 86 | } 87 | va_end(ap); 88 | } 89 | 90 | void serialize(OutputMemoryStream& blob) override { 91 | blob.write(m_scripts.size()); 92 | for (auto iter = m_scripts.begin(), end = m_scripts.end(); iter != end; ++iter) { 93 | blob.write(iter.key()); 94 | ScriptResource* res = iter.value().m_resource; 95 | blob.writeString(res ? res->getPath().c_str() : ""); 96 | } 97 | } 98 | 99 | void deserialize(InputMemoryStream& blob, const EntityMap& entity_map, i32 version) override { 100 | u32 count; 101 | blob.read(count); 102 | ResourceManagerHub& rm = m_engine.getResourceManager(); 103 | for (u32 i = 0; i < count; ++i) { 104 | EntityRef e; 105 | blob.read(e); 106 | e = entity_map.get(e); 107 | const char* path = blob.readString(); 108 | Script script; 109 | script.m_resource = path[0] ? rm.load(Path(path)) : nullptr; 110 | m_scripts.insert(e, static_cast(script)); 111 | m_world.onComponentCreated(e, SCRIPT_TYPE, this); 112 | } 113 | } 114 | 115 | ISystem& getSystem() const override { return m_system; } 116 | World& getWorld() override { return m_world; } 117 | 118 | void stopGame() override { 119 | m_is_game_running = false; 120 | m_mouse_move_scripts.clear(); 121 | m_key_input_scripts.clear(); 122 | } 123 | 124 | void startGame() override { 125 | m_is_game_running = true; 126 | m_environment = m3_NewEnvironment(); 127 | } 128 | 129 | void onKeyEvent(const InputSystem::Event& event) { 130 | for (EntityRef e : m_key_input_scripts) { 131 | tryCall(e, "onKeyEvent", event.data.button.key_id); 132 | } 133 | } 134 | 135 | void onMouseMove(const InputSystem::Event& event) { 136 | for (EntityRef e : m_mouse_move_scripts) { 137 | tryCall(e, "onMouseMove", event.data.axis.x, event.data.axis.y); 138 | } 139 | } 140 | 141 | static m3ApiRawFunction(API_getPropertyFloat) { 142 | m3ApiReturnType(float); 143 | ScriptModuleImpl* module = (ScriptModuleImpl*)m3_GetUserData(runtime); 144 | World& world = module->getWorld(); 145 | m3ApiGetArg(EntityRef, entity); 146 | m3ApiGetArg(StableHash, property_hash); 147 | const reflection::PropertyBase* prop = reflection::getPropertyFromHash(property_hash); 148 | if (!prop) { 149 | logError("Property (hash = ", property_hash.getHashValue(), ") not found"); 150 | return m3Err_none; 151 | } 152 | const reflection::Property* fprop = static_cast*>(prop); 153 | ComponentUID cmp; 154 | cmp.entity = entity; 155 | cmp.module = world.getModule(prop->cmp->component_type); 156 | ASSERT(cmp.module); 157 | const float value = fprop->get(cmp, -1); 158 | ASSERT(false); // TODO check if this function is correct 159 | m3ApiReturn(value); 160 | } 161 | 162 | static m3ApiRawFunction(API_setPropertyFloat) { 163 | ScriptModuleImpl* module = (ScriptModuleImpl*)m3_GetUserData(runtime); 164 | World& world = module->getWorld(); 165 | m3ApiGetArg(EntityRef, entity); 166 | m3ApiGetArg(StableHash, property_hash); 167 | m3ApiGetArg(float, value); 168 | const reflection::PropertyBase* prop = reflection::getPropertyFromHash(property_hash); 169 | if (!prop) { 170 | logError("Property (hash = ", property_hash.getHashValue(), ") not found"); 171 | return m3Err_none; 172 | } 173 | const reflection::Property* fprop = static_cast*>(prop); 174 | ComponentUID cmp; 175 | cmp.entity = entity; 176 | cmp.module = world.getModule(prop->cmp->component_type); 177 | ASSERT(cmp.module); 178 | fprop->set(cmp, -1, value); 179 | return m3Err_none; 180 | } 181 | 182 | static m3ApiRawFunction(API_setYaw) { 183 | ScriptModuleImpl* module = (ScriptModuleImpl*)m3_GetUserData(runtime); 184 | World& world = module->getWorld(); 185 | m3ApiGetArg(EntityRef, entity); 186 | m3ApiGetArg(float, yaw); 187 | Quat rot(Vec3(0, 1, 0), yaw); 188 | world.setRotation(entity, rot); 189 | return m3Err_none; 190 | } 191 | 192 | void processEvents() { 193 | InputSystem& input = m_engine.getInputSystem(); 194 | Span events = input.getEvents(); 195 | for (const InputSystem::Event& e : events) { 196 | switch(e.type) { 197 | case InputSystem::Event::BUTTON: 198 | if (e.device->type == InputSystem::Device::KEYBOARD) { 199 | onKeyEvent(e); 200 | } 201 | break; 202 | case InputSystem::Event::AXIS: 203 | if (e.device->type == InputSystem::Device::MOUSE) { 204 | onMouseMove(e); 205 | } 206 | break; 207 | default: break; 208 | } 209 | } 210 | } 211 | 212 | void update(float time_delta) override { 213 | PROFILE_FUNCTION(); 214 | if (!m_is_game_running) return; 215 | 216 | processEvents(); 217 | 218 | for (auto iter = m_scripts.begin(), end = m_scripts.end(); iter != end; ++iter) { 219 | Script& script = iter.value(); 220 | if (script.m_init_failed) continue; 221 | if (!script.m_resource) continue; 222 | if (!script.m_resource->isReady()) continue; 223 | 224 | bool start = false; 225 | if (!script.m_runtime) { 226 | script.m_runtime = m3_NewRuntime(m_environment, 32 * 1024, this); 227 | // TODO optimize - do not parse for each instance 228 | auto onError = [&](const char* msg){ 229 | logError(script.m_resource->getPath(), ": ", msg); 230 | script.m_init_failed = true; 231 | m3_FreeRuntime(script.m_runtime); 232 | script.m_module = nullptr; 233 | script.m_runtime = nullptr; 234 | }; 235 | const M3Result parse_res = m3_ParseModule(m_environment, &script.m_module, script.m_resource->m_bytecode.data(), (u32)script.m_resource->m_bytecode.size()); 236 | if (parse_res != m3Err_none) { 237 | onError(parse_res); 238 | continue; 239 | } 240 | const M3Result load_res = m3_LoadModule(script.m_runtime, script.m_module); 241 | if (load_res != m3Err_none) { 242 | onError(load_res); 243 | continue; 244 | } 245 | 246 | #define LINK(F) \ 247 | { \ 248 | const M3Result link_res = m3_LinkRawFunction(script.m_module, "LumixAPI", #F, nullptr, &ScriptModuleImpl::API_##F); \ 249 | if (link_res != m3Err_none && link_res != m3Err_functionLookupFailed) { \ 250 | onError(link_res); \ 251 | continue; \ 252 | } \ 253 | } 254 | 255 | LINK(setYaw); 256 | LINK(setPropertyFloat); 257 | LINK(getPropertyFloat); 258 | 259 | #undef LINK 260 | 261 | IM3Global self_global = m3_FindGlobal(script.m_module, "self"); 262 | if (!self_global) { 263 | onError("`self` not found"); 264 | continue; 265 | } 266 | 267 | M3TaggedValue self_value; 268 | self_value.type = c_m3Type_i32; 269 | self_value.value.i32 = iter.key().index; 270 | M3Result set_self_res = m3_SetGlobal(self_global, &self_value); 271 | if (set_self_res != m3Err_none) { 272 | onError(set_self_res); 273 | continue; 274 | } 275 | 276 | IM3Function tmp_fn; 277 | if (m3_FindFunction(&tmp_fn, script.m_runtime, "onMouseMove") == m3Err_none) { 278 | m_mouse_move_scripts.push(iter.key()); 279 | } 280 | if (m3_FindFunction(&tmp_fn, script.m_runtime, "onKeyEvent") == m3Err_none) { 281 | m_key_input_scripts.push(iter.key()); 282 | } 283 | M3Result find_start_res = m3_FindFunction(&tmp_fn, script.m_runtime, "start"); 284 | if (find_start_res == m3Err_none) { 285 | m3_CallV(tmp_fn); 286 | } 287 | } 288 | 289 | IM3Function update_fn; 290 | M3Result find_update_res = m3_FindFunction(&update_fn, script.m_runtime, "update"); 291 | if (find_update_res == m3Err_none) { 292 | m3_CallV(update_fn, time_delta); 293 | } 294 | else if (find_update_res != m3Err_functionLookupFailed) { 295 | logError(script.m_resource->getPath(), ": ", find_update_res); 296 | script.m_init_failed = true; 297 | } 298 | } 299 | } 300 | 301 | void destroyScript(EntityRef entity) { 302 | m_mouse_move_scripts.eraseItem(entity); 303 | m_key_input_scripts.eraseItem(entity); 304 | m_scripts.erase(entity); 305 | m_world.onComponentDestroyed(entity, SCRIPT_TYPE, this); 306 | } 307 | 308 | void createScript(EntityRef entity) { 309 | m_scripts.insert(entity); 310 | m_world.onComponentCreated(entity, SCRIPT_TYPE, this); 311 | } 312 | 313 | Script& getScript(EntityRef entity) override { 314 | return m_scripts[entity]; 315 | } 316 | 317 | void setScriptResource(EntityRef entity, const Path& path) { 318 | Script& script = m_scripts[entity]; 319 | if (script.m_resource) script.m_resource->decRefCount(); 320 | if (path.isEmpty()) { 321 | script.m_resource = nullptr; 322 | return; 323 | } 324 | script.m_resource = m_engine.getResourceManager().load(path); 325 | } 326 | 327 | Path getScriptResource(EntityRef entity) { 328 | Script& script = m_scripts[entity]; 329 | ScriptResource* res = script.m_resource; 330 | return res ? res->getPath() : Path(); 331 | } 332 | 333 | IAllocator& m_allocator; 334 | Engine& m_engine; 335 | ISystem& m_system; 336 | World& m_world; 337 | HashMap m_scripts; 338 | Array m_mouse_move_scripts; 339 | Array m_key_input_scripts; 340 | bool m_is_game_running = false; 341 | IM3Environment m_environment = nullptr; 342 | }; 343 | 344 | struct ScriptManager : ResourceManager { 345 | ScriptManager(IAllocator& allocator) 346 | : ResourceManager(allocator) 347 | {} 348 | 349 | Resource* createResource(const Path& path) override { 350 | return LUMIX_NEW(m_allocator, ScriptResource)(path, *this, m_allocator); 351 | } 352 | 353 | void destroyResource(Resource& resource) override { 354 | LUMIX_DELETE(m_allocator, &resource); 355 | } 356 | }; 357 | 358 | struct VisualScriptPlugin : ISystem { 359 | VisualScriptPlugin(Engine& engine) 360 | : m_engine(engine) 361 | , m_allocator(engine.getAllocator()) 362 | , m_script_manager(engine.getAllocator()) 363 | { 364 | OutputMemoryStream wasm_bin(engine.getAllocator()); 365 | (void)engine.getFileSystem().getContentSync(Path("test.wasm"), wasm_bin); 366 | 367 | m_script_manager.create(ScriptResource::TYPE, engine.getResourceManager()); 368 | 369 | LUMIX_MODULE(ScriptModuleImpl, "script") 370 | .LUMIX_CMP(Script, "script", "Script") 371 | .LUMIX_PROP(ScriptResource, "Script").resourceAttribute(ScriptResource::TYPE); 372 | } 373 | 374 | const char* getName() const override { return "script"; } 375 | void serialize(OutputMemoryStream& serializer) const override {} 376 | bool deserialize(i32 version, InputMemoryStream& serializer) override { return version == 0; } 377 | 378 | void createModules(World& world) override { 379 | UniquePtr module = UniquePtr::create(m_allocator, *this, m_engine, world, m_allocator); 380 | world.addModule(module.move()); 381 | } 382 | 383 | IAllocator& m_allocator; 384 | Engine& m_engine; 385 | ScriptManager m_script_manager; 386 | }; 387 | 388 | LUMIX_PLUGIN_ENTRY(visualscript) { 389 | PROFILE_FUNCTION(); 390 | return LUMIX_NEW(engine.getAllocator(), VisualScriptPlugin)(engine); 391 | } 392 | 393 | } // namespace Lumix 394 | 395 | -------------------------------------------------------------------------------- /src/script.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "engine/plugin.h" 4 | #include "../external/wasm3.h" 5 | 6 | namespace Lumix { 7 | 8 | enum class ScriptValueType : u32 { 9 | U32_DEPRECATED, 10 | I32, 11 | FLOAT, 12 | ENTITY 13 | }; 14 | 15 | struct ScriptResource : Resource { 16 | static ResourceType TYPE; 17 | 18 | enum class Version : u32 { 19 | 20 | LAST 21 | }; 22 | 23 | struct Header { 24 | static const u32 MAGIC = '_scr'; 25 | 26 | u32 magic = MAGIC; 27 | Version version = Version::LAST; 28 | }; 29 | 30 | ScriptResource(const Path& path, ResourceManager& resource_manager, IAllocator& allocator); 31 | 32 | ResourceType getType() const override { return TYPE; } 33 | void unload() override; 34 | bool load(Span mem) override; 35 | 36 | IAllocator& m_allocator; 37 | OutputMemoryStream m_bytecode; 38 | }; 39 | 40 | struct Script { 41 | Script() {} 42 | Script(Script&& script); 43 | ~Script(); 44 | 45 | bool m_init_failed = false; 46 | IM3Runtime m_runtime = nullptr; 47 | IM3Module m_module = nullptr; 48 | ScriptResource* m_resource = nullptr; 49 | }; 50 | 51 | struct ScriptModule : IModule { 52 | virtual Script& getScript(EntityRef entity) = 0; 53 | }; 54 | 55 | 56 | } // namespace Lumix --------------------------------------------------------------------------------