mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
Merge pull request #11048 from Luukdegram/wasm-builtins
stage2: Implement wasm builtins
This commit is contained in:
commit
c9ee3c1e47
15
src/Air.zig
15
src/Air.zig
@ -583,6 +583,17 @@ pub const Inst = struct {
|
||||
/// Uses the `ty_pl` field.
|
||||
field_parent_ptr,
|
||||
|
||||
/// Implements @wasmMemorySize builtin.
|
||||
/// Result type is always `u32`,
|
||||
/// Uses the `pl_op` field, payload represents the index of the target memory.
|
||||
/// The operand is unused and always set to `Ref.none`.
|
||||
wasm_memory_size,
|
||||
|
||||
/// Implements @wasmMemoryGrow builtin.
|
||||
/// Result type is always `i32`,
|
||||
/// Uses the `pl_op` field, payload represents the index of the target memory.
|
||||
wasm_memory_grow,
|
||||
|
||||
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
|
||||
return switch (op) {
|
||||
.lt => .cmp_lt,
|
||||
@ -618,6 +629,7 @@ pub const Inst = struct {
|
||||
pub const Data = union {
|
||||
no_op: void,
|
||||
un_op: Ref,
|
||||
|
||||
bin_op: struct {
|
||||
lhs: Ref,
|
||||
rhs: Ref,
|
||||
@ -945,6 +957,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.frame_addr,
|
||||
=> return Type.initTag(.usize),
|
||||
|
||||
.wasm_memory_grow => return Type.i32,
|
||||
.wasm_memory_size => return Type.u32,
|
||||
|
||||
.bool_to_int => return Type.initTag(.u1),
|
||||
|
||||
.tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0),
|
||||
|
||||
@ -7140,7 +7140,7 @@ fn builtinCall(
|
||||
// zig fmt: on
|
||||
|
||||
.wasm_memory_size => {
|
||||
const operand = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
|
||||
const operand = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
|
||||
const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
|
||||
.node = gz.nodeIndexToRelative(node),
|
||||
.operand = operand,
|
||||
@ -7148,8 +7148,8 @@ fn builtinCall(
|
||||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
.wasm_memory_grow => {
|
||||
const index_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
|
||||
const delta_arg = try expr(gz, scope, .{ .ty = .u32_type }, params[1]);
|
||||
const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
|
||||
const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]);
|
||||
const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
|
||||
.node = gz.nodeIndexToRelative(node),
|
||||
.lhs = index_arg,
|
||||
|
||||
@ -318,6 +318,7 @@ fn analyzeInst(
|
||||
.fence,
|
||||
.ret_addr,
|
||||
.frame_addr,
|
||||
.wasm_memory_size,
|
||||
=> return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
|
||||
|
||||
.not,
|
||||
@ -706,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 });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
src/Sema.zig
39
src/Sema.zig
@ -14033,8 +14033,22 @@ fn zirWasmMemorySize(
|
||||
extended: Zir.Inst.Extended.InstData,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
|
||||
const src: LazySrcLoc = .{ .node_offset = extra.node };
|
||||
return sema.fail(block, src, "TODO: implement Sema.zirWasmMemorySize", .{});
|
||||
const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
|
||||
const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
|
||||
const target = sema.mod.getTarget();
|
||||
if (!target.isWasm()) {
|
||||
return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
|
||||
}
|
||||
|
||||
const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32));
|
||||
try sema.requireRuntimeBlock(block, builtin_src);
|
||||
return block.addInst(.{
|
||||
.tag = .wasm_memory_size,
|
||||
.data = .{ .pl_op = .{
|
||||
.operand = .none,
|
||||
.payload = index,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn zirWasmMemoryGrow(
|
||||
@ -14043,8 +14057,25 @@ fn zirWasmMemoryGrow(
|
||||
extended: Zir.Inst.Extended.InstData,
|
||||
) 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", .{});
|
||||
const builtin_src: LazySrcLoc = .{ .node_offset = extra.node };
|
||||
const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
|
||||
const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
|
||||
const target = sema.mod.getTarget();
|
||||
if (!target.isWasm()) {
|
||||
return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
|
||||
}
|
||||
|
||||
const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32));
|
||||
const delta = try sema.coerce(block, Type.u32, sema.resolveInst(extra.rhs), delta_src);
|
||||
|
||||
try sema.requireRuntimeBlock(block, builtin_src);
|
||||
return block.addInst(.{
|
||||
.tag = .wasm_memory_grow,
|
||||
.data = .{ .pl_op = .{
|
||||
.operand = delta,
|
||||
.payload = 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) {
|
||||
|
||||
@ -1671,6 +1671,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
.wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
|
||||
.wrap_errunion_err => self.airWrapErrUnionErr(inst),
|
||||
|
||||
.wasm_memory_size => self.airWasmMemorySize(inst),
|
||||
.wasm_memory_grow => self.airWasmMemoryGrow(inst),
|
||||
|
||||
.add_sat,
|
||||
.sub_sat,
|
||||
.mul_sat,
|
||||
@ -3426,6 +3429,28 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
return WValue{ .none = {} };
|
||||
}
|
||||
|
||||
fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
|
||||
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
|
||||
const result = try self.allocLocal(self.air.typeOfIndex(inst));
|
||||
try self.addLabel(.memory_size, pl_op.payload);
|
||||
try self.addLabel(.local_set, result.local);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const operand = try self.resolveInst(pl_op.operand);
|
||||
|
||||
const result = try self.allocLocal(self.air.typeOfIndex(inst));
|
||||
try self.emitWValue(operand);
|
||||
try self.addLabel(.memory_grow, pl_op.payload);
|
||||
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);
|
||||
|
||||
@ -89,10 +89,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {
|
||||
.local_set => try emit.emitLabel(tag, inst),
|
||||
.local_tee => try emit.emitLabel(tag, inst),
|
||||
.memory_grow => try emit.emitLabel(tag, inst),
|
||||
.memory_size => try emit.emitLabel(tag, inst),
|
||||
|
||||
// no-ops
|
||||
.end => try emit.emitTag(tag),
|
||||
.memory_size => try emit.emitTag(tag),
|
||||
.@"return" => try emit.emitTag(tag),
|
||||
.@"unreachable" => try emit.emitTag(tag),
|
||||
|
||||
|
||||
@ -226,9 +226,9 @@ pub const Inst = struct {
|
||||
i64_store32 = 0x3E,
|
||||
/// Returns the memory size in amount of pages.
|
||||
///
|
||||
/// Uses `nop`
|
||||
/// Uses `label`
|
||||
memory_size = 0x3F,
|
||||
/// Increases the memory at by given number of pages.
|
||||
/// Increases the memory by given number of pages.
|
||||
///
|
||||
/// Uses `label`
|
||||
memory_grow = 0x40,
|
||||
|
||||
@ -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 => try airWasmMemorySize(f, inst),
|
||||
.wasm_memory_grow => try airWasmMemoryGrow(f, inst),
|
||||
// zig fmt: on
|
||||
};
|
||||
switch (result_value) {
|
||||
@ -3588,6 +3591,36 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return CValue.none;
|
||||
}
|
||||
|
||||
fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||
|
||||
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
|
||||
|
||||
const writer = f.object.writer();
|
||||
const inst_ty = f.air.typeOfIndex(inst);
|
||||
const local = try f.allocLocal(inst_ty, .Const);
|
||||
|
||||
try writer.writeAll(" = ");
|
||||
try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload});
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
|
||||
|
||||
const writer = f.object.writer();
|
||||
const inst_ty = f.air.typeOfIndex(inst);
|
||||
const operand = try f.resolveInst(pl_op.operand);
|
||||
const local = try f.allocLocal(inst_ty, .Const);
|
||||
|
||||
try writer.writeAll(" = ");
|
||||
try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});
|
||||
try f.writeCValue(writer, operand);
|
||||
try writer.writeAll(");\n");
|
||||
return local;
|
||||
}
|
||||
|
||||
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
|
||||
return switch (order) {
|
||||
.Unordered => "memory_order_relaxed",
|
||||
|
||||
@ -2314,6 +2314,9 @@ pub const FuncGen = struct {
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
.wrap_errunion_err => try self.airWrapErrUnionErr(inst),
|
||||
|
||||
.wasm_memory_size => try self.airWasmMemorySize(inst),
|
||||
.wasm_memory_grow => try self.airWasmMemoryGrow(inst),
|
||||
|
||||
.constant => unreachable,
|
||||
.const_ty => unreachable,
|
||||
.unreach => self.airUnreach(inst),
|
||||
@ -3474,6 +3477,30 @@ pub const FuncGen = struct {
|
||||
return partial;
|
||||
}
|
||||
|
||||
fn airWasmMemorySize(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const index = pl_op.payload;
|
||||
const llvm_u32 = self.context.intType(32);
|
||||
const llvm_fn = self.getIntrinsic("llvm.wasm.memory.size.i32", &.{llvm_u32});
|
||||
const args: [1]*const llvm.Value = .{llvm_u32.constInt(index, .False)};
|
||||
return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
|
||||
}
|
||||
|
||||
fn airWasmMemoryGrow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const index = pl_op.payload;
|
||||
const operand = try self.resolveInst(pl_op.operand);
|
||||
const llvm_u32 = self.context.intType(32);
|
||||
const llvm_fn = self.getIntrinsic("llvm.wasm.memory.grow.i32", &.{ llvm_u32, llvm_u32 });
|
||||
const args: [2]*const llvm.Value = .{
|
||||
llvm_u32.constInt(index, .False),
|
||||
operand,
|
||||
};
|
||||
return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
|
||||
}
|
||||
|
||||
fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
|
||||
@ -89,6 +89,14 @@
|
||||
#define zig_prefetch(addr, rw, locality)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index)
|
||||
#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)
|
||||
#else
|
||||
#define zig_wasm_memory_size(index) zig_unimplemented()
|
||||
#define zig_wasm_memory_grow(index, delta) zig_unimplemented()
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
|
||||
#include <stdatomic.h>
|
||||
#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail)
|
||||
|
||||
@ -250,6 +250,8 @@ const Writer = struct {
|
||||
.memcpy => try w.writeMemcpy(s, inst),
|
||||
.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,
|
||||
@ -623,6 +625,17 @@ const Writer = struct {
|
||||
try s.writeAll("}");
|
||||
}
|
||||
|
||||
fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
try s.print("{d}", .{pl_op.payload});
|
||||
}
|
||||
|
||||
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;
|
||||
try s.print("{d}, ", .{pl_op.payload});
|
||||
try w.writeOperand(s, inst, 0, pl_op.operand);
|
||||
}
|
||||
|
||||
fn writeOperand(
|
||||
w: *Writer,
|
||||
s: anytype,
|
||||
|
||||
@ -5256,6 +5256,8 @@ pub const Type = extern union {
|
||||
pub const @"u32" = initTag(.u32);
|
||||
pub const @"u64" = initTag(.u64);
|
||||
|
||||
pub const @"i32" = initTag(.i32);
|
||||
|
||||
pub const @"f16" = initTag(.f16);
|
||||
pub const @"f32" = initTag(.f32);
|
||||
pub const @"f64" = initTag(.f64);
|
||||
|
||||
@ -96,6 +96,10 @@ test {
|
||||
_ = @import("behavior/void.zig");
|
||||
_ = @import("behavior/while.zig");
|
||||
|
||||
if (builtin.stage2_arch == .wasm32) {
|
||||
_ = @import("behavior/wasm.zig");
|
||||
}
|
||||
|
||||
if (builtin.zig_backend != .stage1) {
|
||||
_ = @import("behavior/decltest.zig");
|
||||
}
|
||||
@ -168,9 +172,6 @@ test {
|
||||
_ = @import("behavior/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("behavior/typename.zig");
|
||||
_ = @import("behavior/vector.zig");
|
||||
if (builtin.target.cpu.arch == .wasm32) {
|
||||
_ = @import("behavior/wasm.zig");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "memory size and grow" {
|
||||
var prev = @wasmMemorySize(0);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user