Commit 0d66112643

Luuk de Gram <luuk@degram.dev>
2022-12-20 19:18:09
wasm: refactor extended instructions
The extended instructions starting with opcode `0xFC` are refactored to make the work the same as the SIMD instructions. This means a `Mir` instruction no longer contains a field 'secondary'. Instead, we use the `payload` field to store the index into the extra list which contains the extended opcode value. In case of instructions such as 'memory.fill' which also have an immediate value, such values will also be stored in the extra list right after the instruction itself. This makes each `Mir` instruction smaller.
1 parent a52dcdd
Changed files (4)
lib/std/wasm.zig
@@ -215,7 +215,9 @@ test "Wasm - opcodes" {
 }
 
 /// Opcodes that require a prefix `0xFC`
-pub const PrefixedOpcode = enum(u8) {
+/// Each opcode represents a varuint32, meaning
+/// they are encoded as leb128 in binary.
+pub const PrefixedOpcode = enum(u32) {
     i32_trunc_sat_f32_s = 0x00,
     i32_trunc_sat_f32_u = 0x01,
     i32_trunc_sat_f64_s = 0x02,
src/arch/wasm/CodeGen.zig
@@ -905,7 +905,9 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
 }
 
 fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
-    try func.addInst(.{ .tag = .extended, .secondary = @enumToInt(opcode), .data = .{ .tag = {} } });
+    const extra_index = @intCast(u32, func.mir_extra.items.len);
+    try func.mir_extra.append(func.gpa, @enumToInt(opcode));
+    try func.addInst(.{ .tag = .extended, .data = .{ .payload = extra_index } });
 }
 
 fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
src/arch/wasm/Emit.zig
@@ -423,9 +423,40 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
 }
 
 fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
-    const opcode = emit.mir.instructions.items(.secondary)[inst];
+    const extra_index = emit.mir.instructions.items(.data)[inst].payload;
+    const opcode = emit.mir.extra[extra_index];
+    const writer = emit.code.writer();
+    try emit.code.append(0xFC);
+    try leb128.writeULEB128(writer, opcode);
     switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {
-        .memory_fill => try emit.emitMemFill(),
+        // bulk-memory opcodes
+        .data_drop => {
+            const segment = emit.mir.extra[extra_index + 1];
+            try leb128.writeULEB128(writer, segment);
+        },
+        .memory_init => {
+            const segment = emit.mir.extra[extra_index + 1];
+            try leb128.writeULEB128(writer, segment);
+            try leb128.writeULEB128(writer, @as(u32, 0)); // memory index
+        },
+        .memory_fill => {
+            try leb128.writeULEB128(writer, @as(u32, 0)); // memory index
+        },
+        .memory_copy => {
+            try leb128.writeULEB128(writer, @as(u32, 0)); // dst memory index
+            try leb128.writeULEB128(writer, @as(u32, 0)); // src memory index
+        },
+
+        // nontrapping-float-to-int-conversion opcodes
+        .i32_trunc_sat_f32_s,
+        .i32_trunc_sat_f32_u,
+        .i32_trunc_sat_f64_s,
+        .i32_trunc_sat_f64_u,
+        .i64_trunc_sat_f32_s,
+        .i64_trunc_sat_f32_u,
+        .i64_trunc_sat_f64_s,
+        .i64_trunc_sat_f64_u,
+        => {}, // opcode already written
         else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),
     }
 }
src/arch/wasm/Mir.zig
@@ -19,9 +19,6 @@ extra: []const u32,
 pub const Inst = struct {
     /// The opcode that represents this instruction
     tag: Tag,
-    /// This opcode will be set when `tag` represents an extended
-    /// instruction with prefix 0xFC, or a simd instruction with prefix 0xFD.
-    secondary: u8 = 0,
     /// Data is determined by the set `tag`.
     /// For example, `data` will be an i32 for when `tag` is 'i32_const'.
     data: Data,
@@ -513,10 +510,11 @@ pub const Inst = struct {
         i64_extend16_s = 0xC3,
         /// Uses `tag`
         i64_extend32_s = 0xC4,
-        /// The instruction consists of an extension opcode
-        /// set in `secondary`
+        /// The instruction consists of an extension opcode.
+        /// The prefixed opcode can be found at payload's index.
         ///
-        /// The `data` field depends on the extension instruction
+        /// The `data` field depends on the extension instruction and
+        /// may contain additional data.
         extended = 0xFC,
         /// The instruction consists of a simd opcode.
         /// The actual simd-opcode is found at payload's index.