cbe: Implement wasm builtins

This implements the wasm builtins by lowering to builtins that are supported by c-compilers.
In this case: Clang.

This also simplifies the `AIR` instruction as it now uses the payload field of `ty_pl` and `pl_op`
directly to store the index argument rather than storing it inside Extra. This saves us 4 bytes
per builtin call.
This commit is contained in:
Luuk de Gram 2022-03-03 22:12:53 +01:00 committed by Andrew Kelley
parent 21f0503c01
commit 7fd32de018
6 changed files with 54 additions and 26 deletions

View File

@ -584,11 +584,11 @@ pub const Inst = struct {
field_parent_ptr,
/// Implements @wasmMemorySize builtin.
/// Uses the `ty_pl` field, payload is `WasmMemoryIndex`.
/// Uses the `ty_pl` field, payload represents the index of the target memory.
wasm_memory_size,
/// Implements @wasmMemoryGrow builtin.
/// Uses the `pl_op` field, payload is `WasmMemoryIndex`.
/// Uses the `pl_op` field, payload represents the index of the target memory.
wasm_memory_grow,
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
@ -725,12 +725,6 @@ pub const FieldParentPtr = struct {
field_index: u32,
};
/// Wasm's memory instructions require a comptime-known index
/// which represents the memory it operates on.
pub const WasmMemoryIndex = struct {
index: u32,
};
/// Trailing:
/// 0. `Inst.Ref` for every outputs_len
/// 1. `Inst.Ref` for every inputs_len

View File

@ -14034,7 +14034,7 @@ 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()) {
if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {
return sema.fail(block, src, "builtin '@wasmMemorySize' is a wasm feature only", .{});
}
@ -14045,7 +14045,7 @@ fn zirWasmMemorySize(
.tag = .wasm_memory_size,
.data = .{ .ty_pl = .{
.ty = try sema.addType(Type.u32),
.payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }),
.payload = index,
} },
});
}
@ -14057,7 +14057,7 @@ fn zirWasmMemoryGrow(
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
const src: LazySrcLoc = .{ .node_offset = extra.node };
if (!sema.mod.getTarget().isWasm()) {
if (!sema.mod.getTarget().isWasm() and sema.mod.comp.bin_file.options.object_format != .c) {
return sema.fail(block, src, "builtin '@wasmMemoryGrow' is a wasm feature only", .{});
}
@ -14070,7 +14070,7 @@ fn zirWasmMemoryGrow(
.tag = .wasm_memory_grow,
.data = .{ .pl_op = .{
.operand = delta_arg,
.payload = try sema.addExtra(Air.WasmMemoryIndex{ .index = index }),
.payload = index,
} },
});
}

View File

@ -3431,22 +3431,20 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.WasmMemoryIndex, ty_pl.payload).data;
const result = try self.allocLocal(Type.usize);
try self.addLabel(.memory_size, extra.index);
const result = try self.allocLocal(self.air.typeOfIndex(inst));
try self.addLabel(.memory_size, ty_pl.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 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);
const result = try self.allocLocal(self.air.typeOfIndex(inst));
try self.emitWValue(operand);
try self.addLabel(.memory_grow, extra.index);
try self.addLabel(.memory_grow, pl_op.payload);
try self.addLabel(.local_set, result.local);
return result;
}

View File

@ -1759,8 +1759,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.wrap_errunion_err => try airWrapErrUnionErr(f, inst),
.errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
.wasm_memory_size => try airWasmMemorySize(f, inst),
.wasm_memory_grow => try airWasmMemoryGrow(f, inst),
// zig fmt: on
};
switch (result_value) {
@ -3591,6 +3591,34 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
return CValue.none;
}
fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
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", .{ty_pl.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",

View File

@ -89,6 +89,18 @@
#define zig_prefetch(addr, rw, locality)
#endif
#if defined(__clang__)
#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index)
#else
#define zig_wasm_memory_size(index) 0
#endif
#if defined(__clang__)
#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta)
#else
#define zig_wasm_memory_grow(index, delta) 0
#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)

View File

@ -627,16 +627,12 @@ const Writer = struct {
fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
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});
try s.print("{d}", .{ty_pl.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;
const extra = w.air.extraData(Air.WasmMemoryIndex, pl_op.payload).data;
try s.print("{d}, ", .{extra.index});
try s.print("{d}, ", .{pl_op.payload});
try w.writeOperand(s, inst, 0, pl_op.operand);
}