Merge pull request #22386 from jacobly0/x86_64-rewrite

x86_64: begin rewriting instruction selection
This commit is contained in:
Andrew Kelley 2025-01-17 01:20:11 -05:00 committed by GitHub
commit 4bace0f621
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 15600 additions and 3490 deletions

View File

@ -6,13 +6,13 @@
/// `null` means native.
cpu_arch: ?Target.Cpu.Arch = null,
cpu_model: CpuModel = CpuModel.determined_by_arch_os,
cpu_model: CpuModel = .determined_by_arch_os,
/// Sparse set of CPU features to add to the set from `cpu_model`.
cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
cpu_features_add: Target.Cpu.Feature.Set = .empty,
/// Sparse set of CPU features to remove from the set from `cpu_model`.
cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
cpu_features_sub: Target.Cpu.Feature.Set = .empty,
/// `null` means native.
os_tag: ?Target.Os.Tag = null,
@ -38,7 +38,7 @@ abi: ?Target.Abi = null,
/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
/// based on the `os_tag`.
dynamic_linker: Target.DynamicLinker = Target.DynamicLinker.none,
dynamic_linker: Target.DynamicLinker = .none,
/// `null` means default for the cpu/arch/os combo.
ofmt: ?Target.ObjectFormat = null,

View File

@ -47,6 +47,7 @@ pub const Feature = enum {
bmi2,
branch_hint,
branchfusion,
bsf_bsr_0_clobbers_result,
ccmp,
cf,
cldemote,
@ -167,6 +168,8 @@ pub const Feature = enum {
slow_unaligned_mem_32,
sm3,
sm4,
smap,
smep,
soft_float,
sse,
sse2,
@ -497,6 +500,11 @@ pub const all_features = blk: {
.description = "CMP/TEST can be fused with conditional branches",
.dependencies = featureSet(&[_]Feature{}),
};
result[@intFromEnum(Feature.bsf_bsr_0_clobbers_result)] = .{
.llvm_name = null,
.description = "BSF/BSR may clobber the lower 32-bits of the result register when the source is zero",
.dependencies = featureSet(&[_]Feature{}),
};
result[@intFromEnum(Feature.ccmp)] = .{
.llvm_name = "ccmp",
.description = "Support conditional cmp & test instructions",
@ -1127,6 +1135,16 @@ pub const all_features = blk: {
.avx2,
}),
};
result[@intFromEnum(Feature.smap)] = .{
.llvm_name = null,
.description = "Enable Supervisor Mode Access Prevention",
.dependencies = featureSet(&[_]Feature{}),
};
result[@intFromEnum(Feature.smep)] = .{
.llvm_name = null,
.description = "Enable Supervisor Mode Execution Prevention",
.dependencies = featureSet(&[_]Feature{}),
};
result[@intFromEnum(Feature.soft_float)] = .{
.llvm_name = "soft-float",
.description = "Use software floating point features",
@ -1371,6 +1389,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vaes,
.vpclmulqdq,
@ -1467,6 +1487,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.uintr,
.vaes,
@ -1545,6 +1567,8 @@ pub const cpu = struct {
.slow_3ops_lea,
.sm3,
.sm4,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.uintr,
.vaes,
@ -1783,6 +1807,8 @@ pub const cpu = struct {
.sahf,
.sbb_dep_breaking,
.slow_shld,
.smap,
.smep,
.sse4a,
.vzeroupper,
.x87,
@ -1995,6 +2021,8 @@ pub const cpu = struct {
.rdseed,
.sahf,
.slow_3ops_lea,
.smap,
.smep,
.vzeroupper,
.x87,
.xsaveopt,
@ -2136,6 +2164,8 @@ pub const cpu = struct {
.sahf,
.sha,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vzeroupper,
.x87,
@ -2195,6 +2225,8 @@ pub const cpu = struct {
.rdseed,
.sahf,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vzeroupper,
.x87,
@ -2450,6 +2482,8 @@ pub const cpu = struct {
.serialize,
.sha,
.shstk,
.smap,
.smep,
.tsxldtrk,
.tuning_fast_imm_vector_shift,
.uintr,
@ -2519,6 +2553,8 @@ pub const cpu = struct {
.slow_incdec,
.slow_lea,
.slow_two_mem_ops,
.smap,
.smep,
.sse4_2,
.use_glm_div_sqrt_costs,
.vzeroupper,
@ -2898,6 +2934,7 @@ pub const cpu = struct {
.rdrnd,
.sahf,
.slow_3ops_lea,
.smep,
.vzeroupper,
.x87,
.xsaveopt,
@ -2907,6 +2944,7 @@ pub const cpu = struct {
.name = "i386",
.llvm_name = "i386",
.features = featureSet(&[_]Feature{
.bsf_bsr_0_clobbers_result,
.slow_unaligned_mem_16,
.vzeroupper,
.x87,
@ -2916,6 +2954,7 @@ pub const cpu = struct {
.name = "i486",
.llvm_name = "i486",
.features = featureSet(&[_]Feature{
.bsf_bsr_0_clobbers_result,
.slow_unaligned_mem_16,
.vzeroupper,
.x87,
@ -3096,6 +3135,7 @@ pub const cpu = struct {
.sahf,
.slow_3ops_lea,
.slow_unaligned_mem_32,
.smep,
.vzeroupper,
.x87,
.xsaveopt,
@ -3403,6 +3443,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vaes,
.vpclmulqdq,
@ -3766,6 +3808,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vaes,
.vpclmulqdq,
@ -3831,6 +3875,8 @@ pub const cpu = struct {
.rdseed,
.sahf,
.sha,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vaes,
.vpclmulqdq,
@ -3939,6 +3985,8 @@ pub const cpu = struct {
.serialize,
.sha,
.shstk,
.smap,
.smep,
.tsxldtrk,
.tuning_fast_imm_vector_shift,
.uintr,
@ -4042,6 +4090,7 @@ pub const cpu = struct {
.slow_lea,
.slow_pmulld,
.slow_two_mem_ops,
.smep,
.sse4_2,
.use_slm_arith_costs,
.vzeroupper,
@ -4098,6 +4147,8 @@ pub const cpu = struct {
.rdseed,
.sahf,
.slow_3ops_lea,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vzeroupper,
.x87,
@ -4150,6 +4201,8 @@ pub const cpu = struct {
.rdseed,
.sahf,
.slow_3ops_lea,
.smap,
.smep,
.vzeroupper,
.x87,
.xsavec,
@ -4305,6 +4358,8 @@ pub const cpu = struct {
.sahf,
.sha,
.shstk,
.smap,
.smep,
.tuning_fast_imm_vector_shift,
.vaes,
.vpclmulqdq,
@ -4574,6 +4629,8 @@ pub const cpu = struct {
.sbb_dep_breaking,
.sha,
.slow_shld,
.smap,
.smep,
.sse4a,
.vzeroupper,
.x87,
@ -4629,6 +4686,8 @@ pub const cpu = struct {
.sbb_dep_breaking,
.sha,
.slow_shld,
.smap,
.smep,
.sse4a,
.vzeroupper,
.wbnoinvd,
@ -4686,6 +4745,8 @@ pub const cpu = struct {
.sbb_dep_breaking,
.sha,
.slow_shld,
.smap,
.smep,
.sse4a,
.vaes,
.vpclmulqdq,
@ -4757,6 +4818,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_shld,
.smap,
.smep,
.sse4a,
.vaes,
.vpclmulqdq,
@ -4833,6 +4896,8 @@ pub const cpu = struct {
.sha,
.shstk,
.slow_shld,
.smap,
.smep,
.sse4a,
.vaes,
.vpclmulqdq,

View File

@ -372,9 +372,11 @@ pub const SpawnConfig = struct {
// https://github.com/ziglang/zig/issues/157
/// Size in bytes of the Thread's stack
stack_size: usize = 16 * 1024 * 1024,
stack_size: usize = default_stack_size,
/// The allocator to be used to allocate memory for the to-be-spawned thread
allocator: ?std.mem.Allocator = null,
pub const default_stack_size = 16 * 1024 * 1024;
};
pub const SpawnError = error{

View File

@ -161,17 +161,17 @@ const WindowsImpl = struct {
}
}
if (comptime builtin.mode == .Debug) {
if (builtin.mode == .Debug) {
// The internal state of the DebugMutex needs to be handled here as well.
mutex.impl.locking_thread.store(0, .unordered);
}
const rc = os.windows.kernel32.SleepConditionVariableSRW(
&self.condition,
if (comptime builtin.mode == .Debug) &mutex.impl.impl.srwlock else &mutex.impl.srwlock,
if (builtin.mode == .Debug) &mutex.impl.impl.srwlock else &mutex.impl.srwlock,
timeout_ms,
0, // the srwlock was assumed to acquired in exclusive mode not shared
);
if (comptime builtin.mode == .Debug) {
if (builtin.mode == .Debug) {
// The internal state of the DebugMutex needs to be handled here as well.
mutex.impl.locking_thread.store(std.Thread.getCurrentId(), .unordered);
}

View File

@ -158,7 +158,7 @@ const FutexImpl = struct {
// On x86, use `lock bts` instead of `lock cmpxchg` as:
// - they both seem to mark the cache-line as modified regardless: https://stackoverflow.com/a/63350048
// - `lock bts` is smaller instruction-wise which makes it better for inlining
if (comptime builtin.target.cpu.arch.isX86()) {
if (builtin.target.cpu.arch.isX86()) {
const locked_bit = @ctz(locked);
return self.state.bitSet(locked_bit, .acquire) == 0;
}

View File

@ -27,6 +27,7 @@ pub const Options = struct {
allocator: std.mem.Allocator,
n_jobs: ?usize = null,
track_ids: bool = false,
stack_size: usize = std.Thread.SpawnConfig.default_stack_size,
};
pub fn init(pool: *Pool, options: Options) !void {
@ -54,7 +55,10 @@ pub fn init(pool: *Pool, options: Options) !void {
errdefer pool.join(spawned);
for (pool.threads) |*thread| {
thread.* = try std.Thread.spawn(.{}, worker, .{pool});
thread.* = try std.Thread.spawn(.{
.stack_size = options.stack_size,
.allocator = allocator,
}, worker, .{pool});
spawned += 1;
}
}

View File

@ -4,7 +4,7 @@ const mem = std.mem;
const debug = std.debug;
const has_vaes = builtin.cpu.arch == .x86_64 and std.Target.x86.featureSetHas(builtin.cpu.features, .vaes);
const has_avx512f = builtin.cpu.arch == .x86_64 and std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f);
const has_avx512f = builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_x86_64 and std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f);
/// A single AES block.
pub const Block = struct {

View File

@ -499,11 +499,9 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
fn ChaChaImpl(comptime rounds_nb: usize) type {
switch (builtin.cpu.arch) {
.x86_64 => {
if (builtin.zig_backend == .stage2_x86_64) return ChaChaNonVecImpl(rounds_nb);
const has_avx2 = std.Target.x86.featureSetHas(builtin.cpu.features, .avx2);
const has_avx512f = std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f);
if (has_avx512f) return ChaChaVecImpl(rounds_nb, 4);
if (builtin.zig_backend != .stage2_x86_64 and has_avx512f) return ChaChaVecImpl(rounds_nb, 4);
if (has_avx2) return ChaChaVecImpl(rounds_nb, 2);
return ChaChaVecImpl(rounds_nb, 1);
},

View File

@ -356,14 +356,7 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
if (ciphertext.len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
const cleartext = cleartext_fragment_buf[0..ciphertext.len];
const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
const nonce = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.server_handshake_iv;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ read_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));
@ -400,14 +393,7 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
const record_iv = record_decoder.array(P.record_iv_length).*;
const masked_read_seq = read_seq &
comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.app_cipher.server_write_IV ++ record_iv;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce: [P.AEAD.nonce_length]u8 = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
@ -750,14 +736,7 @@ pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client
.app_cipher = std.mem.bytesToValue(P.Tls_1_2, &key_block),
} };
const pv = &p.version.tls_1_2;
const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ write_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce: [P.AEAD.nonce_length]u8 = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));
@ -1043,14 +1022,7 @@ fn prepareCiphertextRecord(
ciphertext_end += ciphertext_len;
const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];
ciphertext_end += auth_tag.len;
const nonce = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.client_iv;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ std.mem.toBytes(big(c.write_seq));
@ -1098,14 +1070,7 @@ fn prepareCiphertextRecord(
const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len);
const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];
ciphertext_end += P.record_iv_length;
const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.client_write_IV ++ pv.client_salt;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce: [P.AEAD.nonce_length]u8 = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));
@ -1374,14 +1339,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
const ciphertext = frag[in..][0..ciphertext_len];
in += ciphertext_len;
const auth_tag = frag[in..][0..P.AEAD.tag_length].*;
const nonce = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.server_iv;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.read_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ std.mem.toBytes(big(c.read_seq));
@ -1409,14 +1367,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
in += P.record_iv_length;
const masked_read_seq = c.read_seq &
comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
nonce: {
var nonce = pv.server_write_IV ++ record_iv;
const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
break :nonce nonce;
} else nonce: {
const nonce: [P.AEAD.nonce_length]u8 = nonce: {
const V = @Vector(P.AEAD.nonce_length, u8);
const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));

View File

@ -23,6 +23,7 @@ pub const Coverage = @import("debug/Coverage.zig");
pub const FormattedPanic = @import("debug/FormattedPanic.zig");
pub const SimplePanic = @import("debug/SimplePanic.zig");
pub const NoPanic = @import("debug/NoPanic.zig");
/// Unresolved source locations can be represented with a single `usize` that
/// corresponds to a virtual memory address of the program counter. Combined
@ -179,7 +180,7 @@ pub fn dumpHexFallible(bytes: []const u8) !void {
/// TODO multithreaded awareness
pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
nosuspend {
if (comptime builtin.target.isWasm()) {
if (builtin.target.isWasm()) {
if (native_os == .wasi) {
const stderr = io.getStdErr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
@ -267,7 +268,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
/// TODO multithreaded awareness
pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
nosuspend {
if (comptime builtin.target.isWasm()) {
if (builtin.target.isWasm()) {
if (native_os == .wasi) {
const stderr = io.getStdErr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
@ -365,7 +366,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
/// TODO multithreaded awareness
pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
nosuspend {
if (comptime builtin.target.isWasm()) {
if (builtin.target.isWasm()) {
if (native_os == .wasi) {
const stderr = io.getStdErr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;

59
lib/std/debug/NoPanic.zig Normal file
View File

@ -0,0 +1,59 @@
//! This namespace can be used with `pub const Panic = std.debug.NoPanic;` in the root file.
//! It emits as little code as possible, for testing purposes.
//!
//! For a functional alternative, see `std.debug.FormattedPanic`.
const std = @import("../std.zig");
pub fn call(_: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn sentinelMismatch(_: anytype, _: anytype) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn unwrapError(_: ?*std.builtin.StackTrace, _: anyerror) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn outOfBounds(_: usize, _: usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn startGreaterThanEnd(_: usize, _: usize) noreturn {
@branchHint(.cold);
@trap();
}
pub inline fn inactiveUnionField(_: anytype, _: anytype) noreturn {
@branchHint(.cold);
@trap();
}
pub const messages = struct {
pub const reached_unreachable = "";
pub const unwrap_null = "";
pub const cast_to_null = "";
pub const incorrect_alignment = "";
pub const invalid_error_code = "";
pub const cast_truncated_data = "";
pub const negative_to_unsigned = "";
pub const integer_overflow = "";
pub const shl_overflow = "";
pub const shr_overflow = "";
pub const divide_by_zero = "";
pub const exact_division_remainder = "";
pub const integer_part_out_of_bounds = "";
pub const corrupt_switch = "";
pub const shift_rhs_too_big = "";
pub const invalid_enum_value = "";
pub const for_len_mismatch = "";
pub const memcpy_len_mismatch = "";
pub const memcpy_alias = "";
pub const noreturn_returned = "";
};

View File

@ -121,13 +121,13 @@ pub fn deinit(self: *SelfInfo) void {
}
pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {
if (comptime builtin.target.isDarwin()) {
if (builtin.target.isDarwin()) {
return self.lookupModuleDyld(address);
} else if (native_os == .windows) {
return self.lookupModuleWin32(address);
} else if (native_os == .haiku) {
return self.lookupModuleHaiku(address);
} else if (comptime builtin.target.isWasm()) {
} else if (builtin.target.isWasm()) {
return self.lookupModuleWasm(address);
} else {
return self.lookupModuleDl(address);
@ -138,13 +138,13 @@ pub fn getModuleForAddress(self: *SelfInfo, address: usize) !*Module {
// This can be called when getModuleForAddress fails, so implementations should provide
// a path that doesn't rely on any side-effects of a prior successful module lookup.
pub fn getModuleNameForAddress(self: *SelfInfo, address: usize) ?[]const u8 {
if (comptime builtin.target.isDarwin()) {
if (builtin.target.isDarwin()) {
return self.lookupModuleNameDyld(address);
} else if (native_os == .windows) {
return self.lookupModuleNameWin32(address);
} else if (native_os == .haiku) {
return null;
} else if (comptime builtin.target.isWasm()) {
} else if (builtin.target.isWasm()) {
return null;
} else {
return self.lookupModuleNameDl(address);

View File

@ -890,7 +890,7 @@ test {
_ = @import("heap/memory_pool.zig");
_ = ArenaAllocator;
_ = GeneralPurposeAllocator;
if (comptime builtin.target.isWasm()) {
if (builtin.target.isWasm()) {
_ = WasmAllocator;
_ = WasmPageAllocator;
}

View File

@ -4,7 +4,6 @@ const testing = std.testing;
const mem = std.mem;
const assert = std.debug.assert;
const use_vectors = builtin.zig_backend != .stage2_x86_64;
pub const State = enum {
invalid,

View File

@ -2520,12 +2520,13 @@ pub const Const = struct {
return order(a, b) == .eq;
}
/// Returns the number of leading zeros in twos-complement form.
pub fn clz(a: Const, bits: Limb) Limb {
// Limbs are stored in little-endian order but we need
// to iterate big-endian.
// Limbs are stored in little-endian order but we need to iterate big-endian.
if (!a.positive and !a.eqlZero()) return 0;
var total_limb_lz: Limb = 0;
var i: usize = a.limbs.len;
const bits_per_limb = @sizeOf(Limb) * 8;
const bits_per_limb = @bitSizeOf(Limb);
while (i != 0) {
i -= 1;
const limb = a.limbs[i];
@ -2537,13 +2538,15 @@ pub const Const = struct {
return total_limb_lz + bits - total_limb_bits;
}
/// Returns the number of trailing zeros in twos-complement form.
pub fn ctz(a: Const, bits: Limb) Limb {
// Limbs are stored in little-endian order.
// Limbs are stored in little-endian order. Converting a negative number to twos-complement
// flips all bits above the lowest set bit, which does not affect the trailing zero count.
var result: Limb = 0;
for (a.limbs) |limb| {
const limb_tz = @ctz(limb);
result += limb_tz;
if (limb_tz != @sizeOf(Limb) * 8) break;
if (limb_tz != @bitSizeOf(Limb)) break;
}
return @min(result, bits);
}

View File

@ -883,8 +883,8 @@ fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
@compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
}
/// Takes an array, a pointer to an array, a sentinel-terminated pointer, or a slice and
/// iterates searching for the first occurrence of `end`, returning the scanned slice.
/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice and iterates searching for
/// the first occurrence of `end`, returning the scanned slice.
/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
/// If the pointer type is sentinel terminated and `end` matches that terminator, the
/// resulting slice is also sentinel terminated.

View File

@ -157,7 +157,7 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.
return target;
},
.freebsd => {
if (comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) {
if (builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) {
var kfile: std.c.kinfo_file = undefined;
kfile.structsize = std.c.KINFO_FILE_SIZE;
switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {

View File

@ -1061,7 +1061,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
// us INVALID_PARAMETER.
// The same reasoning for win10_rs5 as in os.renameatW() applies (FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5).
var need_fallback = true;
if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) {
if (builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) {
// Deletion with posix semantics if the filesystem supports it.
var info = FILE_DISPOSITION_INFORMATION_EX{
.Flags = FILE_DISPOSITION_DELETE |

View File

@ -6819,7 +6819,7 @@ pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
}
},
.freebsd => {
if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
if (builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
@compileError("memfd_create is unavailable on FreeBSD < 13.0");
const rc = system.memfd_create(name, flags);
switch (errno(rc)) {

View File

@ -804,7 +804,7 @@ test "getrlimit and setrlimit" {
//
// This happens for example if RLIMIT_MEMLOCK is bigger than ~2GiB.
// In that case the following the limit would be RLIM_INFINITY and the following setrlimit fails with EPERM.
if (comptime builtin.cpu.arch.isMIPS() and builtin.link_libc) {
if (builtin.cpu.arch.isMIPS() and builtin.link_libc) {
if (limit.cur != linux.RLIM.INFINITY) {
try posix.setrlimit(resource, limit);
}

View File

@ -163,7 +163,7 @@ pub fn interlace(vecs: anytype) @Vector(vectorLength(@TypeOf(vecs[0])) * vecs.le
// The indices are correct. The problem seems to be with the @shuffle builtin.
// On MIPS, the test that interlaces small_base gives { 0, 2, 0, 0, 64, 255, 248, 200, 0, 0 }.
// Calling this with two inputs seems to work fine, but I'll let the compile error trigger for all inputs, just to be safe.
comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why interlace() doesn't work on MIPS");
if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why interlace() doesn't work on MIPS");
const VecType = @TypeOf(vecs[0]);
const vecs_arr = @as([vecs.len]VecType, vecs);
@ -248,7 +248,7 @@ test "vector patterns" {
try std.testing.expectEqual([8]u32{ 10, 20, 30, 40, 55, 66, 77, 88 }, join(base, other_base));
try std.testing.expectEqual([2]u32{ 20, 30 }, extract(base, 1, 2));
if (comptime !builtin.cpu.arch.isMIPS()) {
if (!builtin.cpu.arch.isMIPS()) {
try std.testing.expectEqual([8]u32{ 10, 55, 20, 66, 30, 77, 40, 88 }, interlace(.{ base, other_base }));
const small_braid = interlace(small_bases);
@ -390,7 +390,7 @@ pub fn prefixScanWithFunc(
comptime identity: std.meta.Child(@TypeOf(vec)),
) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec) {
// I haven't debugged this, but it might be a cousin of sorts to what's going on with interlace.
comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why prefixScan doesn't work on MIPS");
if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why prefixScan doesn't work on MIPS");
const len = vectorLength(@TypeOf(vec));
@ -465,9 +465,7 @@ test "vector prefix scan" {
if ((builtin.cpu.arch == .armeb or builtin.cpu.arch == .thumbeb) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22060
if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21893
if (comptime builtin.cpu.arch.isMIPS()) {
return error.SkipZigTest;
}
if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
const int_base = @Vector(4, i32){ 11, 23, 9, -21 };
const float_base = @Vector(4, f32){ 2, 0.5, -10, 6.54321 };

View File

@ -83,7 +83,7 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths {
// TODO: consider also adding homebrew paths
// TODO: consider also adding macports paths
if (comptime builtin.target.isDarwin()) {
if (builtin.target.isDarwin()) {
if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: {
const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk;
try self.addLibDir(try std.fs.path.join(arena, &.{ sdk, "usr/lib" }));

View File

@ -369,6 +369,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {
setFeature(cpu, .bmi, bit(leaf.ebx, 3));
// AVX2 is only supported if we have the OS save support from AVX.
setFeature(cpu, .avx2, bit(leaf.ebx, 5) and has_avx_save);
setFeature(cpu, .smep, bit(leaf.ebx, 7));
setFeature(cpu, .bmi2, bit(leaf.ebx, 8));
setFeature(cpu, .invpcid, bit(leaf.ebx, 10));
setFeature(cpu, .rtm, bit(leaf.ebx, 11));
@ -377,6 +378,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void {
setFeature(cpu, .avx512dq, bit(leaf.ebx, 17) and has_avx512_save);
setFeature(cpu, .rdseed, bit(leaf.ebx, 18));
setFeature(cpu, .adx, bit(leaf.ebx, 19));
setFeature(cpu, .smap, bit(leaf.ebx, 20));
setFeature(cpu, .avx512ifma, bit(leaf.ebx, 21) and has_avx512_save);
setFeature(cpu, .clflushopt, bit(leaf.ebx, 23));
setFeature(cpu, .clwb, bit(leaf.ebx, 24));

View File

@ -893,14 +893,38 @@ pub const Inst = struct {
pub const Index = enum(u32) {
_,
pub fn toRef(i: Index) Inst.Ref {
assert(@intFromEnum(i) >> 31 == 0);
return @enumFromInt((1 << 31) | @intFromEnum(i));
pub fn unwrap(index: Index) union(enum) { ref: Inst.Ref, target: u31 } {
const low_index: u31 = @truncate(@intFromEnum(index));
return switch (@as(u1, @intCast(@intFromEnum(index) >> 31))) {
0 => .{ .ref = @enumFromInt(@as(u32, 1 << 31) | low_index) },
1 => .{ .target = low_index },
};
}
pub fn toTargetIndex(i: Index) u31 {
assert(@intFromEnum(i) >> 31 == 1);
return @truncate(@intFromEnum(i));
pub fn toRef(index: Index) Inst.Ref {
return index.unwrap().ref;
}
pub fn fromTargetIndex(index: u31) Index {
return @enumFromInt((1 << 31) | @as(u32, index));
}
pub fn toTargetIndex(index: Index) u31 {
return index.unwrap().target;
}
pub fn format(
index: Index,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
try writer.writeByte('%');
switch (index.unwrap()) {
.ref => {},
.target => try writer.writeByte('t'),
}
try writer.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});
}
};
@ -1205,7 +1229,7 @@ pub const VectorCmp = struct {
op: u32,
pub fn compareOperator(self: VectorCmp) std.math.CompareOperator {
return @as(std.math.CompareOperator, @enumFromInt(@as(u3, @truncate(self.op))));
return @enumFromInt(@as(u3, @intCast(self.op)));
}
pub fn encodeOp(compare_operator: std.math.CompareOperator) u32 {
@ -1250,11 +1274,11 @@ pub const Cmpxchg = struct {
flags: u32,
pub fn successOrder(self: Cmpxchg) std.builtin.AtomicOrder {
return @as(std.builtin.AtomicOrder, @enumFromInt(@as(u3, @truncate(self.flags))));
return @enumFromInt(@as(u3, @truncate(self.flags)));
}
pub fn failureOrder(self: Cmpxchg) std.builtin.AtomicOrder {
return @as(std.builtin.AtomicOrder, @enumFromInt(@as(u3, @truncate(self.flags >> 3))));
return @enumFromInt(@as(u3, @intCast(self.flags >> 3)));
}
};
@ -1265,11 +1289,11 @@ pub const AtomicRmw = struct {
flags: u32,
pub fn ordering(self: AtomicRmw) std.builtin.AtomicOrder {
return @as(std.builtin.AtomicOrder, @enumFromInt(@as(u3, @truncate(self.flags))));
return @enumFromInt(@as(u3, @truncate(self.flags)));
}
pub fn op(self: AtomicRmw) std.builtin.AtomicRmwOp {
return @as(std.builtin.AtomicRmwOp, @enumFromInt(@as(u4, @truncate(self.flags >> 3))));
return @enumFromInt(@as(u4, @intCast(self.flags >> 3)));
}
};

View File

@ -3067,6 +3067,7 @@ pub fn saveState(comp: *Compilation) !void {
// linker state
switch (lf.tag) {
.wasm => {
dev.check(link.File.Tag.wasm.devFeature());
const wasm = lf.cast(.wasm).?;
const is_obj = comp.config.output_mode == .Obj;
try bufs.ensureUnusedCapacity(85);

View File

@ -202,14 +202,6 @@ pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool
return (l.tomb_bits[usize_index] & mask) != 0;
}
pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) void {
assert(operand < bpi - 1);
const usize_index = (@intFromEnum(inst) * bpi) / @bitSizeOf(usize);
const mask = @as(usize, 1) <<
@as(Log2Int(usize), @intCast((@intFromEnum(inst) % (@bitSizeOf(usize) / bpi)) * bpi + operand));
l.tomb_bits[usize_index] &= ~mask;
}
const OperandCategory = enum {
/// The operand lives on, but this instruction cannot possibly mutate memory.
none,
@ -727,32 +719,25 @@ pub const SwitchBrTable = struct {
/// Caller owns the memory.
pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len: u32) Allocator.Error!SwitchBrTable {
var index: usize = l.special.get(inst) orelse return SwitchBrTable{
.deaths = &.{},
};
var index: usize = l.special.get(inst) orelse return .{ .deaths = &.{} };
const else_death_count = l.extra[index];
index += 1;
var deaths = std.ArrayList([]const Air.Inst.Index).init(gpa);
defer deaths.deinit();
try deaths.ensureTotalCapacity(cases_len + 1);
var deaths = try gpa.alloc([]const Air.Inst.Index, cases_len);
errdefer gpa.free(deaths);
var case_i: u32 = 0;
while (case_i < cases_len - 1) : (case_i += 1) {
const case_death_count: u32 = l.extra[index];
index += 1;
const case_deaths: []const Air.Inst.Index = @ptrCast(l.extra[index..][0..case_death_count]);
deaths[case_i] = @ptrCast(l.extra[index..][0..case_death_count]);
index += case_death_count;
deaths.appendAssumeCapacity(case_deaths);
}
{
// Else
const else_deaths: []const Air.Inst.Index = @ptrCast(l.extra[index..][0..else_death_count]);
deaths.appendAssumeCapacity(else_deaths);
deaths[case_i] = @ptrCast(l.extra[index..][0..else_death_count]);
}
return SwitchBrTable{
.deaths = try deaths.toOwnedSlice(),
};
return .{ .deaths = deaths };
}
/// Note that this information is technically redundant, but is useful for
@ -844,12 +829,6 @@ const Analysis = struct {
special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
extra: std.ArrayListUnmanaged(u32),
fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
const usize_index = (inst * bpi) / @bitSizeOf(usize);
a.tomb_bits[usize_index] |= @as(usize, tomb_bits) <<
@as(Log2Int(usize), @intCast((inst % (@bitSizeOf(usize) / bpi)) * bpi));
}
fn addExtra(a: *Analysis, extra: anytype) Allocator.Error!u32 {
const fields = std.meta.fields(@TypeOf(extra));
try a.extra.ensureUnusedCapacity(a.gpa, fields.len);

View File

@ -962,7 +962,6 @@ pub fn abiAlignmentInner(
) SemaError!AbiAlignmentInner {
const pt = strat.pt(zcu, tid);
const target = zcu.getTarget();
const use_llvm = zcu.comp.config.use_llvm;
const ip = &zcu.intern_pool;
switch (ty.toIntern()) {
@ -970,7 +969,7 @@ pub fn abiAlignmentInner(
else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| {
if (int_type.bits == 0) return .{ .scalar = .@"1" };
return .{ .scalar = intAbiAlignment(int_type.bits, target, use_llvm) };
return .{ .scalar = intAbiAlignment(int_type.bits, target) };
},
.ptr_type, .anyframe_type => {
return .{ .scalar = ptrAbiAlignment(target) };
@ -1023,7 +1022,7 @@ pub fn abiAlignmentInner(
.error_set_type, .inferred_error_set_type => {
const bits = zcu.errorSetBits();
if (bits == 0) return .{ .scalar = .@"1" };
return .{ .scalar = intAbiAlignment(bits, target, use_llvm) };
return .{ .scalar = intAbiAlignment(bits, target) };
},
// represents machine code; not a pointer
@ -1036,7 +1035,7 @@ pub fn abiAlignmentInner(
.usize,
.isize,
=> return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target, use_llvm) },
=> return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target) },
.c_char => return .{ .scalar = cTypeAlign(target, .char) },
.c_short => return .{ .scalar = cTypeAlign(target, .short) },
@ -1067,7 +1066,7 @@ pub fn abiAlignmentInner(
.anyerror, .adhoc_inferred_error_set => {
const bits = zcu.errorSetBits();
if (bits == 0) return .{ .scalar = .@"1" };
return .{ .scalar = intAbiAlignment(bits, target, use_llvm) };
return .{ .scalar = intAbiAlignment(bits, target) };
},
.void,
@ -1291,7 +1290,6 @@ pub fn abiSizeInner(
tid: strat.Tid(),
) SemaError!AbiSizeInner {
const target = zcu.getTarget();
const use_llvm = zcu.comp.config.use_llvm;
const ip = &zcu.intern_pool;
switch (ty.toIntern()) {
@ -1300,7 +1298,7 @@ pub fn abiSizeInner(
else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| {
if (int_type.bits == 0) return .{ .scalar = 0 };
return .{ .scalar = intAbiSize(int_type.bits, target, use_llvm) };
return .{ .scalar = intAbiSize(int_type.bits, target) };
},
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
.slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
@ -1362,7 +1360,7 @@ pub fn abiSizeInner(
.error_set_type, .inferred_error_set_type => {
const bits = zcu.errorSetBits();
if (bits == 0) return .{ .scalar = 0 };
return .{ .scalar = intAbiSize(bits, target, use_llvm) };
return .{ .scalar = intAbiSize(bits, target) };
},
.error_union_type => |error_union_type| {
@ -1455,7 +1453,7 @@ pub fn abiSizeInner(
.anyerror, .adhoc_inferred_error_set => {
const bits = zcu.errorSetBits();
if (bits == 0) return .{ .scalar = 0 };
return .{ .scalar = intAbiSize(bits, target, use_llvm) };
return .{ .scalar = intAbiSize(bits, target) };
},
.noreturn => unreachable,
@ -1609,11 +1607,11 @@ pub fn ptrAbiAlignment(target: Target) Alignment {
return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8));
}
pub fn intAbiSize(bits: u16, target: Target, use_llvm: bool) u64 {
return intAbiAlignment(bits, target, use_llvm).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8)));
pub fn intAbiSize(bits: u16, target: Target) u64 {
return intAbiAlignment(bits, target).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8)));
}
pub fn intAbiAlignment(bits: u16, target: Target, use_llvm: bool) Alignment {
pub fn intAbiAlignment(bits: u16, target: Target) Alignment {
return switch (target.cpu.arch) {
.x86 => switch (bits) {
0 => .none,
@ -1632,19 +1630,16 @@ pub fn intAbiAlignment(bits: u16, target: Target, use_llvm: bool) Alignment {
9...16 => .@"2",
17...32 => .@"4",
33...64 => .@"8",
else => switch (target_util.zigBackend(target, use_llvm)) {
.stage2_x86_64 => .@"8",
else => .@"16",
},
else => .@"16",
},
else => return Alignment.fromByteUnits(@min(
std.math.ceilPowerOfTwoPromote(u16, @as(u16, @intCast((@as(u17, bits) + 7) / 8))),
maxIntAlignment(target, use_llvm),
maxIntAlignment(target),
)),
};
}
pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {
pub fn maxIntAlignment(target: std.Target) u16 {
return switch (target.cpu.arch) {
.avr => 1,
.msp430 => 2,
@ -1685,10 +1680,7 @@ pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 {
else => 8,
},
.x86_64 => switch (target_util.zigBackend(target, use_llvm)) {
.stage2_x86_64 => 8,
else => 16,
},
.x86_64 => 16,
// Even LLVMABIAlignmentOfType(i128) agrees on these targets.
.x86,
@ -1928,6 +1920,17 @@ pub fn isSlice(ty: Type, zcu: *const Zcu) bool {
};
}
pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool {
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
.ptr_type => |ptr_type| ptr_type.flags.size == .slice,
.opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
.ptr_type => |ptr_type| !ptr_type.flags.is_allowzero and ptr_type.flags.size == .slice,
else => false,
},
else => false,
};
}
pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type {
return Type.fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
}

View File

@ -71,6 +71,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// We postpone the creation of debug info for function args and locals
/// until after all Mir instructions have been generated. Only then we
/// will know saved_regs_stack_space which is necessary in order to
@ -646,6 +648,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.add => try self.airBinOp(inst, .add),
@ -927,16 +930,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -3614,7 +3614,7 @@ fn reuseOperand(
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

View File

@ -72,6 +72,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// We postpone the creation of debug info for function args and locals
/// until after all Mir instructions have been generated. Only then we
/// will know saved_regs_stack_space which is necessary in order to
@ -635,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.add, => try self.airBinOp(inst, .add),
@ -918,16 +921,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -2650,7 +2650,7 @@ fn reuseOperand(
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

View File

@ -82,6 +82,8 @@ scope_generation: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// Whenever there is a runtime branch, we push a Branch onto this stack,
/// and pop it off when the runtime branch joins. This provides an "overlay"
/// of the table of mappings from instructions to `MCValue` from within the branch.
@ -1443,8 +1445,11 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
verbose_tracking_log.debug("{}", .{func.fmtTracking()});
const old_air_bookkeeping = func.air_bookkeeping;
try func.ensureProcessDeathCapacity(Liveness.bpi);
func.reused_operands = @TypeOf(func.reused_operands).initEmpty();
try func.inst_tracking.ensureUnusedCapacity(func.gpa, 1);
const tag: Air.Inst.Tag = air_tags[@intFromEnum(inst)];
const tag = air_tags[@intFromEnum(inst)];
switch (tag) {
// zig fmt: off
.add,
@ -1783,11 +1788,10 @@ fn finishAir(
result: MCValue,
operands: [Liveness.bpi - 1]Air.Inst.Ref,
) !void {
var tomb_bits = func.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const tomb_bits = func.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (func.reused_operands.isSet(op_index)) continue;
try func.processDeath(op.toIndexAllowNone() orelse continue);
}
func.finishAirResult(inst, result);
@ -4424,7 +4428,7 @@ fn reuseOperandAdvanced(
}
// Prevent the operand deaths processing code from deallocating it.
func.liveness.clearOperandDeath(inst, op_index);
func.reused_operands.set(op_index);
const op_inst = operand.toIndex().?;
func.getResolvedInstValue(op_inst).reuse(func, maybe_tracked_inst, op_inst);

View File

@ -78,6 +78,8 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined,
/// Whenever there is a runtime branch, we push a Branch onto this stack,
/// and pop it off when the runtime branch joins. This provides an "overlay"
/// of the table of mappings from instructions to `MCValue` from within the branch.
@ -493,6 +495,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
const old_air_bookkeeping = self.air_bookkeeping;
try self.ensureProcessDeathCapacity(Liveness.bpi);
self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
.ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
@ -3523,16 +3526,13 @@ fn finishAirBookkeeping(self: *Self) void {
}
fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
var tomb_bits = self.liveness.getTombBits(inst);
for (operands) |op| {
const dies = @as(u1, @truncate(tomb_bits)) != 0;
tomb_bits >>= 1;
if (!dies) continue;
const op_index = op.toIndex() orelse continue;
self.processDeath(op_index);
const tomb_bits = self.liveness.getTombBits(inst);
for (0.., operands) |op_index, op| {
if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
if (self.reused_operands.isSet(op_index)) continue;
self.processDeath(op.toIndexAllowNone() orelse continue);
}
const is_used = @as(u1, @truncate(tomb_bits)) == 0;
if (is_used) {
if (tomb_bits & 1 << (Liveness.bpi - 1) == 0) {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
@ -4568,7 +4568,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
}
// Prevent the operand deaths processing code from deallocating it.
self.liveness.clearOperandDeath(inst, op_index);
self.reused_operands.set(op_index);
// That makes us responsible for doing the rest of the stuff that processDeath would have done.
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];

File diff suppressed because it is too large Load Diff

View File

@ -38,8 +38,36 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
const enc = try dis.parseEncoding(prefixes) orelse return error.UnknownOpcode;
switch (enc.data.op_en) {
.zo => return inst(enc, .{}),
.d, .i => {
.z => return inst(enc, .{}),
.o => {
const reg_low_enc: u3 = @truncate(dis.code[dis.pos - 1]);
return inst(enc, .{
.op1 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) },
});
},
.zo => {
const reg_low_enc: u3 = @truncate(dis.code[dis.pos - 1]);
return inst(enc, .{
.op1 = .{ .reg = enc.data.ops[0].toReg() },
.op2 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[1].regBitSize()) },
});
},
.oz => {
const reg_low_enc: u3 = @truncate(dis.code[dis.pos - 1]);
return inst(enc, .{
.op1 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) },
.op2 = .{ .reg = enc.data.ops[1].toReg() },
});
},
.oi => {
const reg_low_enc: u3 = @truncate(dis.code[dis.pos - 1]);
const imm = try dis.parseImm(enc.data.ops[1]);
return inst(enc, .{
.op1 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) },
.op2 = .{ .imm = imm },
});
},
.i, .d => {
const imm = try dis.parseImm(enc.data.ops[0]);
return inst(enc, .{
.op1 = .{ .imm = imm },
@ -48,20 +76,10 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
.zi => {
const imm = try dis.parseImm(enc.data.ops[1]);
return inst(enc, .{
.op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) },
.op1 = .{ .reg = enc.data.ops[0].toReg() },
.op2 = .{ .imm = imm },
});
},
.o, .oi => {
const reg_low_enc = @as(u3, @truncate(dis.code[dis.pos - 1]));
const op2: Instruction.Operand = if (enc.data.op_en == .oi) .{
.imm = try dis.parseImm(enc.data.ops[1]),
} else .none;
return inst(enc, .{
.op1 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) },
.op2 = op2,
});
},
.m, .mi, .m1, .mc => {
const modrm = try dis.parseModRmByte();
const act_enc = Encoding.findByOpcode(enc.opcode(), .{
@ -118,7 +136,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
const seg = segmentRegister(prefixes.legacy);
const offset = try dis.parseOffset();
return inst(enc, .{
.op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) },
.op1 = .{ .reg = enc.data.ops[0].toReg() },
.op2 = .{ .mem = Memory.initMoffs(seg, offset) },
});
},
@ -127,7 +145,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
const offset = try dis.parseOffset();
return inst(enc, .{
.op1 = .{ .mem = Memory.initMoffs(seg, offset) },
.op2 = .{ .reg = Register.rax.toBitSize(enc.data.ops[1].regBitSize()) },
.op2 = .{ .reg = enc.data.ops[1].toReg() },
});
},
.mr, .mri, .mrc => {
@ -223,7 +241,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
.op3 = op3,
});
},
.rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable, // TODO
.rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr, .rmv => unreachable, // TODO
}
}

View File

@ -10,22 +10,21 @@ prev_di_loc: Loc,
/// Relative to the beginning of `code`.
prev_di_pc: usize,
code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
relocs: std.ArrayListUnmanaged(Reloc) = .empty,
pub const Error = Lower.Error || error{
EmitFail,
} || link.File.UpdateDebugInfoError;
pub fn emitMir(emit: *Emit) Error!void {
const gpa = emit.lower.bin_file.comp.gpa;
const code_offset_mapping = try emit.lower.allocator.alloc(u32, emit.lower.mir.instructions.len);
defer emit.lower.allocator.free(code_offset_mapping);
var relocs: std.ArrayListUnmanaged(Reloc) = .empty;
defer relocs.deinit(emit.lower.allocator);
var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty;
defer table_relocs.deinit(emit.lower.allocator);
for (0..emit.lower.mir.instructions.len) |mir_i| {
const mir_index: Mir.Inst.Index = @intCast(mir_i);
try emit.code_offset_mapping.putNoClobber(
emit.lower.allocator,
mir_index,
@intCast(emit.code.items.len),
);
code_offset_mapping[mir_index] = @intCast(emit.code.items.len);
const lowered = try emit.lower.lowerMir(mir_index);
var lowered_relocs = lowered.relocs;
for (lowered.insts, 0..) |lowered_inst, lowered_index| {
@ -89,13 +88,17 @@ pub fn emitMir(emit: *Emit) Error!void {
lowered_relocs[0].lowered_inst_index == lowered_index) : ({
lowered_relocs = lowered_relocs[1..];
}) switch (lowered_relocs[0].target) {
.inst => |target| try emit.relocs.append(emit.lower.allocator, .{
.inst => |target| try relocs.append(emit.lower.allocator, .{
.source = start_offset,
.source_offset = end_offset - 4,
.target = target,
.target_offset = lowered_relocs[0].off,
.length = @intCast(end_offset - start_offset),
}),
.table => try table_relocs.append(emit.lower.allocator, .{
.source_offset = end_offset - 4,
.target_offset = lowered_relocs[0].off,
}),
.linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
// Add relocation to the decl.
const zo = elf_file.zigObjectPtr().?;
@ -103,7 +106,7 @@ pub fn emitMir(emit: *Emit) Error!void {
const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);
try atom_ptr.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
} else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
@ -150,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {
const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
},
@ -161,7 +164,7 @@ pub fn emitMir(emit: *Emit) Error!void {
const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off,
}, zo);
},
@ -176,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void {
@intFromEnum(std.elf.R_X86_64.PC32);
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off - 4,
}, zo);
} else {
@ -186,7 +189,7 @@ pub fn emitMir(emit: *Emit) Error!void {
@intFromEnum(std.elf.R_X86_64.@"32");
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
.r_info = @as(u64, sym_index) << 32 | r_type,
.r_addend = lowered_relocs[0].off,
}, zo);
}
@ -412,7 +415,7 @@ pub fn emitMir(emit: *Emit) Error!void {
loc_buf[0] = switch (mem.base()) {
.none => .{ .constu = 0 },
.reg => |reg| .{ .breg = reg.dwarfNum() },
.frame => unreachable,
.frame, .table => unreachable,
.reloc => |sym_index| .{ .addr = .{ .sym = sym_index } },
};
break :base &loc_buf[0];
@ -463,13 +466,40 @@ pub fn emitMir(emit: *Emit) Error!void {
}
}
}
try emit.fixupRelocs();
}
{
// TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.
// This should be reversed like it is done in aarch64 MIR emit code: start with the smallest
// possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution
// until the entire decl is correctly emitted with all JMP/CALL instructions within range.
for (relocs.items) |reloc| {
const target = code_offset_mapping[reloc.target];
const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset;
std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little);
}
}
if (emit.lower.mir.table.len > 0) {
if (emit.lower.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
pub fn deinit(emit: *Emit) void {
emit.relocs.deinit(emit.lower.allocator);
emit.code_offset_mapping.deinit(emit.lower.allocator);
emit.* = undefined;
const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size);
for (table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
.r_offset = table_reloc.source_offset,
.r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"),
.r_addend = @as(i64, table_offset) + table_reloc.target_offset,
}, zo);
for (emit.lower.mir.table) |entry| {
try atom.addReloc(gpa, .{
.r_offset = table_offset,
.r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"),
.r_addend = code_offset_mapping[entry],
}, zo);
table_offset += ptr_size;
}
try emit.code.appendNTimes(gpa, 0, table_offset - emit.code.items.len);
} else unreachable;
}
}
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
@ -481,7 +511,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error {
const Reloc = struct {
/// Offset of the instruction.
source: usize,
source: u32,
/// Offset of the relocation within the instruction.
source_offset: u32,
/// Target of the relocation.
@ -492,18 +522,12 @@ const Reloc = struct {
length: u5,
};
fn fixupRelocs(emit: *Emit) Error!void {
// TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size.
// This should be reversed like it is done in aarch64 MIR emit code: start with the smallest
// possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution
// until the entire decl is correctly emitted with all JMP/CALL instructions within range.
for (emit.relocs.items) |reloc| {
const target = emit.code_offset_mapping.get(reloc.target) orelse
return emit.fail("JMP/CALL relocation target not found!", .{});
const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset;
std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little);
}
}
const TableReloc = struct {
/// Offset of the relocation.
source_offset: u32,
/// Offset from the start of the table.
target_offset: i32,
};
const Loc = struct {
line: u32,

View File

@ -30,9 +30,10 @@ pub fn findByMnemonic(
prefix: Instruction.Prefix,
mnemonic: Mnemonic,
ops: []const Instruction.Operand,
target: *const std.Target,
) !?Encoding {
var input_ops = [1]Op{.none} ** 4;
for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op);
var input_ops: [4]Op = @splat(.none);
for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op, target);
const rex_required = for (ops) |op| switch (op) {
.reg => |r| switch (r) {
@ -57,6 +58,16 @@ pub fn findByMnemonic(
var shortest_enc: ?Encoding = null;
var shortest_len: ?usize = null;
next: for (mnemonic_to_encodings_map[@intFromEnum(mnemonic)]) |data| {
if (!switch (data.feature) {
.none => true,
inline else => |tag| has_features: {
comptime var feature_it = std.mem.splitScalar(u8, @tagName(tag), ' ');
comptime var features: []const std.Target.x86.Feature = &.{};
inline while (comptime feature_it.next()) |feature| features = features ++ .{@field(std.Target.x86.Feature, feature)};
break :has_features std.Target.x86.featureSetHasAll(target.cpu.features, features[0..].*);
},
}) continue;
switch (data.mode) {
.none, .short => if (rex_required) continue,
.rex, .rex_short => if (!rex_required) continue,
@ -64,7 +75,7 @@ pub fn findByMnemonic(
}
for (input_ops, data.ops) |input_op, data_op| if (!input_op.isSubset(data_op)) continue :next;
const enc = Encoding{ .mnemonic = mnemonic, .data = data };
const enc: Encoding = .{ .mnemonic = mnemonic, .data = data };
if (shortest_enc) |previous_shortest_enc| {
const len = estimateInstructionLength(prefix, enc, ops);
const previous_shortest_len = shortest_len orelse
@ -165,9 +176,14 @@ pub fn format(
for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
switch (encoding.data.op_en) {
.zo, .fd, .td, .i, .zi, .d => {},
.o, .oi => {
const tag = switch (encoding.data.ops[0]) {
.z, .fd, .td, .i, .zi, .d => {},
.o, .zo, .oz, .oi => {
const op = switch (encoding.data.op_en) {
.o, .oz, .oi => encoding.data.ops[0],
.zo => encoding.data.ops[1],
else => unreachable,
};
const tag = switch (op) {
.r8 => "rb",
.r16 => "rw",
.r32 => "rd",
@ -177,7 +193,7 @@ pub fn format(
try writer.print("+{s} ", .{tag});
},
.m, .mi, .m1, .mc, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
.mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr => try writer.writeAll("/r "),
.mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "),
}
switch (encoding.data.op_en) {
@ -202,7 +218,7 @@ pub fn format(
try writer.print("{s} ", .{tag});
},
.rvmr => try writer.writeAll("/is4 "),
.zo, .fd, .td, .o, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .rvm, .mvr => {},
.z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .rvm, .mvr, .rmv => {},
}
try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
@ -239,7 +255,8 @@ pub const Mnemonic = enum {
// General-purpose
adc, add, @"and",
bsf, bsr, bswap, bt, btc, btr, bts,
call, cbw, cdq, cdqe, clflush,
call, cbw, cdq, cdqe,
clac, clc, cld, clflush, cli, clts, clui,
cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz,
@ -260,10 +277,12 @@ pub const Mnemonic = enum {
neg, nop, not,
@"or",
pause, pop, popcnt, popfq, push, pushfq,
rcl, rcr, ret, rol, ror,
sal, sar, sbb,
rcl, rcr, ret, rol, ror, rorx,
sal, sar, sarx, sbb,
scas, scasb, scasd, scasq, scasw,
shl, shld, shr, shrd, sub, syscall,
shl, shld, shlx, shr, shrd, shrx,
stac, stc, std, sti, stui,
sub, syscall,
seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae,
setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns,
setnz, seto, setp, setpe, setpo, sets, setz,
@ -296,7 +315,7 @@ pub const Mnemonic = enum {
ldmxcsr,
maxps, maxss,
minps, minss,
movaps, movhlps, movlhps,
movaps, movhlps, movhps, movlhps, movlps,
movmskps,
movss, movups,
mulps, mulss,
@ -322,6 +341,7 @@ pub const Mnemonic = enum {
minpd, minsd,
movapd,
movdqa, movdqu,
movhpd, movlpd,
movmskpd,
//movsd,
movupd,
@ -353,6 +373,7 @@ pub const Mnemonic = enum {
pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq,
pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq,
pmulld,
ptest,
roundpd, roundps, roundsd, roundss,
// SSE4.2
pcmpgtq,
@ -383,7 +404,7 @@ pub const Mnemonic = enum {
vmovd,
vmovddup,
vmovdqa, vmovdqu,
vmovhlps, vmovlhps,
vmovhlps, vmovhpd, vmovhps, vmovlhps, vmovlpd, vmovlps,
vmovmskpd, vmovmskps,
vmovq,
vmovsd,
@ -413,6 +434,7 @@ pub const Mnemonic = enum {
vpsrad, vpsraq, vpsraw,
vpsrld, vpsrldq, vpsrlq, vpsrlw,
vpsubb, vpsubd, vpsubq, vpsubsb, vpsubsw, vpsubusb, vpsubusw, vpsubw,
vptest,
vpunpckhbw, vpunpckhdq, vpunpckhqdq, vpunpckhwd,
vpunpcklbw, vpunpckldq, vpunpcklqdq, vpunpcklwd,
vpxor,
@ -421,6 +443,7 @@ pub const Mnemonic = enum {
vsqrtpd, vsqrtps, vsqrtsd, vsqrtss,
vstmxcsr,
vsubpd, vsubps, vsubsd, vsubss,
vtestpd, vtestps,
vxorpd, vxorps,
// F16C
vcvtph2ps, vcvtps2ph,
@ -437,14 +460,14 @@ pub const Mnemonic = enum {
pub const OpEn = enum {
// zig fmt: off
zo,
o, oi,
z,
o, zo, oz, oi,
i, zi,
d, m,
fd, td,
m1, mc, mi, mr, rm,
rmi, mri, mrc,
rm0, vmi, rvm, rvmr, rvmi, mvr,
rm0, vmi, rvm, rvmr, rvmi, mvr, rmv,
// zig fmt: on
};
@ -471,7 +494,7 @@ pub const Op = enum {
ymm, ymm_m256,
// zig fmt: on
pub fn fromOperand(operand: Instruction.Operand) Op {
pub fn fromOperand(operand: Instruction.Operand, target: *const std.Target) Op {
return switch (operand) {
.none => .none,
@ -513,7 +536,7 @@ pub const Op = enum {
.mem => |mem| switch (mem) {
.moffs => .moffs,
.sib, .rip => switch (mem.bitSize()) {
.sib, .rip => switch (mem.bitSize(target)) {
0 => .m,
8 => .m8,
16 => .m16,
@ -557,6 +580,21 @@ pub const Op = enum {
};
}
pub fn toReg(op: Op) Register {
return switch (op) {
else => .none,
.al => .al,
.ax => .ax,
.eax => .eax,
.rax => .rax,
.cl => .cl,
.rip => .rip,
.eip => .eip,
.ip => .ip,
.xmm0 => .xmm0,
};
}
pub fn immBitSize(op: Op) u64 {
return switch (op) {
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
@ -808,6 +846,8 @@ pub const Feature = enum {
avx,
avx2,
bmi,
bmi2,
cmov,
f16c,
fma,
lzcnt,
@ -815,6 +855,7 @@ pub const Feature = enum {
pclmul,
@"pclmul avx",
popcnt,
smap,
sse,
sse2,
sse3,
@ -822,6 +863,7 @@ pub const Feature = enum {
sse4_2,
ssse3,
sha,
uintr,
vaes,
vpclmulqdq,
x87,
@ -831,7 +873,7 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
var inst = Instruction{
.prefix = prefix,
.encoding = encoding,
.ops = [1]Operand{.none} ** 4,
.ops = @splat(.none),
};
@memcpy(inst.ops[0..ops.len], ops);
@ -846,7 +888,7 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
const mnemonic_to_encodings_map = init: {
@setEvalBranchQuota(5_000);
const mnemonic_count = @typeInfo(Mnemonic).@"enum".fields.len;
var mnemonic_map: [mnemonic_count][]Data = .{&.{}} ** mnemonic_count;
var mnemonic_map: [mnemonic_count][]Data = @splat(&.{});
const encodings = @import("encodings.zig");
for (encodings.table) |entry| mnemonic_map[@intFromEnum(entry[0])].len += 1;
var data_storage: [encodings.table.len]Data = undefined;
@ -855,7 +897,7 @@ const mnemonic_to_encodings_map = init: {
value.ptr = data_storage[storage_i..].ptr;
storage_i += value.len;
}
var mnemonic_i: [mnemonic_count]usize = .{0} ** mnemonic_count;
var mnemonic_i: [mnemonic_count]usize = @splat(0);
const ops_len = @typeInfo(std.meta.FieldType(Data, .ops)).array.len;
const opc_len = @typeInfo(std.meta.FieldType(Data, .opc)).array.len;
for (encodings.table) |entry| {
@ -872,7 +914,7 @@ const mnemonic_to_encodings_map = init: {
i.* += 1;
}
const final_storage = data_storage;
var final_map: [mnemonic_count][]const Data = .{&.{}} ** mnemonic_count;
var final_map: [mnemonic_count][]const Data = @splat(&.{});
storage_i = 0;
for (&final_map, mnemonic_map) |*final_value, value| {
final_value.* = final_storage[storage_i..][0..value.len];

View File

@ -1,6 +1,7 @@
//! This file contains the functionality for lowering x86_64 MIR to Instructions
bin_file: *link.File,
target: *const std.Target,
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
pic: bool,
@ -56,6 +57,7 @@ pub const Reloc = struct {
const Target = union(enum) {
inst: Mir.Inst.Index,
table,
linker_reloc: u32,
linker_tlsld: u32,
linker_dtpoff: u32,
@ -193,7 +195,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_probe_align_ri_s => {
try lower.emit(.none, .@"test", &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = Immediate.s(@bitCast(inst.data.ri.i)) },
.{ .imm = .s(@bitCast(inst.data.ri.i)) },
});
try lower.emit(.none, .jz, &.{
.{ .imm = lower.reloc(.{ .inst = index + 1 }, 0) },
@ -229,14 +231,14 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
}
try lower.emit(.none, .sub, &.{
.{ .reg = inst.data.ri.r1 },
.{ .imm = Immediate.s(@bitCast(inst.data.ri.i)) },
.{ .imm = .s(@bitCast(inst.data.ri.i)) },
});
assert(lower.result_insts_len <= pseudo_probe_adjust_unrolled_max_insts);
},
.pseudo_probe_adjust_setup_rri_s => {
try lower.emit(.none, .mov, &.{
.{ .reg = inst.data.rri.r2.to32() },
.{ .imm = Immediate.s(@bitCast(inst.data.rri.i)) },
.{ .imm = .s(@bitCast(inst.data.rri.i)) },
});
try lower.emit(.none, .sub, &.{
.{ .reg = inst.data.rri.r1 },
@ -255,7 +257,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
});
try lower.emit(.none, .sub, &.{
.{ .reg = inst.data.rr.r2 },
.{ .imm = Immediate.s(page_size) },
.{ .imm = .s(page_size) },
});
try lower.emit(.none, .jae, &.{
.{ .imm = lower.reloc(.{ .inst = index }, 0) },
@ -347,7 +349,7 @@ pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
return error.LowerFail;
}
pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
return switch (ops) {
.rri_s,
.ri_s,
@ -355,7 +357,7 @@ pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
.mi_s,
.rmi_s,
.pseudo_dbg_local_ai_s,
=> Immediate.s(@bitCast(i)),
=> .s(@bitCast(i)),
.rrri,
.rri_u,
@ -368,18 +370,26 @@ pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
.rrm,
.rrmi,
.pseudo_dbg_local_ai_u,
=> Immediate.u(i),
=> .u(i),
.ri_64,
.pseudo_dbg_local_ai_64,
=> Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
=> .u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
else => unreachable,
};
}
pub fn mem(lower: Lower, payload: u32) Memory {
return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
pub fn mem(lower: *Lower, payload: u32) Memory {
var m = lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
switch (m) {
.sib => |*sib| switch (sib.base) {
else => {},
.table => sib.disp = lower.reloc(.table, sib.disp).signed,
},
else => {},
}
return m;
}
fn reloc(lower: *Lower, target: Reloc.Target, off: i32) Immediate {
@ -389,7 +399,7 @@ fn reloc(lower: *Lower, target: Reloc.Target, off: i32) Immediate {
.off = off,
};
lower.result_relocs_len += 1;
return Immediate.s(0);
return .s(0);
}
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
@ -417,19 +427,17 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
// Here, we currently assume local dynamic TLS vars, and so
// we emit LD model.
_ = lower.reloc(.{ .linker_tlsld = sym_index }, 0);
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .lea, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .lea, &.{
.{ .reg = .rdi },
.{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
});
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(.{
.linker_extern_fn = try elf_file.getGlobalSymbol("__tls_get_addr", null),
}, 0);
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .call, &[_]Operand{
.{ .imm = Immediate.s(0) },
});
lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{
.{ .imm = .s(0) },
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0);
emit_mnemonic = .lea;
@ -439,11 +447,10 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
}) };
} else {
// Since we are linking statically, we emit LE model directly.
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .mov, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = .rax },
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) },
});
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
emit_mnemonic = .lea;
@ -463,11 +470,10 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
.mov => {
if (elf_sym.flags.is_extern_ptr) {
const reg = ops[0].reg;
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .mov, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = reg.to64() },
.{ .mem = Memory.initRip(.qword, 0) },
});
}, lower.target);
lower.result_insts_len += 1;
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
.reg = reg.to64(),
@ -482,7 +488,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
}) },
.lea => {
emit_mnemonic = .mov;
break :op .{ .imm = Immediate.s(0) };
break :op .{ .imm = .s(0) };
},
.mov => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
@ -495,16 +501,14 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
if (macho_sym.flags.tlv) {
_ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .mov, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = .rdi },
.{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
});
}, lower.target);
lower.result_insts_len += 1;
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .call, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .call, &.{
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
});
}, lower.target);
lower.result_insts_len += 1;
emit_mnemonic = .mov;
break :op .{ .reg = .rax };
@ -519,11 +523,10 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
.mov => {
if (macho_sym.flags.is_extern_ptr) {
const reg = ops[0].reg;
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .mov, &[_]Operand{
lower.result_insts[lower.result_insts_len] = try .new(.none, .mov, &.{
.{ .reg = reg.to64() },
.{ .mem = Memory.initRip(.qword, 0) },
});
}, lower.target);
lower.result_insts_len += 1;
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
.reg = reg.to64(),
@ -540,8 +543,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
},
};
}
lower.result_insts[lower.result_insts_len] =
try Instruction.new(emit_prefix, emit_mnemonic, emit_ops);
lower.result_insts[lower.result_insts_len] = try .new(emit_prefix, emit_mnemonic, emit_ops, lower.target);
lower.result_insts_len += 1;
}
@ -743,7 +745,7 @@ fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Er
while (it.next()) |i| {
try lower.emit(.directive, .@".cfi_rel_offset", &.{
.{ .reg = callee_preserved_regs[i] },
.{ .imm = Immediate.s(off) },
.{ .imm = .s(off) },
});
off += 8;
}

View File

@ -9,6 +9,7 @@
instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []const u32,
table: []const Inst.Index,
frame_locs: std.MultiArrayList(FrameLoc).Slice,
pub const Inst = struct {
@ -22,17 +23,26 @@ pub const Inst = struct {
/// ___
@"_",
/// Integer __
/// Integer ___
i_,
/// ___ Left
_l,
/// ___ Left Double
_ld,
/// ___ Left Without Affecting Flags
_lx,
/// ___ Right
_r,
/// ___ Right Double
_rd,
/// ___ Right Without Affecting Flags
_rx,
/// ___ Forward
_f,
/// ___ Reverse
//_r,
/// ___ Above
_a,
@ -43,6 +53,7 @@ pub const Inst = struct {
/// ___ Below Or Equal
_be,
/// ___ Carry
/// ___ Carry Flag
_c,
/// ___ Equal
_e,
@ -94,6 +105,14 @@ pub const Inst = struct {
_s,
/// ___ Zero
_z,
/// ___ Alignment Check Flag
_ac,
/// ___ Direction Flag
//_d,
/// ___ Interrupt Flag
_i,
/// ___ User Interrupt Flag
_ui,
/// ___ Byte
//_b,
@ -210,6 +229,10 @@ pub const Inst = struct {
p_q,
/// Packed ___ Double Quadword
p_dq,
/// ___ Aligned Packed Integer Values
_dqa,
/// ___ Unaligned Packed Integer Values
_dqu,
/// ___ Scalar Single-Precision Values
_ss,
@ -230,6 +253,10 @@ pub const Inst = struct {
v_d,
/// VEX-Encoded ___ QuadWord
v_q,
/// VEX-Encoded ___ Aligned Packed Integer Values
v_dqa,
/// VEX-Encoded ___ Unaligned Packed Integer Values
v_dqu,
/// VEX-Encoded ___ Integer Data
v_i128,
/// VEX-Encoded Packed ___
@ -287,9 +314,8 @@ pub const Inst = struct {
/// Bitwise logical and of packed double-precision floating-point values
@"and",
/// Bit scan forward
bsf,
/// Bit scan reverse
bsr,
bs,
/// Byte swap
bswap,
/// Bit test
@ -305,6 +331,10 @@ pub const Inst = struct {
cdq,
/// Convert doubleword to quadword
cdqe,
/// Clear carry flag
/// Clear direction flag
/// Clear interrupt flag
cl,
/// Flush cache line
clflush,
/// Conditional move
@ -358,6 +388,8 @@ pub const Inst = struct {
/// Move scalar double-precision floating-point value
/// Move doubleword
/// Move quadword
/// Move aligned packed integer values
/// Move unaligned packed integer values
mov,
/// Move data after swapping bytes
movbe,
@ -401,9 +433,11 @@ pub const Inst = struct {
ret,
/// Rotate left
/// Rotate right
/// Rotate right logical without affecting flags
ro,
/// Arithmetic shift left
/// Arithmetic shift right
/// Shift left arithmetic without affecting flags
sa,
/// Integer subtraction with borrow
sbb,
@ -417,6 +451,8 @@ pub const Inst = struct {
/// Double precision shift left
/// Logical shift right
/// Double precision shift right
/// Shift left logical without affecting flags
/// Shift right logical without affecting flags
sh,
/// Subtract
/// Subtract packed integers
@ -425,6 +461,11 @@ pub const Inst = struct {
/// Subtract packed double-precision floating-point values
/// Subtract scalar double-precision floating-point values
sub,
/// Set carry flag
/// Set direction flag
/// Set interrupt flag
/// Store floating-point value
st,
/// Store string
sto,
/// Syscall
@ -460,8 +501,6 @@ pub const Inst = struct {
ldenv,
/// Store x87 FPU environment
nstenv,
/// Store floating-point value
st,
/// Store x87 FPU environment
stenv,
@ -542,8 +581,14 @@ pub const Inst = struct {
/// Move aligned packed single-precision floating-point values
/// Move aligned packed double-precision floating-point values
mova,
/// Move high packed single-precision floating-point values
/// Move high packed double-precision floating-point values
movh,
/// Move packed single-precision floating-point values high to low
movhl,
/// Move low packed single-precision floating-point values
/// Move low packed double-precision floating-point values
movl,
/// Move packed single-precision floating-point values low to high
movlh,
/// Move unaligned packed single-precision floating-point values
@ -601,10 +646,6 @@ pub const Inst = struct {
cvttps2dq,
/// Convert with truncation scalar double-precision floating-point value to doubleword integer
cvttsd2si,
/// Move aligned packed integer values
movdqa,
/// Move unaligned packed integer values
movdqu,
/// Packed interleave shuffle of quadruplets of single-precision floating-point values
/// Packed interleave shuffle of pairs of double-precision floating-point values
/// Shuffle packed doublewords
@ -1127,11 +1168,13 @@ pub const AirOffset = struct { air_inst: Air.Inst.Index, off: i32 };
/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
pub const RegisterList = struct {
bitset: BitSet = BitSet.initEmpty(),
bitset: BitSet,
const BitSet = IntegerBitSet(32);
const Self = @This();
pub const empty: RegisterList = .{ .bitset = .initEmpty() };
fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
for (registers, 0..) |cpreg, i| {
if (reg.id() == cpreg.id()) return @intCast(i);
@ -1157,8 +1200,12 @@ pub const RegisterList = struct {
return @intCast(self.bitset.count());
}
pub fn size(self: Self) i32 {
return @intCast(self.bitset.count() * 8);
pub fn size(self: Self, target: *const std.Target) i32 {
return @intCast(self.bitset.count() * @as(u4, switch (target.cpu.arch) {
else => unreachable,
.x86 => 4,
.x86_64 => 8,
}));
}
};
@ -1197,7 +1244,7 @@ pub const Memory = struct {
size: bits.Memory.Size,
index: Register,
scale: bits.Memory.Scale,
_: u16 = undefined,
_: u15 = undefined,
};
pub fn encode(mem: bits.Memory) Memory {
@ -1220,7 +1267,7 @@ pub const Memory = struct {
},
},
.base = switch (mem.base) {
.none => undefined,
.none, .table => undefined,
.reg => |reg| @intFromEnum(reg),
.frame => |frame_index| @intFromEnum(frame_index),
.reloc => |sym_index| sym_index,
@ -1249,6 +1296,7 @@ pub const Memory = struct {
.none => .none,
.reg => .{ .reg = @enumFromInt(mem.base) },
.frame => .{ .frame = @enumFromInt(mem.base) },
.table => .table,
.reloc => .{ .reloc = mem.base },
},
.scale_index = switch (mem.info.index) {
@ -1277,6 +1325,7 @@ pub const Memory = struct {
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
mir.instructions.deinit(gpa);
gpa.free(mir.extra);
gpa.free(mir.table);
mir.frame_locs.deinit(gpa);
mir.* = undefined;
}
@ -1312,7 +1361,7 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
return switch (mem.info.base) {
.none, .reg, .reloc => mem,
.none, .reg, .table, .reloc => mem,
.frame => if (mir.frame_locs.len > 0) .{
.info = .{
.base = .reg,

View File

@ -242,17 +242,20 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: std.Target, ctx: Context) [8
.sse, .sseup, .sseup, .sseup,
.sseup, .sseup, .sseup, .none,
};
// LLVM always returns vectors byval
if (bits <= 512 or ctx == .ret) return .{
if (bits <= 512 or (ctx == .ret and bits <= @as(u64, if (std.Target.x86.featureSetHas(target.cpu.features, .avx512f))
2048
else if (std.Target.x86.featureSetHas(target.cpu.features, .avx))
1024
else
512))) return .{
.sse, .sseup, .sseup, .sseup,
.sseup, .sseup, .sseup, .sseup,
};
return memory_class;
},
.optional => {
if (ty.isPtrLikeOptional(zcu)) {
result[0] = .integer;
return result;
if (ty.optionalReprIsPayload(zcu)) {
return classifySystemV(ty.optionalChild(zcu), zcu, target, ctx);
}
return memory_class;
},
@ -405,6 +408,31 @@ fn classifySystemVUnion(
return starting_byte_offset + loaded_union.sizeUnordered(ip);
}
pub const zigcc = struct {
pub const stack_align: ?InternPool.Alignment = null;
pub const return_in_regs = true;
pub const params_in_regs = true;
const volatile_gpr = gp_regs.len - 5;
const volatile_x87 = x87_regs.len - 1;
const volatile_sse = sse_avx_regs.len;
/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
/// for anything else but stack offset tracking therefore we exclude them from this set.
pub const callee_preserved_regs = gp_regs[volatile_gpr..] ++ x87_regs[volatile_x87 .. x87_regs.len - 1] ++ sse_avx_regs[volatile_sse..];
/// These registers need to be preserved (saved on the stack) and restored by the caller before
/// the caller relinquishes control to a subroutine via call instruction (or similar).
/// In other words, these registers are free to use by the callee.
pub const caller_preserved_regs = gp_regs[0..volatile_gpr] ++ x87_regs[0..volatile_x87] ++ sse_avx_regs[0..volatile_sse];
const int_param_regs = gp_regs[0 .. volatile_gpr - 1];
const x87_param_regs = x87_regs[0..volatile_x87];
const sse_param_regs = sse_avx_regs[0..volatile_sse];
const int_return_regs = gp_regs[0..volatile_gpr];
const x87_return_regs = x87_regs[0..volatile_x87];
const sse_return_regs = sse_avx_regs[0..volatile_gpr];
};
pub const SysV = struct {
/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
/// for anything else but stack offset tracking therefore we exclude them from this set.
@ -415,9 +443,11 @@ pub const SysV = struct {
pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
pub const c_abi_x87_param_regs = x87_regs[0..0].*;
pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*;
pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
pub const c_abi_sse_return_regs = sse_avx_regs[0..2].*;
pub const c_abi_x87_return_regs = x87_regs[0..2].*;
pub const c_abi_sse_return_regs = sse_avx_regs[0..4].*;
};
pub const Win64 = struct {
@ -430,74 +460,96 @@ pub const Win64 = struct {
pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
pub const c_abi_x87_param_regs = x87_regs[0..0].*;
pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*;
pub const c_abi_int_return_regs = [_]Register{.rax};
pub const c_abi_x87_return_regs = x87_regs[0..0].*;
pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*;
};
pub fn resolveCallingConvention(
cc: std.builtin.CallingConvention,
target: std.Target,
) std.builtin.CallingConvention {
return switch (cc) {
.auto => switch (target.os.tag) {
else => .{ .x86_64_sysv = .{} },
.windows => .{ .x86_64_win = .{} },
},
else => cc,
};
}
pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCalleePreservedRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.callee_preserved_regs,
.x86_64_sysv => &SysV.callee_preserved_regs,
.x86_64_win => &Win64.callee_preserved_regs,
else => unreachable,
};
}
pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCallerPreservedRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.caller_preserved_regs,
.x86_64_sysv => &SysV.caller_preserved_regs,
.x86_64_win => &Win64.caller_preserved_regs,
else => unreachable,
};
}
pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.int_param_regs,
.x86_64_sysv => &SysV.c_abi_int_param_regs,
.x86_64_win => &Win64.c_abi_int_param_regs,
else => unreachable,
};
}
pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCAbiX87ParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.x87_param_regs,
.x86_64_sysv => &SysV.c_abi_x87_param_regs,
.x86_64_win => &Win64.c_abi_x87_param_regs,
else => unreachable,
};
}
pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.sse_param_regs,
.x86_64_sysv => &SysV.c_abi_sse_param_regs,
.x86_64_win => &Win64.c_abi_sse_param_regs,
else => unreachable,
};
}
pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.int_return_regs,
.x86_64_sysv => &SysV.c_abi_int_return_regs,
.x86_64_win => &Win64.c_abi_int_return_regs,
else => unreachable,
};
}
pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register {
pub fn getCAbiX87ReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.x87_return_regs,
.x86_64_sysv => &SysV.c_abi_x87_return_regs,
.x86_64_win => &Win64.c_abi_x87_return_regs,
else => unreachable,
};
}
pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention.Tag) []const Register {
return switch (cc) {
.auto => zigcc.sse_return_regs,
.x86_64_sysv => &SysV.c_abi_sse_return_regs,
.x86_64_win => &Win64.c_abi_sse_return_regs,
else => unreachable,
};
}
pub fn getCAbiLinkerScratchReg(cc: std.builtin.CallingConvention.Tag) Register {
return switch (cc) {
.auto => zigcc.int_return_regs[zigcc.int_return_regs.len - 1],
.x86_64_sysv => SysV.c_abi_int_return_regs[0],
.x86_64_win => Win64.c_abi_int_return_regs[0],
else => unreachable,
};
}
const gp_regs = [_]Register{
.rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
.rax, .rdx, .rbx, .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
};
const x87_regs = [_]Register{
.st0, .st1, .st2, .st3, .st4, .st5, .st6, .st7,

View File

@ -150,6 +150,31 @@ pub const Condition = enum(u5) {
.nz_or_p => .z_and_np,
};
}
/// Returns the equivalent condition when the operands are swapped.
pub fn commute(cond: Condition) Condition {
return switch (cond) {
else => cond,
.a => .b,
.ae => .be,
.b => .a,
.be => .ae,
.c => .a,
.g => .l,
.ge => .le,
.l => .g,
.le => .ge,
.na => .nb,
.nae => .nbe,
.nb => .na,
.nbe => .nae,
.nc => .na,
.ng => .nl,
.nge => .nle,
.nl => .ng,
.nle => .nge,
};
}
};
pub const Register = enum(u7) {
@ -454,37 +479,41 @@ pub const RegisterOffset = struct { reg: Register, off: i32 = 0 };
pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 };
pub const Memory = struct {
base: Base,
mod: Mod,
base: Base = .none,
mod: Mod = .{ .rm = .{} },
pub const Base = union(enum(u2)) {
pub const Base = union(enum(u3)) {
none,
reg: Register,
frame: FrameIndex,
table,
reloc: u32,
pub const Tag = @typeInfo(Base).@"union".tag_type.?;
pub fn isExtended(self: Base) bool {
return switch (self) {
.none, .frame, .reloc => false, // rsp, rbp, and rip are not extended
.none, .frame, .table, .reloc => false, // rsp, rbp, and rip are not extended
.reg => |reg| reg.isExtended(),
};
}
};
pub const Mod = union(enum(u1)) {
rm: struct {
size: Size,
rm: Rm,
off: u64,
pub const Rm = struct {
size: Size = .none,
index: Register = .none,
scale: Scale = .@"1",
disp: i32 = 0,
},
off: u64,
};
};
pub const Size = enum(u4) {
none,
ptr,
byte,
word,
dword,
@ -521,9 +550,10 @@ pub const Memory = struct {
};
}
pub fn bitSize(s: Size) u64 {
pub fn bitSize(s: Size, target: *const std.Target) u64 {
return switch (s) {
.none => 0,
.ptr => target.ptrBitWidth(),
.byte => 8,
.word => 16,
.dword => 32,
@ -543,11 +573,50 @@ pub const Memory = struct {
) @TypeOf(writer).Error!void {
if (s == .none) return;
try writer.writeAll(@tagName(s));
try writer.writeAll(" ptr");
switch (s) {
.none => unreachable,
.ptr => {},
else => {
try writer.writeByte(' ');
try writer.writeAll("ptr");
},
}
}
};
pub const Scale = enum(u2) { @"1", @"2", @"4", @"8" };
pub const Scale = enum(u2) {
@"1",
@"2",
@"4",
@"8",
pub fn fromFactor(factor: u4) Scale {
return switch (factor) {
else => unreachable,
1 => .@"1",
2 => .@"2",
4 => .@"4",
8 => .@"8",
};
}
pub fn toFactor(scale: Scale) u4 {
return switch (scale) {
.@"1" => 1,
.@"2" => 2,
.@"4" => 4,
.@"8" => 8,
};
}
pub fn fromLog2(log2: u2) Scale {
return @enumFromInt(log2);
}
pub fn toLog2(scale: Scale) u2 {
return @intFromEnum(scale);
}
};
};
pub const Immediate = union(enum) {

View File

@ -138,7 +138,7 @@ pub const Instruction = struct {
.moffs => true,
.rip => false,
.sib => |s| switch (s.base) {
.none, .frame, .reloc => false,
.none, .frame, .table, .reloc => false,
.reg => |reg| reg.class() == .segment,
},
};
@ -161,17 +161,17 @@ pub const Instruction = struct {
pub fn disp(mem: Memory) Immediate {
return switch (mem) {
.sib => |s| Immediate.s(s.disp),
.rip => |r| Immediate.s(r.disp),
.moffs => |m| Immediate.u(m.offset),
.sib => |s| .s(s.disp),
.rip => |r| .s(r.disp),
.moffs => |m| .u(m.offset),
};
}
pub fn bitSize(mem: Memory) u64 {
pub fn bitSize(mem: Memory, target: *const std.Target) u64 {
return switch (mem) {
.rip => |r| r.ptr_size.bitSize(),
.sib => |s| s.ptr_size.bitSize(),
.moffs => 64,
.rip => |r| r.ptr_size.bitSize(target),
.sib => |s| s.ptr_size.bitSize(target),
.moffs => target.ptrBitWidth(),
};
}
};
@ -277,6 +277,7 @@ pub const Instruction = struct {
.none => any = false,
.reg => |reg| try writer.print("{s}", .{@tagName(reg)}),
.frame => |frame_index| try writer.print("{}", .{frame_index}),
.table => try writer.print("Table", .{}),
.reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}),
}
if (mem.scaleIndex()) |si| {
@ -314,28 +315,33 @@ pub const Instruction = struct {
}
};
pub fn new(prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) !Instruction {
pub fn new(
prefix: Prefix,
mnemonic: Mnemonic,
ops: []const Operand,
target: *const std.Target,
) !Instruction {
const encoding: Encoding = switch (prefix) {
else => (try Encoding.findByMnemonic(prefix, mnemonic, ops)) orelse {
else => (try Encoding.findByMnemonic(prefix, mnemonic, ops, target)) orelse {
log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{
@tagName(prefix),
@tagName(mnemonic),
@tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none),
@tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none),
@tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none),
@tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none),
@tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0], target) else .none),
@tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1], target) else .none),
@tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2], target) else .none),
@tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3], target) else .none),
});
return error.InvalidInstruction;
},
.directive => .{
.mnemonic = mnemonic,
.data = .{
.op_en = .zo,
.op_en = .z,
.ops = .{
if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none,
if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none,
if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none,
if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none,
if (ops.len > 0) Encoding.Op.fromOperand(ops[0], target) else .none,
if (ops.len > 1) Encoding.Op.fromOperand(ops[1], target) else .none,
if (ops.len > 2) Encoding.Op.fromOperand(ops[2], target) else .none,
if (ops.len > 3) Encoding.Op.fromOperand(ops[3], target) else .none,
},
.opc_len = 0,
.opc = undefined,
@ -395,7 +401,7 @@ pub const Instruction = struct {
}
switch (data.op_en) {
.zo, .o => {},
.z, .o, .zo, .oz => {},
.i, .d => try encodeImm(inst.ops[0].imm, data.ops[0], encoder),
.zi, .oi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder),
.fd => try encoder.imm64(inst.ops[1].mem.moffs.offset),
@ -403,7 +409,7 @@ pub const Instruction = struct {
else => {
const mem_op = switch (data.op_en) {
.m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
.rm, .rmi, .rm0, .vmi => inst.ops[1],
.rm, .rmi, .rm0, .vmi, .rmv => inst.ops[1],
.rvm, .rvmr, .rvmi => inst.ops[2],
else => unreachable,
};
@ -412,7 +418,7 @@ pub const Instruction = struct {
const rm = switch (data.op_en) {
.m, .mi, .m1, .mc, .vmi => enc.modRmExt(),
.mr, .mri, .mrc => inst.ops[1].reg.lowEnc(),
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0].reg.lowEnc(),
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => inst.ops[0].reg.lowEnc(),
.mvr => inst.ops[2].reg.lowEnc(),
else => unreachable,
};
@ -422,7 +428,7 @@ pub const Instruction = struct {
const op = switch (data.op_en) {
.m, .mi, .m1, .mc, .vmi => .none,
.mr, .mri, .mrc => inst.ops[1],
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0],
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => inst.ops[0],
.mvr => inst.ops[2],
else => unreachable,
};
@ -448,7 +454,8 @@ pub const Instruction = struct {
const final = opcode.len - 1;
for (opcode[first..final]) |byte| try encoder.opcode_1byte(byte);
switch (inst.encoding.data.op_en) {
.o, .oi => try encoder.opcode_withReg(opcode[final], inst.ops[0].reg.lowEnc()),
.o, .oz, .oi => try encoder.opcode_withReg(opcode[final], inst.ops[0].reg.lowEnc()),
.zo => try encoder.opcode_withReg(opcode[final], inst.ops[1].reg.lowEnc()),
else => try encoder.opcode_1byte(opcode[final]),
}
}
@ -474,7 +481,7 @@ pub const Instruction = struct {
}
const segment_override: ?Register = switch (op_en) {
.zo, .i, .zi, .o, .oi, .d => null,
.z, .i, .zi, .o, .zo, .oz, .oi, .d => null,
.fd => inst.ops[1].mem.base().reg,
.td => inst.ops[0].mem.base().reg,
.rm, .rmi, .rm0 => if (inst.ops[1].isSegmentRegister())
@ -493,7 +500,7 @@ pub const Instruction = struct {
}
else
null,
.vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable,
.vmi, .rvm, .rvmr, .rvmi, .mvr, .rmv => unreachable,
};
if (segment_override) |seg| {
legacy.setSegmentOverride(seg);
@ -510,11 +517,12 @@ pub const Instruction = struct {
rex.w = inst.encoding.data.mode == .long;
switch (op_en) {
.zo, .i, .zi, .fd, .td, .d => {},
.o, .oi => rex.b = inst.ops[0].reg.isExtended(),
.m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0 => {
.z, .i, .zi, .fd, .td, .d => {},
.o, .oz, .oi => rex.b = inst.ops[0].reg.isExtended(),
.zo => rex.b = inst.ops[1].reg.isExtended(),
.m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .rmv => {
const r_op = switch (op_en) {
.rm, .rmi, .rm0 => inst.ops[0],
.rm, .rmi, .rm0, .rmv => inst.ops[0],
.mr, .mri, .mrc => inst.ops[1],
else => .none,
};
@ -544,11 +552,12 @@ pub const Instruction = struct {
vex.w = inst.encoding.data.mode.isLong();
switch (op_en) {
.zo, .i, .zi, .fd, .td, .d => {},
.o, .oi => vex.b = inst.ops[0].reg.isExtended(),
.m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr => {
.z, .i, .zi, .fd, .td, .d => {},
.o, .oz, .oi => vex.b = inst.ops[0].reg.isExtended(),
.zo => vex.b = inst.ops[1].reg.isExtended(),
.m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr, .rmv => {
const r_op = switch (op_en) {
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi => inst.ops[0],
.rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => inst.ops[0],
.mr, .mri, .mrc => inst.ops[1],
.mvr => inst.ops[2],
.m, .mi, .m1, .mc, .vmi => .none,
@ -557,7 +566,7 @@ pub const Instruction = struct {
vex.r = r_op.isBaseExtended();
const b_x_op = switch (op_en) {
.rm, .rmi, .rm0, .vmi => inst.ops[1],
.rm, .rmi, .rm0, .vmi, .rmv => inst.ops[1],
.m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
.rvm, .rvmr, .rvmi => inst.ops[2],
else => unreachable,
@ -588,6 +597,7 @@ pub const Instruction = struct {
else => {},
.vmi => vex.v = inst.ops[0].reg,
.rvm, .rvmr, .rvmi => vex.v = inst.ops[1].reg,
.rmv => vex.v = inst.ops[2].reg,
}
try encoder.vex(vex);
@ -608,7 +618,7 @@ pub const Instruction = struct {
switch (mem) {
.moffs => unreachable,
.sib => |sib| switch (sib.base) {
.none => {
.none, .table => {
try encoder.modRm_SIBDisp0(operand_enc);
if (mem.scaleIndex()) |si| {
const scale = math.log2_int(u4, si.scale);
@ -676,11 +686,11 @@ pub const Instruction = struct {
else => unreachable,
},
.frame => if (@TypeOf(encoder).options.allow_frame_locs) {
try encoder.modRm_indirectDisp32(operand_enc, undefined);
try encoder.modRm_indirectDisp32(operand_enc, 0);
try encoder.disp32(undefined);
} else return error.CannotEncode,
.reloc => if (@TypeOf(encoder).options.allow_symbols) {
try encoder.modRm_indirectDisp32(operand_enc, undefined);
try encoder.modRm_indirectDisp32(operand_enc, 0);
try encoder.disp32(undefined);
} else return error.CannotEncode,
},
@ -1185,7 +1195,7 @@ const TestEncode = struct {
) !void {
var stream = std.io.fixedBufferStream(&enc.buffer);
var count_writer = std.io.countingWriter(stream.writer());
const inst = try Instruction.new(.none, mnemonic, ops);
const inst: Instruction = try .new(.none, mnemonic, ops);
try inst.encode(count_writer.writer(), .{});
enc.index = count_writer.bytes_written;
}
@ -1199,9 +1209,9 @@ test "encode" {
var buf = std.ArrayList(u8).init(testing.allocator);
defer buf.deinit();
const inst = try Instruction.new(.none, .mov, &.{
const inst: Instruction = try .new(.none, .mov, &.{
.{ .reg = .rbx },
.{ .imm = Instruction.Immediate.u(4) },
.{ .imm = .u(4) },
});
try inst.encode(buf.writer(), .{});
try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
@ -1211,47 +1221,47 @@ test "lower I encoding" {
var enc = TestEncode{};
try enc.encode(.push, &.{
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
try enc.encode(.push, &.{
.{ .imm = Instruction.Immediate.u(0x1000) },
.{ .imm = .u(0x1000) },
});
try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
try enc.encode(.push, &.{
.{ .imm = Instruction.Immediate.u(0x10000000) },
.{ .imm = .u(0x10000000) },
});
try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
try enc.encode(.adc, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x10000000) },
.{ .imm = .u(0x10000000) },
});
try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
try enc.encode(.add, &.{
.{ .reg = .al },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
try enc.encode(.add, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.sbb, &.{
.{ .reg = .ax },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
try enc.encode(.xor, &.{
.{ .reg = .al },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
}
@ -1261,43 +1271,43 @@ test "lower MI encoding" {
try enc.encode(.mov, &.{
.{ .reg = .r12 },
.{ .imm = Instruction.Immediate.u(0x1000) },
.{ .imm = .u(0x1000) },
});
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
try enc.encode(.mov, &.{
.{ .reg = .r12 },
.{ .imm = Instruction.Immediate.u(0x1000) },
.{ .imm = .u(0x1000) },
});
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{
.{ .reg = .r12 },
.{ .imm = Instruction.Immediate.u(0x1000) },
.{ .imm = .u(0x1000) },
});
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
@ -1307,19 +1317,19 @@ test "lower MI encoding" {
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
.{ .imm = Instruction.Immediate.s(-16) },
.{ .imm = .s(-16) },
});
try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
@ -1329,7 +1339,7 @@ test "lower MI encoding" {
.disp = 0x10000000,
.scale_index = .{ .scale = 2, .index = .rcx },
}) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
@ -1339,43 +1349,43 @@ test "lower MI encoding" {
try enc.encode(.adc, &.{
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
try enc.encode(.adc, &.{
.{ .mem = Instruction.Memory.initRip(.qword, 0) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
try enc.encode(.adc, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
try enc.encode(.add, &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
try enc.encode(.add, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.add, &.{
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
.{ .imm = Instruction.Immediate.s(-0x10) },
.{ .imm = .s(-0x10) },
});
try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x83\x24\x25\x00\x00\x00\x10\x10",
@ -1385,7 +1395,7 @@ test "lower MI encoding" {
try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x26\x83\x24\x25\x00\x00\x00\x10\x10",
@ -1395,7 +1405,7 @@ test "lower MI encoding" {
try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
@ -1405,7 +1415,7 @@ test "lower MI encoding" {
try enc.encode(.sub, &.{
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings(
"\x41\x83\xAB\x00\x00\x00\x10\x10",
@ -1624,14 +1634,14 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .r11 },
.{ .reg = .r12 },
.{ .imm = Instruction.Immediate.s(-2) },
.{ .imm = .s(-2) },
});
try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
try enc.encode(.imul, &.{
.{ .reg = .r11 },
.{ .mem = Instruction.Memory.initRip(.qword, -16) },
.{ .imm = Instruction.Immediate.s(-1024) },
.{ .imm = .s(-1024) },
});
try expectEqualHexStrings(
"\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
@ -1642,7 +1652,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .bx },
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
.{ .imm = Instruction.Immediate.s(-1024) },
.{ .imm = .s(-1024) },
});
try expectEqualHexStrings(
"\x66\x69\x5D\xF0\x00\xFC",
@ -1653,7 +1663,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .bx },
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
.{ .imm = Instruction.Immediate.u(1024) },
.{ .imm = .u(1024) },
});
try expectEqualHexStrings(
"\x66\x69\x5D\xF0\x00\x04",
@ -1769,7 +1779,7 @@ test "lower M encoding" {
try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
try enc.encode(.call, &.{
.{ .imm = Instruction.Immediate.s(0) },
.{ .imm = .s(0) },
});
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
@ -1828,7 +1838,7 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{
.{ .reg = .rax },
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
.{ .imm = .u(0x1000000000000000) },
});
try expectEqualHexStrings(
"\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
@ -1838,7 +1848,7 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{
.{ .reg = .r11 },
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
.{ .imm = .u(0x1000000000000000) },
});
try expectEqualHexStrings(
"\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
@ -1848,19 +1858,19 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{
.{ .reg = .r11d },
.{ .imm = Instruction.Immediate.u(0x10000000) },
.{ .imm = .u(0x10000000) },
});
try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
try enc.encode(.mov, &.{
.{ .reg = .r11w },
.{ .imm = Instruction.Immediate.u(0x1000) },
.{ .imm = .u(0x1000) },
});
try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
try enc.encode(.mov, &.{
.{ .reg = .r11b },
.{ .imm = Instruction.Immediate.u(0x10) },
.{ .imm = .u(0x10) },
});
try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
}
@ -1934,7 +1944,7 @@ test "lower NP encoding" {
}
fn invalidInstruction(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void {
const err = Instruction.new(.none, mnemonic, ops);
const err: Instruction = .new(.none, mnemonic, ops);
try testing.expectError(error.InvalidInstruction, err);
}
@ -1982,12 +1992,12 @@ test "invalid instruction" {
.{ .reg = .r12d },
});
try invalidInstruction(.push, &.{
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
.{ .imm = .u(0x1000000000000000) },
});
}
fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void {
try testing.expectError(error.CannotEncode, Instruction.new(.none, mnemonic, ops));
try testing.expectError(error.CannotEncode, .new(.none, mnemonic, ops));
}
test "cannot encode" {
@ -2171,7 +2181,7 @@ const Assembler = struct {
pub fn assemble(as: *Assembler, writer: anytype) !void {
while (try as.next()) |parsed_inst| {
const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
const inst: Instruction = try .new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
try inst.encode(writer, .{});
}
}

View File

@ -124,106 +124,118 @@ pub const table = [_]Entry{
.{ .call, .d, &.{ .rel32 }, &.{ 0xe8 }, 0, .none, .none },
.{ .call, .m, &.{ .rm64 }, &.{ 0xff }, 2, .none, .none },
.{ .cbw, .zo, &.{ .o16 }, &.{ 0x98 }, 0, .short, .none },
.{ .cwde, .zo, &.{ .o32 }, &.{ 0x98 }, 0, .none, .none },
.{ .cdqe, .zo, &.{ .o64 }, &.{ 0x98 }, 0, .long, .none },
.{ .cbw, .z, &.{ .o16 }, &.{ 0x98 }, 0, .short, .none },
.{ .cwde, .z, &.{ .o32 }, &.{ 0x98 }, 0, .none, .none },
.{ .cdqe, .z, &.{ .o64 }, &.{ 0x98 }, 0, .long, .none },
.{ .cwd, .zo, &.{ .o16 }, &.{ 0x99 }, 0, .short, .none },
.{ .cdq, .zo, &.{ .o32 }, &.{ 0x99 }, 0, .none, .none },
.{ .cqo, .zo, &.{ .o64 }, &.{ 0x99 }, 0, .long, .none },
.{ .cwd, .z, &.{ .o16 }, &.{ 0x99 }, 0, .short, .none },
.{ .cdq, .z, &.{ .o32 }, &.{ 0x99 }, 0, .none, .none },
.{ .cqo, .z, &.{ .o64 }, &.{ 0x99 }, 0, .long, .none },
.{ .clac, .z, &.{}, &.{ 0x0f, 0x01, 0xca }, 0, .none, .smap },
.{ .clc, .z, &.{}, &.{ 0xf8 }, 0, .none, .none },
.{ .cld, .z, &.{}, &.{ 0xfc }, 0, .none, .none },
.{ .clflush, .m, &.{ .m8 }, &.{ 0x0f, 0xae }, 7, .none, .none },
.{ .cmova, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .short, .none },
.{ .cmova, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none, .none },
.{ .cmova, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long, .none },
.{ .cmovae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .none },
.{ .cmovae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .none },
.{ .cmovae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .none },
.{ .cmovb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .none },
.{ .cmovb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .none },
.{ .cmovb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .none },
.{ .cmovbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .short, .none },
.{ .cmovbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none, .none },
.{ .cmovbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long, .none },
.{ .cmovc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .none },
.{ .cmovc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .none },
.{ .cmovc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .none },
.{ .cmove, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .short, .none },
.{ .cmove, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none, .none },
.{ .cmove, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long, .none },
.{ .cmovg, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .short, .none },
.{ .cmovg, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none, .none },
.{ .cmovg, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long, .none },
.{ .cmovge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .short, .none },
.{ .cmovge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none, .none },
.{ .cmovge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long, .none },
.{ .cmovl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .short, .none },
.{ .cmovl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none, .none },
.{ .cmovl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long, .none },
.{ .cmovle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .short, .none },
.{ .cmovle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none, .none },
.{ .cmovle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long, .none },
.{ .cmovna, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .short, .none },
.{ .cmovna, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none, .none },
.{ .cmovna, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long, .none },
.{ .cmovnae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .none },
.{ .cmovnae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .none },
.{ .cmovnae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .none },
.{ .cmovnb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .none },
.{ .cmovnb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .none },
.{ .cmovnb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .none },
.{ .cmovnbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .short, .none },
.{ .cmovnbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none, .none },
.{ .cmovnbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long, .none },
.{ .cmovnc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .none },
.{ .cmovnc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .none },
.{ .cmovnc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .none },
.{ .cmovne, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .short, .none },
.{ .cmovne, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none, .none },
.{ .cmovne, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long, .none },
.{ .cmovng, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .short, .none },
.{ .cmovng, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none, .none },
.{ .cmovng, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long, .none },
.{ .cmovnge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .short, .none },
.{ .cmovnge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none, .none },
.{ .cmovnge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long, .none },
.{ .cmovnl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .short, .none },
.{ .cmovnl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none, .none },
.{ .cmovnl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long, .none },
.{ .cmovnle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .short, .none },
.{ .cmovnle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none, .none },
.{ .cmovnle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long, .none },
.{ .cmovno, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x41 }, 0, .short, .none },
.{ .cmovno, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x41 }, 0, .none, .none },
.{ .cmovno, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x41 }, 0, .long, .none },
.{ .cmovnp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .short, .none },
.{ .cmovnp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none, .none },
.{ .cmovnp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long, .none },
.{ .cmovns, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x49 }, 0, .short, .none },
.{ .cmovns, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x49 }, 0, .none, .none },
.{ .cmovns, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x49 }, 0, .long, .none },
.{ .cmovnz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .short, .none },
.{ .cmovnz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none, .none },
.{ .cmovnz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long, .none },
.{ .cmovo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x40 }, 0, .short, .none },
.{ .cmovo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x40 }, 0, .none, .none },
.{ .cmovo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x40 }, 0, .long, .none },
.{ .cmovp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .short, .none },
.{ .cmovp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none, .none },
.{ .cmovp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long, .none },
.{ .cmovpe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .short, .none },
.{ .cmovpe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none, .none },
.{ .cmovpe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long, .none },
.{ .cmovpo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .short, .none },
.{ .cmovpo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none, .none },
.{ .cmovpo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long, .none },
.{ .cmovs, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x48 }, 0, .short, .none },
.{ .cmovs, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x48 }, 0, .none, .none },
.{ .cmovs, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x48 }, 0, .long, .none },
.{ .cmovz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .short, .none },
.{ .cmovz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none, .none },
.{ .cmovz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long, .none },
.{ .cli, .z, &.{}, &.{ 0xfa }, 0, .none, .none },
.{ .clts, .z, &.{}, &.{ 0x0f, 0x06 }, 0, .none, .none },
.{ .clui, .z, &.{}, &.{ 0xf3, 0x0f, 0x01, 0xee }, 0, .none, .uintr },
.{ .cmova, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .short, .cmov },
.{ .cmova, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none, .cmov },
.{ .cmova, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long, .cmov },
.{ .cmovae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .cmov },
.{ .cmovae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .cmov },
.{ .cmovae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .cmov },
.{ .cmovb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .cmov },
.{ .cmovb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .cmov },
.{ .cmovb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .cmov },
.{ .cmovbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .short, .cmov },
.{ .cmovbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none, .cmov },
.{ .cmovbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long, .cmov },
.{ .cmovc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .cmov },
.{ .cmovc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .cmov },
.{ .cmovc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .cmov },
.{ .cmove, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .short, .cmov },
.{ .cmove, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none, .cmov },
.{ .cmove, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long, .cmov },
.{ .cmovg, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .short, .cmov },
.{ .cmovg, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none, .cmov },
.{ .cmovg, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long, .cmov },
.{ .cmovge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .short, .cmov },
.{ .cmovge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none, .cmov },
.{ .cmovge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long, .cmov },
.{ .cmovl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .short, .cmov },
.{ .cmovl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none, .cmov },
.{ .cmovl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long, .cmov },
.{ .cmovle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .short, .cmov },
.{ .cmovle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none, .cmov },
.{ .cmovle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long, .cmov },
.{ .cmovna, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .short, .cmov },
.{ .cmovna, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none, .cmov },
.{ .cmovna, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long, .cmov },
.{ .cmovnae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .short, .cmov },
.{ .cmovnae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none, .cmov },
.{ .cmovnae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long, .cmov },
.{ .cmovnb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .cmov },
.{ .cmovnb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .cmov },
.{ .cmovnb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .cmov },
.{ .cmovnbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .short, .cmov },
.{ .cmovnbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none, .cmov },
.{ .cmovnbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long, .cmov },
.{ .cmovnc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .short, .cmov },
.{ .cmovnc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none, .cmov },
.{ .cmovnc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long, .cmov },
.{ .cmovne, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .short, .cmov },
.{ .cmovne, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none, .cmov },
.{ .cmovne, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long, .cmov },
.{ .cmovng, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .short, .cmov },
.{ .cmovng, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none, .cmov },
.{ .cmovng, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long, .cmov },
.{ .cmovnge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .short, .cmov },
.{ .cmovnge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none, .cmov },
.{ .cmovnge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long, .cmov },
.{ .cmovnl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .short, .cmov },
.{ .cmovnl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none, .cmov },
.{ .cmovnl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long, .cmov },
.{ .cmovnle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .short, .cmov },
.{ .cmovnle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none, .cmov },
.{ .cmovnle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long, .cmov },
.{ .cmovno, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x41 }, 0, .short, .cmov },
.{ .cmovno, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x41 }, 0, .none, .cmov },
.{ .cmovno, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x41 }, 0, .long, .cmov },
.{ .cmovnp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .short, .cmov },
.{ .cmovnp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none, .cmov },
.{ .cmovnp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long, .cmov },
.{ .cmovns, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x49 }, 0, .short, .cmov },
.{ .cmovns, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x49 }, 0, .none, .cmov },
.{ .cmovns, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x49 }, 0, .long, .cmov },
.{ .cmovnz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .short, .cmov },
.{ .cmovnz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none, .cmov },
.{ .cmovnz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long, .cmov },
.{ .cmovo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x40 }, 0, .short, .cmov },
.{ .cmovo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x40 }, 0, .none, .cmov },
.{ .cmovo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x40 }, 0, .long, .cmov },
.{ .cmovp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .short, .cmov },
.{ .cmovp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none, .cmov },
.{ .cmovp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long, .cmov },
.{ .cmovpe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .short, .cmov },
.{ .cmovpe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none, .cmov },
.{ .cmovpe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long, .cmov },
.{ .cmovpo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .short, .cmov },
.{ .cmovpo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none, .cmov },
.{ .cmovpo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long, .cmov },
.{ .cmovs, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x48 }, 0, .short, .cmov },
.{ .cmovs, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x48 }, 0, .none, .cmov },
.{ .cmovs, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x48 }, 0, .long, .cmov },
.{ .cmovz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .short, .cmov },
.{ .cmovz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none, .cmov },
.{ .cmovz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long, .cmov },
.{ .cmp, .zi, &.{ .al, .imm8 }, &.{ 0x3c }, 0, .none, .none },
.{ .cmp, .zi, &.{ .ax, .imm16 }, &.{ 0x3d }, 0, .short, .none },
@ -248,15 +260,15 @@ pub const table = [_]Entry{
.{ .cmp, .rm, &.{ .r32, .rm32 }, &.{ 0x3b }, 0, .none, .none },
.{ .cmp, .rm, &.{ .r64, .rm64 }, &.{ 0x3b }, 0, .long, .none },
.{ .cmps, .zo, &.{ .m8, .m8 }, &.{ 0xa6 }, 0, .none, .none },
.{ .cmps, .zo, &.{ .m16, .m16 }, &.{ 0xa7 }, 0, .short, .none },
.{ .cmps, .zo, &.{ .m32, .m32 }, &.{ 0xa7 }, 0, .none, .none },
.{ .cmps, .zo, &.{ .m64, .m64 }, &.{ 0xa7 }, 0, .long, .none },
.{ .cmps, .z, &.{ .m8, .m8 }, &.{ 0xa6 }, 0, .none, .none },
.{ .cmps, .z, &.{ .m16, .m16 }, &.{ 0xa7 }, 0, .short, .none },
.{ .cmps, .z, &.{ .m32, .m32 }, &.{ 0xa7 }, 0, .none, .none },
.{ .cmps, .z, &.{ .m64, .m64 }, &.{ 0xa7 }, 0, .long, .none },
.{ .cmpsb, .zo, &.{}, &.{ 0xa6 }, 0, .none, .none },
.{ .cmpsw, .zo, &.{}, &.{ 0xa7 }, 0, .short, .none },
.{ .cmpsd, .zo, &.{}, &.{ 0xa7 }, 0, .none, .none },
.{ .cmpsq, .zo, &.{}, &.{ 0xa7 }, 0, .long, .none },
.{ .cmpsb, .z, &.{}, &.{ 0xa6 }, 0, .none, .none },
.{ .cmpsw, .z, &.{}, &.{ 0xa7 }, 0, .short, .none },
.{ .cmpsd, .z, &.{}, &.{ 0xa7 }, 0, .none, .none },
.{ .cmpsq, .z, &.{}, &.{ 0xa7 }, 0, .long, .none },
.{ .cmpxchg, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xb0 }, 0, .none, .none },
.{ .cmpxchg, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xb0 }, 0, .rex, .none },
@ -267,7 +279,7 @@ pub const table = [_]Entry{
.{ .cmpxchg8b, .m, &.{ .m64 }, &.{ 0x0f, 0xc7 }, 1, .none, .none },
.{ .cmpxchg16b, .m, &.{ .m128 }, &.{ 0x0f, 0xc7 }, 1, .long, .none },
.{ .cpuid, .zo, &.{}, &.{ 0x0f, 0xa2 }, 0, .none, .none },
.{ .cpuid, .z, &.{}, &.{ 0x0f, 0xa2 }, 0, .none, .none },
.{ .dec, .m, &.{ .rm8 }, &.{ 0xfe }, 1, .none, .none },
.{ .dec, .m, &.{ .rm8 }, &.{ 0xfe }, 1, .rex, .none },
@ -308,7 +320,7 @@ pub const table = [_]Entry{
.{ .inc, .m, &.{ .rm32 }, &.{ 0xff }, 0, .none, .none },
.{ .inc, .m, &.{ .rm64 }, &.{ 0xff }, 0, .long, .none },
.{ .int3, .zo, &.{}, &.{ 0xcc }, 0, .none, .none },
.{ .int3, .z, &.{}, &.{ 0xcc }, 0, .none, .none },
.{ .ja, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none, .none },
.{ .jae, .d, &.{ .rel32 }, &.{ 0x0f, 0x83 }, 0, .none, .none },
@ -349,23 +361,23 @@ pub const table = [_]Entry{
.{ .lea, .rm, &.{ .r32, .m }, &.{ 0x8d }, 0, .none, .none },
.{ .lea, .rm, &.{ .r64, .m }, &.{ 0x8d }, 0, .long, .none },
.{ .lfence, .zo, &.{}, &.{ 0x0f, 0xae, 0xe8 }, 0, .none, .none },
.{ .lfence, .z, &.{}, &.{ 0x0f, 0xae, 0xe8 }, 0, .none, .none },
.{ .lods, .zo, &.{ .m8 }, &.{ 0xac }, 0, .none, .none },
.{ .lods, .zo, &.{ .m16 }, &.{ 0xad }, 0, .short, .none },
.{ .lods, .zo, &.{ .m32 }, &.{ 0xad }, 0, .none, .none },
.{ .lods, .zo, &.{ .m64 }, &.{ 0xad }, 0, .long, .none },
.{ .lods, .z, &.{ .m8 }, &.{ 0xac }, 0, .none, .none },
.{ .lods, .z, &.{ .m16 }, &.{ 0xad }, 0, .short, .none },
.{ .lods, .z, &.{ .m32 }, &.{ 0xad }, 0, .none, .none },
.{ .lods, .z, &.{ .m64 }, &.{ 0xad }, 0, .long, .none },
.{ .lodsb, .zo, &.{}, &.{ 0xac }, 0, .none, .none },
.{ .lodsw, .zo, &.{}, &.{ 0xad }, 0, .short, .none },
.{ .lodsd, .zo, &.{}, &.{ 0xad }, 0, .none, .none },
.{ .lodsq, .zo, &.{}, &.{ 0xad }, 0, .long, .none },
.{ .lodsb, .z, &.{}, &.{ 0xac }, 0, .none, .none },
.{ .lodsw, .z, &.{}, &.{ 0xad }, 0, .short, .none },
.{ .lodsd, .z, &.{}, &.{ 0xad }, 0, .none, .none },
.{ .lodsq, .z, &.{}, &.{ 0xad }, 0, .long, .none },
.{ .lzcnt, .rm, &.{ .r16, .rm16 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .short, .lzcnt },
.{ .lzcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .none, .lzcnt },
.{ .lzcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .long, .lzcnt },
.{ .mfence, .zo, &.{}, &.{ 0x0f, 0xae, 0xf0 }, 0, .none, .none },
.{ .mfence, .z, &.{}, &.{ 0x0f, 0xae, 0xf0 }, 0, .none, .none },
.{ .mov, .mr, &.{ .rm8, .r8 }, &.{ 0x88 }, 0, .none, .none },
.{ .mov, .mr, &.{ .rm8, .r8 }, &.{ 0x88 }, 0, .rex, .none },
@ -409,15 +421,15 @@ pub const table = [_]Entry{
.{ .movbe, .mr, &.{ .m32, .r32 }, &.{ 0x0f, 0x38, 0xf1 }, 0, .none, .movbe },
.{ .movbe, .mr, &.{ .m64, .r64 }, &.{ 0x0f, 0x38, 0xf1 }, 0, .long, .movbe },
.{ .movs, .zo, &.{ .m8, .m8 }, &.{ 0xa4 }, 0, .none, .none },
.{ .movs, .zo, &.{ .m16, .m16 }, &.{ 0xa5 }, 0, .short, .none },
.{ .movs, .zo, &.{ .m32, .m32 }, &.{ 0xa5 }, 0, .none, .none },
.{ .movs, .zo, &.{ .m64, .m64 }, &.{ 0xa5 }, 0, .long, .none },
.{ .movs, .z, &.{ .m8, .m8 }, &.{ 0xa4 }, 0, .none, .none },
.{ .movs, .z, &.{ .m16, .m16 }, &.{ 0xa5 }, 0, .short, .none },
.{ .movs, .z, &.{ .m32, .m32 }, &.{ 0xa5 }, 0, .none, .none },
.{ .movs, .z, &.{ .m64, .m64 }, &.{ 0xa5 }, 0, .long, .none },
.{ .movsb, .zo, &.{}, &.{ 0xa4 }, 0, .none, .none },
.{ .movsw, .zo, &.{}, &.{ 0xa5 }, 0, .short, .none },
.{ .movsd, .zo, &.{}, &.{ 0xa5 }, 0, .none, .none },
.{ .movsq, .zo, &.{}, &.{ 0xa5 }, 0, .long, .none },
.{ .movsb, .z, &.{}, &.{ 0xa4 }, 0, .none, .none },
.{ .movsw, .z, &.{}, &.{ 0xa5 }, 0, .short, .none },
.{ .movsd, .z, &.{}, &.{ 0xa5 }, 0, .none, .none },
.{ .movsq, .z, &.{}, &.{ 0xa5 }, 0, .long, .none },
.{ .movsx, .rm, &.{ .r16, .rm8 }, &.{ 0x0f, 0xbe }, 0, .short, .none },
.{ .movsx, .rm, &.{ .r16, .rm8 }, &.{ 0x0f, 0xbe }, 0, .rex_short, .none },
@ -453,7 +465,7 @@ pub const table = [_]Entry{
.{ .neg, .m, &.{ .rm32 }, &.{ 0xf7 }, 3, .none, .none },
.{ .neg, .m, &.{ .rm64 }, &.{ 0xf7 }, 3, .long, .none },
.{ .nop, .zo, &.{}, &.{ 0x90 }, 0, .none, .none },
.{ .nop, .z, &.{}, &.{ 0x90 }, 0, .none, .none },
.{ .not, .m, &.{ .rm8 }, &.{ 0xf6 }, 2, .none, .none },
.{ .not, .m, &.{ .rm8 }, &.{ 0xf6 }, 2, .rex, .none },
@ -484,7 +496,7 @@ pub const table = [_]Entry{
.{ .@"or", .rm, &.{ .r32, .rm32 }, &.{ 0x0b }, 0, .none, .none },
.{ .@"or", .rm, &.{ .r64, .rm64 }, &.{ 0x0b }, 0, .long, .none },
.{ .pause, .zo, &.{}, &.{ 0xf3, 0x90 }, 0, .none, .none },
.{ .pause, .z, &.{}, &.{ 0xf3, 0x90 }, 0, .none, .none },
.{ .pop, .o, &.{ .r16 }, &.{ 0x58 }, 0, .short, .none },
.{ .pop, .o, &.{ .r64 }, &.{ 0x58 }, 0, .none, .none },
@ -495,7 +507,7 @@ pub const table = [_]Entry{
.{ .popcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xb8 }, 0, .none, .popcnt },
.{ .popcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xb8 }, 0, .long, .popcnt },
.{ .popfq, .zo, &.{}, &.{ 0x9d }, 0, .none, .none },
.{ .popfq, .z, &.{}, &.{ 0x9d }, 0, .none, .none },
.{ .push, .o, &.{ .r16 }, &.{ 0x50 }, 0, .short, .none },
.{ .push, .o, &.{ .r64 }, &.{ 0x50 }, 0, .none, .none },
@ -505,9 +517,9 @@ pub const table = [_]Entry{
.{ .push, .i, &.{ .imm16 }, &.{ 0x68 }, 0, .short, .none },
.{ .push, .i, &.{ .imm32 }, &.{ 0x68 }, 0, .none, .none },
.{ .pushfq, .zo, &.{}, &.{ 0x9c }, 0, .none, .none },
.{ .pushfq, .z, &.{}, &.{ 0x9c }, 0, .none, .none },
.{ .ret, .zo, &.{}, &.{ 0xc3 }, 0, .none, .none },
.{ .ret, .z, &.{}, &.{ 0xc3 }, 0, .none, .none },
.{ .rcl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 2, .none, .none },
.{ .rcl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 2, .rex, .none },
@ -628,15 +640,15 @@ pub const table = [_]Entry{
.{ .sbb, .rm, &.{ .r32, .rm32 }, &.{ 0x1b }, 0, .none, .none },
.{ .sbb, .rm, &.{ .r64, .rm64 }, &.{ 0x1b }, 0, .long, .none },
.{ .scas, .zo, &.{ .m8 }, &.{ 0xae }, 0, .none, .none },
.{ .scas, .zo, &.{ .m16 }, &.{ 0xaf }, 0, .short, .none },
.{ .scas, .zo, &.{ .m32 }, &.{ 0xaf }, 0, .none, .none },
.{ .scas, .zo, &.{ .m64 }, &.{ 0xaf }, 0, .long, .none },
.{ .scas, .z, &.{ .m8 }, &.{ 0xae }, 0, .none, .none },
.{ .scas, .z, &.{ .m16 }, &.{ 0xaf }, 0, .short, .none },
.{ .scas, .z, &.{ .m32 }, &.{ 0xaf }, 0, .none, .none },
.{ .scas, .z, &.{ .m64 }, &.{ 0xaf }, 0, .long, .none },
.{ .scasb, .zo, &.{}, &.{ 0xae }, 0, .none, .none },
.{ .scasw, .zo, &.{}, &.{ 0xaf }, 0, .short, .none },
.{ .scasd, .zo, &.{}, &.{ 0xaf }, 0, .none, .none },
.{ .scasq, .zo, &.{}, &.{ 0xaf }, 0, .long, .none },
.{ .scasb, .z, &.{}, &.{ 0xae }, 0, .none, .none },
.{ .scasw, .z, &.{}, &.{ 0xaf }, 0, .short, .none },
.{ .scasd, .z, &.{}, &.{ 0xaf }, 0, .none, .none },
.{ .scasq, .z, &.{}, &.{ 0xaf }, 0, .long, .none },
.{ .seta, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .none, .none },
.{ .seta, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .rex, .none },
@ -699,7 +711,7 @@ pub const table = [_]Entry{
.{ .setz, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .none, .none },
.{ .setz, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .rex, .none },
.{ .sfence, .zo, &.{}, &.{ 0x0f, 0xae, 0xf8 }, 0, .none, .none },
.{ .sfence, .z, &.{}, &.{ 0x0f, 0xae, 0xf8 }, 0, .none, .none },
.{ .shl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .none, .none },
.{ .shl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .rex, .none },
@ -747,15 +759,25 @@ pub const table = [_]Entry{
.{ .shrd, .mrc, &.{ .rm32, .r32, .cl }, &.{ 0x0f, 0xad }, 0, .none, .none },
.{ .shrd, .mrc, &.{ .rm64, .r64, .cl }, &.{ 0x0f, 0xad }, 0, .long, .none },
.{ .stos, .zo, &.{ .m8 }, &.{ 0xaa }, 0, .none, .none },
.{ .stos, .zo, &.{ .m16 }, &.{ 0xab }, 0, .short, .none },
.{ .stos, .zo, &.{ .m32 }, &.{ 0xab }, 0, .none, .none },
.{ .stos, .zo, &.{ .m64 }, &.{ 0xab }, 0, .long, .none },
.{ .stac, .z, &.{}, &.{ 0x0f, 0x01, 0xcb }, 0, .none, .smap },
.{ .stosb, .zo, &.{}, &.{ 0xaa }, 0, .none, .none },
.{ .stosw, .zo, &.{}, &.{ 0xab }, 0, .short, .none },
.{ .stosd, .zo, &.{}, &.{ 0xab }, 0, .none, .none },
.{ .stosq, .zo, &.{}, &.{ 0xab }, 0, .long, .none },
.{ .stc, .z, &.{}, &.{ 0xf9 }, 0, .none, .none },
.{ .std, .z, &.{}, &.{ 0xfd }, 0, .none, .none },
.{ .sti, .z, &.{}, &.{ 0xfb }, 0, .none, .none },
.{ .stui, .z, &.{}, &.{ 0xf3, 0x0f, 0x01, 0xef }, 0, .none, .uintr },
.{ .stos, .z, &.{ .m8 }, &.{ 0xaa }, 0, .none, .none },
.{ .stos, .z, &.{ .m16 }, &.{ 0xab }, 0, .short, .none },
.{ .stos, .z, &.{ .m32 }, &.{ 0xab }, 0, .none, .none },
.{ .stos, .z, &.{ .m64 }, &.{ 0xab }, 0, .long, .none },
.{ .stosb, .z, &.{}, &.{ 0xaa }, 0, .none, .none },
.{ .stosw, .z, &.{}, &.{ 0xab }, 0, .short, .none },
.{ .stosd, .z, &.{}, &.{ 0xab }, 0, .none, .none },
.{ .stosq, .z, &.{}, &.{ 0xab }, 0, .long, .none },
.{ .sub, .zi, &.{ .al, .imm8 }, &.{ 0x2c }, 0, .none, .none },
.{ .sub, .zi, &.{ .ax, .imm16 }, &.{ 0x2d }, 0, .short, .none },
@ -780,7 +802,7 @@ pub const table = [_]Entry{
.{ .sub, .rm, &.{ .r32, .rm32 }, &.{ 0x2b }, 0, .none, .none },
.{ .sub, .rm, &.{ .r64, .rm64 }, &.{ 0x2b }, 0, .long, .none },
.{ .syscall, .zo, &.{}, &.{ 0x0f, 0x05 }, 0, .none, .none },
.{ .syscall, .z, &.{}, &.{ 0x0f, 0x05 }, 0, .none, .none },
.{ .@"test", .zi, &.{ .al, .imm8 }, &.{ 0xa8 }, 0, .none, .none },
.{ .@"test", .zi, &.{ .ax, .imm16 }, &.{ 0xa9 }, 0, .short, .none },
@ -801,7 +823,7 @@ pub const table = [_]Entry{
.{ .tzcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xbc }, 0, .none, .bmi },
.{ .tzcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xbc }, 0, .long, .bmi },
.{ .ud2, .zo, &.{}, &.{ 0x0f, 0x0b }, 0, .none, .none },
.{ .ud2, .z, &.{}, &.{ 0x0f, 0x0b }, 0, .none, .none },
.{ .xadd, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xc0 }, 0, .none, .none },
.{ .xadd, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xc0 }, 0, .rex, .none },
@ -809,12 +831,12 @@ pub const table = [_]Entry{
.{ .xadd, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xc1 }, 0, .none, .none },
.{ .xadd, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xc1 }, 0, .long, .none },
.{ .xchg, .o, &.{ .ax, .r16 }, &.{ 0x90 }, 0, .short, .none },
.{ .xchg, .o, &.{ .r16, .ax }, &.{ 0x90 }, 0, .short, .none },
.{ .xchg, .o, &.{ .eax, .r32 }, &.{ 0x90 }, 0, .none, .none },
.{ .xchg, .o, &.{ .rax, .r64 }, &.{ 0x90 }, 0, .long, .none },
.{ .xchg, .o, &.{ .r32, .eax }, &.{ 0x90 }, 0, .none, .none },
.{ .xchg, .o, &.{ .r64, .rax }, &.{ 0x90 }, 0, .long, .none },
.{ .xchg, .zo, &.{ .ax, .r16 }, &.{ 0x90 }, 0, .short, .none },
.{ .xchg, .oz, &.{ .r16, .ax }, &.{ 0x90 }, 0, .short, .none },
.{ .xchg, .zo, &.{ .eax, .r32 }, &.{ 0x90 }, 0, .none, .none },
.{ .xchg, .zo, &.{ .rax, .r64 }, &.{ 0x90 }, 0, .long, .none },
.{ .xchg, .oz, &.{ .r32, .eax }, &.{ 0x90 }, 0, .none, .none },
.{ .xchg, .oz, &.{ .r64, .rax }, &.{ 0x90 }, 0, .long, .none },
.{ .xchg, .mr, &.{ .rm8, .r8 }, &.{ 0x86 }, 0, .none, .none },
.{ .xchg, .mr, &.{ .rm8, .r8 }, &.{ 0x86 }, 0, .rex, .none },
.{ .xchg, .rm, &.{ .r8, .rm8 }, &.{ 0x86 }, 0, .none, .none },
@ -826,7 +848,7 @@ pub const table = [_]Entry{
.{ .xchg, .rm, &.{ .r32, .rm32 }, &.{ 0x87 }, 0, .none, .none },
.{ .xchg, .rm, &.{ .r64, .rm64 }, &.{ 0x87 }, 0, .long, .none },
.{ .xgetbv, .zo, &.{}, &.{ 0x0f, 0x01, 0xd0 }, 0, .none, .none },
.{ .xgetbv, .z, &.{}, &.{ 0x0f, 0x01, 0xd0 }, 0, .none, .none },
.{ .xor, .zi, &.{ .al, .imm8 }, &.{ 0x34 }, 0, .none, .none },
.{ .xor, .zi, &.{ .ax, .imm16 }, &.{ 0x35 }, 0, .short, .none },
@ -852,9 +874,9 @@ pub const table = [_]Entry{
.{ .xor, .rm, &.{ .r64, .rm64 }, &.{ 0x33 }, 0, .long, .none },
// X87
.{ .fabs, .zo, &.{}, &.{ 0xd9, 0xe1 }, 0, .none, .x87 },
.{ .fabs, .z, &.{}, &.{ 0xd9, 0xe1 }, 0, .none, .x87 },
.{ .fchs, .zo, &.{}, &.{ 0xd9, 0xe0 }, 0, .none, .x87 },
.{ .fchs, .z, &.{}, &.{ 0xd9, 0xe0 }, 0, .none, .x87 },
.{ .ffree, .o, &.{ .st }, &.{ 0xdd, 0xc0 }, 0, .none, .x87 },
@ -927,8 +949,14 @@ pub const table = [_]Entry{
.{ .movhlps, .rm, &.{ .xmm, .xmm }, &.{ 0x0f, 0x12 }, 0, .none, .sse },
.{ .movhps, .rm, &.{ .xmm, .m64 }, &.{ 0x0f, 0x16 }, 0, .none, .sse },
.{ .movhps, .mr, &.{ .m64, .xmm }, &.{ 0x0f, 0x17 }, 0, .none, .sse },
.{ .movlhps, .rm, &.{ .xmm, .xmm }, &.{ 0x0f, 0x16 }, 0, .none, .sse },
.{ .movlps, .rm, &.{ .xmm, .m64 }, &.{ 0x0f, 0x12 }, 0, .none, .sse },
.{ .movlps, .mr, &.{ .m64, .xmm }, &.{ 0x0f, 0x13 }, 0, .none, .sse },
.{ .movmskps, .rm, &.{ .r32, .xmm }, &.{ 0x0f, 0x50 }, 0, .none, .sse },
.{ .movmskps, .rm, &.{ .r64, .xmm }, &.{ 0x0f, 0x50 }, 0, .none, .sse },
@ -1037,6 +1065,12 @@ pub const table = [_]Entry{
.{ .movdqu, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0xf3, 0x0f, 0x6f }, 0, .none, .sse2 },
.{ .movdqu, .mr, &.{ .xmm_m128, .xmm }, &.{ 0xf3, 0x0f, 0x7f }, 0, .none, .sse2 },
.{ .movhpd, .rm, &.{ .xmm, .m64 }, &.{ 0x66, 0x0f, 0x16 }, 0, .none, .sse2 },
.{ .movhpd, .mr, &.{ .m64, .xmm }, &.{ 0x66, 0x0f, 0x17 }, 0, .none, .sse2 },
.{ .movlpd, .rm, &.{ .xmm, .m64 }, &.{ 0x66, 0x0f, 0x12 }, 0, .none, .sse2 },
.{ .movlpd, .mr, &.{ .m64, .xmm }, &.{ 0x66, 0x0f, 0x13 }, 0, .none, .sse2 },
.{ .movmskpd, .rm, &.{ .r32, .xmm }, &.{ 0x66, 0x0f, 0x50 }, 0, .none, .sse2 },
.{ .movmskpd, .rm, &.{ .r64, .xmm }, &.{ 0x66, 0x0f, 0x50 }, 0, .none, .sse2 },
@ -1251,6 +1285,8 @@ pub const table = [_]Entry{
.{ .pmulld, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x40 }, 0, .none, .sse4_1 },
.{ .ptest, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x17 }, 0, .none, .sse4_1 },
.{ .roundpd, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x09 }, 0, .none, .sse4_1 },
.{ .roundps, .rmi, &.{ .xmm, .xmm_m128, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x08 }, 0, .none, .sse4_1 },
@ -1287,6 +1323,16 @@ pub const table = [_]Entry{
.{ .sha256rnds2, .rm0, &.{ .xmm, .xmm_m128, .xmm0 }, &.{ 0x0f, 0x38, 0xcb }, 0, .none, .sha },
// AVX
.{ .rorx, .rmi, &.{ .r32, .rm32, .imm8 }, &.{ 0xf2, 0x0f, 0x3a }, 0, .vex_lz_w0, .bmi2 },
.{ .rorx, .rmi, &.{ .r64, .rm64, .imm8 }, &.{ 0xf2, 0x0f, 0x3a }, 0, .vex_lz_w1, .bmi2 },
.{ .sarx, .rmv, &.{ .r32, .rm32, .r32 }, &.{ 0xf3, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w0, .bmi2 },
.{ .shlx, .rmv, &.{ .r32, .rm32, .r32 }, &.{ 0x66, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w0, .bmi2 },
.{ .shrx, .rmv, &.{ .r32, .rm32, .r32 }, &.{ 0xf2, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w0, .bmi2 },
.{ .sarx, .rmv, &.{ .r64, .rm64, .r64 }, &.{ 0xf3, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w1, .bmi2 },
.{ .shlx, .rmv, &.{ .r64, .rm64, .r64 }, &.{ 0x66, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w1, .bmi2 },
.{ .shrx, .rmv, &.{ .r64, .rm64, .r64 }, &.{ 0xf2, 0x0f, 0x38, 0xf7 }, 0, .vex_lz_w1, .bmi2 },
.{ .vaddpd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x58 }, 0, .vex_128_wig, .avx },
.{ .vaddpd, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x58 }, 0, .vex_256_wig, .avx },
@ -1474,8 +1520,20 @@ pub const table = [_]Entry{
.{ .vmovhlps, .rvm, &.{ .xmm, .xmm, .xmm }, &.{ 0x0f, 0x12 }, 0, .vex_128_wig, .avx },
.{ .vmovhpd, .rvm, &.{ .xmm, .xmm, .m64 }, &.{ 0x66, 0x0f, 0x16 }, 0, .vex_128_wig, .avx },
.{ .vmovhpd, .mr, &.{ .m64, .xmm }, &.{ 0x66, 0x0f, 0x17 }, 0, .vex_128_wig, .avx },
.{ .vmovhps, .rvm, &.{ .xmm, .xmm, .m64 }, &.{ 0x0f, 0x16 }, 0, .vex_128_wig, .avx },
.{ .vmovhps, .mr, &.{ .m64, .xmm }, &.{ 0x0f, 0x17 }, 0, .vex_128_wig, .avx },
.{ .vmovlhps, .rvm, &.{ .xmm, .xmm, .xmm }, &.{ 0x0f, 0x16 }, 0, .vex_128_wig, .avx },
.{ .vmovlpd, .rvm, &.{ .xmm, .xmm, .m64 }, &.{ 0x66, 0x0f, 0x12 }, 0, .vex_128_wig, .avx },
.{ .vmovlpd, .mr, &.{ .m64, .xmm }, &.{ 0x66, 0x0f, 0x13 }, 0, .vex_128_wig, .avx },
.{ .vmovlps, .rvm, &.{ .xmm, .xmm, .m64 }, &.{ 0x0f, 0x12 }, 0, .vex_128_wig, .avx },
.{ .vmovlps, .mr, &.{ .m64, .xmm }, &.{ 0x0f, 0x13 }, 0, .vex_128_wig, .avx },
.{ .vmovq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf3, 0x0f, 0x7e }, 0, .vex_128_wig, .avx },
.{ .vmovq, .mr, &.{ .xmm_m64, .xmm }, &.{ 0x66, 0x0f, 0xd6 }, 0, .vex_128_wig, .avx },
@ -1571,14 +1629,14 @@ pub const table = [_]Entry{
.{ .vpextrd, .mri, &.{ .rm32, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x16 }, 0, .vex_128_w0, .avx },
.{ .vpextrq, .mri, &.{ .rm64, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x16 }, 0, .vex_128_w1, .avx },
.{ .vpextrw, .rmi, &.{ .r32, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x15 }, 0, .vex_128_wig, .avx },
.{ .vpextrw, .mri, &.{ .r32_m16, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x15 }, 0, .vex_128_wig, .avx },
.{ .vpextrw, .rmi, &.{ .r32, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0xc5 }, 0, .vex_128_w0, .avx },
.{ .vpextrw, .mri, &.{ .r32_m16, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x15 }, 0, .vex_128_w0, .avx },
.{ .vpinsrb, .rmi, &.{ .xmm, .r32_m8, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x20 }, 0, .vex_128_w0, .avx },
.{ .vpinsrd, .rmi, &.{ .xmm, .rm32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x22 }, 0, .vex_128_w0, .avx },
.{ .vpinsrq, .rmi, &.{ .xmm, .rm64, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x22 }, 0, .vex_128_w1, .avx },
.{ .vpinsrb, .rvmi, &.{ .xmm, .xmm, .r32_m8, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x20 }, 0, .vex_128_w0, .avx },
.{ .vpinsrd, .rvmi, &.{ .xmm, .xmm, .rm32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x22 }, 0, .vex_128_w0, .avx },
.{ .vpinsrq, .rvmi, &.{ .xmm, .xmm, .rm64, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x22 }, 0, .vex_128_w1, .avx },
.{ .vpinsrw, .rvmi, &.{ .xmm, .xmm, .r32_m16, .imm8 }, &.{ 0x66, 0x0f, 0xc4 }, 0, .vex_128_wig, .avx },
.{ .vpinsrw, .rvmi, &.{ .xmm, .xmm, .r32_m16, .imm8 }, &.{ 0x66, 0x0f, 0xc4 }, 0, .vex_128_w0, .avx },
.{ .vpmaxsb, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x3c }, 0, .vex_128_wig, .avx },
.{ .vpmaxsw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xee }, 0, .vex_128_wig, .avx },
@ -1666,6 +1724,9 @@ pub const table = [_]Entry{
.{ .vpsubusb, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xd8 }, 0, .vex_128_wig, .avx },
.{ .vpsubusw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0xd9 }, 0, .vex_128_wig, .avx },
.{ .vptest, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x17 }, 0, .vex_128_wig, .avx },
.{ .vptest, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x17 }, 0, .vex_256_wig, .avx },
.{ .vpunpckhbw, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x68 }, 0, .vex_128_wig, .avx },
.{ .vpunpckhwd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x69 }, 0, .vex_128_wig, .avx },
.{ .vpunpckhdq, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x6a }, 0, .vex_128_wig, .avx },
@ -1716,6 +1777,11 @@ pub const table = [_]Entry{
.{ .vsubss, .rvm, &.{ .xmm, .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5c }, 0, .vex_lig_wig, .avx },
.{ .vtestps, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x0e }, 0, .vex_128_w0, .avx },
.{ .vtestps, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x0e }, 0, .vex_256_w0, .avx },
.{ .vtestpd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x0f }, 0, .vex_128_w0, .avx },
.{ .vtestpd, .rm, &.{ .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x0f }, 0, .vex_256_w0, .avx },
.{ .vxorpd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x57 }, 0, .vex_128_wig, .avx },
.{ .vxorpd, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x57 }, 0, .vex_256_wig, .avx },

View File

@ -1312,10 +1312,10 @@ pub const Pool = struct {
},
else => {
const target = &mod.resolved_target.result;
const abi_align = Type.intAbiAlignment(int_info.bits, target.*, false);
const abi_align = Type.intAbiAlignment(int_info.bits, target.*);
const abi_align_bytes = abi_align.toByteUnits().?;
const array_ctype = try pool.getArray(allocator, .{
.len = @divExact(Type.intAbiSize(int_info.bits, target.*, false), abi_align_bytes),
.len = @divExact(Type.intAbiSize(int_info.bits, target.*), abi_align_bytes),
.elem_ctype = try pool.fromIntInfo(allocator, .{
.signedness = .unsigned,
.bits = @intCast(abi_align_bytes * 8),
@ -1429,7 +1429,7 @@ pub const Pool = struct {
.name = .{ .index = .len },
.ctype = CType.usize,
.alignas = AlignAs.fromAbiAlignment(
Type.intAbiAlignment(target.ptrBitWidth(), target.*, false),
Type.intAbiAlignment(target.ptrBitWidth(), target.*),
),
},
};
@ -1524,7 +1524,7 @@ pub const Pool = struct {
.name = .{ .index = .len },
.ctype = CType.usize,
.alignas = AlignAs.fromAbiAlignment(
Type.intAbiAlignment(target.ptrBitWidth(), target.*, false),
Type.intAbiAlignment(target.ptrBitWidth(), target.*),
),
},
};
@ -1644,7 +1644,7 @@ pub const Pool = struct {
.name = .{ .index = .@"error" },
.ctype = error_set_ctype,
.alignas = AlignAs.fromAbiAlignment(
Type.intAbiAlignment(error_set_bits, target.*, false),
Type.intAbiAlignment(error_set_bits, target.*),
),
},
.{

View File

@ -581,7 +581,7 @@ const DataLayoutBuilder = struct {
switch (kind) {
.integer => {
if (self.target.ptrBitWidth() <= 16 and size >= 128) return;
abi = @min(abi, Type.maxIntAlignment(self.target, true) * 8);
abi = @min(abi, Type.maxIntAlignment(self.target) * 8);
switch (self.target.cpu.arch) {
.aarch64,
.aarch64_be,

View File

@ -135,6 +135,7 @@ pub const Env = enum {
else => Env.ast_gen.supports(feature),
},
.@"x86_64-linux" => switch (feature) {
.build_command,
.stdio_listen,
.incremental,
.x86_64_backend,

View File

@ -396,7 +396,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
else => {},
}
try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{
Type.maxIntAlignment(target, false),
Type.maxIntAlignment(target),
});
return defines;
}

View File

@ -976,6 +976,7 @@ const x86_64 = struct {
it: *RelocsIterator,
) !void {
dev.check(.x86_64_backend);
const t = &elf_file.base.comp.root_mod.resolved_target.result;
const is_static = elf_file.base.isStatic();
const is_dyn_lib = elf_file.isEffectivelyDynLib();
@ -1046,7 +1047,7 @@ const x86_64 = struct {
.GOTTPOFF => {
const should_relax = blk: {
if (is_dyn_lib or symbol.flags.import) break :blk false;
if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..], t)) break :blk false;
break :blk true;
};
if (!should_relax) {
@ -1090,6 +1091,7 @@ const x86_64 = struct {
stream: anytype,
) (error{ InvalidInstruction, CannotEncode } || RelocError)!void {
dev.check(.x86_64_backend);
const t = &elf_file.base.comp.root_mod.resolved_target.result;
const diags = &elf_file.base.comp.link_diags;
const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
@ -1120,7 +1122,7 @@ const x86_64 = struct {
.GOTPCRELX => {
if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk;
x86_64.relaxGotpcrelx(code[r_offset - 2 ..], t) catch break :blk;
try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
return;
}
@ -1129,7 +1131,7 @@ const x86_64 = struct {
.REX_GOTPCRELX => {
if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk;
x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..], t) catch break :blk;
try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
return;
}
@ -1184,7 +1186,7 @@ const x86_64 = struct {
const S_ = target.tlsDescAddress(elf_file);
try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
} else {
x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..], t) catch {
var err = try diags.addErrorWithNotes(1);
try err.addMsg("could not relax {s}", .{@tagName(r_type)});
err.addNote("in {}:{s} at offset 0x{x}", .{
@ -1208,7 +1210,7 @@ const x86_64 = struct {
const S_ = target.gotTpAddress(elf_file);
try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
} else {
x86_64.relaxGotTpOff(code[r_offset - 3 ..]);
x86_64.relaxGotTpOff(code[r_offset - 3 ..], t);
try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
}
},
@ -1269,31 +1271,31 @@ const x86_64 = struct {
}
}
fn relaxGotpcrelx(code: []u8) !void {
fn relaxGotpcrelx(code: []u8, t: *const std.Target) !void {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse return error.RelaxFailure;
const inst = switch (old_inst.encoding.mnemonic) {
.call => try Instruction.new(old_inst.prefix, .call, &.{
const inst: Instruction = switch (old_inst.encoding.mnemonic) {
.call => try .new(old_inst.prefix, .call, &.{
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
}),
.jmp => try Instruction.new(old_inst.prefix, .jmp, &.{
.{ .imm = .s(-129) },
}, t),
.jmp => try .new(old_inst.prefix, .jmp, &.{
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
}),
.{ .imm = .s(-129) },
}, t),
else => return error.RelaxFailure,
};
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
const nop = try Instruction.new(.none, .nop, &.{});
const nop: Instruction = try .new(.none, .nop, &.{}, t);
try encode(&.{ nop, inst }, code);
}
fn relaxRexGotpcrelx(code: []u8) !void {
fn relaxRexGotpcrelx(code: []u8, t: *const std.Target) !void {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse return error.RelaxFailure;
switch (old_inst.encoding.mnemonic) {
.mov => {
const inst = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops);
const inst: Instruction = try .new(old_inst.prefix, .lea, &old_inst.ops, t);
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
try encode(&.{inst}, code);
},
@ -1398,23 +1400,24 @@ const x86_64 = struct {
}
}
fn canRelaxGotTpOff(code: []const u8) bool {
fn canRelaxGotTpOff(code: []const u8, t: *const std.Target) bool {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse return false;
switch (old_inst.encoding.mnemonic) {
.mov => if (Instruction.new(old_inst.prefix, .mov, &.{
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
})) |inst| {
.mov => {
const inst = Instruction.new(old_inst.prefix, .mov, &.{
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = .s(-129) },
}, t) catch return false;
inst.encode(std.io.null_writer, .{}) catch return false;
return true;
} else |_| return false,
},
else => return false,
}
}
fn relaxGotTpOff(code: []u8) void {
fn relaxGotTpOff(code: []u8, t: *const std.Target) void {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse unreachable;
switch (old_inst.encoding.mnemonic) {
@ -1422,8 +1425,8 @@ const x86_64 = struct {
const inst = Instruction.new(old_inst.prefix, .mov, &.{
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
}) catch unreachable;
.{ .imm = .s(-129) },
}, t) catch unreachable;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch unreachable;
},
@ -1431,16 +1434,16 @@ const x86_64 = struct {
}
}
fn relaxGotPcTlsDesc(code: []u8) !void {
fn relaxGotPcTlsDesc(code: []u8, target: *const std.Target) !void {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse return error.RelaxFailure;
switch (old_inst.encoding.mnemonic) {
.lea => {
const inst = try Instruction.new(old_inst.prefix, .mov, &.{
const inst: Instruction = try .new(old_inst.prefix, .mov, &.{
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
});
.{ .imm = .s(-129) },
}, target);
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
try encode(&.{inst}, code);
},
@ -1779,7 +1782,7 @@ const aarch64 = struct {
const off: u12 = @truncate(@as(u64, @bitCast(S_ + A)));
aarch64_util.writeAddImmInst(off, code);
} else {
const old_inst = Instruction{
const old_inst: Instruction = .{
.add_subtract_immediate = mem.bytesToValue(std.meta.TagPayload(
Instruction,
Instruction.add_subtract_immediate,
@ -1793,7 +1796,7 @@ const aarch64 = struct {
},
.TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
const old_inst = Instruction{
const old_inst: Instruction = .{
.unconditional_branch_register = mem.bytesToValue(std.meta.TagPayload(
Instruction,
Instruction.unconditional_branch_register,

View File

@ -3548,7 +3548,7 @@ pub fn getTarget(self: MachO) std.Target {
pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void {
const tracy = trace(@src());
defer tracy.end();
if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
if (builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
try dir.copyFile(sub_path, dir, sub_path, .{});
}
}

View File

@ -640,7 +640,8 @@ fn resolveRelocInner(
macho_file: *MachO,
writer: anytype,
) ResolveError!void {
const cpu_arch = macho_file.getTarget().cpu.arch;
const t = &macho_file.base.comp.root_mod.resolved_target.result;
const cpu_arch = t.cpu.arch;
const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
const A = rel.addend + rel.getRelocAddend(cpu_arch);
@ -747,7 +748,7 @@ fn resolveRelocInner(
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
try writer.writeInt(i32, @intCast(S_ + A - P), .little);
} else {
try x86_64.relaxTlv(code[rel_offset - 3 ..]);
try x86_64.relaxTlv(code[rel_offset - 3 ..], t);
try writer.writeInt(i32, @intCast(S + A - P), .little);
}
},
@ -893,11 +894,12 @@ fn resolveRelocInner(
const x86_64 = struct {
fn relaxGotLoad(self: Atom, code: []u8, rel: Relocation, macho_file: *MachO) ResolveError!void {
dev.check(.x86_64_backend);
const t = &macho_file.base.comp.root_mod.resolved_target.result;
const diags = &macho_file.base.comp.link_diags;
const old_inst = disassemble(code) orelse return error.RelaxFail;
switch (old_inst.encoding.mnemonic) {
.mov => {
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch return error.RelaxFail;
},
@ -916,12 +918,12 @@ const x86_64 = struct {
}
}
fn relaxTlv(code: []u8) error{RelaxFail}!void {
fn relaxTlv(code: []u8, t: *const std.Target) error{RelaxFail}!void {
dev.check(.x86_64_backend);
const old_inst = disassemble(code) orelse return error.RelaxFail;
switch (old_inst.encoding.mnemonic) {
.mov => {
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;
const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t) catch return error.RelaxFail;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch return error.RelaxFail;
},

View File

@ -39,6 +39,11 @@ test {
_ = Package;
}
const thread_stack_size = switch (builtin.zig_backend) {
else => std.Thread.SpawnConfig.default_stack_size,
.stage2_x86_64 => 32 << 20,
};
pub const std_options: std.Options = .{
.wasiCwd = wasi_cwd,
.logFn = log,
@ -3320,6 +3325,7 @@ fn buildOutputType(
.allocator = gpa,
.n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();
@ -5024,6 +5030,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
.allocator = gpa,
.n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();
@ -5460,6 +5467,7 @@ fn jitCmd(
.allocator = gpa,
.n_jobs = @min(@max(std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
.track_ids = true,
.stack_size = thread_stack_size,
});
defer thread_pool.deinit();

View File

@ -96,8 +96,8 @@ const Writer = struct {
fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)];
try s.writeByteNTimes(' ', w.indent);
try s.print("%{d}{c}= {s}(", .{
@intFromEnum(inst),
try s.print("{}{c}= {s}(", .{
inst,
@as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
@tagName(tag),
});
@ -409,7 +409,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_block.deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -728,7 +728,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -739,7 +739,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -765,7 +765,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -776,7 +776,7 @@ const Writer = struct {
try s.writeAll("}");
for (liveness_condbr.then_deaths) |operand| {
try s.print(" %{d}!", .{@intFromEnum(operand)});
try s.print(" {}!", .{operand});
}
}
@ -807,7 +807,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.then_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -827,7 +827,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -884,7 +884,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -910,7 +910,7 @@ const Writer = struct {
try s.writeByteNTimes(' ', w.indent);
for (deaths, 0..) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{@intFromEnum(operand)});
try s.print("{}!", .{operand});
}
try s.writeAll("\n");
}
@ -994,7 +994,7 @@ const Writer = struct {
dies: bool,
) @TypeOf(s).Error!void {
_ = w;
try s.print("%{d}", .{@intFromEnum(inst)});
try s.print("{}", .{inst});
if (dies) try s.writeByte('!');
}

View File

@ -41,12 +41,12 @@ pub fn RegisterManager(
registers: TrackedRegisters = undefined,
/// Tracks which registers are free (in which case the
/// corresponding bit is set to 1)
free_registers: RegisterBitSet = RegisterBitSet.initFull(),
free_registers: RegisterBitSet = .initFull(),
/// Tracks all registers allocated in the course of this
/// function
allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
allocated_registers: RegisterBitSet = .initEmpty(),
/// Tracks registers which are locked from being allocated
locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
locked_registers: RegisterBitSet = .initEmpty(),
const Self = @This();
@ -58,11 +58,6 @@ pub fn RegisterManager(
return @alignCast(@fieldParentPtr("register_manager", self));
}
fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool {
const index = indexOfRegIntoTracked(reg) orelse return true;
return !register_class.isSet(index);
}
fn markRegIndexAllocated(self: *Self, tracked_index: TrackedIndex) void {
self.allocated_registers.set(tracked_index);
}
@ -99,8 +94,7 @@ pub fn RegisterManager(
max_id = @max(elem_id, max_id);
}
const OptionalIndex = std.math.IntFittingRange(0, set.len);
comptime var map = [1]OptionalIndex{set.len} ** (max_id - min_id + 1);
comptime var map: [max_id - min_id + 1]std.math.IntFittingRange(0, set.len) = @splat(set.len);
inline for (set, 0..) |elem, elem_index| map[comptime elem.id() - min_id] = elem_index;
const id_index = reg.id() -% min_id;
@ -112,6 +106,9 @@ pub fn RegisterManager(
pub fn indexOfRegIntoTracked(reg: Register) ?TrackedIndex {
return indexOfReg(tracked_registers, reg);
}
pub inline fn indexOfKnownRegIntoTracked(comptime reg: Register) ?TrackedIndex {
return comptime indexOfRegIntoTracked(reg);
}
pub fn regAtTrackedIndex(tracked_index: TrackedIndex) Register {
return tracked_registers[tracked_index];
@ -124,6 +121,9 @@ pub fn RegisterManager(
pub fn isRegFree(self: Self, reg: Register) bool {
return self.isRegIndexFree(indexOfRegIntoTracked(reg) orelse return true);
}
pub fn isKnownRegFree(self: Self, comptime reg: Register) bool {
return self.isRegIndexFree(indexOfKnownRegIntoTracked(reg) orelse return true);
}
/// Returns whether this register was allocated in the course
/// of this function.
@ -143,6 +143,9 @@ pub fn RegisterManager(
pub fn isRegLocked(self: Self, reg: Register) bool {
return self.isRegIndexLocked(indexOfRegIntoTracked(reg) orelse return false);
}
pub fn isKnownRegLocked(self: Self, comptime reg: Register) bool {
return self.isRegIndexLocked(indexOfKnownRegIntoTracked(reg) orelse return false);
}
pub const RegisterLock = struct { tracked_index: TrackedIndex };
@ -176,6 +179,9 @@ pub fn RegisterManager(
pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock {
return self.lockRegIndexAssumeUnused(indexOfRegIntoTracked(reg) orelse unreachable);
}
pub fn lockKnownRegAssumeUnused(self: *Self, comptime reg: Register) RegisterLock {
return self.lockRegIndexAssumeUnused(indexOfKnownRegIntoTracked(reg) orelse unreachable);
}
/// Like `lockReg` but locks multiple registers.
pub fn lockRegs(
@ -223,28 +229,20 @@ pub fn RegisterManager(
) ?[count]Register {
comptime assert(count > 0 and count <= tracked_registers.len);
var free_and_not_locked_registers = self.free_registers;
free_and_not_locked_registers.setIntersection(register_class);
var unlocked_registers = self.locked_registers;
unlocked_registers.toggleAll();
free_and_not_locked_registers.setIntersection(unlocked_registers);
if (free_and_not_locked_registers.count() < count) return null;
var free_and_unlocked_registers = self.locked_registers;
free_and_unlocked_registers.toggleAll();
free_and_unlocked_registers.setIntersection(self.free_registers);
free_and_unlocked_registers.setIntersection(register_class);
var regs: [count]Register = undefined;
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
if (excludeRegister(reg, register_class)) continue;
if (self.isRegLocked(reg)) continue;
if (!self.isRegFree(reg)) continue;
regs[i] = reg;
var it = free_and_unlocked_registers.iterator(.{});
while (it.next()) |reg_index| {
regs[i] = regAtTrackedIndex(@intCast(reg_index));
i += 1;
if (i >= count) break;
}
assert(i == count);
if (i < count) return null;
for (regs, insts) |reg, inst| {
log.debug("tryAllocReg {} for inst {?}", .{ reg, inst });
@ -279,46 +277,27 @@ pub fn RegisterManager(
) AllocationError![count]Register {
comptime assert(count > 0 and count <= tracked_registers.len);
var locked_registers = self.locked_registers;
locked_registers.setIntersection(register_class);
if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters;
const result = self.tryAllocRegs(count, insts, register_class) orelse blk: {
var unlocked_registers = self.locked_registers;
unlocked_registers.toggleAll();
unlocked_registers.setIntersection(register_class);
// We'll take over the first count registers. Spill
// the instructions that were previously there to a
// stack allocations.
var regs: [count]Register = undefined;
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
if (excludeRegister(reg, register_class)) break;
if (self.isRegLocked(reg)) continue;
log.debug("allocReg {} for inst {?}", .{ reg, insts[i] });
regs[i] = reg;
self.markRegAllocated(reg);
const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
if (insts[i]) |inst| {
// Track the register
if (self.isRegFree(reg)) {
self.markRegUsed(reg);
} else {
const spilled_inst = self.registers[index];
try self.getFunction().spillInstruction(reg, spilled_inst);
}
self.registers[index] = inst;
} else {
// Don't track the register
if (!self.isRegFree(reg)) {
const spilled_inst = self.registers[index];
try self.getFunction().spillInstruction(reg, spilled_inst);
self.freeReg(reg);
}
}
var it = unlocked_registers.iterator(.{});
while (it.next()) |reg_index| {
const tracked_index: TrackedIndex = @intCast(reg_index);
if (!self.isRegIndexFree(tracked_index) and
self.registers[tracked_index].unwrap() == .target) continue;
try self.getRegIndex(tracked_index, insts[i]);
regs[i] = regAtTrackedIndex(tracked_index);
i += 1;
if (i >= count) break;
}
if (i < count) return error.OutOfRegisters;
break :blk regs;
};
@ -340,7 +319,7 @@ pub fn RegisterManager(
/// Spills the register if it is currently allocated. If a
/// corresponding instruction is passed, will also track this
/// register.
fn getRegIndex(
pub fn getRegIndex(
self: *Self,
tracked_index: TrackedIndex,
inst: ?Air.Inst.Index,
@ -366,13 +345,13 @@ pub fn RegisterManager(
comptime reg: Register,
inst: ?Air.Inst.Index,
) AllocationError!void {
return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst);
return self.getRegIndex(indexOfKnownRegIntoTracked(reg) orelse return, inst);
}
/// Allocates the specified register with the specified
/// instruction. Asserts that the register is free and no
/// spilling is necessary.
fn getRegIndexAssumeFree(
pub fn getRegIndexAssumeFree(
self: *Self,
tracked_index: TrackedIndex,
inst: ?Air.Inst.Index,
@ -391,7 +370,7 @@ pub fn RegisterManager(
}
/// Marks the specified register as free
fn freeRegIndex(self: *Self, tracked_index: TrackedIndex) void {
pub fn freeRegIndex(self: *Self, tracked_index: TrackedIndex) void {
log.debug("freeing register {}", .{regAtTrackedIndex(tracked_index)});
self.registers[tracked_index] = undefined;
self.markRegIndexFree(tracked_index);
@ -420,8 +399,8 @@ const MockRegister1 = enum(u2) {
&MockRegister1.allocatable_registers,
);
const gp: RM.RegisterBitSet = blk: {
var set = RM.RegisterBitSet.initEmpty();
const gp = blk: {
var set: RM.RegisterBitSet = .initEmpty();
set.setRangeValue(.{
.start = 0,
.end = allocatable_registers.len,
@ -448,8 +427,8 @@ const MockRegister2 = enum(u2) {
&MockRegister2.allocatable_registers,
);
const gp: RM.RegisterBitSet = blk: {
var set = RM.RegisterBitSet.initEmpty();
const gp = blk: {
var set: RM.RegisterBitSet = .initEmpty();
set.setRangeValue(.{
.start = 0,
.end = allocatable_registers.len,
@ -489,16 +468,16 @@ const MockRegister3 = enum(u3) {
&MockRegister3.allocatable_registers,
);
const gp: RM.RegisterBitSet = blk: {
var set = RM.RegisterBitSet.initEmpty();
const gp = blk: {
var set: RM.RegisterBitSet = .initEmpty();
set.setRangeValue(.{
.start = 0,
.end = gp_regs.len,
}, true);
break :blk set;
};
const ext: RM.RegisterBitSet = blk: {
var set = RM.RegisterBitSet.initEmpty();
const ext = blk: {
var set: RM.RegisterBitSet = .initEmpty();
set.setRangeValue(.{
.start = gp_regs.len,
.end = allocatable_registers.len,

View File

@ -110,6 +110,8 @@ test {
_ = @import("behavior/widening.zig");
_ = @import("behavior/abs.zig");
_ = @import("behavior/x86_64.zig");
if (builtin.cpu.arch == .wasm32) {
_ = @import("behavior/wasm.zig");
}

View File

@ -144,31 +144,17 @@ test "alignment and size of structs with 128-bit fields" {
},
},
.x86_64 => switch (builtin.zig_backend) {
.stage2_x86_64 => .{
.a_align = 8,
.a_size = 16,
.x86_64 => .{
.a_align = 16,
.a_size = 16,
.b_align = 16,
.b_size = 32,
.b_align = 16,
.b_size = 32,
.u128_align = 8,
.u128_size = 16,
.u129_align = 8,
.u129_size = 24,
},
else => .{
.a_align = 16,
.a_size = 16,
.b_align = 16,
.b_size = 32,
.u128_align = 16,
.u128_size = 16,
.u129_align = 16,
.u129_size = 32,
},
.u128_align = 16,
.u128_size = 16,
.u129_align = 16,
.u129_size = 32,
},
.x86,
@ -291,8 +277,8 @@ test "function alignment" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
// function alignment is a compile error on wasm32/wasm64
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
// function alignment is a compile error on wasm
if (native_arch.isWasm()) return error.SkipZigTest;
const S = struct {
fn alignExpr() align(@sizeOf(usize) * 2) i32 {
@ -321,8 +307,8 @@ test "implicitly decreasing fn alignment" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
// function alignment is a compile error on wasm32/wasm64
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
// function alignment is a compile error on wasm
if (native_arch.isWasm()) return error.SkipZigTest;
try testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
try testImplicitlyDecreaseFnAlign(alignedBig, 5678);
@ -345,9 +331,9 @@ test "@alignCast functions" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
// function alignment is a compile error on wasm32/wasm64
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
if (native_arch == .thumb or native_arch == .thumbeb) return error.SkipZigTest;
// function alignment is a compile error on wasm
if (native_arch.isWasm()) return error.SkipZigTest;
if (native_arch.isThumb()) return error.SkipZigTest;
try expect(fnExpectsOnly1(simple4) == 0x19);
}
@ -510,9 +496,9 @@ test "align(N) on functions" {
return error.SkipZigTest;
}
// function alignment is a compile error on wasm32/wasm64
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
if (native_arch == .thumb or native_arch == .thumbeb) return error.SkipZigTest;
// function alignment is a compile error on wasm
if (native_arch.isWasm()) return error.SkipZigTest;
if (native_arch.isThumb()) return error.SkipZigTest;
try expect((@intFromPtr(&overaligned_fn) & (0x1000 - 1)) == 0);
}

View File

@ -178,7 +178,7 @@ test "rw constraint (x86_64)" {
}
test "asm modifiers (AArch64)" {
if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest;
if (!builtin.target.cpu.arch.isAARCH64()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly

View File

@ -1169,7 +1169,7 @@ test "arrays and vectors with big integers" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

View File

@ -660,6 +660,7 @@ test "arguments pointed to on stack into tailcall" {
switch (builtin.cpu.arch) {
.wasm32,
.wasm64,
.mips,
.mipsel,
.mips64,

View File

@ -124,7 +124,7 @@ test "@floatFromInt(f80)" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -1362,7 +1362,7 @@ test "cast f16 to wider types" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -2539,7 +2539,6 @@ test "@intFromBool on vector" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S = struct {

View File

@ -522,7 +522,7 @@ test "runtime 128 bit integer division" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

View File

@ -65,6 +65,8 @@ test "@clz" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
try testClz();
try comptime testClz();
@ -75,6 +77,7 @@ fn testClz() !void {
try expect(testOneClz(u8, 0b00001010) == 4);
try expect(testOneClz(u8, 0b00011010) == 3);
try expect(testOneClz(u8, 0b00000000) == 8);
try expect(testOneClz(i8, -1) == 0);
}
test "@clz big ints" {
@ -100,7 +103,7 @@ fn testOneClz(comptime T: type, x: T) u32 {
test "@clz vectors" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -159,6 +162,8 @@ fn testCtz() !void {
try expect(testOneCtz(u8, 0b10100000) == 5);
try expect(testOneCtz(u8, 0b10001010) == 1);
try expect(testOneCtz(u8, 0b00000000) == 8);
try expect(testOneCtz(i8, -1) == 0);
try expect(testOneCtz(i8, -2) == 1);
try expect(testOneCtz(u16, 0b00000000) == 16);
}
@ -467,7 +472,6 @@ test "division" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -583,7 +587,7 @@ fn testFloatDivision() !void {
}
test "large integer division" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
@ -780,7 +784,7 @@ test "128-bit multiplication" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{
@ -1369,7 +1373,7 @@ test "remainder division" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
@ -1522,7 +1526,7 @@ test "@round f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -1535,7 +1539,7 @@ test "@round f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -1712,7 +1716,7 @@ test "mod lazy values" {
test "@clz works on both vector and scalar inputs" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

View File

@ -122,7 +122,7 @@ test "@min/max for floats" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

View File

@ -58,7 +58,7 @@ test "@mulAdd f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -79,7 +79,7 @@ test "@mulAdd f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -189,7 +189,7 @@ test "vector f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try comptime vector80();
@ -216,7 +216,7 @@ test "vector f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try comptime vector128();

View File

@ -57,7 +57,7 @@ fn testNullPtrsEql() !void {
test "optional with zero-bit type" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const S = struct {

View File

@ -138,7 +138,6 @@ test "packed union initialized with a runtime value" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const Fields = packed struct {

View File

@ -164,10 +164,10 @@ test "saturating multiplication <= 32 bits" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) {
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) {
// https://github.com/ziglang/zig/issues/9660
return error.SkipZigTest;
}
@ -264,10 +264,10 @@ test "saturating multiplication" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) {
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) {
// https://github.com/ziglang/zig/issues/9660
return error.SkipZigTest;
}
@ -311,7 +311,7 @@ test "saturating shift-left" {
try testSatShl(i8, 127, 1, 127);
try testSatShl(i8, -128, 1, -128);
// TODO: remove this check once #9668 is completed
if (builtin.cpu.arch != .wasm32) {
if (!builtin.cpu.arch.isWasm()) {
// skip testing ints > 64 bits on wasm due to miscompilation / wasmtime ci error
try testSatShl(i128, maxInt(i128), 64, maxInt(i128));
try testSatShl(u128, maxInt(u128), 64, maxInt(u128));

View File

@ -418,8 +418,8 @@ test "packed struct 24bits" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO
if (comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO
if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; // TODO
if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
@ -818,7 +818,7 @@ test "non-packed struct with u128 entry in union" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const U = union(enum) {
@ -941,7 +941,7 @@ test "tuple assigned to variable" {
test "comptime struct field" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (comptime builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO
if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO
const T = struct {
a: i32,

View File

@ -2246,12 +2246,12 @@ test "matching captures causes union equivalence" {
}
test "signed enum tag with negative value" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
const Enum = enum(i8) {
a = -1,

View File

@ -100,7 +100,7 @@ test "simple variadic function" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@ -161,7 +161,7 @@ test "coerce reference to var arg" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@ -194,7 +194,7 @@ test "variadic functions" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@ -239,7 +239,7 @@ test "copy VaList" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@ -273,7 +273,7 @@ test "unused VaList arg" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
if (builtin.os.tag != .macos and builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}

View File

@ -101,7 +101,7 @@ test "vector float operators" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
@ -205,7 +205,6 @@ test "array vector coercion - odd sizes" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@ -308,7 +307,6 @@ test "tuple to vector" {
test "vector casts of sizes not divisible by 8" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -646,7 +644,7 @@ test "vector division operators" {
test "vector bitwise not operator" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -754,7 +752,7 @@ test "vector reduce operation" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21091
@ -989,7 +987,7 @@ test "saturating multiplication" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// TODO: once #9660 has been solved, remove this line
if (builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
if (builtin.target.cpu.arch.isWasm()) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
@ -1256,7 +1254,7 @@ test "byte vector initialized in inline function" {
if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
if (comptime builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and
builtin.cpu.features.isEnabled(@intFromEnum(std.Target.x86.Feature.avx512f)))
std.Target.x86.featureSetHas(builtin.cpu.features, .avx512f))
{
// TODO https://github.com/ziglang/zig/issues/13279
return error.SkipZigTest;
@ -1363,7 +1361,7 @@ test "load packed vector element" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
var x: @Vector(2, u15) = .{ 1, 4 };
@ -1411,7 +1409,6 @@ test "store vector with memset" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO

View File

@ -83,7 +83,7 @@ test "wrapping multiplication" {
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
// TODO: once #9660 has been solved, remove this line
if (builtin.cpu.arch == .wasm32) return error.SkipZigTest;
if (builtin.cpu.arch.isWasm()) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {

9
test/behavior/x86_64.zig Normal file
View File

@ -0,0 +1,9 @@
//! CodeGen tests for the x86_64 backend.
test {
const builtin = @import("builtin");
if (builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest;
if (builtin.object_format == .coff) return error.SkipZigTest;
_ = @import("x86_64/math.zig");
_ = @import("x86_64/mem.zig");
}

View File

@ -0,0 +1,115 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const compiler_rt_lib = b.addStaticLibrary(.{
.name = "compiler_rt",
.use_llvm = false,
.use_lld = false,
.root_module = b.createModule(.{
.root_source_file = b.addWriteFiles().add("compiler_rt.zig", ""),
.target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64 }),
}),
});
compiler_rt_lib.bundle_compiler_rt = true;
for ([_]std.Target.Query{
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.bsf_bsr_0_clobbers_result}),
//.cpu_features_sub = std.Target.x86.featureSet(&.{.sse}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.bsf_bsr_0_clobbers_result}),
.cpu_features_sub = std.Target.x86.featureSet(&.{
.cmov,
//.sse,
}),
},
//.{
// .cpu_arch = .x86_64,
// .cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
// .cpu_features_sub = std.Target.x86.featureSet(&.{.sse}),
//},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_sub = std.Target.x86.featureSet(&.{.sse2}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.sse3}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.ssse3}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.sse4_1}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64 },
.cpu_features_add = std.Target.x86.featureSet(&.{.sse4_2}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64_v2 },
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64_v2 },
.cpu_features_add = std.Target.x86.featureSet(&.{.avx}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64_v3 },
.cpu_features_sub = std.Target.x86.featureSet(&.{.avx2}),
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64_v3 },
},
.{
.cpu_arch = .x86_64,
.cpu_model = .{ .explicit = &std.Target.x86.cpu.x86_64_v4 },
},
}) |query| {
const target = b.resolveTargetQuery(query);
const cpu = query.serializeCpuAlloc(b.allocator) catch @panic("OOM");
for ([_][]const u8{
"math.zig",
"mem.zig",
}) |path| {
const test_mod = b.createModule(.{
.root_source_file = b.path(path),
.target = target,
});
const test_exe = b.addTest(.{
.name = std.fs.path.stem(path),
.use_llvm = false,
.use_lld = false,
.root_module = test_mod,
});
if (!std.Target.x86.featureSetHas(target.result.cpu.features, .sse2)) {
test_exe.bundle_compiler_rt = false;
test_mod.linkLibrary(compiler_rt_lib);
}
const test_run = b.addRunArtifact(test_exe);
b.default_step.dependOn(&test_run.step);
for ([_]*std.Build.Step{
&test_exe.step,
&test_run.step,
}) |step| step.name = b.fmt("{s} {s}", .{ step.name, cpu });
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
fn access(comptime array: anytype) !void {
var slice: []const @typeInfo(@TypeOf(array)).array.child = undefined;
slice = &array;
inline for (0.., &array) |ct_index, *elem| {
var rt_index: usize = undefined;
rt_index = ct_index;
if (&(slice.ptr + ct_index)[0] != elem) return error.Unexpected;
if (&(slice.ptr + rt_index)[0] != elem) return error.Unexpected;
if (&slice.ptr[ct_index..][0] != elem) return error.Unexpected;
if (&slice.ptr[rt_index..][0] != elem) return error.Unexpected;
if (&slice.ptr[ct_index] != elem) return error.Unexpected;
if (&slice.ptr[rt_index] != elem) return error.Unexpected;
if (&slice[ct_index..].ptr[0] != elem) return error.Unexpected;
if (&slice[rt_index..].ptr[0] != elem) return error.Unexpected;
if (&slice[ct_index] != elem) return error.Unexpected;
if (&slice[rt_index] != elem) return error.Unexpected;
if (slice.ptr[ct_index] != elem.*) return error.Unexpected;
if (slice.ptr[rt_index] != elem.*) return error.Unexpected;
if (slice[ct_index] != elem.*) return error.Unexpected;
if (slice[rt_index] != elem.*) return error.Unexpected;
}
}
test access {
try access([3]u8{ 0xdb, 0xef, 0xbd });
try access([3]u16{ 0x340e, 0x3654, 0x88d7 });
try access([3]u32{ 0xd424c2c0, 0x2d6ac466, 0x5a0cfaba });
try access([3]u64{
0x9327a4f5221666a6,
0x5c34d3ddd84a8b12,
0xbae087f39f649260,
});
try access([3]u128{
0x601cf010065444d4d42d5536dd9b95db,
0xa03f592fcaa22d40af23a0c735531e3c,
0x5da44907b31602b95c2d93f0b582ceab,
});
}

View File

@ -383,7 +383,7 @@ def InstRef_SummaryProvider(value, _=None):
'InternPool.Index(%d)' % value.unsigned if value.unsigned < 0x80000000 else 'instructions[%d]' % (value.unsigned - 0x80000000))
def InstIndex_SummaryProvider(value, _=None):
return 'instructions[%d]' % value.unsigned
return 'instructions[%d]' % value.unsigned if value.unsigned < 0x80000000 else 'temps[%d]' % (value.unsigned - 0x80000000)
class zig_DeclIndex_SynthProvider:
def __init__(self, value, _=None): self.value = value

View File

@ -902,8 +902,8 @@ const llvm_targets = [_]LlvmTarget{
.features = &.{ "v8a", "exynos" },
},
},
// LLVM removed support for v2 and v3 but zig wants to support targeting old hardware
.extra_features = &.{
// LLVM removed support for v2 and v3 but zig wants to support targeting old hardware
.{
.zig_name = "v2",
.desc = "ARMv2 architecture",
@ -1043,10 +1043,22 @@ const llvm_targets = [_]LlvmTarget{
.llvm_name = "64bit-mode",
.omit = true,
},
.{
.llvm_name = "alderlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "amdfam10",
.extra_deps = &.{"3dnowa"},
},
.{
.llvm_name = "arrowlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "arrowlake-s",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "athlon",
.extra_deps = &.{"3dnowa"},
@ -1081,16 +1093,64 @@ const llvm_targets = [_]LlvmTarget{
},
.{
.llvm_name = "barcelona",
.extra_deps = &.{"3dnowa"},
.extra_deps = &.{ "3dnowa", "smap", "smep" },
},
.{
.llvm_name = "broadwell",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "c3",
.extra_deps = &.{"3dnow"},
},
.{
.llvm_name = "cannonlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "cascadelake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "emeraldrapids",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "geode",
.extra_deps = &.{"3dnowa"},
},
.{
.llvm_name = "goldmont",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "goldmont_plus",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "haswell",
.extra_deps = &.{"smep"},
},
.{
.llvm_name = "i386",
.extra_deps = &.{"bsf_bsr_0_clobbers_result"},
},
.{
.llvm_name = "i486",
.extra_deps = &.{"bsf_bsr_0_clobbers_result"},
},
.{
.llvm_name = "icelake_client",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "icelake_server",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "ivybridge",
.extra_deps = &.{"smep"},
},
.{
.llvm_name = "k6-2",
.extra_deps = &.{"3dnow"},
@ -1127,6 +1187,10 @@ const llvm_targets = [_]LlvmTarget{
.llvm_name = "lakemont",
.extra_deps = &.{"soft_float"},
},
.{
.llvm_name = "meteorlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "opteron",
.extra_deps = &.{"3dnowa"},
@ -1135,6 +1199,38 @@ const llvm_targets = [_]LlvmTarget{
.llvm_name = "opteron-sse3",
.extra_deps = &.{"3dnowa"},
},
.{
.llvm_name = "raptorlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "rocketlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "sapphirerapids",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "silvermont",
.extra_deps = &.{"smep"},
},
.{
.llvm_name = "skx",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "skylake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "skylake_avx512",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "tigerlake",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "winchip2",
.extra_deps = &.{"3dnow"},
@ -1143,9 +1239,29 @@ const llvm_targets = [_]LlvmTarget{
.llvm_name = "sse4.2",
.extra_deps = &.{"crc32"},
},
.{
.llvm_name = "znver1",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "znver2",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "znver3",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "znver4",
.extra_deps = &.{ "smap", "smep" },
},
.{
.llvm_name = "znver5",
.extra_deps = &.{ "smap", "smep" },
},
},
// Features removed from LLVM
.extra_features = &.{
// Features removed from LLVM
.{
.zig_name = "3dnow",
.desc = "Enable 3DNow! instructions",
@ -1171,6 +1287,22 @@ const llvm_targets = [_]LlvmTarget{
.desc = "Prefetch with Intent to Write and T1 Hint",
.deps = &.{},
},
// Custom Zig features
.{
.zig_name = "bsf_bsr_0_clobbers_result",
.desc = "BSF/BSR may clobber the lower 32-bits of the result register when the source is zero",
.deps = &.{},
},
.{
.zig_name = "smap",
.desc = "Enable Supervisor Mode Access Prevention",
.deps = &.{},
},
.{
.zig_name = "smep",
.desc = "Enable Supervisor Mode Execution Prevention",
.deps = &.{},
},
},
.omit_cpus = &.{
// LLVM defines a bunch of dumb aliases with foreach loops in X86.td.