| author | |
| committer | |
| log | e532b0c0b5d55d212d885c779ab9f2fa7443e56a |
| tree | 0a1e9b277c745518f2b64d8913fe5e065fe9cc1b |
| parent | 7fd32de0180c29dc4e3da72e9347f6f5ce167a38 |
* 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, |
| 585 | 585 | ||
| 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, |
| 589 | 591 | ||
| 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, |
| 593 | 596 | ||
| ... | @@ -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), |
| 890 | 893 | ||
| 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), |
| 956 | 959 | ||
| 957 | .wasm_memory_grow => return Type.initTag(.i32), | 960 | .wasm_memory_grow => return Type.i32, |
| 961 | .wasm_memory_size => return Type.u32, | ||
| 958 | 962 | ||
| 959 | .bool_to_int => return Type.initTag(.u1), | 963 | .bool_to_int => return Type.initTag(.u1), |
| 960 | 964 |
src/AstGen.zig+3-3| ... | @@ -7140,7 +7140,7 @@ fn builtinCall( | ... | @@ -7140,7 +7140,7 @@ fn builtinCall( |
| 7140 | // zig fmt: on | 7140 | // zig fmt: on |
| 7141 | 7141 | ||
| 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 | } |
| 14040 | 14042 | ||
| 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 | } |
| 14063 | 14067 | ||
| 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); | ||
| 14067 | 14070 | ||
| 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 | } |
| 3431 | 3431 | ||
| 3432 | fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue { | 3432 | fn 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; | ||
| 3434 | 3436 | ||
| 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 | } |
| 3593 | 3593 | ||
| 3594 | fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { | 3594 | fn 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; | ||
| 3596 | 3598 | ||
| 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); |
| 3600 | 3602 | ||
| 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}); |
| 3603 | 3605 | ||
| 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 | } |
| 3479 | 3479 | ||
| 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 | } |
| 3484 | 3490 | ||
| 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 | } |
| 3489 | 3503 | ||
| 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 @@ |
| 91 | 91 | ||
| 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 | #else | 95 | #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() | ||
| 102 | #endif | 98 | #endif |
| 103 | 99 | ||
| 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 | } |
| 627 | 627 | ||
| 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 | } |
| 632 | 632 | ||
| 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); |
| 5258 | 5258 | ||
| 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"); |
| 98 | 98 | ||
| 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 | } |
| 102 | 102 | ||
| ... | @@ -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; |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | 4 | ||
| 5 | test "memory size and grow" { | 5 | test "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)); |