From ce3f0077cf3c5ae8edc116177f2f3d33258be9f7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 31 May 2020 21:11:06 +0200 Subject: [PATCH] Add builtin for llvm.wasm.memory.size.i32 instrinsic 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); ``` --- src/all_types.hpp | 12 ++++++++++++ src/codegen.cpp | 27 +++++++++++++++++++++++++++ src/ir.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 18 ++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 61b6cbd74a..fdbec760bc 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1825,6 +1825,7 @@ enum BuiltinFnId { BuiltinFnIdAs, BuiltinFnIdCall, BuiltinFnIdBitSizeof, + BuiltinFnIdWasmMemorySize, }; struct BuiltinFnEntry { @@ -2075,6 +2076,7 @@ struct CodeGen { LLVMValueRef err_name_table; LLVMValueRef safety_crash_err_fn; LLVMValueRef return_err_fn; + LLVMValueRef wasm_memory_size; LLVMTypeRef anyframe_fn_type; // reminder: hash tables must be initialized before use @@ -2748,6 +2750,7 @@ enum IrInstSrcId { IrInstSrcIdResume, IrInstSrcIdSpillBegin, IrInstSrcIdSpillEnd, + IrInstSrcIdWasmMemorySize, }; // ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. @@ -2840,6 +2843,7 @@ enum IrInstGenId { IrInstGenIdVectorExtractElem, IrInstGenIdAlloca, IrInstGenIdConst, + IrInstGenIdWasmMemorySize, }; // Common fields between IrInstSrc and IrInstGen. This allows future passes @@ -3727,6 +3731,14 @@ struct IrInstGenMemcpy { IrInstGen *count; }; +struct IrInstSrcWasmMemorySize { + IrInstSrc base; +}; + +struct IrInstGenWasmMemorySize { + IrInstGen base; +}; + struct IrInstSrcSlice { IrInstSrc base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 92317f0965..c00ac41a95 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1045,6 +1045,19 @@ static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstGen *source_instr return gen_assertion_scope(g, msg_id, source_instruction->base.scope); } +static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { + if (g->wasm_memory_size) + return g->wasm_memory_size; + + // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly + LLVMTypeRef param_type = LLVMInt32Type(); + LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), ¶m_type, 1, false); + g->wasm_memory_size = LLVMAddFunction(g->module, "llvm.wasm.memory.size.i32", fn_type); + assert(LLVMGetIntrinsicID(g->wasm_memory_size)); + + return g->wasm_memory_size; +} + static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { if (g->stacksave_fn_val) return g->stacksave_fn_val; @@ -5588,6 +5601,17 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir return nullptr; } +static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemorySize *instruction) { + // When Wasm lands multi-memory support, we can relax this to permit the user specify + // memory index to inquire about. For now, we pass in the recommended default of index 0. + // + // More info: + // https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#current-linear-memory-size + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); + LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), &zero, 1, ""); + return val; +} + static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { Error err; @@ -6798,6 +6822,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl return ir_render_splat(g, executable, (IrInstGenSplat *) instruction); case IrInstGenIdVectorExtractElem: return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); + case IrInstGenIdWasmMemorySize: + return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); } zig_unreachable(); } @@ -8660,6 +8686,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdAs, "as", 2); create_builtin_fn(g, BuiltinFnIdCall, "call", 3); create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); + create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 0); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index a733f282b5..c66b368794 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -556,6 +556,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdCallArgs: return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdWasmMemorySize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } @@ -736,6 +738,8 @@ void destroy_instruction_gen(IrInstGen *inst) { return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdNegationWrapping: return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdWasmMemorySize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } @@ -1334,6 +1338,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) { return IrInstSrcIdBoolNot; } +static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { + return IrInstSrcIdWasmMemorySize; +} + static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) { return IrInstSrcIdMemset; } @@ -1955,6 +1963,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { return IrInstGenIdConst; } +static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { + return IrInstGenIdWasmMemorySize; +} + template static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { T *special_instruction = heap::c_allocator.create(); @@ -3642,6 +3654,20 @@ static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, Ir return &instruction->base; } +static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcWasmMemorySize *instruction = ir_build_instruction(irb, scope, source_node); + + return &instruction->base; +} + +static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenWasmMemorySize *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_i32; + + return &instruction->base; +} + static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count) { @@ -6754,6 +6780,11 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); } + case BuiltinFnIdWasmMemorySize: + { + IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); + } case BuiltinFnIdField: { 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 return ir_const_bool(ira, &instruction->base.base, result); } +static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { + if (!target_is_wasm(ira->codegen->zig_target)) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("@wasmMemorySize is a wasm feature only")); + return ira->codegen->invalid_inst_gen; + } + + return ir_build_wasm_memory_size_gen(ira, &instruction->base.base); +} + static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { return ir_build_breakpoint_gen(ira, &instruction->base.base); } @@ -30892,6 +30933,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); case IrInstSrcIdSpillEnd: return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); + case IrInstSrcIdWasmMemorySize: + return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); } zig_unreachable(); } @@ -31117,6 +31160,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { case IrInstGenIdBinaryNot: case IrInstGenIdNegation: case IrInstGenIdNegationWrapping: + case IrInstGenIdWasmMemorySize: return false; case IrInstGenIdAsm: @@ -31283,6 +31327,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { case IrInstSrcIdHasDecl: case IrInstSrcIdAlloca: case IrInstSrcIdSpillEnd: + case IrInstSrcIdWasmMemorySize: return false; case IrInstSrcIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3e50b7304f..f0caa13479 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -321,6 +321,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { return "SrcSpillBegin"; case IrInstSrcIdSpillEnd: return "SrcSpillEnd"; + case IrInstSrcIdWasmMemorySize: + return "SrcWasmMemorySize"; } zig_unreachable(); } @@ -501,6 +503,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { return "GenNegation"; case IrInstGenIdNegationWrapping: return "GenNegationWrapping"; + case IrInstGenIdWasmMemorySize: + return "GenWasmMemorySize"; } zig_unreachable(); } @@ -1708,6 +1712,14 @@ static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { ir_print_other_inst_gen(irp, instruction->value); } +static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { + fprintf(irp->f, "@wasmMemorySize()"); +} + +static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { + fprintf(irp->f, "@wasmMemorySize()"); +} + static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { fprintf(irp->f, "@memset("); 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 case IrInstSrcIdClz: ir_print_clz(irp, (IrInstSrcClz *)instruction); break; + case IrInstSrcIdWasmMemorySize: + ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); + break; } fprintf(irp->f, "\n"); } @@ -3219,6 +3234,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai case IrInstGenIdNegationWrapping: ir_print_negation_wrapping(irp, (IrInstGenNegationWrapping *)instruction); break; + case IrInstGenIdWasmMemorySize: + ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); + break; } fprintf(irp->f, "\n"); }