machinst: allow passing constant information to the instruction emitter;

A new associated type Info is added to MachInstEmit, which is the
immutable counterpart to State. It can't easily be constructed from an
ABICallee, since it would require adding an associated type to the
latter, and making so leaks the associated type in a lot of places in
the code base and makes the code harder to read. Instead, the EmitInfo
state can simply be passed to the `Vcode::emit` function directly.
This commit is contained in:
Benjamin Bouvier
2020-10-02 16:26:44 +02:00
parent 3778fa025c
commit c5bbc87498
14 changed files with 280 additions and 185 deletions

View File

@@ -463,10 +463,26 @@ impl EmitState {
} }
} }
/// Constant state used during function compilation.
pub struct EmitInfo(settings::Flags);
impl EmitInfo {
pub(crate) fn new(flags: settings::Flags) -> Self {
Self(flags)
}
}
impl MachInstEmitInfo for EmitInfo {
fn flags(&self) -> &settings::Flags {
&self.0
}
}
impl MachInstEmit for Inst { impl MachInstEmit for Inst {
type State = EmitState; type State = EmitState;
type Info = EmitInfo;
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut EmitState) { fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
// N.B.: we *must* not exceed the "worst-case size" used to compute // N.B.: we *must* not exceed the "worst-case size" used to compute
// where to insert islands, except when islands are explicitly triggered // where to insert islands, except when islands are explicitly triggered
// (with an `EmitIsland`). We check this in debug builds. This is `mut` // (with an `EmitIsland`). We check this in debug builds. This is `mut`
@@ -742,7 +758,7 @@ impl MachInstEmit for Inst {
let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state); let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
// ldst encoding helpers take Reg, not Writable<Reg>. // ldst encoding helpers take Reg, not Writable<Reg>.
@@ -887,7 +903,7 @@ impl MachInstEmit for Inst {
let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state); let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
let (op, bits) = match self { let (op, bits) = match self {
@@ -1500,11 +1516,11 @@ impl MachInstEmit for Inst {
mem: AMode::Label(MemLabel::PCRel(8)), mem: AMode::Label(MemLabel::PCRel(8)),
srcloc: None, srcloc: None,
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(8), dest: BranchTarget::ResolvedOffset(8),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
sink.put4(const_data.to_bits()); sink.put4(const_data.to_bits());
} }
&Inst::LoadFpuConst64 { rd, const_data } => { &Inst::LoadFpuConst64 { rd, const_data } => {
@@ -1513,11 +1529,11 @@ impl MachInstEmit for Inst {
mem: AMode::Label(MemLabel::PCRel(8)), mem: AMode::Label(MemLabel::PCRel(8)),
srcloc: None, srcloc: None,
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(12), dest: BranchTarget::ResolvedOffset(12),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
sink.put8(const_data.to_bits()); sink.put8(const_data.to_bits());
} }
&Inst::LoadFpuConst128 { rd, const_data } => { &Inst::LoadFpuConst128 { rd, const_data } => {
@@ -1526,11 +1542,11 @@ impl MachInstEmit for Inst {
mem: AMode::Label(MemLabel::PCRel(8)), mem: AMode::Label(MemLabel::PCRel(8)),
srcloc: None, srcloc: None,
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(20), dest: BranchTarget::ResolvedOffset(20),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
for i in const_data.to_le_bytes().iter() { for i in const_data.to_le_bytes().iter() {
sink.put1(*i); sink.put1(*i);
@@ -1854,7 +1870,7 @@ impl MachInstEmit for Inst {
if top22 != 0 { if top22 != 0 {
sink.put4(enc_extend(top22, rd, rn)); sink.put4(enc_extend(top22, rd, rn));
} else { } else {
Inst::mov32(rd, rn).emit(sink, flags, state); Inst::mov32(rd, rn).emit(sink, emit_info, state);
} }
} }
&Inst::Extend { &Inst::Extend {
@@ -1877,7 +1893,7 @@ impl MachInstEmit for Inst {
rn: zero_reg(), rn: zero_reg(),
rm: rd.to_reg(), rm: rd.to_reg(),
}; };
sub_inst.emit(sink, flags, state); sub_inst.emit(sink, emit_info, state);
} }
&Inst::Extend { &Inst::Extend {
rd, rd,
@@ -1964,7 +1980,7 @@ impl MachInstEmit for Inst {
sink.use_label_at_offset(off, label, LabelUse::Branch19); sink.use_label_at_offset(off, label, LabelUse::Branch19);
// udf // udf
let trap = Inst::Udf { trap_info }; let trap = Inst::Udf { trap_info };
trap.emit(sink, flags, state); trap.emit(sink, emit_info, state);
// LABEL: // LABEL:
sink.bind_label(label); sink.bind_label(label);
} }
@@ -2022,10 +2038,10 @@ impl MachInstEmit for Inst {
// Save index in a tmp (the live range of ridx only goes to start of this // Save index in a tmp (the live range of ridx only goes to start of this
// sequence; rtmp1 or rtmp2 may overwrite it). // sequence; rtmp1 or rtmp2 may overwrite it).
let inst = Inst::gen_move(rtmp2, ridx, I64); let inst = Inst::gen_move(rtmp2, ridx, I64);
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
// Load address of jump table // Load address of jump table
let inst = Inst::Adr { rd: rtmp1, off: 16 }; let inst = Inst::Adr { rd: rtmp1, off: 16 };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
// Load value out of jump table // Load value out of jump table
let inst = Inst::SLoad32 { let inst = Inst::SLoad32 {
rd: rtmp2, rd: rtmp2,
@@ -2037,7 +2053,7 @@ impl MachInstEmit for Inst {
), ),
srcloc: None, // can't cause a user trap. srcloc: None, // can't cause a user trap.
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
// Add base of jump table to jump-table-sourced block offset // Add base of jump table to jump-table-sourced block offset
let inst = Inst::AluRRR { let inst = Inst::AluRRR {
alu_op: ALUOp::Add64, alu_op: ALUOp::Add64,
@@ -2045,14 +2061,14 @@ impl MachInstEmit for Inst {
rn: rtmp1.to_reg(), rn: rtmp1.to_reg(),
rm: rtmp2.to_reg(), rm: rtmp2.to_reg(),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
// Branch to computed address. (`targets` here is only used for successor queries // Branch to computed address. (`targets` here is only used for successor queries
// and is not needed for emission.) // and is not needed for emission.)
let inst = Inst::IndirectBr { let inst = Inst::IndirectBr {
rn: rtmp1.to_reg(), rn: rtmp1.to_reg(),
targets: vec![], targets: vec![],
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
// Emit jump table (table of 32-bit offsets). // Emit jump table (table of 32-bit offsets).
let jt_off = sink.cur_offset(); let jt_off = sink.cur_offset();
for &target in info.targets.iter() { for &target in info.targets.iter() {
@@ -2085,13 +2101,13 @@ impl MachInstEmit for Inst {
mem: AMode::Label(MemLabel::PCRel(8)), mem: AMode::Label(MemLabel::PCRel(8)),
srcloc: None, // can't cause a user trap. srcloc: None, // can't cause a user trap.
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(12), dest: BranchTarget::ResolvedOffset(12),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
sink.add_reloc(srcloc, Reloc::Abs8, name, offset); sink.add_reloc(srcloc, Reloc::Abs8, name, offset);
if flags.emit_all_ones_funcaddrs() { if emit_info.flags().emit_all_ones_funcaddrs() {
sink.put8(u64::max_value()); sink.put8(u64::max_value());
} else { } else {
sink.put8(0); sink.put8(0);
@@ -2100,7 +2116,7 @@ impl MachInstEmit for Inst {
&Inst::LoadAddr { rd, ref mem } => { &Inst::LoadAddr { rd, ref mem } => {
let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state); let (mem_insts, mem) = mem_finalize(sink.cur_offset(), mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
let (reg, offset) = match mem { let (reg, offset) = match mem {
@@ -2121,7 +2137,7 @@ impl MachInstEmit for Inst {
if offset == 0 { if offset == 0 {
let mov = Inst::mov(rd, reg); let mov = Inst::mov(rd, reg);
mov.emit(sink, flags, state); mov.emit(sink, emit_info, state);
} else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) { } else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) {
let add = Inst::AluRRImm12 { let add = Inst::AluRRImm12 {
alu_op, alu_op,
@@ -2129,7 +2145,7 @@ impl MachInstEmit for Inst {
rn: reg, rn: reg,
imm12, imm12,
}; };
add.emit(sink, flags, state); add.emit(sink, emit_info, state);
} else { } else {
// Use `tmp2` here: `reg` may be `spilltmp` if the `AMode` on this instruction // Use `tmp2` here: `reg` may be `spilltmp` if the `AMode` on this instruction
// was initially an `SPOffset`. Assert that `tmp2` is truly free to use. Note // was initially an `SPOffset`. Assert that `tmp2` is truly free to use. Note
@@ -2140,7 +2156,7 @@ impl MachInstEmit for Inst {
debug_assert!(reg != tmp2_reg()); debug_assert!(reg != tmp2_reg());
let tmp = writable_tmp2_reg(); let tmp = writable_tmp2_reg();
for insn in Inst::load_constant(tmp, abs_offset).into_iter() { for insn in Inst::load_constant(tmp, abs_offset).into_iter() {
insn.emit(sink, flags, state); insn.emit(sink, emit_info, state);
} }
let add = Inst::AluRRR { let add = Inst::AluRRR {
alu_op, alu_op,
@@ -2148,7 +2164,7 @@ impl MachInstEmit for Inst {
rn: reg, rn: reg,
rm: tmp.to_reg(), rm: tmp.to_reg(),
}; };
add.emit(sink, flags, state); add.emit(sink, emit_info, state);
} }
} }
&Inst::VirtualSPOffsetAdj { offset } => { &Inst::VirtualSPOffsetAdj { offset } => {
@@ -2165,7 +2181,7 @@ impl MachInstEmit for Inst {
let jmp = Inst::Jump { let jmp = Inst::Jump {
dest: BranchTarget::Label(jump_around_label), dest: BranchTarget::Label(jump_around_label),
}; };
jmp.emit(sink, flags, state); jmp.emit(sink, emit_info, state);
sink.emit_island(); sink.emit_island();
sink.bind_label(jump_around_label); sink.bind_label(jump_around_label);
} }

View File

@@ -9,7 +9,6 @@ use alloc::vec::Vec;
#[test] #[test]
fn test_aarch64_binemit() { fn test_aarch64_binemit() {
let flags = settings::Flags::new(settings::builder());
let mut insns = Vec::<(Inst, &str, &str)>::new(); let mut insns = Vec::<(Inst, &str, &str)>::new();
// N.B.: the architecture is little-endian, so when transcribing the 32-bit // N.B.: the architecture is little-endian, so when transcribing the 32-bit
@@ -4668,7 +4667,9 @@ fn test_aarch64_binemit() {
insns.push((Inst::Fence {}, "BF3B03D5", "dmb ish")); insns.push((Inst::Fence {}, "BF3B03D5", "dmb ish"));
let rru = create_reg_universe(&settings::Flags::new(settings::builder())); let flags = settings::Flags::new(settings::builder());
let rru = create_reg_universe(&flags);
let emit_info = EmitInfo::new(flags);
for (insn, expected_encoding, expected_printing) in insns { for (insn, expected_encoding, expected_printing) in insns {
println!( println!(
"AArch64: {:?}, {}, {}", "AArch64: {:?}, {}, {}",
@@ -4681,7 +4682,7 @@ fn test_aarch64_binemit() {
let mut sink = test_utils::TestCodeSink::new(); let mut sink = test_utils::TestCodeSink::new();
let mut buffer = MachBuffer::new(); let mut buffer = MachBuffer::new();
insn.emit(&mut buffer, &flags, &mut Default::default()); insn.emit(&mut buffer, &emit_info, &mut Default::default());
let buffer = buffer.finish(); let buffer = buffer.finish();
buffer.emit(&mut sink); buffer.emit(&mut sink);
let actual_encoding = &sink.stringify(); let actual_encoding = &sink.stringify();

View File

@@ -20,6 +20,8 @@ mod lower_inst;
use inst::create_reg_universe; use inst::create_reg_universe;
use self::inst::EmitInfo;
/// An AArch64 backend. /// An AArch64 backend.
pub struct AArch64Backend { pub struct AArch64Backend {
triple: Triple, triple: Triple,
@@ -45,8 +47,9 @@ impl AArch64Backend {
func: &Function, func: &Function,
flags: settings::Flags, flags: settings::Flags,
) -> CodegenResult<VCode<inst::Inst>> { ) -> CodegenResult<VCode<inst::Inst>> {
let emit_info = EmitInfo::new(flags.clone());
let abi = Box::new(abi::AArch64ABICallee::new(func, flags)?); let abi = Box::new(abi::AArch64ABICallee::new(func, flags)?);
compile::compile::<AArch64Backend>(func, self, abi) compile::compile::<AArch64Backend>(func, self, abi, emit_info)
} }
} }
@@ -58,6 +61,7 @@ impl MachBackend for AArch64Backend {
) -> CodegenResult<MachCompileResult> { ) -> CodegenResult<MachCompileResult> {
let flags = self.flags(); let flags = self.flags();
let vcode = self.compile_vcode(func, flags.clone())?; let vcode = self.compile_vcode(func, flags.clone())?;
let buffer = vcode.emit(); let buffer = vcode.emit();
let frame_size = vcode.frame_size(); let frame_size = vcode.frame_size();

View File

@@ -255,10 +255,27 @@ impl EmitState {
} }
} }
pub struct EmitInfo {
flags: settings::Flags,
}
impl EmitInfo {
pub(crate) fn new(flags: settings::Flags) -> Self {
EmitInfo { flags }
}
}
impl MachInstEmitInfo for EmitInfo {
fn flags(&self) -> &settings::Flags {
&self.flags
}
}
impl MachInstEmit for Inst { impl MachInstEmit for Inst {
type Info = EmitInfo;
type State = EmitState; type State = EmitState;
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut EmitState) { fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
let start_off = sink.cur_offset(); let start_off = sink.cur_offset();
match self { match self {
@@ -446,7 +463,7 @@ impl MachInstEmit for Inst {
} => { } => {
let (mem_insts, mem) = mem_finalize(mem, state); let (mem_insts, mem) = mem_finalize(mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
if let Some(srcloc) = srcloc { if let Some(srcloc) = srcloc {
// Register the offset at which the store instruction starts. // Register the offset at which the store instruction starts.
@@ -484,7 +501,7 @@ impl MachInstEmit for Inst {
} => { } => {
let (mem_insts, mem) = mem_finalize(mem, state); let (mem_insts, mem) = mem_finalize(mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
if let Some(srcloc) = srcloc { if let Some(srcloc) = srcloc {
// Register the offset at which the load instruction starts. // Register the offset at which the load instruction starts.
@@ -537,7 +554,7 @@ impl MachInstEmit for Inst {
&Inst::LoadAddr { rd, ref mem } => { &Inst::LoadAddr { rd, ref mem } => {
let (mem_insts, mem) = mem_finalize(mem, state); let (mem_insts, mem) = mem_finalize(mem, state);
for inst in mem_insts.into_iter() { for inst in mem_insts.into_iter() {
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
let inst = match mem { let inst = match mem {
AMode::RegReg(reg1, reg2, shift) => { AMode::RegReg(reg1, reg2, shift) => {
@@ -574,7 +591,7 @@ impl MachInstEmit for Inst {
} }
_ => unreachable!(), _ => unreachable!(),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
&Inst::Extend { &Inst::Extend {
rd, rd,
@@ -617,7 +634,7 @@ impl MachInstEmit for Inst {
rn: rm, rn: rm,
imm8: UImm8::maybe_from_i64(1).unwrap(), imm8: UImm8::maybe_from_i64(1).unwrap(),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
if signed { if signed {
let inst = Inst::AluRRImm8 { let inst = Inst::AluRRImm8 {
@@ -626,7 +643,7 @@ impl MachInstEmit for Inst {
rn: rd.to_reg(), rn: rd.to_reg(),
imm8: UImm8::maybe_from_i64(1).unwrap(), imm8: UImm8::maybe_from_i64(1).unwrap(),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
} }
} }
&Inst::Extend { .. } => { &Inst::Extend { .. } => {
@@ -638,7 +655,7 @@ impl MachInstEmit for Inst {
sink.put2(enc_16_it(cond, insts)); sink.put2(enc_16_it(cond, insts));
for inst in insts.iter() { for inst in insts.iter() {
inst.inst.emit(sink, flags, state); inst.inst.emit(sink, emit_info, state);
} }
} }
&Inst::Push { ref reg_list } => match reg_list.len() { &Inst::Push { ref reg_list } => match reg_list.len() {
@@ -703,7 +720,7 @@ impl MachInstEmit for Inst {
// continue: // continue:
// //
if start_off & 0x3 != 0 { if start_off & 0x3 != 0 {
Inst::Nop2.emit(sink, flags, state); Inst::Nop2.emit(sink, emit_info, state);
} }
assert_eq!(sink.cur_offset() & 0x3, 0); assert_eq!(sink.cur_offset() & 0x3, 0);
@@ -715,12 +732,12 @@ impl MachInstEmit for Inst {
bits: 32, bits: 32,
sign_extend: false, sign_extend: false,
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(4), dest: BranchTarget::ResolvedOffset(4),
}; };
inst.emit(sink, flags, state); inst.emit(sink, emit_info, state);
sink.add_reloc(srcloc, Reloc::Abs4, name, offset.into()); sink.add_reloc(srcloc, Reloc::Abs4, name, offset.into());
sink.put4(0); sink.put4(0);
@@ -779,7 +796,7 @@ impl MachInstEmit for Inst {
emit_32(enc_32_cond_branch(cond, dest), sink); emit_32(enc_32_cond_branch(cond, dest), sink);
let trap = Inst::Udf { trap_info }; let trap = Inst::Udf { trap_info };
trap.emit(sink, flags, state); trap.emit(sink, emit_info, state);
} }
&Inst::VirtualSPOffsetAdj { offset } => { &Inst::VirtualSPOffsetAdj { offset } => {
debug!( debug!(

View File

@@ -17,7 +17,7 @@ mod inst;
mod lower; mod lower;
mod lower_inst; mod lower_inst;
use inst::create_reg_universe; use inst::{create_reg_universe, EmitInfo};
/// An ARM32 backend. /// An ARM32 backend.
pub struct Arm32Backend { pub struct Arm32Backend {
@@ -44,8 +44,9 @@ impl Arm32Backend {
) -> CodegenResult<VCode<inst::Inst>> { ) -> CodegenResult<VCode<inst::Inst>> {
// This performs lowering to VCode, register-allocates the code, computes // This performs lowering to VCode, register-allocates the code, computes
// block layout and finalizes branches. The result is ready for binary emission. // block layout and finalizes branches. The result is ready for binary emission.
let emit_info = EmitInfo::new(flags.clone());
let abi = Box::new(abi::Arm32ABICallee::new(func, flags)?); let abi = Box::new(abi::Arm32ABICallee::new(func, flags)?);
compile::compile::<Arm32Backend>(func, self, abi) compile::compile::<Arm32Backend>(func, self, abi, emit_info)
} }
} }

View File

@@ -390,7 +390,7 @@ fn emit_simm(sink: &mut MachBuffer<Inst>, size: u8, simm32: u32) {
/// A small helper to generate a signed conversion instruction. /// A small helper to generate a signed conversion instruction.
fn emit_signed_cvt( fn emit_signed_cvt(
sink: &mut MachBuffer<Inst>, sink: &mut MachBuffer<Inst>,
flags: &settings::Flags, info: &EmitInfo,
state: &mut EmitState, state: &mut EmitState,
src: Reg, src: Reg,
dst: Writable<Reg>, dst: Writable<Reg>,
@@ -404,7 +404,7 @@ fn emit_signed_cvt(
SseOpcode::Cvtsi2ss SseOpcode::Cvtsi2ss
}; };
let inst = Inst::gpr_to_xmm(op, RegMem::reg(src), OperandSize::Size64, dst); let inst = Inst::gpr_to_xmm(op, RegMem::reg(src), OperandSize::Size64, dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
/// Emits a one way conditional jump if CC is set (true). /// Emits a one way conditional jump if CC is set (true).
@@ -472,7 +472,7 @@ fn one_way_jmp(sink: &mut MachBuffer<Inst>, cc: CC, label: MachLabel) {
pub(crate) fn emit( pub(crate) fn emit(
inst: &Inst, inst: &Inst,
sink: &mut MachBuffer<Inst>, sink: &mut MachBuffer<Inst>,
flags: &settings::Flags, info: &EmitInfo,
state: &mut EmitState, state: &mut EmitState,
) { ) {
match inst { match inst {
@@ -767,19 +767,19 @@ pub(crate) fn emit(
// idiv %divisor // idiv %divisor
// //
// $done: // $done:
debug_assert!(flags.avoid_div_traps()); debug_assert!(info.flags().avoid_div_traps());
// Check if the divisor is zero, first. // Check if the divisor is zero, first.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg()); let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerDivisionByZero, *loc); let inst = Inst::trap_if(CC::Z, TrapCode::IntegerDivisionByZero, *loc);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let (do_op, done_label) = if kind.is_signed() { let (do_op, done_label) = if kind.is_signed() {
// Now check if the divisor is -1. // Now check if the divisor is -1.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), divisor.to_reg()); let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), divisor.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let do_op = sink.get_label(); let do_op = sink.get_label();
@@ -796,10 +796,10 @@ pub(crate) fn emit(
0, 0,
Writable::from_reg(regs::rdx()), Writable::from_reg(regs::rdx()),
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done_label)); let inst = Inst::jmp_known(BranchTarget::Label(done_label));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
(Some(do_op), Some(done_label)) (Some(do_op), Some(done_label))
} else { } else {
@@ -808,18 +808,18 @@ pub(crate) fn emit(
let tmp = tmp.expect("temporary for i64 sdiv"); let tmp = tmp.expect("temporary for i64 sdiv");
let inst = Inst::imm(OperandSize::Size64, 0x8000000000000000, tmp); let inst = Inst::imm(OperandSize::Size64, 0x8000000000000000, tmp);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::cmp_rmi_r(8, RegMemImm::reg(tmp.to_reg()), regs::rax()); let inst = Inst::cmp_rmi_r(8, RegMemImm::reg(tmp.to_reg()), regs::rax());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0x80000000), regs::rax()); let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0x80000000), regs::rax());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
// If not equal, jump over the trap. // If not equal, jump over the trap.
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerOverflow, *loc); let inst = Inst::trap_if(CC::Z, TrapCode::IntegerOverflow, *loc);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
(Some(do_op), None) (Some(do_op), None)
} }
@@ -840,15 +840,15 @@ pub(crate) fn emit(
if kind.is_signed() { if kind.is_signed() {
// sign-extend the sign-bit of rax into rdx, for signed opcodes. // sign-extend the sign-bit of rax into rdx, for signed opcodes.
let inst = Inst::sign_extend_data(*size); let inst = Inst::sign_extend_data(*size);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
// zero for unsigned opcodes. // zero for unsigned opcodes.
let inst = Inst::imm(OperandSize::Size64, 0, Writable::from_reg(regs::rdx())); let inst = Inst::imm(OperandSize::Size64, 0, Writable::from_reg(regs::rdx()));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
let inst = Inst::div(*size, kind.is_signed(), RegMem::reg(divisor.to_reg()), *loc); let inst = Inst::div(*size, kind.is_signed(), RegMem::reg(divisor.to_reg()), *loc);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Lowering takes care of moving the result back into the right register, see comment // Lowering takes care of moving the result back into the right register, see comment
// there. // there.
@@ -1382,7 +1382,7 @@ pub(crate) fn emit(
SseOpcode::Movss SseOpcode::Movss
}; };
let inst = Inst::xmm_unary_rm_r(op, src.clone(), *dst); let inst = Inst::xmm_unary_rm_r(op, src.clone(), *dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(next); sink.bind_label(next);
} }
@@ -1623,7 +1623,7 @@ pub(crate) fn emit(
// Copy the index (and make sure to clear the high 32-bits lane of tmp2). // Copy the index (and make sure to clear the high 32-bits lane of tmp2).
let inst = Inst::movzx_rm_r(ExtMode::LQ, RegMem::reg(*idx), *tmp2, None); let inst = Inst::movzx_rm_r(ExtMode::LQ, RegMem::reg(*idx), *tmp2, None);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Load base address of jump table. // Load base address of jump table.
let start_of_jumptable = sink.get_label(); let start_of_jumptable = sink.get_label();
@@ -1631,7 +1631,7 @@ pub(crate) fn emit(
Amode::rip_relative(BranchTarget::Label(start_of_jumptable)), Amode::rip_relative(BranchTarget::Label(start_of_jumptable)),
*tmp1, *tmp1,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Load value out of the jump table. It's a relative offset to the target block, so it // Load value out of the jump table. It's a relative offset to the target block, so it
// might be negative; use a sign-extension. // might be negative; use a sign-extension.
@@ -1641,7 +1641,7 @@ pub(crate) fn emit(
*tmp2, *tmp2,
None, None,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Add base of jump table to jump-table-sourced block offset. // Add base of jump table to jump-table-sourced block offset.
let inst = Inst::alu_rmi_r( let inst = Inst::alu_rmi_r(
@@ -1650,11 +1650,11 @@ pub(crate) fn emit(
RegMemImm::reg(tmp2.to_reg()), RegMemImm::reg(tmp2.to_reg()),
*tmp1, *tmp1,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Branch to computed address. // Branch to computed address.
let inst = Inst::jmp_unknown(RegMem::reg(tmp1.to_reg())); let inst = Inst::jmp_unknown(RegMem::reg(tmp1.to_reg()));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Emit jump table (table of 32-bit offsets). // Emit jump table (table of 32-bit offsets).
sink.bind_label(start_of_jumptable); sink.bind_label(start_of_jumptable);
@@ -1683,7 +1683,7 @@ pub(crate) fn emit(
// Trap! // Trap!
let inst = Inst::trap(*srcloc, *trap_code); let inst = Inst::trap(*srcloc, *trap_code);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(else_label); sink.bind_label(else_label);
} }
@@ -1890,7 +1890,7 @@ pub(crate) fn emit(
}; };
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(*lhs), rhs_dst.to_reg()); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(*lhs), rhs_dst.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::NZ, do_min_max); one_way_jmp(sink, CC::NZ, do_min_max);
one_way_jmp(sink, CC::P, propagate_nan); one_way_jmp(sink, CC::P, propagate_nan);
@@ -1900,23 +1900,23 @@ pub(crate) fn emit(
// case, and are no-ops otherwise. // case, and are no-ops otherwise.
let op = if *is_min { or_op } else { and_op }; let op = if *is_min { or_op } else { and_op };
let inst = Inst::xmm_rm_r(op, RegMem::reg(*lhs), *rhs_dst); let inst = Inst::xmm_rm_r(op, RegMem::reg(*lhs), *rhs_dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// x86's min/max are not symmetric; if either operand is a NaN, they return the // x86's min/max are not symmetric; if either operand is a NaN, they return the
// read-only operand: perform an addition between the two operands, which has the // read-only operand: perform an addition between the two operands, which has the
// desired NaN propagation effects. // desired NaN propagation effects.
sink.bind_label(propagate_nan); sink.bind_label(propagate_nan);
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(*lhs), *rhs_dst); let inst = Inst::xmm_rm_r(add_op, RegMem::reg(*lhs), *rhs_dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::P, done); one_way_jmp(sink, CC::P, done);
sink.bind_label(do_min_max); sink.bind_label(do_min_max);
let inst = Inst::xmm_rm_r(min_max_op, RegMem::reg(*lhs), *rhs_dst); let inst = Inst::xmm_rm_r(min_max_op, RegMem::reg(*lhs), *rhs_dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(done); sink.bind_label(done);
} }
@@ -1986,13 +1986,13 @@ pub(crate) fn emit(
let constant_start_label = sink.get_label(); let constant_start_label = sink.get_label();
let load_offset = Amode::rip_relative(BranchTarget::Label(constant_start_label)); let load_offset = Amode::rip_relative(BranchTarget::Label(constant_start_label));
let load = Inst::load(*ty, load_offset, *dst, ExtKind::None, None); let load = Inst::load(*ty, load_offset, *dst, ExtKind::None, None);
load.emit(sink, flags, state); load.emit(sink, info, state);
// Jump over the constant. // Jump over the constant.
let constant_end_label = sink.get_label(); let constant_end_label = sink.get_label();
let continue_at_offset = BranchTarget::Label(constant_end_label); let continue_at_offset = BranchTarget::Label(constant_end_label);
let jump = Inst::jmp_known(continue_at_offset); let jump = Inst::jmp_known(continue_at_offset);
jump.emit(sink, flags, state); jump.emit(sink, info, state);
// Emit the constant. // Emit the constant.
sink.bind_label(constant_start_label); sink.bind_label(constant_start_label);
@@ -2151,30 +2151,30 @@ pub(crate) fn emit(
// thing. // thing.
// TODO use tst src, src here. // TODO use tst src, src here.
let inst = Inst::cmp_rmi_r(8, RegMemImm::imm(0), src.to_reg()); let inst = Inst::cmp_rmi_r(8, RegMemImm::imm(0), src.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::L, handle_negative); one_way_jmp(sink, CC::L, handle_negative);
// Handle a positive int64, which is the "easy" case: a signed conversion will do the // Handle a positive int64, which is the "easy" case: a signed conversion will do the
// right thing. // right thing.
emit_signed_cvt(sink, flags, state, src.to_reg(), *dst, *to_f64); emit_signed_cvt(sink, info, state, src.to_reg(), *dst, *to_f64);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(handle_negative); sink.bind_label(handle_negative);
// Divide x by two to get it in range for the signed conversion, keep the LSB, and // Divide x by two to get it in range for the signed conversion, keep the LSB, and
// scale it back up on the FP side. // scale it back up on the FP side.
let inst = Inst::gen_move(*tmp_gpr1, src.to_reg(), types::I64); let inst = Inst::gen_move(*tmp_gpr1, src.to_reg(), types::I64);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// tmp_gpr1 := src >> 1 // tmp_gpr1 := src >> 1
let inst = Inst::shift_r(8, ShiftKind::ShiftRightLogical, Some(1), *tmp_gpr1); let inst = Inst::shift_r(8, ShiftKind::ShiftRightLogical, Some(1), *tmp_gpr1);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::gen_move(*tmp_gpr2, src.to_reg(), types::I64); let inst = Inst::gen_move(*tmp_gpr2, src.to_reg(), types::I64);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::alu_rmi_r( let inst = Inst::alu_rmi_r(
true, /* 64bits */ true, /* 64bits */
@@ -2182,7 +2182,7 @@ pub(crate) fn emit(
RegMemImm::imm(1), RegMemImm::imm(1),
*tmp_gpr2, *tmp_gpr2,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::alu_rmi_r( let inst = Inst::alu_rmi_r(
true, /* 64bits */ true, /* 64bits */
@@ -2190,9 +2190,9 @@ pub(crate) fn emit(
RegMemImm::reg(tmp_gpr1.to_reg()), RegMemImm::reg(tmp_gpr1.to_reg()),
*tmp_gpr2, *tmp_gpr2,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
emit_signed_cvt(sink, flags, state, tmp_gpr2.to_reg(), *dst, *to_f64); emit_signed_cvt(sink, info, state, tmp_gpr2.to_reg(), *dst, *to_f64);
let add_op = if *to_f64 { let add_op = if *to_f64 {
SseOpcode::Addsd SseOpcode::Addsd
@@ -2200,7 +2200,7 @@ pub(crate) fn emit(
SseOpcode::Addss SseOpcode::Addss
}; };
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(dst.to_reg()), *dst); let inst = Inst::xmm_rm_r(add_op, RegMem::reg(dst.to_reg()), *dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(done); sink.bind_label(done);
} }
@@ -2273,18 +2273,18 @@ pub(crate) fn emit(
// The truncation. // The truncation.
let inst = Inst::xmm_to_gpr(trunc_op, src, *dst, *dst_size); let inst = Inst::xmm_to_gpr(trunc_op, src, *dst, *dst_size);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Compare against 1, in case of overflow the dst operand was INT_MIN. // Compare against 1, in case of overflow the dst operand was INT_MIN.
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(1), dst.to_reg()); let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(1), dst.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::NO, done); // no overflow => done one_way_jmp(sink, CC::NO, done); // no overflow => done
// Check for NaN. // Check for NaN.
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), src); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), src);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::NP, not_nan); // go to not_nan if not a NaN one_way_jmp(sink, CC::NP, not_nan); // go to not_nan if not a NaN
@@ -2296,10 +2296,10 @@ pub(crate) fn emit(
RegMemImm::reg(dst.to_reg()), RegMemImm::reg(dst.to_reg()),
*dst, *dst,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
sink.bind_label(not_nan); sink.bind_label(not_nan);
@@ -2308,10 +2308,10 @@ pub(crate) fn emit(
// Zero out tmp_xmm. // Zero out tmp_xmm.
let inst = let inst =
Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm); Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg()); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Jump if >= to done. // Jump if >= to done.
one_way_jmp(sink, CC::NB, done); one_way_jmp(sink, CC::NB, done);
@@ -2319,16 +2319,16 @@ pub(crate) fn emit(
// Otherwise, put INT_MAX. // Otherwise, put INT_MAX.
if *dst_size == OperandSize::Size64 { if *dst_size == OperandSize::Size64 {
let inst = Inst::imm(OperandSize::Size64, 0x7fffffffffffffff, *dst); let inst = Inst::imm(OperandSize::Size64, 0x7fffffffffffffff, *dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
let inst = Inst::imm(OperandSize::Size32, 0x7fffffff, *dst); let inst = Inst::imm(OperandSize::Size32, 0x7fffffff, *dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
} else { } else {
let check_positive = sink.get_label(); let check_positive = sink.get_label();
let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger); let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// Check if INT_MIN was the correct result: determine the smallest floating point // Check if INT_MIN was the correct result: determine the smallest floating point
// number that would convert to INT_MIN, put it in a temporary register, and compare // number that would convert to INT_MIN, put it in a temporary register, and compare
@@ -2344,7 +2344,7 @@ pub(crate) fn emit(
OperandSize::Size32 => { OperandSize::Size32 => {
let cst = Ieee32::pow2(output_bits - 1).neg().bits(); let cst = Ieee32::pow2(output_bits - 1).neg().bits();
let inst = Inst::imm(OperandSize::Size32, cst as u64, *tmp_gpr); let inst = Inst::imm(OperandSize::Size32, cst as u64, *tmp_gpr);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
OperandSize::Size64 => { OperandSize::Size64 => {
// An f64 can represent `i32::min_value() - 1` exactly with precision to spare, // An f64 can represent `i32::min_value() - 1` exactly with precision to spare,
@@ -2356,22 +2356,22 @@ pub(crate) fn emit(
Ieee64::pow2(output_bits - 1).neg() Ieee64::pow2(output_bits - 1).neg()
}; };
let inst = Inst::imm(OperandSize::Size64, cst.bits(), *tmp_gpr); let inst = Inst::imm(OperandSize::Size64, cst.bits(), *tmp_gpr);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
} }
let inst = let inst =
Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm); Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// jump over trap if src >= or > threshold // jump over trap if src >= or > threshold
one_way_jmp(sink, no_overflow_cc, check_positive); one_way_jmp(sink, no_overflow_cc, check_positive);
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow); let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
// If positive, it was a real overflow. // If positive, it was a real overflow.
@@ -2380,15 +2380,15 @@ pub(crate) fn emit(
// Zero out the tmp_xmm register. // Zero out the tmp_xmm register.
let inst = let inst =
Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm); Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg()); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::NB, done); // jump over trap if 0 >= src one_way_jmp(sink, CC::NB, done); // jump over trap if 0 >= src
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow); let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
sink.bind_label(done); sink.bind_label(done);
@@ -2464,14 +2464,14 @@ pub(crate) fn emit(
}; };
let inst = Inst::imm(*src_size, cst, *tmp_gpr); let inst = Inst::imm(*src_size, cst, *tmp_gpr);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = let inst =
Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm); Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src.to_reg()); let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let handle_large = sink.get_label(); let handle_large = sink.get_label();
one_way_jmp(sink, CC::NB, handle_large); // jump to handle_large if src >= large_threshold one_way_jmp(sink, CC::NB, handle_large); // jump to handle_large if src >= large_threshold
@@ -2487,14 +2487,14 @@ pub(crate) fn emit(
RegMemImm::reg(dst.to_reg()), RegMemImm::reg(dst.to_reg()),
*dst, *dst,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
// Trap. // Trap.
let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger); let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
sink.bind_label(not_nan); sink.bind_label(not_nan);
@@ -2503,10 +2503,10 @@ pub(crate) fn emit(
// overflow. // overflow.
let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size); let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg()); let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
one_way_jmp(sink, CC::NL, done); // if dst >= 0, jump to done one_way_jmp(sink, CC::NL, done); // if dst >= 0, jump to done
@@ -2519,14 +2519,14 @@ pub(crate) fn emit(
RegMemImm::reg(dst.to_reg()), RegMemImm::reg(dst.to_reg()),
*dst, *dst,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
// Trap. // Trap.
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow); let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
// Now handle large inputs. // Now handle large inputs.
@@ -2534,13 +2534,13 @@ pub(crate) fn emit(
sink.bind_label(handle_large); sink.bind_label(handle_large);
let inst = Inst::xmm_rm_r(sub_op, RegMem::reg(tmp_xmm.to_reg()), *src); let inst = Inst::xmm_rm_r(sub_op, RegMem::reg(tmp_xmm.to_reg()), *src);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size); let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg()); let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg());
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let next_is_large = sink.get_label(); let next_is_large = sink.get_label();
one_way_jmp(sink, CC::NL, next_is_large); // if dst >= 0, jump to next_is_large one_way_jmp(sink, CC::NL, next_is_large); // if dst >= 0, jump to next_is_large
@@ -2557,20 +2557,20 @@ pub(crate) fn emit(
}, },
*dst, *dst,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::jmp_known(BranchTarget::Label(done)); let inst = Inst::jmp_known(BranchTarget::Label(done));
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow); let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
sink.bind_label(next_is_large); sink.bind_label(next_is_large);
if *dst_size == OperandSize::Size64 { if *dst_size == OperandSize::Size64 {
let inst = Inst::imm(OperandSize::Size64, 1 << 63, *tmp_gpr); let inst = Inst::imm(OperandSize::Size64, 1 << 63, *tmp_gpr);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
let inst = Inst::alu_rmi_r( let inst = Inst::alu_rmi_r(
true, true,
@@ -2578,11 +2578,11 @@ pub(crate) fn emit(
RegMemImm::reg(tmp_gpr.to_reg()), RegMemImm::reg(tmp_gpr.to_reg()),
*dst, *dst,
); );
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} else { } else {
let inst = let inst =
Inst::alu_rmi_r(false, AluRmiROpcode::Add, RegMemImm::imm(1 << 31), *dst); Inst::alu_rmi_r(false, AluRmiROpcode::Add, RegMemImm::imm(1 << 31), *dst);
inst.emit(sink, flags, state); inst.emit(sink, info, state);
} }
sink.bind_label(done); sink.bind_label(done);
@@ -2600,7 +2600,7 @@ pub(crate) fn emit(
sink.put1(0x48 | ((enc_dst >> 3) & 1)); sink.put1(0x48 | ((enc_dst >> 3) & 1));
sink.put1(0xB8 | (enc_dst & 7)); sink.put1(0xB8 | (enc_dst & 7));
sink.add_reloc(*srcloc, Reloc::Abs8, name, *offset); sink.add_reloc(*srcloc, Reloc::Abs8, name, *offset);
if flags.emit_all_ones_funcaddrs() { if info.flags().emit_all_ones_funcaddrs() {
sink.put8(u64::max_value()); sink.put8(u64::max_value());
} else { } else {
sink.put8(0); sink.put8(0);
@@ -2664,14 +2664,14 @@ pub(crate) fn emit(
// mov{zbq,zwq,zlq,q} (%r9), %rax // mov{zbq,zwq,zlq,q} (%r9), %rax
// No need to call `add_trap` here, since the `i1` emit will do that. // No need to call `add_trap` here, since the `i1` emit will do that.
let i1 = Inst::load(*ty, amode.clone(), rax_w, ExtKind::ZeroExtend, *srcloc); let i1 = Inst::load(*ty, amode.clone(), rax_w, ExtKind::ZeroExtend, *srcloc);
i1.emit(sink, flags, state); i1.emit(sink, info, state);
// again: // again:
sink.bind_label(again_label); sink.bind_label(again_label);
// movq %rax, %r11 // movq %rax, %r11
let i2 = Inst::mov_r_r(true, rax, r11_w); let i2 = Inst::mov_r_r(true, rax, r11_w);
i2.emit(sink, flags, state); i2.emit(sink, info, state);
// opq %r10, %r11 // opq %r10, %r11
let r10_rmi = RegMemImm::reg(r10); let r10_rmi = RegMemImm::reg(r10);
@@ -2688,7 +2688,7 @@ pub(crate) fn emit(
}; };
Inst::alu_rmi_r(true, alu_op, r10_rmi, r11_w) Inst::alu_rmi_r(true, alu_op, r10_rmi, r11_w)
}; };
i3.emit(sink, flags, state); i3.emit(sink, info, state);
// lock cmpxchg{b,w,l,q} %r11, (%r9) // lock cmpxchg{b,w,l,q} %r11, (%r9)
// No need to call `add_trap` here, since the `i4` emit will do that. // No need to call `add_trap` here, since the `i4` emit will do that.
@@ -2698,7 +2698,7 @@ pub(crate) fn emit(
dst: amode.into(), dst: amode.into(),
srcloc: *srcloc, srcloc: *srcloc,
}; };
i4.emit(sink, flags, state); i4.emit(sink, info, state);
// jnz again // jnz again
one_way_jmp(sink, CC::NZ, again_label); one_way_jmp(sink, CC::NZ, again_label);

View File

@@ -14,6 +14,7 @@
use super::*; use super::*;
use crate::isa::test_utils; use crate::isa::test_utils;
use crate::isa::x64;
use alloc::vec::Vec; use alloc::vec::Vec;
#[test] #[test]
@@ -3660,14 +3661,22 @@ fn test_x64_emit() {
// ======================================================== // ========================================================
// Actually run the tests! // Actually run the tests!
let flags = settings::Flags::new(settings::builder()); let flags = settings::Flags::new(settings::builder());
use crate::settings::Configurable;
let mut isa_flag_builder = x64::settings::builder();
isa_flag_builder.enable("has_ssse3").unwrap();
isa_flag_builder.enable("has_sse41").unwrap();
let isa_flags = x64::settings::Flags::new(&flags, isa_flag_builder);
let rru = regs::create_reg_universe_systemv(&flags); let rru = regs::create_reg_universe_systemv(&flags);
let emit_info = EmitInfo::new(flags, isa_flags);
for (insn, expected_encoding, expected_printing) in insns { for (insn, expected_encoding, expected_printing) in insns {
// Check the printed text is as expected. // Check the printed text is as expected.
let actual_printing = insn.show_rru(Some(&rru)); let actual_printing = insn.show_rru(Some(&rru));
assert_eq!(expected_printing, actual_printing); assert_eq!(expected_printing, actual_printing);
let mut sink = test_utils::TestCodeSink::new(); let mut sink = test_utils::TestCodeSink::new();
let mut buffer = MachBuffer::new(); let mut buffer = MachBuffer::new();
insn.emit(&mut buffer, &flags, &mut Default::default()); insn.emit(&mut buffer, &emit_info, &mut Default::default());
let buffer = buffer.finish(); let buffer = buffer.finish();
buffer.emit(&mut sink); buffer.emit(&mut sink);
let actual_encoding = &sink.stringify(); let actual_encoding = &sink.stringify();

View File

@@ -3,6 +3,7 @@
use crate::binemit::{CodeOffset, StackMap}; use crate::binemit::{CodeOffset, StackMap};
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type}; use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type};
use crate::isa::x64::settings as x64_settings;
use crate::machinst::*; use crate::machinst::*;
use crate::{settings, settings::Flags, CodegenError, CodegenResult}; use crate::{settings, settings::Flags, CodegenError, CodegenResult};
use alloc::boxed::Box; use alloc::boxed::Box;
@@ -2559,11 +2560,30 @@ pub struct EmitState {
stack_map: Option<StackMap>, stack_map: Option<StackMap>,
} }
/// Constant state used during emissions of a sequence of instructions.
pub struct EmitInfo {
flags: settings::Flags,
isa_flags: x64_settings::Flags,
}
impl EmitInfo {
pub(crate) fn new(flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
Self { flags, isa_flags }
}
}
impl MachInstEmitInfo for EmitInfo {
fn flags(&self) -> &Flags {
&self.flags
}
}
impl MachInstEmit for Inst { impl MachInstEmit for Inst {
type State = EmitState; type State = EmitState;
type Info = EmitInfo;
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut Self::State) { fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
emit::emit(self, sink, flags, state); emit::emit(self, sink, info, state);
} }
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, _: &mut Self::State) -> String { fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, _: &mut Self::State) -> String {

View File

@@ -1,5 +1,7 @@
//! X86_64-bit Instruction Set Architecture. //! X86_64-bit Instruction Set Architecture.
use self::inst::EmitInfo;
use super::TargetIsa; use super::TargetIsa;
use crate::ir::{condcodes::IntCC, Function}; use crate::ir::{condcodes::IntCC, Function};
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings}; use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
@@ -20,7 +22,7 @@ mod settings;
pub(crate) struct X64Backend { pub(crate) struct X64Backend {
triple: Triple, triple: Triple,
flags: Flags, flags: Flags,
_x64_flags: x64_settings::Flags, x64_flags: x64_settings::Flags,
reg_universe: RealRegUniverse, reg_universe: RealRegUniverse,
} }
@@ -31,7 +33,7 @@ impl X64Backend {
Self { Self {
triple, triple,
flags, flags,
_x64_flags: x64_flags, x64_flags,
reg_universe, reg_universe,
} }
} }
@@ -39,8 +41,9 @@ impl X64Backend {
fn compile_vcode(&self, func: &Function, flags: Flags) -> CodegenResult<VCode<inst::Inst>> { fn compile_vcode(&self, func: &Function, flags: Flags) -> CodegenResult<VCode<inst::Inst>> {
// This performs lowering to VCode, register-allocates the code, computes // This performs lowering to VCode, register-allocates the code, computes
// block layout and finalizes branches. The result is ready for binary emission. // block layout and finalizes branches. The result is ready for binary emission.
let emit_info = EmitInfo::new(flags.clone(), self.x64_flags.clone());
let abi = Box::new(abi::X64ABICallee::new(&func, flags)?); let abi = Box::new(abi::X64ABICallee::new(&func, flags)?);
compile::compile::<Self>(&func, self, abi) compile::compile::<Self>(&func, self, abi, emit_info)
} }
} }
@@ -52,6 +55,7 @@ impl MachBackend for X64Backend {
) -> CodegenResult<MachCompileResult> { ) -> CodegenResult<MachCompileResult> {
let flags = self.flags(); let flags = self.flags();
let vcode = self.compile_vcode(func, flags.clone())?; let vcode = self.compile_vcode(func, flags.clone())?;
let buffer = vcode.emit(); let buffer = vcode.emit();
let buffer = buffer.finish(); let buffer = buffer.finish();
let frame_size = vcode.frame_size(); let frame_size = vcode.frame_size();

View File

@@ -1421,7 +1421,7 @@ impl MachBranch {
mod test { mod test {
use super::*; use super::*;
use crate::isa::aarch64::inst::xreg; use crate::isa::aarch64::inst::xreg;
use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, Inst}; use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst};
use crate::machinst::MachInstEmit; use crate::machinst::MachInstEmit;
use crate::settings; use crate::settings;
use std::default::Default; use std::default::Default;
@@ -1435,14 +1435,14 @@ mod test {
#[test] #[test]
fn test_elide_jump_to_next() { fn test_elide_jump_to_next() {
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
buf.reserve_labels_for_blocks(2); buf.reserve_labels_for_blocks(2);
buf.bind_label(label(0)); buf.bind_label(label(0));
let inst = Inst::Jump { dest: target(1) }; let inst = Inst::Jump { dest: target(1) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let buf = buf.finish(); let buf = buf.finish();
assert_eq!(0, buf.total_size()); assert_eq!(0, buf.total_size());
@@ -1450,7 +1450,7 @@ mod test {
#[test] #[test]
fn test_elide_trivial_jump_blocks() { fn test_elide_trivial_jump_blocks() {
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1462,15 +1462,15 @@ mod test {
taken: target(1), taken: target(1),
not_taken: target(2), not_taken: target(2),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let inst = Inst::Jump { dest: target(3) }; let inst = Inst::Jump { dest: target(3) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(2)); buf.bind_label(label(2));
let inst = Inst::Jump { dest: target(3) }; let inst = Inst::Jump { dest: target(3) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(3)); buf.bind_label(label(3));
@@ -1480,7 +1480,7 @@ mod test {
#[test] #[test]
fn test_flip_cond() { fn test_flip_cond() {
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1492,17 +1492,17 @@ mod test {
taken: target(1), taken: target(1),
not_taken: target(2), not_taken: target(2),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let inst = Inst::Udf { let inst = Inst::Udf {
trap_info: (SourceLoc::default(), TrapCode::Interrupt), trap_info: (SourceLoc::default(), TrapCode::Interrupt),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(2)); buf.bind_label(label(2));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(3)); buf.bind_label(label(3));
@@ -1514,9 +1514,9 @@ mod test {
kind: CondBrKind::NotZero(xreg(0)), kind: CondBrKind::NotZero(xreg(0)),
trap_info: (SourceLoc::default(), TrapCode::Interrupt), trap_info: (SourceLoc::default(), TrapCode::Interrupt),
}; };
inst.emit(&mut buf2, &flags, &mut state); inst.emit(&mut buf2, &info, &mut state);
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf2, &flags, &mut state); inst.emit(&mut buf2, &info, &mut state);
let buf2 = buf2.finish(); let buf2 = buf2.finish();
@@ -1525,7 +1525,7 @@ mod test {
#[test] #[test]
fn test_island() { fn test_island() {
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1537,7 +1537,7 @@ mod test {
taken: target(2), taken: target(2),
not_taken: target(3), not_taken: target(3),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
while buf.cur_offset() < 2000000 { while buf.cur_offset() < 2000000 {
@@ -1545,16 +1545,16 @@ mod test {
buf.emit_island(); buf.emit_island();
} }
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
} }
buf.bind_label(label(2)); buf.bind_label(label(2));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(3)); buf.bind_label(label(3));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
let buf = buf.finish(); let buf = buf.finish();
@@ -1567,7 +1567,7 @@ mod test {
taken: BranchTarget::ResolvedOffset(1048576 - 4), taken: BranchTarget::ResolvedOffset(1048576 - 4),
not_taken: BranchTarget::ResolvedOffset(2000000 + 4 - 4), not_taken: BranchTarget::ResolvedOffset(2000000 + 4 - 4),
}; };
inst.emit(&mut buf2, &flags, &mut state); inst.emit(&mut buf2, &info, &mut state);
let buf2 = buf2.finish(); let buf2 = buf2.finish();
@@ -1576,7 +1576,7 @@ mod test {
#[test] #[test]
fn test_island_backward() { fn test_island_backward() {
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1584,16 +1584,16 @@ mod test {
buf.bind_label(label(0)); buf.bind_label(label(0));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(2)); buf.bind_label(label(2));
while buf.cur_offset() < 2000000 { while buf.cur_offset() < 2000000 {
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
} }
buf.bind_label(label(3)); buf.bind_label(label(3));
@@ -1602,7 +1602,7 @@ mod test {
taken: target(0), taken: target(0),
not_taken: target(1), not_taken: target(1),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
let buf = buf.finish(); let buf = buf.finish();
@@ -1615,11 +1615,11 @@ mod test {
taken: BranchTarget::ResolvedOffset(8), taken: BranchTarget::ResolvedOffset(8),
not_taken: BranchTarget::ResolvedOffset(4 - (2000000 + 4)), not_taken: BranchTarget::ResolvedOffset(4 - (2000000 + 4)),
}; };
inst.emit(&mut buf2, &flags, &mut state); inst.emit(&mut buf2, &info, &mut state);
let inst = Inst::Jump { let inst = Inst::Jump {
dest: BranchTarget::ResolvedOffset(-(2000000 + 8)), dest: BranchTarget::ResolvedOffset(-(2000000 + 8)),
}; };
inst.emit(&mut buf2, &flags, &mut state); inst.emit(&mut buf2, &info, &mut state);
let buf2 = buf2.finish(); let buf2 = buf2.finish();
@@ -1661,7 +1661,7 @@ mod test {
// label7: // label7:
// ret // ret
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1673,38 +1673,38 @@ mod test {
taken: target(1), taken: target(1),
not_taken: target(2), not_taken: target(2),
}; };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let inst = Inst::Jump { dest: target(3) }; let inst = Inst::Jump { dest: target(3) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(2)); buf.bind_label(label(2));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
let inst = Inst::Jump { dest: target(0) }; let inst = Inst::Jump { dest: target(0) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(3)); buf.bind_label(label(3));
let inst = Inst::Jump { dest: target(4) }; let inst = Inst::Jump { dest: target(4) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(4)); buf.bind_label(label(4));
let inst = Inst::Jump { dest: target(5) }; let inst = Inst::Jump { dest: target(5) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(5)); buf.bind_label(label(5));
let inst = Inst::Jump { dest: target(7) }; let inst = Inst::Jump { dest: target(7) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(6)); buf.bind_label(label(6));
let inst = Inst::Nop4; let inst = Inst::Nop4;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(7)); buf.bind_label(label(7));
let inst = Inst::Ret; let inst = Inst::Ret;
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
let buf = buf.finish(); let buf = buf.finish();
@@ -1737,7 +1737,7 @@ mod test {
// //
// label0, label1, ..., label4: // label0, label1, ..., label4:
// b label0 // b label0
let flags = settings::Flags::new(settings::builder()); let info = EmitInfo::new(settings::Flags::new(settings::builder()));
let mut buf = MachBuffer::new(); let mut buf = MachBuffer::new();
let mut state = Default::default(); let mut state = Default::default();
@@ -1745,23 +1745,23 @@ mod test {
buf.bind_label(label(0)); buf.bind_label(label(0));
let inst = Inst::Jump { dest: target(1) }; let inst = Inst::Jump { dest: target(1) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(1)); buf.bind_label(label(1));
let inst = Inst::Jump { dest: target(2) }; let inst = Inst::Jump { dest: target(2) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(2)); buf.bind_label(label(2));
let inst = Inst::Jump { dest: target(3) }; let inst = Inst::Jump { dest: target(3) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(3)); buf.bind_label(label(3));
let inst = Inst::Jump { dest: target(4) }; let inst = Inst::Jump { dest: target(4) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
buf.bind_label(label(4)); buf.bind_label(label(4));
let inst = Inst::Jump { dest: target(1) }; let inst = Inst::Jump { dest: target(1) };
inst.emit(&mut buf, &flags, &mut state); inst.emit(&mut buf, &info, &mut state);
let buf = buf.finish(); let buf = buf.finish();

View File

@@ -14,6 +14,7 @@ pub fn compile<B: LowerBackend + MachBackend>(
f: &Function, f: &Function,
b: &B, b: &B,
abi: Box<dyn ABICallee<I = B::MInst>>, abi: Box<dyn ABICallee<I = B::MInst>>,
emit_info: <B::MInst as MachInstEmit>::Info,
) -> CodegenResult<VCode<B::MInst>> ) -> CodegenResult<VCode<B::MInst>>
where where
B::MInst: PrettyPrint, B::MInst: PrettyPrint,
@@ -21,7 +22,7 @@ where
// Compute lowered block order. // Compute lowered block order.
let block_order = BlockLoweringOrder::new(f); let block_order = BlockLoweringOrder::new(f);
// Build the lowering context. // Build the lowering context.
let lower = Lower::new(f, abi, block_order)?; let lower = Lower::new(f, abi, emit_info, block_order)?;
// Lower the IR. // Lower the IR.
let (mut vcode, stack_map_request_info) = { let (mut vcode, stack_map_request_info) = {
let _tt = timing::vcode_lower(); let _tt = timing::vcode_lower();

View File

@@ -315,9 +315,10 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
pub fn new( pub fn new(
f: &'func Function, f: &'func Function,
abi: Box<dyn ABICallee<I = I>>, abi: Box<dyn ABICallee<I = I>>,
emit_info: I::Info,
block_order: BlockLoweringOrder, block_order: BlockLoweringOrder,
) -> CodegenResult<Lower<'func, I>> { ) -> CodegenResult<Lower<'func, I>> {
let mut vcode = VCodeBuilder::new(abi, block_order); let mut vcode = VCodeBuilder::new(abi, emit_info, block_order);
let mut next_vreg: u32 = 0; let mut next_vreg: u32 = 0;

View File

@@ -275,12 +275,21 @@ pub enum MachTerminator<'a> {
pub trait MachInstEmit: MachInst { pub trait MachInstEmit: MachInst {
/// Persistent state carried across `emit` invocations. /// Persistent state carried across `emit` invocations.
type State: MachInstEmitState<Self>; type State: MachInstEmitState<Self>;
/// Constant information used in `emit` invocations.
type Info: MachInstEmitInfo;
/// Emit the instruction. /// Emit the instruction.
fn emit(&self, code: &mut MachBuffer<Self>, flags: &Flags, state: &mut Self::State); fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
/// Pretty-print the instruction. /// Pretty-print the instruction.
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut Self::State) -> String; fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut Self::State) -> String;
} }
/// Constant information used to emit an instruction.
pub trait MachInstEmitInfo {
/// Return the target-independent settings used for the compilation of this
/// particular function.
fn flags(&self) -> &Flags;
}
/// A trait describing the emission state carried between MachInsts when /// A trait describing the emission state carried between MachInsts when
/// emitting a function body. /// emitting a function body.
pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug { pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {

View File

@@ -88,6 +88,10 @@ pub struct VCode<I: VCodeInst> {
/// ABI object. /// ABI object.
abi: Box<dyn ABICallee<I = I>>, abi: Box<dyn ABICallee<I = I>>,
/// Constant information used during code emission. This should be
/// immutable across function compilations within the same module.
emit_info: I::Info,
/// Safepoint instruction indices. Filled in post-regalloc. (Prior to /// Safepoint instruction indices. Filled in post-regalloc. (Prior to
/// regalloc, the safepoint instructions are listed in the separate /// regalloc, the safepoint instructions are listed in the separate
/// `StackmapRequestInfo` held separate from the `VCode`.) /// `StackmapRequestInfo` held separate from the `VCode`.)
@@ -132,9 +136,13 @@ pub struct VCodeBuilder<I: VCodeInst> {
impl<I: VCodeInst> VCodeBuilder<I> { impl<I: VCodeInst> VCodeBuilder<I> {
/// Create a new VCodeBuilder. /// Create a new VCodeBuilder.
pub fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> { pub fn new(
abi: Box<dyn ABICallee<I = I>>,
emit_info: I::Info,
block_order: BlockLoweringOrder,
) -> VCodeBuilder<I> {
let reftype_class = I::ref_type_regclass(abi.flags()); let reftype_class = I::ref_type_regclass(abi.flags());
let vcode = VCode::new(abi, block_order); let vcode = VCode::new(abi, emit_info, block_order);
let stack_map_info = StackmapRequestInfo { let stack_map_info = StackmapRequestInfo {
reftype_class, reftype_class,
reftyped_vregs: vec![], reftyped_vregs: vec![],
@@ -263,7 +271,11 @@ fn is_reftype(ty: Type) -> bool {
impl<I: VCodeInst> VCode<I> { impl<I: VCodeInst> VCode<I> {
/// New empty VCode. /// New empty VCode.
fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> { fn new(
abi: Box<dyn ABICallee<I = I>>,
emit_info: I::Info,
block_order: BlockLoweringOrder,
) -> VCode<I> {
VCode { VCode {
liveins: abi.liveins(), liveins: abi.liveins(),
liveouts: abi.liveouts(), liveouts: abi.liveouts(),
@@ -277,6 +289,7 @@ impl<I: VCodeInst> VCode<I> {
block_succs: vec![], block_succs: vec![],
block_order, block_order,
abi, abi,
emit_info,
safepoint_insns: vec![], safepoint_insns: vec![],
safepoint_slots: vec![], safepoint_slots: vec![],
} }
@@ -431,7 +444,6 @@ impl<I: VCodeInst> VCode<I> {
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices. buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices.
let flags = self.abi.flags();
let mut safepoint_idx = 0; let mut safepoint_idx = 0;
let mut cur_srcloc = None; let mut cur_srcloc = None;
for block in 0..self.num_blocks() { for block in 0..self.num_blocks() {
@@ -440,7 +452,7 @@ impl<I: VCodeInst> VCode<I> {
while new_offset > buffer.cur_offset() { while new_offset > buffer.cur_offset() {
// Pad with NOPs up to the aligned block offset. // Pad with NOPs up to the aligned block offset.
let nop = I::gen_nop((new_offset - buffer.cur_offset()) as usize); let nop = I::gen_nop((new_offset - buffer.cur_offset()) as usize);
nop.emit(&mut buffer, flags, &mut Default::default()); nop.emit(&mut buffer, &self.emit_info, &mut Default::default());
} }
assert_eq!(buffer.cur_offset(), new_offset); assert_eq!(buffer.cur_offset(), new_offset);
@@ -469,7 +481,7 @@ impl<I: VCodeInst> VCode<I> {
safepoint_idx += 1; safepoint_idx += 1;
} }
self.insts[iix as usize].emit(&mut buffer, flags, &mut state); self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state);
} }
if cur_srcloc.is_some() { if cur_srcloc.is_some() {