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) {
189189 i64_extend16_s = 0xC3,
190190 i64_extend32_s = 0xC4,
191191
192 prefixed = 0xFC,
192 misc_prefix = 0xFC,
193 simd_prefix = 0xFD,
194 atomics_prefix = 0xFE,
193195 _,
194196};
195197
......@@ -217,7 +219,7 @@ test "Wasm - opcodes" {
217219/// Opcodes that require a prefix `0xFC`
218220/// Each opcode represents a varuint32, meaning
219221/// they are encoded as leb128 in binary.
220pub const PrefixedOpcode = enum(u32) {
222pub const MiscOpcode = enum(u32) {
221223 i32_trunc_sat_f32_s = 0x00,
222224 i32_trunc_sat_f32_u = 0x01,
223225 i32_trunc_sat_f64_s = 0x02,
......@@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {
239241 _,
240242};
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
242250/// Simd opcodes that require a prefix `0xFD`.
243251/// Each opcode represents a varuint32, meaning
244252/// they are encoded as leb128 in binary.
......@@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {
510518 return @enumToInt(op);
511519}
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
513601/// Enum representing all Wasm value types as per spec:
514602/// https://webassembly.github.io/spec/core/binary/types.html
515603pub 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 {
895895 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
896896}
897897
898fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
898fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {
899899 const extra_index = @intCast(u32, func.mir_extra.items.len);
900900 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 } });
902902}
903903
904904fn 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 {
925925 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
926926 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
927927 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 } });
929929}
930930
931931fn 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
23102310 offset + lhs.offset(),
23112311 ty.abiAlignment(func.target),
23122312 });
2313 return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2313 return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
23142314 },
23152315 },
23162316 .Pointer => {
......@@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
24202420 offset + operand.offset(),
24212421 ty.abiAlignment(func.target),
24222422 });
2423 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2423 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
24242424 return WValue{ .stack = {} };
24252425 }
24262426
......@@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44774477 operand.offset(),
44784478 elem_ty.abiAlignment(func.target),
44794479 });
4480 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
4480 try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
44814481 try func.addLabel(.local_set, result.local.value);
44824482 return func.finishAir(inst, result, &.{ty_op.operand});
44834483 },
......@@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44934493 try func.emitWValue(operand);
44944494 const extra_index = @intCast(u32, func.mir_extra.items.len);
44954495 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 } });
44974497 try func.addLabel(.local_set, result.local.value);
44984498 return func.finishAir(inst, result, &.{ty_op.operand});
44994499 },
src/arch/wasm/Emit.zig+12-6
......@@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {
239239 .i64_clz => try emit.emitTag(tag),
240240 .i64_ctz => try emit.emitTag(tag),
241241
242 .extended => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),
242 .misc_prefix => try emit.emitExtended(inst),
243 .simd_prefix => try emit.emitSimd(inst),
244 .atomics_prefix => try emit.emitAtomic(inst),
244245 }
245246 }
246247}
......@@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
433434 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
434435 const opcode = emit.mir.extra[extra_index];
435436 const writer = emit.code.writer();
436 try emit.code.append(0xFC);
437 try emit.code.append(std.wasm.opcode(.misc_prefix));
437438 try leb128.writeULEB128(writer, opcode);
438 switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {
439 switch (@intToEnum(std.wasm.MiscOpcode, opcode)) {
439440 // bulk-memory opcodes
440441 .data_drop => {
441442 const segment = emit.mir.extra[extra_index + 1];
......@@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
472473 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
473474 const opcode = emit.mir.extra[extra_index];
474475 const writer = emit.code.writer();
475 try emit.code.append(0xFD);
476 try emit.code.append(std.wasm.opcode(.simd_prefix));
476477 try leb128.writeULEB128(writer, opcode);
477478 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
478479 .v128_store,
......@@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
496497 .f32x4_splat,
497498 .f64x2_splat,
498499 => {}, // 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)}),
500501 }
501502}
502503
504fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
505 _ = inst;
506 return emit.fail("TODO: Implement atomics instructions", .{});
507}
508
503509fn emitMemFill(emit: *Emit) !void {
504510 try emit.code.append(0xFC);
505511 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+15-8
......@@ -87,6 +87,13 @@ pub const Inst = struct {
8787 ///
8888 /// Uses `label`
8989 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,
9097 /// Pops three values from the stack and pushes
9198 /// the first or second value dependent on the third value.
9299 /// Uses `tag`
......@@ -510,24 +517,24 @@ pub const Inst = struct {
510517 i64_extend16_s = 0xC3,
511518 /// Uses `tag`
512519 i64_extend32_s = 0xC4,
513 /// The instruction consists of an extension opcode.
520 /// The instruction consists of a prefixed opcode.
514521 /// The prefixed opcode can be found at payload's index.
515522 ///
516523 /// The `data` field depends on the extension instruction and
517524 /// may contain additional data.
518 extended = 0xFC,
525 misc_prefix = 0xFC,
519526 /// The instruction consists of a simd opcode.
520527 /// The actual simd-opcode is found at payload's index.
521528 ///
522529 /// The `data` field depends on the simd instruction and
523530 /// may contain additional data.
524 simd = 0xFD,
525 /// Contains a symbol to a function pointer
526 /// uses `label`
531 simd_prefix = 0xFD,
532 /// The instruction consists of an atomics opcode.
533 /// The actual atomics-opcode is found at payload's index.
527534 ///
528 /// Note: This uses `0xFE` as value as it is unused and not reserved
529 /// by the wasm specification, making it safe to use.
530 function_index = 0xFE,
535 /// The `data` field depends on the atomics instruction and
536 /// may contain additional data.
537 atomics_prefix = 0xFE,
531538 /// Contains a symbol to a memory address
532539 /// Uses `label`
533540 ///
src/link/Wasm.zig+2-2
......@@ -2128,8 +2128,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
21282128 }
21292129
21302130 // perform the bulk-memory operation to initialize the data segment
2131 try writer.writeByte(std.wasm.opcode(.prefixed));
2132 try leb.writeULEB128(writer, @enumToInt(std.wasm.PrefixedOpcode.memory_init));
2131 try writer.writeByte(std.wasm.opcode(.misc_prefix));
2132 try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
21332133 // segment immediate
21342134 try leb.writeULEB128(writer, @intCast(u32, data_index));
21352135 // memory index immediate (always 0)