From ec4c30ae483e6700a1fd1d5edaadbb042790c52e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 2 Mar 2022 22:18:45 +0100 Subject: wasm: Implement `@wasmMemorySize()` builtin This implements the `wasmMemorySize` builtin, in Sema and the Wasm backend. The Stage2 implementation differs from stage1 in the way that `index` must be a comptime value. The stage1 variant is incorrect, as the index is part of the instruction encoding, and therefore, cannot be a runtime value. --- src/codegen/llvm.zig | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/codegen') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 252d449fc2..a22d8e01b0 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2314,6 +2314,8 @@ pub const FuncGen = struct { .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + .wasm_memory_size => try self.airWasmMemorySize(inst), + .constant => unreachable, .const_ty => unreachable, .unreach => self.airUnreach(inst), @@ -3474,6 +3476,11 @@ pub const FuncGen = struct { return partial; } + fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + _ = inst; + return self.todo("`@wasmMemorySize()`", .{}); + } + fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; -- cgit v1.2.3 From 43cb19ea4da63dcaa8a18a06e3ab23f1c822c1fe Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Mar 2022 19:10:58 +0100 Subject: wasm: Implement `@wasmMemoryGrow` builtin Similarly to the other wasm builtin, this implements the grow variation where the memory index is a comptime known value. The operand as well as the result are runtime values. This also verifies during semantic analysis the target we're building for is wasm, or else emits a compilation error. This means that other backends do not have to handle this AIR instruction, other than the wasm and LLVM backends. --- src/Air.zig | 6 ++++++ src/AstGen.zig | 2 +- src/Liveness.zig | 4 ++++ src/Sema.zig | 20 +++++++++++++++++++- src/arch/aarch64/CodeGen.zig | 3 +++ src/arch/arm/CodeGen.zig | 3 +++ src/arch/riscv64/CodeGen.zig | 3 +++ src/arch/wasm/CodeGen.zig | 13 +++++++++++++ src/arch/x86_64/CodeGen.zig | 3 +++ src/codegen/c.zig | 3 +++ src/codegen/llvm.zig | 7 ++++++- src/print_air.zig | 13 +++++++++++-- 12 files changed, 75 insertions(+), 5 deletions(-) (limited to 'src/codegen') diff --git a/src/Air.zig b/src/Air.zig index 0a8771f6ac..e00720f534 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -587,6 +587,10 @@ pub const Inst = struct { /// Uses the `ty_pl` field, payload is `WasmMemoryIndex`. wasm_memory_size, + /// Implements @wasmMemoryGrow builtin. + /// Uses the `pl_op` field, payload is `WasmMemoryIndex`. + wasm_memory_grow, + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { .lt => .cmp_lt, @@ -956,6 +960,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .frame_addr, => return Type.initTag(.usize), + .wasm_memory_grow => return Type.initTag(.i32), + .bool_to_int => return Type.initTag(.u1), .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0), diff --git a/src/AstGen.zig b/src/AstGen.zig index a82d9dc360..d6d29d4a54 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7148,7 +7148,7 @@ fn builtinCall( return rvalue(gz, rl, result, node); }, .wasm_memory_grow => { - const index_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[0]); + 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 result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), diff --git a/src/Liveness.zig b/src/Liveness.zig index e90a9ab10e..7f007b5718 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -707,6 +707,10 @@ fn analyzeInst( return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none }); }, + .wasm_memory_grow => { + const pl_op = inst_datas[inst].pl_op; + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none }); + }, } } diff --git a/src/Sema.zig b/src/Sema.zig index 3dbbdaeb27..a8dc872a99 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14034,6 +14034,9 @@ fn zirWasmMemorySize( ) 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()) { + return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{}); + } const operand = try sema.resolveInt(block, src, extra.operand, Type.u32); const index = @intCast(u32, operand); @@ -14054,7 +14057,22 @@ fn zirWasmMemoryGrow( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{}); + if (!sema.mod.getTarget().isWasm()) { + return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{}); + } + + 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); + + try sema.requireRuntimeBlock(block, src); + return block.addInst(.{ + .tag = .wasm_memory_grow, + .data = .{ .pl_op = .{ + .operand = delta_arg, + .payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }), + } }, + }); } fn zirPrefetch( diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8c214f0091..19b444cb15 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -671,6 +671,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 366cfe67b1..04dc68db5a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -669,6 +669,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a51b8b40cc..15600c09dd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -642,6 +642,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } if (std.debug.runtime_safety) { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0ea0418d7f..b4917738dd 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1672,6 +1672,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .wrap_errunion_err => self.airWrapErrUnionErr(inst), .wasm_memory_size => self.airWasmMemorySize(inst), + .wasm_memory_grow => self.airWasmMemoryGrow(inst), .add_sat, .sub_sat, @@ -3438,6 +3439,18 @@ fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue { return result; } +fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; + const operand = try self.resolveInst(pl_op.operand); + + const result = try self.allocLocal(Type.usize); + try self.emitWValue(operand); + try self.addLabel(.memory_grow, extra.index); + try self.addLabel(.local_set, result.local); + return result; +} + fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { assert(operand_ty.hasRuntimeBits()); assert(op == .eq or op == .neq); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 33fd2c8321..fcffb6ddda 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -759,6 +759,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3e47637c76..988576a6f1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1758,6 +1758,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), .wrap_errunion_err => try airWrapErrUnionErr(f, inst), .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on }; switch (result_value) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a22d8e01b0..3e1be05d55 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3478,7 +3478,12 @@ pub const FuncGen = struct { fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { _ = inst; - return self.todo("`@wasmMemorySize()`", .{}); + return self.todo("implement builtin `@wasmMemorySize()`", .{}); + } + + fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + _ = inst; + return self.todo("implement builtin `@wasmMemoryGrow()`", .{}); } fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/src/print_air.zig b/src/print_air.zig index 4129fc5c43..3dd1decf53 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -251,6 +251,7 @@ const Writer = struct { .memset => try w.writeMemset(s, inst), .field_parent_ptr => try w.writeFieldParentPtr(s, inst), .wasm_memory_size => try w.writeWasmMemorySize(s, inst), + .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), .add_with_overflow, .sub_with_overflow, @@ -625,10 +626,18 @@ const Writer = struct { } fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[inst].ty_pl; + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.WasmMemoryIndex, ty_pl.payload).data; + + try s.print("{d}", .{extra.index}); + } + + fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; - try s.print(", {d}", .{extra.index}); + try s.print("{d}, ", .{extra.index}); + try w.writeOperand(s, inst, 0, pl_op.operand); } fn writeOperand( -- cgit v1.2.3 From 21f0503c0137b7bb59edd87e17e1649152d342ba Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Mar 2022 19:16:30 +0100 Subject: Update behavior tests --- src/codegen/llvm.zig | 1 + test/behavior.zig | 4 ++++ test/behavior/wasm.zig | 3 +++ 3 files changed, 8 insertions(+) (limited to 'src/codegen') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3e1be05d55..bf754c975b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2315,6 +2315,7 @@ pub const FuncGen = struct { .wrap_errunion_err => try self.airWrapErrUnionErr(inst), .wasm_memory_size => try self.airWasmMemorySize(inst), + .wasm_memory_grow => try self.airWasmMemoryGrow(inst), .constant => unreachable, .const_ty => unreachable, diff --git a/test/behavior.zig b/test/behavior.zig index 7be45e638c..7cbbf96d17 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -96,6 +96,10 @@ test { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); + if (builtin.zig_backend == .stage2_wasm) { + _ = @import("behavior/wasm.zig"); + } + if (builtin.zig_backend != .stage1) { _ = @import("behavior/decltest.zig"); } diff --git a/test/behavior/wasm.zig b/test/behavior/wasm.zig index 2190295514..ca8a0892dd 100644 --- a/test/behavior/wasm.zig +++ b/test/behavior/wasm.zig @@ -1,7 +1,10 @@ const std = @import("std"); 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)); -- cgit v1.2.3 From 7fd32de0180c29dc4e3da72e9347f6f5ce167a38 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Mar 2022 22:12:53 +0100 Subject: cbe: Implement wasm builtins This implements the wasm builtins by lowering to builtins that are supported by c-compilers. In this case: Clang. This also simplifies the `AIR` instruction as it now uses the payload field of `ty_pl` and `pl_op` directly to store the index argument rather than storing it inside Extra. This saves us 4 bytes per builtin call. --- src/Air.zig | 10 ++-------- src/Sema.zig | 8 ++++---- src/arch/wasm/CodeGen.zig | 10 ++++------ src/codegen/c.zig | 32 ++++++++++++++++++++++++++++++-- src/link/C/zig.h | 12 ++++++++++++ src/print_air.zig | 8 ++------ 6 files changed, 54 insertions(+), 26 deletions(-) (limited to 'src/codegen') diff --git a/src/Air.zig b/src/Air.zig index e00720f534..874af09b71 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -584,11 +584,11 @@ pub const Inst = struct { field_parent_ptr, /// Implements @wasmMemorySize builtin. - /// Uses the `ty_pl` field, payload is `WasmMemoryIndex`. + /// Uses the `ty_pl` field, payload represents the index of the target memory. wasm_memory_size, /// Implements @wasmMemoryGrow builtin. - /// Uses the `pl_op` field, payload is `WasmMemoryIndex`. + /// Uses the `pl_op` field, payload represents the index of the target memory. wasm_memory_grow, pub fn fromCmpOp(op: std.math.CompareOperator) Tag { @@ -725,12 +725,6 @@ pub const FieldParentPtr = struct { field_index: u32, }; -/// Wasm's memory instructions require a comptime-known index -/// which represents the memory it operates on. -pub const WasmMemoryIndex = struct { - index: u32, -}; - /// Trailing: /// 0. `Inst.Ref` for every outputs_len /// 1. `Inst.Ref` for every inputs_len diff --git a/src/Sema.zig b/src/Sema.zig index a8dc872a99..65536cdbde 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14034,7 +14034,7 @@ fn zirWasmMemorySize( ) 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()) { + 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", .{}); } @@ -14045,7 +14045,7 @@ fn zirWasmMemorySize( .tag = .wasm_memory_size, .data = .{ .ty_pl = .{ .ty = try sema.addType(Type.u32), - .payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }), + .payload = index, } }, }); } @@ -14057,7 +14057,7 @@ fn zirWasmMemoryGrow( ) 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()) { + 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", .{}); } @@ -14070,7 +14070,7 @@ fn zirWasmMemoryGrow( .tag = .wasm_memory_grow, .data = .{ .pl_op = .{ .operand = delta_arg, - .payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }), + .payload = index, } }, }); } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b4917738dd..b1c4be9fef 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3431,22 +3431,20 @@ 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; - const extra = self.air.extraData(Air.WasmMemoryIndex, ty_pl.payload).data; - const result = try self.allocLocal(Type.usize); - try self.addLabel(.memory_size, extra.index); + const result = try self.allocLocal(self.air.typeOfIndex(inst)); + try self.addLabel(.memory_size, ty_pl.payload); try self.addLabel(.local_set, result.local); return result; } fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; const operand = try self.resolveInst(pl_op.operand); - const result = try self.allocLocal(Type.usize); + const result = try self.allocLocal(self.air.typeOfIndex(inst)); try self.emitWValue(operand); - try self.addLabel(.memory_grow, extra.index); + try self.addLabel(.memory_grow, 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 988576a6f1..a36b58041c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1759,8 +1759,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .wrap_errunion_err => try airWrapErrUnionErr(f, inst), .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst), - .wasm_memory_size => unreachable, - .wasm_memory_grow => unreachable, + .wasm_memory_size => try airWasmMemorySize(f, inst), + .wasm_memory_grow => try airWasmMemoryGrow(f, inst), // zig fmt: on }; switch (result_value) { @@ -3591,6 +3591,34 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } +fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + + 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}); + + return local; +} + +fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(pl_op.operand); + const local = try f.allocLocal(inst_ty, .Const); + + try writer.writeAll(" = "); + try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload}); + try f.writeCValue(writer, operand); + try writer.writeAll(");\n"); + return local; +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { .Unordered => "memory_order_relaxed", diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 4a725ca678..bf94f44c8f 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -89,6 +89,18 @@ #define zig_prefetch(addr, rw, locality) #endif +#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 +#endif + #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) #include #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) diff --git a/src/print_air.zig b/src/print_air.zig index 3dd1decf53..3158daee6d 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -627,16 +627,12 @@ 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; - const extra = w.air.extraData(Air.WasmMemoryIndex, ty_pl.payload).data; - - try s.print("{d}", .{extra.index}); + try s.print("{d}", .{ty_pl.payload}); } fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; - const extra = w.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; - - try s.print("{d}, ", .{extra.index}); + try s.print("{d}, ", .{pl_op.payload}); try w.writeOperand(s, inst, 0, pl_op.operand); } -- cgit v1.2.3 From e532b0c0b5d55d212d885c779ab9f2fa7443e56a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Mar 2022 18:31:55 -0700 Subject: stage2: cleanups to wasm memory intrinsics * AIR: use pl_op instead of ty_pl for wasm_memory_size. No need to store the type because the type is always `u32`. * AstGen: use coerced_ty for `@wasmMemorySize` and `@wasmMemoryGrow` and do the coercions in Sema. * Sema: use more accurate source locations for errors. * Provide more information in the compiler error message. * Codegen: use liveness data to avoid lowering unused `@wasmMemorySize`. * LLVM backend: add implementation - I wasn't able to test it because we are hitting a linker error for `-target wasm32-wasi -fLLVM`. * C backend: use `zig_unimplemented()` instead of silently doing wrong behavior for these builtins. * behavior tests: branch only on stage2_arch for inclusion of the wasm.zig file. We would change it to `builtin.cpu.arch` but that is causing a compiler crash on some backends. --- src/Air.zig | 10 +++++++--- src/AstGen.zig | 6 +++--- src/Sema.zig | 35 +++++++++++++++++++---------------- src/arch/wasm/CodeGen.zig | 6 ++++-- src/codegen/c.zig | 6 ++++-- src/codegen/llvm.zig | 22 ++++++++++++++++++---- src/link/C/zig.h | 8 ++------ src/print_air.zig | 4 ++-- src/type.zig | 2 ++ test/behavior.zig | 5 +---- test/behavior/wasm.zig | 2 -- 11 files changed, 62 insertions(+), 44 deletions(-) (limited to 'src/codegen') 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)); -- cgit v1.2.3