Merge pull request #15597 from jacobly0/x86_64-behavior

x86_64: more behavior than ever before
This commit is contained in:
Jakub Konka 2023-05-08 19:04:05 +02:00 committed by GitHub
commit f40539e5d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 4699 additions and 2692 deletions

View File

@ -326,7 +326,7 @@ pub const all_features = blk: {
};
result[@enumToInt(Feature.avx512ifma)] = .{
.llvm_name = "avx512ifma",
.description = "Enable AVX-512 Integer Fused Multiple-Add",
.description = "Enable AVX-512 Integer Fused Multiply-Add",
.dependencies = featureSet(&[_]Feature{
.avx512f,
}),
@ -599,14 +599,14 @@ pub const all_features = blk: {
};
result[@enumToInt(Feature.fma)] = .{
.llvm_name = "fma",
.description = "Enable three-operand fused multiple-add",
.description = "Enable three-operand fused multiply-add",
.dependencies = featureSet(&[_]Feature{
.avx,
}),
};
result[@enumToInt(Feature.fma4)] = .{
.llvm_name = "fma4",
.description = "Enable four-operand fused multiple-add",
.description = "Enable four-operand fused multiply-add",
.dependencies = featureSet(&[_]Feature{
.avx,
.sse4a,

File diff suppressed because it is too large Load Diff

View File

@ -18,142 +18,152 @@ pub const Error = Lower.Error || error{
};
pub fn emitMir(emit: *Emit) Error!void {
for (0..emit.lower.mir.instructions.len) |i| {
const index = @intCast(Mir.Inst.Index, i);
const inst = emit.lower.mir.instructions.get(index);
const start_offset = @intCast(u32, emit.code.items.len);
try emit.code_offset_mapping.putNoClobber(emit.lower.allocator, index, start_offset);
for (try emit.lower.lowerMir(inst)) |lower_inst| try lower_inst.encode(emit.code.writer(), .{});
const end_offset = @intCast(u32, emit.code.items.len);
switch (inst.tag) {
else => {},
.jmp_reloc => try emit.relocs.append(emit.lower.allocator, .{
.source = start_offset,
.target = inst.data.inst,
.offset = end_offset - 4,
.length = 5,
}),
.call_extern => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
// Add relocation to the decl.
const atom_index = macho_file.getAtomIndexForSymbol(
.{ .sym_index = inst.data.relocation.atom_index, .file = null },
).?;
const target = macho_file.getGlobalByIndex(inst.data.relocation.sym_index);
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = .branch,
for (0..emit.lower.mir.instructions.len) |mir_i| {
const mir_index = @intCast(Mir.Inst.Index, mir_i);
try emit.code_offset_mapping.putNoClobber(
emit.lower.allocator,
mir_index,
@intCast(u32, 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| {
const start_offset = @intCast(u32, emit.code.items.len);
try lowered_inst.encode(emit.code.writer(), .{});
const end_offset = @intCast(u32, emit.code.items.len);
while (lowered_relocs.len > 0 and
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, .{
.source = start_offset,
.target = target,
.offset = end_offset - 4,
.addend = 0,
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
// Add relocation to the decl.
const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = inst.data.relocation.atom_index, .file = null },
).?;
const target = coff_file.getGlobalByIndex(inst.data.relocation.sym_index);
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
.type = .direct,
.target = target,
.offset = end_offset - 4,
.addend = 0,
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
.length = @intCast(u5, end_offset - start_offset),
}),
.linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
// Add relocation to the decl.
const atom_index = macho_file.getAtomIndexForSymbol(
.{ .sym_index = symbol.atom_index, .file = null },
).?;
const target = macho_file.getGlobalByIndex(symbol.sym_index);
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = .branch,
.target = target,
.offset = end_offset - 4,
.addend = 0,
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
// Add relocation to the decl.
const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = symbol.atom_index, .file = null },
).?;
const target = coff_file.getGlobalByIndex(symbol.sym_index);
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
.type = .direct,
.target = target,
.offset = end_offset - 4,
.addend = 0,
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement extern reloc for {s}", .{
@tagName(emit.bin_file.tag),
}),
.linker_got,
.linker_direct,
.linker_import,
.linker_tlv,
=> |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
const atom_index = macho_file.getAtomIndexForSymbol(.{
.sym_index = symbol.atom_index,
.file = null,
}).?;
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = switch (lowered_relocs[0].target) {
.linker_got => .got,
.linker_direct => .signed,
.linker_tlv => .tlv,
else => unreachable,
},
.target = .{ .sym_index = symbol.sym_index, .file = null },
.offset = @intCast(u32, end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
const atom_index = coff_file.getAtomIndexForSymbol(.{
.sym_index = symbol.atom_index,
.file = null,
}).?;
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
.type = switch (lowered_relocs[0].target) {
.linker_got => .got,
.linker_direct => .direct,
.linker_import => .import,
else => unreachable,
},
.target = switch (lowered_relocs[0].target) {
.linker_got,
.linker_direct,
=> .{ .sym_index = symbol.sym_index, .file = null },
.linker_import => coff_file.getGlobalByIndex(symbol.sym_index),
else => unreachable,
},
.offset = @intCast(u32, end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement linker reloc for {s}", .{
@tagName(emit.bin_file.tag),
}),
};
}
std.debug.assert(lowered_relocs.len == 0);
.mov_linker, .lea_linker => if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
const metadata =
emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
const atom_index = macho_file.getAtomIndexForSymbol(.{
.sym_index = metadata.atom_index,
.file = null,
}).?;
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = switch (inst.ops) {
.got_reloc => .got,
.direct_reloc => .signed,
.tlv_reloc => .tlv,
else => unreachable,
if (lowered.insts.len == 0) {
const mir_inst = emit.lower.mir.instructions.get(mir_index);
switch (mir_inst.tag) {
else => unreachable,
.pseudo => switch (mir_inst.ops) {
else => unreachable,
.pseudo_dbg_prologue_end_none => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setPrologueEnd();
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.target = .{ .sym_index = metadata.sym_index, .file = null },
.offset = @intCast(u32, end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
const metadata =
emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
const atom_index = coff_file.getAtomIndexForSymbol(.{
.sym_index = metadata.atom_index,
.file = null,
}).?;
try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
.type = switch (inst.ops) {
.got_reloc => .got,
.direct_reloc => .direct,
.import_reloc => .import,
else => unreachable,
.pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(
mir_inst.data.line_column.line,
mir_inst.data.line_column.column,
),
.pseudo_dbg_epilogue_begin_none => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.target = switch (inst.ops) {
.got_reloc,
.direct_reloc,
=> .{ .sym_index = metadata.sym_index, .file = null },
.import_reloc => coff_file.getGlobalByIndex(metadata.sym_index),
else => unreachable,
},
.offset = @intCast(u32, end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
});
} else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }),
.jcc => try emit.relocs.append(emit.lower.allocator, .{
.source = start_offset,
.target = inst.data.inst_cc.inst,
.offset = end_offset - 4,
.length = 6,
}),
.dbg_line => try emit.dbgAdvancePCAndLine(
inst.data.line_column.line,
inst.data.line_column.column,
),
.dbg_prologue_end => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setPrologueEnd();
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.dbg_epilogue_begin => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dead_none => {},
},
}
}
}
try emit.fixupRelocs();

View File

@ -23,6 +23,7 @@ const Data = struct {
opc: [7]u8,
modrm_ext: u3,
mode: Mode,
feature: Feature,
};
pub fn findByMnemonic(
@ -57,9 +58,9 @@ pub fn findByMnemonic(
var shortest_len: ?usize = null;
next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| {
switch (data.mode) {
.rex => if (!rex_required) continue,
.long, .sse_long, .sse2_long => {},
else => if (rex_required) continue,
.none, .short => if (rex_required) continue,
.rex, .rex_short => if (!rex_required) continue,
else => {},
}
for (input_ops, data.ops) |input_op, data_op|
if (!input_op.isSubset(data_op)) continue :next;
@ -88,28 +89,13 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct {
if (modrm_ext) |ext| if (ext != data.modrm_ext) continue;
if (!std.mem.eql(u8, opc, enc.opcode())) continue;
if (prefixes.rex.w) {
switch (data.mode) {
.short, .fpu, .sse, .sse2, .sse4_1, .none => continue,
.long, .sse_long, .sse2_long, .rex => {},
}
if (!data.mode.isLong()) continue;
} else if (prefixes.rex.present and !prefixes.rex.isSet()) {
switch (data.mode) {
.rex => {},
else => continue,
}
if (!data.mode.isRex()) continue;
} else if (prefixes.legacy.prefix_66) {
switch (enc.operandBitSize()) {
16 => {},
else => continue,
}
if (!data.mode.isShort()) continue;
} else {
switch (data.mode) {
.none => switch (enc.operandBitSize()) {
16 => continue,
else => {},
},
else => continue,
}
if (data.mode.isShort()) continue;
}
return enc;
};
@ -130,30 +116,11 @@ pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
pub fn modRmExt(encoding: Encoding) u3 {
return switch (encoding.data.op_en) {
.m, .mi, .m1, .mc => encoding.data.modrm_ext,
.m, .mi, .m1, .mc, .vmi => encoding.data.modrm_ext,
else => unreachable,
};
}
pub fn operandBitSize(encoding: Encoding) u64 {
switch (encoding.data.mode) {
.short => return 16,
.long, .sse_long, .sse2_long => return 64,
else => {},
}
const bit_size: u64 = switch (encoding.data.op_en) {
.np => switch (encoding.data.ops[0]) {
.o16 => 16,
.o32 => 32,
.o64 => 64,
else => 32,
},
.td => encoding.data.ops[1].bitSize(),
else => encoding.data.ops[0].bitSize(),
};
return bit_size;
}
pub fn format(
encoding: Encoding,
comptime fmt: []const u8,
@ -162,14 +129,41 @@ pub fn format(
) !void {
_ = options;
_ = fmt;
switch (encoding.data.mode) {
.long, .sse_long, .sse2_long => try writer.writeAll("REX.W + "),
else => {},
}
for (encoding.opcode()) |byte| {
try writer.print("{x:0>2} ", .{byte});
}
var opc = encoding.opcode();
if (encoding.data.mode.isVex()) {
try writer.writeAll("VEX.");
try writer.writeAll(switch (encoding.data.mode) {
.vex_128_w0, .vex_128_w1, .vex_128_wig => "128",
.vex_256_w0, .vex_256_w1, .vex_256_wig => "256",
.vex_lig_w0, .vex_lig_w1, .vex_lig_wig => "LIG",
.vex_lz_w0, .vex_lz_w1, .vex_lz_wig => "LZ",
else => unreachable,
});
switch (opc[0]) {
else => {},
0x66, 0xf3, 0xf2 => {
try writer.print(".{X:0>2}", .{opc[0]});
opc = opc[1..];
},
}
try writer.print(".{}", .{std.fmt.fmtSliceHexUpper(opc[0 .. opc.len - 1])});
opc = opc[opc.len - 1 ..];
try writer.writeAll(".W");
try writer.writeAll(switch (encoding.data.mode) {
.vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",
.vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",
.vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",
else => unreachable,
});
try writer.writeByte(' ');
} else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + ");
for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
switch (encoding.data.op_en) {
.np, .fd, .td, .i, .zi, .d => {},
@ -183,16 +177,17 @@ pub fn format(
};
try writer.print("+{s} ", .{tag});
},
.m, .mi, .m1, .mc => try writer.print("/{d} ", .{encoding.modRmExt()}),
.mr, .rm, .rmi, .mri, .mrc => try writer.writeAll("/r "),
.m, .mi, .m1, .mc, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
.mr, .rm, .rmi, .mri, .mrc, .rvm, .rvmi, .mvr => try writer.writeAll("/r "),
}
switch (encoding.data.op_en) {
.i, .d, .zi, .oi, .mi, .rmi, .mri => {
.i, .d, .zi, .oi, .mi, .rmi, .mri, .vmi, .rvmi => {
const op = switch (encoding.data.op_en) {
.i, .d => encoding.data.ops[0],
.zi, .oi, .mi => encoding.data.ops[1],
.rmi, .mri => encoding.data.ops[2],
.rmi, .mri, .vmi => encoding.data.ops[2],
.rvmi => encoding.data.ops[3],
else => unreachable,
};
const tag = switch (op) {
@ -207,7 +202,7 @@ pub fn format(
};
try writer.print("{s} ", .{tag});
},
.np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm, .mrc => {},
.np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm, .mrc, .rvm, .mvr => {},
}
try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
@ -267,44 +262,79 @@ pub const Mnemonic = enum {
// MMX
movd,
// SSE
addss,
addps, addss,
andps,
andnps,
cmpss,
cvtsi2ss,
divss,
maxss, minss,
movaps, movss, movups,
mulss,
divps, divss,
maxps, maxss,
minps, minss,
movaps, movhlps, movss, movups,
mulps, mulss,
orps,
pextrw,
pinsrw,
sqrtps,
sqrtss,
subss,
pextrw, pinsrw,
sqrtps, sqrtss,
subps, subss,
ucomiss,
xorps,
// SSE2
addsd,
addpd, addsd,
andpd,
andnpd,
//cmpsd,
cvtsd2ss, cvtsi2sd, cvtss2sd,
divsd,
maxsd, minsd,
divpd, divsd,
maxpd, maxsd,
minpd, minsd,
movapd,
movq, //movd, movsd,
movupd,
mulsd,
mulpd, mulsd,
orpd,
sqrtpd,
sqrtsd,
subsd,
pshufhw, pshuflw,
psrld, psrlq, psrlw,
punpckhbw, punpckhdq, punpckhqdq, punpckhwd,
punpcklbw, punpckldq, punpcklqdq, punpcklwd,
sqrtpd, sqrtsd,
subpd, subsd,
ucomisd,
xorpd,
// SSE3
movddup, movshdup, movsldup,
// SSE4.1
roundss,
roundsd,
pextrb, pextrd, pextrq,
pinsrb, pinsrd, pinsrq,
roundpd, roundps, roundsd, roundss,
// AVX
vaddpd, vaddps, vaddsd, vaddss,
vcvtsd2ss, vcvtsi2sd, vcvtsi2ss, vcvtss2sd,
vdivpd, vdivps, vdivsd, vdivss,
vmaxpd, vmaxps, vmaxsd, vmaxss,
vminpd, vminps, vminsd, vminss,
vmovapd, vmovaps,
vmovddup, vmovhlps,
vmovsd,
vmovshdup, vmovsldup,
vmovss,
vmovupd, vmovups,
vmulpd, vmulps, vmulsd, vmulss,
vpextrb, vpextrd, vpextrq, vpextrw,
vpinsrb, vpinsrd, vpinsrq, vpinsrw,
vpshufhw, vpshuflw,
vpsrld, vpsrlq, vpsrlw,
vpunpckhbw, vpunpckhdq, vpunpckhqdq, vpunpckhwd,
vpunpcklbw, vpunpckldq, vpunpcklqdq, vpunpcklwd,
vroundpd, vroundps, vroundsd, vroundss,
vsqrtpd, vsqrtps, vsqrtsd, vsqrtss,
vsubpd, vsubps, vsubsd, vsubss,
// F16C
vcvtph2ps, vcvtps2ph,
// FMA
vfmadd132pd, vfmadd213pd, vfmadd231pd,
vfmadd132ps, vfmadd213ps, vfmadd231ps,
vfmadd132sd, vfmadd213sd, vfmadd231sd,
vfmadd132ss, vfmadd213ss, vfmadd231ss,
// zig fmt: on
};
@ -317,6 +347,7 @@ pub const OpEn = enum {
fd, td,
m1, mc, mi, mr, rm,
rmi, mri, mrc,
vmi, rvm, rvmi, mvr,
// zig fmt: on
};
@ -331,12 +362,14 @@ pub const Op = enum {
cl,
r8, r16, r32, r64,
rm8, rm16, rm32, rm64,
m8, m16, m32, m64, m80, m128,
r32_m8, r32_m16, r64_m16,
m8, m16, m32, m64, m80, m128, m256,
rel8, rel16, rel32,
m,
moffs,
sreg,
xmm, xmm_m32, xmm_m64, xmm_m128,
ymm, ymm_m256,
// zig fmt: on
pub fn fromOperand(operand: Instruction.Operand) Op {
@ -348,6 +381,7 @@ pub const Op = enum {
.segment => return .sreg,
.floating_point => return switch (reg.bitSize()) {
128 => .xmm,
256 => .ymm,
else => unreachable,
},
.general_purpose => {
@ -381,6 +415,7 @@ pub const Op = enum {
64 => .m64,
80 => .m80,
128 => .m128,
256 => .m256,
else => unreachable,
};
},
@ -409,16 +444,52 @@ pub const Op = enum {
}
}
pub fn bitSize(op: Op) u64 {
pub fn immBitSize(op: Op) u64 {
return switch (op) {
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
.al, .cl, .r8, .rm8, .r32_m8 => unreachable,
.ax, .r16, .rm16 => unreachable,
.eax, .r32, .rm32, .r32_m16 => unreachable,
.rax, .r64, .rm64, .r64_m16 => unreachable,
.xmm, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
.ymm, .ymm_m256 => unreachable,
.m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
.unity => 1,
.imm8, .imm8s, .al, .cl, .r8, .m8, .rm8, .rel8 => 8,
.imm16, .imm16s, .ax, .r16, .m16, .rm16, .rel16 => 16,
.imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32,
.imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64,
.imm8, .imm8s, .rel8 => 8,
.imm16, .imm16s, .rel16 => 16,
.imm32, .imm32s, .rel32 => 32,
.imm64 => 64,
};
}
pub fn regBitSize(op: Op) u64 {
return switch (op) {
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
.unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
.rel8, .rel16, .rel32 => unreachable,
.m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
.al, .cl, .r8, .rm8 => 8,
.ax, .r16, .rm16 => 16,
.eax, .r32, .rm32, .r32_m8, .r32_m16 => 32,
.rax, .r64, .rm64, .r64_m16 => 64,
.xmm, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
.ymm, .ymm_m256 => 256,
};
}
pub fn memBitSize(op: Op) u64 {
return switch (op) {
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
.unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
.rel8, .rel16, .rel32 => unreachable,
.al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64, .xmm, .ymm => unreachable,
.m8, .rm8, .r32_m8 => 8,
.m16, .rm16, .r32_m16, .r64_m16 => 16,
.m32, .rm32, .xmm_m32 => 32,
.m64, .rm64, .xmm_m64 => 64,
.m80 => 80,
.m128, .xmm, .xmm_m128 => 128,
.m128, .xmm_m128 => 128,
.m256, .ymm_m256 => 256,
};
}
@ -441,7 +512,9 @@ pub const Op = enum {
.al, .ax, .eax, .rax,
.r8, .r16, .r32, .r64,
.rm8, .rm16, .rm32, .rm64,
.r32_m8, .r32_m16, .r64_m16,
.xmm, .xmm_m32, .xmm_m64, .xmm_m128,
.ymm, .ymm_m256,
=> true,
else => false,
};
@ -465,9 +538,11 @@ pub const Op = enum {
// zig fmt: off
return switch (op) {
.rm8, .rm16, .rm32, .rm64,
.m8, .m16, .m32, .m64, .m80, .m128,
.r32_m8, .r32_m16, .r64_m16,
.m8, .m16, .m32, .m64, .m80, .m128, .m256,
.m,
.xmm_m32, .xmm_m64, .xmm_m128,
.ymm_m256,
=> true,
else => false,
};
@ -487,15 +562,10 @@ pub const Op = enum {
.al, .ax, .eax, .rax, .cl => .general_purpose,
.r8, .r16, .r32, .r64 => .general_purpose,
.rm8, .rm16, .rm32, .rm64 => .general_purpose,
.r32_m8, .r32_m16, .r64_m16 => .general_purpose,
.sreg => .segment,
.xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .floating_point,
};
}
pub fn isFloatingPointRegister(op: Op) bool {
return switch (op) {
.xmm, .xmm_m32, .xmm_m64, .xmm_m128 => true,
else => false,
.ymm, .ymm_m256 => .floating_point,
};
}
@ -512,30 +582,27 @@ pub const Op = enum {
if (op.isRegister() and target.isRegister()) {
return switch (target) {
.cl, .al, .ax, .eax, .rax => op == target,
else => op.class() == target.class() and switch (target.class()) {
.floating_point => true,
else => op.bitSize() == target.bitSize(),
},
else => op.class() == target.class() and op.regBitSize() == target.regBitSize(),
};
}
if (op.isMemory() and target.isMemory()) {
switch (target) {
.m => return true,
else => return op.bitSize() == target.bitSize(),
else => return op.memBitSize() == target.memBitSize(),
}
}
if (op.isImmediate() and target.isImmediate()) {
switch (target) {
.imm64 => if (op.bitSize() <= 64) return true,
.imm32s, .rel32 => if (op.bitSize() < 32 or (op.bitSize() == 32 and op.isSigned()))
.imm64 => if (op.immBitSize() <= 64) return true,
.imm32s, .rel32 => if (op.immBitSize() < 32 or (op.immBitSize() == 32 and op.isSigned()))
return true,
.imm32 => if (op.bitSize() <= 32) return true,
.imm16s, .rel16 => if (op.bitSize() < 16 or (op.bitSize() == 16 and op.isSigned()))
.imm32 => if (op.immBitSize() <= 32) return true,
.imm16s, .rel16 => if (op.immBitSize() < 16 or (op.immBitSize() == 16 and op.isSigned()))
return true,
.imm16 => if (op.bitSize() <= 16) return true,
.imm8s, .rel8 => if (op.bitSize() < 8 or (op.bitSize() == 8 and op.isSigned()))
.imm16 => if (op.immBitSize() <= 16) return true,
.imm8s, .rel8 => if (op.immBitSize() < 8 or (op.immBitSize() == 8 and op.isSigned()))
return true,
.imm8 => if (op.bitSize() <= 8) return true,
.imm8 => if (op.immBitSize() <= 8) return true,
else => {},
}
return op == target;
@ -547,16 +614,81 @@ pub const Op = enum {
};
pub const Mode = enum {
// zig fmt: off
none,
short,
fpu,
rex,
long,
short, long,
rex, rex_short,
vex_128_w0, vex_128_w1, vex_128_wig,
vex_256_w0, vex_256_w1, vex_256_wig,
vex_lig_w0, vex_lig_w1, vex_lig_wig,
vex_lz_w0, vex_lz_w1, vex_lz_wig,
// zig fmt: on
pub fn isShort(mode: Mode) bool {
return switch (mode) {
.short, .rex_short => true,
else => false,
};
}
pub fn isLong(mode: Mode) bool {
return switch (mode) {
.long,
.vex_128_w1,
.vex_256_w1,
.vex_lig_w1,
.vex_lz_w1,
=> true,
else => false,
};
}
pub fn isRex(mode: Mode) bool {
return switch (mode) {
else => false,
.rex, .rex_short => true,
};
}
pub fn isVex(mode: Mode) bool {
return switch (mode) {
// zig fmt: off
else => false,
.vex_128_w0, .vex_128_w1, .vex_128_wig,
.vex_256_w0, .vex_256_w1, .vex_256_wig,
.vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
.vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
=> true,
// zig fmt: on
};
}
pub fn isVecLong(mode: Mode) bool {
return switch (mode) {
// zig fmt: off
else => unreachable,
.vex_128_w0, .vex_128_w1, .vex_128_wig,
.vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
.vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
=> false,
.vex_256_w0, .vex_256_w1, .vex_256_wig,
=> true,
// zig fmt: on
};
}
};
pub const Feature = enum {
none,
avx,
avx2,
f16c,
fma,
sse,
sse_long,
sse2,
sse2_long,
sse3,
sse4_1,
x87,
};
fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize {
@ -573,7 +705,7 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
}
const mnemonic_to_encodings_map = init: {
@setEvalBranchQuota(100_000);
@setEvalBranchQuota(20_000);
const encodings = @import("encodings.zig");
var entries = encodings.table;
std.sort.sort(encodings.Entry, &entries, {}, struct {
@ -593,6 +725,7 @@ const mnemonic_to_encodings_map = init: {
.opc = undefined,
.modrm_ext = entry[4],
.mode = entry[5],
.feature = entry[6],
};
// TODO: use `@memcpy` for these. When I did that, I got a false positive
// compile error for this copy happening at compile time.

View File

@ -5,13 +5,22 @@ mir: Mir,
target: *const std.Target,
err_msg: ?*ErrorMsg = null,
src_loc: Module.SrcLoc,
result: [
result_insts_len: u8 = undefined,
result_relocs_len: u8 = undefined,
result_insts: [
std.mem.max(usize, &.{
abi.Win64.callee_preserved_regs.len,
abi.SysV.callee_preserved_regs.len,
2, // cmovcc: cmovcc \ cmovcc
3, // setcc: setcc \ setcc \ logicop
2, // jcc: jcc \ jcc
abi.Win64.callee_preserved_regs.len, // push_regs/pop_regs
abi.SysV.callee_preserved_regs.len, // push_regs/pop_regs
})
]Instruction = undefined,
result_len: usize = undefined,
result_relocs: [
std.mem.max(usize, &.{
2, // jcc: jcc \ jcc
})
]Reloc = undefined,
pub const Error = error{
OutOfMemory,
@ -20,155 +29,155 @@ pub const Error = error{
CannotEncode,
};
pub const Reloc = struct {
lowered_inst_index: u8,
target: Target,
const Target = union(enum) {
inst: Mir.Inst.Index,
linker_extern_fn: Mir.Reloc,
linker_got: Mir.Reloc,
linker_direct: Mir.Reloc,
linker_import: Mir.Reloc,
linker_tlv: Mir.Reloc,
};
};
/// The returned slice is overwritten by the next call to lowerMir.
pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
lower.result = undefined;
errdefer lower.result = undefined;
lower.result_len = 0;
defer lower.result_len = undefined;
pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
insts: []const Instruction,
relocs: []const Reloc,
} {
lower.result_insts = undefined;
lower.result_relocs = undefined;
errdefer lower.result_insts = undefined;
errdefer lower.result_relocs = undefined;
lower.result_insts_len = 0;
lower.result_relocs_len = 0;
defer lower.result_insts_len = undefined;
defer lower.result_relocs_len = undefined;
const inst = lower.mir.instructions.get(index);
switch (inst.tag) {
.adc,
.add,
.@"and",
.bsf,
.bsr,
.bswap,
.bt,
.btc,
.btr,
.bts,
.call,
.cbw,
.cwde,
.cdqe,
.cwd,
.cdq,
.cqo,
.cmp,
.cmpxchg,
.div,
.fisttp,
.fld,
.idiv,
.imul,
.int3,
.jmp,
.lea,
.lfence,
.lzcnt,
.mfence,
.mov,
.movbe,
.movd,
.movq,
.movzx,
.mul,
.neg,
.nop,
.not,
.@"or",
.pop,
.popcnt,
.push,
.rcl,
.rcr,
.ret,
.rol,
.ror,
.sal,
.sar,
.sbb,
.sfence,
.shl,
.shld,
.shr,
.shrd,
.sub,
.syscall,
.@"test",
.tzcnt,
.ud2,
.xadd,
.xchg,
.xor,
else => try lower.generic(inst),
.pseudo => switch (inst.ops) {
.pseudo_cmov_z_and_np_rr => {
try lower.emit(.none, .cmovnz, &.{
.{ .reg = inst.data.rr.r2 },
.{ .reg = inst.data.rr.r1 },
});
try lower.emit(.none, .cmovnp, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_cmov_nz_or_p_rr => {
try lower.emit(.none, .cmovnz, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
try lower.emit(.none, .cmovp, &.{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_cmov_nz_or_p_rm_sib,
.pseudo_cmov_nz_or_p_rm_rip,
=> {
try lower.emit(.none, .cmovnz, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
});
try lower.emit(.none, .cmovp, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
});
},
.pseudo_set_z_and_np_r => {
try lower.emit(.none, .setz, &.{
.{ .reg = inst.data.r_scratch.r1 },
});
try lower.emit(.none, .setnp, &.{
.{ .reg = inst.data.r_scratch.scratch_reg },
});
try lower.emit(.none, .@"and", &.{
.{ .reg = inst.data.r_scratch.r1 },
.{ .reg = inst.data.r_scratch.scratch_reg },
});
},
.pseudo_set_z_and_np_m_sib,
.pseudo_set_z_and_np_m_rip,
=> {
try lower.emit(.none, .setz, &.{
.{ .mem = lower.mem(inst.ops, inst.data.x_scratch.payload) },
});
try lower.emit(.none, .setnp, &.{
.{ .reg = inst.data.x_scratch.scratch_reg },
});
try lower.emit(.none, .@"and", &.{
.{ .mem = lower.mem(inst.ops, inst.data.x_scratch.payload) },
.{ .reg = inst.data.x_scratch.scratch_reg },
});
},
.pseudo_set_nz_or_p_r => {
try lower.emit(.none, .setnz, &.{
.{ .reg = inst.data.r_scratch.r1 },
});
try lower.emit(.none, .setp, &.{
.{ .reg = inst.data.r_scratch.scratch_reg },
});
try lower.emit(.none, .@"or", &.{
.{ .reg = inst.data.r_scratch.r1 },
.{ .reg = inst.data.r_scratch.scratch_reg },
});
},
.pseudo_set_nz_or_p_m_sib,
.pseudo_set_nz_or_p_m_rip,
=> {
try lower.emit(.none, .setnz, &.{
.{ .mem = lower.mem(inst.ops, inst.data.x_scratch.payload) },
});
try lower.emit(.none, .setp, &.{
.{ .reg = inst.data.x_scratch.scratch_reg },
});
try lower.emit(.none, .@"or", &.{
.{ .mem = lower.mem(inst.ops, inst.data.x_scratch.payload) },
.{ .reg = inst.data.x_scratch.scratch_reg },
});
},
.pseudo_j_z_and_np_inst => {
try lower.emit(.none, .jnz, &.{
.{ .imm = lower.reloc(.{ .inst = index + 1 }) },
});
try lower.emit(.none, .jnp, &.{
.{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
});
},
.pseudo_j_nz_or_p_inst => {
try lower.emit(.none, .jnz, &.{
.{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
});
try lower.emit(.none, .jp, &.{
.{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
});
},
.addss,
.andnps,
.andps,
.cmpss,
.cvtsi2ss,
.divss,
.maxss,
.minss,
.movaps,
.movss,
.movups,
.mulss,
.orps,
.pextrw,
.pinsrw,
.roundss,
.sqrtps,
.sqrtss,
.subss,
.ucomiss,
.xorps,
.addsd,
.andnpd,
.andpd,
.cmpsd,
.cvtsd2ss,
.cvtsi2sd,
.cvtss2sd,
.divsd,
.maxsd,
.minsd,
.movsd,
.mulsd,
.orpd,
.roundsd,
.sqrtpd,
.sqrtsd,
.subsd,
.ucomisd,
.xorpd,
=> try lower.mirGeneric(inst),
.pseudo_push_reg_list => try lower.pushPopRegList(.push, inst),
.pseudo_pop_reg_list => try lower.pushPopRegList(.pop, inst),
.cmps,
.lods,
.movs,
.scas,
.stos,
=> try lower.mirString(inst),
.cmpxchgb => try lower.mirCmpxchgBytes(inst),
.jmp_reloc => try lower.emit(.none, .jmp, &.{.{ .imm = Immediate.s(0) }}),
.call_extern => try lower.emit(.none, .call, &.{.{ .imm = Immediate.s(0) }}),
.lea_linker => try lower.mirLeaLinker(inst),
.mov_linker => try lower.mirMovLinker(inst),
.mov_moffs => try lower.mirMovMoffs(inst),
.movsx => try lower.mirMovsx(inst),
.cmovcc => try lower.mirCmovcc(inst),
.setcc => try lower.mirSetcc(inst),
.jcc => try lower.emit(.none, mnem_cc(.j, inst.data.inst_cc.cc), &.{.{ .imm = Immediate.s(0) }}),
.push_regs => try lower.mirPushPopRegisterList(inst, .push),
.pop_regs => try lower.mirPushPopRegisterList(inst, .pop),
.dbg_line,
.dbg_prologue_end,
.dbg_epilogue_begin,
.dead,
=> {},
.pseudo_dbg_prologue_end_none,
.pseudo_dbg_line_line_column,
.pseudo_dbg_epilogue_begin_none,
.pseudo_dead_none,
=> {},
else => unreachable,
},
}
return lower.result[0..lower.result_len];
return .{
.insts = lower.result_insts[0..lower.result_insts_len],
.relocs = lower.result_relocs[0..lower.result_relocs_len],
};
}
pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
@ -178,12 +187,6 @@ pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
return error.LowerFail;
}
fn mnem_cc(comptime base: @Type(.EnumLiteral), cc: bits.Condition) Mnemonic {
return switch (cc) {
inline else => |c| @field(Mnemonic, @tagName(base) ++ @tagName(c)),
};
}
fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
return switch (ops) {
.rri_s,
@ -191,21 +194,22 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
.i_s,
.mi_sib_s,
.mi_rip_s,
.lock_mi_sib_s,
.lock_mi_rip_s,
=> Immediate.s(@bitCast(i32, i)),
.rrri,
.rri_u,
.ri_u,
.i_u,
.mi_sib_u,
.mi_rip_u,
.lock_mi_sib_u,
.lock_mi_rip_u,
.rmi_sib,
.rmi_rip,
.mri_sib,
.mri_rip,
.rrm_sib,
.rrm_rip,
.rrmi_sib,
.rrmi_rip,
=> Immediate.u(i),
.ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
@ -217,76 +221,108 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
return lower.mir.resolveFrameLoc(switch (ops) {
.rm_sib,
.rm_sib_cc,
.rmi_sib,
.m_sib,
.m_sib_cc,
.mi_sib_u,
.mi_sib_s,
.mr_sib,
.mrr_sib,
.mri_sib,
.lock_m_sib,
.lock_mi_sib_u,
.lock_mi_sib_s,
.lock_mr_sib,
.rrm_sib,
.rrmi_sib,
.pseudo_cmov_nz_or_p_rm_sib,
.pseudo_set_z_and_np_m_sib,
.pseudo_set_nz_or_p_m_sib,
=> lower.mir.extraData(Mir.MemorySib, payload).data.decode(),
.rm_rip,
.rm_rip_cc,
.rmi_rip,
.m_rip,
.m_rip_cc,
.mi_rip_u,
.mi_rip_s,
.mr_rip,
.mrr_rip,
.mri_rip,
.lock_m_rip,
.lock_mi_rip_u,
.lock_mi_rip_s,
.lock_mr_rip,
.rrm_rip,
.rrmi_rip,
.pseudo_cmov_nz_or_p_rm_rip,
.pseudo_set_z_and_np_m_rip,
.pseudo_set_nz_or_p_m_rip,
=> lower.mir.extraData(Mir.MemoryRip, payload).data.decode(),
.rax_moffs,
.moffs_rax,
.lock_moffs_rax,
=> lower.mir.extraData(Mir.MemoryMoffs, payload).data.decode(),
else => unreachable,
});
}
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
lower.result[lower.result_len] = try Instruction.new(prefix, mnemonic, ops);
lower.result_len += 1;
fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
lower.result_relocs[lower.result_relocs_len] = .{
.lowered_inst_index = lower.result_insts_len,
.target = target,
};
lower.result_relocs_len += 1;
return Immediate.s(0);
}
fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
try lower.emit(switch (inst.ops) {
else => .none,
.lock_m_sib,
.lock_m_rip,
.lock_mi_sib_u,
.lock_mi_rip_u,
.lock_mi_sib_s,
.lock_mi_rip_s,
.lock_mr_sib,
.lock_mr_rip,
.lock_moffs_rax,
=> .lock,
}, switch (inst.tag) {
inline else => |tag| if (@hasField(Mnemonic, @tagName(tag)))
@field(Mnemonic, @tagName(tag))
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
lower.result_insts[lower.result_insts_len] = try Instruction.new(prefix, mnemonic, ops);
lower.result_insts_len += 1;
}
fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
const fixes = switch (inst.ops) {
.none => inst.data.none.fixes,
.inst => inst.data.inst.fixes,
.i_s, .i_u => inst.data.i.fixes,
.r => inst.data.r.fixes,
.rr => inst.data.rr.fixes,
.rrr => inst.data.rrr.fixes,
.rrri => inst.data.rrri.fixes,
.rri_s, .rri_u => inst.data.rri.fixes,
.ri_s, .ri_u => inst.data.ri.fixes,
.ri64, .rm_sib, .rm_rip, .mr_sib, .mr_rip => inst.data.rx.fixes,
.mrr_sib, .mrr_rip, .rrm_sib, .rrm_rip => inst.data.rrx.fixes,
.rmi_sib, .rmi_rip, .mri_sib, .mri_rip => inst.data.rix.fixes,
.rrmi_sib, .rrmi_rip => inst.data.rrix.fixes,
.mi_sib_u, .mi_rip_u, .mi_sib_s, .mi_rip_s => inst.data.x.fixes,
.m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes,
.extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._,
else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
};
try lower.emit(switch (fixes) {
inline else => |tag| comptime if (std.mem.indexOfScalar(u8, @tagName(tag), ' ')) |space|
@field(Prefix, @tagName(tag)[0..space])
else
unreachable,
.none,
}, mnemonic: {
comptime var max_len = 0;
inline for (@typeInfo(Mnemonic).Enum.fields) |field| max_len = @max(field.name.len, max_len);
var buf: [max_len]u8 = undefined;
const fixes_name = @tagName(fixes);
const pattern = fixes_name[if (std.mem.indexOfScalar(u8, fixes_name, ' ')) |i| i + 1 else 0..];
const wildcard_i = std.mem.indexOfScalar(u8, pattern, '_').?;
const parts = .{ pattern[0..wildcard_i], @tagName(inst.tag), pattern[wildcard_i + 1 ..] };
const err_msg = "unsupported mnemonic: ";
const mnemonic = std.fmt.bufPrint(&buf, "{s}{s}{s}", parts) catch
return lower.fail(err_msg ++ "'{s}{s}{s}'", parts);
break :mnemonic std.meta.stringToEnum(Mnemonic, mnemonic) orelse
return lower.fail(err_msg ++ "'{s}'", .{mnemonic});
}, switch (inst.ops) {
.none => &.{},
.inst => &.{
.{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) },
},
.i_s, .i_u => &.{
.{ .imm = lower.imm(inst.ops, inst.data.i) },
.{ .imm = lower.imm(inst.ops, inst.data.i.i) },
},
.r => &.{
.{ .reg = inst.data.r },
.{ .reg = inst.data.r.r1 },
},
.rr => &.{
.{ .reg = inst.data.rr.r1 },
@ -297,12 +333,18 @@ fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .reg = inst.data.rrr.r2 },
.{ .reg = inst.data.rrr.r3 },
},
.rrri => &.{
.{ .reg = inst.data.rrri.r1 },
.{ .reg = inst.data.rrri.r2 },
.{ .reg = inst.data.rrri.r3 },
.{ .imm = lower.imm(inst.ops, inst.data.rrri.i) },
},
.ri_s, .ri_u => &.{
.{ .reg = inst.data.ri.r },
.{ .reg = inst.data.ri.r1 },
.{ .imm = lower.imm(inst.ops, inst.data.ri.i) },
},
.ri64 => &.{
.{ .reg = inst.data.rx.r },
.{ .reg = inst.data.rx.r1 },
.{ .imm = lower.imm(inst.ops, inst.data.rx.payload) },
},
.rri_s, .rri_u => &.{
@ -310,33 +352,28 @@ fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .reg = inst.data.rri.r2 },
.{ .imm = lower.imm(inst.ops, inst.data.rri.i) },
},
.m_sib, .lock_m_sib, .m_rip, .lock_m_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
.m_sib, .m_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
},
.mi_sib_s,
.lock_mi_sib_s,
.mi_sib_u,
.lock_mi_sib_u,
.mi_rip_u,
.lock_mi_rip_u,
.mi_rip_s,
.lock_mi_rip_s,
=> &.{
.{ .mem = lower.mem(inst.ops, inst.data.ix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.ix.i) },
.mi_sib_s, .mi_sib_u, .mi_rip_u, .mi_rip_s => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload + 1) },
.{ .imm = lower.imm(
inst.ops,
lower.mir.extraData(Mir.Imm32, inst.data.x.payload).data.imm,
) },
},
.rm_sib, .rm_rip => &.{
.{ .reg = inst.data.rx.r },
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
},
.rmi_sib, .rmi_rip => &.{
.{ .reg = inst.data.rix.r },
.{ .reg = inst.data.rix.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
},
.mr_sib, .lock_mr_sib, .mr_rip, .lock_mr_rip => &.{
.mr_sib, .mr_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .reg = inst.data.rx.r },
.{ .reg = inst.data.rx.r1 },
},
.mrr_sib, .mrr_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
@ -345,137 +382,60 @@ fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
},
.mri_sib, .mri_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) },
.{ .reg = inst.data.rix.r },
.{ .reg = inst.data.rix.r1 },
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
});
}
fn mirString(lower: *Lower, inst: Mir.Inst) Error!void {
switch (inst.ops) {
.string => try lower.emit(switch (inst.data.string.repeat) {
inline else => |repeat| @field(Prefix, @tagName(repeat)),
}, switch (inst.tag) {
inline .cmps, .lods, .movs, .scas, .stos => |tag| switch (inst.data.string.width) {
inline else => |width| @field(Mnemonic, @tagName(tag) ++ @tagName(width)),
},
else => unreachable,
}, &.{}),
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
}
}
fn mirCmpxchgBytes(lower: *Lower, inst: Mir.Inst) Error!void {
const ops: [1]Operand = switch (inst.ops) {
.m_sib, .lock_m_sib, .m_rip, .lock_m_rip => .{
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
.rrm_sib, .rrm_rip => &.{
.{ .reg = inst.data.rrx.r1 },
.{ .reg = inst.data.rrx.r2 },
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
},
.rrmi_sib, .rrmi_rip => &.{
.{ .reg = inst.data.rrix.r1 },
.{ .reg = inst.data.rrix.r2 },
.{ .mem = lower.mem(inst.ops, inst.data.rrix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
};
try lower.emit(switch (inst.ops) {
.m_sib, .m_rip => .none,
.lock_m_sib, .lock_m_rip => .lock,
else => unreachable,
}, switch (@divExact(ops[0].bitSize(), 8)) {
8 => .cmpxchg8b,
16 => .cmpxchg16b,
else => return lower.fail("invalid operand for {s}", .{@tagName(inst.tag)}),
}, &ops);
}
fn mirMovMoffs(lower: *Lower, inst: Mir.Inst) Error!void {
try lower.emit(switch (inst.ops) {
.rax_moffs, .moffs_rax => .none,
.lock_moffs_rax => .lock,
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
}, .mov, switch (inst.ops) {
.rax_moffs => &.{
.{ .reg = .rax },
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
},
.moffs_rax, .lock_moffs_rax => &.{
.{ .mem = lower.mem(inst.ops, inst.data.payload) },
.moffs_rax => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
.{ .reg = .rax },
},
else => unreachable,
.extern_fn_reloc => &.{
.{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
},
.got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
_ = lower.reloc(switch (inst.ops) {
.got_reloc => .{ .linker_got = extra },
.direct_reloc => .{ .linker_direct = extra },
.import_reloc => .{ .linker_import = extra },
.tlv_reloc => .{ .linker_tlv = extra },
else => unreachable,
});
break :ops &.{
.{ .reg = reg },
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
};
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
});
}
fn mirMovsx(lower: *Lower, inst: Mir.Inst) Error!void {
const ops: [2]Operand = switch (inst.ops) {
.rr => .{
.{ .reg = inst.data.rr.r1 },
.{ .reg = inst.data.rr.r2 },
},
.rm_sib, .rm_rip => .{
.{ .reg = inst.data.rx.r },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
};
try lower.emit(.none, switch (ops[0].bitSize()) {
32, 64 => switch (ops[1].bitSize()) {
32 => .movsxd,
else => .movsx,
},
else => .movsx,
}, &ops);
}
fn mirCmovcc(lower: *Lower, inst: Mir.Inst) Error!void {
switch (inst.ops) {
.rr_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rr_cc.cc), &.{
.{ .reg = inst.data.rr_cc.r1 },
.{ .reg = inst.data.rr_cc.r2 },
}),
.rm_sib_cc, .rm_rip_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rx_cc.cc), &.{
.{ .reg = inst.data.rx_cc.r },
.{ .mem = lower.mem(inst.ops, inst.data.rx_cc.payload) },
}),
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
}
}
fn mirSetcc(lower: *Lower, inst: Mir.Inst) Error!void {
switch (inst.ops) {
.r_cc => try lower.emit(.none, mnem_cc(.set, inst.data.r_cc.cc), &.{
.{ .reg = inst.data.r_cc.r },
}),
.m_sib_cc, .m_rip_cc => try lower.emit(.none, mnem_cc(.set, inst.data.x_cc.cc), &.{
.{ .mem = lower.mem(inst.ops, inst.data.x_cc.payload) },
}),
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
}
}
fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst, comptime mnemonic: Mnemonic) Error!void {
const reg_list = Mir.RegisterList.fromInt(inst.data.payload);
fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*);
var it = reg_list.iterator(.{ .direction = switch (mnemonic) {
var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {
.push => .reverse,
.pop => .forward,
else => unreachable,
} });
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
}
fn mirLeaLinker(lower: *Lower, inst: Mir.Inst) Error!void {
const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
const reg = @intToEnum(Register, metadata.reg);
try lower.emit(.none, .lea, &.{
.{ .reg = reg },
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
});
}
fn mirMovLinker(lower: *Lower, inst: Mir.Inst) Error!void {
const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data;
const reg = @intToEnum(Register, metadata.reg);
try lower.emit(.none, .mov, &.{
.{ .reg = reg },
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
});
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{
.reg = callee_preserved_regs[i],
}});
}
const abi = @import("abi.zig");

File diff suppressed because it is too large Load Diff

View File

@ -6,9 +6,6 @@ const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const DW = std.dwarf;
pub const StringRepeat = enum(u3) { none, rep, repe, repz, repne, repnz };
pub const StringWidth = enum(u2) { b, w, d, q };
/// EFLAGS condition codes
pub const Condition = enum(u5) {
/// above
@ -72,6 +69,12 @@ pub const Condition = enum(u5) {
/// zero
z,
// Pseudo conditions
/// zero and not parity
z_and_np,
/// not zero or parity
nz_or_p,
/// Converts a std.math.CompareOperator into a condition flag,
/// i.e. returns the condition that is true iff the result of the
/// comparison is true. Assumes signed comparison
@ -143,6 +146,9 @@ pub const Condition = enum(u5) {
.po => .pe,
.s => .ns,
.z => .nz,
.z_and_np => .nz_or_p,
.nz_or_p => .z_and_np,
};
}
};
@ -476,7 +482,9 @@ pub const Memory = union(enum) {
dword,
qword,
tbyte,
dqword,
xword,
yword,
zword,
pub fn fromSize(size: u32) PtrSize {
return switch (size) {
@ -484,7 +492,9 @@ pub const Memory = union(enum) {
2...2 => .word,
3...4 => .dword,
5...8 => .qword,
9...16 => .dqword,
9...16 => .xword,
17...32 => .yword,
33...64 => .zword,
else => unreachable,
};
}
@ -496,7 +506,9 @@ pub const Memory = union(enum) {
32 => .dword,
64 => .qword,
80 => .tbyte,
128 => .dqword,
128 => .xword,
256 => .yword,
512 => .zword,
else => unreachable,
};
}
@ -508,7 +520,9 @@ pub const Memory = union(enum) {
.dword => 32,
.qword => 64,
.tbyte => 80,
.dqword => 128,
.xword => 128,
.yword => 256,
.zword => 512,
};
}
};

View File

@ -151,15 +151,12 @@ pub const Instruction = struct {
moffs.offset,
}),
},
.imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.bitSize())}),
.imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.immBitSize())}),
}
}
pub fn fmtPrint(op: Operand, enc_op: Encoding.Op) std.fmt.Formatter(fmt) {
return .{ .data = .{
.op = op,
.enc_op = enc_op,
} };
return .{ .data = .{ .op = op, .enc_op = enc_op } };
}
};
@ -209,10 +206,16 @@ pub const Instruction = struct {
const enc = inst.encoding;
const data = enc.data;
try inst.encodeLegacyPrefixes(encoder);
try inst.encodeMandatoryPrefix(encoder);
try inst.encodeRexPrefix(encoder);
try inst.encodeOpcode(encoder);
if (data.mode.isVex()) {
try inst.encodeVexPrefix(encoder);
const opc = inst.encoding.opcode();
try encoder.opcode_1byte(opc[opc.len - 1]);
} else {
try inst.encodeLegacyPrefixes(encoder);
try inst.encodeMandatoryPrefix(encoder);
try inst.encodeRexPrefix(encoder);
try inst.encodeOpcode(encoder);
}
switch (data.op_en) {
.np, .o => {},
@ -222,25 +225,28 @@ pub const Instruction = struct {
.td => try encoder.imm64(inst.ops[0].mem.moffs.offset),
else => {
const mem_op = switch (data.op_en) {
.m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0],
.rm, .rmi => inst.ops[1],
.m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
.rm, .rmi, .vmi => inst.ops[1],
.rvm, .rvmi => inst.ops[2],
else => unreachable,
};
switch (mem_op) {
.reg => |reg| {
const rm = switch (data.op_en) {
.m, .mi, .m1, .mc => enc.modRmExt(),
.m, .mi, .m1, .mc, .vmi => enc.modRmExt(),
.mr, .mri, .mrc => inst.ops[1].reg.lowEnc(),
.rm, .rmi => inst.ops[0].reg.lowEnc(),
.rm, .rmi, .rvm, .rvmi => inst.ops[0].reg.lowEnc(),
.mvr => inst.ops[2].reg.lowEnc(),
else => unreachable,
};
try encoder.modRm_direct(rm, reg.lowEnc());
},
.mem => |mem| {
const op = switch (data.op_en) {
.m, .mi, .m1, .mc => .none,
.m, .mi, .m1, .mc, .vmi => .none,
.mr, .mri, .mrc => inst.ops[1],
.rm, .rmi => inst.ops[0],
.rm, .rmi, .rvm, .rvmi => inst.ops[0],
.mvr => inst.ops[2],
else => unreachable,
};
try encodeMemory(enc, mem, op, encoder);
@ -250,7 +256,8 @@ pub const Instruction = struct {
switch (data.op_en) {
.mi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder),
.rmi, .mri => try encodeImm(inst.ops[2].imm, data.ops[2], encoder),
.rmi, .mri, .vmi => try encodeImm(inst.ops[2].imm, data.ops[2], encoder),
.rvmi => try encodeImm(inst.ops[3].imm, data.ops[3], encoder),
else => {},
}
},
@ -282,11 +289,9 @@ pub const Instruction = struct {
.rep, .repe, .repz => legacy.prefix_f3 = true,
}
if (data.mode == .none) {
const bit_size = enc.operandBitSize();
if (bit_size == 16) {
legacy.set16BitOverride();
}
switch (data.mode) {
.short, .rex_short => legacy.set16BitOverride(),
else => {},
}
const segment_override: ?Register = switch (op_en) {
@ -309,6 +314,7 @@ pub const Instruction = struct {
}
else
null,
.vmi, .rvm, .rvmi, .mvr => unreachable,
};
if (segment_override) |seg| {
legacy.setSegmentOverride(seg);
@ -322,10 +328,7 @@ pub const Instruction = struct {
var rex = Rex{};
rex.present = inst.encoding.data.mode == .rex;
switch (inst.encoding.data.mode) {
.long, .sse_long, .sse2_long => rex.w = true,
else => {},
}
rex.w = inst.encoding.data.mode == .long;
switch (op_en) {
.np, .i, .zi, .fd, .td, .d => {},
@ -346,11 +349,71 @@ pub const Instruction = struct {
rex.b = b_x_op.isBaseExtended();
rex.x = b_x_op.isIndexExtended();
},
.vmi, .rvm, .rvmi, .mvr => unreachable,
}
try encoder.rex(rex);
}
fn encodeVexPrefix(inst: Instruction, encoder: anytype) !void {
const op_en = inst.encoding.data.op_en;
const opc = inst.encoding.opcode();
const mand_pre = inst.encoding.mandatoryPrefix();
var vex = Vex{};
vex.w = inst.encoding.data.mode.isLong();
switch (op_en) {
.np, .i, .zi, .fd, .td, .d => {},
.o, .oi => vex.b = inst.ops[0].reg.isExtended(),
.m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .vmi, .rvm, .rvmi, .mvr => {
const r_op = switch (op_en) {
.rm, .rmi, .rvm, .rvmi => inst.ops[0],
.mr, .mri, .mrc => inst.ops[1],
.mvr => inst.ops[2],
.m, .mi, .m1, .mc, .vmi => .none,
else => unreachable,
};
vex.r = r_op.isBaseExtended();
const b_x_op = switch (op_en) {
.rm, .rmi, .vmi => inst.ops[1],
.m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
.rvm, .rvmi => inst.ops[2],
else => unreachable,
};
vex.b = b_x_op.isBaseExtended();
vex.x = b_x_op.isIndexExtended();
},
}
vex.l = inst.encoding.data.mode.isVecLong();
vex.p = if (mand_pre) |mand| switch (mand) {
0x66 => .@"66",
0xf2 => .f2,
0xf3 => .f3,
else => unreachable,
} else .none;
const leading: usize = if (mand_pre) |_| 1 else 0;
assert(opc[leading] == 0x0f);
vex.m = switch (opc[leading + 1]) {
else => .@"0f",
0x38 => .@"0f38",
0x3a => .@"0f3a",
};
switch (op_en) {
else => {},
.vmi => vex.v = inst.ops[0].reg,
.rvm, .rvmi => vex.v = inst.ops[1].reg,
}
try encoder.vex(vex);
}
fn encodeMandatoryPrefix(inst: Instruction, encoder: anytype) !void {
const prefix = inst.encoding.mandatoryPrefix() orelse return;
try encoder.opcode_1byte(prefix);
@ -443,8 +506,8 @@ pub const Instruction = struct {
}
fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void {
const raw = imm.asUnsigned(kind.bitSize());
switch (kind.bitSize()) {
const raw = imm.asUnsigned(kind.immBitSize());
switch (kind.immBitSize()) {
8 => try encoder.imm8(@intCast(u8, raw)),
16 => try encoder.imm16(@intCast(u16, raw)),
32 => try encoder.imm32(@intCast(u32, raw)),
@ -562,17 +625,48 @@ fn Encoder(comptime T: type, comptime opts: Options) type {
/// or one of reg, index, r/m, base, or opcode-reg might be extended.
///
/// See struct `Rex` for a description of each field.
pub fn rex(self: Self, byte: Rex) !void {
if (!byte.present and !byte.isSet()) return;
pub fn rex(self: Self, fields: Rex) !void {
if (!fields.present and !fields.isSet()) return;
var value: u8 = 0b0100_0000;
var byte: u8 = 0b0100_0000;
if (byte.w) value |= 0b1000;
if (byte.r) value |= 0b0100;
if (byte.x) value |= 0b0010;
if (byte.b) value |= 0b0001;
if (fields.w) byte |= 0b1000;
if (fields.r) byte |= 0b0100;
if (fields.x) byte |= 0b0010;
if (fields.b) byte |= 0b0001;
try self.writer.writeByte(value);
try self.writer.writeByte(byte);
}
/// Encodes a VEX prefix given all the fields
///
/// See struct `Vex` for a description of each field.
pub fn vex(self: Self, fields: Vex) !void {
if (fields.is3Byte()) {
try self.writer.writeByte(0b1100_0100);
try self.writer.writeByte(
@as(u8, ~@boolToInt(fields.r)) << 7 |
@as(u8, ~@boolToInt(fields.x)) << 6 |
@as(u8, ~@boolToInt(fields.b)) << 5 |
@as(u8, @enumToInt(fields.m)) << 0,
);
try self.writer.writeByte(
@as(u8, @boolToInt(fields.w)) << 7 |
@as(u8, ~fields.v.enc()) << 3 |
@as(u8, @boolToInt(fields.l)) << 2 |
@as(u8, @enumToInt(fields.p)) << 0,
);
} else {
try self.writer.writeByte(0b1100_0101);
try self.writer.writeByte(
@as(u8, ~@boolToInt(fields.r)) << 7 |
@as(u8, ~fields.v.enc()) << 3 |
@as(u8, @boolToInt(fields.l)) << 2 |
@as(u8, @enumToInt(fields.p)) << 0,
);
}
}
// ------
@ -848,6 +942,31 @@ pub const Rex = struct {
}
};
pub const Vex = struct {
w: bool = false,
r: bool = false,
x: bool = false,
b: bool = false,
l: bool = false,
p: enum(u2) {
none = 0b00,
@"66" = 0b01,
f3 = 0b10,
f2 = 0b11,
} = .none,
m: enum(u5) {
@"0f" = 0b0_0001,
@"0f38" = 0b0_0010,
@"0f3a" = 0b0_0011,
_,
} = .@"0f",
v: Register = .ymm0,
pub fn is3Byte(vex: Vex) bool {
return vex.w or vex.x or vex.b or vex.m != .@"0f";
}
};
// Tests
fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
assert(expected.len > 0);

File diff suppressed because it is too large Load Diff

View File

@ -526,7 +526,7 @@ pub const DeclState = struct {
.ErrorUnion => {
const error_ty = ty.errorUnionSet();
const payload_ty = ty.errorUnionPayload();
const payload_align = payload_ty.abiAlignment(target);
const payload_align = if (payload_ty.isNoReturn()) 0 else payload_ty.abiAlignment(target);
const error_align = Type.anyerror.abiAlignment(target);
const abi_size = ty.abiSize(target);
const payload_off = if (error_align >= payload_align) Type.anyerror.abiSize(target) else 0;
@ -540,31 +540,35 @@ pub const DeclState = struct {
const name = try ty.nameAllocArena(arena, module);
try dbg_info_buffer.writer().print("{s}\x00", .{name});
// DW.AT.member
try dbg_info_buffer.ensureUnusedCapacity(7);
dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
// DW.AT.name, DW.FORM.string
dbg_info_buffer.appendSliceAssumeCapacity("value");
dbg_info_buffer.appendAssumeCapacity(0);
// DW.AT.type, DW.FORM.ref4
var index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
// DW.AT.data_member_location, DW.FORM.sdata
try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);
if (!payload_ty.isNoReturn()) {
// DW.AT.member
try dbg_info_buffer.ensureUnusedCapacity(7);
dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
// DW.AT.name, DW.FORM.string
dbg_info_buffer.appendSliceAssumeCapacity("value");
dbg_info_buffer.appendAssumeCapacity(0);
// DW.AT.type, DW.FORM.ref4
const index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
// DW.AT.data_member_location, DW.FORM.sdata
try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);
}
// DW.AT.member
try dbg_info_buffer.ensureUnusedCapacity(5);
dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
// DW.AT.name, DW.FORM.string
dbg_info_buffer.appendSliceAssumeCapacity("err");
dbg_info_buffer.appendAssumeCapacity(0);
// DW.AT.type, DW.FORM.ref4
index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(u32, index));
// DW.AT.data_member_location, DW.FORM.sdata
try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);
{
// DW.AT.member
try dbg_info_buffer.ensureUnusedCapacity(5);
dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
// DW.AT.name, DW.FORM.string
dbg_info_buffer.appendSliceAssumeCapacity("err");
dbg_info_buffer.appendAssumeCapacity(0);
// DW.AT.type, DW.FORM.ref4
const index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(u32, index));
// DW.AT.data_member_location, DW.FORM.sdata
try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);
}
// DW.AT.structure_type delimit children
try dbg_info_buffer.append(0);

View File

@ -29,7 +29,6 @@ test "inf >= 1" {
test "isNan(nan * 1)" {
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_x86_64) return error.SkipZigTest; // TODO
const nan_times_one = comptime std.math.nan(f64) * 1;
try std.testing.expect(std.math.isNan(nan_times_one));
@ -37,7 +36,6 @@ test "isNan(nan * 1)" {
test "runtime isNan(nan * 1)" {
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_x86_64) return error.SkipZigTest; // TODO
const nan_times_one = std.math.nan(f64) * 1;
try std.testing.expect(std.math.isNan(nan_times_one));
@ -45,7 +43,6 @@ test "runtime isNan(nan * 1)" {
test "isNan(nan * 0)" {
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_x86_64) return error.SkipZigTest; // TODO
const nan_times_zero = comptime std.math.nan(f64) * 0;
try std.testing.expect(std.math.isNan(nan_times_zero));
@ -55,7 +52,6 @@ test "isNan(nan * 0)" {
test "isNan(inf * 0)" {
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_x86_64) return error.SkipZigTest; // TODO
const inf_times_zero = comptime std.math.inf(f64) * 0;
try std.testing.expect(std.math.isNan(inf_times_zero));
@ -65,7 +61,6 @@ test "isNan(inf * 0)" {
test "runtime isNan(nan * 0)" {
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_x86_64) return error.SkipZigTest; // TODO
const nan_times_zero = std.math.nan(f64) * 0;
try std.testing.expect(std.math.isNan(nan_times_zero));
@ -75,7 +70,6 @@ test "runtime isNan(nan * 0)" {
test "runtime isNan(inf * 0)" {
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_x86_64) return error.SkipZigTest; // TODO
const inf_times_zero = std.math.inf(f64) * 0;
try std.testing.expect(std.math.isNan(inf_times_zero));

View File

@ -9,7 +9,8 @@ fn ctz(x: anytype) usize {
test "fixed" {
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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .bmi)) 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

View File

@ -757,7 +757,6 @@ test "error union of noreturn used with if" {
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
NoReturn.a = 64;
@ -772,7 +771,6 @@ test "error union of noreturn used with try" {
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
NoReturn.a = 64;
@ -784,7 +782,6 @@ test "error union of noreturn used with catch" {
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
NoReturn.a = 64;

View File

@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
const builtin = @import("builtin");
test "@fieldParentPtr non-first field" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
try testParentFieldPtr(&foo.c);

View File

@ -8,6 +8,8 @@ const has_f80_rt = switch (builtin.cpu.arch) {
.x86_64, .x86 => true,
else => false,
};
const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and
!std.Target.x86.featureSetHas(builtin.cpu.features, .f16c);
const epsilon_16 = 0.001;
const epsilon = 0.000001;
@ -52,7 +54,7 @@ fn testFloatComparisons() !void {
}
test "different sized float comparisons" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -134,7 +136,6 @@ fn testSqrt() !void {
test "@sqrt with 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -152,7 +153,7 @@ fn testSqrtWithVectors() !void {
}
test "more @sqrt f16 tests" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -202,7 +203,7 @@ fn testSqrtLegacy(comptime T: type, x: T) !void {
}
test "@sin" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -241,7 +242,7 @@ fn testSinWithVectors() !void {
}
test "@cos" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -280,7 +281,7 @@ fn testCosWithVectors() !void {
}
test "@exp" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -318,7 +319,7 @@ fn testExpWithVectors() !void {
}
test "@exp2" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -403,7 +404,7 @@ test "@log with @vectors" {
}
test "@log2" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) 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
@ -445,7 +446,7 @@ fn testLog2WithVectors() !void {
}
test "@log10" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) 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
@ -617,7 +618,8 @@ fn testFloor() !void {
test "@floor with 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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -707,7 +709,8 @@ fn testCeil() !void {
test "@ceil with 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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -797,7 +800,8 @@ fn testTrunc() !void {
test "@trunc with 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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -878,7 +882,7 @@ fn testTruncLegacy(comptime T: type, x: T) !void {
}
test "negation f16" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -1037,7 +1041,6 @@ test "comptime_float zero divided by zero produces zero" {
}
test "nan negation f16" {
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

View File

@ -77,7 +77,8 @@ fn testClz() !void {
}
test "@clz big ints" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .lzcnt)) 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
@ -398,7 +399,8 @@ fn testBinaryNot128(comptime Type: type, x: Type) !void {
test "division" {
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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) 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

View File

@ -1,8 +1,12 @@
const std = @import("std");
const builtin = @import("builtin");
const expect = @import("std").testing.expect;
const expect = std.testing.expect;
const no_x86_64_hardware_fma_support = builtin.zig_backend == .stage2_x86_64 and
!std.Target.x86.featureSetHas(builtin.cpu.features, .fma);
test "@mulAdd" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_fma_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -116,7 +120,7 @@ fn vector32() !void {
test "vector f32" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_fma_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@ -139,7 +143,7 @@ fn vector64() !void {
test "vector f64" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (no_x86_64_hardware_fma_support) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

View File

@ -168,7 +168,8 @@ test "array to vector" {
test "array to vector with element type coercion" {
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
!comptime std.Target.x86.featureSetHas(builtin.cpu.features, .f16c)) 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