diff --git a/src/Air.zig b/src/Air.zig index 874af09b71..268b6c8631 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -584,10 +584,13 @@ pub const Inst = struct { field_parent_ptr, /// Implements @wasmMemorySize builtin. - /// Uses the `ty_pl` field, payload represents the index of the target memory. + /// Result type is always `u32`, + /// Uses the `pl_op` field, payload represents the index of the target memory. + /// The operand is unused and always set to `Ref.none`. wasm_memory_size, /// Implements @wasmMemoryGrow builtin. + /// Result type is always `i32`, /// Uses the `pl_op` field, payload represents the index of the target memory. wasm_memory_grow, @@ -626,6 +629,7 @@ pub const Inst = struct { pub const Data = union { no_op: void, un_op: Ref, + bin_op: struct { lhs: Ref, rhs: Ref, @@ -885,7 +889,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .aggregate_init, .union_init, .field_parent_ptr, - .wasm_memory_size, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -954,7 +957,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .frame_addr, => return Type.initTag(.usize), - .wasm_memory_grow => return Type.initTag(.i32), + .wasm_memory_grow => return Type.i32, + .wasm_memory_size => return Type.u32, .bool_to_int => return Type.initTag(.u1), diff --git a/src/AstGen.zig b/src/AstGen.zig index d6d29d4a54..3fe9630c75 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7140,7 +7140,7 @@ fn builtinCall( // zig fmt: on .wasm_memory_size => { - const operand = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]); + const operand = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]); const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), .operand = operand, @@ -7148,8 +7148,8 @@ fn builtinCall( return rvalue(gz, rl, result, node); }, .wasm_memory_grow => { - const index_arg = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]); - const delta_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[1]); + const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]); + const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]); const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), .lhs = index_arg, diff --git a/src/Sema.zig b/src/Sema.zig index 65536cdbde..eb5265d404 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14033,18 +14033,19 @@ fn zirWasmMemorySize( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; - if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) { - return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{}); + const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const builtin_src: LazySrcLoc = .{ .node_offset = extra.node }; + const target = sema.mod.getTarget(); + if (!target.isWasm()) { + return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const operand = try sema.resolveInt(block, src, extra.operand, Type.u32); - const index = @intCast(u32, operand); - try sema.requireRuntimeBlock(block, src); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32)); + try sema.requireRuntimeBlock(block, builtin_src); return block.addInst(.{ .tag = .wasm_memory_size, - .data = .{ .ty_pl = .{ - .ty = try sema.addType(Type.u32), + .data = .{ .pl_op = .{ + .operand = .none, .payload = index, } }, }); @@ -14056,20 +14057,22 @@ fn zirWasmMemoryGrow( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; - if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) { - return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{}); + const builtin_src: LazySrcLoc = .{ .node_offset = extra.node }; + const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; + const target = sema.mod.getTarget(); + if (!target.isWasm()) { + return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const index_arg = try sema.resolveInt(block, src, extra.lhs, Type.u32); - const index = @intCast(u32, index_arg); - const delta_arg = sema.resolveInst(extra.rhs); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32)); + const delta = try sema.coerce(block, Type.u32, sema.resolveInst(extra.rhs), delta_src); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, builtin_src); return block.addInst(.{ .tag = .wasm_memory_grow, .data = .{ .pl_op = .{ - .operand = delta_arg, + .operand = delta, .payload = index, } }, }); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b1c4be9fef..ca571370ad 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3430,10 +3430,12 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; const result = try self.allocLocal(self.air.typeOfIndex(inst)); - try self.addLabel(.memory_size, ty_pl.payload); + try self.addLabel(.memory_size, pl_op.payload); try self.addLabel(.local_set, result.local); return result; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a36b58041c..ba7bb6fa3a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3592,14 +3592,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { } fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { - const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + if (f.liveness.isUnused(inst)) return CValue.none; + + const pl_op = f.air.instructions.items(.data)[inst].pl_op; const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try writer.print("zig_wasm_memory_size({d});\n", .{ty_pl.payload}); + try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload}); return local; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bf754c975b..368a67f4b4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3478,13 +3478,27 @@ pub const FuncGen = struct { } fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - _ = inst; - return self.todo("implement builtin `@wasmMemorySize()`", .{}); + if (self.liveness.isUnused(inst)) return null; + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const index = pl_op.payload; + const llvm_u32 = self.context.intType(32); + const llvm_fn = self.getIntrinsic("llvm.wasm.memory.size.i32", &.{llvm_u32}); + const args: [1]*const llvm.Value = .{llvm_u32.constInt(index, .False)}; + return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, ""); } fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - _ = inst; - return self.todo("implement builtin `@wasmMemoryGrow()`", .{}); + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const index = pl_op.payload; + const operand = try self.resolveInst(pl_op.operand); + const llvm_u32 = self.context.intType(32); + const llvm_fn = self.getIntrinsic("llvm.wasm.memory.grow.i32", &.{ llvm_u32, llvm_u32 }); + const args: [2]*const llvm.Value = .{ + llvm_u32.constInt(index, .False), + operand, + }; + return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, ""); } fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/src/link/C/zig.h b/src/link/C/zig.h index bf94f44c8f..873daafd51 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -91,14 +91,10 @@ #if defined(__clang__) #define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index) -#else -#define zig_wasm_memory_size(index) 0 -#endif - -#if defined(__clang__) #define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta) #else -#define zig_wasm_memory_grow(index, delta) 0 +#define zig_wasm_memory_size(index) zig_unimplemented() +#define zig_wasm_memory_grow(index, delta) zig_unimplemented() #endif #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) diff --git a/src/print_air.zig b/src/print_air.zig index 3158daee6d..2149be764a 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -626,8 +626,8 @@ const Writer = struct { } fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; - try s.print("{d}", .{ty_pl.payload}); + const pl_op = w.air.instructions.items(.data)[inst].pl_op; + try s.print("{d}", .{pl_op.payload}); } fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/type.zig b/src/type.zig index ef794ffe74..169574bbd4 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5256,6 +5256,8 @@ pub const Type = extern union { pub const @"u32" = initTag(.u32); pub const @"u64" = initTag(.u64); + pub const @"i32" = initTag(.i32); + pub const @"f16" = initTag(.f16); pub const @"f32" = initTag(.f32); pub const @"f64" = initTag(.f64); diff --git a/test/behavior.zig b/test/behavior.zig index 7cbbf96d17..e1cc491d48 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -96,7 +96,7 @@ test { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); - if (builtin.zig_backend == .stage2_wasm) { + if (builtin.stage2_arch == .wasm32) { _ = @import("behavior/wasm.zig"); } @@ -172,9 +172,6 @@ test { _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/vector.zig"); - if (builtin.target.cpu.arch == .wasm32) { - _ = @import("behavior/wasm.zig"); - } } } } diff --git a/test/behavior/wasm.zig b/test/behavior/wasm.zig index ca8a0892dd..11bf6cff05 100644 --- a/test/behavior/wasm.zig +++ b/test/behavior/wasm.zig @@ -3,8 +3,6 @@ const expect = std.testing.expect; const builtin = @import("builtin"); test "memory size and grow" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - var prev = @wasmMemorySize(0); try expect(prev == @wasmMemoryGrow(0, 1)); try expect(prev + 1 == @wasmMemorySize(0));