Commit 7fd32de018

Luuk de Gram <luuk@degram.dev>
2022-03-03 22:12:53
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.
1 parent 21f0503
Changed files (6)
src/arch/wasm/CodeGen.zig
@@ -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;
 }
src/codegen/c.zig
@@ -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",
src/link/C/zig.h
@@ -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)
src/Air.zig
@@ -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
src/print_air.zig
@@ -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);
     }
 
src/Sema.zig
@@ -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,
         } },
     });
 }