mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #15597 from jacobly0/x86_64-behavior
x86_64: more behavior than ever before
This commit is contained in:
commit
f40539e5d8
@ -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
@ -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();
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
@ -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,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -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
@ -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);
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user