| author | |
| committer | |
| log | ce3f0077cf3c5ae8edc116177f2f3d33258be9f7 |
| tree | a8cc4c5d9721c90b4ccd0304049551952f662dfd |
| parent | 12051b02f1f455b85d5a519dd1747a67d4bb68d0 |
This will allow the developer to poll the runtime for currently
allocated memory in the number of Wasm pages. Typical usage:
```zig
var wasm_pages = @wasmMemorySize();
@import("std").debug.assert(wasm_pages > 0);
```4 files changed, 102 insertions(+), 0 deletions(-)
src/all_types.hpp+12| ... | ... | @@ -1825,6 +1825,7 @@ enum BuiltinFnId { |
| 1825 | 1825 | BuiltinFnIdAs, |
| 1826 | 1826 | BuiltinFnIdCall, |
| 1827 | 1827 | BuiltinFnIdBitSizeof, |
| 1828 | BuiltinFnIdWasmMemorySize, | |
| 1828 | 1829 | }; |
| 1829 | 1830 | |
| 1830 | 1831 | struct BuiltinFnEntry { |
| ... | ... | @@ -2075,6 +2076,7 @@ struct CodeGen { |
| 2075 | 2076 | LLVMValueRef err_name_table; |
| 2076 | 2077 | LLVMValueRef safety_crash_err_fn; |
| 2077 | 2078 | LLVMValueRef return_err_fn; |
| 2079 | LLVMValueRef wasm_memory_size; | |
| 2078 | 2080 | LLVMTypeRef anyframe_fn_type; |
| 2079 | 2081 | |
| 2080 | 2082 | // reminder: hash tables must be initialized before use |
| ... | ... | @@ -2748,6 +2750,7 @@ enum IrInstSrcId { |
| 2748 | 2750 | IrInstSrcIdResume, |
| 2749 | 2751 | IrInstSrcIdSpillBegin, |
| 2750 | 2752 | IrInstSrcIdSpillEnd, |
| 2753 | IrInstSrcIdWasmMemorySize, | |
| 2751 | 2754 | }; |
| 2752 | 2755 | |
| 2753 | 2756 | // ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. |
| ... | ... | @@ -2840,6 +2843,7 @@ enum IrInstGenId { |
| 2840 | 2843 | IrInstGenIdVectorExtractElem, |
| 2841 | 2844 | IrInstGenIdAlloca, |
| 2842 | 2845 | IrInstGenIdConst, |
| 2846 | IrInstGenIdWasmMemorySize, | |
| 2843 | 2847 | }; |
| 2844 | 2848 | |
| 2845 | 2849 | // Common fields between IrInstSrc and IrInstGen. This allows future passes |
| ... | ... | @@ -3727,6 +3731,14 @@ struct IrInstGenMemcpy { |
| 3727 | 3731 | IrInstGen *count; |
| 3728 | 3732 | }; |
| 3729 | 3733 | |
| 3734 | struct IrInstSrcWasmMemorySize { | |
| 3735 | IrInstSrc base; | |
| 3736 | }; | |
| 3737 | ||
| 3738 | struct IrInstGenWasmMemorySize { | |
| 3739 | IrInstGen base; | |
| 3740 | }; | |
| 3741 | ||
| 3730 | 3742 | struct IrInstSrcSlice { |
| 3731 | 3743 | IrInstSrc base; |
| 3732 | 3744 |
src/codegen.cpp+27| ... | ... | @@ -1045,6 +1045,19 @@ static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstGen *source_instr |
| 1045 | 1045 | return gen_assertion_scope(g, msg_id, source_instruction->base.scope); |
| 1046 | 1046 | } |
| 1047 | 1047 | |
| 1048 | static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { | |
| 1049 | if (g->wasm_memory_size) | |
| 1050 | return g->wasm_memory_size; | |
| 1051 | ||
| 1052 | // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly | |
| 1053 | LLVMTypeRef param_type = LLVMInt32Type(); | |
| 1054 | LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), &param_type, 1, false); | |
| 1055 | g->wasm_memory_size = LLVMAddFunction(g->module, "llvm.wasm.memory.size.i32", fn_type); | |
| 1056 | assert(LLVMGetIntrinsicID(g->wasm_memory_size)); | |
| 1057 | ||
| 1058 | return g->wasm_memory_size; | |
| 1059 | } | |
| 1060 | ||
| 1048 | 1061 | static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { |
| 1049 | 1062 | if (g->stacksave_fn_val) |
| 1050 | 1063 | return g->stacksave_fn_val; |
| ... | ... | @@ -5588,6 +5601,17 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir |
| 5588 | 5601 | return nullptr; |
| 5589 | 5602 | } |
| 5590 | 5603 | |
| 5604 | static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemorySize *instruction) { | |
| 5605 | // When Wasm lands multi-memory support, we can relax this to permit the user specify | |
| 5606 | // memory index to inquire about. For now, we pass in the recommended default of index 0. | |
| 5607 | // | |
| 5608 | // More info: | |
| 5609 | // https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#current-linear-memory-size | |
| 5610 | LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); | |
| 5611 | LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), &zero, 1, ""); | |
| 5612 | return val; | |
| 5613 | } | |
| 5614 | ||
| 5591 | 5615 | static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { |
| 5592 | 5616 | Error err; |
| 5593 | 5617 | |
| ... | ... | @@ -6798,6 +6822,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl |
| 6798 | 6822 | return ir_render_splat(g, executable, (IrInstGenSplat *) instruction); |
| 6799 | 6823 | case IrInstGenIdVectorExtractElem: |
| 6800 | 6824 | return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); |
| 6825 | case IrInstGenIdWasmMemorySize: | |
| 6826 | return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); | |
| 6801 | 6827 | } |
| 6802 | 6828 | zig_unreachable(); |
| 6803 | 6829 | } |
| ... | ... | @@ -8660,6 +8686,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8660 | 8686 | create_builtin_fn(g, BuiltinFnIdAs, "as", 2); |
| 8661 | 8687 | create_builtin_fn(g, BuiltinFnIdCall, "call", 3); |
| 8662 | 8688 | create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); |
| 8689 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 0); | |
| 8663 | 8690 | } |
| 8664 | 8691 | |
| 8665 | 8692 | static const char *bool_to_str(bool b) { |
src/ir.cpp+45| ... | ... | @@ -556,6 +556,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 556 | 556 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst)); |
| 557 | 557 | case IrInstSrcIdCallArgs: |
| 558 | 558 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst)); |
| 559 | case IrInstSrcIdWasmMemorySize: | |
| 560 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst)); | |
| 559 | 561 | } |
| 560 | 562 | zig_unreachable(); |
| 561 | 563 | } |
| ... | ... | @@ -736,6 +738,8 @@ void destroy_instruction_gen(IrInstGen *inst) { |
| 736 | 738 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegation *>(inst)); |
| 737 | 739 | case IrInstGenIdNegationWrapping: |
| 738 | 740 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegationWrapping *>(inst)); |
| 741 | case IrInstGenIdWasmMemorySize: | |
| 742 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemorySize *>(inst)); | |
| 739 | 743 | } |
| 740 | 744 | zig_unreachable(); |
| 741 | 745 | } |
| ... | ... | @@ -1334,6 +1338,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) { |
| 1334 | 1338 | return IrInstSrcIdBoolNot; |
| 1335 | 1339 | } |
| 1336 | 1340 | |
| 1341 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { | |
| 1342 | return IrInstSrcIdWasmMemorySize; | |
| 1343 | } | |
| 1344 | ||
| 1337 | 1345 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) { |
| 1338 | 1346 | return IrInstSrcIdMemset; |
| 1339 | 1347 | } |
| ... | ... | @@ -1955,6 +1963,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { |
| 1955 | 1963 | return IrInstGenIdConst; |
| 1956 | 1964 | } |
| 1957 | 1965 | |
| 1966 | static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { | |
| 1967 | return IrInstGenIdWasmMemorySize; | |
| 1968 | } | |
| 1969 | ||
| 1958 | 1970 | template<typename T> |
| 1959 | 1971 | static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { |
| 1960 | 1972 | T *special_instruction = heap::c_allocator.create<T>(); |
| ... | ... | @@ -3642,6 +3654,20 @@ static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, Ir |
| 3642 | 3654 | return &instruction->base; |
| 3643 | 3655 | } |
| 3644 | 3656 | |
| 3657 | static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { | |
| 3658 | IrInstSrcWasmMemorySize *instruction = ir_build_instruction<IrInstSrcWasmMemorySize>(irb, scope, source_node); | |
| 3659 | ||
| 3660 | return &instruction->base; | |
| 3661 | } | |
| 3662 | ||
| 3663 | static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr) { | |
| 3664 | IrInstGenWasmMemorySize *instruction = ir_build_inst_gen<IrInstGenWasmMemorySize>(&ira->new_irb, | |
| 3665 | source_instr->scope, source_instr->source_node); | |
| 3666 | instruction->base.value->type = ira->codegen->builtin_types.entry_i32; | |
| 3667 | ||
| 3668 | return &instruction->base; | |
| 3669 | } | |
| 3670 | ||
| 3645 | 3671 | static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 3646 | 3672 | IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count) |
| 3647 | 3673 | { |
| ... | ... | @@ -6754,6 +6780,11 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod |
| 6754 | 6780 | IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); |
| 6755 | 6781 | return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); |
| 6756 | 6782 | } |
| 6783 | case BuiltinFnIdWasmMemorySize: | |
| 6784 | { | |
| 6785 | IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node); | |
| 6786 | return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); | |
| 6787 | } | |
| 6757 | 6788 | case BuiltinFnIdField: |
| 6758 | 6789 | { |
| 6759 | 6790 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -27651,6 +27682,16 @@ static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasF |
| 27651 | 27682 | return ir_const_bool(ira, &instruction->base.base, result); |
| 27652 | 27683 | } |
| 27653 | 27684 | |
| 27685 | static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { | |
| 27686 | if (!target_is_wasm(ira->codegen->zig_target)) { | |
| 27687 | ir_add_error_node(ira, instruction->base.base.source_node, | |
| 27688 | buf_sprintf("@wasmMemorySize is a wasm feature only")); | |
| 27689 | return ira->codegen->invalid_inst_gen; | |
| 27690 | } | |
| 27691 | ||
| 27692 | return ir_build_wasm_memory_size_gen(ira, &instruction->base.base); | |
| 27693 | } | |
| 27694 | ||
| 27654 | 27695 | static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { |
| 27655 | 27696 | return ir_build_breakpoint_gen(ira, &instruction->base.base); |
| 27656 | 27697 | } |
| ... | ... | @@ -30892,6 +30933,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 30892 | 30933 | return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); |
| 30893 | 30934 | case IrInstSrcIdSpillEnd: |
| 30894 | 30935 | return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); |
| 30936 | case IrInstSrcIdWasmMemorySize: | |
| 30937 | return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); | |
| 30895 | 30938 | } |
| 30896 | 30939 | zig_unreachable(); |
| 30897 | 30940 | } |
| ... | ... | @@ -31117,6 +31160,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { |
| 31117 | 31160 | case IrInstGenIdBinaryNot: |
| 31118 | 31161 | case IrInstGenIdNegation: |
| 31119 | 31162 | case IrInstGenIdNegationWrapping: |
| 31163 | case IrInstGenIdWasmMemorySize: | |
| 31120 | 31164 | return false; |
| 31121 | 31165 | |
| 31122 | 31166 | case IrInstGenIdAsm: |
| ... | ... | @@ -31283,6 +31327,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 31283 | 31327 | case IrInstSrcIdHasDecl: |
| 31284 | 31328 | case IrInstSrcIdAlloca: |
| 31285 | 31329 | case IrInstSrcIdSpillEnd: |
| 31330 | case IrInstSrcIdWasmMemorySize: | |
| 31286 | 31331 | return false; |
| 31287 | 31332 | |
| 31288 | 31333 | case IrInstSrcIdAsm: |
src/ir_print.cpp+18| ... | ... | @@ -321,6 +321,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 321 | 321 | return "SrcSpillBegin"; |
| 322 | 322 | case IrInstSrcIdSpillEnd: |
| 323 | 323 | return "SrcSpillEnd"; |
| 324 | case IrInstSrcIdWasmMemorySize: | |
| 325 | return "SrcWasmMemorySize"; | |
| 324 | 326 | } |
| 325 | 327 | zig_unreachable(); |
| 326 | 328 | } |
| ... | ... | @@ -501,6 +503,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { |
| 501 | 503 | return "GenNegation"; |
| 502 | 504 | case IrInstGenIdNegationWrapping: |
| 503 | 505 | return "GenNegationWrapping"; |
| 506 | case IrInstGenIdWasmMemorySize: | |
| 507 | return "GenWasmMemorySize"; | |
| 504 | 508 | } |
| 505 | 509 | zig_unreachable(); |
| 506 | 510 | } |
| ... | ... | @@ -1708,6 +1712,14 @@ static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { |
| 1708 | 1712 | ir_print_other_inst_gen(irp, instruction->value); |
| 1709 | 1713 | } |
| 1710 | 1714 | |
| 1715 | static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { | |
| 1716 | fprintf(irp->f, "@wasmMemorySize()"); | |
| 1717 | } | |
| 1718 | ||
| 1719 | static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { | |
| 1720 | fprintf(irp->f, "@wasmMemorySize()"); | |
| 1721 | } | |
| 1722 | ||
| 1711 | 1723 | static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { |
| 1712 | 1724 | fprintf(irp->f, "@memset("); |
| 1713 | 1725 | ir_print_other_inst_src(irp, instruction->dest_ptr); |
| ... | ... | @@ -2952,6 +2964,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2952 | 2964 | case IrInstSrcIdClz: |
| 2953 | 2965 | ir_print_clz(irp, (IrInstSrcClz *)instruction); |
| 2954 | 2966 | break; |
| 2967 | case IrInstSrcIdWasmMemorySize: | |
| 2968 | ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); | |
| 2969 | break; | |
| 2955 | 2970 | } |
| 2956 | 2971 | fprintf(irp->f, "\n"); |
| 2957 | 2972 | } |
| ... | ... | @@ -3219,6 +3234,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai |
| 3219 | 3234 | case IrInstGenIdNegationWrapping: |
| 3220 | 3235 | ir_print_negation_wrapping(irp, (IrInstGenNegationWrapping *)instruction); |
| 3221 | 3236 | break; |
| 3237 | case IrInstGenIdWasmMemorySize: | |
| 3238 | ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); | |
| 3239 | break; | |
| 3222 | 3240 | } |
| 3223 | 3241 | fprintf(irp->f, "\n"); |
| 3224 | 3242 | } |