use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand};
use crate::selection_dag::dag_builder::SelectionDAGBuilder;
use crate::selection_dag::dag_combine::DAGCombiner;
use crate::selection_dag::legalize_ops::{
OpLegalizeAction, OperationLegalizer, PromoteAction, TargetLegalizer,
};
use crate::selection_dag::legalize_types::{LegalizeAction, TypeActionMap, TypeLegalizer};
use crate::selection_dag::sd_node::{
EVTKind, SDNode, SDNodeFlags, SDOpcode, SDValue, SelectionDAG, EVT, MVT,
};
use crate::types::Type;
use crate::value::ValueRef;
use crate::x86::{
x86_calling_convention::X86CallingConvention,
x86_instr_info::{
OperandType, X86InstrDesc, X86InstrInfo, X86MemOperand, X86Opcode, X86Operand, X86SchedInfo,
},
x86_register_info::X86RegisterInfo,
x86_schedule_model::{
instruction_latency, instruction_resources, instruction_uops, lookup_itinerary,
InstrItinerary, ProcResource, SchedMachineModel, SchedModel, X86SchedModelKind,
},
x86_subtarget::X86Subtarget,
};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86ISD {
Cmp,
Test,
FCmp,
SetCC,
CMov,
BitTest,
BitTestAndSet,
BitTestAndReset,
BitTestAndComplement,
Shld,
Shrd,
UDivRem,
SDivRem,
MulHW,
UMulH,
RepMovs,
RepStos,
Call,
Ret,
TailCall,
FrameAddr,
RetAddr,
LeaAddr,
GlobalBaseReg,
AddCarry,
SubBorrow,
VBroadcast,
VBroadcastLoad,
MovdToVec,
VecExtract,
VecInsert,
VShuffle,
Unpckl,
Unpckh,
HAdd,
HSub,
AddSub,
Fmadd,
Fmsub,
Fnmadd,
Fnmsub,
Fmaddsub,
MaskOp,
MaskedOp,
Gather,
Scatter,
Compress,
Expand,
EhReturn,
TlsAddr,
Wrapper,
WrapperRIP,
}
impl X86ISD {
pub fn as_str(&self) -> &'static str {
match self {
X86ISD::Cmp => "X86ISD::Cmp",
X86ISD::Test => "X86ISD::Test",
X86ISD::FCmp => "X86ISD::FCmp",
X86ISD::SetCC => "X86ISD::SetCC",
X86ISD::CMov => "X86ISD::CMov",
X86ISD::BitTest => "X86ISD::BitTest",
X86ISD::BitTestAndSet => "X86ISD::BitTestAndSet",
X86ISD::BitTestAndReset => "X86ISD::BitTestAndReset",
X86ISD::BitTestAndComplement => "X86ISD::BitTestAndComplement",
X86ISD::Shld => "X86ISD::Shld",
X86ISD::Shrd => "X86ISD::Shrd",
X86ISD::UDivRem => "X86ISD::UDivRem",
X86ISD::SDivRem => "X86ISD::SDivRem",
X86ISD::MulHW => "X86ISD::MulHW",
X86ISD::UMulH => "X86ISD::UMulH",
X86ISD::RepMovs => "X86ISD::RepMovs",
X86ISD::RepStos => "X86ISD::RepStos",
X86ISD::Call => "X86ISD::Call",
X86ISD::Ret => "X86ISD::Ret",
X86ISD::TailCall => "X86ISD::TailCall",
X86ISD::FrameAddr => "X86ISD::FrameAddr",
X86ISD::RetAddr => "X86ISD::RetAddr",
X86ISD::LeaAddr => "X86ISD::LeaAddr",
X86ISD::GlobalBaseReg => "X86ISD::GlobalBaseReg",
X86ISD::AddCarry => "X86ISD::AddCarry",
X86ISD::SubBorrow => "X86ISD::SubBorrow",
X86ISD::VBroadcast => "X86ISD::VBroadcast",
X86ISD::VBroadcastLoad => "X86ISD::VBroadcastLoad",
X86ISD::MovdToVec => "X86ISD::MovdToVec",
X86ISD::VecExtract => "X86ISD::VecExtract",
X86ISD::VecInsert => "X86ISD::VecInsert",
X86ISD::VShuffle => "X86ISD::VShuffle",
X86ISD::Unpckl => "X86ISD::Unpckl",
X86ISD::Unpckh => "X86ISD::Unpckh",
X86ISD::HAdd => "X86ISD::HAdd",
X86ISD::HSub => "X86ISD::HSub",
X86ISD::AddSub => "X86ISD::AddSub",
X86ISD::Fmadd => "X86ISD::Fmadd",
X86ISD::Fmsub => "X86ISD::Fmsub",
X86ISD::Fnmadd => "X86ISD::Fnmadd",
X86ISD::Fnmsub => "X86ISD::Fnmsub",
X86ISD::Fmaddsub => "X86ISD::Fmaddsub",
X86ISD::MaskOp => "X86ISD::MaskOp",
X86ISD::MaskedOp => "X86ISD::MaskedOp",
X86ISD::Gather => "X86ISD::Gather",
X86ISD::Scatter => "X86ISD::Scatter",
X86ISD::Compress => "X86ISD::Compress",
X86ISD::Expand => "X86ISD::Expand",
X86ISD::EhReturn => "X86ISD::EhReturn",
X86ISD::TlsAddr => "X86ISD::TlsAddr",
X86ISD::Wrapper => "X86ISD::Wrapper",
X86ISD::WrapperRIP => "X86ISD::WrapperRIP",
}
}
pub fn is_comparison(&self) -> bool {
matches!(self, X86ISD::Cmp | X86ISD::Test | X86ISD::FCmp)
}
pub fn sets_flags(&self) -> bool {
matches!(
self,
X86ISD::Cmp | X86ISD::Test | X86ISD::FCmp | X86ISD::AddCarry | X86ISD::SubBorrow
)
}
pub fn to_x86_opcode(&self) -> Option<X86Opcode> {
match self {
X86ISD::Cmp => Some(X86Opcode::CMP),
X86ISD::Test => Some(X86Opcode::TEST),
X86ISD::RepMovs => Some(X86Opcode::MOVSB),
X86ISD::RepStos => Some(X86Opcode::STOSB),
X86ISD::Shld => Some(X86Opcode::SHLD),
X86ISD::Shrd => Some(X86Opcode::SHRD),
X86ISD::AddCarry => Some(X86Opcode::ADC),
X86ISD::SubBorrow => Some(X86Opcode::SBB),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DAGNode {
pub node: SDNode,
pub x86_opcode: Option<X86ISD>,
pub condition_code: Option<X86CondCode>,
pub addressing_mode: Option<X86AddressMode>,
pub mem_operand: Option<X86MemOperand>,
pub target_constant: Option<i64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CondCode {
O,
NO,
B,
AE,
E,
NE,
BE,
A,
S,
NS,
P,
NP,
L,
GE,
LE,
G,
}
impl X86CondCode {
pub fn as_str(&self) -> &'static str {
match self {
X86CondCode::O => "o",
X86CondCode::NO => "no",
X86CondCode::B => "b",
X86CondCode::AE => "ae",
X86CondCode::E => "e",
X86CondCode::NE => "ne",
X86CondCode::BE => "be",
X86CondCode::A => "a",
X86CondCode::S => "s",
X86CondCode::NS => "ns",
X86CondCode::P => "p",
X86CondCode::NP => "np",
X86CondCode::L => "l",
X86CondCode::GE => "ge",
X86CondCode::LE => "le",
X86CondCode::G => "g",
}
}
pub fn complement(&self) -> X86CondCode {
match self {
X86CondCode::O => X86CondCode::NO,
X86CondCode::NO => X86CondCode::O,
X86CondCode::B => X86CondCode::AE,
X86CondCode::AE => X86CondCode::B,
X86CondCode::E => X86CondCode::NE,
X86CondCode::NE => X86CondCode::E,
X86CondCode::BE => X86CondCode::A,
X86CondCode::A => X86CondCode::BE,
X86CondCode::S => X86CondCode::NS,
X86CondCode::NS => X86CondCode::S,
X86CondCode::P => X86CondCode::NP,
X86CondCode::NP => X86CondCode::P,
X86CondCode::L => X86CondCode::GE,
X86CondCode::GE => X86CondCode::L,
X86CondCode::LE => X86CondCode::G,
X86CondCode::G => X86CondCode::LE,
}
}
pub fn swap_operands(&self) -> X86CondCode {
match self {
X86CondCode::B => X86CondCode::A,
X86CondCode::A => X86CondCode::B,
X86CondCode::BE => X86CondCode::AE,
X86CondCode::AE => X86CondCode::BE,
X86CondCode::L => X86CondCode::G,
X86CondCode::G => X86CondCode::L,
X86CondCode::LE => X86CondCode::GE,
X86CondCode::GE => X86CondCode::LE,
other => *other,
}
}
pub fn from_unsigned_icmp(pred: u32) -> X86CondCode {
match pred {
0 => X86CondCode::E, 1 => X86CondCode::NE, 2 => X86CondCode::A, 3 => X86CondCode::BE, 4 => X86CondCode::AE, 5 => X86CondCode::B, _ => X86CondCode::E,
}
}
pub fn from_signed_icmp(pred: u32) -> X86CondCode {
match pred {
0 => X86CondCode::E, 1 => X86CondCode::NE, 2 => X86CondCode::G, 3 => X86CondCode::LE, 4 => X86CondCode::GE, 5 => X86CondCode::L, _ => X86CondCode::E,
}
}
pub fn from_fcmp(pred: u32) -> X86CondCode {
match pred {
0 => X86CondCode::E, 1 => X86CondCode::NE, 2 => X86CondCode::A, 3 => X86CondCode::AE, 4 => X86CondCode::B, 5 => X86CondCode::BE, 6 => X86CondCode::NP, 7 => X86CondCode::P, 8 => X86CondCode::NE, 9 => X86CondCode::G, 10 => X86CondCode::GE, 11 => X86CondCode::L, 12 => X86CondCode::LE, 13 => X86CondCode::NE, _ => X86CondCode::E,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86AddressMode {
pub base_reg: Option<u32>,
pub index_reg: Option<u32>,
pub scale: u8,
pub displacement: i64,
pub segment: Option<u32>,
pub is_rip_relative: bool,
pub is_frame_relative: bool,
pub global_value: Option<String>,
}
impl X86AddressMode {
pub fn new_base_disp(base: u32, disp: i64) -> Self {
Self {
base_reg: Some(base),
displacement: disp,
..Default::default()
}
}
pub fn new_base_index_scale(base: u32, index: u32, scale: u8, disp: i64) -> Self {
Self {
base_reg: Some(base),
index_reg: Some(index),
scale,
displacement: disp,
..Default::default()
}
}
pub fn new_rip_relative(disp: i64) -> Self {
Self {
displacement: disp,
is_rip_relative: true,
..Default::default()
}
}
pub fn new_absolute(addr: i64) -> Self {
Self {
displacement: addr,
..Default::default()
}
}
pub fn has_base(&self) -> bool {
self.base_reg.is_some()
}
pub fn has_index(&self) -> bool {
self.index_reg.is_some()
}
pub fn fits_disp8(&self) -> bool {
self.displacement >= -128 && self.displacement <= 127
}
pub fn fits_disp32(&self) -> bool {
self.displacement >= -2_147_483_648 && self.displacement <= 2_147_483_647
}
pub fn classify(&self) -> AddressingModePattern {
if self.is_rip_relative {
return AddressingModePattern::RIPRelative;
}
match (self.has_base(), self.has_index()) {
(false, false) => AddressingModePattern::Absolute,
(true, false) if self.displacement == 0 => AddressingModePattern::BaseOnly,
(true, false) => AddressingModePattern::BaseDisp,
(false, true) => AddressingModePattern::IndexScale,
(true, true) if self.displacement == 0 => AddressingModePattern::BaseIndexScale,
(true, true) => AddressingModePattern::BaseIndexScaleDisp,
}
}
pub fn hash_key(&self) -> u64 {
let mut key: u64 = 0;
if let Some(r) = self.base_reg {
key ^= (r as u64) << 8;
}
if let Some(r) = self.index_reg {
key ^= (r as u64) << 16;
}
key ^= (self.scale as u64) << 24;
key ^= (self.displacement as u64).wrapping_mul(0x9E3779B97F4A7C15);
if self.is_rip_relative {
key ^= 1 << 63;
}
key
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressingModePattern {
BaseOnly,
BaseDisp,
IndexScale,
BaseIndexScale,
BaseIndexScaleDisp,
RIPRelative,
Absolute,
}
#[derive(Debug, Clone)]
pub struct X86SelectionDAG {
pub dag: SelectionDAG,
pub subtarget: X86Subtarget,
pub calling_conv: X86CallingConvention,
pub node_info: HashMap<usize, X86DAGNode>,
pub is_64bit: bool,
pub is_32bit: bool,
pub has_frame_pointer: bool,
pub next_frame_index: u32,
pub next_vreg: u32,
}
impl X86SelectionDAG {
pub fn new(subtarget: X86Subtarget, calling_conv: X86CallingConvention) -> Self {
let mut dag = SelectionDAG::with_target("x86_64-unknown-linux-gnu");
dag.set_root(SDValue::new(0, 0)); Self {
dag,
subtarget,
calling_conv,
node_info: HashMap::new(),
is_64bit: true,
is_32bit: false,
has_frame_pointer: false,
next_frame_index: 0,
next_vreg: 0,
}
}
pub fn new_32bit(subtarget: X86Subtarget, calling_conv: X86CallingConvention) -> Self {
let mut dag = SelectionDAG::with_target("i686-unknown-linux-gnu");
dag.set_root(SDValue::new(0, 0));
Self {
dag,
subtarget,
calling_conv,
node_info: HashMap::new(),
is_64bit: false,
is_32bit: true,
has_frame_pointer: false,
next_frame_index: 0,
next_vreg: 0,
}
}
pub fn pointer_type(&self) -> Type {
if self.is_64bit {
Type::pointer(0)
} else {
Type::integer(32)
}
}
pub fn int_reg_width(&self) -> u32 {
if self.is_64bit {
64
} else {
32
}
}
pub fn get_vreg(&mut self, ty: Type) -> SDValue {
let vreg = self.next_vreg;
self.next_vreg += 1;
self.dag.get_register(ty, vreg)
}
pub fn get_frame_index(&mut self, ty: Type) -> SDValue {
let fi = self.next_frame_index;
self.next_frame_index += 1;
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::FrameIndex,
vec![ty.clone()],
);
let node_id = self.dag.add_node(node);
SDValue::new(node_id, 0)
}
pub fn get_target_node(
&mut self,
x86_opcode: X86ISD,
value_types: Vec<Type>,
operands: Vec<SDValue>,
) -> SDValue {
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::TargetConstant, value_types,
)
.with_operands(operands)
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(x86_opcode),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_x86_cmp(&mut self, lhs: SDValue, rhs: SDValue, cc: X86CondCode) -> SDValue {
let x86_op = X86ISD::Cmp;
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::SetCC,
vec![Type::integer(1)], )
.with_operands(vec![lhs, rhs])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(x86_op),
condition_code: Some(cc),
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_setcc(&mut self, cc: X86CondCode, flag: SDValue, ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::SetCC,
vec![ty],
)
.with_operands(vec![flag])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(X86ISD::SetCC),
condition_code: Some(cc),
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_cmov(
&mut self,
cc: X86CondCode,
flag: SDValue,
true_val: SDValue,
false_val: SDValue,
ty: Type,
) -> SDValue {
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Select,
vec![ty],
)
.with_operands(vec![flag, true_val, false_val])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(X86ISD::CMov),
condition_code: Some(cc),
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_x86_call(
&mut self,
chain: SDValue,
callee: SDValue,
args: Vec<SDValue>,
ret_ty: Type,
) -> (SDValue, SDValue) {
let mut ops = vec![chain, callee];
ops.extend(args);
let types = if ret_ty.is_void() {
vec![Type::token()]
} else {
vec![ret_ty, Type::token()]
};
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Call,
types,
)
.with_operands(ops)
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(X86ISD::Call),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let val = SDValue::new(node_id, 0);
let chain_val = SDValue::new(node_id, 1);
(val, chain_val)
}
pub fn get_x86_ret(&mut self, chain: SDValue, retval: Option<SDValue>) -> SDValue {
let mut ops = vec![chain];
if let Some(rv) = retval {
ops.push(rv);
}
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Ret,
vec![Type::token()],
)
.with_operands(ops)
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(X86ISD::Ret),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
self.dag.set_root(SDValue::new(node_id, 0));
SDValue::new(node_id, 0)
}
pub fn get_lea(&mut self, addr_mode: X86AddressMode) -> SDValue {
let ty = self.pointer_type();
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Add,
vec![ty],
)
.with_operands(Vec::new())
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: Some(X86ISD::LeaAddr),
condition_code: None,
addressing_mode: Some(addr_mode),
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_x86_load(
&mut self,
chain: SDValue,
ptr: SDValue,
ty: Type,
addr_mode: X86AddressMode,
) -> (SDValue, SDValue) {
let types = vec![ty.clone(), Type::token()];
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Load,
types,
)
.with_operands(vec![chain, ptr])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: None,
condition_code: None,
addressing_mode: Some(addr_mode),
mem_operand: None,
target_constant: None,
},
);
let val = SDValue::new(node_id, 0);
let chain_val = SDValue::new(node_id, 1);
(val, chain_val)
}
pub fn get_x86_store(
&mut self,
chain: SDValue,
val: SDValue,
ptr: SDValue,
addr_mode: X86AddressMode,
) -> SDValue {
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::Store,
vec![Type::token()],
)
.with_operands(vec![chain, val, ptr])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: None,
condition_code: None,
addressing_mode: Some(addr_mode),
mem_operand: None,
target_constant: None,
},
);
SDValue::new(node_id, 0)
}
pub fn get_copy_to_reg(&mut self, chain: SDValue, val: SDValue, reg: u32) -> SDValue {
let reg_ty = self
.dag
.get_node(val.node_id)
.and_then(|n| n.get_value_type(0).cloned())
.unwrap_or_else(|| Type::integer(32));
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::CopyToReg,
vec![Type::token()],
)
.with_operands(vec![chain, SDValue::new(0, 0), val])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(reg as i64),
},
);
SDValue::new(node_id, 0)
}
pub fn get_copy_from_reg(&mut self, chain: SDValue, reg: u32, ty: Type) -> (SDValue, SDValue) {
let types = vec![ty.clone(), Type::token()];
let node = SDNode::new_with_node_id(
self.dag.nodes.len(),
self.dag.all_nodes.len(),
SDOpcode::CopyFromReg,
types,
)
.with_operands(vec![chain, SDValue::new(0, 0)])
.with_target_node(true);
let node_id = self.dag.add_node(node);
self.node_info.insert(
node_id,
X86DAGNode {
node: self.dag.get_node(node_id).cloned().unwrap_or_default(),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(reg as i64),
},
);
let val = SDValue::new(node_id, 0);
let chain_val = SDValue::new(node_id, 1);
(val, chain_val)
}
pub fn get_node_info(&self, node_id: usize) -> Option<&X86DAGNode> {
self.node_info.get(&node_id)
}
pub fn is_x86_opcode(&self, node_id: usize, op: X86ISD) -> bool {
self.node_info
.get(&node_id)
.and_then(|n| n.x86_opcode)
.map_or(false, |x86op| x86op == op)
}
}
pub struct X86DAGBuilder {
pub x86_dag: X86SelectionDAG,
pub generic_builder: SelectionDAGBuilder,
pub value_map: HashMap<u64, SDValue>,
pub current_chain: SDValue,
pub current_glue: Option<SDValue>,
pub in_prologue: bool,
pub in_epilogue: bool,
pub func_name: Option<String>,
pub frame_base: Option<SDValue>,
pub stack_slots: HashMap<u64, SDValue>,
}
impl X86DAGBuilder {
pub fn new(subtarget: X86Subtarget, calling_conv: X86CallingConvention) -> Self {
let x86_dag = X86SelectionDAG::new(subtarget, calling_conv);
let generic_builder = SelectionDAGBuilder::with_target("x86_64-unknown-linux-gnu");
let chain = x86_dag.dag.entry_token;
Self {
current_chain: chain,
x86_dag,
generic_builder,
value_map: HashMap::new(),
current_glue: None,
in_prologue: false,
in_epilogue: false,
func_name: None,
frame_base: None,
stack_slots: HashMap::new(),
}
}
pub fn new_32bit(subtarget: X86Subtarget, calling_conv: X86CallingConvention) -> Self {
let x86_dag = X86SelectionDAG::new_32bit(subtarget, calling_conv);
let generic_builder = SelectionDAGBuilder::with_target("i686-unknown-linux-gnu");
let chain = x86_dag.dag.entry_token;
Self {
current_chain: chain,
x86_dag,
generic_builder,
value_map: HashMap::new(),
current_glue: None,
in_prologue: false,
in_epilogue: false,
func_name: None,
frame_base: None,
stack_slots: HashMap::new(),
}
}
pub fn set_value(&mut self, val_id: u64, sd_val: SDValue) {
self.value_map.insert(val_id, sd_val);
self.generic_builder.set_value(val_id, sd_val);
}
pub fn get_value(&self, val_id: u64) -> Option<SDValue> {
self.value_map.get(&val_id).copied()
}
pub fn update_chain(&mut self, chain: SDValue) {
self.current_chain = chain;
}
pub fn get_chain(&self) -> SDValue {
self.current_chain
}
pub fn get_token_factor(&mut self) -> SDValue {
let chain = self.current_chain;
if let Some(glue) = self.current_glue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::TokenFactor,
vec![Type::token()],
)
.with_operands(vec![chain, glue]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
} else {
chain
}
}
pub fn lower_add(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), lhs, rhs)
}
pub fn lower_sub(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Sub, Type::integer(32), lhs, rhs)
}
pub fn lower_mul(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), lhs, rhs)
}
pub fn lower_sdiv(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::SDiv, Type::integer(32), lhs, rhs)
}
pub fn lower_udiv(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::UDiv, Type::integer(32), lhs, rhs)
}
pub fn lower_and(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::And, Type::integer(32), lhs, rhs)
}
pub fn lower_or(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Or, Type::integer(32), lhs, rhs)
}
pub fn lower_xor(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Xor, Type::integer(32), lhs, rhs)
}
pub fn lower_shl(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Shl, Type::integer(32), lhs, rhs)
}
pub fn lower_sra(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Sra, Type::integer(32), lhs, rhs)
}
pub fn lower_srl(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::Srl, Type::integer(32), lhs, rhs)
}
pub fn lower_fadd(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::FAdd, Type::double_precision(), lhs, rhs)
}
pub fn lower_fsub(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::FSub, Type::double_precision(), lhs, rhs)
}
pub fn lower_fmul(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::FMul, Type::double_precision(), lhs, rhs)
}
pub fn lower_fdiv(&mut self, lhs: SDValue, rhs: SDValue) -> SDValue {
self.x86_dag.dag.get_binary_op(SDOpcode::FDiv, Type::double_precision(), lhs, rhs)
}
pub fn lower_icmp(
&mut self,
lhs: SDValue,
rhs: SDValue,
pred: u32,
is_signed: bool,
) -> SDValue {
let cc = if is_signed {
X86CondCode::from_signed_icmp(pred)
} else {
X86CondCode::from_unsigned_icmp(pred)
};
self.x86_dag.get_x86_cmp(lhs, rhs, cc)
}
pub fn lower_fcmp(&mut self, lhs: SDValue, rhs: SDValue, pred: u32) -> SDValue {
let cc = X86CondCode::from_fcmp(pred);
self.x86_dag.get_x86_cmp(lhs, rhs, cc)
}
pub fn lower_load(&mut self, ptr: SDValue, ty: Type) -> (SDValue, SDValue) {
let chain = self.get_token_factor();
let addr_mode = self.recognize_addressing_mode(ptr);
let (val, new_chain) = self.x86_dag.get_x86_load(chain, ptr, ty, addr_mode);
self.update_chain(new_chain);
(val, new_chain)
}
pub fn lower_store(&mut self, val: SDValue, ptr: SDValue) -> SDValue {
let chain = self.get_token_factor();
let addr_mode = self.recognize_addressing_mode(ptr);
let new_chain = self.x86_dag.get_x86_store(chain, val, ptr, addr_mode);
self.update_chain(new_chain);
new_chain
}
pub fn recognize_addressing_mode(&self, ptr: SDValue) -> X86AddressMode {
let ptr_node = self.x86_dag.dag.get_node(ptr.node_id);
match ptr_node {
Some(node) => match node.opcode {
SDOpcode::FrameIndex => {
X86AddressMode {
base_reg: Some(5), displacement: 0,
is_frame_relative: true,
..Default::default()
}
}
SDOpcode::Add => {
if node.operands.len() >= 2 {
let base = node.operands[0];
let offset = node.operands[1];
let disp = self.get_constant_value(offset);
if let Some(d) = disp {
let base_reg = self.recognize_base_reg(base);
return X86AddressMode::new_base_disp(base_reg, d);
}
}
X86AddressMode::default()
}
SDOpcode::GlobalAddress => {
X86AddressMode::new_rip_relative(0)
}
SDOpcode::Constant => {
let v = self
.x86_dag
.dag
.get_node(ptr.node_id)
.and_then(|n| n.get_operand(0))
.and_then(|op| self.get_constant_value(op))
.unwrap_or(0);
X86AddressMode::new_absolute(v)
}
_ => X86AddressMode::default(),
},
None => X86AddressMode::default(),
}
}
fn recognize_base_reg(&self, val: SDValue) -> u32 {
let node = self.x86_dag.dag.get_node(val.node_id);
match node {
Some(n) if n.opcode == SDOpcode::Register => {
0
}
Some(n) if n.opcode == SDOpcode::CopyFromReg => {
self.x86_dag
.get_node_info(val.node_id)
.and_then(|info| info.target_constant)
.map(|tc| tc as u32)
.unwrap_or(0)
}
_ => 0,
}
}
pub fn get_constant_value(&self, val: SDValue) -> Option<i64> {
let node = self.x86_dag.dag.get_node(val.node_id)?;
match node.opcode {
SDOpcode::Constant | SDOpcode::TargetConstant => {
self.x86_dag
.get_node_info(val.node_id)
.and_then(|n| n.target_constant)
.or_else(|| {
node.get_operand(0)
.and_then(|op| self.get_constant_value(op))
})
}
_ => None,
}
}
pub fn lower_br(&mut self, dest: SDValue) -> SDValue {
let chain = self.get_token_factor();
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::Br,
vec![Type::token()],
)
.with_operands(vec![chain, dest]);
let nid = self.x86_dag.dag.add_node(node);
let br = SDValue::new(nid, 0);
self.x86_dag.dag.set_root(br);
br
}
pub fn lower_brcond(
&mut self,
cond: SDValue,
true_dest: SDValue,
false_dest: SDValue,
) -> SDValue {
let chain = self.get_token_factor();
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::BrCond,
vec![Type::token()],
)
.with_operands(vec![chain, cond, true_dest, false_dest]);
let nid = self.x86_dag.dag.add_node(node);
let br = SDValue::new(nid, 0);
self.x86_dag.dag.set_root(br);
br
}
pub fn lower_ret(&mut self, retval: Option<SDValue>) -> SDValue {
let chain = self.get_token_factor();
self.x86_dag.get_x86_ret(chain, retval)
}
pub fn lower_call(
&mut self,
callee: SDValue,
args: &[SDValue],
ret_ty: Type,
is_tail_call: bool,
) -> (SDValue, SDValue) {
let mut chain = self.get_token_factor();
let arg_regs_64: [u32; 6] = [5, 4, 1, 2, 8, 9]; let arg_regs_32: [u32; 6] = [1, 2, 3, 4, 5, 6];
let arg_regs = if self.x86_dag.is_64bit {
&arg_regs_64[..]
} else {
&arg_regs_32[..]
};
let mut arg_nodes: Vec<SDValue> = Vec::new();
for (i, arg) in args.iter().enumerate() {
if i < arg_regs.len() {
let reg = arg_regs[i];
chain = self.x86_dag.get_copy_to_reg(chain, *arg, reg);
arg_nodes.push(*arg);
} else {
arg_nodes.push(*arg);
}
}
if is_tail_call {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::Call,
vec![Type::token()],
)
.with_operands({
let mut ops = vec![chain, callee];
ops.extend(arg_nodes.iter().copied());
ops
})
.with_target_node(true);
let nid = self.x86_dag.dag.add_node(node);
self.x86_dag.node_info.insert(
nid,
X86DAGNode {
node: SDNode::new(nid, SDOpcode::Call, vec![]),
x86_opcode: Some(X86ISD::TailCall),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let call_chain = SDValue::new(nid, 0);
self.update_chain(call_chain);
return (SDValue::new(0, 0), call_chain);
}
let (ret_val, call_chain) = self.x86_dag.get_x86_call(chain, callee, arg_nodes, ret_ty);
self.update_chain(call_chain);
let result = if !ret_ty.is_void() {
let (rax_val, _rax_chain) = self.x86_dag.get_copy_from_reg(call_chain, 0, ret_ty);
rax_val
} else {
ret_val
};
(result, call_chain)
}
pub fn lower_alloca(&mut self, size: u64, align: u32, val_id: u64) -> SDValue {
let fi_type = Type::pointer(0);
let fi = self.x86_dag.get_frame_index(fi_type.clone());
self.stack_slots.insert(val_id, fi);
self.value_map.insert(val_id, fi);
fi
}
pub fn lower_gep(&mut self, base: SDValue, indices: &[SDValue]) -> SDValue {
let mut result = base;
for idx in indices {
let addr_mode = X86AddressMode {
base_reg: Some(0),
index_reg: Some(0),
scale: 1,
displacement: 0,
..Default::default()
};
result = self.x86_dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), result, *idx);
}
result
}
pub fn lower_select(
&mut self,
cond: SDValue,
true_val: SDValue,
false_val: SDValue,
ty: Type,
) -> SDValue {
if self.x86_dag.subtarget.has_cmov() {
let cc = self.extract_condition_code(cond);
match cc {
Some(cc) => self.x86_dag.get_cmov(cc, cond, true_val, false_val, ty),
None => {
self.x86_dag.dag.get_select(cond, true_val, false_val)
}
}
} else {
self.x86_dag.dag.get_select(cond, true_val, false_val)
}
}
fn extract_condition_code(&self, val: SDValue) -> Option<X86CondCode> {
self.x86_dag
.get_node_info(val.node_id)
.and_then(|n| n.condition_code)
}
pub fn lower_zext(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::ZExt,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_sext(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::SExt,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_trunc(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::Trunc,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_sitofp(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::SIToFP,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_uitofp(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::UIToFP,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_fptosi(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::FPToSI,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_fptoui(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::FPToUI,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_bitcast(&mut self, val: SDValue, dst_ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::Bitcast,
vec![dst_ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_phi(&mut self, incoming: &[(SDValue, SDValue)]) -> SDValue {
let ty = self
.x86_dag
.dag
.get_node(incoming[0].0.node_id)
.and_then(|n| n.get_value_type(0).cloned())
.unwrap_or_else(|| Type::integer(32));
self.x86_dag.get_vreg(ty)
}
pub fn lower_build_vector(&mut self, elements: &[SDValue], ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::BuildVector,
vec![ty],
)
.with_operands(elements.to_vec());
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_extract_element(&mut self, vec: SDValue, idx: SDValue, ty: Type) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::ExtractElement,
vec![ty],
)
.with_operands(vec![vec, idx]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_insert_element(
&mut self,
vec: SDValue,
elt: SDValue,
idx: SDValue,
ty: Type,
) -> SDValue {
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::InsertElement,
vec![ty],
)
.with_operands(vec![vec, elt, idx]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_fsqrt(&mut self, val: SDValue) -> SDValue {
let ty = self
.x86_dag
.dag
.get_node(val.node_id)
.and_then(|n| n.get_value_type(0).cloned())
.unwrap_or_else(|| Type::double_precision());
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::FSqrt,
vec![ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_fabs(&mut self, val: SDValue) -> SDValue {
let ty = self
.x86_dag
.dag
.get_node(val.node_id)
.and_then(|n| n.get_value_type(0).cloned())
.unwrap_or_else(|| Type::double_precision());
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::FAbs,
vec![ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn lower_fneg(&mut self, val: SDValue) -> SDValue {
let ty = self
.x86_dag
.dag
.get_node(val.node_id)
.and_then(|n| n.get_value_type(0).cloned())
.unwrap_or_else(|| Type::double_precision());
let node = SDNode::new_with_node_id(
self.x86_dag.dag.nodes.len(),
self.x86_dag.dag.all_nodes.len(),
SDOpcode::FNeg,
vec![ty],
)
.with_operands(vec![val]);
let nid = self.x86_dag.dag.add_node(node);
SDValue::new(nid, 0)
}
pub fn get_constant(&mut self, val: i64, ty: Type) -> SDValue {
self.x86_dag.dag.get_constant(val, ty)
}
pub fn get_constant_fp(&mut self, val: f64, ty: Type) -> SDValue {
self.x86_dag.dag.get_constant_fp(val, ty)
}
pub fn finish(self) -> X86SelectionDAG {
self.x86_dag
}
pub fn dag_mut(&mut self) -> &mut X86SelectionDAG {
&mut self.x86_dag
}
pub fn dag(&self) -> &X86SelectionDAG {
&self.x86_dag
}
}
pub struct X86DAGCombine {
pub generic_combiner: DAGCombiner,
pub x86_num_combined: u64,
pub modified: bool,
pub max_iterations: u32,
pub subtarget: X86Subtarget,
}
impl X86DAGCombine {
pub fn new(subtarget: X86Subtarget) -> Self {
Self {
generic_combiner: DAGCombiner::new(),
x86_num_combined: 0,
modified: false,
max_iterations: 4,
subtarget,
}
}
pub fn combine(&mut self, x86_dag: &mut X86SelectionDAG) {
self.modified = true;
let mut iteration = 0;
while self.modified && iteration < self.max_iterations {
self.modified = false;
iteration += 1;
self.generic_combiner.combine(&mut x86_dag.dag);
self.combine_x86_patterns(x86_dag);
}
}
fn combine_x86_patterns(&mut self, x86_dag: &mut X86SelectionDAG) {
let node_ids: Vec<usize> = (0..x86_dag.dag.nodes.len()).collect();
for node_id in node_ids {
if let Some(node) = x86_dag.dag.get_node(node_id).cloned() {
match node.opcode {
SDOpcode::Add => {
self.combine_add_lea(x86_dag, node_id);
self.combine_add_identity(x86_dag, node_id);
}
SDOpcode::Sub => {
self.combine_sub_identity(x86_dag, node_id);
self.combine_cmp_sub(x86_dag, node_id);
}
SDOpcode::Mul => {
self.combine_mul_to_lea(x86_dag, node_id);
self.combine_mul_identity(x86_dag, node_id);
}
SDOpcode::And => {
self.combine_and_to_test(x86_dag, node_id);
self.combine_and_identity(x86_dag, node_id);
}
SDOpcode::Or => {
self.combine_or_identity(x86_dag, node_id);
}
SDOpcode::Xor => {
self.combine_xor_zero(x86_dag, node_id);
}
SDOpcode::Load => {
self.combine_load_store_forward(x86_dag, node_id);
self.combine_fold_load(x86_dag, node_id);
}
SDOpcode::Store => {
self.combine_store_load(x86_dag, node_id);
}
SDOpcode::Select => {
self.combine_select_to_cmov(x86_dag, node_id);
}
SDOpcode::ZExt | SDOpcode::SExt => {
self.combine_ext_trunc(x86_dag, node_id);
}
SDOpcode::Shl | SDOpcode::Sra | SDOpcode::Srl => {
self.combine_shift_amount(x86_dag, node_id);
}
SDOpcode::BrCond => {
self.combine_brcond_setcc(x86_dag, node_id);
}
_ => {}
}
}
}
}
fn combine_add_lea(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op0 = node.operands[0];
let op1 = node.operands[1];
let is_fi = x86_dag
.dag
.get_node(op0.node_id)
.map_or(false, |n| n.opcode == SDOpcode::FrameIndex);
if is_fi {
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_add_identity(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant_zero(x86_dag, op1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_sub_identity(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant_zero(x86_dag, op1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_mul_identity(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant(x86_dag, op1, 1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_and_identity(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant(x86_dag, op1, -1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_or_identity(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant_zero(x86_dag, op1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_xor_zero(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant_zero(x86_dag, op1) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_mul_to_lea(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
for &c in &[3i64, 5, 9] {
if self.is_constant(x86_dag, op1, c) {
self.modified = true;
self.x86_num_combined += 1;
return;
}
}
}
fn combine_and_to_test(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let op1 = node.operands[1];
if self.is_constant(x86_dag, op1, 0) || self.is_constant(x86_dag, op1, -1) {
return; }
self.modified = true;
self.x86_num_combined += 1;
}
fn combine_cmp_sub(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
self.modified = true;
self.x86_num_combined += 1;
}
fn combine_load_store_forward(&mut self, x86_dag: &mut X86SelectionDAG, _node_id: usize) {
self.modified = true;
self.x86_num_combined += 1;
}
fn combine_fold_load(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
self.modified = true;
self.x86_num_combined += 1;
}
fn combine_store_load(&mut self, _x86_dag: &mut X86SelectionDAG, _node_id: usize) {
self.x86_num_combined += 1;
}
fn combine_select_to_cmov(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
if !x86_dag.subtarget.has_cmov() {
return;
}
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 3 {
return;
}
let cond = node.operands[0];
let cond_node = x86_dag.dag.get_node(cond.node_id);
if let Some(cn) = cond_node {
if cn.opcode == SDOpcode::SetCC {
self.modified = true;
self.x86_num_combined += 1;
}
}
}
fn combine_ext_trunc(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.is_empty() {
return;
}
let src = node.operands[0];
let src_node = x86_dag.dag.get_node(src.node_id);
if let Some(sn) = src_node {
match (node.opcode, sn.opcode) {
(SDOpcode::ZExt, SDOpcode::Trunc) => {
if let Some(inner) = sn.get_operand(0) {
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), inner);
self.modified = true;
self.x86_num_combined += 1;
}
}
(SDOpcode::SExt, SDOpcode::Trunc) => {
if let Some(inner) = sn.get_operand(0) {
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), inner);
self.modified = true;
self.x86_num_combined += 1;
}
}
_ => {}
}
}
}
fn combine_shift_amount(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 2 {
return;
}
let amount = node.operands[1];
if self.is_constant_zero(x86_dag, amount) {
let result = node.operands[0];
x86_dag
.dag
.replace_all_uses_with(SDValue::new(node_id, 0), result);
self.modified = true;
self.x86_num_combined += 1;
}
}
fn combine_brcond_setcc(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
if node.operands.len() < 4 {
return;
}
let cond = node.operands[1];
let cond_node = x86_dag.dag.get_node(cond.node_id);
if let Some(cn) = cond_node {
if cn.opcode == SDOpcode::SetCC {
if let Some(cc) = x86_dag
.get_node_info(cond.node_id)
.and_then(|n| n.condition_code)
{
self.modified = true;
self.x86_num_combined += 1;
}
}
}
}
fn is_constant_zero(&self, x86_dag: &X86SelectionDAG, val: SDValue) -> bool {
self.is_constant(x86_dag, val, 0)
}
fn is_constant(&self, x86_dag: &X86SelectionDAG, val: SDValue, expected: i64) -> bool {
let node = match x86_dag.dag.get_node(val.node_id) {
Some(n) => n,
None => return false,
};
if node.opcode == SDOpcode::Constant || node.opcode == SDOpcode::TargetConstant {
if let Some(tc) = x86_dag
.get_node_info(val.node_id)
.and_then(|n| n.target_constant)
{
return tc == expected;
}
}
false
}
fn get_const_val(&self, x86_dag: &X86SelectionDAG, val: SDValue) -> Option<i64> {
let node = x86_dag.dag.get_node(val.node_id)?;
if node.opcode == SDOpcode::Constant || node.opcode == SDOpcode::TargetConstant {
x86_dag
.get_node_info(val.node_id)
.and_then(|n| n.target_constant)
} else {
None
}
}
}
pub struct X86DAGLegalize {
pub target_legalizer: TargetLegalizer,
pub type_legalizer: TypeLegalizer,
pub subtarget: X86Subtarget,
}
impl X86DAGLegalize {
pub fn new(subtarget: X86Subtarget) -> Self {
let target_legalizer = TargetLegalizer::x86_64();
let type_legalizer = Self::create_type_legalizer(&subtarget);
Self {
target_legalizer,
type_legalizer,
subtarget,
}
}
pub fn new_32bit(subtarget: X86Subtarget) -> Self {
let target_legalizer = TargetLegalizer::minimal_32bit();
let type_legalizer = Self::create_type_legalizer_32(&subtarget);
Self {
target_legalizer,
type_legalizer,
subtarget,
}
}
fn create_type_legalizer(subtarget: &X86Subtarget) -> TypeLegalizer {
let mut tl = TypeLegalizer::new();
tl.target_pointer_size = 64;
tl.max_legal_int_size = 64;
tl.min_legal_int_size = 8;
tl.supports_i1 = false;
if subtarget.has_avx512() {
tl.legal_vector_widths = vec![128, 256, 512];
tl = tl.with_avx512();
} else if subtarget.has_avx2() || subtarget.has_avx() {
tl.legal_vector_widths = vec![128, 256];
} else {
tl.legal_vector_widths = vec![128]; }
tl
}
fn create_type_legalizer_32(subtarget: &X86Subtarget) -> TypeLegalizer {
let mut tl = Self::create_type_legalizer(subtarget);
tl.target_pointer_size = 32;
tl.max_legal_int_size = 32;
tl
}
pub fn legalize(&mut self, x86_dag: &mut X86SelectionDAG) {
self.type_legalizer
.legalize_dag(&mut x86_dag.dag, &x86_dag.dag.nodes.clone());
self.target_legalizer.legalize(&mut x86_dag.dag);
self.legalize_x86_ops(x86_dag);
}
fn legalize_x86_ops(&mut self, x86_dag: &mut X86SelectionDAG) {
let node_ids: Vec<usize> = (0..x86_dag.dag.nodes.len()).collect();
for node_id in node_ids {
if let Some(node) = x86_dag.dag.get_node(node_id).cloned() {
match node.opcode {
SDOpcode::Add
| SDOpcode::Sub
| SDOpcode::Mul
| SDOpcode::And
| SDOpcode::Or
| SDOpcode::Xor => {
self.legalize_wide_int_op(x86_dag, node_id);
}
SDOpcode::SDiv | SDOpcode::UDiv => {
self.legalize_div(x86_dag, node_id);
}
SDOpcode::Select => {
self.legalize_select(x86_dag, node_id);
}
SDOpcode::BuildVector => {
self.legalize_build_vector(x86_dag, node_id);
}
SDOpcode::ExtractElement => {
self.legalize_extract_element(x86_dag, node_id);
}
_ => {}
}
}
}
}
fn legalize_wide_int_op(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
let ty = match node.get_value_type(0) {
Some(t) => t.clone(),
None => return,
};
let ty_bits = Self::type_size_bits(&ty);
let max_bits = if x86_dag.is_64bit { 64 } else { 32 };
if ty_bits <= max_bits {
return; }
if ty_bits == 128 {
if node.opcode == SDOpcode::SDiv || node.opcode == SDOpcode::UDiv {
self.replace_with_libcall_128_div(x86_dag, node_id);
}
}
}
fn replace_with_libcall_128_div(&mut self, _x86_dag: &mut X86SelectionDAG, _node_id: usize) {
}
fn legalize_div(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
let ty = match node.get_value_type(0) {
Some(t) => t.clone(),
None => return,
};
let ty_bits = Self::type_size_bits(&ty);
if ty_bits < 32 {
}
}
fn legalize_select(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
if x86_dag.subtarget.has_cmov() {
return; }
}
fn legalize_build_vector(&mut self, x86_dag: &mut X86SelectionDAG, node_id: usize) {
let node = match x86_dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
let ty = match node.get_value_type(0) {
Some(t) => t.clone(),
None => return,
};
let num_elems = Self::vector_element_count(&ty);
let max_elems = if x86_dag.subtarget.has_avx512() {
16
} else if x86_dag.subtarget.has_avx() || x86_dag.subtarget.has_avx2() {
8
} else {
4
};
if num_elems > max_elems {
}
}
fn legalize_extract_element(&mut self, _x86_dag: &mut X86SelectionDAG, _node_id: usize) {
}
fn type_size_bits(ty: &Type) -> u32 {
match &ty.kind {
crate::types::TypeKind::Integer { bits } => *bits,
crate::types::TypeKind::Float => 32,
crate::types::TypeKind::Double => 64,
crate::types::TypeKind::Pointer { .. } => 64,
_ => 32,
}
}
fn vector_element_count(ty: &Type) -> u32 {
match &ty.kind {
crate::types::TypeKind::FixedVector { len, .. } => *len,
_ => 1,
}
}
}
pub struct X86DAGToDAGISel {
dag: Option<X86SelectionDAG>,
mf: Option<MachineFunction>,
current_mbb: Option<MachineBasicBlock>,
instr_info: X86InstrInfo,
reg_info: X86RegisterInfo,
subtarget: X86Subtarget,
vreg_map: HashMap<usize, u32>,
next_vreg: u32,
num_selected: u64,
is_64bit: bool,
}
impl X86DAGToDAGISel {
pub fn new(subtarget: X86Subtarget) -> Self {
Self {
dag: None,
mf: None,
current_mbb: None,
instr_info: X86InstrInfo::default(),
reg_info: X86RegisterInfo::default(),
subtarget,
vreg_map: HashMap::new(),
next_vreg: 0,
num_selected: 0,
is_64bit: true,
}
}
pub fn new_32bit(subtarget: X86Subtarget) -> Self {
Self {
dag: None,
mf: None,
current_mbb: None,
instr_info: X86InstrInfo::default(),
reg_info: X86RegisterInfo::default(),
subtarget,
vreg_map: HashMap::new(),
next_vreg: 0,
num_selected: 0,
is_64bit: false,
}
}
pub fn select(&mut self, x86_dag: X86SelectionDAG, mf: &mut MachineFunction) {
self.dag = Some(x86_dag);
self.num_selected = 0;
self.select_dag_nodes();
}
fn select_dag_nodes(&mut self) {
let dag = match &self.dag {
Some(d) => d,
None => return,
};
let node_count = dag.dag.nodes.len();
let mut selected: Vec<bool> = vec![false; node_count];
for node_id in 0..node_count {
if selected[node_id] {
continue;
}
self.select_node(node_id, &mut selected);
}
for node_id in 0..dag.dag.all_nodes.len() {
if node_id < selected.len() && selected[node_id] {
continue;
}
}
}
fn select_node(&mut self, node_id: usize, selected: &mut Vec<bool>) {
let dag = match &self.dag {
Some(d) => d,
None => return,
};
let node = match dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return,
};
for op in &node.operands {
if op.node_id < selected.len() && !selected[op.node_id] {
self.select_node(op.node_id, selected);
}
}
if let Some(instrs) = self.select_instruction(node_id) {
for instr in instrs {
if let Some(ref mut mbb) = self.current_mbb {
mbb.instructions.push(instr);
self.num_selected += 1;
}
}
}
if node_id < selected.len() {
selected[node_id] = true;
}
}
fn select_instruction(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if let Some(info) = dag.get_node_info(node_id) {
if let Some(x86_op) = info.x86_opcode {
return self.select_target_node(node_id, x86_op, info);
}
}
match node.opcode {
SDOpcode::Add => self.select_add(node_id),
SDOpcode::Sub => self.select_sub(node_id),
SDOpcode::Mul => self.select_mul(node_id),
SDOpcode::SDiv => self.select_sdiv(node_id),
SDOpcode::UDiv => self.select_udiv(node_id),
SDOpcode::And => self.select_and(node_id),
SDOpcode::Or => self.select_or(node_id),
SDOpcode::Xor => self.select_xor(node_id),
SDOpcode::Shl => self.select_shl(node_id),
SDOpcode::Sra => self.select_sra(node_id),
SDOpcode::Srl => self.select_srl(node_id),
SDOpcode::Load => self.select_load(node_id),
SDOpcode::Store => self.select_store(node_id),
SDOpcode::Br => self.select_br(node_id),
SDOpcode::BrCond => self.select_brcond(node_id),
SDOpcode::Ret => self.select_ret(node_id),
SDOpcode::Call => self.select_call(node_id),
SDOpcode::SetCC => self.select_setcc(node_id),
SDOpcode::Select => self.select_select(node_id),
SDOpcode::ZExt => self.select_zext(node_id),
SDOpcode::SExt => self.select_sext(node_id),
SDOpcode::Trunc => self.select_trunc(node_id),
SDOpcode::FAdd => self.select_fadd(node_id),
SDOpcode::FSub => self.select_fsub(node_id),
SDOpcode::FMul => self.select_fmul(node_id),
SDOpcode::FDiv => self.select_fdiv(node_id),
SDOpcode::FSqrt => self.select_fsqrt(node_id),
SDOpcode::FAbs => self.select_fabs(node_id),
SDOpcode::FNeg => self.select_fneg(node_id),
SDOpcode::SIToFP => self.select_sitofp(node_id),
SDOpcode::UIToFP => self.select_uitofp(node_id),
SDOpcode::FPToSI => self.select_fptosi(node_id),
SDOpcode::FPToUI => self.select_fptoui(node_id),
SDOpcode::FrameIndex => self.select_frameindex(node_id),
SDOpcode::Constant => self.select_constant(node_id),
SDOpcode::ConstantFP => self.select_constant_fp(node_id),
SDOpcode::CopyToReg => self.select_copy_to_reg(node_id),
SDOpcode::CopyFromReg => self.select_copy_from_reg(node_id),
SDOpcode::BuildVector => self.select_build_vector(node_id),
SDOpcode::ExtractElement => self.select_extract_element(node_id),
SDOpcode::InsertElement => self.select_insert_element(node_id),
SDOpcode::TokenFactor | SDOpcode::EntryToken => Some(Vec::new()),
_ => None,
}
}
fn select_target_node(
&mut self,
node_id: usize,
x86_op: X86ISD,
info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
match x86_op {
X86ISD::Cmp | X86ISD::Test => self.select_x86_cmp(node_id, info),
X86ISD::FCmp => self.select_x86_fcmp(node_id, info),
X86ISD::SetCC => self.select_x86_setcc(node_id, info),
X86ISD::CMov => self.select_x86_cmov(node_id, info),
X86ISD::Call => self.select_x86_call(node_id, info),
X86ISD::Ret => self.select_x86_ret(node_id, info),
X86ISD::TailCall => self.select_x86_tail_call(node_id, info),
X86ISD::LeaAddr => self.select_x86_lea(node_id, info),
X86ISD::Shld => self.select_x86_shld(node_id, info),
X86ISD::Shrd => self.select_x86_shrd(node_id, info),
X86ISD::AddCarry => self.select_x86_adc(node_id, info),
X86ISD::SubBorrow => self.select_x86_sbb(node_id, info),
X86ISD::VBroadcast => self.select_x86_vbroadcast(node_id, info),
X86ISD::VShuffle => self.select_x86_vshuffle(node_id, info),
X86ISD::Fmadd => self.select_x86_fmadd(node_id, info),
X86ISD::Fmsub => self.select_x86_fmsub(node_id, info),
X86ISD::Gather => self.select_x86_gather(node_id, info),
_ => None,
}
}
fn select_add(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = node.operands[1];
if let Some(imm) = self.get_immediate(rhs) {
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::ADD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(imm),
],
false,
);
self.set_vreg(node_id, dst);
return Some(vec![instr]);
}
let rhs_vreg = self.get_vreg_for(rhs);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::ADD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_sub(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = node.operands[1];
if let Some(imm) = self.get_immediate(rhs) {
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SUB as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(imm),
],
false,
);
self.set_vreg(node_id, dst);
return Some(vec![instr]);
}
let rhs_vreg = self.get_vreg_for(rhs);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SUB as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_mul(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = node.operands[1];
let dst = self.new_vreg();
if let Some(imm) = self.get_immediate(rhs) {
let mut instr = MachineInstr::new(
X86Opcode::IMUL as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(imm),
],
false,
);
self.set_vreg(node_id, dst);
return Some(vec![instr]);
}
let rhs_vreg = self.get_vreg_for(rhs);
let mut instr = MachineInstr::new(
X86Opcode::IMUL as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_sdiv(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instrs = Vec::new();
let cdq = MachineInstr::new(
X86Opcode::ADD as u32, vec![MachineOperand::Reg(lhs)],
false,
);
instrs.push(cdq);
let idiv = MachineInstr::new(
X86Opcode::IDIV as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
instrs.push(idiv);
self.set_vreg(node_id, dst);
Some(instrs)
}
fn select_udiv(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instrs = Vec::new();
let zero_edx = MachineInstr::new(
X86Opcode::XOR as u32,
vec![
MachineOperand::Reg(self.new_vreg()),
MachineOperand::Reg(self.new_vreg()),
],
false,
);
instrs.push(zero_edx);
let div = MachineInstr::new(
X86Opcode::DIV as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
instrs.push(div);
self.set_vreg(node_id, dst);
Some(instrs)
}
fn select_and(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_binary_op(node_id, X86Opcode::AND)
}
fn select_or(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_binary_op(node_id, X86Opcode::OR)
}
fn select_xor(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_binary_op(node_id, X86Opcode::XOR)
}
fn select_shl(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_shift_op(node_id, X86Opcode::SHL)
}
fn select_sra(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_shift_op(node_id, X86Opcode::SAR)
}
fn select_srl(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_shift_op(node_id, X86Opcode::SHR)
}
fn select_binary_op(&mut self, node_id: usize, opcode: X86Opcode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = node.operands[1];
let dst = self.new_vreg();
if let Some(imm) = self.get_immediate(rhs) {
let mut instr = MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(imm),
],
false,
);
self.set_vreg(node_id, dst);
return Some(vec![instr]);
}
let rhs_vreg = self.get_vreg_for(rhs);
let mut instr = MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_shift_op(&mut self, node_id: usize, opcode: X86Opcode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let amount = node.operands[1];
let dst = self.new_vreg();
if let Some(imm) = self.get_immediate(amount) {
let shift_imm = imm & 0x3F; let mut instr = MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(shift_imm),
],
false,
);
self.set_vreg(node_id, dst);
return Some(vec![instr]);
}
let amount_vreg = self.get_vreg_for(amount);
let cl_vreg = self.new_vreg();
let mov_cl = MachineInstr::new(
X86Opcode::MOV as u32,
vec![
MachineOperand::Reg(cl_vreg),
MachineOperand::Reg(amount_vreg),
],
false,
);
let shift = MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(cl_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov_cl, shift])
}
fn select_load(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let ptr = node.operands[1]; let ptr_vreg = self.get_vreg_for(ptr);
if let Some(info) = dag.get_node_info(node_id) {
if let Some(ref addr_mode) = info.addressing_mode {
return self.select_load_with_addr(node_id, ptr_vreg, addr_mode);
}
}
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(ptr_vreg)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_load_with_addr(
&mut self,
node_id: usize,
base_vreg: u32,
_addr_mode: &X86AddressMode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(base_vreg)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_store(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let val = node.operands[1];
let ptr = node.operands[2];
let val_vreg = self.get_vreg_for(val);
let ptr_vreg = self.get_vreg_for(ptr);
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(ptr_vreg), MachineOperand::Reg(val_vreg)],
false,
);
self.set_vreg(node_id, 0);
Some(vec![instr])
}
fn select_br(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let target = node.operands[1];
let target_name = self.get_label(target);
let mut instr = MachineInstr::new(
X86Opcode::JMP as u32,
vec![MachineOperand::Label(target_name)],
false,
);
Some(vec![instr])
}
fn select_brcond(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 4 {
return None;
}
let cond = node.operands[1];
let true_dest = node.operands[2];
let false_dest = node.operands[3];
let true_label = self.get_label(true_dest);
let false_label = self.get_label(false_dest);
let cc = dag
.get_node_info(cond.node_id)
.and_then(|n| n.condition_code)
.unwrap_or(X86CondCode::E);
let jcc_opcode = self.cond_code_to_jcc(cc);
let mut instrs = Vec::new();
let jcc = MachineInstr::new(
jcc_opcode as u32,
vec![MachineOperand::Label(true_label)],
false,
);
instrs.push(jcc);
let jmp = MachineInstr::new(
X86Opcode::JMP as u32,
vec![MachineOperand::Label(false_label)],
false,
);
instrs.push(jmp);
Some(instrs)
}
fn select_ret(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let mut instr = MachineInstr::new(X86Opcode::RET as u32, Vec::new(), false);
Some(vec![instr])
}
fn select_call(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let callee = node.operands[1];
let callee_name = self.get_label(callee);
let mut instr = MachineInstr::new(
X86Opcode::CALL as u32,
vec![MachineOperand::Label(callee_name)],
false,
);
Some(vec![instr])
}
fn select_setcc(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let info = dag.get_node_info(node_id)?;
let cc = info.condition_code?;
let setcc_opcode = self.cond_code_to_setcc(cc);
let dst = self.new_vreg();
let mut instr =
MachineInstr::new(setcc_opcode as u32, vec![MachineOperand::Reg(dst)], false);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_select(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
if self.subtarget.has_cmov() {
return self.select_cmov(node_id);
}
self.select_select_as_branch(node_id)
}
fn select_cmov(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
let info = dag.get_node_info(node_id)?;
let cc = info.condition_code.unwrap_or(X86CondCode::E);
let true_val = node.operands[1];
let false_val = node.operands[2];
let true_vreg = self.get_vreg_for(true_val);
let false_vreg = self.get_vreg_for(false_val);
let dst = self.new_vreg();
let mov = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(false_vreg)],
false,
);
let cmov_opcode = self.cond_code_to_cmov(cc);
let cmov = MachineInstr::new(
cmov_opcode as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(true_vreg)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov, cmov])
}
fn select_select_as_branch(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let cond = node.operands[0];
let true_val = node.operands[1];
let false_val = node.operands[2];
let cond_vreg = self.get_vreg_for(cond);
let true_vreg = self.get_vreg_for(true_val);
let false_vreg = self.get_vreg_for(false_val);
let dst = self.new_vreg();
let cmp = MachineInstr::new(
X86Opcode::CMP as u32,
vec![MachineOperand::Reg(cond_vreg), MachineOperand::Imm(0)],
false,
);
let dst2 = self.new_vreg();
let mov_true = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst2), MachineOperand::Reg(true_vreg)],
false,
);
let mov_false = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst2), MachineOperand::Reg(false_vreg)],
false,
);
self.set_vreg(node_id, dst2);
Some(vec![cmp, mov_false, mov_true])
}
fn select_zext(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOVZX as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_sext(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOVSX as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_trunc(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
self.set_vreg(node_id, src);
Some(Vec::new())
}
fn select_fadd(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_fp_binary_op(node_id, X86Opcode::ADDSS, X86Opcode::ADDSD)
}
fn select_fsub(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_fp_binary_op(node_id, X86Opcode::SUBSS, X86Opcode::SUBSD)
}
fn select_fmul(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_fp_binary_op(node_id, X86Opcode::MULSS, X86Opcode::MULSD)
}
fn select_fdiv(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_fp_binary_op(node_id, X86Opcode::DIVSS, X86Opcode::DIVSD)
}
fn select_fsqrt(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let opcode = X86Opcode::SQRTSS;
let mut instr = MachineInstr::new(
opcode as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_fabs(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mask_vreg = self.new_vreg();
let mov_mask = MachineInstr::new(
X86Opcode::MOV as u32,
vec![
MachineOperand::Reg(mask_vreg),
MachineOperand::Imm(0x7FFFFFFF),
],
false,
);
let andps = MachineInstr::new(
X86Opcode::ANDPS as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(src),
MachineOperand::Reg(mask_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov_mask, andps])
}
fn select_fneg(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let sign_mask_vreg = self.new_vreg();
let mov_mask = MachineInstr::new(
X86Opcode::MOV as u32,
vec![
MachineOperand::Reg(sign_mask_vreg),
MachineOperand::Imm(0x80000000u64 as i64),
],
false,
);
let xorps = MachineInstr::new(
X86Opcode::XORPS as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(src),
MachineOperand::Reg(sign_mask_vreg),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov_mask, xorps])
}
fn select_fp_binary_op(
&mut self,
node_id: usize,
sse_op: X86Opcode,
sse2_op: X86Opcode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let opcode = sse2_op;
let mut instr = MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_sitofp(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::CVTSI2SD as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_uitofp(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_sitofp(node_id)
}
fn select_fptosi(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::CVTSD2SI as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_fptoui(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
self.select_fptosi(node_id)
}
fn select_frameindex(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::LEA as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(5), MachineOperand::Imm(node_id as i64),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_constant(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let info = dag.get_node_info(node_id);
let val = info.and_then(|n| n.target_constant).unwrap_or(0);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Imm(val)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_constant_fp(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOVSD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Imm(node_id as i64),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_copy_to_reg(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let val = node.operands[2];
let val_vreg = self.get_vreg_for(val);
let target_reg = dag
.get_node_info(node_id)
.and_then(|n| n.target_constant)
.map(|tc| tc as u32)
.unwrap_or(0);
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![
MachineOperand::Reg(target_reg),
MachineOperand::Reg(val_vreg),
],
false,
);
Some(vec![instr])
}
fn select_copy_from_reg(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let source_reg = dag
.get_node_info(node_id)
.and_then(|n| n.target_constant)
.map(|tc| tc as u32)
.unwrap_or(0);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(source_reg)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_build_vector(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
let dst = self.new_vreg();
let mut instrs = Vec::new();
if let Some(&first) = node.operands.first() {
let first_vreg = self.get_vreg_for(first);
let movd = MachineInstr::new(
X86Opcode::MOVD as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(first_vreg)],
false,
);
instrs.push(movd);
}
for (i, &elem) in node.operands.iter().enumerate().skip(1) {
let elem_vreg = self.get_vreg_for(elem);
let pinsr = MachineInstr::new(
X86Opcode::PINSRD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(elem_vreg),
MachineOperand::Imm(i as i64),
],
false,
);
instrs.push(pinsr);
}
self.set_vreg(node_id, dst);
Some(instrs)
}
fn select_extract_element(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let vec = self.get_vreg_for(node.operands[0]);
let idx = self.get_immediate(node.operands[1]).unwrap_or(0);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::PEXTRD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(vec),
MachineOperand::Imm(idx),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_insert_element(&mut self, node_id: usize) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let vec = self.get_vreg_for(node.operands[0]);
let elt = self.get_vreg_for(node.operands[1]);
let idx = self.get_immediate(node.operands[2]).unwrap_or(0);
let dst = self.new_vreg();
let mov = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(vec)],
false,
);
let pinsr = MachineInstr::new(
X86Opcode::PINSRD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(elt),
MachineOperand::Imm(idx),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov, pinsr])
}
fn select_x86_cmp(&mut self, node_id: usize, info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = node.operands[1];
if let Some(imm) = self.get_immediate(rhs) {
let mut instr = MachineInstr::new(
X86Opcode::CMP as u32,
vec![MachineOperand::Reg(lhs), MachineOperand::Imm(imm)],
false,
);
return Some(vec![instr]);
}
let rhs_vreg = self.get_vreg_for(rhs);
let mut instr = MachineInstr::new(
X86Opcode::CMP as u32,
vec![MachineOperand::Reg(lhs), MachineOperand::Reg(rhs_vreg)],
false,
);
Some(vec![instr])
}
fn select_x86_fcmp(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let mut instr = MachineInstr::new(
X86Opcode::UCOMISD as u32,
vec![MachineOperand::Reg(lhs), MachineOperand::Reg(rhs)],
false,
);
Some(vec![instr])
}
fn select_x86_setcc(&mut self, node_id: usize, info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let cc = info.condition_code?;
let setcc_opcode = self.cond_code_to_setcc(cc);
let dst = self.new_vreg();
let mut instr =
MachineInstr::new(setcc_opcode as u32, vec![MachineOperand::Reg(dst)], false);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_cmov(&mut self, node_id: usize, info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let cc = info.condition_code?;
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let flag = node.operands[0];
let true_val = node.operands[1];
let false_val = node.operands[2];
let true_vreg = self.get_vreg_for(true_val);
let false_vreg = self.get_vreg_for(false_val);
let dst = self.new_vreg();
let mov = MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(false_vreg)],
false,
);
let cmov_opcode = self.cond_code_to_cmov(cc);
let cmov = MachineInstr::new(
cmov_opcode as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(true_vreg)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![mov, cmov])
}
fn select_x86_call(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let callee = node.operands[1];
let callee_name = self.get_label(callee);
let mut instr = MachineInstr::new(
X86Opcode::CALL as u32,
vec![MachineOperand::Label(callee_name)],
false,
);
Some(vec![instr])
}
fn select_x86_ret(&mut self, _node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let mut instr = MachineInstr::new(X86Opcode::RET as u32, Vec::new(), false);
Some(vec![instr])
}
fn select_x86_tail_call(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let callee = node.operands[1];
let callee_name = self.get_label(callee);
let mut instr = MachineInstr::new(
X86Opcode::JMP as u32,
vec![MachineOperand::Label(callee_name)],
false,
);
Some(vec![instr])
}
fn select_x86_lea(&mut self, node_id: usize, info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let addr_mode = match &info.addressing_mode {
Some(am) => am.clone(),
None => X86AddressMode::default(),
};
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::LEA as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(0), MachineOperand::Imm(addr_mode.displacement),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_shld(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SHLD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_shrd(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SHRD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_adc(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::ADC as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_sbb(&mut self, node_id: usize, _info: &X86DAGNode) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let lhs = self.get_vreg_for(node.operands[0]);
let rhs = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SBB as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_vbroadcast(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.is_empty() {
return None;
}
let src = self.get_vreg_for(node.operands[0]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::VBROADCASTSS as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_vshuffle(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let src1 = self.get_vreg_for(node.operands[0]);
let src2 = self.get_vreg_for(node.operands[1]);
let mask = self.get_immediate(node.operands[2]).unwrap_or(0);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::SHUFPS as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(src1),
MachineOperand::Reg(src2),
MachineOperand::Imm(mask),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_fmadd(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let a = self.get_vreg_for(node.operands[0]);
let b = self.get_vreg_for(node.operands[1]);
let c = self.get_vreg_for(node.operands[2]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::VFMADD132PD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(a),
MachineOperand::Reg(b),
MachineOperand::Reg(c),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_fmsub(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 3 {
return None;
}
let a = self.get_vreg_for(node.operands[0]);
let b = self.get_vreg_for(node.operands[1]);
let c = self.get_vreg_for(node.operands[2]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::VFMSUB132PD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(a),
MachineOperand::Reg(b),
MachineOperand::Reg(c),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn select_x86_gather(
&mut self,
node_id: usize,
_info: &X86DAGNode,
) -> Option<Vec<MachineInstr>> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(node_id)?;
if node.operands.len() < 2 {
return None;
}
let base = self.get_vreg_for(node.operands[0]);
let index = self.get_vreg_for(node.operands[1]);
let dst = self.new_vreg();
let mut instr = MachineInstr::new(
X86Opcode::VPGATHERDD as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(base),
MachineOperand::Reg(index),
],
false,
);
self.set_vreg(node_id, dst);
Some(vec![instr])
}
fn cond_code_to_jcc(&self, cc: X86CondCode) -> X86Opcode {
match cc {
X86CondCode::O => X86Opcode::JO,
X86CondCode::NO => X86Opcode::JNO,
X86CondCode::B => X86Opcode::JB,
X86CondCode::AE => X86Opcode::JAE,
X86CondCode::E => X86Opcode::JE,
X86CondCode::NE => X86Opcode::JNE,
X86CondCode::BE => X86Opcode::JBE,
X86CondCode::A => X86Opcode::JA,
X86CondCode::S => X86Opcode::JS,
X86CondCode::NS => X86Opcode::JNS,
X86CondCode::P => X86Opcode::JP,
X86CondCode::NP => X86Opcode::JNP,
X86CondCode::L => X86Opcode::JL,
X86CondCode::GE => X86Opcode::JGE,
X86CondCode::LE => X86Opcode::JLE,
X86CondCode::G => X86Opcode::JG,
}
}
fn cond_code_to_setcc(&self, cc: X86CondCode) -> X86Opcode {
match cc {
X86CondCode::O => X86Opcode::SETO,
X86CondCode::NO => X86Opcode::SETNO,
X86CondCode::B => X86Opcode::SETB,
X86CondCode::AE => X86Opcode::SETAE,
X86CondCode::E => X86Opcode::SETE,
X86CondCode::NE => X86Opcode::SETNE,
X86CondCode::BE => X86Opcode::SETBE,
X86CondCode::A => X86Opcode::SETA,
X86CondCode::S => X86Opcode::SETS,
X86CondCode::NS => X86Opcode::SETNS,
X86CondCode::P => X86Opcode::SETP,
X86CondCode::NP => X86Opcode::SETNP,
X86CondCode::L => X86Opcode::SETL,
X86CondCode::GE => X86Opcode::SETGE,
X86CondCode::LE => X86Opcode::SETLE,
X86CondCode::G => X86Opcode::SETG,
}
}
fn cond_code_to_cmov(&self, cc: X86CondCode) -> X86Opcode {
match cc {
X86CondCode::O => X86Opcode::CMOVO,
X86CondCode::NO => X86Opcode::CMOVNO,
X86CondCode::B => X86Opcode::CMOVB,
X86CondCode::AE => X86Opcode::CMOVAE,
X86CondCode::E => X86Opcode::CMOVE,
X86CondCode::NE => X86Opcode::CMOVNE,
X86CondCode::BE => X86Opcode::CMOVBE,
X86CondCode::A => X86Opcode::CMOVA,
X86CondCode::S => X86Opcode::CMOVS,
X86CondCode::NS => X86Opcode::CMOVNS,
X86CondCode::P => X86Opcode::CMOVP,
X86CondCode::NP => X86Opcode::CMOVNP,
X86CondCode::L => X86Opcode::CMOVL,
X86CondCode::GE => X86Opcode::CMOVGE,
X86CondCode::LE => X86Opcode::CMOVLE,
X86CondCode::G => X86Opcode::CMOVG,
}
}
fn new_vreg(&mut self) -> u32 {
let vreg = self.next_vreg;
self.next_vreg += 1;
vreg
}
fn set_vreg(&mut self, node_id: usize, vreg: u32) {
self.vreg_map.insert(node_id, vreg);
}
fn get_vreg_for(&mut self, val: SDValue) -> u32 {
if let Some(&vreg) = self.vreg_map.get(&val.node_id) {
return vreg;
}
let vreg = self.new_vreg();
self.vreg_map.insert(val.node_id, vreg);
vreg
}
fn get_immediate(&self, val: SDValue) -> Option<i64> {
let dag = self.dag.as_ref()?;
let node = dag.dag.get_node(val.node_id)?;
if node.opcode == SDOpcode::Constant || node.opcode == SDOpcode::TargetConstant {
dag.get_node_info(val.node_id)
.and_then(|n| n.target_constant)
.or_else(|| {
let fp_bits = dag
.dag
.constant_fp_pool
.iter()
.find(|(_, sv)| sv.node_id == val.node_id)
.map(|(k, _)| k.0);
fp_bits.map(|b| b as i64)
})
} else {
None
}
}
fn get_label(&self, val: SDValue) -> String {
let dag = match self.dag.as_ref() {
Some(d) => d,
None => return format!("label_{}", val.node_id),
};
if let Some(node) = dag.dag.get_node(val.node_id) {
if node.opcode == SDOpcode::GlobalAddress
|| node.opcode == SDOpcode::ExternalSymbol
|| node.opcode == SDOpcode::BlockAddress
{
return format!("symbol_{}", val.node_id);
}
}
format!("bb_{}", val.node_id)
}
}
pub struct X86DAGSchedModel {
pub model: SchedModel,
pub model_kind: X86SchedModelKind,
pub opcode_cache: HashMap<SDOpcode, Option<InstrItinerary>>,
pub x86_opcode_cache: HashMap<X86ISD, Option<InstrItinerary>>,
}
impl X86DAGSchedModel {
pub fn new(model_kind: X86SchedModelKind) -> Self {
let model = model_kind.model();
Self {
model,
model_kind,
opcode_cache: HashMap::new(),
x86_opcode_cache: HashMap::new(),
}
}
pub fn default_x86() -> Self {
Self::new(X86SchedModelKind::Zen3)
}
pub fn get_latency(&mut self, opcode: SDOpcode) -> u32 {
if let Some(&Some(ref itin)) = self.opcode_cache.get(&opcode) {
return itin.latency;
}
let x86_opcode = self.map_sdopcode_to_x86(opcode);
let itin = lookup_itinerary(&self.model, x86_opcode as u32);
let latency = itin.as_ref().map(|i| i.latency).unwrap_or(1);
self.opcode_cache.insert(opcode, itin);
latency
}
pub fn get_x86_latency(&mut self, x86_op: X86ISD) -> u32 {
if let Some(&Some(ref itin)) = self.x86_opcode_cache.get(&x86_op) {
return itin.latency;
}
let x86_opcode = x86_op.to_x86_opcode().unwrap_or(X86Opcode::NOP);
let itin = lookup_itinerary(&self.model, x86_opcode as u32);
let latency = itin.as_ref().map(|i| i.latency).unwrap_or(1);
self.x86_opcode_cache.insert(x86_op, itin);
latency
}
pub fn get_resources(&mut self, opcode: SDOpcode) -> Vec<ProcResource> {
let x86_opcode = self.map_sdopcode_to_x86(opcode);
instruction_resources(&self.model, x86_opcode as u32)
}
pub fn get_uops(&mut self, opcode: SDOpcode) -> u32 {
let x86_opcode = self.map_sdopcode_to_x86(opcode);
instruction_uops(&self.model, x86_opcode as u32)
}
fn map_sdopcode_to_x86(&self, opcode: SDOpcode) -> X86Opcode {
match opcode {
SDOpcode::Add => X86Opcode::ADD,
SDOpcode::Sub => X86Opcode::SUB,
SDOpcode::Mul => X86Opcode::IMUL,
SDOpcode::SDiv => X86Opcode::IDIV,
SDOpcode::UDiv => X86Opcode::DIV,
SDOpcode::And => X86Opcode::AND,
SDOpcode::Or => X86Opcode::OR,
SDOpcode::Xor => X86Opcode::XOR,
SDOpcode::Shl => X86Opcode::SHL,
SDOpcode::Sra => X86Opcode::SAR,
SDOpcode::Srl => X86Opcode::SHR,
SDOpcode::Load => X86Opcode::MOV,
SDOpcode::Store => X86Opcode::MOV,
SDOpcode::Br => X86Opcode::JMP,
SDOpcode::BrCond => X86Opcode::JE,
SDOpcode::Call => X86Opcode::CALL,
SDOpcode::Ret => X86Opcode::RET,
SDOpcode::FAdd => X86Opcode::ADDSS,
SDOpcode::FSub => X86Opcode::SUBSS,
SDOpcode::FMul => X86Opcode::MULSS,
SDOpcode::FDiv => X86Opcode::DIVSS,
SDOpcode::FSqrt => X86Opcode::SQRTSS,
SDOpcode::ZExt | SDOpcode::SExt => X86Opcode::MOVSX,
SDOpcode::Trunc => X86Opcode::MOV,
SDOpcode::SIToFP | SDOpcode::UIToFP => X86Opcode::CVTSI2SD,
SDOpcode::FPToSI | SDOpcode::FPToUI => X86Opcode::CVTSD2SI,
SDOpcode::Select => X86Opcode::CMOVE,
SDOpcode::SetCC => X86Opcode::SETE,
_ => X86Opcode::NOP,
}
}
pub fn compute_critical_path(&mut self, dag: &X86SelectionDAG) -> u32 {
let mut latency: HashMap<usize, u32> = HashMap::new();
let mut max_lat = 0;
for node_id in 0..dag.dag.nodes.len() {
let node = match dag.dag.get_node(node_id) {
Some(n) => n,
None => continue,
};
let node_lat = self.get_latency(node.opcode);
let mut incoming_max = 0;
for op in &node.operands {
if let Some(&op_lat) = latency.get(&op.node_id) {
if op_lat > incoming_max {
incoming_max = op_lat;
}
}
}
let total_lat = incoming_max + node_lat;
latency.insert(node_id, total_lat);
if total_lat > max_lat {
max_lat = total_lat;
}
}
max_lat
}
}
pub struct X86InstrEmitter {
pub isel: X86DAGToDAGISel,
pub mf: MachineFunction,
pub instr_info: X86InstrInfo,
pub vreg_counter: u32,
}
impl X86InstrEmitter {
pub fn new(subtarget: X86Subtarget, func_name: &str) -> Self {
Self {
isel: X86DAGToDAGISel::new(subtarget),
mf: MachineFunction::new(func_name),
instr_info: X86InstrInfo::default(),
vreg_counter: 0,
}
}
pub fn new_32bit(subtarget: X86Subtarget, func_name: &str) -> Self {
Self {
isel: X86DAGToDAGISel::new_32bit(subtarget),
mf: MachineFunction::new(func_name),
instr_info: X86InstrInfo::default(),
vreg_counter: 0,
}
}
pub fn emit(&mut self, x86_dag: X86SelectionDAG) -> &MachineFunction {
let mbb_name = x86_dag
.dag
.nodes
.first()
.and_then(|n| n.debug_loc.clone())
.unwrap_or_else(|| "entry".to_string());
self.mf.push_block(MachineBasicBlock {
name: mbb_name,
instructions: Vec::new(),
successors: Vec::new(),
});
self.isel.current_mbb = self.mf.blocks.last().cloned();
self.isel.select(x86_dag, &mut self.mf);
&self.mf
}
pub fn emit_instr(&self, opcode: X86Opcode, operands: Vec<MachineOperand>) -> MachineInstr {
MachineInstr::new(opcode as u32, operands, false)
}
pub fn emit_mov(&self, dst: u32, src: u32) -> MachineInstr {
MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
)
}
pub fn emit_mov_imm(&self, dst: u32, imm: i64) -> MachineInstr {
MachineInstr::new(
X86Opcode::MOV as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Imm(imm)],
false,
)
}
pub fn emit_binary(&self, opcode: X86Opcode, dst: u32, lhs: u32, rhs: u32) -> MachineInstr {
MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Reg(rhs),
],
false,
)
}
pub fn emit_binary_imm(&self, opcode: X86Opcode, dst: u32, lhs: u32, imm: i64) -> MachineInstr {
MachineInstr::new(
opcode as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(lhs),
MachineOperand::Imm(imm),
],
false,
)
}
pub fn emit_unary(&self, opcode: X86Opcode, dst: u32, src: u32) -> MachineInstr {
MachineInstr::new(
opcode as u32,
vec![MachineOperand::Reg(dst), MachineOperand::Reg(src)],
false,
)
}
pub fn emit_cmp(&self, lhs: u32, rhs: u32) -> MachineInstr {
MachineInstr::new(
X86Opcode::CMP as u32,
vec![MachineOperand::Reg(lhs), MachineOperand::Reg(rhs)],
false,
)
}
pub fn emit_ret(&self) -> MachineInstr {
MachineInstr::new(X86Opcode::RET as u32, Vec::new(), false)
}
pub fn emit_call(&self, target: &str) -> MachineInstr {
MachineInstr::new(
X86Opcode::CALL as u32,
vec![MachineOperand::Label(target.to_string())],
false,
)
}
pub fn emit_jmp(&self, target: &str) -> MachineInstr {
MachineInstr::new(
X86Opcode::JMP as u32,
vec![MachineOperand::Label(target.to_string())],
false,
)
}
pub fn emit_jcc(&self, cc: X86CondCode, target: &str) -> MachineInstr {
let opcode = match cc {
X86CondCode::E => X86Opcode::JE,
X86CondCode::NE => X86Opcode::JNE,
X86CondCode::L => X86Opcode::JL,
X86CondCode::G => X86Opcode::JG,
X86CondCode::LE => X86Opcode::JLE,
X86CondCode::GE => X86Opcode::JGE,
X86CondCode::B => X86Opcode::JB,
X86CondCode::A => X86Opcode::JA,
X86CondCode::BE => X86Opcode::JBE,
X86CondCode::AE => X86Opcode::JAE,
_ => X86Opcode::JE,
};
MachineInstr::new(
opcode as u32,
vec![MachineOperand::Label(target.to_string())],
false,
)
}
pub fn emit_setcc(&self, cc: X86CondCode, dst: u32) -> MachineInstr {
let opcode = match cc {
X86CondCode::E => X86Opcode::SETE,
X86CondCode::NE => X86Opcode::SETNE,
X86CondCode::L => X86Opcode::SETL,
X86CondCode::G => X86Opcode::SETG,
X86CondCode::LE => X86Opcode::SETLE,
X86CondCode::GE => X86Opcode::SETGE,
X86CondCode::B => X86Opcode::SETB,
X86CondCode::A => X86Opcode::SETA,
X86CondCode::BE => X86Opcode::SETBE,
X86CondCode::AE => X86Opcode::SETAE,
_ => X86Opcode::SETE,
};
MachineInstr::new(opcode as u32, vec![MachineOperand::Reg(dst)], false)
}
pub fn emit_lea(&self, dst: u32, addr_mode: &X86AddressMode) -> MachineInstr {
let base = addr_mode.base_reg.unwrap_or(0);
let index = addr_mode.index_reg.unwrap_or(0);
let scale = addr_mode.scale as i64;
let disp = addr_mode.displacement;
MachineInstr::new(
X86Opcode::LEA as u32,
vec![
MachineOperand::Reg(dst),
MachineOperand::Reg(base),
MachineOperand::Reg(index),
MachineOperand::Imm(scale),
MachineOperand::Imm(disp),
],
false,
)
}
pub fn finish(self) -> MachineFunction {
self.mf
}
}
pub fn run_x86_dag_pipeline(
subtarget: &X86Subtarget,
func_name: &str,
build_fn: impl FnOnce(&mut X86DAGBuilder),
) -> MachineFunction {
let calling_conv = X86CallingConvention::default();
let mut builder = X86DAGBuilder::new(subtarget.clone(), calling_conv.clone());
build_fn(&mut builder);
let mut x86_dag = builder.finish();
{
let mut combiner = X86DAGCombine::new(subtarget.clone());
combiner.combine(&mut x86_dag);
}
{
let mut legalizer = X86DAGLegalize::new(subtarget.clone());
legalizer.legalize(&mut x86_dag);
}
let mut emitter = X86InstrEmitter::new(subtarget.clone(), func_name);
emitter.emit(x86_dag);
emitter.finish()
}
pub fn run_x86_dag_pipeline_32bit(
subtarget: &X86Subtarget,
func_name: &str,
build_fn: impl FnOnce(&mut X86DAGBuilder),
) -> MachineFunction {
let calling_conv = X86CallingConvention::default();
let mut builder = X86DAGBuilder::new_32bit(subtarget.clone(), calling_conv.clone());
build_fn(&mut builder);
let mut x86_dag = builder.finish();
{
let mut combiner = X86DAGCombine::new(subtarget.clone());
combiner.combine(&mut x86_dag);
}
{
let mut legalizer = X86DAGLegalize::new_32bit(subtarget.clone());
legalizer.legalize(&mut x86_dag);
}
let mut emitter = X86InstrEmitter::new_32bit(subtarget.clone(), func_name);
emitter.emit(x86_dag);
emitter.finish()
}
pub struct X86ComplexPatternMatcher {
pub addr_mode: X86AddressMode,
pub matched: bool,
pub result: Option<SDValue>,
pub displacement: i64,
pub base_node_id: Option<usize>,
pub index_node_id: Option<usize>,
pub scale: u8,
}
impl X86ComplexPatternMatcher {
pub fn new() -> Self {
Self {
addr_mode: X86AddressMode::default(),
matched: false,
result: None,
displacement: 0,
base_node_id: None,
index_node_id: None,
scale: 1,
}
}
pub fn match_addressing_mode(&mut self, dag: &X86SelectionDAG, node_id: usize) -> bool {
let node = match dag.dag.get_node(node_id) {
Some(n) => n.clone(),
None => return false,
};
match node.opcode {
SDOpcode::FrameIndex => {
self.base_node_id = Some(node_id);
self.addr_mode = X86AddressMode {
base_reg: Some(5), displacement: 0,
is_frame_relative: true,
..Default::default()
};
self.matched = true;
true
}
SDOpcode::GlobalAddress => {
self.addr_mode = X86AddressMode::new_rip_relative(0);
self.matched = true;
true
}
SDOpcode::Add => self.match_add_pattern(dag, &node),
SDOpcode::Shl => self.match_shl_pattern(dag, &node),
SDOpcode::Constant => {
if let Some(val) = self.get_constant_val(dag, node_id) {
self.addr_mode = X86AddressMode::new_absolute(val);
self.matched = true;
return true;
}
false
}
_ => false,
}
}
fn match_add_pattern(&mut self, dag: &X86SelectionDAG, node: &SDNode) -> bool {
if node.operands.len() < 2 {
return false;
}
let op0 = node.operands[0];
let op1 = node.operands[1];
if self.try_match_scale_index(dag, op0) {
if self.try_match_base_disp(dag, op1) {
self.build_address_mode();
self.matched = true;
return true;
}
}
if self.try_match_scale_index(dag, op1) {
if self.try_match_base_disp(dag, op0) {
self.build_address_mode();
self.matched = true;
return true;
}
}
if self.try_match_base_disp(dag, op0) {
if let Some(disp) = self.get_constant_val(dag, op1.node_id) {
self.displacement += disp;
self.build_address_mode();
self.matched = true;
return true;
}
}
if self.try_match_base_disp(dag, op1) {
if let Some(disp) = self.get_constant_val(dag, op0.node_id) {
self.displacement += disp;
self.build_address_mode();
self.matched = true;
return true;
}
}
false
}
fn match_shl_pattern(&mut self, dag: &X86SelectionDAG, node: &SDNode) -> bool {
if node.operands.len() < 2 {
return false;
}
let idx = node.operands[0];
let amount = node.operands[1];
if let Some(shift_amt) = self.get_constant_val(dag, amount.node_id) {
match shift_amt {
0 => {
self.index_node_id = Some(idx.node_id);
self.scale = 1;
}
1 => {
self.index_node_id = Some(idx.node_id);
self.scale = 2;
}
2 => {
self.index_node_id = Some(idx.node_id);
self.scale = 4;
}
3 => {
self.index_node_id = Some(idx.node_id);
self.scale = 8;
}
_ => return false, }
return true;
}
false
}
fn try_match_scale_index(&mut self, dag: &X86SelectionDAG, val: SDValue) -> bool {
let node = match dag.dag.get_node(val.node_id) {
Some(n) => n,
None => return false,
};
if node.opcode == SDOpcode::Shl {
return self.match_shl_pattern(dag, node);
}
if node.opcode == SDOpcode::Register || node.opcode == SDOpcode::CopyFromReg {
self.index_node_id = Some(val.node_id);
self.scale = 1;
return true;
}
false
}
fn try_match_base_disp(&mut self, dag: &X86SelectionDAG, val: SDValue) -> bool {
let node = match dag.dag.get_node(val.node_id) {
Some(n) => n,
None => return false,
};
match node.opcode {
SDOpcode::FrameIndex => {
if self.base_node_id.is_none() {
self.base_node_id = Some(val.node_id);
self.addr_mode.is_frame_relative = true;
return true;
}
false
}
SDOpcode::Register | SDOpcode::CopyFromReg => {
if self.base_node_id.is_none() {
self.base_node_id = Some(val.node_id);
return true;
}
false
}
SDOpcode::Add => {
if node.operands.len() >= 2 {
let inner_op0 = node.operands[0];
let inner_op1 = node.operands[1];
if let Some(disp) = self.get_constant_val(dag, inner_op1.node_id) {
self.displacement += disp;
return self.try_match_base_disp(dag, inner_op0);
}
if let Some(disp) = self.get_constant_val(dag, inner_op0.node_id) {
self.displacement += disp;
return self.try_match_base_disp(dag, inner_op1);
}
}
false
}
_ => false,
}
}
fn build_address_mode(&mut self) {
self.addr_mode = X86AddressMode {
base_reg: self.base_node_id.map(|_| 0u32), index_reg: self.index_node_id.map(|_| 0u32),
scale: self.scale,
displacement: self.displacement,
is_rip_relative: self.addr_mode.is_rip_relative,
is_frame_relative: self.addr_mode.is_frame_relative,
..Default::default()
};
}
fn get_constant_val(&self, dag: &X86SelectionDAG, node_id: usize) -> Option<i64> {
let node = dag.dag.get_node(node_id)?;
match node.opcode {
SDOpcode::Constant | SDOpcode::TargetConstant => {
dag.get_node_info(node_id)
.and_then(|n| n.target_constant)
.or_else(|| {
dag.dag
.constant_pool
.iter()
.find(|(_, sv)| sv.node_id == node_id)
.map(|((val, _), _)| *val as i64)
})
}
_ => None,
}
}
pub fn reset(&mut self) {
self.addr_mode = X86AddressMode::default();
self.matched = false;
self.result = None;
self.displacement = 0;
self.base_node_id = None;
self.index_node_id = None;
self.scale = 1;
}
}
impl Default for X86ComplexPatternMatcher {
fn default() -> Self {
Self::new()
}
}
pub struct X86ImmediateFolder {
pub enabled: bool,
pub max_imm_bits: u32,
pub allow_imm64: bool,
}
impl X86ImmediateFolder {
pub fn new(is_64bit: bool) -> Self {
Self {
enabled: true,
max_imm_bits: if is_64bit { 64 } else { 32 },
allow_imm64: is_64bit,
}
}
pub fn can_fold_immediate(&self, imm: i64) -> bool {
if !self.enabled {
return false;
}
let bits_needed = if imm >= 0 {
64 - imm.leading_zeros()
} else {
64 - (!imm).leading_zeros() + 1
};
bits_needed <= self.max_imm_bits
}
pub fn fits_imm8(&self, imm: i64) -> bool {
imm >= -128 && imm <= 127
}
pub fn fits_imm32(&self, imm: i64) -> bool {
imm >= -2_147_483_648 && imm <= 2_147_483_647
}
pub fn try_fold_imm_binary(&self, opcode: SDOpcode, imm_val: i64) -> Option<X86Opcode> {
if !self.can_fold_immediate(imm_val) {
return None;
}
match opcode {
SDOpcode::Add => Some(X86Opcode::ADD),
SDOpcode::Sub => Some(X86Opcode::SUB),
SDOpcode::And => Some(X86Opcode::AND),
SDOpcode::Or => Some(X86Opcode::OR),
SDOpcode::Xor => Some(X86Opcode::XOR),
SDOpcode::Mul => Some(X86Opcode::IMUL),
_ => None,
}
}
pub fn try_fold_imm_shift(
&self,
opcode: SDOpcode,
shift_amount: i64,
) -> Option<(X86Opcode, i64)> {
let masked = if self.allow_imm64 {
shift_amount & 0x3F
} else {
shift_amount & 0x1F
};
let x86_op = match opcode {
SDOpcode::Shl => X86Opcode::SHL,
SDOpcode::Sra => X86Opcode::SAR,
SDOpcode::Srl => X86Opcode::SHR,
_ => return None,
};
Some((x86_op, masked))
}
pub fn try_fold_cmp_imm(&self, imm_val: i64) -> Option<X86Opcode> {
if self.can_fold_immediate(imm_val) {
Some(X86Opcode::CMP)
} else {
None
}
}
}
impl Default for X86ImmediateFolder {
fn default() -> Self {
Self::new(true)
}
}
#[derive(Debug, Clone)]
pub struct X86ISelPattern {
pub sd_opcode: SDOpcode,
pub x86_opcode: X86Opcode,
pub num_operands: u32,
pub name: String,
pub feature: Option<String>,
pub sets_flags: bool,
pub is_memory_fold: bool,
pub is_3addr: bool,
pub condition_code: Option<X86CondCode>,
pub addr_pattern: Option<AddressingModePattern>,
}
pub struct X86ISelPatternTable {
pub patterns: Vec<X86ISelPattern>,
pub by_opcode: HashMap<SDOpcode, Vec<usize>>,
pub by_x86_opcode: HashMap<X86Opcode, Vec<usize>>,
}
impl X86ISelPatternTable {
pub fn new() -> Self {
let patterns = Self::build_patterns();
let mut by_opcode: HashMap<SDOpcode, Vec<usize>> = HashMap::new();
let mut by_x86_opcode: HashMap<X86Opcode, Vec<usize>> = HashMap::new();
for (i, p) in patterns.iter().enumerate() {
by_opcode.entry(p.sd_opcode).or_default().push(i);
by_x86_opcode.entry(p.x86_opcode).or_default().push(i);
}
Self {
patterns,
by_opcode,
by_x86_opcode,
}
}
fn build_patterns() -> Vec<X86ISelPattern> {
let mut p = Vec::new();
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Add,
x86_opcode: X86Opcode::ADD,
num_operands: 2,
name: "add_rr".into(),
feature: None,
sets_flags: true,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Sub,
x86_opcode: X86Opcode::SUB,
num_operands: 2,
name: "sub_rr".into(),
feature: None,
sets_flags: true,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Mul,
x86_opcode: X86Opcode::IMUL,
num_operands: 2,
name: "imul_rr".into(),
feature: None,
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::SDiv,
x86_opcode: X86Opcode::IDIV,
num_operands: 2,
name: "idiv".into(),
feature: None,
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::UDiv,
x86_opcode: X86Opcode::DIV,
num_operands: 2,
name: "div".into(),
feature: None,
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
for (sd_op, x86_op, name) in &[
(SDOpcode::And, X86Opcode::AND, "and_rr"),
(SDOpcode::Or, X86Opcode::OR, "or_rr"),
(SDOpcode::Xor, X86Opcode::XOR, "xor_rr"),
] {
p.push(X86ISelPattern {
sd_opcode: *sd_op,
x86_opcode: *x86_op,
num_operands: 2,
name: name.to_string(),
feature: None,
sets_flags: true,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
}
for (sd_op, x86_op, name) in &[
(SDOpcode::Shl, X86Opcode::SHL, "shl"),
(SDOpcode::Sra, X86Opcode::SAR, "sar"),
(SDOpcode::Srl, X86Opcode::SHR, "shr"),
] {
p.push(X86ISelPattern {
sd_opcode: *sd_op,
x86_opcode: *x86_op,
num_operands: 2,
name: name.to_string(),
feature: None,
sets_flags: true,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
}
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Load,
x86_opcode: X86Opcode::MOV,
num_operands: 2,
name: "load".into(),
feature: None,
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: Some(AddressingModePattern::BaseDisp),
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Store,
x86_opcode: X86Opcode::MOV,
num_operands: 3,
name: "store".into(),
feature: None,
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: Some(AddressingModePattern::BaseDisp),
});
for (sd_op, sse_op, sse2_op, name) in &[
(SDOpcode::FAdd, X86Opcode::ADDSS, X86Opcode::ADDSD, "fadd"),
(SDOpcode::FSub, X86Opcode::SUBSS, X86Opcode::SUBSD, "fsub"),
(SDOpcode::FMul, X86Opcode::MULSS, X86Opcode::MULSD, "fmul"),
(SDOpcode::FDiv, X86Opcode::DIVSS, X86Opcode::DIVSD, "fdiv"),
] {
p.push(X86ISelPattern {
sd_opcode: *sd_op,
x86_opcode: *sse2_op,
num_operands: 2,
name: name.to_string(),
feature: Some("sse2".into()),
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
}
for (sd_op, x86_op, name) in &[
(SDOpcode::FAdd, X86Opcode::ADDPS, "addps"),
(SDOpcode::FSub, X86Opcode::SUBPS, "subps"),
(SDOpcode::FMul, X86Opcode::MULPS, "mulps"),
(SDOpcode::FDiv, X86Opcode::DIVPS, "divps"),
(SDOpcode::And, X86Opcode::ANDPS, "andps"),
(SDOpcode::Or, X86Opcode::ORPS, "orps"),
(SDOpcode::Xor, X86Opcode::XORPS, "xorps"),
] {
p.push(X86ISelPattern {
sd_opcode: *sd_op,
x86_opcode: *x86_op,
num_operands: 2,
name: name.to_string(),
feature: Some("sse".into()),
sets_flags: false,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
}
for (sd_op, x86_op, name) in &[
(SDOpcode::FAdd, X86Opcode::VADDPS, "vaddps"),
(SDOpcode::FSub, X86Opcode::VSUBPS, "vsubps"),
(SDOpcode::FMul, X86Opcode::VMULPS, "vmulps"),
(SDOpcode::FDiv, X86Opcode::VDIVPS, "vdivps"),
(SDOpcode::And, X86Opcode::VANDPS, "vandps"),
(SDOpcode::Or, X86Opcode::VORPS, "vorps"),
(SDOpcode::Xor, X86Opcode::VXORPS, "vxorps"),
] {
p.push(X86ISelPattern {
sd_opcode: *sd_op,
x86_opcode: *x86_op,
num_operands: 3,
name: name.to_string(),
feature: Some("avx".into()),
sets_flags: false,
is_memory_fold: true,
is_3addr: true,
condition_code: None,
addr_pattern: None,
});
}
p.push(X86ISelPattern {
sd_opcode: SDOpcode::ZExt,
x86_opcode: X86Opcode::MOVZX,
num_operands: 1,
name: "movzx".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::SExt,
x86_opcode: X86Opcode::MOVSX,
num_operands: 1,
name: "movsx".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::SIToFP,
x86_opcode: X86Opcode::CVTSI2SD,
num_operands: 1,
name: "cvtsi2sd".into(),
feature: Some("sse2".into()),
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::FPToSI,
x86_opcode: X86Opcode::CVTSD2SI,
num_operands: 1,
name: "cvtsd2si".into(),
feature: Some("sse2".into()),
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Br,
x86_opcode: X86Opcode::JMP,
num_operands: 1,
name: "jmp".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::BrCond,
x86_opcode: X86Opcode::JE,
num_operands: 1,
name: "je".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: Some(X86CondCode::E),
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Ret,
x86_opcode: X86Opcode::RET,
num_operands: 0,
name: "ret".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Call,
x86_opcode: X86Opcode::CALL,
num_operands: 1,
name: "call".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: None,
addr_pattern: None,
});
p.push(X86ISelPattern {
sd_opcode: SDOpcode::Select,
x86_opcode: X86Opcode::CMOVE,
num_operands: 3,
name: "cmove".into(),
feature: Some("cmov".into()),
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: Some(X86CondCode::E),
addr_pattern: None,
});
p
}
pub fn lookup(&self, sd_opcode: SDOpcode) -> &[usize] {
self.by_opcode
.get(&sd_opcode)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn lookup_by_x86(&self, x86_opcode: X86Opcode) -> &[usize] {
self.by_x86_opcode
.get(&x86_opcode)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn len(&self) -> usize {
self.patterns.len()
}
pub fn is_empty(&self) -> bool {
self.patterns.is_empty()
}
}
impl Default for X86ISelPatternTable {
fn default() -> Self {
Self::new()
}
}
pub struct X86ABIArgLayout {
pub int_regs: Vec<u32>,
pub sse_regs: Vec<u32>,
pub num_int_reg_args: u32,
pub num_sse_reg_args: u32,
pub stack_arg_offset: u32,
pub stack_arg_size: u32,
pub ret_regs: Vec<u32>,
}
impl X86ABIArgLayout {
pub fn x86_64_sysv() -> Self {
Self {
int_regs: vec![5, 4, 1, 2, 8, 9], sse_regs: (0..8).collect(), num_int_reg_args: 6,
num_sse_reg_args: 8,
stack_arg_offset: 8, stack_arg_size: 0,
ret_regs: vec![0, 1], }
}
pub fn x86_64_win64() -> Self {
Self {
int_regs: vec![1, 2, 8, 9], sse_regs: (0..4).collect(), num_int_reg_args: 4,
num_sse_reg_args: 4,
stack_arg_offset: 32, stack_arg_size: 0,
ret_regs: vec![0],
}
}
pub fn x86_32_cdecl() -> Self {
Self {
int_regs: Vec::new(), sse_regs: Vec::new(),
num_int_reg_args: 0,
num_sse_reg_args: 0,
stack_arg_offset: 4, stack_arg_size: 0,
ret_regs: vec![0, 1], }
}
pub fn get_int_arg_reg(&self, i: u32) -> Option<u32> {
self.int_regs.get(i as usize).copied()
}
pub fn get_sse_arg_reg(&self, i: u32) -> Option<u32> {
self.sse_regs.get(i as usize).copied()
}
pub fn stack_offset_for_arg(&self, n: u32) -> u32 {
self.stack_arg_offset + n * 8
}
}
impl Default for X86ABIArgLayout {
fn default() -> Self {
Self::x86_64_sysv()
}
}
pub struct X86DAGUtils;
impl X86DAGUtils {
pub fn is_type_legal(ty: &Type, is_64bit: bool) -> bool {
match &ty.kind {
crate::types::TypeKind::Integer { bits } => match bits {
8 | 16 | 32 => true,
64 => is_64bit,
_ => false,
},
crate::types::TypeKind::Float => true,
crate::types::TypeKind::Double => true,
crate::types::TypeKind::Pointer { .. } => true,
_ => false,
}
}
pub fn get_reg_class_for_type(ty: &Type, is_64bit: bool) -> X86RegClass {
match &ty.kind {
crate::types::TypeKind::Integer { bits } => match bits {
8 => X86RegClass::Gpr8,
16 => X86RegClass::Gpr16,
32 => X86RegClass::Gpr32,
64 if is_64bit => X86RegClass::Gpr64,
_ => X86RegClass::Gpr32,
},
crate::types::TypeKind::Float | crate::types::TypeKind::Double => X86RegClass::Xmm,
crate::types::TypeKind::Pointer { .. } => {
if is_64bit {
X86RegClass::Gpr64
} else {
X86RegClass::Gpr32
}
}
_ => X86RegClass::Gpr32,
}
}
pub fn type_size_bytes(ty: &Type) -> u32 {
match &ty.kind {
crate::types::TypeKind::Integer { bits } => (*bits + 7) / 8,
crate::types::TypeKind::Float => 4,
crate::types::TypeKind::Double => 8,
crate::types::TypeKind::Pointer { .. } => 8,
_ => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86RegClass {
Gpr8,
Gpr16,
Gpr32,
Gpr64,
Xmm,
Ymm,
Zmm,
Mask,
}
impl X86RegClass {
pub fn size_bits(&self) -> u32 {
match self {
X86RegClass::Gpr8 => 8,
X86RegClass::Gpr16 => 16,
X86RegClass::Gpr32 => 32,
X86RegClass::Gpr64 => 64,
X86RegClass::Xmm => 128,
X86RegClass::Ymm => 256,
X86RegClass::Zmm => 512,
X86RegClass::Mask => 64,
}
}
pub fn name(&self) -> &'static str {
match self {
X86RegClass::Gpr8 => "GR8",
X86RegClass::Gpr16 => "GR16",
X86RegClass::Gpr32 => "GR32",
X86RegClass::Gpr64 => "GR64",
X86RegClass::Xmm => "VR128",
X86RegClass::Ymm => "VR256",
X86RegClass::Zmm => "VR512",
X86RegClass::Mask => "MASK",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_subtarget() -> X86Subtarget {
X86Subtarget::new("x86-64", "generic")
}
fn make_x86_dag() -> X86SelectionDAG {
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
X86SelectionDAG::new(subtarget, cc)
}
fn make_builder() -> X86DAGBuilder {
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
X86DAGBuilder::new(subtarget, cc)
}
fn make_combiner() -> X86DAGCombine {
X86DAGCombine::new(make_subtarget())
}
fn make_legalizer() -> X86DAGLegalize {
X86DAGLegalize::new(make_subtarget())
}
fn make_isel() -> X86DAGToDAGISel {
X86DAGToDAGISel::new(make_subtarget())
}
fn make_emitter() -> X86InstrEmitter {
X86InstrEmitter::new(make_subtarget(), "test_func")
}
#[test]
fn test_x86isd_as_str() {
assert_eq!(X86ISD::Cmp.as_str(), "X86ISD::Cmp");
assert_eq!(X86ISD::Test.as_str(), "X86ISD::Test");
assert_eq!(X86ISD::FCmp.as_str(), "X86ISD::FCmp");
assert_eq!(X86ISD::SetCC.as_str(), "X86ISD::SetCC");
assert_eq!(X86ISD::CMov.as_str(), "X86ISD::CMov");
assert_eq!(X86ISD::Call.as_str(), "X86ISD::Call");
assert_eq!(X86ISD::Ret.as_str(), "X86ISD::Ret");
assert_eq!(X86ISD::TailCall.as_str(), "X86ISD::TailCall");
assert_eq!(X86ISD::Fmadd.as_str(), "X86ISD::Fmadd");
assert_eq!(X86ISD::Gather.as_str(), "X86ISD::Gather");
}
#[test]
fn test_x86isd_is_comparison() {
assert!(X86ISD::Cmp.is_comparison());
assert!(X86ISD::Test.is_comparison());
assert!(X86ISD::FCmp.is_comparison());
assert!(!X86ISD::Call.is_comparison());
assert!(!X86ISD::SetCC.is_comparison());
}
#[test]
fn test_x86isd_sets_flags() {
assert!(X86ISD::Cmp.sets_flags());
assert!(X86ISD::Test.sets_flags());
assert!(X86ISD::FCmp.sets_flags());
assert!(X86ISD::AddCarry.sets_flags());
assert!(X86ISD::SubBorrow.sets_flags());
assert!(!X86ISD::Call.sets_flags());
}
#[test]
fn test_x86isd_to_x86_opcode() {
assert_eq!(X86ISD::Cmp.to_x86_opcode(), Some(X86Opcode::CMP));
assert_eq!(X86ISD::Test.to_x86_opcode(), Some(X86Opcode::TEST));
assert_eq!(X86ISD::Shld.to_x86_opcode(), Some(X86Opcode::SHLD));
assert_eq!(X86ISD::Shrd.to_x86_opcode(), Some(X86Opcode::SHRD));
assert_eq!(X86ISD::AddCarry.to_x86_opcode(), Some(X86Opcode::ADC));
assert_eq!(X86ISD::SubBorrow.to_x86_opcode(), Some(X86Opcode::SBB));
}
#[test]
fn test_cond_code_as_str() {
assert_eq!(X86CondCode::E.as_str(), "e");
assert_eq!(X86CondCode::NE.as_str(), "ne");
assert_eq!(X86CondCode::L.as_str(), "l");
assert_eq!(X86CondCode::G.as_str(), "g");
assert_eq!(X86CondCode::B.as_str(), "b");
assert_eq!(X86CondCode::A.as_str(), "a");
}
#[test]
fn test_cond_code_complement() {
assert_eq!(X86CondCode::E.complement(), X86CondCode::NE);
assert_eq!(X86CondCode::NE.complement(), X86CondCode::E);
assert_eq!(X86CondCode::L.complement(), X86CondCode::GE);
assert_eq!(X86CondCode::G.complement(), X86CondCode::LE);
assert_eq!(X86CondCode::B.complement(), X86CondCode::AE);
assert_eq!(X86CondCode::A.complement(), X86CondCode::BE);
}
#[test]
fn test_cond_code_swap_operands() {
assert_eq!(X86CondCode::B.swap_operands(), X86CondCode::A);
assert_eq!(X86CondCode::A.swap_operands(), X86CondCode::B);
assert_eq!(X86CondCode::L.swap_operands(), X86CondCode::G);
assert_eq!(X86CondCode::G.swap_operands(), X86CondCode::L);
assert_eq!(X86CondCode::E.swap_operands(), X86CondCode::E); }
#[test]
fn test_cond_code_from_icmp_unsigned() {
assert_eq!(X86CondCode::from_unsigned_icmp(0), X86CondCode::E);
assert_eq!(X86CondCode::from_unsigned_icmp(1), X86CondCode::NE);
assert_eq!(X86CondCode::from_unsigned_icmp(2), X86CondCode::A); assert_eq!(X86CondCode::from_unsigned_icmp(5), X86CondCode::B); }
#[test]
fn test_cond_code_from_icmp_signed() {
assert_eq!(X86CondCode::from_signed_icmp(0), X86CondCode::E);
assert_eq!(X86CondCode::from_signed_icmp(2), X86CondCode::G); assert_eq!(X86CondCode::from_signed_icmp(5), X86CondCode::L); }
#[test]
fn test_address_mode_base_disp() {
let am = X86AddressMode::new_base_disp(5, -8);
assert!(am.has_base());
assert!(!am.has_index());
assert_eq!(am.displacement, -8);
assert!(am.fits_disp8());
assert!(am.fits_disp32());
}
#[test]
fn test_address_mode_base_index_scale() {
let am = X86AddressMode::new_base_index_scale(5, 2, 4, 0);
assert!(am.has_base());
assert!(am.has_index());
assert_eq!(am.scale, 4);
assert_eq!(am.classify(), AddressingModePattern::BaseIndexScale);
}
#[test]
fn test_address_mode_rip_relative() {
let am = X86AddressMode::new_rip_relative(42);
assert!(am.is_rip_relative);
assert_eq!(am.classify(), AddressingModePattern::RIPRelative);
}
#[test]
fn test_address_mode_absolute() {
let am = X86AddressMode::new_absolute(0x1000);
assert!(!am.has_base());
assert!(!am.has_index());
assert_eq!(am.classify(), AddressingModePattern::Absolute);
}
#[test]
fn test_address_mode_classify() {
assert_eq!(
X86AddressMode::new_base_disp(5, 0).classify(),
AddressingModePattern::BaseOnly
);
assert_eq!(
X86AddressMode::new_base_disp(5, 16).classify(),
AddressingModePattern::BaseDisp
);
assert_eq!(
X86AddressMode::new_base_index_scale(5, 2, 4, 0).classify(),
AddressingModePattern::BaseIndexScale
);
assert_eq!(
X86AddressMode::new_base_index_scale(5, 2, 4, 32).classify(),
AddressingModePattern::BaseIndexScaleDisp
);
}
#[test]
fn test_address_mode_fits_disp8() {
assert!(X86AddressMode::new_base_disp(5, 0).fits_disp8());
assert!(X86AddressMode::new_base_disp(5, 127).fits_disp8());
assert!(X86AddressMode::new_base_disp(5, -128).fits_disp8());
assert!(!X86AddressMode::new_base_disp(5, 128).fits_disp8());
assert!(!X86AddressMode::new_base_disp(5, -129).fits_disp8());
}
#[test]
fn test_address_mode_hash_key() {
let am1 = X86AddressMode::new_base_disp(5, 8);
let am2 = X86AddressMode::new_base_disp(5, 8);
assert_eq!(am1.hash_key(), am2.hash_key());
let am3 = X86AddressMode::new_base_disp(5, 16);
assert_ne!(am1.hash_key(), am3.hash_key());
}
#[test]
fn test_x86_dag_creation() {
let dag = make_x86_dag();
assert!(dag.is_64bit);
assert!(!dag.is_32bit);
assert_eq!(dag.next_vreg, 0);
assert_eq!(dag.next_frame_index, 0);
}
#[test]
fn test_x86_dag_32bit() {
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
let dag = X86SelectionDAG::new_32bit(subtarget, cc);
assert!(!dag.is_64bit);
assert!(dag.is_32bit);
}
#[test]
fn test_x86_dag_pointer_type() {
let dag = make_x86_dag();
let ptr_ty = dag.pointer_type();
assert!(matches!(
ptr_ty.kind,
crate::types::TypeKind::Pointer { .. }
));
}
#[test]
fn test_x86_dag_int_reg_width() {
let dag = make_x86_dag();
assert_eq!(dag.int_reg_width(), 64);
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
let dag32 = X86SelectionDAG::new_32bit(subtarget, cc);
assert_eq!(dag32.int_reg_width(), 32);
}
#[test]
fn test_x86_dag_vreg() {
let mut dag = make_x86_dag();
let vreg1 = dag.get_vreg(Type::integer(32));
let vreg2 = dag.get_vreg(Type::integer(64));
assert_ne!(vreg1.node_id, vreg2.node_id);
}
#[test]
fn test_x86_dag_frame_index() {
let mut dag = make_x86_dag();
let fi1 = dag.get_frame_index(Type::pointer(0));
let fi2 = dag.get_frame_index(Type::pointer(0));
assert_ne!(fi1.node_id, fi2.node_id);
}
#[test]
fn test_x86_dag_get_target_node() {
let mut dag = make_x86_dag();
let node = dag.get_target_node(X86ISD::LeaAddr, vec![Type::integer(64)], Vec::new());
assert!(dag.is_x86_opcode(node.node_id, X86ISD::LeaAddr));
assert!(!dag.is_x86_opcode(node.node_id, X86ISD::Call));
}
#[test]
fn test_x86_dag_get_cmp() {
let mut dag = make_x86_dag();
let constant = dag.dag.get_constant(42u64, Type::integer(32));
let vreg = dag.get_vreg(Type::integer(32));
let cmp = dag.get_x86_cmp(vreg, constant, X86CondCode::E);
assert!(dag.is_x86_opcode(cmp.node_id, X86ISD::Cmp));
let info = dag.get_node_info(cmp.node_id).unwrap();
assert_eq!(info.condition_code, Some(X86CondCode::E));
}
#[test]
fn test_x86_dag_get_setcc() {
let mut dag = make_x86_dag();
let vreg = dag.get_vreg(Type::integer(32));
let flag = dag.get_x86_cmp(vreg, vreg, X86CondCode::NE);
let setcc = dag.get_setcc(X86CondCode::NE, flag, Type::integer(8));
assert!(dag.is_x86_opcode(setcc.node_id, X86ISD::SetCC));
}
#[test]
fn test_x86_dag_get_cmov() {
let mut dag = make_x86_dag();
let vreg = dag.get_vreg(Type::integer(32));
let flag = dag.get_x86_cmp(vreg, vreg, X86CondCode::G);
let cmov = dag.get_cmov(X86CondCode::G, flag, vreg, vreg, Type::integer(32));
assert!(dag.is_x86_opcode(cmov.node_id, X86ISD::CMov));
}
#[test]
fn test_x86_dag_get_call() {
let mut dag = make_x86_dag();
let chain = dag.dag.entry_token;
let callee = dag.dag.get_constant(0u64, Type::pointer(0));
let (val, call_chain) = dag.get_x86_call(chain, callee, Vec::new(), Type::integer(32));
assert_ne!(val.node_id, 0);
assert_ne!(call_chain.node_id, 0);
}
#[test]
fn test_x86_dag_get_ret() {
let mut dag = make_x86_dag();
let chain = dag.dag.entry_token;
let ret = dag.get_x86_ret(chain, None);
assert!(dag.is_x86_opcode(ret.node_id, X86ISD::Ret));
}
#[test]
fn test_x86_dag_copy_to_from_reg() {
let mut dag = make_x86_dag();
let chain = dag.dag.entry_token;
let val = dag.get_vreg(Type::integer(32));
let ctr = dag.get_copy_to_reg(chain, val, 0); assert_ne!(ctr.node_id, chain.node_id);
let (cfr_val, cfr_chain) = dag.get_copy_from_reg(ctr, 0, Type::integer(32));
assert_ne!(cfr_val.node_id, 0);
}
#[test]
fn test_builder_creation() {
let builder = make_builder();
assert!(builder.x86_dag.is_64bit);
}
#[test]
fn test_builder_32bit_creation() {
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
let builder = X86DAGBuilder::new_32bit(subtarget, cc);
assert!(!builder.x86_dag.is_64bit);
}
#[test]
fn test_builder_lower_add() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_add(a, b);
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_sub() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_sub(a, b);
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_mul() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_mul(a, b);
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_sdiv() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_sdiv(a, b);
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_udiv() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_udiv(a, b);
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_and_or_xor() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let and_r = builder.lower_and(a, b);
let or_r = builder.lower_or(a, b);
let xor_r = builder.lower_xor(a, b);
assert_ne!(and_r.node_id, 0);
assert_ne!(or_r.node_id, 0);
assert_ne!(xor_r.node_id, 0);
}
#[test]
fn test_builder_lower_shifts() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let shl = builder.lower_shl(a, b);
let sra = builder.lower_sra(a, b);
let srl = builder.lower_srl(a, b);
assert_ne!(shl.node_id, 0);
assert_ne!(sra.node_id, 0);
assert_ne!(srl.node_id, 0);
}
#[test]
fn test_builder_lower_icmp() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let eq = builder.lower_icmp(a, b, 0, false); assert!(builder.x86_dag.is_x86_opcode(eq.node_id, X86ISD::Cmp));
let slt = builder.lower_icmp(a, b, 5, true); assert!(builder.x86_dag.is_x86_opcode(slt.node_id, X86ISD::Cmp));
}
#[test]
fn test_builder_lower_fadd_fsub_fmul_fdiv() {
let mut builder = make_builder();
let a = builder.x86_dag.get_vreg(Type::double_precision());
let b = builder.x86_dag.get_vreg(Type::double_precision());
let fadd = builder.lower_fadd(a, b);
let fsub = builder.lower_fsub(a, b);
let fmul = builder.lower_fmul(a, b);
let fdiv = builder.lower_fdiv(a, b);
assert_ne!(fadd.node_id, 0);
assert_ne!(fsub.node_id, 0);
assert_ne!(fmul.node_id, 0);
assert_ne!(fdiv.node_id, 0);
}
#[test]
fn test_builder_lower_zext_sext_trunc() {
let mut builder = make_builder();
let val = builder.x86_dag.get_vreg(Type::integer(32));
let zext = builder.lower_zext(val, Type::integer(64));
let sext = builder.lower_sext(val, Type::integer(64));
let trunc = builder.lower_trunc(val, Type::integer(16));
assert_ne!(zext.node_id, 0);
assert_ne!(sext.node_id, 0);
assert_ne!(trunc.node_id, 0);
}
#[test]
fn test_builder_lower_sitofp_fptosi() {
let mut builder = make_builder();
let ival = builder.x86_dag.get_vreg(Type::integer(32));
let fval = builder.x86_dag.get_vreg(Type::float_precision());
let sitofp = builder.lower_sitofp(ival, Type::float_precision());
let fptosi = builder.lower_fptosi(fval, Type::integer(32));
assert_ne!(sitofp.node_id, 0);
assert_ne!(fptosi.node_id, 0);
}
#[test]
fn test_builder_lower_select() {
let mut builder = make_builder();
let cond = builder.x86_dag.get_vreg(Type::integer(1));
let tval = builder.x86_dag.get_vreg(Type::integer(32));
let fval = builder.x86_dag.get_vreg(Type::integer(32));
let result = builder.lower_select(cond, tval, fval, Type::integer(32));
assert_ne!(result.node_id, 0);
}
#[test]
fn test_builder_lower_br() {
let mut builder = make_builder();
let dest = builder.x86_dag.get_vreg(Type::label());
let br = builder.lower_br(dest);
assert_ne!(br.node_id, 0);
}
#[test]
fn test_builder_lower_brcond() {
let mut builder = make_builder();
let cond = builder.x86_dag.get_vreg(Type::integer(1));
let tdest = builder.x86_dag.get_vreg(Type::label());
let fdest = builder.x86_dag.get_vreg(Type::label());
let br = builder.lower_brcond(cond, tdest, fdest);
assert_ne!(br.node_id, 0);
}
#[test]
fn test_builder_lower_ret() {
let mut builder = make_builder();
let ret = builder.lower_ret(None);
assert!(builder.x86_dag.is_x86_opcode(ret.node_id, X86ISD::Ret));
}
#[test]
fn test_builder_lower_call() {
let mut builder = make_builder();
let callee = builder.x86_dag.get_vreg(Type::pointer(0));
let (result, chain) = builder.lower_call(callee, &[], Type::integer(32), false);
assert_ne!(chain.node_id, 0);
}
#[test]
fn test_builder_lower_tail_call() {
let mut builder = make_builder();
let callee = builder.x86_dag.get_vreg(Type::pointer(0));
let (result, chain) = builder.lower_call(callee, &[], Type::integer(32), true);
assert_ne!(chain.node_id, 0);
}
#[test]
fn test_builder_lower_alloca() {
let mut builder = make_builder();
let fi = builder.lower_alloca(16, 16, 100);
assert_ne!(fi.node_id, 0);
}
#[test]
fn test_builder_lower_load_store() {
let mut builder = make_builder();
let ptr = builder.x86_dag.get_vreg(Type::pointer(0));
let (val, chain) = builder.lower_load(ptr, Type::integer(32));
assert_ne!(val.node_id, 0);
assert_ne!(chain.node_id, 0);
}
#[test]
fn test_builder_lower_fsqrt_fabs_fneg() {
let mut builder = make_builder();
let val = builder.x86_dag.get_vreg(Type::double_precision());
let sqrt = builder.lower_fsqrt(val);
let fabs = builder.lower_fabs(val);
let fneg = builder.lower_fneg(val);
assert_ne!(sqrt.node_id, 0);
assert_ne!(fabs.node_id, 0);
assert_ne!(fneg.node_id, 0);
}
#[test]
fn test_builder_lower_build_vector() {
let mut builder = make_builder();
let e0 = builder.x86_dag.get_vreg(Type::integer(32));
let e1 = builder.x86_dag.get_vreg(Type::integer(32));
let e2 = builder.x86_dag.get_vreg(Type::integer(32));
let e3 = builder.x86_dag.get_vreg(Type::integer(32));
let vec = builder.lower_build_vector(
&[e0, e1, e2, e3],
Type::fixed_vector(4, 0), );
assert_ne!(vec.node_id, 0);
}
#[test]
fn test_builder_lower_extract_insert_element() {
let mut builder = make_builder();
let vec = builder.x86_dag.get_vreg(Type::fixed_vector(4, 0));
let idx = builder.x86_dag.get_vreg(Type::integer(32));
let elt = builder.x86_dag.get_vreg(Type::integer(32));
let extract = builder.lower_extract_element(vec, idx, Type::integer(32));
let insert = builder.lower_insert_element(vec, elt, idx, Type::fixed_vector(4, 0));
assert_ne!(extract.node_id, 0);
assert_ne!(insert.node_id, 0);
}
#[test]
fn test_builder_value_map() {
let mut builder = make_builder();
let val = builder.x86_dag.get_vreg(Type::integer(32));
builder.set_value(42, val);
assert_eq!(builder.get_value(42), Some(val));
assert_eq!(builder.get_value(99), None);
}
#[test]
fn test_builder_chain_management() {
let mut builder = make_builder();
let chain = builder.get_chain();
assert_ne!(chain.node_id, 0);
}
#[test]
fn test_builder_recognize_addressing_mode_frame_index() {
let mut builder = make_builder();
let fi = builder.x86_dag.get_frame_index(Type::pointer(0));
let am = builder.recognize_addressing_mode(fi);
assert!(am.is_frame_relative);
}
#[test]
fn test_builder_get_constant() {
let mut builder = make_builder();
let c = builder.get_constant(42u64, Type::integer(32));
assert_ne!(c.node_id, 0);
let cfp = builder.get_constant_fp(f64::to_bits(3.14), Type::double_precision());
assert_ne!(cfp.node_id, 0);
}
#[test]
fn test_combiner_creation() {
let combiner = make_combiner();
assert_eq!(combiner.x86_num_combined, 0);
assert!(!combiner.modified);
}
#[test]
fn test_combine_add_identity() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, zero);
let mut combiner = make_combiner();
combiner.combine_add_identity(&mut dag, add.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_sub_identity() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let sub = dag.dag.get_binary_op(SDOpcode::Sub, Type::integer(32), a, zero);
let mut combiner = make_combiner();
combiner.combine_sub_identity(&mut dag, sub.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_mul_identity() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let one = dag.dag.get_constant(1u64, Type::integer(32));
let mul = dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), a, one);
let mut combiner = make_combiner();
combiner.combine_mul_identity(&mut dag, mul.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_and_identity() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let all_ones = dag.dag.get_constant((-1i64) as u64, Type::integer(32));
let and = dag.dag.get_binary_op(SDOpcode::And, Type::integer(32), a, all_ones);
let mut combiner = make_combiner();
combiner.combine_and_identity(&mut dag, and.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_or_identity() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let or_op = dag.dag.get_binary_op(SDOpcode::Or, Type::integer(32), a, zero);
let mut combiner = make_combiner();
combiner.combine_or_identity(&mut dag, or_op.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_xor_zero() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let xor_op = dag.dag.get_binary_op(SDOpcode::Xor, Type::integer(32), a, zero);
let mut combiner = make_combiner();
combiner.combine_xor_zero(&mut dag, xor_op.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_mul_to_lea() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
for &c in &[3, 5, 9] {
let const_val = dag.dag.get_constant(c, Type::integer(32));
let mul = dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), a, const_val);
let mut combiner = make_combiner();
combiner.combine_mul_to_lea(&mut dag, mul.node_id);
assert!(combiner.modified);
}
}
#[test]
fn test_combine_shift_amount_zero() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
for opcode in &[SDOpcode::Shl, SDOpcode::Sra, SDOpcode::Srl] {
let shift = dag.dag.get_binary_op(*opcode, a, zero);
let mut combiner = make_combiner();
combiner.combine_shift_amount(&mut dag, shift.node_id);
assert!(combiner.modified);
}
}
#[test]
fn test_combine_ext_trunc() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let trunc = dag
.dag
.get_binary_op(SDOpcode::Trunc, a, SDValue::new(0, 0));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Trunc,
vec![Type::integer(16)],
)
.with_operands(vec![a]);
let trunc_id = dag.dag.add_node(node);
let zext_node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::ZExt,
vec![Type::integer(32)],
)
.with_operands(vec![SDValue::new(trunc_id, 0)]);
let zext_id = dag.dag.add_node(zext_node);
let mut combiner = make_combiner();
combiner.combine_ext_trunc(&mut dag, zext_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_is_constant_zero() {
let combiner = make_combiner();
let mut dag = make_x86_dag();
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let one = dag.dag.get_constant(1u64, Type::integer(32));
dag.node_info.insert(
zero.node_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(0),
},
);
dag.node_info.insert(
one.node_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(1),
},
);
assert!(combiner.is_constant_zero(&dag, zero));
assert!(!combiner.is_constant_zero(&dag, one));
}
#[test]
fn test_combine_full_run() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let _add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, zero);
let mut combiner = make_combiner();
combiner.combine(&mut dag);
assert!(combiner.x86_num_combined > 0 || combiner.modified);
}
#[test]
fn test_legalizer_creation() {
let legalizer = make_legalizer();
assert_eq!(legalizer.type_legalizer.target_pointer_size, 64);
}
#[test]
fn test_legalizer_32bit() {
let subtarget = make_subtarget();
let legalizer = X86DAGLegalize::new_32bit(subtarget);
assert_eq!(legalizer.type_legalizer.target_pointer_size, 32);
}
#[test]
fn test_legalize_x86_dag() {
let mut dag = make_x86_dag();
let mut legalizer = make_legalizer();
legalizer.legalize(&mut dag);
}
#[test]
fn test_type_size_bits() {
assert_eq!(X86DAGLegalize::type_size_bits(&Type::integer(8)), 8);
assert_eq!(X86DAGLegalize::type_size_bits(&Type::integer(32)), 32);
assert_eq!(X86DAGLegalize::type_size_bits(&Type::integer(64)), 64);
assert_eq!(X86DAGLegalize::type_size_bits(&Type::float_precision()), 32);
assert_eq!(
X86DAGLegalize::type_size_bits(&Type::double_precision()),
64
);
}
#[test]
fn test_isel_creation() {
let isel = make_isel();
assert!(isel.is_64bit);
assert_eq!(isel.num_selected, 0);
}
#[test]
fn test_isel_32bit() {
let isel = X86DAGToDAGISel::new_32bit(make_subtarget());
assert!(!isel.is_64bit);
}
#[test]
fn test_isel_select_add() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, b);
let result = isel.select_add(add.node_id);
assert!(result.is_some());
let instrs = result.unwrap();
assert!(!instrs.is_empty());
}
#[test]
fn test_isel_select_add_imm() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let imm = dag.dag.get_constant(5u64, Type::integer(32));
let add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, imm);
let result = isel.select_add(add.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_sub() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let sub = dag.dag.get_binary_op(SDOpcode::Sub, Type::integer(32), a, b);
let result = isel.select_sub(sub.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_mul() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let mul = dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), a, b);
let result = isel.select_mul(mul.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_sdiv() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let sdiv = dag.dag.get_binary_op(SDOpcode::SDiv, Type::integer(32), a, b);
let result = isel.select_sdiv(sdiv.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_udiv() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let udiv = dag.dag.get_binary_op(SDOpcode::UDiv, Type::integer(32), a, b);
let result = isel.select_udiv(udiv.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_bitwise_ops() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
for opcode in &[SDOpcode::And, SDOpcode::Or, SDOpcode::Xor] {
let op = dag.dag.get_binary_op(*opcode, a, b);
let result = match opcode {
SDOpcode::And => isel.select_and(op.node_id),
SDOpcode::Or => isel.select_or(op.node_id),
SDOpcode::Xor => isel.select_xor(op.node_id),
_ => None,
};
assert!(result.is_some(), "Failed for opcode: {:?}", opcode);
}
}
#[test]
fn test_isel_select_shifts() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let amount = dag.dag.get_constant(3u64, Type::integer(32));
let shl = isel.select_shl(dag.dag.get_binary_op(SDOpcode::Shl, Type::integer(32), a, amount).node_id);
let sra = isel.select_sra(dag.dag.get_binary_op(SDOpcode::Sra, Type::integer(32), a, amount).node_id);
let srl = isel.select_srl(dag.dag.get_binary_op(SDOpcode::Srl, Type::integer(32), a, amount).node_id);
assert!(shl.is_some());
assert!(sra.is_some());
assert!(srl.is_some());
}
#[test]
fn test_isel_select_zext_sext() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::integer(32));
let node_zext = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::ZExt,
vec![Type::integer(64)],
)
.with_operands(vec![val]);
let zext_id = dag.dag.add_node(node_zext);
let node_sext = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::SExt,
vec![Type::integer(64)],
)
.with_operands(vec![val]);
let sext_id = dag.dag.add_node(node_sext);
assert!(isel.select_zext(zext_id).is_some());
assert!(isel.select_sext(sext_id).is_some());
}
#[test]
fn test_isel_select_trunc() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::integer(32));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Trunc,
vec![Type::integer(16)],
)
.with_operands(vec![val]);
let trunc_id = dag.dag.add_node(node);
assert!(isel.select_trunc(trunc_id).is_some());
}
#[test]
fn test_isel_select_fadd() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::double_precision());
let b = dag.get_vreg(Type::double_precision());
let fadd = dag.dag.get_binary_op(SDOpcode::FAdd, Type::double_precision(), a, b);
let result = isel.select_fadd(fadd.node_id);
assert!(result.is_some());
}
#[test]
fn test_isel_select_fsqrt() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::double_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::FSqrt,
vec![Type::double_precision()],
)
.with_operands(vec![val]);
let sqrt_id = dag.dag.add_node(node);
assert!(isel.select_fsqrt(sqrt_id).is_some());
}
#[test]
fn test_isel_select_fabs() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::double_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::FAbs,
vec![Type::double_precision()],
)
.with_operands(vec![val]);
let fabs_id = dag.dag.add_node(node);
assert!(isel.select_fabs(fabs_id).is_some());
}
#[test]
fn test_isel_select_fneg() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::double_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::FNeg,
vec![Type::double_precision()],
)
.with_operands(vec![val]);
let fneg_id = dag.dag.add_node(node);
assert!(isel.select_fneg(fneg_id).is_some());
}
#[test]
fn test_isel_select_sitofp() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::integer(32));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::SIToFP,
vec![Type::double_precision()],
)
.with_operands(vec![val]);
let sitofp_id = dag.dag.add_node(node);
assert!(isel.select_sitofp(sitofp_id).is_some());
}
#[test]
fn test_isel_select_fptosi() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let val = dag.get_vreg(Type::double_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::FPToSI,
vec![Type::integer(32)],
)
.with_operands(vec![val]);
let fptosi_id = dag.dag.add_node(node);
assert!(isel.select_fptosi(fptosi_id).is_some());
}
#[test]
fn test_isel_select_br() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let dest = dag.get_vreg(Type::label());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Br,
vec![Type::token()],
)
.with_operands(vec![dag.dag.entry_token, dest]);
let br_id = dag.dag.add_node(node);
assert!(isel.select_br(br_id).is_some());
}
#[test]
fn test_isel_select_ret() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Ret,
vec![Type::token()],
)
.with_operands(vec![dag.dag.entry_token]);
let ret_id = dag.dag.add_node(node);
assert!(isel.select_ret(ret_id).is_some());
}
#[test]
fn test_isel_select_call() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let callee = dag.get_vreg(Type::pointer(0));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Call,
vec![Type::integer(32), Type::token()],
)
.with_operands(vec![dag.dag.entry_token, callee]);
let call_id = dag.dag.add_node(node);
assert!(isel.select_call(call_id).is_some());
}
#[test]
fn test_isel_select_setcc() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let cmp = dag.get_x86_cmp(a, b, X86CondCode::E);
assert!(isel.select_setcc(cmp.node_id).is_some());
}
#[test]
fn test_isel_select_frameindex() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let fi = dag.get_frame_index(Type::pointer(0));
assert!(isel.select_frameindex(fi.node_id).is_some());
}
#[test]
fn test_isel_select_constant() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let c = dag.dag.get_constant(42u64, Type::integer(32));
dag.node_info.insert(
c.node_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(42),
},
);
assert!(isel.select_constant(c.node_id).is_some());
}
#[test]
fn test_isel_select_build_vector() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let e0 = dag.get_vreg(Type::integer(32));
let e1 = dag.get_vreg(Type::integer(32));
let e2 = dag.get_vreg(Type::integer(32));
let e3 = dag.get_vreg(Type::integer(32));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::BuildVector,
vec![Type::fixed_vector(4, 0)],
)
.with_operands(vec![e0, e1, e2, e3]);
let bv_id = dag.dag.add_node(node);
assert!(isel.select_build_vector(bv_id).is_some());
}
#[test]
fn test_isel_select_extract_element() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let vec = dag.get_vreg(Type::fixed_vector(4, 0));
let idx = dag.dag.get_constant(2u64, Type::integer(32));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::ExtractElement,
vec![Type::integer(32)],
)
.with_operands(vec![vec, idx]);
let ee_id = dag.dag.add_node(node);
assert!(isel.select_extract_element(ee_id).is_some());
}
#[test]
fn test_isel_select_insert_element() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let vec = dag.get_vreg(Type::fixed_vector(4, 0));
let elt = dag.get_vreg(Type::integer(32));
let idx = dag.dag.get_constant(1u64, Type::integer(32));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::InsertElement,
vec![Type::fixed_vector(4, 0)],
)
.with_operands(vec![vec, elt, idx]);
let ie_id = dag.dag.add_node(node);
assert!(isel.select_insert_element(ie_id).is_some());
}
#[test]
fn test_isel_cond_code_to_jcc() {
let isel = make_isel();
assert_eq!(isel.cond_code_to_jcc(X86CondCode::E), X86Opcode::JE);
assert_eq!(isel.cond_code_to_jcc(X86CondCode::NE), X86Opcode::JNE);
assert_eq!(isel.cond_code_to_jcc(X86CondCode::L), X86Opcode::JL);
assert_eq!(isel.cond_code_to_jcc(X86CondCode::G), X86Opcode::JG);
}
#[test]
fn test_isel_cond_code_to_setcc() {
let isel = make_isel();
assert_eq!(isel.cond_code_to_setcc(X86CondCode::E), X86Opcode::SETE);
assert_eq!(isel.cond_code_to_setcc(X86CondCode::NE), X86Opcode::SETNE);
}
#[test]
fn test_isel_cond_code_to_cmov() {
let isel = make_isel();
assert_eq!(isel.cond_code_to_cmov(X86CondCode::E), X86Opcode::CMOVE);
assert_eq!(isel.cond_code_to_cmov(X86CondCode::G), X86Opcode::CMOVG);
}
#[test]
fn test_isel_vreg_management() {
let mut isel = make_isel();
let vreg1 = isel.new_vreg();
let vreg2 = isel.new_vreg();
assert_ne!(vreg1, vreg2);
assert!(vreg2 > vreg1);
}
#[test]
fn test_isel_get_immediate() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let c = dag.dag.get_constant(42u64, Type::integer(32));
dag.node_info.insert(
c.node_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None,
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: Some(42),
},
);
isel.dag = Some(dag);
let imm = isel.get_immediate(c);
assert_eq!(imm, Some(42));
}
#[test]
fn test_isel_select_copy_to_from_reg() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let chain = dag.dag.entry_token;
let val = dag.get_vreg(Type::integer(32));
let ctr = dag.get_copy_to_reg(chain, val, 0);
let (cfr_val, cfr_chain) = dag.get_copy_from_reg(ctr, 0, Type::integer(32));
isel.dag = Some(dag);
let ctr_instrs = isel.select_copy_to_reg(ctr.node_id);
let cfr_instrs = isel.select_copy_from_reg(cfr_val.node_id);
assert!(ctr_instrs.is_some());
assert!(cfr_instrs.is_some());
}
#[test]
fn test_sched_model_creation() {
let model = X86DAGSchedModel::default_x86();
assert_eq!(model.model_kind, X86SchedModelKind::Zen3);
}
#[test]
fn test_sched_model_latency() {
let mut model = X86DAGSchedModel::default_x86();
let lat = model.get_latency(SDOpcode::Add);
assert!(lat > 0);
}
#[test]
fn test_sched_model_x86_latency() {
let mut model = X86DAGSchedModel::default_x86();
let lat = model.get_x86_latency(X86ISD::Cmp);
assert!(lat > 0);
}
#[test]
fn test_sched_model_critical_path() {
let mut model = X86DAGSchedModel::default_x86();
let dag = make_x86_dag();
let cp = model.compute_critical_path(&dag);
assert!(cp >= 0);
}
#[test]
fn test_sched_model_uops() {
let mut model = X86DAGSchedModel::default_x86();
let uops = model.get_uops(SDOpcode::Add);
assert!(uops > 0);
}
#[test]
fn test_sched_model_map_sdopcode() {
let model = X86DAGSchedModel::default_x86();
assert_eq!(model.map_sdopcode_to_x86(SDOpcode::Add), X86Opcode::ADD);
assert_eq!(model.map_sdopcode_to_x86(SDOpcode::Sub), X86Opcode::SUB);
assert_eq!(model.map_sdopcode_to_x86(SDOpcode::Mul), X86Opcode::IMUL);
assert_eq!(model.map_sdopcode_to_x86(SDOpcode::SDiv), X86Opcode::IDIV);
}
#[test]
fn test_emitter_creation() {
let emitter = make_emitter();
assert_eq!(emitter.mf.name, "test_func");
}
#[test]
fn test_emitter_emit_mov() {
let emitter = make_emitter();
let instr = emitter.emit_mov(1, 2);
assert_eq!(instr.opcode, X86Opcode::MOV as u32);
}
#[test]
fn test_emitter_emit_mov_imm() {
let emitter = make_emitter();
let instr = emitter.emit_mov_imm(1, 42);
assert_eq!(instr.opcode, X86Opcode::MOV as u32);
}
#[test]
fn test_emitter_emit_binary() {
let emitter = make_emitter();
let instr = emitter.emit_binary(X86Opcode::ADD, 1, 2, 3);
assert_eq!(instr.opcode, X86Opcode::ADD as u32);
assert_eq!(instr.operands.len(), 3);
}
#[test]
fn test_emitter_emit_binary_imm() {
let emitter = make_emitter();
let instr = emitter.emit_binary_imm(X86Opcode::ADD, 1, 2, 42);
assert_eq!(instr.opcode, X86Opcode::ADD as u32);
}
#[test]
fn test_emitter_emit_cmp() {
let emitter = make_emitter();
let instr = emitter.emit_cmp(1, 2);
assert_eq!(instr.opcode, X86Opcode::CMP as u32);
}
#[test]
fn test_emitter_emit_ret() {
let emitter = make_emitter();
let instr = emitter.emit_ret();
assert_eq!(instr.opcode, X86Opcode::RET as u32);
}
#[test]
fn test_emitter_emit_call() {
let emitter = make_emitter();
let instr = emitter.emit_call("printf");
assert_eq!(instr.opcode, X86Opcode::CALL as u32);
}
#[test]
fn test_emitter_emit_jmp() {
let emitter = make_emitter();
let instr = emitter.emit_jmp(".L1");
assert_eq!(instr.opcode, X86Opcode::JMP as u32);
}
#[test]
fn test_emitter_emit_jcc() {
let emitter = make_emitter();
let instr = emitter.emit_jcc(X86CondCode::E, ".L1");
assert_eq!(instr.opcode, X86Opcode::JE as u32);
}
#[test]
fn test_emitter_emit_setcc() {
let emitter = make_emitter();
let instr = emitter.emit_setcc(X86CondCode::NE, 1);
assert_eq!(instr.opcode, X86Opcode::SETNE as u32);
}
#[test]
fn test_emitter_emit_lea() {
let emitter = make_emitter();
let am = X86AddressMode::new_base_index_scale(5, 2, 4, 16);
let instr = emitter.emit_lea(1, &am);
assert_eq!(instr.opcode, X86Opcode::LEA as u32);
}
#[test]
fn test_full_pipeline_simple_function() {
let subtarget = make_subtarget();
let mf = run_x86_dag_pipeline(&subtarget, "simple_add", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(a, b);
let _ret = builder.lower_ret(Some(sum));
});
assert_eq!(mf.name, "simple_add");
assert!(!mf.blocks.is_empty());
}
#[test]
fn test_full_pipeline_arithmetic() {
let subtarget = make_subtarget();
let mf = run_x86_dag_pipeline(&subtarget, "arith", |builder| {
let x = builder.x86_dag.get_vreg(Type::integer(32));
let y = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(x, y);
let diff = builder.lower_sub(sum, x);
let prod = builder.lower_mul(diff, y);
let _ret = builder.lower_ret(Some(prod));
});
assert_eq!(mf.name, "arith");
}
#[test]
fn test_full_pipeline_32bit() {
let subtarget = make_subtarget();
let mf = run_x86_dag_pipeline_32bit(&subtarget, "simple32", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(a, b);
let _ret = builder.lower_ret(Some(sum));
});
assert_eq!(mf.name, "simple32");
}
#[test]
fn test_full_pipeline_with_calls() {
let subtarget = make_subtarget();
let mf = run_x86_dag_pipeline(&subtarget, "call_func", |builder| {
let callee = builder.x86_dag.get_vreg(Type::pointer(0));
let arg = builder.x86_dag.get_vreg(Type::integer(32));
let (result, _chain) = builder.lower_call(callee, &[arg], Type::integer(32), false);
let _ret = builder.lower_ret(Some(result));
});
assert_eq!(mf.name, "call_func");
}
#[test]
fn test_x86isd_enum_exhaustiveness() {
let variants = [
X86ISD::Cmp,
X86ISD::Test,
X86ISD::FCmp,
X86ISD::SetCC,
X86ISD::CMov,
X86ISD::BitTest,
X86ISD::BitTestAndSet,
X86ISD::BitTestAndReset,
X86ISD::BitTestAndComplement,
X86ISD::Shld,
X86ISD::Shrd,
X86ISD::UDivRem,
X86ISD::SDivRem,
X86ISD::MulHW,
X86ISD::UMulH,
];
for v in &variants {
assert!(!v.as_str().is_empty());
}
}
#[test]
fn test_cond_code_from_icmp_all_predicates() {
for pred in 0..6u32 {
let _unsigned = X86CondCode::from_unsigned_icmp(pred);
let _signed = X86CondCode::from_signed_icmp(pred);
}
}
#[test]
fn test_cond_code_from_fcmp_all_predicates() {
for pred in 0..14u32 {
let _fcmp = X86CondCode::from_fcmp(pred);
}
}
#[test]
fn test_builder_full_instruction_set() {
let subtarget = make_subtarget();
let cc = X86CallingConvention::default();
let mut builder = X86DAGBuilder::new(subtarget, cc);
let i32 = Type::integer(32);
let i64 = Type::integer(64);
let f64 = Type::double_precision();
let ptr = Type::pointer(0);
let a = builder.x86_dag.get_vreg(i32.clone());
let b = builder.x86_dag.get_vreg(i32.clone());
let fa = builder.x86_dag.get_vreg(f64.clone());
let fb = builder.x86_dag.get_vreg(f64.clone());
let p = builder.x86_dag.get_vreg(ptr.clone());
let _ = builder.lower_add(a, b);
let _ = builder.lower_sub(a, b);
let _ = builder.lower_mul(a, b);
let _ = builder.lower_sdiv(a, b);
let _ = builder.lower_udiv(a, b);
let _ = builder.lower_and(a, b);
let _ = builder.lower_or(a, b);
let _ = builder.lower_xor(a, b);
let _ = builder.lower_shl(a, b);
let _ = builder.lower_sra(a, b);
let _ = builder.lower_srl(a, b);
let _ = builder.lower_fadd(fa, fb);
let _ = builder.lower_fsub(fa, fb);
let _ = builder.lower_fmul(fa, fb);
let _ = builder.lower_fdiv(fa, fb);
let _ = builder.lower_icmp(a, b, 0, false); let _ = builder.lower_fcmp(fa, fb, 0);
let _ = builder.lower_zext(a, i64.clone());
let _ = builder.lower_sext(a, i64.clone());
let _ = builder.lower_trunc(a, Type::integer(16));
let _ = builder.lower_sitofp(a, f64.clone());
let _ = builder.lower_fptosi(fa, i32.clone());
let _ = builder.lower_fsqrt(fa);
let _ = builder.lower_fabs(fa);
let _ = builder.lower_fneg(fa);
let _ = builder.lower_br(p);
let _ = builder.lower_brcond(a, p, p);
let _ = builder.lower_ret(Some(a));
let (_, _) = builder.lower_call(p, &[a], i32.clone(), false);
}
#[test]
fn test_isel_all_opcode_selection() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let i32 = Type::integer(32);
let f64 = Type::double_precision();
let ptr = Type::pointer(0);
let a = dag.get_vreg(i32.clone());
let b = dag.get_vreg(i32.clone());
let c = dag.get_vreg(f64.clone());
let d = dag.get_vreg(f64.clone());
let p = dag.get_vreg(ptr.clone());
isel.dag = Some(dag);
let add = isel
.dag
.as_ref()
.unwrap()
.dag
.get_binary_op(SDOpcode::Add, Type::integer(32), a, b);
assert!(isel.select_add(add.node_id).is_some());
let sub = isel
.dag
.as_ref()
.unwrap()
.dag
.get_binary_op(SDOpcode::Sub, Type::integer(32), a, b);
assert!(isel.select_sub(sub.node_id).is_some());
let fadd = isel
.dag
.as_ref()
.unwrap()
.dag
.get_binary_op(SDOpcode::FAdd, Type::double_precision(), c, d);
assert!(isel.select_fadd(fadd.node_id).is_some());
}
#[test]
fn test_pipeline_idempotent() {
let subtarget = make_subtarget();
let mf1 = run_x86_dag_pipeline(&subtarget, "idem", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let _ret = builder.lower_ret(Some(a));
});
let mf2 = run_x86_dag_pipeline(&subtarget, "idem", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let _ret = builder.lower_ret(Some(a));
});
assert_eq!(mf1.name, mf2.name);
}
#[test]
fn test_emitter_finish() {
let mut emitter = make_emitter();
let mf = emitter.finish();
assert_eq!(mf.name, "test_func");
}
#[test]
fn test_x86dag_combine_multiple_iterations() {
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, zero);
let _add2 = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), add, zero);
let mut combiner = make_combiner();
combiner.combine(&mut dag);
assert!(combiner.x86_num_combined > 0);
}
#[test]
fn test_complex_pattern_matcher_new() {
let matcher = X86ComplexPatternMatcher::new();
assert!(!matcher.matched);
assert_eq!(matcher.scale, 1);
assert_eq!(matcher.displacement, 0);
}
#[test]
fn test_complex_pattern_match_frame_index() {
let mut matcher = X86ComplexPatternMatcher::new();
let mut dag = make_x86_dag();
let fi = dag.get_frame_index(Type::pointer(0));
let matched = matcher.match_addressing_mode(&dag, fi.node_id);
assert!(matched);
assert!(matcher.matched);
assert!(matcher.addr_mode.is_frame_relative);
}
#[test]
fn test_complex_pattern_reset() {
let mut matcher = X86ComplexPatternMatcher::new();
let mut dag = make_x86_dag();
let fi = dag.get_frame_index(Type::pointer(0));
matcher.match_addressing_mode(&dag, fi.node_id);
assert!(matcher.matched);
matcher.reset();
assert!(!matcher.matched);
assert_eq!(matcher.scale, 1);
assert_eq!(matcher.displacement, 0);
}
#[test]
fn test_complex_pattern_default() {
let matcher = X86ComplexPatternMatcher::default();
assert!(!matcher.matched);
}
#[test]
fn test_immediate_folder_new_64bit() {
let folder = X86ImmediateFolder::new(true);
assert!(folder.enabled);
assert!(folder.allow_imm64);
assert_eq!(folder.max_imm_bits, 64);
}
#[test]
fn test_immediate_folder_new_32bit() {
let folder = X86ImmediateFolder::new(false);
assert!(!folder.allow_imm64);
assert_eq!(folder.max_imm_bits, 32);
}
#[test]
fn test_immediate_folder_fits_imm8() {
let folder = X86ImmediateFolder::default();
assert!(folder.fits_imm8(0));
assert!(folder.fits_imm8(127));
assert!(folder.fits_imm8(-128));
assert!(!folder.fits_imm8(128));
assert!(!folder.fits_imm8(-129));
}
#[test]
fn test_immediate_folder_fits_imm32() {
let folder = X86ImmediateFolder::default();
assert!(folder.fits_imm32(0));
assert!(folder.fits_imm32(2147483647));
assert!(folder.fits_imm32(-2147483648));
assert!(!folder.fits_imm32(2147483648i64));
assert!(!folder.fits_imm32(-2147483649i64));
}
#[test]
fn test_immediate_folder_can_fold() {
let folder = X86ImmediateFolder::new(true);
assert!(folder.can_fold_immediate(0));
assert!(folder.can_fold_immediate(42));
assert!(folder.can_fold_immediate(-1));
}
#[test]
fn test_immediate_folder_try_fold_binary() {
let folder = X86ImmediateFolder::default();
assert_eq!(
folder.try_fold_imm_binary(SDOpcode::Add, 42),
Some(X86Opcode::ADD)
);
assert_eq!(
folder.try_fold_imm_binary(SDOpcode::Sub, -8),
Some(X86Opcode::SUB)
);
assert_eq!(
folder.try_fold_imm_binary(SDOpcode::And, 0xFF),
Some(X86Opcode::AND)
);
assert_eq!(
folder.try_fold_imm_binary(SDOpcode::Mul, 3),
Some(X86Opcode::IMUL)
);
assert_eq!(folder.try_fold_imm_binary(SDOpcode::Load, 0), None);
}
#[test]
fn test_immediate_folder_try_fold_shift() {
let folder = X86ImmediateFolder::default();
assert_eq!(
folder.try_fold_imm_shift(SDOpcode::Shl, 3),
Some((X86Opcode::SHL, 3))
);
assert_eq!(
folder.try_fold_imm_shift(SDOpcode::Sra, 5),
Some((X86Opcode::SAR, 5))
);
assert_eq!(
folder.try_fold_imm_shift(SDOpcode::Srl, 7),
Some((X86Opcode::SHR, 7))
);
assert_eq!(folder.try_fold_imm_shift(SDOpcode::Add, 1), None);
}
#[test]
fn test_immediate_folder_try_fold_cmp() {
let folder = X86ImmediateFolder::default();
assert_eq!(folder.try_fold_cmp_imm(0), Some(X86Opcode::CMP));
}
#[test]
fn test_isel_pattern_table_creation() {
let table = X86ISelPatternTable::new();
assert!(!table.is_empty());
assert!(table.len() > 0);
}
#[test]
fn test_isel_pattern_table_lookup() {
let table = X86ISelPatternTable::new();
let add_patterns = table.lookup(SDOpcode::Add);
assert!(!add_patterns.is_empty());
}
#[test]
fn test_isel_pattern_table_lookup_by_x86() {
let table = X86ISelPatternTable::new();
let add_patterns = table.lookup_by_x86(X86Opcode::ADD);
assert!(!add_patterns.is_empty());
}
#[test]
fn test_isel_pattern_table_lookup_unknown() {
let table = X86ISelPatternTable::new();
let pats = table.lookup(SDOpcode::LibCall);
assert!(pats.is_empty());
}
#[test]
fn test_isel_pattern_table_default() {
let table = X86ISelPatternTable::default();
assert!(!table.is_empty());
}
#[test]
fn test_isel_pattern_has_avx_patterns() {
let table = X86ISelPatternTable::new();
let avx_patterns = table.lookup_by_x86(X86Opcode::VADDPS);
assert!(!avx_patterns.is_empty());
}
#[test]
fn test_isel_pattern_has_sse_patterns() {
let table = X86ISelPatternTable::new();
let sse_patterns = table.lookup_by_x86(X86Opcode::ADDPS);
assert!(!sse_patterns.is_empty());
}
#[test]
fn test_abi_layout_x86_64_sysv() {
let layout = X86ABIArgLayout::x86_64_sysv();
assert_eq!(layout.int_regs.len(), 6);
assert_eq!(layout.sse_regs.len(), 8);
assert_eq!(layout.get_int_arg_reg(0), Some(5));
assert_eq!(layout.get_int_arg_reg(1), Some(4));
assert_eq!(layout.get_int_arg_reg(2), Some(1));
assert_eq!(layout.get_int_arg_reg(6), None);
}
#[test]
fn test_abi_layout_x86_64_win64() {
let layout = X86ABIArgLayout::x86_64_win64();
assert_eq!(layout.int_regs.len(), 4);
assert_eq!(layout.get_int_arg_reg(0), Some(1));
assert_eq!(layout.get_int_arg_reg(3), Some(9));
assert_eq!(layout.get_int_arg_reg(4), None);
}
#[test]
fn test_abi_layout_x86_32_cdecl() {
let layout = X86ABIArgLayout::x86_32_cdecl();
assert_eq!(layout.int_regs.len(), 0);
assert_eq!(layout.get_int_arg_reg(0), None);
}
#[test]
fn test_abi_layout_stack_offset() {
let layout = X86ABIArgLayout::x86_64_sysv();
assert_eq!(layout.stack_offset_for_arg(0), 8);
assert_eq!(layout.stack_offset_for_arg(1), 16);
assert_eq!(layout.stack_offset_for_arg(2), 24);
}
#[test]
fn test_abi_layout_default() {
let layout = X86ABIArgLayout::default();
assert_eq!(layout.int_regs.len(), 6);
}
#[test]
fn test_dag_utils_is_type_legal() {
assert!(X86DAGUtils::is_type_legal(&Type::integer(8), true));
assert!(X86DAGUtils::is_type_legal(&Type::integer(32), true));
assert!(X86DAGUtils::is_type_legal(&Type::integer(64), true));
assert!(!X86DAGUtils::is_type_legal(&Type::integer(64), false));
assert!(!X86DAGUtils::is_type_legal(&Type::integer(128), true));
assert!(X86DAGUtils::is_type_legal(&Type::float_precision(), true));
assert!(X86DAGUtils::is_type_legal(&Type::double_precision(), true));
}
#[test]
fn test_dag_utils_get_reg_class() {
assert_eq!(
X86DAGUtils::get_reg_class_for_type(&Type::integer(8), true),
X86RegClass::Gpr8
);
assert_eq!(
X86DAGUtils::get_reg_class_for_type(&Type::integer(32), true),
X86RegClass::Gpr32
);
assert_eq!(
X86DAGUtils::get_reg_class_for_type(&Type::integer(64), true),
X86RegClass::Gpr64
);
assert_eq!(
X86DAGUtils::get_reg_class_for_type(&Type::integer(64), false),
X86RegClass::Gpr32
);
assert_eq!(
X86DAGUtils::get_reg_class_for_type(&Type::float_precision(), true),
X86RegClass::Xmm
);
}
#[test]
fn test_dag_utils_type_size_bytes() {
assert_eq!(X86DAGUtils::type_size_bytes(&Type::integer(8)), 1);
assert_eq!(X86DAGUtils::type_size_bytes(&Type::integer(32)), 4);
assert_eq!(X86DAGUtils::type_size_bytes(&Type::integer(64)), 8);
assert_eq!(X86DAGUtils::type_size_bytes(&Type::float_precision()), 4);
assert_eq!(X86DAGUtils::type_size_bytes(&Type::double_precision()), 8);
}
#[test]
fn test_reg_class_size_bits() {
assert_eq!(X86RegClass::Gpr8.size_bits(), 8);
assert_eq!(X86RegClass::Gpr16.size_bits(), 16);
assert_eq!(X86RegClass::Gpr32.size_bits(), 32);
assert_eq!(X86RegClass::Gpr64.size_bits(), 64);
assert_eq!(X86RegClass::Xmm.size_bits(), 128);
assert_eq!(X86RegClass::Ymm.size_bits(), 256);
assert_eq!(X86RegClass::Zmm.size_bits(), 512);
assert_eq!(X86RegClass::Mask.size_bits(), 64);
}
#[test]
fn test_reg_class_name() {
assert_eq!(X86RegClass::Gpr8.name(), "GR8");
assert_eq!(X86RegClass::Gpr64.name(), "GR64");
assert_eq!(X86RegClass::Xmm.name(), "VR128");
assert_eq!(X86RegClass::Ymm.name(), "VR256");
assert_eq!(X86RegClass::Zmm.name(), "VR512");
}
#[test]
fn test_isel_pattern_individual() {
let p = X86ISelPattern {
sd_opcode: SDOpcode::Add,
x86_opcode: X86Opcode::ADD,
num_operands: 2,
name: "test_add".into(),
feature: None,
sets_flags: true,
is_memory_fold: true,
is_3addr: false,
condition_code: None,
addr_pattern: None,
};
assert_eq!(p.name, "test_add");
assert!(p.sets_flags);
assert!(p.is_memory_fold);
assert!(!p.is_3addr);
}
#[test]
fn test_isel_pattern_with_condition_code() {
let p = X86ISelPattern {
sd_opcode: SDOpcode::BrCond,
x86_opcode: X86Opcode::JE,
num_operands: 1,
name: "je".into(),
feature: None,
sets_flags: false,
is_memory_fold: false,
is_3addr: false,
condition_code: Some(X86CondCode::E),
addr_pattern: None,
};
assert_eq!(p.condition_code, Some(X86CondCode::E));
}
#[test]
fn test_isel_select_x86_cmp() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let cmp = dag.get_x86_cmp(a, b, X86CondCode::E);
let info = dag.get_node_info(cmp.node_id).unwrap().clone();
isel.dag = Some(dag);
let result = isel.select_x86_cmp(cmp.node_id, &info);
assert!(result.is_some());
}
#[test]
fn test_isel_select_x86_setcc() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let flag = dag.get_vreg(Type::integer(1));
let setcc = dag.get_setcc(X86CondCode::NE, flag, Type::integer(8));
let info = dag.get_node_info(setcc.node_id).unwrap().clone();
isel.dag = Some(dag);
let result = isel.select_x86_setcc(setcc.node_id, &info);
assert!(result.is_some());
}
#[test]
fn test_isel_select_x86_cmov() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let flag = dag.get_vreg(Type::integer(1));
let tval = dag.get_vreg(Type::integer(32));
let fval = dag.get_vreg(Type::integer(32));
let cmov = dag.get_cmov(X86CondCode::G, flag, tval, fval, Type::integer(32));
let info = dag.get_node_info(cmov.node_id).unwrap().clone();
isel.dag = Some(dag);
let result = isel.select_x86_cmov(cmov.node_id, &info);
assert!(result.is_some());
}
#[test]
fn test_isel_select_x86_shld_shrd() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let node_shld = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Shl,
vec![Type::integer(32)],
)
.with_operands(vec![a, b])
.with_target_node(true);
let shld_id = dag.dag.add_node(node_shld);
dag.node_info.insert(
shld_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Shl, vec![]),
x86_opcode: Some(X86ISD::Shld),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(shld_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_shld(shld_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_adc_sbb() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let node_adc = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Add,
vec![Type::integer(32)],
)
.with_operands(vec![a, b])
.with_target_node(true);
let adc_id = dag.dag.add_node(node_adc);
dag.node_info.insert(
adc_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Add, vec![]),
x86_opcode: Some(X86ISD::AddCarry),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(adc_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_adc(adc_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_vbroadcast() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let src = dag.get_vreg(Type::float_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::ScalarToVector,
vec![Type::fixed_vector(4, 0)],
)
.with_operands(vec![src])
.with_target_node(true);
let vb_id = dag.dag.add_node(node);
dag.node_info.insert(
vb_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::ScalarToVector, vec![]),
x86_opcode: Some(X86ISD::VBroadcast),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(vb_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_vbroadcast(vb_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_vshuffle() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let src1 = dag.get_vreg(Type::fixed_vector(4, 0));
let src2 = dag.get_vreg(Type::fixed_vector(4, 0));
let mask = dag.dag.get_constant(0xE4, Type::integer(8));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::VectorShuffle,
vec![Type::fixed_vector(4, 0)],
)
.with_operands(vec![src1, src2, mask])
.with_target_node(true);
let vs_id = dag.dag.add_node(node);
dag.node_info.insert(
vs_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::VectorShuffle, vec![]),
x86_opcode: Some(X86ISD::VShuffle),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(vs_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_vshuffle(vs_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_fma() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let a = dag.get_vreg(Type::double_precision());
let b = dag.get_vreg(Type::double_precision());
let c = dag.get_vreg(Type::double_precision());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::FMA,
vec![Type::double_precision()],
)
.with_operands(vec![a, b, c])
.with_target_node(true);
let fma_id = dag.dag.add_node(node);
dag.node_info.insert(
fma_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::FMA, vec![]),
x86_opcode: Some(X86ISD::Fmadd),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(fma_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_fmadd(fma_id, &info).is_some());
assert!(isel.select_x86_fmsub(fma_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_gather() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let base = dag.get_vreg(Type::pointer(0));
let index = dag.get_vreg(Type::fixed_vector(4, 0));
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Load,
vec![Type::fixed_vector(4, 0)],
)
.with_operands(vec![dag.dag.entry_token, base, index])
.with_target_node(true);
let gather_id = dag.dag.add_node(node);
dag.node_info.insert(
gather_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Load, vec![]),
x86_opcode: Some(X86ISD::Gather),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(gather_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_gather(gather_id, &info).is_some());
}
#[test]
fn test_isel_select_x86_call_ret() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let callee = dag.get_vreg(Type::pointer(0));
let node_call = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Call,
vec![Type::integer(32), Type::token()],
)
.with_operands(vec![dag.dag.entry_token, callee])
.with_target_node(true);
let call_id = dag.dag.add_node(node_call);
let info_call = X86DAGNode {
node: SDNode::new(0, SDOpcode::Call, vec![]),
x86_opcode: Some(X86ISD::Call),
condition_code: None,
addressing_mode: None,
mem_operand: None,
target_constant: None,
};
isel.dag = Some(dag);
assert!(isel.select_x86_call(call_id, &info_call).is_some());
}
#[test]
fn test_isel_select_x86_lea() {
let mut isel = make_isel();
let mut dag = make_x86_dag();
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(),
dag.dag.all_nodes.len(),
SDOpcode::Add,
vec![Type::pointer(0)],
)
.with_operands(Vec::new())
.with_target_node(true);
let lea_id = dag.dag.add_node(node);
dag.node_info.insert(
lea_id,
X86DAGNode {
node: SDNode::new(0, SDOpcode::Add, vec![]),
x86_opcode: Some(X86ISD::LeaAddr),
condition_code: None,
addressing_mode: Some(X86AddressMode::new_base_index_scale(5, 2, 4, 16)),
mem_operand: None,
target_constant: None,
},
);
let info = dag.get_node_info(lea_id).unwrap().clone();
isel.dag = Some(dag);
assert!(isel.select_x86_lea(lea_id, &info).is_some());
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
fn make_dag() -> X86SelectionDAG {
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
X86SelectionDAG::new(subtarget, cc)
}
fn make_builder() -> X86DAGBuilder {
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
X86DAGBuilder::new(subtarget, cc)
}
#[test]
fn test_x86isd_full_enum_variants() {
let all = [
X86ISD::Cmp, X86ISD::Test, X86ISD::FCmp, X86ISD::SetCC, X86ISD::CMov,
X86ISD::BitTest, X86ISD::BitTestAndSet, X86ISD::BitTestAndReset,
X86ISD::BitTestAndComplement, X86ISD::Shld, X86ISD::Shrd,
X86ISD::UDivRem, X86ISD::SDivRem, X86ISD::MulHW, X86ISD::UMulH,
X86ISD::RepMovs, X86ISD::RepStos, X86ISD::Call, X86ISD::Ret,
X86ISD::TailCall, X86ISD::FrameAddr, X86ISD::RetAddr,
X86ISD::LeaAddr, X86ISD::GlobalBaseReg, X86ISD::AddCarry, X86ISD::SubBorrow,
X86ISD::VBroadcast, X86ISD::VBroadcastLoad, X86ISD::MovdToVec,
X86ISD::VecExtract, X86ISD::VecInsert, X86ISD::VShuffle,
X86ISD::Unpckl, X86ISD::Unpckh, X86ISD::HAdd, X86ISD::HSub, X86ISD::AddSub,
X86ISD::Fmadd, X86ISD::Fmsub, X86ISD::Fnmadd, X86ISD::Fnmsub, X86ISD::Fmaddsub,
X86ISD::MaskOp, X86ISD::MaskedOp, X86ISD::Gather, X86ISD::Scatter,
X86ISD::Compress, X86ISD::Expand, X86ISD::EhReturn, X86ISD::TlsAddr,
X86ISD::Wrapper, X86ISD::WrapperRIP,
];
for v in &all {
let s = v.as_str();
assert!(!s.is_empty(), "Missing as_str for {:?}", v);
let is_cmp = v.is_comparison();
let sets_f = v.sets_flags();
let _ = v.to_x86_opcode();
}
}
#[test]
fn test_copy_to_from_reg_chain_preservation() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let val = dag.get_vreg(Type::integer(32));
let ctr = dag.get_copy_to_reg(chain, val, 5);
let (cfr_val, cfr_chain) = dag.get_copy_from_reg(ctr, 0, Type::integer(32));
assert_ne!(cfr_val.node_id, val.node_id);
assert_ne!(cfr_chain.node_id, chain.node_id);
}
#[test]
fn test_load_with_addressing_mode() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let ptr = dag.get_vreg(Type::pointer(0));
let am = X86AddressMode::new_base_disp(5, 16);
let (val, new_chain) = dag.get_x86_load(chain, ptr, Type::integer(32), am.clone());
assert_ne!(val.node_id, 0);
assert_ne!(new_chain.node_id, 0);
let info = dag.get_node_info(val.node_id).unwrap();
assert!(info.addressing_mode.is_some());
}
#[test]
fn test_store_with_addressing_mode() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let val = dag.get_vreg(Type::integer(32));
let ptr = dag.get_vreg(Type::pointer(0));
let am = X86AddressMode::new_base_index_scale(5, 2, 4, 8);
let new_chain = dag.get_x86_store(chain, val, ptr, am);
assert_ne!(new_chain.node_id, 0);
}
#[test]
fn test_call_with_arguments() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let callee = dag.dag.get_constant(0x4000, Type::pointer(0));
let arg1 = dag.get_vreg(Type::integer(32));
let arg2 = dag.get_vreg(Type::integer(32));
let (result, call_chain) = dag.get_x86_call(
chain, callee,
vec![arg1, arg2],
Type::integer(32),
);
assert_ne!(result.node_id, 0);
assert_ne!(call_chain.node_id, 0);
}
#[test]
fn test_call_void_return() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let callee = dag.dag.get_constant(0x4000, Type::pointer(0));
let (result, call_chain) = dag.get_x86_call(
chain, callee, Vec::new(),
Type::void(),
);
assert_ne!(call_chain.node_id, 0);
}
#[test]
fn test_branch_with_chain() {
let mut dag = make_dag();
let chain = dag.dag.entry_token;
let dest = dag.get_vreg(Type::label());
let node = SDNode::new_with_node_id(
dag.dag.nodes.len(), dag.dag.all_nodes.len(),
SDOpcode::Br, vec![Type::token()],
).with_operands(vec![chain, dest]);
let br_id = dag.dag.add_node(node);
dag.dag.set_root(SDValue::new(br_id, 0));
assert_eq!(dag.dag.root, Some(SDValue::new(br_id, 0)));
}
#[test]
fn test_builder_recognize_multiple_addressing_modes() {
let mut builder = make_builder();
let fi = builder.x86_dag.get_frame_index(Type::pointer(0));
let am = builder.recognize_addressing_mode(fi);
assert!(am.is_frame_relative);
let gv = SDValue::new(0, 0);
let ga_node = SDNode::new_with_node_id(
builder.x86_dag.dag.nodes.len(),
builder.x86_dag.dag.all_nodes.len(),
SDOpcode::GlobalAddress,
vec![Type::pointer(0)],
);
let ga_id = builder.x86_dag.dag.add_node(ga_node);
let am2 = builder.recognize_addressing_mode(SDValue::new(ga_id, 0));
assert!(am2.is_rip_relative);
}
#[test]
fn test_builder_constant_folding_in_lower() {
let mut builder = make_builder();
let c42 = builder.get_constant(42u64, Type::integer(32));
let c58 = builder.get_constant(58u64, Type::integer(32));
let sum = builder.lower_add(c42, c58);
assert_ne!(sum.node_id, 0);
}
#[test]
fn test_builder_get_constant_fp() {
let mut builder = make_builder();
let cfp = builder.get_constant_fp(f64::to_bits(3.14159), Type::double_precision());
assert_ne!(cfp.node_id, 0);
}
#[test]
fn test_combine_all_identity_patterns() {
let mut dag = make_dag();
let x = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let one = dag.dag.get_constant(1u64, Type::integer(32));
let all_ones = dag.dag.get_constant((-1i64) as u64, Type::integer(32));
for (c, val) in &[
(zero.node_id, 0i64),
(one.node_id, 1i64),
(all_ones.node_id, -1i64),
] {
dag.node_info.insert(*c, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(*val),
});
}
let mut combiner = X86DAGCombine::new(X86Subtarget::new("x86-64", "generic"));
let add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), x, zero);
combiner.combine_add_identity(&mut dag, add.node_id);
assert!(combiner.modified);
let sub = dag.dag.get_binary_op(SDOpcode::Sub, Type::integer(32), x, zero);
combiner.combine_sub_identity(&mut dag, sub.node_id);
assert!(combiner.modified);
let mul = dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), x, one);
combiner.combine_mul_identity(&mut dag, mul.node_id);
assert!(combiner.modified);
let and = dag.dag.get_binary_op(SDOpcode::And, Type::integer(32), x, all_ones);
combiner.combine_and_identity(&mut dag, and.node_id);
assert!(combiner.modified);
let or = dag.dag.get_binary_op(SDOpcode::Or, Type::integer(32), x, zero);
combiner.combine_or_identity(&mut dag, or.node_id);
assert!(combiner.modified);
let xor = dag.dag.get_binary_op(SDOpcode::Xor, Type::integer(32), x, zero);
combiner.combine_xor_zero(&mut dag, xor.node_id);
assert!(combiner.modified);
}
#[test]
fn test_combine_shift_all_amounts() {
let mut dag = make_dag();
let x = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(8));
dag.node_info.insert(zero.node_id, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(0),
});
let mut combiner = X86DAGCombine::new(X86Subtarget::new("x86-64", "generic"));
for &opcode in &[SDOpcode::Shl, SDOpcode::Sra, SDOpcode::Srl] {
let shift = dag.dag.get_binary_op(opcode, x, zero);
combiner.combine_shift_amount(&mut dag, shift.node_id);
}
assert!(combiner.modified);
}
#[test]
fn test_legalizer_vector_widths() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let legalizer = X86DAGLegalize::new(subtarget.clone());
assert!(!legalizer.type_legalizer.legal_vector_widths.is_empty());
}
#[test]
fn test_legalizer_32bit_config() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let legalizer = X86DAGLegalize::new_32bit(subtarget);
assert_eq!(legalizer.type_legalizer.target_pointer_size, 32);
assert_eq!(legalizer.type_legalizer.max_legal_int_size, 32);
}
#[test]
fn test_legalize_empty_dag() {
let mut dag = make_dag();
let subtarget = X86Subtarget::new("x86-64", "generic");
let mut legalizer = X86DAGLegalize::new(subtarget);
legalizer.legalize(&mut dag);
}
#[test]
fn test_isel_all_binary_opcode_selections() {
let mut isel = X86DAGToDAGISel::new(X86Subtarget::new("x86-64", "generic"));
let mut dag = make_dag();
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let c = dag.get_vreg(Type::double_precision());
let d = dag.get_vreg(Type::double_precision());
isel.dag = Some(dag);
let add = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, b);
assert!(isel.select_add(add.node_id).is_some());
let sub = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::Sub, Type::integer(32), a, b);
assert!(isel.select_sub(sub.node_id).is_some());
let mul = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::Mul, Type::integer(32), a, b);
assert!(isel.select_mul(mul.node_id).is_some());
let and = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::And, Type::integer(32), a, b);
assert!(isel.select_and(and.node_id).is_some());
let or = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::Or, Type::integer(32), a, b);
assert!(isel.select_or(or.node_id).is_some());
let xor = isel.dag.as_ref().unwrap().dag.get_binary_op(SDOpcode::Xor, Type::integer(32), a, b);
assert!(isel.select_xor(xor.node_id).is_some());
}
#[test]
fn test_isel_all_condition_codes() {
let isel = X86DAGToDAGISel::new(X86Subtarget::new("x86-64", "generic"));
let all_ccs = [
X86CondCode::O, X86CondCode::NO, X86CondCode::B, X86CondCode::AE,
X86CondCode::E, X86CondCode::NE, X86CondCode::BE, X86CondCode::A,
X86CondCode::S, X86CondCode::NS, X86CondCode::P, X86CondCode::NP,
X86CondCode::L, X86CondCode::GE, X86CondCode::LE, X86CondCode::G,
];
for &cc in &all_ccs {
let jcc = isel.cond_code_to_jcc(cc);
let setcc = isel.cond_code_to_setcc(cc);
let cmov = isel.cond_code_to_cmov(cc);
assert!(jcc.as_u32() > 0);
assert!(setcc.as_u32() > 0);
assert!(cmov.as_u32() > 0);
}
}
#[test]
fn test_isel_vreg_allocation() {
let mut isel = X86DAGToDAGISel::new(X86Subtarget::new("x86-64", "generic"));
let v1 = isel.new_vreg();
let v2 = isel.new_vreg();
let v3 = isel.new_vreg();
assert!(v1 != v2);
assert!(v2 != v3);
assert!(v3 > v2);
assert!(v2 > v1);
}
#[test]
fn test_isel_get_label() {
let isel = X86DAGToDAGISel::new(X86Subtarget::new("x86-64", "generic"));
let label = isel.get_label(SDValue::new(42, 0));
assert!(label.contains("42"));
}
#[test]
fn test_sched_model_all_kinds() {
let kinds = [
X86SchedModelKind::SkylakeClient,
X86SchedModelKind::IceLake,
X86SchedModelKind::AlderLakePcore,
X86SchedModelKind::GraniteRapids,
X86SchedModelKind::Zen3,
X86SchedModelKind::Zen4,
X86SchedModelKind::Zen5,
];
for &kind in &kinds {
let mut model = X86DAGSchedModel::new(kind);
assert_eq!(model.model_kind, kind);
let lat = model.get_latency(SDOpcode::Add);
assert!(lat > 0, "Zero latency for {:?}", kind);
}
}
#[test]
fn test_sched_model_critical_path_empty() {
let mut model = X86DAGSchedModel::default_x86();
let dag = make_dag();
let cp = model.compute_critical_path(&dag);
assert!(cp >= 0);
}
#[test]
fn test_emitter_all_instruction_types() {
let emitter = X86InstrEmitter::new(X86Subtarget::new("x86-64", "generic"), "test");
assert_eq!(emitter.emit_mov(1, 2).opcode, X86Opcode::MOV as u32);
assert_eq!(emitter.emit_mov_imm(1, 42).opcode, X86Opcode::MOV as u32);
assert_eq!(emitter.emit_binary(X86Opcode::ADD, 1, 2, 3).opcode, X86Opcode::ADD as u32);
assert_eq!(emitter.emit_binary_imm(X86Opcode::SUB, 1, 2, 5).opcode, X86Opcode::SUB as u32);
assert_eq!(emitter.emit_unary(X86Opcode::NOT, 1, 2).opcode, X86Opcode::NOT as u32);
assert_eq!(emitter.emit_cmp(1, 2).opcode, X86Opcode::CMP as u32);
assert_eq!(emitter.emit_ret().opcode, X86Opcode::RET as u32);
assert_eq!(emitter.emit_call("target").opcode, X86Opcode::CALL as u32);
assert_eq!(emitter.emit_jmp("target").opcode, X86Opcode::JMP as u32);
for &cc in &[X86CondCode::E, X86CondCode::NE, X86CondCode::L, X86CondCode::G,
X86CondCode::B, X86CondCode::A, X86CondCode::BE, X86CondCode::AE] {
let jcc = emitter.emit_jcc(cc, "target");
assert!(jcc.opcode > 0);
let setcc = emitter.emit_setcc(cc, 1);
assert!(setcc.opcode > 0);
}
let am = X86AddressMode::new_base_index_scale(5, 2, 4, 16);
let lea = emitter.emit_lea(1, &am);
assert_eq!(lea.opcode, X86Opcode::LEA as u32);
}
#[test]
fn test_emitter_32bit_mode() {
let emitter = X86InstrEmitter::new_32bit(X86Subtarget::new("x86-64", "generic"), "test32");
assert!(!emitter.isel.is_64bit);
}
#[test]
fn test_pipeline_empty_function() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline(&subtarget, "empty", |builder| {
builder.lower_ret(None);
});
assert_eq!(mf.name, "empty");
}
#[test]
fn test_pipeline_full_arithmetic_chain() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline(&subtarget, "arith_chain", |builder| {
let x = builder.x86_dag.get_vreg(Type::integer(32));
let y = builder.x86_dag.get_vreg(Type::integer(32));
let z = builder.x86_dag.get_vreg(Type::integer(32));
let t1 = builder.lower_add(x, y);
let t2 = builder.lower_sub(t1, z);
let t3 = builder.lower_mul(t2, x);
let t4 = builder.lower_and(t3, y);
let t5 = builder.lower_or(t4, z);
let t6 = builder.lower_xor(t5, x);
builder.lower_ret(Some(t6));
});
assert_eq!(mf.name, "arith_chain");
assert!(!mf.blocks.is_empty());
}
#[test]
fn test_pipeline_with_comparisons() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline(&subtarget, "cmp_chain", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let eq = builder.lower_icmp(a, b, 0, false);
let gt = builder.lower_icmp(a, b, 2, true);
let fa = builder.x86_dag.get_vreg(Type::double_precision());
let fb = builder.x86_dag.get_vreg(Type::double_precision());
let oeq = builder.lower_fcmp(fa, fb, 0);
builder.lower_ret(Some(eq));
});
assert_eq!(mf.name, "cmp_chain");
}
#[test]
fn test_pipeline_32bit_full() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline_32bit(&subtarget, "full32", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(a, b);
let ext = builder.lower_zext(sum, Type::integer(64));
builder.lower_ret(Some(ext));
});
assert_eq!(mf.name, "full32");
}
#[test]
fn test_pipeline_with_calls_and_args() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline(&subtarget, "call_with_args", |builder| {
let callee = builder.x86_dag.get_vreg(Type::pointer(0));
let arg1 = builder.x86_dag.get_vreg(Type::integer(32));
let arg2 = builder.x86_dag.get_vreg(Type::integer(32));
let (result, _chain) = builder.lower_call(
callee,
&[arg1, arg2],
Type::integer(32),
false,
);
builder.lower_ret(Some(result));
});
assert_eq!(mf.name, "call_with_args");
}
#[test]
fn test_pipeline_tail_call() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf = run_x86_dag_pipeline(&subtarget, "tail_call", |builder| {
let callee = builder.x86_dag.get_vreg(Type::pointer(0));
let arg = builder.x86_dag.get_vreg(Type::integer(32));
let (_, _) = builder.lower_call(
callee,
&[arg],
Type::integer(32),
true,
);
builder.lower_ret(None);
});
assert_eq!(mf.name, "tail_call");
}
#[test]
fn test_complex_pattern_all_addressing_patterns() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
let mut dag = X86SelectionDAG::new(subtarget, cc);
let fi = dag.get_frame_index(Type::pointer(0));
let mut matcher = X86ComplexPatternMatcher::new();
assert!(matcher.match_addressing_mode(&dag, fi.node_id));
matcher.reset();
let c = dag.dag.get_constant(0x1000, Type::integer(64));
dag.node_info.insert(c.node_id, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(0x1000),
});
assert!(matcher.match_addressing_mode(&dag, c.node_id));
}
#[test]
fn test_complex_pattern_shl_scale_all() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
let mut dag = X86SelectionDAG::new(subtarget, cc);
let x = dag.get_vreg(Type::integer(32));
for &shift in &[0i64, 1, 2, 3] {
let amount = dag.dag.get_constant(shift, Type::integer(8));
dag.node_info.insert(amount.node_id, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(shift),
});
let shl = dag.dag.get_binary_op(SDOpcode::Shl, Type::integer(32), x, amount);
let mut matcher = X86ComplexPatternMatcher::new();
let matched = matcher.match_addressing_mode(&dag, shl.node_id);
assert!(matched || shift > 3); }
}
#[test]
fn test_immediate_folder_all_binary_ops() {
let folder = X86ImmediateFolder::new(true);
let test_cases = [
(SDOpcode::Add, X86Opcode::ADD),
(SDOpcode::Sub, X86Opcode::SUB),
(SDOpcode::And, X86Opcode::AND),
(SDOpcode::Or, X86Opcode::OR),
(SDOpcode::Xor, X86Opcode::XOR),
(SDOpcode::Mul, X86Opcode::IMUL),
];
for &(sd_op, x86_op) in &test_cases {
assert_eq!(folder.try_fold_imm_binary(sd_op, 42), Some(x86_op));
}
}
#[test]
fn test_immediate_folder_shift_masking() {
let folder = X86ImmediateFolder::new(true);
let result = folder.try_fold_imm_shift(SDOpcode::Shl, 100);
assert_eq!(result, Some((X86Opcode::SHL, 100 & 0x3F)));
}
#[test]
fn test_pattern_table_all_basic_ops_have_patterns() {
let table = X86ISelPatternTable::new();
let basic_ops = [
SDOpcode::Add, SDOpcode::Sub, SDOpcode::Mul,
SDOpcode::SDiv, SDOpcode::UDiv,
SDOpcode::And, SDOpcode::Or, SDOpcode::Xor,
SDOpcode::Shl, SDOpcode::Sra, SDOpcode::Srl,
SDOpcode::Load, SDOpcode::Store,
SDOpcode::Br, SDOpcode::BrCond, SDOpcode::Ret, SDOpcode::Call,
SDOpcode::ZExt, SDOpcode::SExt,
SDOpcode::SIToFP, SDOpcode::FPToSI,
SDOpcode::Select,
];
for &op in &basic_ops {
let pats = table.lookup(op);
assert!(!pats.is_empty(), "No patterns for {:?}", op);
}
}
#[test]
fn test_abi_layout_sse_args() {
let layout = X86ABIArgLayout::x86_64_sysv();
for i in 0..8u32 {
assert_eq!(layout.get_sse_arg_reg(i), Some(i));
}
assert_eq!(layout.get_sse_arg_reg(8), None);
}
#[test]
fn test_abi_layout_ret_regs() {
let layout = X86ABIArgLayout::x86_64_sysv();
assert_eq!(layout.ret_regs.len(), 2);
assert_eq!(layout.ret_regs[0], 0); assert_eq!(layout.ret_regs[1], 1); }
#[test]
fn test_dag_utils_all_integer_types() {
for bits in &[1u32, 8, 16, 32, 64, 128] {
let ty = Type::integer(*bits);
let legal_64 = X86DAGUtils::is_type_legal(&ty, true);
let legal_32 = X86DAGUtils::is_type_legal(&ty, false);
match bits {
8 | 16 | 32 => {
assert!(legal_64);
assert!(legal_32);
}
64 => {
assert!(legal_64);
assert!(!legal_32);
}
_ => {
assert!(!legal_64);
assert!(!legal_32);
}
}
}
}
#[test]
fn test_reg_class_all_sizes() {
assert_eq!(X86RegClass::Gpr8.size_bits(), 8);
assert_eq!(X86RegClass::Gpr16.size_bits(), 16);
assert_eq!(X86RegClass::Gpr32.size_bits(), 32);
assert_eq!(X86RegClass::Gpr64.size_bits(), 64);
assert_eq!(X86RegClass::Xmm.size_bits(), 128);
assert_eq!(X86RegClass::Ymm.size_bits(), 256);
assert_eq!(X86RegClass::Zmm.size_bits(), 512);
assert_eq!(X86RegClass::Mask.size_bits(), 64);
}
#[test]
fn test_full_flow_builder_to_emitter() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
let mut builder = X86DAGBuilder::new(subtarget.clone(), cc);
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(a, b);
builder.lower_ret(Some(sum));
let mut x86_dag = builder.finish();
let mut combiner = X86DAGCombine::new(subtarget.clone());
combiner.combine(&mut x86_dag);
let mut legalizer = X86DAGLegalize::new(subtarget.clone());
legalizer.legalize(&mut x86_dag);
let mut emitter = X86InstrEmitter::new(subtarget, "full_flow");
let mf = emitter.emit(x86_dag);
assert_eq!(mf.name, "full_flow");
assert!(!mf.blocks.is_empty());
}
#[test]
fn test_all_combine_patterns_together() {
let mut dag = make_dag();
let x = dag.get_vreg(Type::integer(32));
let zero = dag.dag.get_constant(0u64, Type::integer(32));
let one = dag.dag.get_constant(1u64, Type::integer(32));
dag.node_info.insert(zero.node_id, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(0),
});
dag.node_info.insert(one.node_id, X86DAGNode {
node: SDNode::new(0, SDOpcode::Constant, vec![]),
x86_opcode: None, condition_code: None,
addressing_mode: None, mem_operand: None,
target_constant: Some(1),
});
let _add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), x, zero);
let _sub = dag.dag.get_binary_op(SDOpcode::Sub, Type::integer(32), x, zero);
let _mul = dag.dag.get_binary_op(SDOpcode::Mul, Type::integer(32), x, one);
let _xor = dag.dag.get_binary_op(SDOpcode::Xor, Type::integer(32), x, zero);
let _shl = dag.dag.get_binary_op(SDOpcode::Shl, Type::integer(32), x, zero);
let mut combiner = X86DAGCombine::new(X86Subtarget::new("x86-64", "generic"));
combiner.combine(&mut dag);
assert!(combiner.x86_num_combined > 0);
}
}
pub struct X86AddressResolver {
pub base_reg: Option<u32>,
pub index_reg: Option<u32>,
pub scale: u8,
pub displacement: i64,
pub is_rip_relative: bool,
pub segment_override: Option<u32>,
}
impl X86AddressResolver {
pub fn new() -> Self {
Self {
base_reg: None,
index_reg: None,
scale: 1,
displacement: 0,
is_rip_relative: false,
segment_override: None,
}
}
pub fn resolve(&mut self, addr_mode: &X86AddressMode) {
self.base_reg = addr_mode.base_reg;
self.index_reg = addr_mode.index_reg;
self.scale = addr_mode.scale;
self.displacement = addr_mode.displacement;
self.is_rip_relative = addr_mode.is_rip_relative;
self.segment_override = addr_mode.segment;
}
pub fn needs_regalloc(&self) -> bool {
self.base_reg.is_some() || self.index_reg.is_some()
}
pub fn get_encoding_form(&self) -> X86EncodingForm {
match (self.base_reg, self.index_reg) {
(None, None) => X86EncodingForm::DirectAddress,
(Some(_), None) => X86EncodingForm::BaseDisp8Or32,
(None, Some(_)) => X86EncodingForm::IndexScale,
(Some(_), Some(_)) => X86EncodingForm::BaseIndexScale,
}
}
}
impl Default for X86AddressResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86EncodingForm {
DirectAddress,
BaseDisp8Or32,
IndexScale,
BaseIndexScale,
RIPRelative,
}
impl X86EncodingForm {
pub fn name(&self) -> &'static str {
match self {
X86EncodingForm::DirectAddress => "DirectAddress",
X86EncodingForm::BaseDisp8Or32 => "Base+Disp",
X86EncodingForm::IndexScale => "Index*Scale+Disp",
X86EncodingForm::BaseIndexScale => "Base+Index*Scale+Disp",
X86EncodingForm::RIPRelative => "RIP+Disp32",
}
}
}
#[derive(Debug, Clone)]
pub struct X86ConstantData {
pub int_val: Option<i64>,
pub fp_val: Option<u64>,
pub ty: Type,
pub is_pool_constant: bool,
pub section: Option<String>,
}
impl X86ConstantData {
pub fn new_int(val: i64, ty: Type) -> Self {
Self {
int_val: Some(val),
fp_val: None,
ty,
is_pool_constant: false,
section: None,
}
}
pub fn new_fp(val: f64, ty: Type) -> Self {
Self {
int_val: None,
fp_val: Some(val.to_bits()),
ty,
is_pool_constant: true,
section: Some(".rodata".into()),
}
}
pub fn fits_imm32(&self) -> bool {
match self.int_val {
Some(v) => v >= -2147483648 && v <= 2147483647,
None => false,
}
}
pub fn is_zero(&self) -> bool {
self.int_val == Some(0)
}
pub fn is_one(&self) -> bool {
self.int_val == Some(1)
}
pub fn size_bytes(&self) -> u32 {
X86DAGUtils::type_size_bytes(&self.ty)
}
}
#[derive(Debug, Clone, Default)]
pub struct X86FlagTracker {
pub uses_zf: bool,
pub uses_sf: bool,
pub uses_of: bool,
pub uses_cf: bool,
pub uses_pf: bool,
pub last_flag_definer: Option<usize>,
pub last_cc: Option<X86CondCode>,
}
impl X86FlagTracker {
pub fn new() -> Self {
Self::default()
}
pub fn record_definer(&mut self, node_id: usize) {
self.last_flag_definer = Some(node_id);
self.uses_zf = true;
self.uses_sf = true;
self.uses_cf = true;
self.uses_of = true;
}
pub fn record_use(&mut self, cc: X86CondCode) {
self.last_cc = Some(cc);
match cc {
X86CondCode::E | X86CondCode::NE => {
self.uses_zf = true;
}
X86CondCode::L | X86CondCode::G | X86CondCode::LE | X86CondCode::GE => {
self.uses_sf = true;
self.uses_of = true;
}
X86CondCode::B | X86CondCode::A | X86CondCode::BE | X86CondCode::AE => {
self.uses_cf = true;
}
X86CondCode::S | X86CondCode::NS => {
self.uses_sf = true;
}
X86CondCode::O | X86CondCode::NO => {
self.uses_of = true;
}
X86CondCode::P | X86CondCode::NP => {
self.uses_pf = true;
}
}
}
pub fn can_eliminate_cmp(&self) -> bool {
self.last_flag_definer.is_some()
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
#[derive(Debug, Clone, Default)]
pub struct X86DAGStats {
pub total_nodes: u64,
pub nodes_combined: u64,
pub nodes_legalized: u64,
pub instructions_emitted: u64,
pub virtual_registers: u64,
pub memory_ops: u64,
pub branches: u64,
pub calls: u64,
pub vector_ops: u64,
pub critical_path: u32,
pub estimated_cycles: u64,
pub sse_instructions: u64,
pub avx_instructions: u64,
pub avx512_instructions: u64,
}
impl X86DAGStats {
pub fn new() -> Self {
Self::default()
}
pub fn accumulate(&mut self, dag: &X86SelectionDAG) {
self.total_nodes += dag.dag.nodes.len() as u64;
for node_id in 0..dag.dag.nodes.len() {
if let Some(node) = dag.dag.get_node(node_id) {
match node.opcode {
SDOpcode::Load | SDOpcode::Store => self.memory_ops += 1,
SDOpcode::Br | SDOpcode::BrCond => self.branches += 1,
SDOpcode::Call => self.calls += 1,
SDOpcode::BuildVector | SDOpcode::ExtractElement
| SDOpcode::InsertElement | SDOpcode::VectorShuffle => {
self.vector_ops += 1;
}
_ => {}
}
}
}
}
pub fn summary(&self) -> String {
format!(
"X86DAGStats: nodes={} combined={} legalized={} instrs={} vregs={} \
memops={} branches={} calls={} vecops={} critpath={} cycles={} \
sse={} avx={} avx512={}",
self.total_nodes,
self.nodes_combined,
self.nodes_legalized,
self.instructions_emitted,
self.virtual_registers,
self.memory_ops,
self.branches,
self.calls,
self.vector_ops,
self.critical_path,
self.estimated_cycles,
self.sse_instructions,
self.avx_instructions,
self.avx512_instructions,
)
}
}
#[cfg(test)]
mod extended2_tests {
use super::*;
#[test]
fn test_address_resolver_new() {
let resolver = X86AddressResolver::new();
assert!(resolver.base_reg.is_none());
assert!(resolver.index_reg.is_none());
assert_eq!(resolver.scale, 1);
assert_eq!(resolver.displacement, 0);
}
#[test]
fn test_address_resolver_resolve() {
let am = X86AddressMode::new_base_index_scale(5, 2, 4, 16);
let mut resolver = X86AddressResolver::new();
resolver.resolve(&am);
assert_eq!(resolver.base_reg, Some(5));
assert_eq!(resolver.index_reg, Some(2));
assert_eq!(resolver.scale, 4);
assert_eq!(resolver.displacement, 16);
}
#[test]
fn test_address_resolver_needs_regalloc() {
let mut resolver = X86AddressResolver::new();
assert!(!resolver.needs_regalloc());
resolver.base_reg = Some(5);
assert!(resolver.needs_regalloc());
}
#[test]
fn test_address_resolver_encoding_forms() {
let mut resolver = X86AddressResolver::new();
assert_eq!(resolver.get_encoding_form(), X86EncodingForm::DirectAddress);
resolver.base_reg = Some(5);
assert_eq!(resolver.get_encoding_form(), X86EncodingForm::BaseDisp8Or32);
resolver.base_reg = None;
resolver.index_reg = Some(2);
assert_eq!(resolver.get_encoding_form(), X86EncodingForm::IndexScale);
resolver.base_reg = Some(5);
assert_eq!(resolver.get_encoding_form(), X86EncodingForm::BaseIndexScale);
}
#[test]
fn test_address_resolver_default() {
let resolver = X86AddressResolver::default();
assert!(!resolver.needs_regalloc());
}
#[test]
fn test_encoding_form_name() {
assert_eq!(X86EncodingForm::DirectAddress.name(), "DirectAddress");
assert_eq!(X86EncodingForm::BaseDisp8Or32.name(), "Base+Disp");
assert_eq!(X86EncodingForm::IndexScale.name(), "Index*Scale+Disp");
assert_eq!(X86EncodingForm::BaseIndexScale.name(), "Base+Index*Scale+Disp");
assert_eq!(X86EncodingForm::RIPRelative.name(), "RIP+Disp32");
}
#[test]
fn test_constant_data_int() {
let cd = X86ConstantData::new_int(42, Type::integer(32));
assert_eq!(cd.int_val, Some(42));
assert!(!cd.is_pool_constant);
assert!(cd.fits_imm32());
assert!(!cd.is_zero());
assert!(!cd.is_one());
}
#[test]
fn test_constant_data_fp() {
let cd = X86ConstantData::new_fp(3.14, Type::double_precision());
assert!(cd.is_pool_constant);
assert_eq!(cd.section, Some(".rodata".into()));
}
#[test]
fn test_constant_data_zero_one() {
let cd0 = X86ConstantData::new_int(0, Type::integer(32));
assert!(cd0.is_zero());
assert!(!cd0.is_one());
let cd1 = X86ConstantData::new_int(1, Type::integer(32));
assert!(!cd1.is_zero());
assert!(cd1.is_one());
}
#[test]
fn test_constant_data_fits_imm32() {
assert!(X86ConstantData::new_int(0, Type::integer(32)).fits_imm32());
assert!(X86ConstantData::new_int(2147483647, Type::integer(32)).fits_imm32());
assert!(X86ConstantData::new_int(-2147483648, Type::integer(32)).fits_imm32());
assert!(!X86ConstantData::new_int(2147483648, Type::integer(64)).fits_imm32());
assert!(!X86ConstantData::new_int(-2147483649, Type::integer(64)).fits_imm32());
}
#[test]
fn test_constant_data_size_bytes() {
assert_eq!(X86ConstantData::new_int(0, Type::integer(8)).size_bytes(), 1);
assert_eq!(X86ConstantData::new_int(0, Type::integer(32)).size_bytes(), 4);
assert_eq!(X86ConstantData::new_int(0, Type::integer(64)).size_bytes(), 8);
}
#[test]
fn test_flag_tracker_new() {
let ft = X86FlagTracker::new();
assert!(!ft.uses_zf);
assert!(!ft.uses_sf);
assert!(ft.last_flag_definer.is_none());
}
#[test]
fn test_flag_tracker_record_definer() {
let mut ft = X86FlagTracker::new();
ft.record_definer(42);
assert!(ft.uses_zf);
assert!(ft.uses_sf);
assert!(ft.uses_cf);
assert!(ft.uses_of);
assert_eq!(ft.last_flag_definer, Some(42));
}
#[test]
fn test_flag_tracker_record_use_all_ccs() {
let all_ccs = [
X86CondCode::E, X86CondCode::NE,
X86CondCode::L, X86CondCode::G, X86CondCode::LE, X86CondCode::GE,
X86CondCode::B, X86CondCode::A, X86CondCode::BE, X86CondCode::AE,
X86CondCode::S, X86CondCode::NS,
X86CondCode::O, X86CondCode::NO,
X86CondCode::P, X86CondCode::NP,
];
for &cc in &all_ccs {
let mut ft = X86FlagTracker::new();
ft.record_use(cc);
assert_eq!(ft.last_cc, Some(cc));
}
}
#[test]
fn test_flag_tracker_can_eliminate_cmp() {
let mut ft = X86FlagTracker::new();
assert!(!ft.can_eliminate_cmp());
ft.record_definer(10);
assert!(ft.can_eliminate_cmp());
}
#[test]
fn test_flag_tracker_reset() {
let mut ft = X86FlagTracker::new();
ft.record_definer(10);
ft.record_use(X86CondCode::E);
ft.reset();
assert!(!ft.uses_zf);
assert!(ft.last_flag_definer.is_none());
assert!(ft.last_cc.is_none());
}
#[test]
fn test_flag_tracker_default() {
let ft = X86FlagTracker::default();
assert!(!ft.can_eliminate_cmp());
}
#[test]
fn test_dag_stats_new() {
let stats = X86DAGStats::new();
assert_eq!(stats.total_nodes, 0);
assert_eq!(stats.nodes_combined, 0);
assert_eq!(stats.instructions_emitted, 0);
}
#[test]
fn test_dag_stats_accumulate() {
let mut stats = X86DAGStats::new();
let subtarget = X86Subtarget::new("x86-64", "generic");
let cc = X86CallingConvention::default();
let mut dag = X86SelectionDAG::new(subtarget, cc);
let a = dag.get_vreg(Type::integer(32));
let b = dag.get_vreg(Type::integer(32));
let _add = dag.dag.get_binary_op(SDOpcode::Add, Type::integer(32), a, b);
let _load = dag.dag.get_binary_op(SDOpcode::Load, a, b);
stats.accumulate(&dag);
assert!(stats.total_nodes > 0);
}
#[test]
fn test_dag_stats_summary() {
let stats = X86DAGStats::new();
let summary = stats.summary();
assert!(summary.contains("X86DAGStats"));
assert!(summary.contains("nodes="));
}
}
pub struct X86DAGPipeline {
pub subtarget: X86Subtarget,
pub calling_conv: X86CallingConvention,
pub enable_combiner: bool,
pub enable_legalizer: bool,
pub max_combine_iterations: u32,
pub sched_model_kind: X86SchedModelKind,
pub stats: X86DAGStats,
}
impl X86DAGPipeline {
pub fn new(subtarget: X86Subtarget) -> Self {
Self {
subtarget,
calling_conv: X86CallingConvention::default(),
enable_combiner: true,
enable_legalizer: true,
max_combine_iterations: 4,
sched_model_kind: X86SchedModelKind::Zen3,
stats: X86DAGStats::new(),
}
}
pub fn new_32bit(subtarget: X86Subtarget) -> Self {
Self {
subtarget,
calling_conv: X86CallingConvention::default(),
enable_combiner: true,
enable_legalizer: true,
max_combine_iterations: 4,
sched_model_kind: X86SchedModelKind::Zen3,
stats: X86DAGStats::new(),
}
}
pub fn without_combiner(mut self) -> Self {
self.enable_combiner = false;
self
}
pub fn without_legalizer(mut self) -> Self {
self.enable_legalizer = false;
self
}
pub fn with_max_combine_iterations(mut self, n: u32) -> Self {
self.max_combine_iterations = n;
self
}
pub fn with_sched_model(mut self, kind: X86SchedModelKind) -> Self {
self.sched_model_kind = kind;
self
}
pub fn run(
&mut self,
func_name: &str,
build_fn: impl FnOnce(&mut X86DAGBuilder),
) -> MachineFunction {
let mut builder = X86DAGBuilder::new(
self.subtarget.clone(),
self.calling_conv.clone(),
);
build_fn(&mut builder);
let mut x86_dag = builder.finish();
self.stats.accumulate(&x86_dag);
self.stats.total_nodes = x86_dag.dag.nodes.len() as u64;
if self.enable_combiner {
let mut combiner = X86DAGCombine::new(self.subtarget.clone());
combiner.max_iterations = self.max_combine_iterations;
combiner.combine(&mut x86_dag);
self.stats.nodes_combined = combiner.x86_num_combined;
}
if self.enable_legalizer {
let mut legalizer = X86DAGLegalize::new(self.subtarget.clone());
legalizer.legalize(&mut x86_dag);
self.stats.nodes_legalized =
x86_dag.dag.nodes.len() as u64 - self.stats.total_nodes;
}
{
let mut sched = X86DAGSchedModel::new(self.sched_model_kind);
self.stats.critical_path = sched.compute_critical_path(&x86_dag);
}
let mut emitter = X86InstrEmitter::new(self.subtarget.clone(), func_name);
emitter.emit(x86_dag);
let mf = emitter.finish();
for block in &mf.blocks {
self.stats.instructions_emitted += block.instructions.len() as u64;
for instr in &block.instructions {
let opcode = instr.opcode;
if opcode >= X86Opcode::ADDSS as u32 && opcode <= X86Opcode::HSUBPD as u32 {
self.stats.sse_instructions += 1;
}
if opcode >= X86Opcode::VADDPS as u32 && opcode <= X86Opcode::VZEROUPPER as u32 {
self.stats.avx_instructions += 1;
}
if opcode >= X86Opcode::VPADDD_Z as u32 {
self.stats.avx512_instructions += 1;
}
}
}
mf
}
pub fn run_32bit(
&mut self,
func_name: &str,
build_fn: impl FnOnce(&mut X86DAGBuilder),
) -> MachineFunction {
let mut builder = X86DAGBuilder::new_32bit(
self.subtarget.clone(),
self.calling_conv.clone(),
);
build_fn(&mut builder);
let mut x86_dag = builder.finish();
self.stats.accumulate(&x86_dag);
if self.enable_combiner {
let mut combiner = X86DAGCombine::new(self.subtarget.clone());
combiner.combine(&mut x86_dag);
self.stats.nodes_combined = combiner.x86_num_combined;
}
if self.enable_legalizer {
let mut legalizer = X86DAGLegalize::new_32bit(self.subtarget.clone());
legalizer.legalize(&mut x86_dag);
}
let mut emitter = X86InstrEmitter::new_32bit(self.subtarget.clone(), func_name);
emitter.emit(x86_dag);
emitter.finish()
}
}
#[cfg(test)]
mod pipeline_extended_tests {
use super::*;
fn make_pipeline() -> X86DAGPipeline {
X86DAGPipeline::new(X86Subtarget::new("x86-64", "generic"))
}
#[test]
fn test_pipeline_creation() {
let pipeline = make_pipeline();
assert!(pipeline.enable_combiner);
assert!(pipeline.enable_legalizer);
assert_eq!(pipeline.max_combine_iterations, 4);
}
#[test]
fn test_pipeline_without_combiner() {
let pipeline = make_pipeline().without_combiner();
assert!(!pipeline.enable_combiner);
assert!(pipeline.enable_legalizer);
}
#[test]
fn test_pipeline_without_legalizer() {
let pipeline = make_pipeline().without_legalizer();
assert!(pipeline.enable_combiner);
assert!(!pipeline.enable_legalizer);
}
#[test]
fn test_pipeline_with_max_iterations() {
let pipeline = make_pipeline().with_max_combine_iterations(10);
assert_eq!(pipeline.max_combine_iterations, 10);
}
#[test]
fn test_pipeline_with_sched_model() {
let pipeline = make_pipeline().with_sched_model(X86SchedModelKind::IceLake);
assert_eq!(pipeline.sched_model_kind, X86SchedModelKind::IceLake);
}
#[test]
fn test_pipeline_run_simple() {
let mut pipeline = make_pipeline();
let mf = pipeline.run("simple", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
});
assert_eq!(mf.name, "simple");
assert!(pipeline.stats.instructions_emitted > 0);
}
#[test]
fn test_pipeline_run_arithmetic() {
let mut pipeline = make_pipeline();
let mf = pipeline.run("arith", |builder| {
let x = builder.x86_dag.get_vreg(Type::integer(32));
let y = builder.x86_dag.get_vreg(Type::integer(32));
let z = builder.x86_dag.get_vreg(Type::integer(32));
let t1 = builder.lower_add(x, y);
let t2 = builder.lower_mul(t1, z);
builder.lower_ret(Some(t2));
});
assert_eq!(mf.name, "arith");
}
#[test]
fn test_pipeline_run_32bit() {
let mut pipeline = X86DAGPipeline::new_32bit(X86Subtarget::new("x86-64", "generic"));
let mf = pipeline.run_32bit("simple32", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
});
assert_eq!(mf.name, "simple32");
}
#[test]
fn test_pipeline_stats_accumulation() {
let mut pipeline = make_pipeline();
pipeline.run("stats_test", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
let b = builder.x86_dag.get_vreg(Type::integer(32));
let sum = builder.lower_add(a, b);
let diff = builder.lower_sub(sum, a);
builder.lower_ret(Some(diff));
});
assert!(pipeline.stats.instructions_emitted > 0);
assert!(pipeline.stats.total_nodes > 0);
}
#[test]
fn test_pipeline_run_without_combiner() {
let mut pipeline = make_pipeline().without_combiner();
let mf = pipeline.run("no_combine", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
});
assert_eq!(mf.name, "no_combine");
}
#[test]
fn test_pipeline_run_without_legalizer() {
let mut pipeline = make_pipeline().without_legalizer();
let mf = pipeline.run("no_legalize", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
});
assert_eq!(mf.name, "no_legalize");
}
#[test]
fn test_pipeline_idempotent_runs() {
let subtarget = X86Subtarget::new("x86-64", "generic");
let mf1 = {
let mut pipeline = X86DAGPipeline::new(subtarget.clone());
pipeline.run("idem", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
})
};
let mf2 = {
let mut pipeline = X86DAGPipeline::new(subtarget.clone());
pipeline.run("idem", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
})
};
assert_eq!(mf1.name, mf2.name);
assert_eq!(mf1.blocks.len(), mf2.blocks.len());
}
#[test]
fn test_pipeline_multiple_sched_models() {
let models = [
X86SchedModelKind::SkylakeClient,
X86SchedModelKind::IceLake,
X86SchedModelKind::Zen3,
X86SchedModelKind::Zen4,
X86SchedModelKind::Zen5,
];
for &model in &models {
let mut pipeline = make_pipeline().with_sched_model(model);
let mf = pipeline.run("sched_test", |builder| {
let a = builder.x86_dag.get_vreg(Type::integer(32));
builder.lower_ret(Some(a));
});
assert_eq!(mf.name, "sched_test");
}
}
}