From ce3f0077cf3c5ae8edc116177f2f3d33258be9f7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 31 May 2020 21:11:06 +0200 Subject: [PATCH 01/10] 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"); } From 601e831f1d15ba5c8f89fbab265a72bfabb6af96 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 1 Jun 2020 21:54:54 +0200 Subject: [PATCH 02/10] Add builtin for llvm.wasm.memory.grow.i32 intrinsic This will allow the developer to request additional memory pages from the runtime to be allocated for the Wasm app. Typical usage: ```zig var wasm_pages = @wasmMemorySize(); @wasmMemoryGrow(1); @import("std").debug.assert((wasm_pages + 1) == @wasmMemorySize()); ``` --- src/all_types.hpp | 20 ++++++++- src/codegen.cpp | 39 ++++++++++++++++- src/ir.cpp | 107 ++++++++++++++++++++++++++++++++++++++-------- src/ir_print.cpp | 22 ++++++++++ 4 files changed, 166 insertions(+), 22 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index fdbec760bc..eaf7203df8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1826,6 +1826,7 @@ enum BuiltinFnId { BuiltinFnIdCall, BuiltinFnIdBitSizeof, BuiltinFnIdWasmMemorySize, + BuiltinFnIdWasmMemoryGrow, }; struct BuiltinFnEntry { @@ -2077,6 +2078,7 @@ struct CodeGen { LLVMValueRef safety_crash_err_fn; LLVMValueRef return_err_fn; LLVMValueRef wasm_memory_size; + LLVMValueRef wasm_memory_grow; LLVMTypeRef anyframe_fn_type; // reminder: hash tables must be initialized before use @@ -2751,6 +2753,7 @@ enum IrInstSrcId { IrInstSrcIdSpillBegin, IrInstSrcIdSpillEnd, IrInstSrcIdWasmMemorySize, + IrInstSrcIdWasmMemoryGrow, }; // ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. @@ -2844,6 +2847,7 @@ enum IrInstGenId { IrInstGenIdAlloca, IrInstGenIdConst, IrInstGenIdWasmMemorySize, + IrInstGenIdWasmMemoryGrow, }; // Common fields between IrInstSrc and IrInstGen. This allows future passes @@ -3732,11 +3736,23 @@ struct IrInstGenMemcpy { }; struct IrInstSrcWasmMemorySize { - IrInstSrc base; + IrInstSrc base; }; struct IrInstGenWasmMemorySize { - IrInstGen base; + IrInstGen base; +}; + +struct IrInstSrcWasmMemoryGrow { + IrInstSrc base; + + IrInstSrc *delta; +}; + +struct IrInstGenWasmMemoryGrow { + IrInstGen base; + + IrInstGen *delta; }; struct IrInstSrcSlice { diff --git a/src/codegen.cpp b/src/codegen.cpp index c00ac41a95..765d57d7db 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1047,8 +1047,9 @@ static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstGen *source_instr static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { if (g->wasm_memory_size) - return g->wasm_memory_size; + return g->wasm_memory_size; + // TODO adjust for wasm64 as well // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly LLVMTypeRef param_type = LLVMInt32Type(); LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), ¶m_type, 1, false); @@ -1058,6 +1059,23 @@ static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { return g->wasm_memory_size; } +static LLVMValueRef gen_wasm_memory_grow(CodeGen *g) { + if (g->wasm_memory_grow) + return g->wasm_memory_grow; + + // TODO adjust for wasm64 as well + // declare i32 @llvm.wasm.memory.grow.i32(i32, i32) nounwind + LLVMTypeRef param_types[] = { + LLVMInt32Type(), + LLVMInt32Type(), + }; + LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, false); + g->wasm_memory_grow = LLVMAddFunction(g->module, "llvm.wasm.memory.grow.i32", fn_type); + assert(LLVMGetIntrinsicID(g->wasm_memory_grow)); + + return g->wasm_memory_grow; +} + static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { if (g->stacksave_fn_val) return g->stacksave_fn_val; @@ -5607,11 +5625,27 @@ static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *exec // // More info: // https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#current-linear-memory-size + // TODO adjust for wasm64 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_wasm_memory_grow(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemoryGrow *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#grow-linear-memory-size + // TODO adjust for wasm64 + LLVMValueRef params[] = { + LLVMConstNull(g->builtin_types.entry_i32->llvm_type), + ir_llvm_value(g, instruction->delta), + }; + LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_grow(g), params, 2, ""); + return val; +} + static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { Error err; @@ -6824,6 +6858,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); case IrInstGenIdWasmMemorySize: return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); + case IrInstGenIdWasmMemoryGrow: + return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction); } zig_unreachable(); } @@ -8687,6 +8723,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCall, "call", 3); create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 0); + create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 1); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index c66b368794..3edb61556e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -558,6 +558,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstSrcIdWasmMemorySize: return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdWasmMemoryGrow: + return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } @@ -740,6 +742,8 @@ void destroy_instruction_gen(IrInstGen *inst) { return heap::c_allocator.destroy(reinterpret_cast(inst)); case IrInstGenIdWasmMemorySize: return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdWasmMemoryGrow: + return heap::c_allocator.destroy(reinterpret_cast(inst)); } zig_unreachable(); } @@ -1338,10 +1342,6 @@ 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; } @@ -1618,6 +1618,14 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) { return IrInstSrcIdSpillEnd; } +static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { + return IrInstSrcIdWasmMemorySize; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) { + return IrInstSrcIdWasmMemoryGrow; +} + static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { return IrInstGenIdDeclVar; @@ -1967,6 +1975,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { return IrInstGenIdWasmMemorySize; } +static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) { + return IrInstGenIdWasmMemoryGrow; +} + template static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { T *special_instruction = heap::c_allocator.create(); @@ -3654,20 +3666,6 @@ 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) { @@ -4987,6 +4985,41 @@ static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_in 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_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *delta) { + IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction(irb, scope, source_node); + instruction->delta = delta; + + ir_ref_instruction(delta, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *delta) { + IrInstGenWasmMemoryGrow *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; + instruction->delta = delta; + + ir_ref_inst_gen(delta); + + return &instruction->base; +} + + static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -6785,6 +6818,16 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod 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 BuiltinFnIdWasmMemoryGrow: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); + } case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -27683,15 +27726,37 @@ static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasF } static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { + // TODO generate compile error for target_arch different than 32bit 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")); + buf_sprintf("@wasmMemorySize is a wasm32 feature only")); return ira->codegen->invalid_inst_gen; } return ir_build_wasm_memory_size_gen(ira, &instruction->base.base); } +static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { + // TODO generate compile error for target_arch different than 32bit + if (!target_is_wasm(ira->codegen->zig_target)) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("@wasmMemoryGrow is a wasm32 feature only")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *delta = instruction->delta->child; + if (type_is_invalid(delta->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *i32_type = ira->codegen->builtin_types.entry_i32; + + IrInstGen *casted_delta = ir_implicit_cast(ira, delta, i32_type); + if (type_is_invalid(casted_delta->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_delta); +} + static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { return ir_build_breakpoint_gen(ira, &instruction->base.base); } @@ -30935,6 +31000,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); case IrInstSrcIdWasmMemorySize: return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); + case IrInstSrcIdWasmMemoryGrow: + return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction); } zig_unreachable(); } @@ -31111,6 +31178,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { case IrInstGenIdResume: case IrInstGenIdAwait: case IrInstGenIdSpillBegin: + case IrInstGenIdWasmMemoryGrow: return true; case IrInstGenIdPhi: @@ -31243,6 +31311,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { case IrInstSrcIdResume: case IrInstSrcIdAwait: case IrInstSrcIdSpillBegin: + case IrInstSrcIdWasmMemoryGrow: return true; case IrInstSrcIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f0caa13479..e287ca74fc 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -323,6 +323,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { return "SrcSpillEnd"; case IrInstSrcIdWasmMemorySize: return "SrcWasmMemorySize"; + case IrInstSrcIdWasmMemoryGrow: + return "SrcWasmMemoryGrow"; } zig_unreachable(); } @@ -505,6 +507,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { return "GenNegationWrapping"; case IrInstGenIdWasmMemorySize: return "GenWasmMemorySize"; + case IrInstGenIdWasmMemoryGrow: + return "GenWasmMemoryGrow"; } zig_unreachable(); } @@ -1720,6 +1724,18 @@ static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize * fprintf(irp->f, "@wasmMemorySize()"); } +static void ir_print_wasm_memory_grow(IrPrintSrc *irp, IrInstSrcWasmMemoryGrow *instruction) { + fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_src(irp, instruction->delta); + fprintf(irp->f, ")"); +} + +static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *instruction) { + fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_gen(irp, instruction->delta); + fprintf(irp->f, ")"); +} + static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { fprintf(irp->f, "@memset("); ir_print_other_inst_src(irp, instruction->dest_ptr); @@ -2967,6 +2983,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai case IrInstSrcIdWasmMemorySize: ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); break; + case IrInstSrcIdWasmMemoryGrow: + ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction); + break; } fprintf(irp->f, "\n"); } @@ -3237,6 +3256,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai case IrInstGenIdWasmMemorySize: ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); break; + case IrInstGenIdWasmMemoryGrow: + ir_print_wasm_memory_grow(irp, (IrInstGenWasmMemoryGrow *)instruction); + break; } fprintf(irp->f, "\n"); } From 146be2a8cb3272673a99050351b5f394fd1de30b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 2 Jun 2020 13:04:02 +0200 Subject: [PATCH 03/10] Use builtins in std.heap.WasmAllocator --- lib/std/heap.zig | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 6bbb688ef0..f28f5c4b25 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -250,11 +250,6 @@ const PageAllocator = struct { } }; -// TODO Exposed LLVM intrinsics is a bug -// See: https://github.com/ziglang/zig/issues/2291 -extern fn @"llvm.wasm.memory.size.i32"(u32) u32; -extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; - const WasmPageAllocator = struct { comptime { if (!std.Target.current.isWasm()) { @@ -357,7 +352,7 @@ const WasmPageAllocator = struct { return idx + extendedOffset(); } - const prev_page_count = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, page_count)); + const prev_page_count = @wasmMemoryGrow(@intCast(u32, page_count)); if (prev_page_count <= 0) { return error.OutOfMemory; } From 73a3bfd1dd63a160fd3776b8394d9336abd1ae99 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 2 Jun 2020 13:26:49 +0200 Subject: [PATCH 04/10] Add basic tests for the new builtins --- test/compile_errors.zig | 18 ++++++++++++++++++ test/stage1/behavior.zig | 3 +++ test/stage1/behavior/wasm.zig | 8 ++++++++ 3 files changed, 29 insertions(+) create mode 100644 test/stage1/behavior/wasm.zig diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 17a9e0663e..77438e5bb0 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7504,4 +7504,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ ":3:52: error: slice '[]const u8' cannot have its bytes reinterpreted", }); + + cases.add("wasmMemorySize is a compile error in non-Wasm targets", + \\export fn foo() void { + \\ _ = @wasmMemorySize(); + \\ return; + \\} + , &[_][]const u8{ + "tmp.zig:2:9: error: @wasmMemorySize is a wasm32 feature only", + }); + + cases.add("wasmMemoryGrow is a compile error in non-Wasm targets", + \\export fn foo() void { + \\ _ = @wasmMemoryGrow(1); + \\ return; + \\} + , &[_][]const u8{ + "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only", + }); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 83bd2ddce8..1620aa52df 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -126,6 +126,9 @@ comptime { _ = @import("behavior/var_args.zig"); _ = @import("behavior/vector.zig"); _ = @import("behavior/void.zig"); + if (builtin.arch == .wasm32) { + _ = @import("behavior/wasm.zig"); + } _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); } diff --git a/test/stage1/behavior/wasm.zig b/test/stage1/behavior/wasm.zig new file mode 100644 index 0000000000..c55700c7c0 --- /dev/null +++ b/test/stage1/behavior/wasm.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "memory size and grow" { + var prev = @wasmMemorySize(); + expect(prev == @wasmMemoryGrow(1)); + expect(prev + 1 == @wasmMemorySize()); +} From 8ffa8ed9a8c66d2c8cbdffe619b2c5302d17b814 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 2 Jun 2020 14:08:58 +0200 Subject: [PATCH 05/10] Expose full llvm intrinsic --- lib/std/heap.zig | 2 +- src/all_types.hpp | 6 +++++ src/codegen.cpp | 20 ++++---------- src/ir.cpp | 58 ++++++++++++++++++++++++++++++++++------- src/ir_print.cpp | 12 +++++++-- test/compile_errors.zig | 4 +-- 6 files changed, 72 insertions(+), 30 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index f28f5c4b25..f05378c215 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -352,7 +352,7 @@ const WasmPageAllocator = struct { return idx + extendedOffset(); } - const prev_page_count = @wasmMemoryGrow(@intCast(u32, page_count)); + const prev_page_count = @wasmMemoryGrow(0, @intCast(u32, page_count)); if (prev_page_count <= 0) { return error.OutOfMemory; } diff --git a/src/all_types.hpp b/src/all_types.hpp index eaf7203df8..9413ea73a4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3737,21 +3737,27 @@ struct IrInstGenMemcpy { struct IrInstSrcWasmMemorySize { IrInstSrc base; + + IrInstSrc *index; }; struct IrInstGenWasmMemorySize { IrInstGen base; + + IrInstGen *index; }; struct IrInstSrcWasmMemoryGrow { IrInstSrc base; + IrInstSrc *index; IrInstSrc *delta; }; struct IrInstGenWasmMemoryGrow { IrInstGen base; + IrInstGen *index; IrInstGen *delta; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 765d57d7db..f945dd6545 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5620,26 +5620,16 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir } 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 // TODO adjust for wasm64 - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); - LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), &zero, 1, ""); + LLVMValueRef param = ir_llvm_value(g, instruction->index); + LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), ¶m, 1, ""); return val; } static LLVMValueRef ir_render_wasm_memory_grow(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemoryGrow *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#grow-linear-memory-size // TODO adjust for wasm64 LLVMValueRef params[] = { - LLVMConstNull(g->builtin_types.entry_i32->llvm_type), + ir_llvm_value(g, instruction->index), ir_llvm_value(g, instruction->delta), }; LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_grow(g), params, 2, ""); @@ -8722,8 +8712,8 @@ 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); - create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 1); + create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); + create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index 3edb61556e..aaa9599a1f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4985,35 +4985,45 @@ static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_in return &instruction->base; } -static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { +static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) { IrInstSrcWasmMemorySize *instruction = ir_build_instruction(irb, scope, source_node); + instruction->index = index; + + ir_ref_instruction(index, irb->current_basic_block); return &instruction->base; } -static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr) { +static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { 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; + instruction->index = index; + + ir_ref_inst_gen(index); return &instruction->base; } -static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *delta) { +static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) { IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction(irb, scope, source_node); + instruction->index = index; instruction->delta = delta; + ir_ref_instruction(index, irb->current_basic_block); ir_ref_instruction(delta, irb->current_basic_block); return &instruction->base; } -static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *delta) { +static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { IrInstGenWasmMemoryGrow *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; + instruction->index = index; instruction->delta = delta; + ir_ref_inst_gen(index); ir_ref_inst_gen(delta); return &instruction->base; @@ -6815,7 +6825,12 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod } case BuiltinFnIdWasmMemorySize: { - IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node); + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); } case BuiltinFnIdWasmMemoryGrow: @@ -6825,7 +6840,12 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod if (arg0_value == irb->codegen->invalid_inst_src) return arg0_value; - IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value); + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); } case BuiltinFnIdField: @@ -27733,7 +27753,17 @@ static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInst return ira->codegen->invalid_inst_gen; } - return ir_build_wasm_memory_size_gen(ira, &instruction->base.base); + IrInstGen *index = instruction->index->child; + if (type_is_invalid(index->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *i32_type = ira->codegen->builtin_types.entry_i32; + + IrInstGen *casted_index = ir_implicit_cast(ira, index, i32_type); + if (type_is_invalid(casted_index->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_build_wasm_memory_size_gen(ira, &instruction->base.base, casted_index); } static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { @@ -27744,17 +27774,25 @@ static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInst return ira->codegen->invalid_inst_gen; } - IrInstGen *delta = instruction->delta->child; - if (type_is_invalid(delta->value->type)) + IrInstGen *index = instruction->index->child; + if (type_is_invalid(index->value->type)) return ira->codegen->invalid_inst_gen; ZigType *i32_type = ira->codegen->builtin_types.entry_i32; + IrInstGen *casted_index = ir_implicit_cast(ira, index, i32_type); + if (type_is_invalid(casted_index->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *delta = instruction->delta->child; + if (type_is_invalid(delta->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *casted_delta = ir_implicit_cast(ira, delta, i32_type); if (type_is_invalid(casted_delta->value->type)) return ira->codegen->invalid_inst_gen; - return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_delta); + return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_index, casted_delta); } static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index e287ca74fc..c826d76e03 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1717,21 +1717,29 @@ static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { } static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { - fprintf(irp->f, "@wasmMemorySize()"); + fprintf(irp->f, "@wasmMemorySize("); + ir_print_other_inst_src(irp, instruction->index); + fprintf(irp->f, ")"); } static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { - fprintf(irp->f, "@wasmMemorySize()"); + fprintf(irp->f, "@wasmMemorySize("); + ir_print_other_inst_gen(irp, instruction->index); + fprintf(irp->f, ")"); } static void ir_print_wasm_memory_grow(IrPrintSrc *irp, IrInstSrcWasmMemoryGrow *instruction) { fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_src(irp, instruction->index); + fprintf(irp->f, ", "); ir_print_other_inst_src(irp, instruction->delta); fprintf(irp->f, ")"); } static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *instruction) { fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_gen(irp, instruction->index); + fprintf(irp->f, ", "); ir_print_other_inst_gen(irp, instruction->delta); fprintf(irp->f, ")"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 77438e5bb0..278d66ab9f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7507,7 +7507,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add("wasmMemorySize is a compile error in non-Wasm targets", \\export fn foo() void { - \\ _ = @wasmMemorySize(); + \\ _ = @wasmMemorySize(0); \\ return; \\} , &[_][]const u8{ @@ -7516,7 +7516,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add("wasmMemoryGrow is a compile error in non-Wasm targets", \\export fn foo() void { - \\ _ = @wasmMemoryGrow(1); + \\ _ = @wasmMemoryGrow(0, 1); \\ return; \\} , &[_][]const u8{ From 3f0a3cea6e0369c24ba0303b58da5d7154ad7ace Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 2 Jun 2020 16:19:10 +0200 Subject: [PATCH 06/10] Fix builtins to return and accept u32 instead of i32 --- src/ir.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index aaa9599a1f..3cd861700b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4997,7 +4997,7 @@ static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { 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; + instruction->base.value->type = ira->codegen->builtin_types.entry_u32; instruction->index = index; ir_ref_inst_gen(index); @@ -5019,7 +5019,7 @@ static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { IrInstGenWasmMemoryGrow *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; + instruction->base.value->type = ira->codegen->builtin_types.entry_u32; instruction->index = index; instruction->delta = delta; @@ -27757,9 +27757,9 @@ static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInst if (type_is_invalid(index->value->type)) return ira->codegen->invalid_inst_gen; - ZigType *i32_type = ira->codegen->builtin_types.entry_i32; + ZigType *u32 = ira->codegen->builtin_types.entry_u32; - IrInstGen *casted_index = ir_implicit_cast(ira, index, i32_type); + IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); if (type_is_invalid(casted_index->value->type)) return ira->codegen->invalid_inst_gen; @@ -27778,9 +27778,9 @@ static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInst if (type_is_invalid(index->value->type)) return ira->codegen->invalid_inst_gen; - ZigType *i32_type = ira->codegen->builtin_types.entry_i32; + ZigType *u32 = ira->codegen->builtin_types.entry_u32; - IrInstGen *casted_index = ir_implicit_cast(ira, index, i32_type); + IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); if (type_is_invalid(casted_index->value->type)) return ira->codegen->invalid_inst_gen; @@ -27788,7 +27788,7 @@ static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInst if (type_is_invalid(delta->value->type)) return ira->codegen->invalid_inst_gen; - IrInstGen *casted_delta = ir_implicit_cast(ira, delta, i32_type); + IrInstGen *casted_delta = ir_implicit_cast(ira, delta, u32); if (type_is_invalid(casted_delta->value->type)) return ira->codegen->invalid_inst_gen; From 057d97c093c81150a3761b50a1f646703b6cfd1b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 2 Jun 2020 19:05:21 +0200 Subject: [PATCH 07/10] Return should be i32 due to error signaling in memory.grow Also, fix tests. --- src/ir.cpp | 4 ++-- test/stage1/behavior/wasm.zig | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3cd861700b..86304ff448 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4997,7 +4997,7 @@ static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { 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_u32; + instruction->base.value->type = ira->codegen->builtin_types.entry_i32; instruction->index = index; ir_ref_inst_gen(index); @@ -5019,7 +5019,7 @@ static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_u32; + instruction->base.value->type = ira->codegen->builtin_types.entry_i32; instruction->index = index; instruction->delta = delta; diff --git a/test/stage1/behavior/wasm.zig b/test/stage1/behavior/wasm.zig index c55700c7c0..24557ee19b 100644 --- a/test/stage1/behavior/wasm.zig +++ b/test/stage1/behavior/wasm.zig @@ -2,7 +2,7 @@ const std = @import("std"); const expect = std.testing.expect; test "memory size and grow" { - var prev = @wasmMemorySize(); - expect(prev == @wasmMemoryGrow(1)); - expect(prev + 1 == @wasmMemorySize()); + var prev = @wasmMemorySize(0); + expect(prev == @wasmMemoryGrow(0, 1)); + expect(prev + 1 == @wasmMemorySize(0)); } From 52b97eeef1fbb72f1b0bd5d466383cad13a67d54 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 5 Jun 2020 09:27:33 +0200 Subject: [PATCH 08/10] Return u32 in @wasmMemorySize instead of i32 --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 86304ff448..887f0a2f9b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4997,7 +4997,7 @@ static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { 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; + instruction->base.value->type = ira->codegen->builtin_types.entry_u32; instruction->index = index; ir_ref_inst_gen(index); From 660eef9a43dab695c4aa26330067ce72d375a1be Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 5 Jun 2020 09:27:52 +0200 Subject: [PATCH 09/10] Document the builtins --- doc/langref.html.in | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index ad5a688a04..6b14daad6e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7643,6 +7643,46 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} mem.set(u8, dest, c);{#endsyntax#} {#header_close#} + {#header_open|@wasmMemorySize#} +
{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}
+

+ This function returns the size of the Wasm memory identified by {#syntax#}index{#endsyntax#} as + an unsigned value in units of Wasm pages. Note that each Wasm page is 64KB in size. +

+

+ This function is a low level intrinsic with no safety mechanisms usually useful for allocator + designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use + something like {#syntax#}@import("std").heap.WasmAllocator{#endsyntax#}. +

+ {#see_also|@wasmMemoryGrow#} + {#header_close#} + + {#header_open|@wasmMemoryGrow#} +
{#syntax#}@wasmMemoryGrow(index: u32, delta: u32) i32{#endsyntax#}
+

+ This function increases the size of the Wasm memory identified by {#syntax#}index{#endsyntax#} by + {#syntax#}delta{#endsyntax#} in units of unsigned number of Wasm pages. Note that each Wasm page + is 64KB in size. On success, returns previous memory size; on failure, if the allocation fails, + returns -1. +

+

+ This function is a low level intrinsic with no safety mechanisms usually useful for allocator + designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use + something like {#syntax#}@import("std").heap.WasmAllocator{#endsyntax#}. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "@wasmMemoryGrow" { + var prev = @wasmMemorySize(0); + assert(prev == @wasmMemoryGrow(0, 1)); + assert(prev + 1 == @wasmMemorySize(0)); +} + {#code_end#} + {#see_also|@wasmMemorySize#} + {#header_close#} + {#header_open|@mod#}
{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}

From 42c95a64d67ec6fc2839fad36ef50bacc7545258 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 5 Jun 2020 10:16:41 +0200 Subject: [PATCH 10/10] Update langref.html.in --- doc/langref.html.in | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 6b14daad6e..e5c814d663 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7652,7 +7652,7 @@ mem.set(u8, dest, c);{#endsyntax#}

This function is a low level intrinsic with no safety mechanisms usually useful for allocator designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use - something like {#syntax#}@import("std").heap.WasmAllocator{#endsyntax#}. + something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}.

{#see_also|@wasmMemoryGrow#} {#header_close#} @@ -7668,13 +7668,16 @@ mem.set(u8, dest, c);{#endsyntax#}

This function is a low level intrinsic with no safety mechanisms usually useful for allocator designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use - something like {#syntax#}@import("std").heap.WasmAllocator{#endsyntax#}. + something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}.

{#code_begin|test#} const std = @import("std"); +const builtin = @import("builtin"); const assert = std.debug.assert; test "@wasmMemoryGrow" { + if (builtin.arch != .wasm32) return error.SkipZigTest; + var prev = @wasmMemorySize(0); assert(prev == @wasmMemoryGrow(0, 1)); assert(prev + 1 == @wasmMemorySize(0));