| author | |
| committer | |
| log | 4302f276ed3083b4f261f9e50a9546c9877d2785 |
| tree | c857775bf067e7838764125d68210376491058c5 |
| parent | 12051b02f1f455b85d5a519dd1747a67d4bb68d0 |
| parent | 42c95a64d67ec6fc2839fad36ef50bacc7545258 |
closes #55079 files changed, 361 insertions(+), 6 deletions(-)
doc/langref.html.in+43| ... | ... | @@ -7643,6 +7643,49 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre> |
| 7643 | 7643 | mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 7644 | 7644 | {#header_close#} |
| 7645 | 7645 | |
| 7646 | {#header_open|@wasmMemorySize#} | |
| 7647 | <pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre> | |
| 7648 | <p> | |
| 7649 | This function returns the size of the Wasm memory identified by {#syntax#}index{#endsyntax#} as | |
| 7650 | an unsigned value in units of Wasm pages. Note that each Wasm page is 64KB in size. | |
| 7651 | </p> | |
| 7652 | <p> | |
| 7653 | This function is a low level intrinsic with no safety mechanisms usually useful for allocator | |
| 7654 | designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use | |
| 7655 | something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}. | |
| 7656 | </p> | |
| 7657 | {#see_also|@wasmMemoryGrow#} | |
| 7658 | {#header_close#} | |
| 7659 | ||
| 7660 | {#header_open|@wasmMemoryGrow#} | |
| 7661 | <pre>{#syntax#}@wasmMemoryGrow(index: u32, delta: u32) i32{#endsyntax#}</pre> | |
| 7662 | <p> | |
| 7663 | This function increases the size of the Wasm memory identified by {#syntax#}index{#endsyntax#} by | |
| 7664 | {#syntax#}delta{#endsyntax#} in units of unsigned number of Wasm pages. Note that each Wasm page | |
| 7665 | is 64KB in size. On success, returns previous memory size; on failure, if the allocation fails, | |
| 7666 | returns -1. | |
| 7667 | </p> | |
| 7668 | <p> | |
| 7669 | This function is a low level intrinsic with no safety mechanisms usually useful for allocator | |
| 7670 | designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use | |
| 7671 | something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}. | |
| 7672 | </p> | |
| 7673 | {#code_begin|test#} | |
| 7674 | const std = @import("std"); | |
| 7675 | const builtin = @import("builtin"); | |
| 7676 | const assert = std.debug.assert; | |
| 7677 | ||
| 7678 | test "@wasmMemoryGrow" { | |
| 7679 | if (builtin.arch != .wasm32) return error.SkipZigTest; | |
| 7680 | ||
| 7681 | var prev = @wasmMemorySize(0); | |
| 7682 | assert(prev == @wasmMemoryGrow(0, 1)); | |
| 7683 | assert(prev + 1 == @wasmMemorySize(0)); | |
| 7684 | } | |
| 7685 | {#code_end#} | |
| 7686 | {#see_also|@wasmMemorySize#} | |
| 7687 | {#header_close#} | |
| 7688 | ||
| 7646 | 7689 | {#header_open|@mod#} |
| 7647 | 7690 | <pre>{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 7648 | 7691 | <p> |
lib/std/heap.zig+1-6| ... | ... | @@ -250,11 +250,6 @@ const PageAllocator = struct { |
| 250 | 250 | } |
| 251 | 251 | }; |
| 252 | 252 | |
| 253 | // TODO Exposed LLVM intrinsics is a bug | |
| 254 | // See: https://github.com/ziglang/zig/issues/2291 | |
| 255 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; | |
| 256 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; | |
| 257 | ||
| 258 | 253 | const WasmPageAllocator = struct { |
| 259 | 254 | comptime { |
| 260 | 255 | if (!std.Target.current.isWasm()) { |
| ... | ... | @@ -357,7 +352,7 @@ const WasmPageAllocator = struct { |
| 357 | 352 | return idx + extendedOffset(); |
| 358 | 353 | } |
| 359 | 354 | |
| 360 | const prev_page_count = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, page_count)); | |
| 355 | const prev_page_count = @wasmMemoryGrow(0, @intCast(u32, page_count)); | |
| 361 | 356 | if (prev_page_count <= 0) { |
| 362 | 357 | return error.OutOfMemory; |
| 363 | 358 | } |
src/all_types.hpp+34| ... | ... | @@ -1825,6 +1825,8 @@ enum BuiltinFnId { |
| 1825 | 1825 | BuiltinFnIdAs, |
| 1826 | 1826 | BuiltinFnIdCall, |
| 1827 | 1827 | BuiltinFnIdBitSizeof, |
| 1828 | BuiltinFnIdWasmMemorySize, | |
| 1829 | BuiltinFnIdWasmMemoryGrow, | |
| 1828 | 1830 | }; |
| 1829 | 1831 | |
| 1830 | 1832 | struct BuiltinFnEntry { |
| ... | ... | @@ -2075,6 +2077,8 @@ struct CodeGen { |
| 2075 | 2077 | LLVMValueRef err_name_table; |
| 2076 | 2078 | LLVMValueRef safety_crash_err_fn; |
| 2077 | 2079 | LLVMValueRef return_err_fn; |
| 2080 | LLVMValueRef wasm_memory_size; | |
| 2081 | LLVMValueRef wasm_memory_grow; | |
| 2078 | 2082 | LLVMTypeRef anyframe_fn_type; |
| 2079 | 2083 | |
| 2080 | 2084 | // reminder: hash tables must be initialized before use |
| ... | ... | @@ -2748,6 +2752,8 @@ enum IrInstSrcId { |
| 2748 | 2752 | IrInstSrcIdResume, |
| 2749 | 2753 | IrInstSrcIdSpillBegin, |
| 2750 | 2754 | IrInstSrcIdSpillEnd, |
| 2755 | IrInstSrcIdWasmMemorySize, | |
| 2756 | IrInstSrcIdWasmMemoryGrow, | |
| 2751 | 2757 | }; |
| 2752 | 2758 | |
| 2753 | 2759 | // ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. |
| ... | ... | @@ -2840,6 +2846,8 @@ enum IrInstGenId { |
| 2840 | 2846 | IrInstGenIdVectorExtractElem, |
| 2841 | 2847 | IrInstGenIdAlloca, |
| 2842 | 2848 | IrInstGenIdConst, |
| 2849 | IrInstGenIdWasmMemorySize, | |
| 2850 | IrInstGenIdWasmMemoryGrow, | |
| 2843 | 2851 | }; |
| 2844 | 2852 | |
| 2845 | 2853 | // Common fields between IrInstSrc and IrInstGen. This allows future passes |
| ... | ... | @@ -3727,6 +3735,32 @@ struct IrInstGenMemcpy { |
| 3727 | 3735 | IrInstGen *count; |
| 3728 | 3736 | }; |
| 3729 | 3737 | |
| 3738 | struct IrInstSrcWasmMemorySize { | |
| 3739 | IrInstSrc base; | |
| 3740 | ||
| 3741 | IrInstSrc *index; | |
| 3742 | }; | |
| 3743 | ||
| 3744 | struct IrInstGenWasmMemorySize { | |
| 3745 | IrInstGen base; | |
| 3746 | ||
| 3747 | IrInstGen *index; | |
| 3748 | }; | |
| 3749 | ||
| 3750 | struct IrInstSrcWasmMemoryGrow { | |
| 3751 | IrInstSrc base; | |
| 3752 | ||
| 3753 | IrInstSrc *index; | |
| 3754 | IrInstSrc *delta; | |
| 3755 | }; | |
| 3756 | ||
| 3757 | struct IrInstGenWasmMemoryGrow { | |
| 3758 | IrInstGen base; | |
| 3759 | ||
| 3760 | IrInstGen *index; | |
| 3761 | IrInstGen *delta; | |
| 3762 | }; | |
| 3763 | ||
| 3730 | 3764 | struct IrInstSrcSlice { |
| 3731 | 3765 | IrInstSrc base; |
| 3732 | 3766 |
src/codegen.cpp+54| ... | ... | @@ -1045,6 +1045,37 @@ 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 | // TODO adjust for wasm64 as well | |
| 1053 | // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly | |
| 1054 | LLVMTypeRef param_type = LLVMInt32Type(); | |
| 1055 | LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), &param_type, 1, false); | |
| 1056 | g->wasm_memory_size = LLVMAddFunction(g->module, "llvm.wasm.memory.size.i32", fn_type); | |
| 1057 | assert(LLVMGetIntrinsicID(g->wasm_memory_size)); | |
| 1058 | ||
| 1059 | return g->wasm_memory_size; | |
| 1060 | } | |
| 1061 | ||
| 1062 | static LLVMValueRef gen_wasm_memory_grow(CodeGen *g) { | |
| 1063 | if (g->wasm_memory_grow) | |
| 1064 | return g->wasm_memory_grow; | |
| 1065 | ||
| 1066 | // TODO adjust for wasm64 as well | |
| 1067 | // declare i32 @llvm.wasm.memory.grow.i32(i32, i32) nounwind | |
| 1068 | LLVMTypeRef param_types[] = { | |
| 1069 | LLVMInt32Type(), | |
| 1070 | LLVMInt32Type(), | |
| 1071 | }; | |
| 1072 | LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, false); | |
| 1073 | g->wasm_memory_grow = LLVMAddFunction(g->module, "llvm.wasm.memory.grow.i32", fn_type); | |
| 1074 | assert(LLVMGetIntrinsicID(g->wasm_memory_grow)); | |
| 1075 | ||
| 1076 | return g->wasm_memory_grow; | |
| 1077 | } | |
| 1078 | ||
| 1048 | 1079 | static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { |
| 1049 | 1080 | if (g->stacksave_fn_val) |
| 1050 | 1081 | return g->stacksave_fn_val; |
| ... | ... | @@ -5588,6 +5619,23 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir |
| 5588 | 5619 | return nullptr; |
| 5589 | 5620 | } |
| 5590 | 5621 | |
| 5622 | static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemorySize *instruction) { | |
| 5623 | // TODO adjust for wasm64 | |
| 5624 | LLVMValueRef param = ir_llvm_value(g, instruction->index); | |
| 5625 | LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), &param, 1, ""); | |
| 5626 | return val; | |
| 5627 | } | |
| 5628 | ||
| 5629 | static LLVMValueRef ir_render_wasm_memory_grow(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemoryGrow *instruction) { | |
| 5630 | // TODO adjust for wasm64 | |
| 5631 | LLVMValueRef params[] = { | |
| 5632 | ir_llvm_value(g, instruction->index), | |
| 5633 | ir_llvm_value(g, instruction->delta), | |
| 5634 | }; | |
| 5635 | LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_grow(g), params, 2, ""); | |
| 5636 | return val; | |
| 5637 | } | |
| 5638 | ||
| 5591 | 5639 | static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { |
| 5592 | 5640 | Error err; |
| 5593 | 5641 | |
| ... | ... | @@ -6798,6 +6846,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl |
| 6798 | 6846 | return ir_render_splat(g, executable, (IrInstGenSplat *) instruction); |
| 6799 | 6847 | case IrInstGenIdVectorExtractElem: |
| 6800 | 6848 | return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); |
| 6849 | case IrInstGenIdWasmMemorySize: | |
| 6850 | return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); | |
| 6851 | case IrInstGenIdWasmMemoryGrow: | |
| 6852 | return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction); | |
| 6801 | 6853 | } |
| 6802 | 6854 | zig_unreachable(); |
| 6803 | 6855 | } |
| ... | ... | @@ -8660,6 +8712,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 8660 | 8712 | create_builtin_fn(g, BuiltinFnIdAs, "as", 2); |
| 8661 | 8713 | create_builtin_fn(g, BuiltinFnIdCall, "call", 3); |
| 8662 | 8714 | create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); |
| 8715 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); | |
| 8716 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); | |
| 8663 | 8717 | } |
| 8664 | 8718 | |
| 8665 | 8719 | static const char *bool_to_str(bool b) { |
src/ir.cpp+152| ... | ... | @@ -556,6 +556,10 @@ 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)); | |
| 561 | case IrInstSrcIdWasmMemoryGrow: | |
| 562 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst)); | |
| 559 | 563 | } |
| 560 | 564 | zig_unreachable(); |
| 561 | 565 | } |
| ... | ... | @@ -736,6 +740,10 @@ void destroy_instruction_gen(IrInstGen *inst) { |
| 736 | 740 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegation *>(inst)); |
| 737 | 741 | case IrInstGenIdNegationWrapping: |
| 738 | 742 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenNegationWrapping *>(inst)); |
| 743 | case IrInstGenIdWasmMemorySize: | |
| 744 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemorySize *>(inst)); | |
| 745 | case IrInstGenIdWasmMemoryGrow: | |
| 746 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemoryGrow *>(inst)); | |
| 739 | 747 | } |
| 740 | 748 | zig_unreachable(); |
| 741 | 749 | } |
| ... | ... | @@ -1610,6 +1618,14 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) { |
| 1610 | 1618 | return IrInstSrcIdSpillEnd; |
| 1611 | 1619 | } |
| 1612 | 1620 | |
| 1621 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { | |
| 1622 | return IrInstSrcIdWasmMemorySize; | |
| 1623 | } | |
| 1624 | ||
| 1625 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) { | |
| 1626 | return IrInstSrcIdWasmMemoryGrow; | |
| 1627 | } | |
| 1628 | ||
| 1613 | 1629 | |
| 1614 | 1630 | static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { |
| 1615 | 1631 | return IrInstGenIdDeclVar; |
| ... | ... | @@ -1955,6 +1971,14 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { |
| 1955 | 1971 | return IrInstGenIdConst; |
| 1956 | 1972 | } |
| 1957 | 1973 | |
| 1974 | static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { | |
| 1975 | return IrInstGenIdWasmMemorySize; | |
| 1976 | } | |
| 1977 | ||
| 1978 | static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) { | |
| 1979 | return IrInstGenIdWasmMemoryGrow; | |
| 1980 | } | |
| 1981 | ||
| 1958 | 1982 | template<typename T> |
| 1959 | 1983 | static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { |
| 1960 | 1984 | T *special_instruction = heap::c_allocator.create<T>(); |
| ... | ... | @@ -4961,6 +4985,51 @@ static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_in |
| 4961 | 4985 | return &instruction->base; |
| 4962 | 4986 | } |
| 4963 | 4987 | |
| 4988 | static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) { | |
| 4989 | IrInstSrcWasmMemorySize *instruction = ir_build_instruction<IrInstSrcWasmMemorySize>(irb, scope, source_node); | |
| 4990 | instruction->index = index; | |
| 4991 | ||
| 4992 | ir_ref_instruction(index, irb->current_basic_block); | |
| 4993 | ||
| 4994 | return &instruction->base; | |
| 4995 | } | |
| 4996 | ||
| 4997 | static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { | |
| 4998 | IrInstGenWasmMemorySize *instruction = ir_build_inst_gen<IrInstGenWasmMemorySize>(&ira->new_irb, | |
| 4999 | source_instr->scope, source_instr->source_node); | |
| 5000 | instruction->base.value->type = ira->codegen->builtin_types.entry_u32; | |
| 5001 | instruction->index = index; | |
| 5002 | ||
| 5003 | ir_ref_inst_gen(index); | |
| 5004 | ||
| 5005 | return &instruction->base; | |
| 5006 | } | |
| 5007 | ||
| 5008 | static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) { | |
| 5009 | IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction<IrInstSrcWasmMemoryGrow>(irb, scope, source_node); | |
| 5010 | instruction->index = index; | |
| 5011 | instruction->delta = delta; | |
| 5012 | ||
| 5013 | ir_ref_instruction(index, irb->current_basic_block); | |
| 5014 | ir_ref_instruction(delta, irb->current_basic_block); | |
| 5015 | ||
| 5016 | return &instruction->base; | |
| 5017 | } | |
| 5018 | ||
| 5019 | static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { | |
| 5020 | IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen<IrInstGenWasmMemoryGrow>(&ira->new_irb, | |
| 5021 | source_instr->scope, source_instr->source_node); | |
| 5022 | instruction->base.value->type = ira->codegen->builtin_types.entry_i32; | |
| 5023 | instruction->index = index; | |
| 5024 | instruction->delta = delta; | |
| 5025 | ||
| 5026 | ir_ref_inst_gen(index); | |
| 5027 | ir_ref_inst_gen(delta); | |
| 5028 | ||
| 5029 | return &instruction->base; | |
| 5030 | } | |
| 5031 | ||
| 5032 | ||
| 4964 | 5033 | static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 4965 | 5034 | results[ReturnKindUnconditional] = 0; |
| 4966 | 5035 | results[ReturnKindError] = 0; |
| ... | ... | @@ -6754,6 +6823,31 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod |
| 6754 | 6823 | IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); |
| 6755 | 6824 | return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); |
| 6756 | 6825 | } |
| 6826 | case BuiltinFnIdWasmMemorySize: | |
| 6827 | { | |
| 6828 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 6829 | IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 6830 | if (arg0_value == irb->codegen->invalid_inst_src) | |
| 6831 | return arg0_value; | |
| 6832 | ||
| 6833 | IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value); | |
| 6834 | return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); | |
| 6835 | } | |
| 6836 | case BuiltinFnIdWasmMemoryGrow: | |
| 6837 | { | |
| 6838 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 6839 | IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 6840 | if (arg0_value == irb->codegen->invalid_inst_src) | |
| 6841 | return arg0_value; | |
| 6842 | ||
| 6843 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 6844 | IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 6845 | if (arg1_value == irb->codegen->invalid_inst_src) | |
| 6846 | return arg1_value; | |
| 6847 | ||
| 6848 | IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value); | |
| 6849 | return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); | |
| 6850 | } | |
| 6757 | 6851 | case BuiltinFnIdField: |
| 6758 | 6852 | { |
| 6759 | 6853 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -27651,6 +27745,56 @@ static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasF |
| 27651 | 27745 | return ir_const_bool(ira, &instruction->base.base, result); |
| 27652 | 27746 | } |
| 27653 | 27747 | |
| 27748 | static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { | |
| 27749 | // TODO generate compile error for target_arch different than 32bit | |
| 27750 | if (!target_is_wasm(ira->codegen->zig_target)) { | |
| 27751 | ir_add_error_node(ira, instruction->base.base.source_node, | |
| 27752 | buf_sprintf("@wasmMemorySize is a wasm32 feature only")); | |
| 27753 | return ira->codegen->invalid_inst_gen; | |
| 27754 | } | |
| 27755 | ||
| 27756 | IrInstGen *index = instruction->index->child; | |
| 27757 | if (type_is_invalid(index->value->type)) | |
| 27758 | return ira->codegen->invalid_inst_gen; | |
| 27759 | ||
| 27760 | ZigType *u32 = ira->codegen->builtin_types.entry_u32; | |
| 27761 | ||
| 27762 | IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); | |
| 27763 | if (type_is_invalid(casted_index->value->type)) | |
| 27764 | return ira->codegen->invalid_inst_gen; | |
| 27765 | ||
| 27766 | return ir_build_wasm_memory_size_gen(ira, &instruction->base.base, casted_index); | |
| 27767 | } | |
| 27768 | ||
| 27769 | static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { | |
| 27770 | // TODO generate compile error for target_arch different than 32bit | |
| 27771 | if (!target_is_wasm(ira->codegen->zig_target)) { | |
| 27772 | ir_add_error_node(ira, instruction->base.base.source_node, | |
| 27773 | buf_sprintf("@wasmMemoryGrow is a wasm32 feature only")); | |
| 27774 | return ira->codegen->invalid_inst_gen; | |
| 27775 | } | |
| 27776 | ||
| 27777 | IrInstGen *index = instruction->index->child; | |
| 27778 | if (type_is_invalid(index->value->type)) | |
| 27779 | return ira->codegen->invalid_inst_gen; | |
| 27780 | ||
| 27781 | ZigType *u32 = ira->codegen->builtin_types.entry_u32; | |
| 27782 | ||
| 27783 | IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); | |
| 27784 | if (type_is_invalid(casted_index->value->type)) | |
| 27785 | return ira->codegen->invalid_inst_gen; | |
| 27786 | ||
| 27787 | IrInstGen *delta = instruction->delta->child; | |
| 27788 | if (type_is_invalid(delta->value->type)) | |
| 27789 | return ira->codegen->invalid_inst_gen; | |
| 27790 | ||
| 27791 | IrInstGen *casted_delta = ir_implicit_cast(ira, delta, u32); | |
| 27792 | if (type_is_invalid(casted_delta->value->type)) | |
| 27793 | return ira->codegen->invalid_inst_gen; | |
| 27794 | ||
| 27795 | return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_index, casted_delta); | |
| 27796 | } | |
| 27797 | ||
| 27654 | 27798 | static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { |
| 27655 | 27799 | return ir_build_breakpoint_gen(ira, &instruction->base.base); |
| 27656 | 27800 | } |
| ... | ... | @@ -30892,6 +31036,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 30892 | 31036 | return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); |
| 30893 | 31037 | case IrInstSrcIdSpillEnd: |
| 30894 | 31038 | return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); |
| 31039 | case IrInstSrcIdWasmMemorySize: | |
| 31040 | return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); | |
| 31041 | case IrInstSrcIdWasmMemoryGrow: | |
| 31042 | return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction); | |
| 30895 | 31043 | } |
| 30896 | 31044 | zig_unreachable(); |
| 30897 | 31045 | } |
| ... | ... | @@ -31068,6 +31216,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { |
| 31068 | 31216 | case IrInstGenIdResume: |
| 31069 | 31217 | case IrInstGenIdAwait: |
| 31070 | 31218 | case IrInstGenIdSpillBegin: |
| 31219 | case IrInstGenIdWasmMemoryGrow: | |
| 31071 | 31220 | return true; |
| 31072 | 31221 | |
| 31073 | 31222 | case IrInstGenIdPhi: |
| ... | ... | @@ -31117,6 +31266,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { |
| 31117 | 31266 | case IrInstGenIdBinaryNot: |
| 31118 | 31267 | case IrInstGenIdNegation: |
| 31119 | 31268 | case IrInstGenIdNegationWrapping: |
| 31269 | case IrInstGenIdWasmMemorySize: | |
| 31120 | 31270 | return false; |
| 31121 | 31271 | |
| 31122 | 31272 | case IrInstGenIdAsm: |
| ... | ... | @@ -31199,6 +31349,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 31199 | 31349 | case IrInstSrcIdResume: |
| 31200 | 31350 | case IrInstSrcIdAwait: |
| 31201 | 31351 | case IrInstSrcIdSpillBegin: |
| 31352 | case IrInstSrcIdWasmMemoryGrow: | |
| 31202 | 31353 | return true; |
| 31203 | 31354 | |
| 31204 | 31355 | case IrInstSrcIdPhi: |
| ... | ... | @@ -31283,6 +31434,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 31283 | 31434 | case IrInstSrcIdHasDecl: |
| 31284 | 31435 | case IrInstSrcIdAlloca: |
| 31285 | 31436 | case IrInstSrcIdSpillEnd: |
| 31437 | case IrInstSrcIdWasmMemorySize: | |
| 31286 | 31438 | return false; |
| 31287 | 31439 | |
| 31288 | 31440 | case IrInstSrcIdAsm: |
src/ir_print.cpp+48| ... | ... | @@ -321,6 +321,10 @@ 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"; | |
| 326 | case IrInstSrcIdWasmMemoryGrow: | |
| 327 | return "SrcWasmMemoryGrow"; | |
| 324 | 328 | } |
| 325 | 329 | zig_unreachable(); |
| 326 | 330 | } |
| ... | ... | @@ -501,6 +505,10 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { |
| 501 | 505 | return "GenNegation"; |
| 502 | 506 | case IrInstGenIdNegationWrapping: |
| 503 | 507 | return "GenNegationWrapping"; |
| 508 | case IrInstGenIdWasmMemorySize: | |
| 509 | return "GenWasmMemorySize"; | |
| 510 | case IrInstGenIdWasmMemoryGrow: | |
| 511 | return "GenWasmMemoryGrow"; | |
| 504 | 512 | } |
| 505 | 513 | zig_unreachable(); |
| 506 | 514 | } |
| ... | ... | @@ -1708,6 +1716,34 @@ static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { |
| 1708 | 1716 | ir_print_other_inst_gen(irp, instruction->value); |
| 1709 | 1717 | } |
| 1710 | 1718 | |
| 1719 | static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { | |
| 1720 | fprintf(irp->f, "@wasmMemorySize("); | |
| 1721 | ir_print_other_inst_src(irp, instruction->index); | |
| 1722 | fprintf(irp->f, ")"); | |
| 1723 | } | |
| 1724 | ||
| 1725 | static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { | |
| 1726 | fprintf(irp->f, "@wasmMemorySize("); | |
| 1727 | ir_print_other_inst_gen(irp, instruction->index); | |
| 1728 | fprintf(irp->f, ")"); | |
| 1729 | } | |
| 1730 | ||
| 1731 | static void ir_print_wasm_memory_grow(IrPrintSrc *irp, IrInstSrcWasmMemoryGrow *instruction) { | |
| 1732 | fprintf(irp->f, "@wasmMemoryGrow("); | |
| 1733 | ir_print_other_inst_src(irp, instruction->index); | |
| 1734 | fprintf(irp->f, ", "); | |
| 1735 | ir_print_other_inst_src(irp, instruction->delta); | |
| 1736 | fprintf(irp->f, ")"); | |
| 1737 | } | |
| 1738 | ||
| 1739 | static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *instruction) { | |
| 1740 | fprintf(irp->f, "@wasmMemoryGrow("); | |
| 1741 | ir_print_other_inst_gen(irp, instruction->index); | |
| 1742 | fprintf(irp->f, ", "); | |
| 1743 | ir_print_other_inst_gen(irp, instruction->delta); | |
| 1744 | fprintf(irp->f, ")"); | |
| 1745 | } | |
| 1746 | ||
| 1711 | 1747 | static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { |
| 1712 | 1748 | fprintf(irp->f, "@memset("); |
| 1713 | 1749 | ir_print_other_inst_src(irp, instruction->dest_ptr); |
| ... | ... | @@ -2952,6 +2988,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2952 | 2988 | case IrInstSrcIdClz: |
| 2953 | 2989 | ir_print_clz(irp, (IrInstSrcClz *)instruction); |
| 2954 | 2990 | break; |
| 2991 | case IrInstSrcIdWasmMemorySize: | |
| 2992 | ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); | |
| 2993 | break; | |
| 2994 | case IrInstSrcIdWasmMemoryGrow: | |
| 2995 | ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction); | |
| 2996 | break; | |
| 2955 | 2997 | } |
| 2956 | 2998 | fprintf(irp->f, "\n"); |
| 2957 | 2999 | } |
| ... | ... | @@ -3219,6 +3261,12 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai |
| 3219 | 3261 | case IrInstGenIdNegationWrapping: |
| 3220 | 3262 | ir_print_negation_wrapping(irp, (IrInstGenNegationWrapping *)instruction); |
| 3221 | 3263 | break; |
| 3264 | case IrInstGenIdWasmMemorySize: | |
| 3265 | ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); | |
| 3266 | break; | |
| 3267 | case IrInstGenIdWasmMemoryGrow: | |
| 3268 | ir_print_wasm_memory_grow(irp, (IrInstGenWasmMemoryGrow *)instruction); | |
| 3269 | break; | |
| 3222 | 3270 | } |
| 3223 | 3271 | fprintf(irp->f, "\n"); |
| 3224 | 3272 | } |
test/compile_errors.zig+18| ... | ... | @@ -7504,4 +7504,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 7504 | 7504 | , &[_][]const u8{ |
| 7505 | 7505 | ":3:52: error: slice '[]const u8' cannot have its bytes reinterpreted", |
| 7506 | 7506 | }); |
| 7507 | ||
| 7508 | cases.add("wasmMemorySize is a compile error in non-Wasm targets", | |
| 7509 | \\export fn foo() void { | |
| 7510 | \\ _ = @wasmMemorySize(0); | |
| 7511 | \\ return; | |
| 7512 | \\} | |
| 7513 | , &[_][]const u8{ | |
| 7514 | "tmp.zig:2:9: error: @wasmMemorySize is a wasm32 feature only", | |
| 7515 | }); | |
| 7516 | ||
| 7517 | cases.add("wasmMemoryGrow is a compile error in non-Wasm targets", | |
| 7518 | \\export fn foo() void { | |
| 7519 | \\ _ = @wasmMemoryGrow(0, 1); | |
| 7520 | \\ return; | |
| 7521 | \\} | |
| 7522 | , &[_][]const u8{ | |
| 7523 | "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only", | |
| 7524 | }); | |
| 7507 | 7525 | } |
test/stage1/behavior.zig+3| ... | ... | @@ -126,6 +126,9 @@ comptime { |
| 126 | 126 | _ = @import("behavior/var_args.zig"); |
| 127 | 127 | _ = @import("behavior/vector.zig"); |
| 128 | 128 | _ = @import("behavior/void.zig"); |
| 129 | if (builtin.arch == .wasm32) { | |
| 130 | _ = @import("behavior/wasm.zig"); | |
| 131 | } | |
| 129 | 132 | _ = @import("behavior/while.zig"); |
| 130 | 133 | _ = @import("behavior/widening.zig"); |
| 131 | 134 | } |
test/stage1/behavior/wasm.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | test "memory size and grow" { | |
| 5 | var prev = @wasmMemorySize(0); | |
| 6 | expect(prev == @wasmMemoryGrow(0, 1)); | |
| 7 | expect(prev + 1 == @wasmMemorySize(0)); | |
| 8 | } |