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 {
584584 field_parent_ptr,
585585
586586 /// 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`.
588590 wasm_memory_size,
589591
590592 /// Implements @wasmMemoryGrow builtin.
593 /// Result type is always `i32`,
591594 /// Uses the `pl_op` field, payload represents the index of the target memory.
592595 wasm_memory_grow,
593596
......@@ -626,6 +629,7 @@ pub const Inst = struct {
626629 pub const Data = union {
627630 no_op: void,
628631 un_op: Ref,
632
629633 bin_op: struct {
630634 lhs: Ref,
631635 rhs: Ref,
......@@ -885,7 +889,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
885889 .aggregate_init,
886890 .union_init,
887891 .field_parent_ptr,
888 .wasm_memory_size,
889892 => return air.getRefType(datas[inst].ty_pl.ty),
890893
891894 .not,
......@@ -954,7 +957,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
954957 .frame_addr,
955958 => 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
959963 .bool_to_int => return Type.initTag(.u1),
960964
src/AstGen.zig+3-3
......@@ -7140,7 +7140,7 @@ fn builtinCall(
71407140 // zig fmt: on
71417141
71427142 .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]);
71447144 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
71457145 .node = gz.nodeIndexToRelative(node),
71467146 .operand = operand,
......@@ -7148,8 +7148,8 @@ fn builtinCall(
71487148 return rvalue(gz, rl, result, node);
71497149 },
71507150 .wasm_memory_grow => {
7151 const index_arg = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]);
7152 const delta_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[1]);
7151 const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
7152 const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]);
71537153 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
71547154 .node = gz.nodeIndexToRelative(node),
71557155 .lhs = index_arg,
src/Sema.zig+19-16
......@@ -14033,18 +14033,19 @@ fn zirWasmMemorySize(
1403314033 extended: Zir.Inst.Extended.InstData,
1403414034) CompileError!Air.Inst.Ref {
1403514035 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
14036 const src: LazySrcLoc = .{ .node_offset = extra.node };
14037 if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {
14038 return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{});
14036 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
14037 const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
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)});
1403914041 }
1404014042
14041 const operand = try sema.resolveInt(block, src, extra.operand, Type.u32);
14042 const index = @intCast(u32, operand);
14043 try sema.requireRuntimeBlock(block, src);
14043 const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32));
14044 try sema.requireRuntimeBlock(block, builtin_src);
1404414045 return block.addInst(.{
1404514046 .tag = .wasm_memory_size,
14046 .data = .{ .ty_pl = .{
14047 .ty = try sema.addType(Type.u32),
14047 .data = .{ .pl_op = .{
14048 .operand = .none,
1404814049 .payload = index,
1404914050 } },
1405014051 });
......@@ -14056,20 +14057,22 @@ fn zirWasmMemoryGrow(
1405614057 extended: Zir.Inst.Extended.InstData,
1405714058) CompileError!Air.Inst.Ref {
1405814059 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
14059 const src: LazySrcLoc = .{ .node_offset = extra.node };
14060 if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {
14061 return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{});
14060 const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
14061 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
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)});
1406214066 }
1406314067
14064 const index_arg = try sema.resolveInt(block, src, extra.lhs, Type.u32);
14065 const index = @intCast(u32, index_arg);
14066 const delta_arg = sema.resolveInst(extra.rhs);
14068 const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32));
14069 const delta = try sema.coerce(block, Type.u32, sema.resolveInst(extra.rhs), delta_src);
1406714070
14068 try sema.requireRuntimeBlock(block, src);
14071 try sema.requireRuntimeBlock(block, builtin_src);
1406914072 return block.addInst(.{
1407014073 .tag = .wasm_memory_grow,
1407114074 .data = .{ .pl_op = .{
14072 .operand = delta_arg,
14075 .operand = delta,
1407314076 .payload = index,
1407414077 } },
1407514078 });
src/arch/wasm/CodeGen.zig+4-2
......@@ -3430,10 +3430,12 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34303430}
34313431
34323432fn 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
34353437 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);
34373439 try self.addLabel(.local_set, result.local);
34383440 return result;
34393441}
src/codegen/c.zig+4-2
......@@ -3592,14 +3592,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
35923592}
35933593
35943594fn 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
35973599 const writer = f.object.writer();
35983600 const inst_ty = f.air.typeOfIndex(inst);
35993601 const local = try f.allocLocal(inst_ty, .Const);
36003602
36013603 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
36043606 return local;
36053607}
src/codegen/llvm.zig+18-4
......@@ -3478,13 +3478,27 @@ pub const FuncGen = struct {
34783478 }
34793479
34803480 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3481 _ = inst;
3482 return self.todo("implement builtin `@wasmMemorySize()`", .{});
3481 if (self.liveness.isUnused(inst)) return null;
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, "");
34833489 }
34843490
34853491 fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3486 _ = inst;
3487 return self.todo("implement builtin `@wasmMemoryGrow()`", .{});
3492 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
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, "");
34883502 }
34893503
34903504 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/link/C/zig.h+2-6
......@@ -91,14 +91,10 @@
9191
9292#if defined(__clang__)
9393#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__)
9994#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)
10095#else
101#define zig_wasm_memory_grow(index, delta) 0
96#define zig_wasm_memory_size(index) zig_unimplemented()
97#define zig_wasm_memory_grow(index, delta) zig_unimplemented()
10298#endif
10399
104100#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
src/print_air.zig+2-2
......@@ -626,8 +626,8 @@ const Writer = struct {
626626 }
627627
628628 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;
630 try s.print("{d}", .{ty_pl.payload});
629 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
630 try s.print("{d}", .{pl_op.payload});
631631 }
632632
633633 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 {
52565256 pub const @"u32" = initTag(.u32);
52575257 pub const @"u64" = initTag(.u64);
52585258
5259 pub const @"i32" = initTag(.i32);
5260
52595261 pub const @"f16" = initTag(.f16);
52605262 pub const @"f32" = initTag(.f32);
52615263 pub const @"f64" = initTag(.f64);
test/behavior.zig+1-4
......@@ -96,7 +96,7 @@ test {
9696 _ = @import("behavior/void.zig");
9797 _ = @import("behavior/while.zig");
9898
99 if (builtin.zig_backend == .stage2_wasm) {
99 if (builtin.stage2_arch == .wasm32) {
100100 _ = @import("behavior/wasm.zig");
101101 }
102102
......@@ -172,9 +172,6 @@ test {
172172 _ = @import("behavior/struct_contains_slice_of_itself.zig");
173173 _ = @import("behavior/typename.zig");
174174 _ = @import("behavior/vector.zig");
175 if (builtin.target.cpu.arch == .wasm32) {
176 _ = @import("behavior/wasm.zig");
177 }
178175 }
179176 }
180177 }
test/behavior/wasm.zig-2
......@@ -3,8 +3,6 @@ const expect = std.testing.expect;
33const builtin = @import("builtin");
44
55test "memory size and grow" {
6 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
7
86 var prev = @wasmMemorySize(0);
97 try expect(prev == @wasmMemoryGrow(0, 1));
108 try expect(prev + 1 == @wasmMemorySize(0));