mirror of
https://github.com/ziglang/zig.git
synced 2025-12-07 14:53:08 +00:00
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.
This commit is contained in:
parent
9fce1df4cd
commit
09d6938df9
@ -189,7 +189,9 @@ pub const Opcode = enum(u8) {
|
||||
i64_extend16_s = 0xC3,
|
||||
i64_extend32_s = 0xC4,
|
||||
|
||||
prefixed = 0xFC,
|
||||
misc_prefix = 0xFC,
|
||||
simd_prefix = 0xFD,
|
||||
atomics_prefix = 0xFE,
|
||||
_,
|
||||
};
|
||||
|
||||
@ -217,7 +219,7 @@ test "Wasm - opcodes" {
|
||||
/// Opcodes that require a prefix `0xFC`
|
||||
/// Each opcode represents a varuint32, meaning
|
||||
/// they are encoded as leb128 in binary.
|
||||
pub const PrefixedOpcode = enum(u32) {
|
||||
pub const MiscOpcode = enum(u32) {
|
||||
i32_trunc_sat_f32_s = 0x00,
|
||||
i32_trunc_sat_f32_u = 0x01,
|
||||
i32_trunc_sat_f64_s = 0x02,
|
||||
@ -239,6 +241,12 @@ pub const PrefixedOpcode = enum(u32) {
|
||||
_,
|
||||
};
|
||||
|
||||
/// Returns the integer value of an `MiscOpcode`. Used by the Zig compiler
|
||||
/// to write instructions to the wasm binary file
|
||||
pub fn miscOpcode(op: MiscOpcode) u32 {
|
||||
return @enumToInt(op);
|
||||
}
|
||||
|
||||
/// Simd opcodes that require a prefix `0xFD`.
|
||||
/// Each opcode represents a varuint32, meaning
|
||||
/// they are encoded as leb128 in binary.
|
||||
@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {
|
||||
return @enumToInt(op);
|
||||
}
|
||||
|
||||
/// Simd opcodes that require a prefix `0xFE`.
|
||||
/// Each opcode represents a varuint32, meaning
|
||||
/// they are encoded as leb128 in binary.
|
||||
pub const AtomicsOpcode = enum(u32) {
|
||||
memory_atomic_notify = 0x00,
|
||||
memory_atomic_wait32 = 0x01,
|
||||
memory_atomic_wait64 = 0x02,
|
||||
atomic_fence = 0x03,
|
||||
i32_atomic_load = 0x10,
|
||||
i64_atomic_load = 0x11,
|
||||
i32_atomic_load8_u = 0x12,
|
||||
i32_atomic_load16_u = 0x13,
|
||||
i64_atomic_load8_u = 0x14,
|
||||
i64_atomic_load16_u = 0x15,
|
||||
i64_atomic_load32_u = 0x16,
|
||||
i32_atomic_store = 0x17,
|
||||
i64_atomic_store = 0x18,
|
||||
i32_atomic_store8 = 0x19,
|
||||
i32_atomic_store16 = 0x1A,
|
||||
i64_atomic_store8 = 0x1B,
|
||||
i64_atomic_store16 = 0x1C,
|
||||
i64_atomic_store32 = 0x1D,
|
||||
i32_atomic_rmw_add = 0x1E,
|
||||
i64_atomic_rmw_add = 0x1F,
|
||||
i32_atomic_rmw8_add_u = 0x20,
|
||||
i32_atomic_rmw16_add_u = 0x21,
|
||||
i64_atomic_rmw8_add_u = 0x22,
|
||||
i64_atomic_rmw16_add_u = 0x23,
|
||||
i64_atomic_rmw32_add_u = 0x24,
|
||||
i32_atomic_rmw_sub = 0x25,
|
||||
i64_atomic_rmw_sub = 0x26,
|
||||
i32_atomic_rmw8_sub_u = 0x27A,
|
||||
i32_atomic_rmw16_sub_u = 0x28A,
|
||||
i64_atomic_rmw8_sub_u = 0x29A,
|
||||
i64_atomic_rmw16_sub_u = 0x2A,
|
||||
i64_atomic_rmw32_sub_u = 0x2B,
|
||||
i32_atomic_rmw_and = 0x2C,
|
||||
i64_atomic_rmw_and = 0x2D,
|
||||
i32_atomic_rmw8_and_u = 0x2E,
|
||||
i32_atomic_rmw16_and_u = 0x2F,
|
||||
i64_atomic_rmw8_and_u = 0x30,
|
||||
i64_atomic_rmw16_and_u = 0x31,
|
||||
i64_atomic_rmw32_and_u = 0x32,
|
||||
i32_atomic_rmw_or = 0x33,
|
||||
i64_atomic_rmw_or = 0x34,
|
||||
i32_atomic_rmw8_or_u = 0x35,
|
||||
i32_atomic_rmw16_or_u = 0x36,
|
||||
i64_atomic_rmw8_or_u = 0x37,
|
||||
i64_atomic_rmw16_or_u = 0x38,
|
||||
i64_atomic_rmw32_or_u = 0x39,
|
||||
i32_atomic_rmw_xor = 0x3A,
|
||||
i64_atomic_rmw_xor = 0x3B,
|
||||
i32_atomic_rmw8_xor_u = 0x3C,
|
||||
i32_atomic_rmw16_xor_u = 0x3D,
|
||||
i64_atomic_rmw8_xor_u = 0x3E,
|
||||
i64_atomic_rmw16_xor_u = 0x3F,
|
||||
i64_atomic_rmw32_xor_u = 0x40,
|
||||
i32_atomic_rmw_xchg = 0x41,
|
||||
i64_atomic_rmw_xchg = 0x42,
|
||||
i32_atomic_rmw8_xchg_u = 0x43,
|
||||
i32_atomic_rmw16_xchg_u = 0x44,
|
||||
i64_atomic_rmw8_xchg_u = 0x45,
|
||||
i64_atomic_rmw16_xchg_u = 0x46,
|
||||
i64_atomic_rmw32_xchg_u = 0x47,
|
||||
|
||||
i32_atomic_rmw_cmpxchg = 0x48,
|
||||
i64_atomic_rmw_cmpxchg = 0x49,
|
||||
i32_atomic_rmw8_cmpxchg_u = 0x4A,
|
||||
i32_atomic_rmw16_cmpxchg_u = 0x4B,
|
||||
i64_atomic_rmw8_cmpxchg_u = 0x4C,
|
||||
i64_atomic_rmw16_cmpxchg_u = 0x4D,
|
||||
i64_atomic_rmw32_cmpxchg_u = 0x4E,
|
||||
};
|
||||
|
||||
/// Returns the integer value of an `AtomicsOpcode`. Used by the Zig compiler
|
||||
/// to write instructions to the wasm binary file
|
||||
pub fn atomicsOpcode(op: AtomicsOpcode) u32 {
|
||||
return @enumToInt(op);
|
||||
}
|
||||
|
||||
/// Enum representing all Wasm value types as per spec:
|
||||
/// https://webassembly.github.io/spec/core/binary/types.html
|
||||
pub const Valtype = enum(u8) {
|
||||
|
||||
@ -895,10 +895,10 @@ fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
|
||||
try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
|
||||
}
|
||||
|
||||
fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
|
||||
fn addExtended(func: *CodeGen, opcode: wasm.MiscOpcode) error{OutOfMemory}!void {
|
||||
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 } });
|
||||
try func.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
|
||||
}
|
||||
|
||||
fn 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 {
|
||||
try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
|
||||
func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
|
||||
func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
|
||||
try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
||||
try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||
}
|
||||
|
||||
fn 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
|
||||
offset + lhs.offset(),
|
||||
ty.abiAlignment(func.target),
|
||||
});
|
||||
return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
||||
return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||
},
|
||||
},
|
||||
.Pointer => {
|
||||
@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
|
||||
offset + operand.offset(),
|
||||
ty.abiAlignment(func.target),
|
||||
});
|
||||
try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
||||
try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||
return WValue{ .stack = {} };
|
||||
}
|
||||
|
||||
@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||
operand.offset(),
|
||||
elem_ty.abiAlignment(func.target),
|
||||
});
|
||||
try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
||||
try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||
try func.addLabel(.local_set, result.local.value);
|
||||
return func.finishAir(inst, result, &.{ty_op.operand});
|
||||
},
|
||||
@ -4493,7 +4493,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||
try func.emitWValue(operand);
|
||||
const extra_index = @intCast(u32, func.mir_extra.items.len);
|
||||
try func.mir_extra.append(func.gpa, opcode);
|
||||
try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
||||
try func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||
try func.addLabel(.local_set, result.local.value);
|
||||
return func.finishAir(inst, result, &.{ty_op.operand});
|
||||
},
|
||||
|
||||
@ -239,8 +239,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {
|
||||
.i64_clz => try emit.emitTag(tag),
|
||||
.i64_ctz => try emit.emitTag(tag),
|
||||
|
||||
.extended => try emit.emitExtended(inst),
|
||||
.simd => try emit.emitSimd(inst),
|
||||
.misc_prefix => try emit.emitExtended(inst),
|
||||
.simd_prefix => try emit.emitSimd(inst),
|
||||
.atomics_prefix => try emit.emitAtomic(inst),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -433,9 +434,9 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
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 emit.code.append(std.wasm.opcode(.misc_prefix));
|
||||
try leb128.writeULEB128(writer, opcode);
|
||||
switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {
|
||||
switch (@intToEnum(std.wasm.MiscOpcode, opcode)) {
|
||||
// bulk-memory opcodes
|
||||
.data_drop => {
|
||||
const segment = emit.mir.extra[extra_index + 1];
|
||||
@ -472,7 +473,7 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
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(0xFD);
|
||||
try emit.code.append(std.wasm.opcode(.simd_prefix));
|
||||
try leb128.writeULEB128(writer, opcode);
|
||||
switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
|
||||
.v128_store,
|
||||
@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
.f32x4_splat,
|
||||
.f64x2_splat,
|
||||
=> {}, // opcode already written
|
||||
else => |tag| return emit.fail("TODO: Implement simd instruction: {s}\n", .{@tagName(tag)}),
|
||||
else => |tag| return emit.fail("TODO: Implement simd instruction: {s}", .{@tagName(tag)}),
|
||||
}
|
||||
}
|
||||
|
||||
fn emitAtomic(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return emit.fail("TODO: Implement atomics instructions", .{});
|
||||
}
|
||||
|
||||
fn emitMemFill(emit: *Emit) !void {
|
||||
try emit.code.append(0xFC);
|
||||
try emit.code.append(0x0B);
|
||||
|
||||
@ -87,6 +87,13 @@ pub const Inst = struct {
|
||||
///
|
||||
/// Uses `label`
|
||||
call_indirect = 0x11,
|
||||
/// Contains a symbol to a function pointer
|
||||
/// uses `label`
|
||||
///
|
||||
/// Note: This uses `0x16` as value which is reserved by the WebAssembly
|
||||
/// specification but unused, meaning we must update this if the specification were to
|
||||
/// use this value.
|
||||
function_index = 0x16,
|
||||
/// Pops three values from the stack and pushes
|
||||
/// the first or second value dependent on the third value.
|
||||
/// Uses `tag`
|
||||
@ -510,24 +517,24 @@ pub const Inst = struct {
|
||||
i64_extend16_s = 0xC3,
|
||||
/// Uses `tag`
|
||||
i64_extend32_s = 0xC4,
|
||||
/// The instruction consists of an extension opcode.
|
||||
/// The instruction consists of a prefixed opcode.
|
||||
/// The prefixed opcode can be found at payload's index.
|
||||
///
|
||||
/// The `data` field depends on the extension instruction and
|
||||
/// may contain additional data.
|
||||
extended = 0xFC,
|
||||
misc_prefix = 0xFC,
|
||||
/// The instruction consists of a simd opcode.
|
||||
/// The actual simd-opcode is found at payload's index.
|
||||
///
|
||||
/// The `data` field depends on the simd instruction and
|
||||
/// may contain additional data.
|
||||
simd = 0xFD,
|
||||
/// Contains a symbol to a function pointer
|
||||
/// uses `label`
|
||||
simd_prefix = 0xFD,
|
||||
/// The instruction consists of an atomics opcode.
|
||||
/// The actual atomics-opcode is found at payload's index.
|
||||
///
|
||||
/// Note: This uses `0xFE` as value as it is unused and not reserved
|
||||
/// by the wasm specification, making it safe to use.
|
||||
function_index = 0xFE,
|
||||
/// The `data` field depends on the atomics instruction and
|
||||
/// may contain additional data.
|
||||
atomics_prefix = 0xFE,
|
||||
/// Contains a symbol to a memory address
|
||||
/// Uses `label`
|
||||
///
|
||||
|
||||
@ -2128,8 +2128,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
|
||||
}
|
||||
|
||||
// perform the bulk-memory operation to initialize the data segment
|
||||
try writer.writeByte(std.wasm.opcode(.prefixed));
|
||||
try leb.writeULEB128(writer, @enumToInt(std.wasm.PrefixedOpcode.memory_init));
|
||||
try writer.writeByte(std.wasm.opcode(.misc_prefix));
|
||||
try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
|
||||
// segment immediate
|
||||
try leb.writeULEB128(writer, @intCast(u32, data_index));
|
||||
// memory index immediate (always 0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user