authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-20 19:18:09+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-21 17:02:53+01:00
log0d66112643286a754101eb71382f860ae1874012
tree80fd56e6adff4e99cbc3638fc8f03a967b0be61e
parenta52dcdd3c5ebd3a1d5a0189ded830dadd253193c

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.

4 files changed, 43 insertions(+), 10 deletions(-)

lib/std/wasm.zig+3-1
...@@ -215,7 +215,9 @@ test "Wasm - opcodes" {...@@ -215,7 +215,9 @@ test "Wasm - opcodes" {
215}215}
216216
217/// Opcodes that require a prefix `0xFC`217/// Opcodes that require a prefix `0xFC`
218pub const PrefixedOpcode = enum(u8) {218/// Each opcode represents a varuint32, meaning
219/// they are encoded as leb128 in binary.
220pub const PrefixedOpcode = enum(u32) {
219 i32_trunc_sat_f32_s = 0x00,221 i32_trunc_sat_f32_s = 0x00,
220 i32_trunc_sat_f32_u = 0x01,222 i32_trunc_sat_f32_u = 0x01,
221 i32_trunc_sat_f64_s = 0x02,223 i32_trunc_sat_f64_s = 0x02,
src/arch/wasm/CodeGen.zig+3-1
...@@ -905,7 +905,9 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {...@@ -905,7 +905,9 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
905}905}
906906
907fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {907fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
908 try func.addInst(.{ .tag = .extended, .secondary = @enumToInt(opcode), .data = .{ .tag = {} } });908 const extra_index = @intCast(u32, func.mir_extra.items.len);
909 try func.mir_extra.append(func.gpa, @enumToInt(opcode));
910 try func.addInst(.{ .tag = .extended, .data = .{ .payload = extra_index } });
909}911}
910912
911fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {913fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
src/arch/wasm/Emit.zig+33-2
...@@ -423,9 +423,40 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -423,9 +423,40 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
423}423}
424424
425fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {425fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
426 const opcode = emit.mir.instructions.items(.secondary)[inst];426 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
427 const opcode = emit.mir.extra[extra_index];
428 const writer = emit.code.writer();
429 try emit.code.append(0xFC);
430 try leb128.writeULEB128(writer, opcode);
427 switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {431 switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {
428 .memory_fill => try emit.emitMemFill(),432 // bulk-memory opcodes
433 .data_drop => {
434 const segment = emit.mir.extra[extra_index + 1];
435 try leb128.writeULEB128(writer, segment);
436 },
437 .memory_init => {
438 const segment = emit.mir.extra[extra_index + 1];
439 try leb128.writeULEB128(writer, segment);
440 try leb128.writeULEB128(writer, @as(u32, 0)); // memory index
441 },
442 .memory_fill => {
443 try leb128.writeULEB128(writer, @as(u32, 0)); // memory index
444 },
445 .memory_copy => {
446 try leb128.writeULEB128(writer, @as(u32, 0)); // dst memory index
447 try leb128.writeULEB128(writer, @as(u32, 0)); // src memory index
448 },
449
450 // nontrapping-float-to-int-conversion opcodes
451 .i32_trunc_sat_f32_s,
452 .i32_trunc_sat_f32_u,
453 .i32_trunc_sat_f64_s,
454 .i32_trunc_sat_f64_u,
455 .i64_trunc_sat_f32_s,
456 .i64_trunc_sat_f32_u,
457 .i64_trunc_sat_f64_s,
458 .i64_trunc_sat_f64_u,
459 => {}, // opcode already written
429 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),460 else => |tag| return emit.fail("TODO: Implement extension instruction: {s}\n", .{@tagName(tag)}),
430 }461 }
431}462}
src/arch/wasm/Mir.zig+4-6
...@@ -19,9 +19,6 @@ extra: []const u32,...@@ -19,9 +19,6 @@ extra: []const u32,
19pub const Inst = struct {19pub const Inst = struct {
20 /// The opcode that represents this instruction20 /// The opcode that represents this instruction
21 tag: Tag,21 tag: Tag,
22 /// This opcode will be set when `tag` represents an extended
23 /// instruction with prefix 0xFC, or a simd instruction with prefix 0xFD.
24 secondary: u8 = 0,
25 /// Data is determined by the set `tag`.22 /// Data is determined by the set `tag`.
26 /// For example, `data` will be an i32 for when `tag` is 'i32_const'.23 /// For example, `data` will be an i32 for when `tag` is 'i32_const'.
27 data: Data,24 data: Data,
...@@ -513,10 +510,11 @@ pub const Inst = struct {...@@ -513,10 +510,11 @@ pub const Inst = struct {
513 i64_extend16_s = 0xC3,510 i64_extend16_s = 0xC3,
514 /// Uses `tag`511 /// Uses `tag`
515 i64_extend32_s = 0xC4,512 i64_extend32_s = 0xC4,
516 /// The instruction consists of an extension opcode513 /// The instruction consists of an extension opcode.
517 /// set in `secondary`514 /// The prefixed opcode can be found at payload's index.
518 ///515 ///
519 /// The `data` field depends on the extension instruction516 /// The `data` field depends on the extension instruction and
517 /// may contain additional data.
520 extended = 0xFC,518 extended = 0xFC,
521 /// The instruction consists of a simd opcode.519 /// The instruction consists of a simd opcode.
522 /// The actual simd-opcode is found at payload's index.520 /// The actual simd-opcode is found at payload's index.