mirror of
https://github.com/ziglang/zig.git
synced 2025-12-08 07:13: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_extend16_s = 0xC3,
|
||||||
i64_extend32_s = 0xC4,
|
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`
|
/// Opcodes that require a prefix `0xFC`
|
||||||
/// Each opcode represents a varuint32, meaning
|
/// Each opcode represents a varuint32, meaning
|
||||||
/// they are encoded as leb128 in binary.
|
/// 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_s = 0x00,
|
||||||
i32_trunc_sat_f32_u = 0x01,
|
i32_trunc_sat_f32_u = 0x01,
|
||||||
i32_trunc_sat_f64_s = 0x02,
|
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`.
|
/// Simd opcodes that require a prefix `0xFD`.
|
||||||
/// Each opcode represents a varuint32, meaning
|
/// Each opcode represents a varuint32, meaning
|
||||||
/// they are encoded as leb128 in binary.
|
/// they are encoded as leb128 in binary.
|
||||||
@ -510,6 +518,86 @@ pub fn simdOpcode(op: SimdOpcode) u32 {
|
|||||||
return @enumToInt(op);
|
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:
|
/// Enum representing all Wasm value types as per spec:
|
||||||
/// https://webassembly.github.io/spec/core/binary/types.html
|
/// https://webassembly.github.io/spec/core/binary/types.html
|
||||||
pub const Valtype = enum(u8) {
|
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 = {} } });
|
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);
|
const extra_index = @intCast(u32, func.mir_extra.items.len);
|
||||||
try func.mir_extra.append(func.gpa, @enumToInt(opcode));
|
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 {
|
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);
|
try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
|
||||||
func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
|
func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
|
||||||
func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
|
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 {
|
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(),
|
offset + lhs.offset(),
|
||||||
ty.abiAlignment(func.target),
|
ty.abiAlignment(func.target),
|
||||||
});
|
});
|
||||||
return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
|
return func.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.Pointer => {
|
.Pointer => {
|
||||||
@ -2420,7 +2420,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
|
|||||||
offset + operand.offset(),
|
offset + operand.offset(),
|
||||||
ty.abiAlignment(func.target),
|
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 = {} };
|
return WValue{ .stack = {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4477,7 +4477,7 @@ fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
|||||||
operand.offset(),
|
operand.offset(),
|
||||||
elem_ty.abiAlignment(func.target),
|
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);
|
try func.addLabel(.local_set, result.local.value);
|
||||||
return func.finishAir(inst, result, &.{ty_op.operand});
|
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);
|
try func.emitWValue(operand);
|
||||||
const extra_index = @intCast(u32, func.mir_extra.items.len);
|
const extra_index = @intCast(u32, func.mir_extra.items.len);
|
||||||
try func.mir_extra.append(func.gpa, opcode);
|
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);
|
try func.addLabel(.local_set, result.local.value);
|
||||||
return func.finishAir(inst, result, &.{ty_op.operand});
|
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_clz => try emit.emitTag(tag),
|
||||||
.i64_ctz => try emit.emitTag(tag),
|
.i64_ctz => try emit.emitTag(tag),
|
||||||
|
|
||||||
.extended => try emit.emitExtended(inst),
|
.misc_prefix => try emit.emitExtended(inst),
|
||||||
.simd => try emit.emitSimd(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 extra_index = emit.mir.instructions.items(.data)[inst].payload;
|
||||||
const opcode = emit.mir.extra[extra_index];
|
const opcode = emit.mir.extra[extra_index];
|
||||||
const writer = emit.code.writer();
|
const writer = emit.code.writer();
|
||||||
try emit.code.append(0xFC);
|
try emit.code.append(std.wasm.opcode(.misc_prefix));
|
||||||
try leb128.writeULEB128(writer, opcode);
|
try leb128.writeULEB128(writer, opcode);
|
||||||
switch (@intToEnum(std.wasm.PrefixedOpcode, opcode)) {
|
switch (@intToEnum(std.wasm.MiscOpcode, opcode)) {
|
||||||
// bulk-memory opcodes
|
// bulk-memory opcodes
|
||||||
.data_drop => {
|
.data_drop => {
|
||||||
const segment = emit.mir.extra[extra_index + 1];
|
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 extra_index = emit.mir.instructions.items(.data)[inst].payload;
|
||||||
const opcode = emit.mir.extra[extra_index];
|
const opcode = emit.mir.extra[extra_index];
|
||||||
const writer = emit.code.writer();
|
const writer = emit.code.writer();
|
||||||
try emit.code.append(0xFD);
|
try emit.code.append(std.wasm.opcode(.simd_prefix));
|
||||||
try leb128.writeULEB128(writer, opcode);
|
try leb128.writeULEB128(writer, opcode);
|
||||||
switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
|
switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
|
||||||
.v128_store,
|
.v128_store,
|
||||||
@ -496,10 +497,15 @@ fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
|
|||||||
.f32x4_splat,
|
.f32x4_splat,
|
||||||
.f64x2_splat,
|
.f64x2_splat,
|
||||||
=> {}, // opcode already written
|
=> {}, // 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 {
|
fn emitMemFill(emit: *Emit) !void {
|
||||||
try emit.code.append(0xFC);
|
try emit.code.append(0xFC);
|
||||||
try emit.code.append(0x0B);
|
try emit.code.append(0x0B);
|
||||||
|
|||||||
@ -87,6 +87,13 @@ pub const Inst = struct {
|
|||||||
///
|
///
|
||||||
/// Uses `label`
|
/// Uses `label`
|
||||||
call_indirect = 0x11,
|
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
|
/// Pops three values from the stack and pushes
|
||||||
/// the first or second value dependent on the third value.
|
/// the first or second value dependent on the third value.
|
||||||
/// Uses `tag`
|
/// Uses `tag`
|
||||||
@ -510,24 +517,24 @@ pub const Inst = struct {
|
|||||||
i64_extend16_s = 0xC3,
|
i64_extend16_s = 0xC3,
|
||||||
/// Uses `tag`
|
/// Uses `tag`
|
||||||
i64_extend32_s = 0xC4,
|
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 prefixed opcode can be found at payload's index.
|
||||||
///
|
///
|
||||||
/// The `data` field depends on the extension instruction and
|
/// The `data` field depends on the extension instruction and
|
||||||
/// may contain additional data.
|
/// may contain additional data.
|
||||||
extended = 0xFC,
|
misc_prefix = 0xFC,
|
||||||
/// The instruction consists of a simd opcode.
|
/// The instruction consists of a simd opcode.
|
||||||
/// The actual simd-opcode is found at payload's index.
|
/// The actual simd-opcode is found at payload's index.
|
||||||
///
|
///
|
||||||
/// The `data` field depends on the simd instruction and
|
/// The `data` field depends on the simd instruction and
|
||||||
/// may contain additional data.
|
/// may contain additional data.
|
||||||
simd = 0xFD,
|
simd_prefix = 0xFD,
|
||||||
/// Contains a symbol to a function pointer
|
/// The instruction consists of an atomics opcode.
|
||||||
/// uses `label`
|
/// The actual atomics-opcode is found at payload's index.
|
||||||
///
|
///
|
||||||
/// Note: This uses `0xFE` as value as it is unused and not reserved
|
/// The `data` field depends on the atomics instruction and
|
||||||
/// by the wasm specification, making it safe to use.
|
/// may contain additional data.
|
||||||
function_index = 0xFE,
|
atomics_prefix = 0xFE,
|
||||||
/// Contains a symbol to a memory address
|
/// Contains a symbol to a memory address
|
||||||
/// Uses `label`
|
/// Uses `label`
|
||||||
///
|
///
|
||||||
|
|||||||
@ -2128,8 +2128,8 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// perform the bulk-memory operation to initialize the data segment
|
// perform the bulk-memory operation to initialize the data segment
|
||||||
try writer.writeByte(std.wasm.opcode(.prefixed));
|
try writer.writeByte(std.wasm.opcode(.misc_prefix));
|
||||||
try leb.writeULEB128(writer, @enumToInt(std.wasm.PrefixedOpcode.memory_init));
|
try leb.writeULEB128(writer, std.wasm.miscOpcode(.memory_init));
|
||||||
// segment immediate
|
// segment immediate
|
||||||
try leb.writeULEB128(writer, @intCast(u32, data_index));
|
try leb.writeULEB128(writer, @intCast(u32, data_index));
|
||||||
// memory index immediate (always 0)
|
// memory index immediate (always 0)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user