authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 18:31:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 18:31:55-07:00
loge532b0c0b5d55d212d885c779ab9f2fa7443e56a
tree0a1e9b277c745518f2b64d8913fe5e065fe9cc1b
parent7fd32de0180c29dc4e3da72e9347f6f5ce167a38

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.

11 files changed, 62 insertions(+), 44 deletions(-)

src/Air.zig+7-3
...@@ -584,10 +584,13 @@ pub const Inst = struct {...@@ -584,10 +584,13 @@ pub const Inst = struct {
584 field_parent_ptr,584 field_parent_ptr,
585585
586 /// Implements @wasmMemorySize builtin.586 /// Implements @wasmMemorySize builtin.
587 /// Uses the `ty_pl` field, payload represents the index of the target memory.587 /// Result type is always `u32`,
588 /// Uses the `pl_op` field, payload represents the index of the target memory.
589 /// The operand is unused and always set to `Ref.none`.
588 wasm_memory_size,590 wasm_memory_size,
589591
590 /// Implements @wasmMemoryGrow builtin.592 /// Implements @wasmMemoryGrow builtin.
593 /// Result type is always `i32`,
591 /// Uses the `pl_op` field, payload represents the index of the target memory.594 /// Uses the `pl_op` field, payload represents the index of the target memory.
592 wasm_memory_grow,595 wasm_memory_grow,
593596
...@@ -626,6 +629,7 @@ pub const Inst = struct {...@@ -626,6 +629,7 @@ pub const Inst = struct {
626 pub const Data = union {629 pub const Data = union {
627 no_op: void,630 no_op: void,
628 un_op: Ref,631 un_op: Ref,
632
629 bin_op: struct {633 bin_op: struct {
630 lhs: Ref,634 lhs: Ref,
631 rhs: Ref,635 rhs: Ref,
...@@ -885,7 +889,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -885,7 +889,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
885 .aggregate_init,889 .aggregate_init,
886 .union_init,890 .union_init,
887 .field_parent_ptr,891 .field_parent_ptr,
888 .wasm_memory_size,
889 => return air.getRefType(datas[inst].ty_pl.ty),892 => return air.getRefType(datas[inst].ty_pl.ty),
890893
891 .not,894 .not,
...@@ -954,7 +957,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -954,7 +957,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
954 .frame_addr,957 .frame_addr,
955 => return Type.initTag(.usize),958 => return Type.initTag(.usize),
956959
957 .wasm_memory_grow => return Type.initTag(.i32),960 .wasm_memory_grow => return Type.i32,
961 .wasm_memory_size => return Type.u32,
958962
959 .bool_to_int => return Type.initTag(.u1),963 .bool_to_int => return Type.initTag(.u1),
960964
src/AstGen.zig+3-3
...@@ -7140,7 +7140,7 @@ fn builtinCall(...@@ -7140,7 +7140,7 @@ fn builtinCall(
7140 // zig fmt: on7140 // zig fmt: on
71417141
7142 .wasm_memory_size => {7142 .wasm_memory_size => {
7143 const operand = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]);7143 const operand = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
7144 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{7144 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
7145 .node = gz.nodeIndexToRelative(node),7145 .node = gz.nodeIndexToRelative(node),
7146 .operand = operand,7146 .operand = operand,
...@@ -7148,8 +7148,8 @@ fn builtinCall(...@@ -7148,8 +7148,8 @@ fn builtinCall(
7148 return rvalue(gz, rl, result, node);7148 return rvalue(gz, rl, result, node);
7149 },7149 },
7150 .wasm_memory_grow => {7150 .wasm_memory_grow => {
7151 const index_arg = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]);7151 const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
7152 const delta_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[1]);7152 const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]);
7153 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{7153 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
7154 .node = gz.nodeIndexToRelative(node),7154 .node = gz.nodeIndexToRelative(node),
7155 .lhs = index_arg,7155 .lhs = index_arg,
src/Sema.zig+19-16
...@@ -14033,18 +14033,19 @@ fn zirWasmMemorySize(...@@ -14033,18 +14033,19 @@ fn zirWasmMemorySize(
14033 extended: Zir.Inst.Extended.InstData,14033 extended: Zir.Inst.Extended.InstData,
14034) CompileError!Air.Inst.Ref {14034) CompileError!Air.Inst.Ref {
14035 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;14035 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
14036 const src: LazySrcLoc = .{ .node_offset = extra.node };14036 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
14037 if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {14037 const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
14038 return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{});14038 const target = sema.mod.getTarget();
14039 if (!target.isWasm()) {
14040 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
14039 }14041 }
1404014042
14041 const operand = try sema.resolveInt(block, src, extra.operand, Type.u32);14043 const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32));
14042 const index = @intCast(u32, operand);14044 try sema.requireRuntimeBlock(block, builtin_src);
14043 try sema.requireRuntimeBlock(block, src);
14044 return block.addInst(.{14045 return block.addInst(.{
14045 .tag = .wasm_memory_size,14046 .tag = .wasm_memory_size,
14046 .data = .{ .ty_pl = .{14047 .data = .{ .pl_op = .{
14047 .ty = try sema.addType(Type.u32),14048 .operand = .none,
14048 .payload = index,14049 .payload = index,
14049 } },14050 } },
14050 });14051 });
...@@ -14056,20 +14057,22 @@ fn zirWasmMemoryGrow(...@@ -14056,20 +14057,22 @@ fn zirWasmMemoryGrow(
14056 extended: Zir.Inst.Extended.InstData,14057 extended: Zir.Inst.Extended.InstData,
14057) CompileError!Air.Inst.Ref {14058) CompileError!Air.Inst.Ref {
14058 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;14059 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
14059 const src: LazySrcLoc = .{ .node_offset = extra.node };14060 const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
14060 if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {14061 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
14061 return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{});14062 const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
14063 const target = sema.mod.getTarget();
14064 if (!target.isWasm()) {
14065 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
14062 }14066 }
1406314067
14064 const index_arg = try sema.resolveInt(block, src, extra.lhs, Type.u32);14068 const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32));
14065 const index = @intCast(u32, index_arg);14069 const delta = try sema.coerce(block, Type.u32, sema.resolveInst(extra.rhs), delta_src);
14066 const delta_arg = sema.resolveInst(extra.rhs);
1406714070
14068 try sema.requireRuntimeBlock(block, src);14071 try sema.requireRuntimeBlock(block, builtin_src);
14069 return block.addInst(.{14072 return block.addInst(.{
14070 .tag = .wasm_memory_grow,14073 .tag = .wasm_memory_grow,
14071 .data = .{ .pl_op = .{14074 .data = .{ .pl_op = .{
14072 .operand = delta_arg,14075 .operand = delta,
14073 .payload = index,14076 .payload = index,
14074 } },14077 } },
14075 });14078 });
src/arch/wasm/CodeGen.zig+4-2
...@@ -3430,10 +3430,12 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3430,10 +3430,12 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3430}3430}
34313431
3432fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {3432fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {
3433 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3433 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3434
3435 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
34343436
3435 const result = try self.allocLocal(self.air.typeOfIndex(inst));3437 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3436 try self.addLabel(.memory_size, ty_pl.payload);3438 try self.addLabel(.memory_size, pl_op.payload);
3437 try self.addLabel(.local_set, result.local);3439 try self.addLabel(.local_set, result.local);
3438 return result;3440 return result;
3439}3441}
src/codegen/c.zig+4-2
...@@ -3592,14 +3592,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3592,14 +3592,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
3592}3592}
35933593
3594fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {3594fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
3595 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;3595 if (f.liveness.isUnused(inst)) return CValue.none;
3596
3597 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
35963598
3597 const writer = f.object.writer();3599 const writer = f.object.writer();
3598 const inst_ty = f.air.typeOfIndex(inst);3600 const inst_ty = f.air.typeOfIndex(inst);
3599 const local = try f.allocLocal(inst_ty, .Const);3601 const local = try f.allocLocal(inst_ty, .Const);
36003602
3601 try writer.writeAll(" = ");3603 try writer.writeAll(" = ");
3602 try writer.print("zig_wasm_memory_size({d});\n", .{ty_pl.payload});3604 try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload});
36033605
3604 return local;3606 return local;
3605}3607}
src/codegen/llvm.zig+18-4
...@@ -3478,13 +3478,27 @@ pub const FuncGen = struct {...@@ -3478,13 +3478,27 @@ pub const FuncGen = struct {
3478 }3478 }
34793479
3480 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3480 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3481 _ = inst;3481 if (self.liveness.isUnused(inst)) return null;
3482 return self.todo("implement builtin `@wasmMemorySize()`", .{});3482
3483 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3484 const index = pl_op.payload;
3485 const llvm_u32 = self.context.intType(32);
3486 const llvm_fn = self.getIntrinsic("llvm.wasm.memory.size.i32", &.{llvm_u32});
3487 const args: [1]*const llvm.Value = .{llvm_u32.constInt(index, .False)};
3488 return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
3483 }3489 }
34843490
3485 fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3491 fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3486 _ = inst;3492 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3487 return self.todo("implement builtin `@wasmMemoryGrow()`", .{});3493 const index = pl_op.payload;
3494 const operand = try self.resolveInst(pl_op.operand);
3495 const llvm_u32 = self.context.intType(32);
3496 const llvm_fn = self.getIntrinsic("llvm.wasm.memory.grow.i32", &.{ llvm_u32, llvm_u32 });
3497 const args: [2]*const llvm.Value = .{
3498 llvm_u32.constInt(index, .False),
3499 operand,
3500 };
3501 return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
3488 }3502 }
34893503
3490 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3504 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/link/C/zig.h+2-6
...@@ -91,14 +91,10 @@...@@ -91,14 +91,10 @@
9191
92#if defined(__clang__)92#if defined(__clang__)
93#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index)93#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index)
94#else
95#define zig_wasm_memory_size(index) 0
96#endif
97
98#if defined(__clang__)
99#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)94#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)
100#else95#else
101#define zig_wasm_memory_grow(index, delta) 096#define zig_wasm_memory_size(index) zig_unimplemented()
97#define zig_wasm_memory_grow(index, delta) zig_unimplemented()
102#endif98#endif
10399
104#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)100#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
src/print_air.zig+2-2
...@@ -626,8 +626,8 @@ const Writer = struct {...@@ -626,8 +626,8 @@ const Writer = struct {
626 }626 }
627627
628 fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {628 fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
629 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;629 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
630 try s.print("{d}", .{ty_pl.payload});630 try s.print("{d}", .{pl_op.payload});
631 }631 }
632632
633 fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {633 fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/type.zig+2
...@@ -5256,6 +5256,8 @@ pub const Type = extern union {...@@ -5256,6 +5256,8 @@ pub const Type = extern union {
5256 pub const @"u32" = initTag(.u32);5256 pub const @"u32" = initTag(.u32);
5257 pub const @"u64" = initTag(.u64);5257 pub const @"u64" = initTag(.u64);
52585258
5259 pub const @"i32" = initTag(.i32);
5260
5259 pub const @"f16" = initTag(.f16);5261 pub const @"f16" = initTag(.f16);
5260 pub const @"f32" = initTag(.f32);5262 pub const @"f32" = initTag(.f32);
5261 pub const @"f64" = initTag(.f64);5263 pub const @"f64" = initTag(.f64);
test/behavior.zig+1-4
...@@ -96,7 +96,7 @@ test {...@@ -96,7 +96,7 @@ test {
96 _ = @import("behavior/void.zig");96 _ = @import("behavior/void.zig");
97 _ = @import("behavior/while.zig");97 _ = @import("behavior/while.zig");
9898
99 if (builtin.zig_backend == .stage2_wasm) {99 if (builtin.stage2_arch == .wasm32) {
100 _ = @import("behavior/wasm.zig");100 _ = @import("behavior/wasm.zig");
101 }101 }
102102
...@@ -172,9 +172,6 @@ test {...@@ -172,9 +172,6 @@ test {
172 _ = @import("behavior/struct_contains_slice_of_itself.zig");172 _ = @import("behavior/struct_contains_slice_of_itself.zig");
173 _ = @import("behavior/typename.zig");173 _ = @import("behavior/typename.zig");
174 _ = @import("behavior/vector.zig");174 _ = @import("behavior/vector.zig");
175 if (builtin.target.cpu.arch == .wasm32) {
176 _ = @import("behavior/wasm.zig");
177 }
178 }175 }
179 }176 }
180 }177 }
test/behavior/wasm.zig-2
...@@ -3,8 +3,6 @@ const expect = std.testing.expect;...@@ -3,8 +3,6 @@ const expect = std.testing.expect;
3const builtin = @import("builtin");3const builtin = @import("builtin");
44
5test "memory size and grow" {5test "memory size and grow" {
6 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
7
8 var prev = @wasmMemorySize(0);6 var prev = @wasmMemorySize(0);
9 try expect(prev == @wasmMemoryGrow(0, 1));7 try expect(prev == @wasmMemoryGrow(0, 1));
10 try expect(prev + 1 == @wasmMemorySize(0));8 try expect(prev + 1 == @wasmMemorySize(0));