diff --git a/doc/langref.html.in b/doc/langref.html.in
index ad5a688a0432..e5c814d66327 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -7643,6 +7643,49 @@ 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.WasmPageAllocator{#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.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));
+}
+ {#code_end#}
+ {#see_also|@wasmMemorySize#}
+ {#header_close#}
+
{#header_open|@mod#}
{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index 6bbb688ef039..f05378c21587 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(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 61b6cbd74a52..9413ea73a4a7 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1825,6 +1825,8 @@ enum BuiltinFnId {
BuiltinFnIdAs,
BuiltinFnIdCall,
BuiltinFnIdBitSizeof,
+ BuiltinFnIdWasmMemorySize,
+ BuiltinFnIdWasmMemoryGrow,
};
struct BuiltinFnEntry {
@@ -2075,6 +2077,8 @@ struct CodeGen {
LLVMValueRef err_name_table;
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
@@ -2748,6 +2752,8 @@ enum IrInstSrcId {
IrInstSrcIdResume,
IrInstSrcIdSpillBegin,
IrInstSrcIdSpillEnd,
+ IrInstSrcIdWasmMemorySize,
+ IrInstSrcIdWasmMemoryGrow,
};
// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
@@ -2840,6 +2846,8 @@ enum IrInstGenId {
IrInstGenIdVectorExtractElem,
IrInstGenIdAlloca,
IrInstGenIdConst,
+ IrInstGenIdWasmMemorySize,
+ IrInstGenIdWasmMemoryGrow,
};
// Common fields between IrInstSrc and IrInstGen. This allows future passes
@@ -3727,6 +3735,32 @@ struct IrInstGenMemcpy {
IrInstGen *count;
};
+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;
+};
+
struct IrInstSrcSlice {
IrInstSrc base;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 92317f09650d..f945dd65457a 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1045,6 +1045,37 @@ 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;
+
+ // 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);
+ 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 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;
@@ -5588,6 +5619,23 @@ 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) {
+ // TODO adjust for wasm64
+ 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) {
+ // TODO adjust for wasm64
+ LLVMValueRef params[] = {
+ ir_llvm_value(g, instruction->index),
+ 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;
@@ -6798,6 +6846,10 @@ 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);
+ case IrInstGenIdWasmMemoryGrow:
+ return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction);
}
zig_unreachable();
}
@@ -8660,6 +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", 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 54442861ca51..9ae0d713d80a 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -556,6 +556,10 @@ 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));
+ case IrInstSrcIdWasmMemoryGrow:
+ return heap::c_allocator.destroy(reinterpret_cast(inst));
}
zig_unreachable();
}
@@ -736,6 +740,10 @@ 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));
+ case IrInstGenIdWasmMemoryGrow:
+ return heap::c_allocator.destroy(reinterpret_cast(inst));
}
zig_unreachable();
}
@@ -1610,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;
@@ -1955,6 +1971,14 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) {
return IrInstGenIdConst;
}
+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();
@@ -4961,6 +4985,51 @@ 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, 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, 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->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 *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 *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;
+}
+
+
static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -6754,6 +6823,31 @@ 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:
+ {
+ 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:
+ {
+ 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;
+
+ 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:
{
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
return ir_const_bool(ira, &instruction->base.base, result);
}
+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 wasm32 feature only"));
+ return ira->codegen->invalid_inst_gen;
+ }
+
+ IrInstGen *index = instruction->index->child;
+ if (type_is_invalid(index->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ ZigType *u32 = ira->codegen->builtin_types.entry_u32;
+
+ IrInstGen *casted_index = ir_implicit_cast(ira, index, u32);
+ 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) {
+ // 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 *index = instruction->index->child;
+ if (type_is_invalid(index->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ ZigType *u32 = ira->codegen->builtin_types.entry_u32;
+
+ IrInstGen *casted_index = ir_implicit_cast(ira, index, u32);
+ 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, u32);
+ 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_index, casted_delta);
+}
+
static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) {
return ir_build_breakpoint_gen(ira, &instruction->base.base);
}
@@ -30879,6 +31023,10 @@ 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);
+ case IrInstSrcIdWasmMemoryGrow:
+ return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction);
}
zig_unreachable();
}
@@ -31055,6 +31203,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) {
case IrInstGenIdResume:
case IrInstGenIdAwait:
case IrInstGenIdSpillBegin:
+ case IrInstGenIdWasmMemoryGrow:
return true;
case IrInstGenIdPhi:
@@ -31104,6 +31253,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) {
case IrInstGenIdBinaryNot:
case IrInstGenIdNegation:
case IrInstGenIdNegationWrapping:
+ case IrInstGenIdWasmMemorySize:
return false;
case IrInstGenIdAsm:
@@ -31186,6 +31336,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
case IrInstSrcIdResume:
case IrInstSrcIdAwait:
case IrInstSrcIdSpillBegin:
+ case IrInstSrcIdWasmMemoryGrow:
return true;
case IrInstSrcIdPhi:
@@ -31270,6 +31421,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 3e50b7304fbe..c826d76e0358 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -321,6 +321,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
return "SrcSpillBegin";
case IrInstSrcIdSpillEnd:
return "SrcSpillEnd";
+ case IrInstSrcIdWasmMemorySize:
+ return "SrcWasmMemorySize";
+ case IrInstSrcIdWasmMemoryGrow:
+ return "SrcWasmMemoryGrow";
}
zig_unreachable();
}
@@ -501,6 +505,10 @@ const char* ir_inst_gen_type_str(IrInstGenId id) {
return "GenNegation";
case IrInstGenIdNegationWrapping:
return "GenNegationWrapping";
+ case IrInstGenIdWasmMemorySize:
+ return "GenWasmMemorySize";
+ case IrInstGenIdWasmMemoryGrow:
+ return "GenWasmMemoryGrow";
}
zig_unreachable();
}
@@ -1708,6 +1716,34 @@ 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(");
+ 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(");
+ 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, ")");
+}
+
static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) {
fprintf(irp->f, "@memset(");
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
case IrInstSrcIdClz:
ir_print_clz(irp, (IrInstSrcClz *)instruction);
break;
+ 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");
}
@@ -3219,6 +3261,12 @@ 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;
+ case IrInstGenIdWasmMemoryGrow:
+ ir_print_wasm_memory_grow(irp, (IrInstGenWasmMemoryGrow *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 7fbe3f284d34..603f6be63619 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -7495,4 +7495,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
":22:12: error: cannot compare types '?[3]i32' and '[3]i32'",
":22:12: note: operator not supported for type '[3]i32'",
});
+
+ cases.add("wasmMemorySize is a compile error in non-Wasm targets",
+ \\export fn foo() void {
+ \\ _ = @wasmMemorySize(0);
+ \\ 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(0, 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 2961ffee47f3..dc3b4442a515 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -125,6 +125,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 000000000000..24557ee19bb0
--- /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(0);
+ expect(prev == @wasmMemoryGrow(0, 1));
+ expect(prev + 1 == @wasmMemorySize(0));
+}