| 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 | 584 | field_parent_ptr, |
| 585 | 585 | |
| 586 | 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 | 590 | wasm_memory_size, |
| 589 | 591 | |
| 590 | 592 | /// Implements @wasmMemoryGrow builtin. |
| 593 | /// Result type is always `i32`, | |
| 591 | 594 | /// Uses the `pl_op` field, payload represents the index of the target memory. |
| 592 | 595 | wasm_memory_grow, |
| 593 | 596 | |
| ... | ... | @@ -626,6 +629,7 @@ pub const Inst = struct { |
| 626 | 629 | pub const Data = union { |
| 627 | 630 | no_op: void, |
| 628 | 631 | un_op: Ref, |
| 632 | ||
| 629 | 633 | bin_op: struct { |
| 630 | 634 | lhs: Ref, |
| 631 | 635 | rhs: Ref, |
| ... | ... | @@ -885,7 +889,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 885 | 889 | .aggregate_init, |
| 886 | 890 | .union_init, |
| 887 | 891 | .field_parent_ptr, |
| 888 | .wasm_memory_size, | |
| 889 | 892 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 890 | 893 | |
| 891 | 894 | .not, |
| ... | ... | @@ -954,7 +957,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 954 | 957 | .frame_addr, |
| 955 | 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 | 963 | .bool_to_int => return Type.initTag(.u1), |
| 960 | 964 |
src/AstGen.zig+3-3| ... | ... | @@ -7140,7 +7140,7 @@ fn builtinCall( |
| 7140 | 7140 | // zig fmt: on |
| 7141 | 7141 | |
| 7142 | 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 | 7144 | const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{ |
| 7145 | 7145 | .node = gz.nodeIndexToRelative(node), |
| 7146 | 7146 | .operand = operand, |
| ... | ... | @@ -7148,8 +7148,8 @@ fn builtinCall( |
| 7148 | 7148 | return rvalue(gz, rl, result, node); |
| 7149 | 7149 | }, |
| 7150 | 7150 | .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]); | |
| 7153 | 7153 | const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ |
| 7154 | 7154 | .node = gz.nodeIndexToRelative(node), |
| 7155 | 7155 | .lhs = index_arg, |
src/Sema.zig+19-16| ... | ... | @@ -14033,18 +14033,19 @@ fn zirWasmMemorySize( |
| 14033 | 14033 | extended: Zir.Inst.Extended.InstData, |
| 14034 | 14034 | ) CompileError!Air.Inst.Ref { |
| 14035 | 14035 | 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)}); | |
| 14039 | 14041 | } |
| 14040 | 14042 | |
| 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); | |
| 14044 | 14045 | return block.addInst(.{ |
| 14045 | 14046 | .tag = .wasm_memory_size, |
| 14046 | .data = .{ .ty_pl = .{ | |
| 14047 | .ty = try sema.addType(Type.u32), | |
| 14047 | .data = .{ .pl_op = .{ | |
| 14048 | .operand = .none, | |
| 14048 | 14049 | .payload = index, |
| 14049 | 14050 | } }, |
| 14050 | 14051 | }); |
| ... | ... | @@ -14056,20 +14057,22 @@ fn zirWasmMemoryGrow( |
| 14056 | 14057 | extended: Zir.Inst.Extended.InstData, |
| 14057 | 14058 | ) CompileError!Air.Inst.Ref { |
| 14058 | 14059 | 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)}); | |
| 14062 | 14066 | } |
| 14063 | 14067 | |
| 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); | |
| 14067 | 14070 | |
| 14068 | try sema.requireRuntimeBlock(block, src); | |
| 14071 | try sema.requireRuntimeBlock(block, builtin_src); | |
| 14069 | 14072 | return block.addInst(.{ |
| 14070 | 14073 | .tag = .wasm_memory_grow, |
| 14071 | 14074 | .data = .{ .pl_op = .{ |
| 14072 | .operand = delta_arg, | |
| 14075 | .operand = delta, | |
| 14073 | 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 | 3430 | } |
| 3431 | 3431 | |
| 3432 | 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 | 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 | 3439 | try self.addLabel(.local_set, result.local); |
| 3438 | 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 | 3592 | } |
| 3593 | 3593 | |
| 3594 | 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 | 3599 | const writer = f.object.writer(); |
| 3598 | 3600 | const inst_ty = f.air.typeOfIndex(inst); |
| 3599 | 3601 | const local = try f.allocLocal(inst_ty, .Const); |
| 3600 | 3602 | |
| 3601 | 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 | 3606 | return local; |
| 3605 | 3607 | } |
src/codegen/llvm.zig+18-4| ... | ... | @@ -3478,13 +3478,27 @@ pub const FuncGen = struct { |
| 3478 | 3478 | } |
| 3479 | 3479 | |
| 3480 | 3480 | 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, ""); | |
| 3483 | 3489 | } |
| 3484 | 3490 | |
| 3485 | 3491 | 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, ""); | |
| 3488 | 3502 | } |
| 3489 | 3503 | |
| 3490 | 3504 | fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
src/link/C/zig.h+2-6| ... | ... | @@ -91,14 +91,10 @@ |
| 91 | 91 | |
| 92 | 92 | #if defined(__clang__) |
| 93 | 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 | 94 | #define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta) |
| 100 | 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 | 98 | #endif |
| 103 | 99 | |
| 104 | 100 | #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) |
src/print_air.zig+2-2| ... | ... | @@ -626,8 +626,8 @@ const Writer = struct { |
| 626 | 626 | } |
| 627 | 627 | |
| 628 | 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; | |
| 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}); | |
| 631 | 631 | } |
| 632 | 632 | |
| 633 | 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 | 5256 | pub const @"u32" = initTag(.u32); |
| 5257 | 5257 | pub const @"u64" = initTag(.u64); |
| 5258 | 5258 | |
| 5259 | pub const @"i32" = initTag(.i32); | |
| 5260 | ||
| 5259 | 5261 | pub const @"f16" = initTag(.f16); |
| 5260 | 5262 | pub const @"f32" = initTag(.f32); |
| 5261 | 5263 | pub const @"f64" = initTag(.f64); |
test/behavior.zig+1-4| ... | ... | @@ -96,7 +96,7 @@ test { |
| 96 | 96 | _ = @import("behavior/void.zig"); |
| 97 | 97 | _ = @import("behavior/while.zig"); |
| 98 | 98 | |
| 99 | if (builtin.zig_backend == .stage2_wasm) { | |
| 99 | if (builtin.stage2_arch == .wasm32) { | |
| 100 | 100 | _ = @import("behavior/wasm.zig"); |
| 101 | 101 | } |
| 102 | 102 | |
| ... | ... | @@ -172,9 +172,6 @@ test { |
| 172 | 172 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); |
| 173 | 173 | _ = @import("behavior/typename.zig"); |
| 174 | 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 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | |
| 5 | 5 | test "memory size and grow" { |
| 6 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | |
| 7 | ||
| 8 | 6 | var prev = @wasmMemorySize(0); |
| 9 | 7 | try expect(prev == @wasmMemoryGrow(0, 1)); |
| 10 | 8 | try expect(prev + 1 == @wasmMemorySize(0)); |