x86_64: demolish the old

This commit is contained in:
Jacob Young 2024-12-23 15:53:13 -05:00
parent 73a42953c9
commit a1828ebcda
8 changed files with 2677 additions and 2006 deletions

File diff suppressed because it is too large Load Diff

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..features.len].*);
},
}) 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
@ -474,7 +485,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,
@ -516,7 +527,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,
@ -835,7 +846,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);
@ -850,7 +861,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;
@ -859,7 +870,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| {
@ -876,7 +887,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,
@ -193,7 +194,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 +230,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 +256,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) },
@ -355,7 +356,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,11 +369,11 @@ 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,
};
@ -389,7 +390,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 {
@ -421,15 +422,15 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
try Instruction.new(.none, .lea, &[_]Operand{
.{ .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) },
});
.{ .imm = .s(0) },
}, lower.target);
lower.result_insts_len += 1;
_ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0);
emit_mnemonic = .lea;
@ -443,7 +444,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
try Instruction.new(.none, .mov, &[_]Operand{
.{ .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;
@ -467,7 +468,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
try Instruction.new(.none, .mov, &[_]Operand{
.{ .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 +483,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 },
@ -541,7 +542,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);
try Instruction.new(emit_prefix, emit_mnemonic, emit_ops, lower.target);
lower.result_insts_len += 1;
}
@ -743,7 +744,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

@ -214,6 +214,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,
@ -234,6 +238,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 ___
@ -362,6 +370,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,
@ -609,10 +619,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

View File

@ -479,8 +479,8 @@ 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)) {
none,
@ -503,7 +503,7 @@ pub const Memory = struct {
off: u64,
pub const Rm = struct {
size: Size,
size: Size = .none,
index: Register = .none,
scale: Scale = .@"1",
disp: i32 = 0,
@ -512,6 +512,7 @@ pub const Memory = struct {
pub const Size = enum(u4) {
none,
ptr,
byte,
word,
dword,
@ -548,9 +549,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,
@ -569,8 +571,11 @@ pub const Memory = struct {
writer: anytype,
) @TypeOf(writer).Error!void {
if (s == .none) return;
try writer.writeAll(@tagName(s));
try writer.writeAll(" ptr");
if (s != .ptr) {
try writer.writeAll(@tagName(s));
try writer.writeByte(' ');
}
try writer.writeAll("ptr");
}
};

View File

@ -167,11 +167,11 @@ pub const Instruction = struct {
};
}
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(),
};
}
};
@ -314,16 +314,21 @@ 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;
},
@ -332,10 +337,10 @@ pub const Instruction = struct {
.data = .{
.op_en = .zo,
.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,

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, &.{
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
}),
}, t),
.jmp => try Instruction.new(old_inst.prefix, .jmp, &.{
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.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 = try Instruction.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 = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops, t);
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
try encode(&.{inst}, code);
},
@ -1398,7 +1400,7 @@ 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) {
@ -1406,7 +1408,7 @@ const x86_64 = struct {
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
})) |inst| {
}, t)) |inst| {
inst.encode(std.io.null_writer, .{}) catch return false;
return true;
} else |_| return false,
@ -1414,7 +1416,7 @@ const x86_64 = struct {
}
}
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) {
@ -1423,7 +1425,7 @@ const x86_64 = struct {
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
}) catch unreachable;
}, t) catch unreachable;
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
encode(&.{inst}, code) catch unreachable;
},
@ -1431,7 +1433,7 @@ 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) {
@ -1440,7 +1442,7 @@ const x86_64 = struct {
old_inst.ops[0],
// TODO: hack to force imm32s in the assembler
.{ .imm = Immediate.s(-129) },
});
}, target);
relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
try encode(&.{inst}, code);
},

View File

@ -264,11 +264,48 @@ fn testBinary(comptime op: anytype) !void {
0xed533d18f8657f3f, 0x1ddd7cd7f6bab957,
});
if (false) try testType(@Vector(1, u128), .{
try testType(@Vector(1, u128), .{
0x5f11e16b0ca3392f907a857881455d2e,
}, .{
0xf9142d73b408fd6955922f9fc147f7d7,
});
try testType(@Vector(2, u128), .{
0xee0fb41fabd805923fb21b5c658e3a87,
0x2352e74aad6c58b3255ff0bba5aa6552,
}, .{
0x8d822f9fdd9cb9a5b43513b14419b224,
0x1aef2a02704379e38ead4d53d69e4cc4,
});
try testType(@Vector(4, u128), .{
0xc74437a4ea3bbbb193dbf0ea2f0c5281,
0x039e4b1640868248780db1834a0027eb,
0xb9e8bb34155b2b238da20331d08ff85b,
0x863802d34a54c2e6aa71dd0f067c4904,
}, .{
0x7471bae24ff7b84ab107f86ba2b7d1e7,
0x8f34c449d0576e682c20bda74aa6b6c9,
0x1f34c3efa167b61c48c9d5ec01a1a93f,
0x71c8318fcf3ddc7be058c73a52dce9e3,
});
try testType(@Vector(8, u128), .{
0xbf2db71463037f55ee338431f902a906,
0xb7ad317626655f38ab25ae30d8a1aa67,
0x7d3c5a3ffaa607b5560d69ae3fcf7863,
0x009a39a8badf8b628c686dc176aa1273,
0x49dba3744c91304cc7bbbdab61b6c969,
0x6ec664b624f7acf79ce69d80ed7bc85c,
0xe02d7a303c0f00c39010f3b815547f1c,
0xb13e1ee914616f58cffe6acd33d9b5c8,
}, .{
0x2f2d355a071942a7384f82ba72a945b8,
0x61f151b3afec8cb7664f813cecf581d1,
0x5bfbf5484f3a07f0eacc4739ff48af80,
0x59c0abbf8d829cf525a87d5c9c41a38a,
0xdad8b18eb680f0520ca49ebfb5842e22,
0xa05adcaedd9057480b3ba0413d003cec,
0x8b0b4a27fc94a0e90652d19bc755b63d,
0xa858bce5ad0e48c13588a4e170e8667c,
});
}
inline fn bitAnd(comptime Type: type, lhs: Type, rhs: Type) @TypeOf(lhs & rhs) {