authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-17 07:09:01+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-18 20:13:30+01:00
log09d6938df9246bac20e84f5512743e96bccdfa3d
treee9a4fc0d5c8f7a410d619526a89c45ddf18f73d9
parent9fce1df4cdab57951137c0da2b44fbe6da2442f2
signaturelock-open Commit is signed but in an unrecognized format.

wasm: add atomics opcodes and refactoring

This adds the atomic opcodes for the Threads proposal to the WebAssembly specification: https://github.com/WebAssembly/threads PrefixedOpcode has been renamed to MiscOpcode as there's multiple types of prefixed opcodes. This naming is similar to other tools such as LLVM. As we now use the 0xFE prefix, we moved the function_index MIR instruction as it was occupying the same value. This commit includes renaming all related opcodes.

5 files changed, 126 insertions(+), 25 deletions(-)

lib/std/wasm.zig+90-2
...@@ -189,7 +189,9 @@ pub const Opcode = enum(u8) {...@@ -189,7 +189,9 @@ pub const Opcode = enum(u8) {
189 i64_extend16_s = 0xC3,189 i64_extend16_s = 0xC3,
190 i64_extend32_s = 0xC4,190 i64_extend32_s = 0xC4,
191191
192 prefixed = 0xFC,192 misc_prefix = 0xFC,
193 simd_prefix = 0xFD,
194 atomics_prefix = 0xFE,
193 _,195 _,
194};196};
195197
...@@ -217,7 +219,7 @@ test "Wasm - opcodes" {...@@ -217,7 +219,7 @@ test "Wasm - opcodes" {
217/// Opcodes that require a prefix `0xFC`219/// Opcodes that require a prefix `0xFC`
218/// Each opcode represents a varuint32, meaning220/// Each opcode represents a varuint32, meaning
219/// they are encoded as leb128 in binary.221/// they are encoded as leb128 in binary.
220pub const PrefixedOpcode = enum(u32) {222pub const MiscOpcode = enum(u32) {
221 i32_trunc_sat_f32_s = 0x00,223 i32_trunc_sat_f32_s = 0x00,
222 i32_trunc_sat_f32_u = 0x01,224 i32_trunc_sat_f32_u = 0x01,
223 i32_trunc_sat_f64_s = 0x02,225 i32_trunc_sat_f64_s = 0x02,
...@@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {...@@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {
239 _,241 _,
240};242};
241243
244/// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler
245/// to write instructions to the wasm binary file
246pub fn miscOpcode(op: MiscOpcode) u32 {
247 return @enumToInt(op);
248}
249
242/// Simd opcodes that require a prefix `0xFD`.250/// Simd opcodes that require a prefix `0xFD`.
243/// Each opcode represents a varuint32, meaning251/// Each opcode represents a varuint32, meaning
244/// they are encoded as leb128 in binary.252/// they are encoded as leb128 in binary.
...@@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {...@@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {
510 return @enumToInt(op);518 return @enumToInt(op);
511}519}
512520
521/// Simd opcodes that require a prefix `0xFE`.
522/// Each opcode represents a varuint32, meaning
523/// they are encoded as leb128 in binary.
524pub const AtomicsOpcode = enum(u32) {
525 memory_atomic_notify = 0x00,
526 memory_atomic_wait32 = 0x01,
527 memory_atomic_wait64 = 0x02,
528 atomic_fence = 0x03,
529 i32_atomic_load = 0x10,
530 i64_atomic_load = 0x11,
531 i32_atomic_load8_u = 0x12,
532 i32_atomic_load16_u = 0x13,
533 i64_atomic_load8_u = 0x14,
534 i64_atomic_load16_u = 0x15,
535 i64_atomic_load32_u = 0x16,
536 i32_atomic_store = 0x17,
537 i64_atomic_store = 0x18,
538 i32_atomic_store8 = 0x19,
539 i32_atomic_store16 = 0x1A,
540 i64_atomic_store8 = 0x1B,
541 i64_atomic_store16 = 0x1C,
542 i64_atomic_store32 = 0x1D,
543 i32_atomic_rmw_add = 0x1E,
544 i64_atomic_rmw_add = 0x1F,
545 i32_atomic_rmw8_add_u = 0x20,
546 i32_atomic_rmw16_add_u = 0x21,
547 i64_atomic_rmw8_add_u = 0x22,
548 i64_atomic_rmw16_add_u = 0x23,
549 i64_atomic_rmw32_add_u = 0x24,
550 i32_atomic_rmw_sub = 0x25,
551 i64_atomic_rmw_sub = 0x26,
552 i32_atomic_rmw8_sub_u = 0x27A,
553 i32_atomic_rmw16_sub_u = 0x28A,
554 i64_atomic_rmw8_sub_u = 0x29A,
555 i64_atomic_rmw16_sub_u = 0x2A,
556 i64_atomic_rmw32_sub_u = 0x2B,
557 i32_atomic_rmw_and = 0x2C,
558 i64_atomic_rmw_and = 0x2D,
559 i32_atomic_rmw8_and_u = 0x2E,
560 i32_atomic_rmw16_and_u = 0x2F,
561 i64_atomic_rmw8_and_u = 0x30,
562 i64_atomic_rmw16_and_u = 0x31,
563 i64_atomic_rmw32_and_u = 0x32,
564 i32_atomic_rmw_or = 0x33,
565 i64_atomic_rmw_or = 0x34,
566 i32_atomic_rmw8_or_u = 0x35,
567 i32_atomic_rmw16_or_u = 0x36,
568 i64_atomic_rmw8_or_u = 0x37,
569 i64_atomic_rmw16_or_u = 0x38,
570 i64_atomic_rmw32_or_u = 0x39,
571 i32_atomic_rmw_xor = 0x3A,
572 i64_atomic_rmw_xor = 0x3B,
573 i32_atomic_rmw8_xor_u = 0x3C,
574 i32_atomic_rmw16_xor_u = 0x3D,
575 i64_atomic_rmw8_xor_u = 0x3E,
576 i64_atomic_rmw16_xor_u = 0x3F,
577 i64_atomic_rmw32_xor_u = 0x40,
578 i32_atomic_rmw_xchg = 0x41,
579 i64_atomic_rmw_xchg = 0x42,
580 i32_atomic_rmw8_xchg_u = 0x43,
581 i32_atomic_rmw16_xchg_u = 0x44,
582 i64_atomic_rmw8_xchg_u = 0x45,
583 i64_atomic_rmw16_xchg_u = 0x46,
584 i64_atomic_rmw32_xchg_u = 0x47,
585
586 i32_atomic_rmw_cmpxchg = 0x48,
587 i64_atomic_rmw_cmpxchg = 0x49,
588 i32_atomic_rmw8_cmpxchg_u = 0x4A,
589 i32_atomic_rmw16_cmpxchg_u = 0x4B,
590 i64_atomic_rmw8_cmpxchg_u = 0x4C,
591 i64_atomic_rmw16_cmpxchg_u = 0x4D,
592 i64_atomic_rmw32_cmpxchg_u = 0x4E,
593};
594
595/// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler
596/// to write instructions to the wasm binary file
597pub fn atomicsOpcode(op: AtomicsOpcode) u32 {
598 return @enumToInt(op);
599}
600
513/// Enum representing all Wasm value types as per spec:601/// Enum representing all Wasm value types as per spec:
514/// https://webassembly.github.io/spec/core/binary/types.html602/// https://webassembly.github.io/spec/core/binary/types.html
515pub const Valtype = enum(u8) {603pub const Valtype = enum(u8) {
src/arch/wasm/CodeGen.zig+7-7
...@@ -895,10 +895,10 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {...@@ -895,10 +895,10 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
895 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });895 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
896}896}
897897
898fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {898fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {
899 const extra_index = @intCast(u32, func.mir_extra.items.len);899 const extra_index = @intCast(u32, func.mir_extra.items.len);
900 try func.mir_extra.append(func.gpa, @enumToInt(opcode));900 try func.mir_extra.append(func.gpa, @enumToInt(opcode));
901 try func.addInst(.{ .tag = .extended, .data = .{ .payload = extra_index } });901 try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
902}902}
903903
904fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {904fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
...@@ -925,7 +925,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {...@@ -925,7 +925,7 @@ fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
925 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);925 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
926 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));926 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
927 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));927 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
928 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });928 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
929}929}
930930
931fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {931fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
...@@ -2310,7 +2310,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2310,7 +2310,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2310 offset + lhs.offset(),2310 offset + lhs.offset(),
2311 ty.abiAlignment(func.target),2311 ty.abiAlignment(func.target),
2312 });2312 });
2313 return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });2313 return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2314 },2314 },
2315 },2315 },
2316 .Pointer => {2316 .Pointer => {
...@@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2420 offset + operand.offset(),2420 offset + operand.offset(),
2421 ty.abiAlignment(func.target),2421 ty.abiAlignment(func.target),
2422 });2422 });
2423 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });2423 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
2424 return WValue{ .stack = {} };2424 return WValue{ .stack = {} };
2425 }2425 }
24262426
...@@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4477 operand.offset(),4477 operand.offset(),
4478 elem_ty.abiAlignment(func.target),4478 elem_ty.abiAlignment(func.target),
4479 });4479 });
4480 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });4480 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4481 try func.addLabel(.local_set, result.local.value);4481 try func.addLabel(.local_set, result.local.value);
4482 return func.finishAir(inst, result, &.{ty_op.operand});4482 return func.finishAir(inst, result, &.{ty_op.operand});
4483 },4483 },
...@@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4493 try func.emitWValue(operand);4493 try func.emitWValue(operand);
4494 const extra_index = @intCast(u32, func.mir_extra.items.len);4494 const extra_index = @intCast(u32, func.mir_extra.items.len);
4495 try func.mir_extra.append(func.gpa, opcode);4495 try func.mir_extra.append(func.gpa, opcode);
4496 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });4496 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
4497 try func.addLabel(.local_set, result.local.value);4497 try func.addLabel(.local_set, result.local.value);
4498 return func.finishAir(inst, result, &.{ty_op.operand});4498 return func.finishAir(inst, result, &.{ty_op.operand});
4499 },4499 },
src/arch/wasm/Emit.zig+12-6
...@@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {
239 .i64_clz => try emit.emitTag(tag),239 .i64_clz => try emit.emitTag(tag),
240 .i64_ctz => try emit.emitTag(tag),240 .i64_ctz => try emit.emitTag(tag),
241241
242 .extended => try emit.emitExtended(inst),242 .misc_prefix => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),243 .simd_prefix => try emit.emitSimd(inst),
244 .atomics_prefix => try emit.emitAtomic(inst),
244 }245 }
245 }246 }
246}247}
...@@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
433 const extra_index = emit.mir.instructions.items(.data)[inst].payload;434 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
434 const opcode = emit.mir.extra[extra_index];435 const opcode = emit.mir.extra[extra_index];
435 const writer = emit.code.writer();436 const writer = emit.code.writer();
436 try emit.code.append(0xFC);437 try emit.code.append(std.wasm.opcode(.misc_prefix));
437 try leb128.writeULEB128(writer, opcode);438 try leb128.writeULEB128(writer, opcode);
438 switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {439 switch (@intToEnum(std.wasm.MiscOpcode, opcode)) {
439 // bulk-memory opcodes440 // bulk-memory opcodes
440 .data_drop => {441 .data_drop => {
441 const segment = emit.mir.extra[extra_index + 1];442 const segment = emit.mir.extra[extra_index + 1];
...@@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
472 const extra_index = emit.mir.instructions.items(.data)[inst].payload;473 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
473 const opcode = emit.mir.extra[extra_index];474 const opcode = emit.mir.extra[extra_index];
474 const writer = emit.code.writer();475 const writer = emit.code.writer();
475 try emit.code.append(0xFD);476 try emit.code.append(std.wasm.opcode(.simd_prefix));
476 try leb128.writeULEB128(writer, opcode);477 try leb128.writeULEB128(writer, opcode);
477 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {478 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
478 .v128_store,479 .v128_store,
...@@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
496 .f32x4_splat,497 .f32x4_splat,
497 .f64x2_splat,498 .f64x2_splat,
498 => {}, // opcode already written499 => {}, // opcode already written
499 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}\n", .{@tagName(tag)}),500 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
500 }501 }
501}502}
502503
504fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
505 _ = inst;
506 return emit.fail("TODO: Implement atomics instructions", .{});
507}
508
503fn emitMemFill(emit: *Emit) !void {509fn emitMemFill(emit: *Emit) !void {
504 try emit.code.append(0xFC);510 try emit.code.append(0xFC);
505 try emit.code.append(0x0B);511 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+15-8
...@@ -87,6 +87,13 @@ pub const Inst = struct {...@@ -87,6 +87,13 @@ pub const Inst = struct {
87 ///87 ///
88 /// Uses `label`88 /// Uses `label`
89 call_indirect = 0x11,89 call_indirect = 0x11,
90 /// Contains a symbol to a function pointer
91 /// uses `label`
92 ///
93 /// Note: This uses `0x16` as value which is reserved by the WebAssembly
94 /// specification but unused, meaning we must update this if the specification were to
95 /// use this value.
96 function_index = 0x16,
90 /// Pops three values from the stack and pushes97 /// Pops three values from the stack and pushes
91 /// the first or second value dependent on the third value.98 /// the first or second value dependent on the third value.
92 /// Uses `tag`99 /// Uses `tag`
...@@ -510,24 +517,24 @@ pub const Inst = struct {...@@ -510,24 +517,24 @@ pub const Inst = struct {
510 i64_extend16_s = 0xC3,517 i64_extend16_s = 0xC3,
511 /// Uses `tag`518 /// Uses `tag`
512 i64_extend32_s = 0xC4,519 i64_extend32_s = 0xC4,
513 /// The instruction consists of an extension opcode.520 /// The instruction consists of a prefixed opcode.
514 /// The prefixed opcode can be found at payload's index.521 /// The prefixed opcode can be found at payload's index.
515 ///522 ///
516 /// The `data` field depends on the extension instruction and523 /// The `data` field depends on the extension instruction and
517 /// may contain additional data.524 /// may contain additional data.
518 extended = 0xFC,525 misc_prefix = 0xFC,
519 /// The instruction consists of a simd opcode.526 /// The instruction consists of a simd opcode.
520 /// The actual simd-opcode is found at payload's index.527 /// The actual simd-opcode is found at payload's index.
521 ///528 ///
522 /// The `data` field depends on the simd instruction and529 /// The `data` field depends on the simd instruction and
523 /// may contain additional data.530 /// may contain additional data.
524 simd = 0xFD,531 simd_prefix = 0xFD,
525 /// Contains a symbol to a function pointer532 /// The instruction consists of an atomics opcode.
526 /// uses `label`533 /// The actual atomics-opcode is found at payload's index.
527 ///534 ///
528 /// Note: This uses `0xFE` as value as it is unused and not reserved535 /// The `data` field depends on the atomics instruction and
529 /// by the wasm specification, making it safe to use.536 /// may contain additional data.
530 function_index = 0xFE,537 atomics_prefix = 0xFE,
531 /// Contains a symbol to a memory address538 /// Contains a symbol to a memory address
532 /// Uses `label`539 /// Uses `label`
533 ///540 ///
src/link/Wasm.zig+2-2
...@@ -2128,8 +2128,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {...@@ -2128,8 +2128,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
2128 }2128 }
21292129
2130 // perform the bulk-memory operation to initialize the data segment2130 // perform the bulk-memory operation to initialize the data segment
2131 try writer.writeByte(std.wasm.opcode(.prefixed));2131 try writer.writeByte(std.wasm.opcode(.misc_prefix));
2132 try leb.writeULEB128(writer, @enumToInt(std.wasm.PrefixedOpcode.memory_init));2132 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
2133 // segment immediate2133 // segment immediate
2134 try leb.writeULEB128(writer, @intCast(u32, data_index));2134 try leb.writeULEB128(writer, @intCast(u32, data_index));
2135 // memory index immediate (always 0)2135 // memory index immediate (always 0)