authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 20:39:10-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-03 20:39:10-05:00
logc9ee3c1e474a7b10fb806b60ef108057395a3cca
tree0a1e9b277c745518f2b64d8913fe5e065fe9cc1b
parent0ea51f7f494cd84a48fd997b60196d6c4254ccac
parente532b0c0b5d55d212d885c779ab9f2fa7443e56a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11048 from Luukdegram/wasm-builtins

stage2: Implement wasm builtins

18 files changed, 186 insertions(+), 13 deletions(-)

src/Air.zig+15
......@@ -583,6 +583,17 @@ pub const Inst = struct {
583583 /// Uses the `ty_pl` field.
584584 field_parent_ptr,
585585
586 /// Implements @wasmMemorySize builtin.
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`.
590 wasm_memory_size,
591
592 /// Implements @wasmMemoryGrow builtin.
593 /// Result type is always `i32`,
594 /// Uses the `pl_op` field, payload represents the index of the target memory.
595 wasm_memory_grow,
596
586597 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
587598 return switch (op) {
588599 .lt => .cmp_lt,
......@@ -618,6 +629,7 @@ pub const Inst = struct {
618629 pub const Data = union {
619630 no_op: void,
620631 un_op: Ref,
632
621633 bin_op: struct {
622634 lhs: Ref,
623635 rhs: Ref,
......@@ -945,6 +957,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
945957 .frame_addr,
946958 => return Type.initTag(.usize),
947959
960 .wasm_memory_grow => return Type.i32,
961 .wasm_memory_size => return Type.u32,
962
948963 .bool_to_int => return Type.initTag(.u1),
949964
950965 .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0),
src/AstGen.zig+3-3
......@@ -7140,7 +7140,7 @@ fn builtinCall(
71407140 // zig fmt: on
71417141
71427142 .wasm_memory_size => {
7143 const operand = try expr(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 expr(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/Liveness.zig+5
......@@ -318,6 +318,7 @@ fn analyzeInst(
318318 .fence,
319319 .ret_addr,
320320 .frame_addr,
321 .wasm_memory_size,
321322 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
322323
323324 .not,
......@@ -706,6 +707,10 @@ fn analyzeInst(
706707
707708 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });
708709 },
710 .wasm_memory_grow => {
711 const pl_op = inst_datas[inst].pl_op;
712 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });
713 },
709714 }
710715}
711716
src/Sema.zig+35-4
......@@ -14033,8 +14033,22 @@ 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 return sema.fail(block, src, "TODO: implement Sema.zirWasmMemorySize", .{});
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)});
14041 }
14042
14043 const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32));
14044 try sema.requireRuntimeBlock(block, builtin_src);
14045 return block.addInst(.{
14046 .tag = .wasm_memory_size,
14047 .data = .{ .pl_op = .{
14048 .operand = .none,
14049 .payload = index,
14050 } },
14051 });
1403814052}
1403914053
1404014054fn zirWasmMemoryGrow(
......@@ -14043,8 +14057,25 @@ fn zirWasmMemoryGrow(
1404314057 extended: Zir.Inst.Extended.InstData,
1404414058) CompileError!Air.Inst.Ref {
1404514059 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
14046 const src: LazySrcLoc = .{ .node_offset = extra.node };
14047 return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
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)});
14066 }
14067
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);
14070
14071 try sema.requireRuntimeBlock(block, builtin_src);
14072 return block.addInst(.{
14073 .tag = .wasm_memory_grow,
14074 .data = .{ .pl_op = .{
14075 .operand = delta,
14076 .payload = index,
14077 } },
14078 });
1404814079}
1404914080
1405014081fn zirPrefetch(
src/arch/aarch64/CodeGen.zig+3
......@@ -671,6 +671,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
671671 .wrap_optional => try self.airWrapOptional(inst),
672672 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
673673 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
674
675 .wasm_memory_size => unreachable,
676 .wasm_memory_grow => unreachable,
674677 // zig fmt: on
675678 }
676679
src/arch/arm/CodeGen.zig+3
......@@ -669,6 +669,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
669669 .wrap_optional => try self.airWrapOptional(inst),
670670 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
671671 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
672
673 .wasm_memory_size => unreachable,
674 .wasm_memory_grow => unreachable,
672675 // zig fmt: on
673676 }
674677
src/arch/riscv64/CodeGen.zig+3
......@@ -642,6 +642,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
642642 .wrap_optional => try self.airWrapOptional(inst),
643643 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
644644 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
645
646 .wasm_memory_size => unreachable,
647 .wasm_memory_grow => unreachable,
645648 // zig fmt: on
646649 }
647650 if (std.debug.runtime_safety) {
src/arch/wasm/CodeGen.zig+25
......@@ -1671,6 +1671,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
16711671 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
16721672 .wrap_errunion_err => self.airWrapErrUnionErr(inst),
16731673
1674 .wasm_memory_size => self.airWasmMemorySize(inst),
1675 .wasm_memory_grow => self.airWasmMemoryGrow(inst),
1676
16741677 .add_sat,
16751678 .sub_sat,
16761679 .mul_sat,
......@@ -3426,6 +3429,28 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
34263429 return WValue{ .none = {} };
34273430}
34283431
3432fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {
3433 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3434
3435 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3436
3437 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3438 try self.addLabel(.memory_size, pl_op.payload);
3439 try self.addLabel(.local_set, result.local);
3440 return result;
3441}
3442
3443fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue {
3444 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3445 const operand = try self.resolveInst(pl_op.operand);
3446
3447 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3448 try self.emitWValue(operand);
3449 try self.addLabel(.memory_grow, pl_op.payload);
3450 try self.addLabel(.local_set, result.local);
3451 return result;
3452}
3453
34293454fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
34303455 assert(operand_ty.hasRuntimeBits());
34313456 assert(op == .eq or op == .neq);
src/arch/wasm/Emit.zig+1-1
......@@ -89,10 +89,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {
8989 .local_set => try emit.emitLabel(tag, inst),
9090 .local_tee => try emit.emitLabel(tag, inst),
9191 .memory_grow => try emit.emitLabel(tag, inst),
92 .memory_size => try emit.emitLabel(tag, inst),
9293
9394 // no-ops
9495 .end => try emit.emitTag(tag),
95 .memory_size => try emit.emitTag(tag),
9696 .@"return" => try emit.emitTag(tag),
9797 .@"unreachable" => try emit.emitTag(tag),
9898
src/arch/wasm/Mir.zig+2-2
......@@ -226,9 +226,9 @@ pub const Inst = struct {
226226 i64_store32 = 0x3E,
227227 /// Returns the memory size in amount of pages.
228228 ///
229 /// Uses `nop`
229 /// Uses `label`
230230 memory_size = 0x3F,
231 /// Increases the memory at by given number of pages.
231 /// Increases the memory by given number of pages.
232232 ///
233233 /// Uses `label`
234234 memory_grow = 0x40,
src/arch/x86_64/CodeGen.zig+3
......@@ -759,6 +759,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
759759 .wrap_optional => try self.airWrapOptional(inst),
760760 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
761761 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
762
763 .wasm_memory_size => unreachable,
764 .wasm_memory_grow => unreachable,
762765 // zig fmt: on
763766 }
764767
src/codegen/c.zig+33
......@@ -1758,6 +1758,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
17581758 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
17591759 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
17601760 .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
1761
1762 .wasm_memory_size => try airWasmMemorySize(f, inst),
1763 .wasm_memory_grow => try airWasmMemoryGrow(f, inst),
17611764 // zig fmt: on
17621765 };
17631766 switch (result_value) {
......@@ -3588,6 +3591,36 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
35883591 return CValue.none;
35893592}
35903593
3594fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
3595 if (f.liveness.isUnused(inst)) return CValue.none;
3596
3597 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
3598
3599 const writer = f.object.writer();
3600 const inst_ty = f.air.typeOfIndex(inst);
3601 const local = try f.allocLocal(inst_ty, .Const);
3602
3603 try writer.writeAll(" = ");
3604 try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload});
3605
3606 return local;
3607}
3608
3609fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
3610 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
3611
3612 const writer = f.object.writer();
3613 const inst_ty = f.air.typeOfIndex(inst);
3614 const operand = try f.resolveInst(pl_op.operand);
3615 const local = try f.allocLocal(inst_ty, .Const);
3616
3617 try writer.writeAll(" = ");
3618 try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});
3619 try f.writeCValue(writer, operand);
3620 try writer.writeAll(");\n");
3621 return local;
3622}
3623
35913624fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
35923625 return switch (order) {
35933626 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+27
......@@ -2314,6 +2314,9 @@ pub const FuncGen = struct {
23142314 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
23152315 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
23162316
2317 .wasm_memory_size => try self.airWasmMemorySize(inst),
2318 .wasm_memory_grow => try self.airWasmMemoryGrow(inst),
2319
23172320 .constant => unreachable,
23182321 .const_ty => unreachable,
23192322 .unreach => self.airUnreach(inst),
......@@ -3474,6 +3477,30 @@ pub const FuncGen = struct {
34743477 return partial;
34753478 }
34763479
3480 fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
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, "");
3489 }
3490
3491 fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
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, "");
3502 }
3503
34773504 fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
34783505 if (self.liveness.isUnused(inst)) return null;
34793506
src/link/C/zig.h+8
......@@ -89,6 +89,14 @@
8989#define zig_prefetch(addr, rw, locality)
9090#endif
9191
92#if defined(__clang__)
93#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index)
94#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)
95#else
96#define zig_wasm_memory_size(index) zig_unimplemented()
97#define zig_wasm_memory_grow(index, delta) zig_unimplemented()
98#endif
99
92100#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
93101#include <stdatomic.h>
94102#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail)
src/print_air.zig+13
......@@ -250,6 +250,8 @@ const Writer = struct {
250250 .memcpy => try w.writeMemcpy(s, inst),
251251 .memset => try w.writeMemset(s, inst),
252252 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
253 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
254 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
253255
254256 .add_with_overflow,
255257 .sub_with_overflow,
......@@ -623,6 +625,17 @@ const Writer = struct {
623625 try s.writeAll("}");
624626 }
625627
628 fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
629 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
630 try s.print("{d}", .{pl_op.payload});
631 }
632
633 fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
634 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
635 try s.print("{d}, ", .{pl_op.payload});
636 try w.writeOperand(s, inst, 0, pl_op.operand);
637 }
638
626639 fn writeOperand(
627640 w: *Writer,
628641 s: anytype,
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+4-3
......@@ -96,6 +96,10 @@ test {
9696 _ = @import("behavior/void.zig");
9797 _ = @import("behavior/while.zig");
9898
99 if (builtin.stage2_arch == .wasm32) {
100 _ = @import("behavior/wasm.zig");
101 }
102
99103 if (builtin.zig_backend != .stage1) {
100104 _ = @import("behavior/decltest.zig");
101105 }
......@@ -168,9 +172,6 @@ test {
168172 _ = @import("behavior/struct_contains_slice_of_itself.zig");
169173 _ = @import("behavior/typename.zig");
170174 _ = @import("behavior/vector.zig");
171 if (builtin.target.cpu.arch == .wasm32) {
172 _ = @import("behavior/wasm.zig");
173 }
174175 }
175176 }
176177 }
test/behavior/wasm.zig+1
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const expect = std.testing.expect;
3const builtin = @import("builtin");
34
45test "memory size and grow" {
56 var prev = @wasmMemorySize(0);