From 43cb19ea4da63dcaa8a18a06e3ab23f1c822c1fe Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Mar 2022 19:10:58 +0100 Subject: [PATCH] wasm: Implement `@wasmMemoryGrow` builtin Similarly to the other wasm builtin, this implements the grow variation where the memory index is a comptime known value. The operand as well as the result are runtime values. This also verifies during semantic analysis the target we're building for is wasm, or else emits a compilation error. This means that other backends do not have to handle this AIR instruction, other than the wasm and LLVM backends. --- src/Air.zig | 6 ++++++ src/AstGen.zig | 2 +- src/Liveness.zig | 4 ++++ src/Sema.zig | 20 +++++++++++++++++++- src/arch/aarch64/CodeGen.zig | 3 +++ src/arch/arm/CodeGen.zig | 3 +++ src/arch/riscv64/CodeGen.zig | 3 +++ src/arch/wasm/CodeGen.zig | 13 +++++++++++++ src/arch/x86_64/CodeGen.zig | 3 +++ src/codegen/c.zig | 3 +++ src/codegen/llvm.zig | 7 ++++++- src/print_air.zig | 13 +++++++++++-- 12 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 0a8771f6ac..e00720f534 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -587,6 +587,10 @@ pub const Inst = struct { /// Uses the `ty_pl` field, payload is `WasmMemoryIndex`. wasm_memory_size, + /// Implements @wasmMemoryGrow builtin. + /// Uses the `pl_op` field, payload is `WasmMemoryIndex`. + wasm_memory_grow, + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { .lt => .cmp_lt, @@ -956,6 +960,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .frame_addr, => return Type.initTag(.usize), + .wasm_memory_grow => return Type.initTag(.i32), + .bool_to_int => return Type.initTag(.u1), .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0), diff --git a/src/AstGen.zig b/src/AstGen.zig index a82d9dc360..d6d29d4a54 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7148,7 +7148,7 @@ fn builtinCall( return rvalue(gz, rl, result, node); }, .wasm_memory_grow => { - const index_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[0]); + const index_arg = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]); const delta_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[1]); const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), diff --git a/src/Liveness.zig b/src/Liveness.zig index e90a9ab10e..7f007b5718 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -707,6 +707,10 @@ fn analyzeInst( return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none }); }, + .wasm_memory_grow => { + const pl_op = inst_datas[inst].pl_op; + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none }); + }, } } diff --git a/src/Sema.zig b/src/Sema.zig index 3dbbdaeb27..a8dc872a99 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14034,6 +14034,9 @@ fn zirWasmMemorySize( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; + if (!sema.mod.getTarget().isWasm()) { + return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{}); + } const operand = try sema.resolveInt(block, src, extra.operand, Type.u32); const index = @intCast(u32, operand); @@ -14054,7 +14057,22 @@ fn zirWasmMemoryGrow( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{}); + if (!sema.mod.getTarget().isWasm()) { + return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{}); + } + + const index_arg = try sema.resolveInt(block, src, extra.lhs, Type.u32); + const index = @intCast(u32, index_arg); + const delta_arg = sema.resolveInst(extra.rhs); + + try sema.requireRuntimeBlock(block, src); + return block.addInst(.{ + .tag = .wasm_memory_grow, + .data = .{ .pl_op = .{ + .operand = delta_arg, + .payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }), + } }, + }); } fn zirPrefetch( diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8c214f0091..19b444cb15 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -671,6 +671,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 366cfe67b1..04dc68db5a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -669,6 +669,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a51b8b40cc..15600c09dd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -642,6 +642,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } if (std.debug.runtime_safety) { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0ea0418d7f..b4917738dd 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1672,6 +1672,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .wrap_errunion_err => self.airWrapErrUnionErr(inst), .wasm_memory_size => self.airWasmMemorySize(inst), + .wasm_memory_grow => self.airWasmMemoryGrow(inst), .add_sat, .sub_sat, @@ -3438,6 +3439,18 @@ fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue { return result; } +fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; + const operand = try self.resolveInst(pl_op.operand); + + const result = try self.allocLocal(Type.usize); + try self.emitWValue(operand); + try self.addLabel(.memory_grow, extra.index); + try self.addLabel(.local_set, result.local); + return result; +} + fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { assert(operand_ty.hasRuntimeBits()); assert(op == .eq or op == .neq); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 33fd2c8321..fcffb6ddda 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -759,6 +759,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3e47637c76..988576a6f1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1758,6 +1758,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), .wrap_errunion_err => try airWrapErrUnionErr(f, inst), .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, // zig fmt: on }; switch (result_value) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a22d8e01b0..3e1be05d55 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3478,7 +3478,12 @@ pub const FuncGen = struct { fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { _ = inst; - return self.todo("`@wasmMemorySize()`", .{}); + return self.todo("implement builtin `@wasmMemorySize()`", .{}); + } + + fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + _ = inst; + return self.todo("implement builtin `@wasmMemoryGrow()`", .{}); } fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/src/print_air.zig b/src/print_air.zig index 4129fc5c43..3dd1decf53 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -251,6 +251,7 @@ const Writer = struct { .memset => try w.writeMemset(s, inst), .field_parent_ptr => try w.writeFieldParentPtr(s, inst), .wasm_memory_size => try w.writeWasmMemorySize(s, inst), + .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), .add_with_overflow, .sub_with_overflow, @@ -625,10 +626,18 @@ const Writer = struct { } fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[inst].ty_pl; + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.WasmMemoryIndex, ty_pl.payload).data; + + try s.print("{d}", .{extra.index}); + } + + fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data; - try s.print(", {d}", .{extra.index}); + try s.print("{d}, ", .{extra.index}); + try w.writeOperand(s, inst, 0, pl_op.operand); } fn writeOperand(