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.
This commit is contained in:
committed by
Andrew Kelley
parent
ec4c30ae48
commit
43cb19ea4d
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
src/Sema.zig
20
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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user