use super::sd_node::*;
use crate::support::math_extras::*;
use crate::types::{Type, TypeKind};
use std::collections::HashSet;
pub struct DAGCombiner {
pub num_combined: usize,
pub modified: bool,
max_iterations: usize,
worklist: Vec<usize>,
visited: HashSet<usize>,
}
impl Default for DAGCombiner {
fn default() -> Self {
Self::new()
}
}
impl DAGCombiner {
pub fn new() -> Self {
Self {
num_combined: 0,
modified: false,
max_iterations: 100,
worklist: Vec::new(),
visited: HashSet::new(),
}
}
pub fn with_max_iterations(max_iterations: usize) -> Self {
Self {
num_combined: 0,
modified: false,
max_iterations,
worklist: Vec::new(),
visited: HashSet::new(),
}
}
pub fn combine(&mut self, dag: &mut SelectionDAG) -> usize {
self.num_combined = 0;
self.modified = false;
self.run_combine_passes(dag)
}
pub fn run_combine_passes(&mut self, dag: &mut SelectionDAG) -> usize {
let total_nodes_initial = dag.nodes.len();
let mut pass = 0;
loop {
self.modified = false;
self.worklist.clear();
self.visited.clear();
for i in 0..dag.nodes.len() {
self.worklist.push(i);
}
while let Some(node_id) = self.worklist.pop() {
if self.visited.contains(&node_id) {
continue;
}
self.visited.insert(node_id);
self.combine_node(dag, node_id);
}
pass += 1;
if !self.modified || pass >= self.max_iterations {
break;
}
}
self.num_combined
}
pub fn combine_node(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
if node_id >= dag.nodes.len() {
return false;
}
if dag.nodes[node_id].value_types.is_empty() && dag.nodes[node_id].operands.is_empty() {
return false;
}
let opcode = dag.nodes[node_id].opcode;
if self.propagate_undef(dag, node_id) {
self.num_combined += 1;
self.modified = true;
return true;
}
let changed = match opcode {
SDOpcode::Add
| SDOpcode::Sub
| SDOpcode::Mul
| SDOpcode::And
| SDOpcode::Or
| SDOpcode::Xor
| SDOpcode::Shl
| SDOpcode::Sra
| SDOpcode::Srl => self.combine_binary_op(dag, node_id),
SDOpcode::FAdd | SDOpcode::FSub | SDOpcode::FMul | SDOpcode::FDiv => {
self.combine_fp_op(dag, node_id)
}
SDOpcode::UDiv | SDOpcode::SDiv | SDOpcode::URem | SDOpcode::SRem => {
self.combine_div_rem(dag, node_id)
}
SDOpcode::Select | SDOpcode::VSelect => self.combine_select(dag, node_id),
SDOpcode::SetCC => self.combine_setcc(dag, node_id),
SDOpcode::Load => self.combine_load(dag, node_id),
SDOpcode::Store => self.combine_store(dag, node_id),
SDOpcode::BuildVector => self.combine_build_vector(dag, node_id),
SDOpcode::ExtractElement => self.combine_extract_element(dag, node_id),
SDOpcode::InsertElement => self.combine_insert_element(dag, node_id),
SDOpcode::ZExt | SDOpcode::SExt | SDOpcode::Trunc => {
self.combine_conversion(dag, node_id)
}
SDOpcode::AssertZExt | SDOpcode::AssertSExt => self.combine_assert_ext(dag, node_id),
SDOpcode::Bitcast => self.combine_bitcast(dag, node_id),
_ => false,
};
if changed {
self.modified = true;
self.num_combined += 1;
}
changed
}
fn try_constant_fold(&mut self, dag: &mut SelectionDAG, node_id: usize) -> Option<SDValue> {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return None;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let (lhs_const, rhs_const) = match (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
(Some(l), Some(r)) => (l, r),
_ => return None,
};
let ty = node.value_types.first()?.clone();
let bits = self.get_type_bits(&ty);
let result: Option<u64> = match node.opcode {
SDOpcode::Add => Some(lhs_const.wrapping_add(rhs_const)),
SDOpcode::Sub => Some(lhs_const.wrapping_sub(rhs_const)),
SDOpcode::Mul => Some(lhs_const.wrapping_mul(rhs_const)),
SDOpcode::And => Some(lhs_const & rhs_const),
SDOpcode::Or => Some(lhs_const | rhs_const),
SDOpcode::Xor => Some(lhs_const ^ rhs_const),
SDOpcode::Shl => {
let shift = rhs_const % (bits as u64);
Some(lhs_const.wrapping_shl(shift as u32))
}
SDOpcode::Srl => {
let shift = rhs_const % (bits as u64);
Some(lhs_const.wrapping_shr(shift as u32))
}
SDOpcode::Sra => {
let shift = rhs_const % (bits as u64);
let signed = lhs_const as i64;
let result = signed.wrapping_shr(shift as u32);
Some(result as u64)
}
SDOpcode::UDiv => {
if rhs_const == 0 {
None
} else {
Some(lhs_const / rhs_const)
}
}
SDOpcode::SDiv => {
if rhs_const == 0 {
None
} else {
let result = (lhs_const as i64) / (rhs_const as i64);
Some(result as u64)
}
}
SDOpcode::URem => {
if rhs_const == 0 {
None
} else {
Some(lhs_const % rhs_const)
}
}
SDOpcode::SRem => {
if rhs_const == 0 {
None
} else {
let result = (lhs_const as i64) % (rhs_const as i64);
Some(result as u64)
}
}
_ => None,
};
result.map(|val| {
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
dag.get_constant(val & mask, ty)
})
}
fn get_constant_value(&self, dag: &SelectionDAG, val: SDValue) -> Option<u64> {
let node = dag.get_node(val);
if node.opcode == SDOpcode::Constant {
for entry in &dag.constant_pool {
if entry.1.node_id == val.node_id && entry.1.res_no == val.res_no {
return Some(entry.0 .0);
}
}
None
} else {
None
}
}
fn combine_binary_op(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let opcode = dag.nodes[node_id].opcode;
let operands = dag.nodes[node_id].operands.clone();
let ty = dag.nodes[node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
if let Some(folded) = self.try_constant_fold(dag, node_id) {
self.replace_node(dag, node_id, folded);
return true;
}
if operands.len() < 2 {
return false;
}
let lhs = operands[0];
let rhs = operands[1];
if let Some(identity_val) = self.get_constant_value(dag, rhs) {
match opcode {
SDOpcode::Add if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Sub if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Mul if identity_val == 1 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Mul if identity_val == 0 => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::And if identity_val == self.get_all_ones(&ty) => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::And if identity_val == 0 => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::Or if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Or if identity_val == self.get_all_ones(&ty) => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::Xor if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Shl if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Srl if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Sra if identity_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
_ => {}
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
match opcode {
SDOpcode::Add if lhs_val == 0 => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::Mul if lhs_val == 1 => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::Mul if lhs_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::And if lhs_val == 0 => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Or if lhs_val == self.get_all_ones(&ty) => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Or if lhs_val == 0 => {
self.replace_node(dag, node_id, rhs);
return true;
}
SDOpcode::Xor if lhs_val == 0 => {
self.replace_node(dag, node_id, rhs);
return true;
}
_ => {}
}
}
if lhs == rhs {
match opcode {
SDOpcode::Xor => {
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
return true;
}
SDOpcode::Sub => {
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
return true;
}
SDOpcode::And => {
self.replace_node(dag, node_id, lhs);
return true;
}
SDOpcode::Or => {
self.replace_node(dag, node_id, lhs);
return true;
}
_ => {}
}
}
if opcode == SDOpcode::Mul {
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val > 1 && is_power_of_2_u64(rhs_val) {
let shift = floor_log2_u64(rhs_val);
let shift_val = dag.get_constant(shift as u64, ty.clone());
let shl_node = dag.get_binary_op(SDOpcode::Shl, ty.clone(), lhs, shift_val);
self.replace_node(dag, node_id, shl_node);
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val > 1 && is_power_of_2_u64(lhs_val) {
let shift = floor_log2_u64(lhs_val);
let shift_val = dag.get_constant(shift as u64, ty.clone());
let shl_node = dag.get_binary_op(SDOpcode::Shl, ty.clone(), rhs, shift_val);
self.replace_node(dag, node_id, shl_node);
return true;
}
}
}
if opcode == SDOpcode::Add && lhs == rhs {
let one = dag.get_constant(1, ty.clone());
let shl_node = dag.get_binary_op(SDOpcode::Shl, ty.clone(), lhs, one);
self.replace_node(dag, node_id, shl_node);
return true;
}
if opcode == SDOpcode::Sub {
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val == 0 {
}
}
}
if opcode == SDOpcode::UDiv || opcode == SDOpcode::URem {
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val > 1 && is_power_of_2_u64(rhs_val) {
let shift = floor_log2_u64(rhs_val);
if opcode == SDOpcode::UDiv {
let shift_val = dag.get_constant(shift as u64, ty.clone());
let shr = dag.get_binary_op(SDOpcode::Srl, ty.clone(), lhs, shift_val);
self.replace_node(dag, node_id, shr);
return true;
} else {
let mask_val = dag.get_constant(rhs_val - 1, ty.clone());
let and = dag.get_binary_op(SDOpcode::And, ty, lhs, mask_val);
self.replace_node(dag, node_id, and);
return true;
}
}
}
}
if opcode == SDOpcode::SDiv {
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val > 1 && is_power_of_2_u64(rhs_val) {
}
}
}
if opcode == SDOpcode::And {
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 1 {
}
}
}
if opcode == SDOpcode::Add {
if let Some(rhs_val2) = self.get_constant_value(dag, rhs) {
let (lhs_opcode, lhs_op0, lhs_op1) = {
let lhs_node = dag.get_node(lhs);
(
lhs_node.opcode,
lhs_node.operands.first().copied(),
lhs_node.operands.get(1).copied(),
)
};
if lhs_opcode == SDOpcode::Add && lhs_op0.is_some() && lhs_op1.is_some() {
if let Some(inner_c) = self.get_constant_value(dag, lhs_op1.unwrap()) {
let new_c = inner_c.wrapping_add(rhs_val2);
let bits = self.get_type_bits(&ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let c_val = dag.get_constant(new_c & mask, ty.clone());
let inner_add =
dag.get_binary_op(SDOpcode::Add, ty.clone(), lhs_op0.unwrap(), c_val);
self.replace_node(dag, node_id, inner_add);
return true;
}
}
}
}
if opcode == SDOpcode::Mul {
if let Some(rhs_val2) = self.get_constant_value(dag, rhs) {
let (lhs_opcode, lhs_op0, lhs_op1) = {
let lhs_node = dag.get_node(lhs);
(
lhs_node.opcode,
lhs_node.operands.first().copied(),
lhs_node.operands.get(1).copied(),
)
};
if lhs_opcode == SDOpcode::Mul && lhs_op0.is_some() && lhs_op1.is_some() {
if let Some(inner_c) = self.get_constant_value(dag, lhs_op1.unwrap()) {
let new_c = inner_c.wrapping_mul(rhs_val2);
let bits = self.get_type_bits(&ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let c_val = dag.get_constant(new_c & mask, ty.clone());
let inner_mul =
dag.get_binary_op(SDOpcode::Mul, ty.clone(), lhs_op0.unwrap(), c_val);
self.replace_node(dag, node_id, inner_mul);
return true;
}
}
}
}
false
}
fn combine_fp_op(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let ty = node.value_types.first().cloned().unwrap_or(Type::float());
let opcode = node.opcode;
if let (Some(lhs_val), Some(rhs_val)) = (
self.get_fp_constant_value(dag, lhs),
self.get_fp_constant_value(dag, rhs),
) {
if let Some(folded) = self.try_fp_constant_fold(dag, opcode, lhs_val, rhs_val, &ty) {
self.replace_node(dag, node_id, folded);
return true;
}
}
if opcode == SDOpcode::FAdd {
if let Some(rhs_val) = self.get_fp_constant_value(dag, rhs) {
if rhs_val == 0.0 {
self.replace_node(dag, node_id, lhs);
return true;
}
}
if let Some(lhs_val) = self.get_fp_constant_value(dag, lhs) {
if lhs_val == 0.0 {
self.replace_node(dag, node_id, rhs);
return true;
}
}
}
if opcode == SDOpcode::FSub {
if let Some(rhs_val) = self.get_fp_constant_value(dag, rhs) {
if rhs_val == 0.0 {
self.replace_node(dag, node_id, lhs);
return true;
}
}
if lhs == rhs {
let zero = dag.get_constant_fp(0, ty);
self.replace_node(dag, node_id, zero);
return true;
}
}
if opcode == SDOpcode::FMul {
if let Some(rhs_val) = self.get_fp_constant_value(dag, rhs) {
if (rhs_val - 1.0).abs() < 1e-15 {
self.replace_node(dag, node_id, lhs);
return true;
}
if rhs_val == 0.0 {
self.replace_node(dag, node_id, rhs);
return true;
}
}
if let Some(lhs_val) = self.get_fp_constant_value(dag, lhs) {
if (lhs_val - 1.0).abs() < 1e-15 {
self.replace_node(dag, node_id, rhs);
return true;
}
if lhs_val == 0.0 {
self.replace_node(dag, node_id, lhs);
return true;
}
}
if let Some(rhs_val) = self.get_fp_constant_value(dag, rhs) {
if (rhs_val - 2.0).abs() < 1e-15 {
let fadd_node = dag.get_binary_op(SDOpcode::FAdd, ty.clone(), lhs, lhs);
self.replace_node(dag, node_id, fadd_node);
return true;
}
}
}
if opcode == SDOpcode::FDiv {
if let Some(rhs_val) = self.get_fp_constant_value(dag, rhs) {
if (rhs_val - 1.0).abs() < 1e-15 {
self.replace_node(dag, node_id, lhs);
return true;
}
}
}
if opcode == SDOpcode::FSub {
if let Some(lhs_val) = self.get_fp_constant_value(dag, lhs) {
if lhs_val == 0.0 {
let rhs_node = dag.get_node(rhs);
if rhs_node.opcode == SDOpcode::FSub && rhs_node.operands.len() >= 2 {
if let Some(inner_lhs) =
self.get_fp_constant_value(dag, rhs_node.operands[0])
{
if inner_lhs == 0.0 {
self.replace_node(dag, node_id, rhs_node.operands[1]);
return true;
}
}
}
}
}
}
false
}
fn get_fp_constant_value(&self, dag: &SelectionDAG, val: SDValue) -> Option<f64> {
let node = dag.get_node(val);
if node.opcode == SDOpcode::ConstantFP {
for entry in &dag.constant_fp_pool {
if entry.1.node_id == val.node_id && entry.1.res_no == val.res_no {
return Some(f64::from_bits(entry.0 .0));
}
}
}
None
}
fn try_fp_constant_fold(
&mut self,
dag: &mut SelectionDAG,
opcode: SDOpcode,
a: f64,
b: f64,
ty: &Type,
) -> Option<SDValue> {
let result = match opcode {
SDOpcode::FAdd => a + b,
SDOpcode::FSub => a - b,
SDOpcode::FMul => a * b,
SDOpcode::FDiv => {
if b == 0.0 {
return None;
}
a / b
}
_ => return None,
};
Some(dag.get_constant_fp(result.to_bits(), ty.clone()))
}
fn combine_div_rem(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let opcode = node.opcode;
if let Some(folded) = self.try_constant_fold(dag, node_id) {
self.replace_node(dag, node_id, folded);
return true;
}
if opcode == SDOpcode::UDiv && lhs == rhs {
let one = dag.get_constant(1, ty);
self.replace_node(dag, node_id, one);
return true;
}
if opcode == SDOpcode::URem && lhs == rhs {
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
return true;
}
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val > 1 && is_power_of_2_u64(rhs_val) {
let shift = floor_log2_u64(rhs_val);
if opcode == SDOpcode::UDiv {
let shift_val = dag.get_constant(shift as u64, ty.clone());
let shr = dag.get_binary_op(SDOpcode::Srl, ty.clone(), lhs, shift_val);
self.replace_node(dag, node_id, shr);
return true;
} else if opcode == SDOpcode::URem {
let mask_val = dag.get_constant(rhs_val - 1, ty.clone());
let and = dag.get_binary_op(SDOpcode::And, ty, lhs, mask_val);
self.replace_node(dag, node_id, and);
return true;
}
}
}
false
}
fn combine_select(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 3 {
return false;
}
let cond = node.operands[0];
let true_val = node.operands[1];
let false_val = node.operands[2];
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
if let Some(c_val) = self.get_constant_value(dag, cond) {
if c_val != 0 {
self.replace_node(dag, node_id, true_val);
return true;
} else {
self.replace_node(dag, node_id, false_val);
return true;
}
}
if true_val == false_val {
self.replace_node(dag, node_id, true_val);
return true;
}
if dag.get_node(cond).opcode == SDOpcode::Undef {
let undef = dag.get_undef(ty);
self.replace_node(dag, node_id, undef);
return true;
}
if dag.get_node(true_val).opcode == SDOpcode::Undef {
self.replace_node(dag, node_id, false_val);
return true;
}
if dag.get_node(false_val).opcode == SDOpcode::Undef {
self.replace_node(dag, node_id, true_val);
return true;
}
false
}
fn combine_setcc(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
if lhs == rhs {
let one = dag.get_constant(1, Type::i1());
self.replace_node(dag, node_id, one);
return true;
}
if let (Some(l), Some(r)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
let result = if l == r { 0 } else { 1 };
let val = dag.get_constant(result, Type::i1());
self.replace_node(dag, node_id, val);
return true;
}
if dag.get_node(lhs).opcode == SDOpcode::SetCC {
if let Some(r_val) = self.get_constant_value(dag, rhs) {
if r_val == 0 {
self.replace_node(dag, node_id, lhs);
return true;
}
}
}
false
}
fn combine_load(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let chain = node.operands[0];
let ptr = node.operands[1];
let load_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
if dag.get_node(ptr).opcode == SDOpcode::Undef {
let undef = dag.get_undef(load_ty);
self.replace_node(dag, node_id, undef);
return true;
}
if dag.get_node(ptr).opcode == SDOpcode::Add && dag.get_node(ptr).operands.len() >= 2 {
if let Some(offset) = self.get_constant_value(dag, dag.get_node(ptr).operands[1]) {
}
}
if let Some(store_node_id) = self.find_previous_store(dag, chain, ptr) {
let store_node = &dag.nodes[store_node_id];
if store_node.operands.len() >= 3 {
let stored_val = store_node.operands[1];
self.replace_node(dag, node_id, stored_val);
return true;
}
}
false
}
fn find_previous_store(
&self,
dag: &SelectionDAG,
chain: SDValue,
ptr: SDValue,
) -> Option<usize> {
let mut current = chain;
let mut visited_nodes = HashSet::new();
loop {
if visited_nodes.contains(¤t.node_id) {
break;
}
visited_nodes.insert(current.node_id);
let node = dag.get_node(current);
if node.opcode == SDOpcode::Store && node.operands.len() >= 3 {
let store_ptr = node.operands[2];
if store_ptr == ptr {
return Some(current.node_id);
}
}
if node.operands.is_empty() {
break;
}
current = node.operands[0];
}
None
}
fn combine_store(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 3 {
return false;
}
let val = node.operands[1];
let ptr = node.operands[2];
if dag.get_node(val).opcode == SDOpcode::Undef {
let chain = node.operands[0];
self.replace_node(dag, node_id, chain);
return true;
}
false
}
fn combine_build_vector(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let first = &node.operands[0];
let first_node = dag.get_node(*first);
if first_node.opcode == SDOpcode::ExtractElement && first_node.operands.len() >= 2 {
let source_vec = first_node.operands[0];
let mut all_same_source = true;
for (i, op) in node.operands.iter().enumerate() {
let op_node = dag.get_node(*op);
if op_node.opcode != SDOpcode::ExtractElement
|| op_node.operands.len() < 2
|| op_node.operands[0] != source_vec
{
all_same_source = false;
break;
}
if let Some(idx_val) = self.get_constant_value(dag, op_node.operands[1]) {
if idx_val != i as u64 {
all_same_source = false;
break;
}
} else {
all_same_source = false;
break;
}
}
if all_same_source {
self.replace_node(dag, node_id, source_vec);
return true;
}
}
let all_undef = node
.operands
.iter()
.all(|op| dag.get_node(*op).opcode == SDOpcode::Undef);
if all_undef {
let vec_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let undef = dag.get_undef(vec_ty);
self.replace_node(dag, node_id, undef);
return true;
}
false
}
fn combine_extract_element(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let vec = node.operands[0];
let idx = node.operands[1];
let vec_node = dag.get_node(vec);
if vec_node.opcode == SDOpcode::BuildVector {
if let Some(idx_val) = self.get_constant_value(dag, idx) {
let idx_usize = idx_val as usize;
if idx_usize < vec_node.operands.len() {
self.replace_node(dag, node_id, vec_node.operands[idx_usize]);
return true;
}
}
}
if vec_node.opcode == SDOpcode::InsertElement && vec_node.operands.len() >= 3 {
let insert_idx = vec_node.operands[2];
if insert_idx == idx {
self.replace_node(dag, node_id, vec_node.operands[1]);
return true;
}
}
false
}
fn combine_insert_element(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 3 {
return false;
}
let vec = node.operands[0];
let elem = node.operands[1];
let idx = node.operands[2];
let vec_node = dag.get_node(vec);
if vec_node.opcode == SDOpcode::InsertElement && vec_node.operands.len() >= 3 {
let prev_idx = vec_node.operands[2];
if prev_idx == idx {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let new_insert = dag.add_node(
SDOpcode::InsertElement,
vec![ty],
vec![vec_node.operands[0], elem, idx],
);
self.replace_node(dag, node_id, new_insert);
return true;
}
}
if vec_node.opcode == SDOpcode::Undef {
if let Some(idx_val) = self.get_constant_value(dag, idx) {
let first_ty = node.value_types.first().cloned();
let num_elems = match first_ty.as_ref().and_then(|t| match &t.kind {
TypeKind::FixedVector { len, .. } => Some(*len as usize),
_ => None,
}) {
Some(n) => n,
None => return false,
};
if (idx_val as usize) < num_elems {
let elem_ty = first_ty.unwrap();
let undef_elem = dag.get_undef(Type::i32());
let mut elements = vec![undef_elem; num_elems];
elements[idx_val as usize] = elem;
let build =
dag.add_node(SDOpcode::BuildVector, vec![elem_ty.clone()], elements);
self.replace_node(dag, node_id, build);
return true;
}
}
}
false
}
fn combine_conversion(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let op = node.operands[0];
let op_node = dag.get_node(op);
match node.opcode {
SDOpcode::ZExt => {
if op_node.opcode == SDOpcode::Trunc && op_node.operands.len() >= 1 {
}
if op_node.opcode == SDOpcode::ZExt && op_node.operands.len() >= 1 {
let src_ty = op_node.value_types.first().cloned().unwrap_or(Type::i32());
let dst_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let src_bits = self.get_type_bits(&src_ty);
let dst_bits = self.get_type_bits(&dst_ty);
if src_bits <= dst_bits {
let new_zext =
dag.add_node(SDOpcode::ZExt, vec![dst_ty], vec![op_node.operands[0]]);
self.replace_node(dag, node_id, new_zext);
return true;
}
}
}
SDOpcode::SExt => {
if op_node.opcode == SDOpcode::SExt && op_node.operands.len() >= 1 {
let dst_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let new_sext =
dag.add_node(SDOpcode::SExt, vec![dst_ty], vec![op_node.operands[0]]);
self.replace_node(dag, node_id, new_sext);
return true;
}
}
SDOpcode::Trunc => {
if op_node.opcode == SDOpcode::ZExt || op_node.opcode == SDOpcode::SExt {
if op_node.operands.len() >= 1 {
let src = op_node.operands[0];
let src_ty = src.get_value_type(dag);
let dst_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let src_bits = self.get_type_bits(&src_ty);
let dst_bits = self.get_type_bits(&dst_ty);
if src_bits <= dst_bits {
if src_bits == dst_bits {
self.replace_node(dag, node_id, src);
return true;
}
} else {
let new_trunc = dag.add_node(SDOpcode::Trunc, vec![dst_ty], vec![src]);
self.replace_node(dag, node_id, new_trunc);
return true;
}
}
}
if op_node.opcode == SDOpcode::Trunc && op_node.operands.len() >= 1 {
let dst_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let new_trunc =
dag.add_node(SDOpcode::Trunc, vec![dst_ty], vec![op_node.operands[0]]);
self.replace_node(dag, node_id, new_trunc);
return true;
}
}
_ => {}
}
false
}
fn combine_assert_ext(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let op = node.operands[0];
let op_node = dag.get_node(op);
if op_node.opcode == SDOpcode::ZExt || op_node.opcode == SDOpcode::SExt {
self.replace_node(dag, node_id, op);
return true;
}
if op_node.opcode == SDOpcode::AssertZExt || op_node.opcode == SDOpcode::AssertSExt {
if op_node.operands.len() >= 1 {
self.replace_node(dag, node_id, op_node.operands[0]);
return true;
}
}
false
}
fn combine_bitcast(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let op = node.operands[0];
let op_node = dag.get_node(op);
if op_node.opcode == SDOpcode::Bitcast && op_node.operands.len() >= 1 {
let inner = op_node.operands[0];
self.replace_node(dag, node_id, inner);
return true;
}
false
}
fn propagate_undef(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.is_binary_op() {
for op in &node.operands {
if dag.get_node(*op).opcode == SDOpcode::Undef {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let undef = dag.get_undef(ty);
self.replace_node(dag, node_id, undef);
return true;
}
}
}
if node.opcode == SDOpcode::Load && node.operands.len() >= 2 {
if dag.get_node(node.operands[1]).opcode == SDOpcode::Undef {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let undef = dag.get_undef(ty);
self.replace_node(dag, node_id, undef);
return true;
}
}
false
}
fn get_type_bits(&self, ty: &Type) -> u32 {
match &ty.kind {
TypeKind::Integer { bits } => *bits,
TypeKind::Half => 16,
TypeKind::Float => 32,
TypeKind::Double => 64,
TypeKind::FP128 => 128,
TypeKind::X86FP80 => 80,
_ => 32,
}
}
fn get_all_ones(&self, ty: &Type) -> u64 {
let bits = self.get_type_bits(ty);
if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
}
}
fn replace_node(&mut self, dag: &mut SelectionDAG, node_id: usize, replacement: SDValue) {
let orig_val = SDValue::new(node_id, 0);
dag.replace_all_uses_with(orig_val, replacement);
if node_id < dag.nodes.len() {
dag.nodes[node_id].operands.clear();
dag.nodes[node_id].value_types.clear();
}
}
pub fn combine_add_add_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let opcode = dag.nodes[node_id].opcode;
if opcode != SDOpcode::Add {
return false;
}
let operands = dag.nodes[node_id].operands.clone();
if operands.len() < 2 {
return false;
}
let lhs = operands[0];
let rhs = operands[1];
let c2 = self.get_constant_value(dag, rhs);
if c2.is_none() {
return false;
}
let c2_val = c2.unwrap();
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode != SDOpcode::Add {
return false;
}
let lhs_operands = lhs_node.operands.clone();
if lhs_operands.len() < 2 {
return false;
}
let c1 = self.get_constant_value(dag, lhs_operands[1]);
if let Some(c1_val) = c1 {
let combined = c1_val.wrapping_add(c2_val);
let ty = dag.nodes[node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
let new_c = dag.get_constant(combined as u64, ty.clone());
let new_add = dag.add_node(SDOpcode::Add, vec![ty], vec![lhs_operands[0], new_c]);
self.replace_node(dag, node_id, new_add);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_sub_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Sub {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
if operands[0] == operands[1] {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_xor_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
if operands[0] == operands[1] {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_or_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
if operands[0] == operands[1] {
self.replace_node(dag, node_id, operands[0]);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_and_zero(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let val = self.get_constant_value(dag, operands[1]);
if val == Some(0) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_or_all_ones(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let val = self.get_constant_value(dag, operands[1]);
let all_ones = self.get_all_ones(&ty);
if val == Some(all_ones) {
self.replace_node(dag, node_id, operands[1]);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_mul_pow2_to_shl(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Mul {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let val = self.get_constant_value(dag, operands[1]);
if let Some(v) = val {
if v > 0 && (v & (v - 1)) == 0 {
let shift = v.trailing_zeros() as u64;
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let shift_c = dag.get_constant(shift, ty.clone());
let shl = dag.add_node(SDOpcode::Shl, vec![ty], vec![operands[0], shift_c]);
self.replace_node(dag, node_id, shl);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_udiv_pow2_to_srl(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::UDiv {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let val = self.get_constant_value(dag, operands[1]);
if let Some(v) = val {
if v > 0 && (v & (v - 1)) == 0 {
let shift = v.trailing_zeros() as u64;
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let shift_c = dag.get_constant(shift, ty.clone());
let srl = dag.add_node(SDOpcode::Srl, vec![ty], vec![operands[0], shift_c]);
self.replace_node(dag, node_id, srl);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_add_mul_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let lhs = operands[0];
let rhs = operands[1];
let rhs_node = dag.get_node(rhs);
if rhs_node.opcode == SDOpcode::Mul {
let rhs_ops = rhs_node.operands.clone();
if rhs_ops.len() >= 2 && rhs_ops[0] == lhs {
if let Some(c) = self.get_constant_value(dag, rhs_ops[1]) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let new_c = dag.get_constant((c + 1) as u64, ty.clone());
let new_mul = dag.add_node(SDOpcode::Mul, vec![ty], vec![lhs, new_c]);
self.replace_node(dag, node_id, new_mul);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_fadd_fneg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::FAdd {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let rhs_node = dag.get_node(operands[1]);
if rhs_node.opcode == SDOpcode::FNeg {
let fneg_ops = rhs_node.operands.clone();
if !fneg_ops.is_empty() {
let ty = node.value_types.first().cloned().unwrap_or(Type::float());
let fsub = dag.add_node(SDOpcode::FSub, vec![ty], vec![operands[0], fneg_ops[0]]);
self.replace_node(dag, node_id, fsub);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_fsub_fneg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::FSub {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let rhs_node = dag.get_node(operands[1]);
if rhs_node.opcode == SDOpcode::FNeg {
let fneg_ops = rhs_node.operands.clone();
if !fneg_ops.is_empty() {
let ty = node.value_types.first().cloned().unwrap_or(Type::float());
let fadd = dag.add_node(SDOpcode::FAdd, vec![ty], vec![operands[0], fneg_ops[0]]);
self.replace_node(dag, node_id, fadd);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_abs_abs(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::FAbs) {
return false;
}
let operands = node.operands.clone();
if operands.is_empty() {
return false;
}
let operand_node = dag.get_node(operands[0]);
if matches!(operand_node.opcode, SDOpcode::FAbs) {
self.replace_node(dag, node_id, operands[0]);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_neg_neg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::FNeg) {
return false;
}
let operands = node.operands.clone();
if operands.is_empty() {
return false;
}
let operand_node = dag.get_node(operands[0]);
if operand_node.opcode == SDOpcode::FNeg {
let inner_ops = operand_node.operands.clone();
if !inner_ops.is_empty() {
self.replace_node(dag, node_id, inner_ops[0]);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_select_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::Select | SDOpcode::SelectCC) {
return false;
}
let operands = node.operands.clone();
if operands.len() < 3 {
return false;
}
let (true_idx, false_idx) = if node.opcode == SDOpcode::Select {
(1, 2)
} else {
(2, 3)
};
if operands[true_idx] == operands[false_idx] {
self.replace_node(dag, node_id, operands[true_idx]);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_setcc_eq_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::SelectCC {
return false;
}
let operands = node.operands.clone();
if operands.len() < 5 {
return false;
}
if operands[0] == operands[1] {
let cc_val = self.get_constant_value(dag, operands[4]);
if cc_val == Some(0) {
self.replace_node(dag, node_id, operands[2]);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_bitcast_bitcast(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Bitcast {
return false;
}
let operands = node.operands.clone();
if operands.is_empty() {
return false;
}
let operand_node = dag.get_node(operands[0]);
if operand_node.opcode == SDOpcode::Bitcast {
let inner_ops = operand_node.operands.clone();
if !inner_ops.is_empty() {
let inner_ty = &dag.get_node(inner_ops[0]).value_types;
let outer_ty = &node.value_types;
if inner_ty == outer_ty {
self.replace_node(dag, node_id, inner_ops[0]);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_trunc_of_zext(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Trunc {
return false;
}
let operands = node.operands.clone();
if operands.is_empty() {
return false;
}
let operand_node = dag.get_node(operands[0]);
if operand_node.opcode == SDOpcode::ZExt {
let inner_ops = operand_node.operands.clone();
if !inner_ops.is_empty() {
let inner_ty = &dag.get_node(inner_ops[0]).value_types;
let outer_ty = &node.value_types;
if inner_ty == outer_ty {
self.replace_node(dag, node_id, inner_ops[0]);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_store_load_forward(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Load {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let ptr = operands[1];
let chain_val = operands[0];
let chain_node = dag.get_node(chain_val);
if chain_node.opcode == SDOpcode::Store {
let store_ops = chain_node.operands.clone();
if store_ops.len() >= 2 && store_ops[1] == ptr {
self.replace_node(dag, node_id, store_ops[0]);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_shl_masked_amount(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Shl {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let amount_node = dag.get_node(operands[1]);
if amount_node.opcode == SDOpcode::And {
let and_ops = amount_node.operands.clone();
if and_ops.len() >= 2 {
let mask = self.get_constant_value(dag, and_ops[1]);
let bits =
self.get_type_bits(node.value_types.first().unwrap_or(&Type::i32())) as u64;
if let Some(m) = mask {
if m as u64 >= bits - 1 {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let new_shl =
dag.add_node(SDOpcode::Shl, vec![ty], vec![operands[0], and_ops[0]]);
self.replace_node(dag, node_id, new_shl);
self.num_combined += 1;
return true;
}
}
}
}
false
}
pub fn combine_or_shifts_to_rotate(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let lhs_node = dag.get_node(operands[0]);
let rhs_node = dag.get_node(operands[1]);
let (shl_ops, srl_ops) =
if lhs_node.opcode == SDOpcode::Shl && rhs_node.opcode == SDOpcode::Srl {
(lhs_node.operands.clone(), rhs_node.operands.clone())
} else if rhs_node.opcode == SDOpcode::Shl && lhs_node.opcode == SDOpcode::Srl {
(rhs_node.operands.clone(), lhs_node.operands.clone())
} else {
return false;
};
if shl_ops.len() < 2 || srl_ops.len() < 2 {
return false;
}
if shl_ops[0] != srl_ops[0] {
return false;
}
let shl_amt = self.get_constant_value(dag, shl_ops[1]);
let srl_amt = self.get_constant_value(dag, srl_ops[1]);
let bits = self.get_type_bits(node.value_types.first().unwrap_or(&Type::i32())) as u64;
if let (Some(sa), Some(sra)) = (shl_amt, srl_amt) {
if sa as u64 + sra as u64 == bits {
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_add_shl_same(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let lhs_node = dag.get_node(operands[0]);
let rhs = operands[1];
if lhs_node.opcode == SDOpcode::Shl {
let shl_ops = lhs_node.operands.clone();
if shl_ops.len() >= 2 && shl_ops[0] == rhs {
if let Some(amt) = self.get_constant_value(dag, shl_ops[1]) {
let multiplier = (1u64 << amt) + 1;
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let c = dag.get_constant(multiplier, ty.clone());
let mul = dag.add_node(SDOpcode::Mul, vec![ty], vec![rhs, c]);
self.replace_node(dag, node_id, mul);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_demorgan_and(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let rhs_val = self.get_constant_value(dag, operands[1]);
let all_ones = self.get_all_ones(&ty);
if rhs_val == Some(all_ones) {
let lhs_node = dag.get_node(operands[0]);
if lhs_node.opcode == SDOpcode::And {
let and_ops = lhs_node.operands.clone();
if and_ops.len() >= 2 {
let neg_all = dag.get_constant(all_ones as u64, ty.clone());
let not_a =
dag.add_node(SDOpcode::Xor, vec![ty.clone()], vec![and_ops[0], neg_all]);
let not_b =
dag.add_node(SDOpcode::Xor, vec![ty.clone()], vec![and_ops[1], neg_all]);
let or_val = dag.add_node(SDOpcode::Or, vec![ty], vec![not_a, not_b]);
self.replace_node(dag, node_id, or_val);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_demorgan_or(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let rhs_val = self.get_constant_value(dag, operands[1]);
let all_ones = self.get_all_ones(&ty);
if rhs_val == Some(all_ones) {
let lhs_node = dag.get_node(operands[0]);
if lhs_node.opcode == SDOpcode::Or {
let or_ops = lhs_node.operands.clone();
if or_ops.len() >= 2 {
let neg_all = dag.get_constant(all_ones as u64, ty.clone());
let not_a =
dag.add_node(SDOpcode::Xor, vec![ty.clone()], vec![or_ops[0], neg_all]);
let not_b =
dag.add_node(SDOpcode::Xor, vec![ty.clone()], vec![or_ops[1], neg_all]);
let and_val = dag.add_node(SDOpcode::And, vec![ty], vec![not_a, not_b]);
self.replace_node(dag, node_id, and_val);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn simplify_demanded_bits_truncate(
&mut self,
dag: &mut SelectionDAG,
node_id: usize,
) -> bool {
let node_opcode;
let node_operands;
let node_dst_ty;
{
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Trunc {
return false;
}
node_opcode = node.opcode;
node_operands = node.operands.clone();
node_dst_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
}
if node_operands.is_empty() {
return false;
}
let src = node_operands[0];
let (src_opcode, src_operands, src_ty) = {
let src_node = dag.get_node(src);
let st = src.get_value_type(dag);
(src_node.opcode, src_node.operands.clone(), st)
};
let dst_bits = self.get_type_bits(&node_dst_ty);
if src_opcode == SDOpcode::And && src_operands.len() >= 2 {
if let Some(mask_val) = self.get_constant_value(dag, src_operands[1]) {
let truncated_mask = mask_val & ((1u64 << dst_bits) - 1);
if truncated_mask == mask_val {
let trunc_val = dag.add_node(
SDOpcode::Trunc,
vec![node_dst_ty.clone()],
vec![src_operands[0]],
);
let mask_c = dag.get_constant(truncated_mask, node_dst_ty.clone());
let and_val =
dag.add_node(SDOpcode::And, vec![node_dst_ty], vec![trunc_val, mask_c]);
self.replace_node(dag, node_id, and_val);
self.num_combined += 1;
return true;
}
}
}
let src_bits = self.get_type_bits(&src_ty);
if src_opcode == SDOpcode::ZExt && src_operands.len() >= 1 {
let inner = src_operands[0];
let inner_ty = inner.get_value_type(dag);
let inner_bits = self.get_type_bits(&inner_ty);
if inner_bits <= dst_bits {
if inner_bits == src_bits && inner_bits == dst_bits {
self.replace_node(dag, node_id, inner);
} else {
let new_zext =
dag.add_node(SDOpcode::ZExt, vec![node_dst_ty.clone()], vec![inner]);
self.replace_node(dag, node_id, new_zext);
}
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::SExt && src_operands.len() >= 1 {
let inner = src_operands[0];
let inner_ty = inner.get_value_type(dag);
let inner_bits = self.get_type_bits(&inner_ty);
if inner_bits <= dst_bits {
if inner_bits == src_bits && inner_bits == dst_bits {
self.replace_node(dag, node_id, inner);
} else {
let new_sext =
dag.add_node(SDOpcode::SExt, vec![node_dst_ty.clone()], vec![inner]);
self.replace_node(dag, node_id, new_sext);
}
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::Or && src_operands.len() >= 2 {
if let Some(rhs_val) = self.get_constant_value(dag, src_operands[1]) {
if rhs_val == 0 {
let trunc_val = dag.add_node(
SDOpcode::Trunc,
vec![node_dst_ty.clone()],
vec![src_operands[0]],
);
self.replace_node(dag, node_id, trunc_val);
self.num_combined += 1;
return true;
}
}
}
if src_opcode == SDOpcode::Xor && src_operands.len() >= 2 {
if src_operands[0] == src_operands[1] {
let zero = dag.get_constant(0, node_dst_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
}
false
}
pub fn simplify_demanded_bits_zext(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_dst_ty, node_operands) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ZExt {
return false;
}
(
node.value_types.first().cloned().unwrap_or(Type::i32()),
node.operands.clone(),
)
};
if node_operands.is_empty() {
return false;
}
let src = node_operands[0];
let (src_opcode, src_operands, src_vt) = {
let sn = dag.get_node(src);
(
sn.opcode,
sn.operands.clone(),
sn.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if src_opcode == SDOpcode::Trunc && src_operands.len() >= 1 {
let inner = src_operands[0];
let inner_ty = inner.get_value_type(dag);
let src_bits = self.get_type_bits(&src_vt);
let inner_bits = self.get_type_bits(&inner_ty);
let dst_bits = self.get_type_bits(&node_dst_ty);
if inner_bits <= dst_bits && inner_bits > src_bits {
if inner_bits == dst_bits {
self.replace_node(dag, node_id, inner);
} else {
let new_zext = dag.add_node(SDOpcode::ZExt, vec![node_dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_zext);
}
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::ZExt && src_operands.len() >= 1 {
let new_zext = dag.add_node(SDOpcode::ZExt, vec![node_dst_ty], vec![src_operands[0]]);
self.replace_node(dag, node_id, new_zext);
self.num_combined += 1;
return true;
}
false
}
pub fn simplify_demanded_bits_sext(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_dst_ty, node_operands) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::SExt {
return false;
}
(
node.value_types.first().cloned().unwrap_or(Type::i32()),
node.operands.clone(),
)
};
if node_operands.is_empty() {
return false;
}
let src = node_operands[0];
let (src_opcode, src_operands, src_vt) = {
let sn = dag.get_node(src);
(
sn.opcode,
sn.operands.clone(),
sn.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if src_opcode == SDOpcode::SExt && src_operands.len() >= 1 {
let new_sext = dag.add_node(SDOpcode::SExt, vec![node_dst_ty], vec![src_operands[0]]);
self.replace_node(dag, node_id, new_sext);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::ZExt && src_operands.len() >= 1 {
let inner = src_operands[0];
let inner_ty = inner.get_value_type(dag);
let src_bits = self.get_type_bits(&src_vt);
let inner_bits = self.get_type_bits(&inner_ty);
let dst_bits = self.get_type_bits(&node_dst_ty);
if inner_bits < src_bits && src_bits <= dst_bits {
let new_zext = dag.add_node(SDOpcode::ZExt, vec![node_dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_zext);
self.num_combined += 1;
return true;
}
}
false
}
pub fn simplify_demanded_bits_and_mask(
&mut self,
dag: &mut SelectionDAG,
node_id: usize,
) -> bool {
let (node_ty, node_operands) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And {
return false;
}
(
node.value_types.first().cloned().unwrap_or(Type::i32()),
node.operands.clone(),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(mask) = self.get_constant_value(dag, rhs) {
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
let bits = self.get_type_bits(&node_ty);
let all_ones_mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
if lhs_opcode == SDOpcode::Shl && lhs_operands.len() >= 2 {
if let Some(shift_amt) = self.get_constant_value(dag, lhs_operands[1]) {
let max_shifted = all_ones_mask.wrapping_shl(shift_amt as u32);
if (mask & max_shifted) == max_shifted {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
}
if lhs_opcode == SDOpcode::Srl && lhs_operands.len() >= 2 {
if let Some(shift_amt) = self.get_constant_value(dag, lhs_operands[1]) {
let max_shifted = all_ones_mask >> (shift_amt as u32);
if (mask & max_shifted) == max_shifted {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
}
if lhs_opcode == SDOpcode::Or && lhs_operands.len() >= 2 {
let mask_c = dag.get_constant(mask, node_ty.clone());
let and_x = dag.add_node(
SDOpcode::And,
vec![node_ty.clone()],
vec![lhs_operands[0], mask_c],
);
let mask_c2 = dag.get_constant(mask, node_ty.clone());
let and_y = dag.add_node(
SDOpcode::And,
vec![node_ty.clone()],
vec![lhs_operands[1], mask_c2],
);
let or_val = dag.add_node(SDOpcode::Or, vec![node_ty], vec![and_x, and_y]);
self.replace_node(dag, node_id, or_val);
self.num_combined += 1;
return true;
}
if lhs_opcode == SDOpcode::And && lhs_operands.len() >= 2 {
if let Some(inner_mask) = self.get_constant_value(dag, lhs_operands[1]) {
let combined = (mask & inner_mask) & all_ones_mask;
let new_c = dag.get_constant(combined, node_ty.clone());
let new_and =
dag.add_node(SDOpcode::And, vec![node_ty], vec![lhs_operands[0], new_c]);
self.replace_node(dag, node_id, new_and);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn simplify_demanded_bits_or_zero(
&mut self,
dag: &mut SelectionDAG,
node_id: usize,
) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
if let Some(rhs_val) = self.get_constant_value(dag, operands[1]) {
if rhs_val == 0 {
self.replace_node(dag, node_id, operands[0]);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, operands[0]) {
if lhs_val == 0 {
self.replace_node(dag, node_id, operands[1]);
self.num_combined += 1;
return true;
}
}
false
}
pub fn simplify_demanded_bits_xor_self(
&mut self,
dag: &mut SelectionDAG,
node_id: usize,
) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
if operands[0] == operands[1] {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let zero = dag.get_constant(0, ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
false
}
pub fn fold_constant_arithmetic(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let lhs_val = self.get_constant_value(dag, lhs);
let rhs_val = self.get_constant_value(dag, rhs);
if lhs_val.is_none() || rhs_val.is_none() {
return false;
}
let l = lhs_val.unwrap();
let r = rhs_val.unwrap();
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let bits = self.get_type_bits(&ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let has_nuw = node.flags.has_no_unsigned_wrap;
let has_nsw = node.flags.has_no_signed_wrap;
let has_exact = node.flags.has_exact;
match node.opcode {
SDOpcode::Add => {
let result = l.wrapping_add(r);
if has_nuw && result < l {
return false; }
if has_nsw {
let l_signed = l as i64;
let r_signed = r as i64;
let result_signed = result as i64;
if (l_signed >= 0 && r_signed >= 0 && result_signed < 0)
|| (l_signed < 0 && r_signed < 0 && result_signed >= 0)
{
return false; }
}
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Sub => {
let result = l.wrapping_sub(r);
if has_nuw && l < r {
return false;
}
if has_nsw {
let l_signed = l as i64;
let r_signed = r as i64;
let result_signed = result as i64;
if (l_signed >= 0 && r_signed < 0 && result_signed < 0)
|| (l_signed < 0 && r_signed >= 0 && result_signed >= 0)
{
return false;
}
}
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Mul => {
let result = l.wrapping_mul(r);
if has_nuw || has_nsw {
let wide = (l as u128).wrapping_mul(r as u128);
if has_nuw && wide > u64::MAX as u128 {
return false;
}
if has_nsw {
let l_ext = l as i64 as i128;
let r_ext = r as i64 as i128;
let product = l_ext.wrapping_mul(r_ext);
let truncated = product as i64 as u64;
if truncated != result {
return false;
}
}
}
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::UDiv => {
if r == 0 {
return false;
}
let result = l / r;
if has_exact && l % r != 0 {
return false;
}
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::SDiv => {
if r == 0 {
return false;
}
if l == (1u64 << (bits - 1)) && r == u64::MAX {
return false;
}
let result = ((l as i64).wrapping_div(r as i64)) as u64;
if has_exact && ((l as i64) % (r as i64)) != 0 {
return false;
}
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::URem => {
if r == 0 {
return false;
}
let result = l % r;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::SRem => {
if r == 0 {
return false;
}
let result = ((l as i64).wrapping_rem(r as i64)) as u64;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::And => {
let result = l & r;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Or => {
let result = l | r;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Xor => {
let result = l ^ r;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Shl => {
let shift = r % (bits as u64);
let result = l.wrapping_shl(shift as u32);
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Srl => {
let shift = r % (bits as u64);
let result = l.wrapping_shr(shift as u32);
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
SDOpcode::Sra => {
let shift = r % (bits as u64);
let result = ((l as i64).wrapping_shr(shift as u32)) as u64;
let c = dag.get_constant(result & mask, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
_ => {}
}
false
}
pub fn simplify_select_bitwise(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::Select | SDOpcode::VSelect) {
return false;
}
let operands = node.operands.clone();
if operands.len() < 3 {
return false;
}
let cond = operands[0];
let true_val = operands[1];
let false_val = operands[2];
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let true_node = dag.get_node(true_val);
let false_node = dag.get_node(false_val);
if true_node.opcode == SDOpcode::And
&& false_node.opcode == SDOpcode::And
&& true_node.operands.len() >= 2
&& false_node.operands.len() >= 2
{
if true_node.operands[0] == false_node.operands[0] {
let common = true_node.operands[0];
let sel = dag.add_node(
SDOpcode::Select,
vec![ty.clone()],
vec![cond, true_node.operands[1], false_node.operands[1]],
);
let and_val = dag.add_node(SDOpcode::And, vec![ty], vec![common, sel]);
self.replace_node(dag, node_id, and_val);
self.num_combined += 1;
return true;
}
}
if true_node.opcode == SDOpcode::Or
&& false_node.opcode == SDOpcode::Or
&& true_node.operands.len() >= 2
&& false_node.operands.len() >= 2
{
if true_node.operands[0] == false_node.operands[0] {
let common = true_node.operands[0];
let sel = dag.add_node(
SDOpcode::Select,
vec![ty.clone()],
vec![cond, true_node.operands[1], false_node.operands[1]],
);
let or_val = dag.add_node(SDOpcode::Or, vec![ty], vec![common, sel]);
self.replace_node(dag, node_id, or_val);
self.num_combined += 1;
return true;
}
}
false
}
pub fn merge_consecutive_loads(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Load {
return false;
}
let operands = node.operands.clone();
if operands.len() < 2 {
return false;
}
let chain = operands[0];
let ptr = operands[1];
let load_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let load_bits = self.get_type_bits(&load_ty);
let chain_node = dag.get_node(chain);
if chain_node.opcode == SDOpcode::Load && chain_node.operands.len() >= 2 {
let chain2 = chain_node.operands[0];
let ptr2 = chain_node.operands[1];
let chain_node_id = chain_node.node_id;
let load2_ty = chain_node
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
let load2_bits = self.get_type_bits(&load2_ty);
if load_bits == load2_bits && load_bits < 64 {
let ptr2_node = dag.get_node(ptr2);
let ptr2_opcode = ptr2_node.opcode;
let ptr2_operands = ptr2_node.operands.clone();
drop(ptr2_node);
if ptr2_opcode == SDOpcode::Add && ptr2_operands.len() >= 2 {
let base_ptr = ptr2_operands[0];
if base_ptr == ptr {
if let Some(offset) = self.get_constant_value(dag, ptr2_operands[1]) {
let byte_size = (load_bits / 8) as u64;
if offset == byte_size {
let merged_bits = load_bits * 2;
let merged_ty = Type::int(merged_bits);
let merged_load = dag.add_node(
SDOpcode::Load,
vec![merged_ty.clone(), Type::void()],
vec![chain2, ptr],
);
dag.replace_all_uses_with(
SDValue::new(node_id, 1),
SDValue::new(chain_node_id, 1),
);
self.replace_node(dag, node_id, merged_load);
self.num_combined += 1;
return true;
}
}
}
}
}
}
false
}
pub fn merge_consecutive_stores(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Store {
return false;
}
let operands = node.operands.clone();
if operands.len() < 3 {
return false;
}
let chain = operands[0];
let val = operands[1];
let ptr = operands[2];
let val_ty = val.get_value_type(dag);
let val_bits = self.get_type_bits(&val_ty);
let chain_node = dag.get_node(chain);
if chain_node.opcode == SDOpcode::Store && chain_node.operands.len() >= 3 {
let chain2 = chain_node.operands[0];
let val2 = chain_node.operands[1];
let ptr2 = chain_node.operands[2];
let val2_ty = val2.get_value_type(dag);
let val2_bits = self.get_type_bits(&val2_ty);
if val_bits == val2_bits && val_bits < 64 {
if dag.get_node(ptr).opcode == SDOpcode::Add
&& dag.get_node(ptr).operands.len() >= 2
{
let base = dag.get_node(ptr).operands[0];
if base == ptr2 {
if let Some(offset) =
self.get_constant_value(dag, dag.get_node(ptr).operands[1])
{
let byte_size = (val_bits / 8) as u64;
if offset == byte_size {
let merged_bits = val_bits * 2;
let merged_ty = Type::int(merged_bits);
let zext1 = dag.add_node(
SDOpcode::ZExt,
vec![merged_ty.clone()],
vec![val2],
);
let zext2 = dag.add_node(
SDOpcode::ZExt,
vec![merged_ty.clone()],
vec![val],
);
let shl_c = dag.get_constant(val_bits as u64, merged_ty.clone());
let shl_val = dag.add_node(
SDOpcode::Shl,
vec![merged_ty.clone()],
vec![zext2, shl_c],
);
let merged_val = dag.add_node(
SDOpcode::Or,
vec![merged_ty.clone()],
vec![zext1, shl_val],
);
let merged_store = dag.add_node(
SDOpcode::Store,
vec![Type::void()],
vec![chain2, merged_val, ptr2],
);
self.replace_node(dag, node_id, merged_store);
self.num_combined += 1;
return true;
}
}
}
}
}
}
false
}
pub fn simplify_setcc_advanced(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands, node_vt) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::SetCC {
return false;
}
(
node.opcode,
node.operands.clone(),
node.value_types.first().cloned(),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if lhs == rhs {
let one = dag.get_constant(1, Type::i1());
self.replace_node(dag, node_id, one);
self.num_combined += 1;
return true;
}
if let (Some(l), Some(r)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
let ty = node_vt.unwrap_or(Type::i1());
let result = if l == r { 0 } else { 1 };
let val = dag.get_constant(result, ty);
self.replace_node(dag, node_id, val);
self.num_combined += 1;
return true;
}
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
if lhs_opcode == SDOpcode::And && lhs_operands.len() >= 2 {
if let (Some(l_val), Some(r_const)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
if r_const == 0 {
if l_val == 0 {
let one = dag.get_constant(1, Type::i1());
self.replace_node(dag, node_id, one);
self.num_combined += 1;
return true;
}
if is_power_of_2_u64(l_val) {
let shift = floor_log2_u64(l_val) as u64;
let shift_c = dag.get_constant(shift, Type::i32());
let shr = dag.add_node(
SDOpcode::Srl,
vec![Type::i32()],
vec![lhs_operands[0], shift_c],
);
let one_c = dag.get_constant(1, Type::i32());
let and_check =
dag.add_node(SDOpcode::And, vec![Type::i1()], vec![shr, one_c]);
self.replace_node(dag, node_id, and_check);
self.num_combined += 1;
return true;
}
}
}
}
if lhs_opcode == SDOpcode::Add && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let new_rhs = c2.wrapping_sub(c1);
let ty_node = node_vt.unwrap_or(Type::i32());
let new_c = dag.get_constant(new_rhs, ty_node.clone());
let new_setcc = dag.add_node(
SDOpcode::SetCC,
vec![Type::i1()],
vec![lhs_operands[0], new_c],
);
self.replace_node(dag, node_id, new_setcc);
self.num_combined += 1;
return true;
}
}
false
}
pub fn simplify_unary_op(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
let operands = node.operands.clone();
if operands.is_empty() {
return false;
}
let src = operands[0];
let src_node = dag.get_node(src);
match node.opcode {
SDOpcode::FAbs => {
if src_node.opcode == SDOpcode::FAbs && src_node.operands.len() >= 1 {
self.replace_node(dag, node_id, src);
self.num_combined += 1;
return true;
}
}
SDOpcode::BSwap => {
if src_node.opcode == SDOpcode::BSwap && src_node.operands.len() >= 1 {
self.replace_node(dag, node_id, src_node.operands[0]);
self.num_combined += 1;
return true;
}
if let Some(val) = self.get_constant_value(dag, src) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let bits = self.get_type_bits(&ty);
let swapped = match bits {
16 => ((val & 0xFF) << 8) | ((val >> 8) & 0xFF),
32 => {
((val & 0xFF) << 24)
| ((val & 0xFF00) << 8)
| ((val >> 8) & 0xFF00)
| ((val >> 24) & 0xFF)
}
64 => {
((val & 0xFF) << 56)
| ((val & 0xFF00) << 40)
| ((val & 0xFF_0000) << 24)
| ((val & 0xFF_0000_00) << 8)
| ((val >> 8) & 0xFF_0000_00)
| ((val >> 24) & 0xFF_0000)
| ((val >> 40) & 0xFF00)
| ((val >> 56) & 0xFF)
}
_ => return false,
};
let c = dag.get_constant(swapped, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
SDOpcode::CtLz => {
if let Some(val) = self.get_constant_value(dag, src) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let bits = self.get_type_bits(&ty);
let clz = if val == 0 {
bits as u64
} else {
val.leading_zeros() as u64 - (64 - bits as u32) as u64
};
let c = dag.get_constant(clz, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
SDOpcode::CtTz => {
if let Some(val) = self.get_constant_value(dag, src) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let bits = self.get_type_bits(&ty);
let ctz = if val == 0 {
bits as u64
} else {
val.trailing_zeros() as u64
};
let c = dag.get_constant(ctz, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
SDOpcode::CtPop => {
if let Some(val) = self.get_constant_value(dag, src) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let pop = val.count_ones() as u64;
let c = dag.get_constant(pop, ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
SDOpcode::Rotl | SDOpcode::Rotr => {
if node.operands.len() >= 2 {
if let Some(amt) = self.get_constant_value(dag, node.operands[1]) {
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let bits = self.get_type_bits(&ty) as u64;
if amt % bits == 0 {
self.replace_node(dag, node_id, src);
self.num_combined += 1;
return true;
}
}
}
}
_ => {}
}
false
}
pub fn reassociate_ops(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands, node_vt) = {
let node = &dag.nodes[node_id];
(
node.opcode,
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
match node_opcode {
SDOpcode::Add => {
if lhs_opcode == SDOpcode::Add && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_vt);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1.wrapping_add(c2)) & mask;
let c = dag.get_constant(combined, node_vt.clone());
let new_add =
dag.add_node(SDOpcode::Add, vec![node_vt], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_add);
self.num_combined += 1;
return true;
}
if lhs_operands[0] == rhs || lhs_operands[1] == rhs {
let other = if lhs_operands[0] == rhs {
lhs_operands[1]
} else {
lhs_operands[0]
};
let one = dag.get_constant(1, node_vt.clone());
let shl =
dag.add_node(SDOpcode::Shl, vec![node_vt.clone()], vec![rhs, one]);
let new_add = dag.add_node(SDOpcode::Add, vec![node_vt], vec![shl, other]);
self.replace_node(dag, node_id, new_add);
self.num_combined += 1;
return true;
}
}
}
SDOpcode::Mul => {
if lhs_opcode == SDOpcode::Mul && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_vt);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1.wrapping_mul(c2)) & mask;
let c = dag.get_constant(combined, node_vt.clone());
let new_mul =
dag.add_node(SDOpcode::Mul, vec![node_vt], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_mul);
self.num_combined += 1;
return true;
}
}
}
SDOpcode::And => {
if lhs_opcode == SDOpcode::And && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_vt);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1 & c2) & mask;
let c = dag.get_constant(combined, node_vt.clone());
let new_and =
dag.add_node(SDOpcode::And, vec![node_vt], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_and);
self.num_combined += 1;
return true;
}
}
}
SDOpcode::Or => {
if lhs_opcode == SDOpcode::Or && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_vt);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1 | c2) & mask;
let c = dag.get_constant(combined, node_vt.clone());
let new_or =
dag.add_node(SDOpcode::Or, vec![node_vt], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_or);
self.num_combined += 1;
return true;
}
}
}
SDOpcode::Xor => {
if lhs_opcode == SDOpcode::Xor && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_vt);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1 ^ c2) & mask;
let c = dag.get_constant(combined, node_vt.clone());
let new_xor =
dag.add_node(SDOpcode::Xor, vec![node_vt], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_xor);
self.num_combined += 1;
return true;
}
}
}
_ => {}
}
false
}
pub fn visit_and(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
let all_ones = self.get_all_ones(&node_ty);
if rhs_val == all_ones {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
if rhs_val == 0 {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
let all_ones = self.get_all_ones(&node_ty);
if lhs_val == all_ones {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
if lhs_val == 0 {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
if let Some(mask) = self.get_constant_value(dag, rhs) {
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
if lhs_opcode == SDOpcode::Srl && lhs_operands.len() >= 2 {
if let Some(shift) = self.get_constant_value(dag, lhs_operands[1]) {
let bits = self.get_type_bits(&node_ty);
let max_possible = if shift < bits as u64 {
(1u64 << (bits as u64 - shift)) - 1
} else {
0
};
if mask <= max_possible {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
}
}
false
}
pub fn visit_or(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
let all_ones = self.get_all_ones(&node_ty);
if rhs_val == all_ones {
let all = dag.get_constant(all_ones, node_ty);
self.replace_node(dag, node_id, all);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val == 0 {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
let all_ones = self.get_all_ones(&node_ty);
if lhs_val == all_ones {
let all = dag.get_constant(all_ones, node_ty);
self.replace_node(dag, node_id, all);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_xor(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val == 0 {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
let lhs_node = dag.get_node(lhs);
let lhs_opcode = lhs_node.opcode;
let lhs_operands = lhs_node.operands.clone();
drop(lhs_node);
if lhs_opcode == SDOpcode::Xor && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1 ^ c2) & mask;
let c = dag.get_constant(combined, node_ty.clone());
let new_xor = dag.add_node(SDOpcode::Xor, vec![node_ty], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_xor);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_add(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val == 0 {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
let one = dag.get_constant(1, node_ty.clone());
let shl = dag.add_node(SDOpcode::Shl, vec![node_ty], vec![lhs, one]);
self.replace_node(dag, node_id, shl);
self.num_combined += 1;
return true;
}
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
if lhs_opcode == SDOpcode::Mul && lhs_operands.len() >= 2 {
if lhs_operands[0] == rhs {
if let Some(c) = self.get_constant_value(dag, lhs_operands[1]) {
let new_c = dag.get_constant(c.wrapping_add(1), node_ty.clone());
let mul = dag.add_node(SDOpcode::Mul, vec![node_ty], vec![rhs, new_c]);
self.replace_node(dag, node_id, mul);
self.num_combined += 1;
return true;
}
} else if lhs_operands[1] == rhs {
if let Some(c) = self.get_constant_value(dag, lhs_operands[0]) {
if c < u64::MAX {
let new_c = dag.get_constant(c.wrapping_add(1), node_ty.clone());
let mul = dag.add_node(SDOpcode::Mul, vec![node_ty], vec![rhs, new_c]);
self.replace_node(dag, node_id, mul);
self.num_combined += 1;
return true;
}
}
}
}
false
}
pub fn visit_sub(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Sub {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
if lhs_opcode == SDOpcode::Add && lhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1.wrapping_sub(c2)) & mask;
let c = dag.get_constant(combined, node_ty.clone());
let new_add = dag.add_node(SDOpcode::Add, vec![node_ty], vec![lhs_operands[0], c]);
self.replace_node(dag, node_id, new_add);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_mul(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Mul {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if node_operands.len() < 2 {
return false;
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 1 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
if rhs_val == 0 {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
if rhs_val > 1 && is_power_of_2_u64(rhs_val) {
let shift = floor_log2_u64(rhs_val) as u64;
let shift_c = dag.get_constant(shift, node_ty.clone());
let shl = dag.add_node(SDOpcode::Shl, vec![node_ty], vec![lhs, shift_c]);
self.replace_node(dag, node_id, shl);
self.num_combined += 1;
return true;
}
}
if let Some(lhs_val) = self.get_constant_value(dag, lhs) {
if lhs_val == 1 {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
if lhs_val == 0 {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
if lhs_val > 1 && is_power_of_2_u64(lhs_val) {
let shift = floor_log2_u64(lhs_val) as u64;
let shift_c = dag.get_constant(shift, node_ty.clone());
let shl = dag.add_node(SDOpcode::Shl, vec![node_ty], vec![rhs, shift_c]);
self.replace_node(dag, node_id, shl);
self.num_combined += 1;
return true;
}
}
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
if lhs_opcode == SDOpcode::Shl && lhs_operands.len() >= 2 {
if let (Some(shift1), Some(c2)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs),
) {
if is_power_of_2_u64(c2) {
let total_shift = shift1 + floor_log2_u64(c2) as u64;
let bits = self.get_type_bits(&node_ty) as u64;
if total_shift < bits {
let shift_c = dag.get_constant(total_shift, node_ty.clone());
let new_shl = dag.add_node(
SDOpcode::Shl,
vec![node_ty],
vec![lhs_operands[0], shift_c],
);
self.replace_node(dag, node_id, new_shl);
self.num_combined += 1;
return true;
}
}
}
}
false
}
pub fn combine_trunc_and(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Trunc || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_operands) = {
let sn = dag.get_node(src);
(sn.opcode, sn.operands.clone())
};
if src_opcode == SDOpcode::And && src_operands.len() >= 2 {
if let Some(mask) = self.get_constant_value(dag, src_operands[1]) {
let dst_bits = self.get_type_bits(&node_ty);
let dst_mask = if dst_bits < 64 {
(1u64 << dst_bits) - 1
} else {
u64::MAX
};
if mask <= dst_mask {
let trunc_val = dag.add_node(
SDOpcode::Trunc,
vec![node_ty.clone()],
vec![src_operands[0]],
);
let mask_c = dag.get_constant(mask, node_ty.clone());
let and_val =
dag.add_node(SDOpcode::And, vec![node_ty], vec![trunc_val, mask_c]);
self.replace_node(dag, node_id, and_val);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_select_const(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands) = {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::Select | SDOpcode::VSelect)
|| node.operands.len() < 3
{
return false;
}
(node.opcode, node.operands.clone())
};
let cond = node_operands[0];
let true_val = node_operands[1];
let false_val = node_operands[2];
if let Some(cond_val) = self.get_constant_value(dag, cond) {
if cond_val != 0 {
self.replace_node(dag, node_id, true_val);
} else {
self.replace_node(dag, node_id, false_val);
}
self.num_combined += 1;
return true;
}
if true_val == false_val {
self.replace_node(dag, node_id, true_val);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_setcc_canonicalize(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::SetCC || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i1()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
if lhs == rhs {
let one = dag.get_constant(1, node_ty);
self.replace_node(dag, node_id, one);
self.num_combined += 1;
return true;
}
if let (Some(l), Some(r)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
let result = if l == r { 0 } else { 1 };
let val = dag.get_constant(result, node_ty);
self.replace_node(dag, node_id, val);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_merge_stores(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, _node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Store || node.operands.len() < 3 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::void()),
)
};
let val = node_operands[1];
if dag.get_node(val).opcode == SDOpcode::Undef {
let chain = node_operands[0];
self.replace_node(dag, node_id, chain);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_add_of_neg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if rhs_opcode == SDOpcode::Sub && rhs_operands.len() >= 2 {
if let Some(lhs_c) = self.get_constant_value(dag, rhs_operands[0]) {
if lhs_c == 0 {
let sub_val =
dag.add_node(SDOpcode::Sub, vec![node_ty], vec![lhs, rhs_operands[1]]);
self.replace_node(dag, node_id, sub_val);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_sub_of_neg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Sub || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if rhs_opcode == SDOpcode::Sub && rhs_operands.len() >= 2 {
if let Some(lhs_c) = self.get_constant_value(dag, rhs_operands[0]) {
if lhs_c == 0 {
let add_val =
dag.add_node(SDOpcode::Add, vec![node_ty], vec![lhs, rhs_operands[1]]);
self.replace_node(dag, node_id, add_val);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_shl_to_mul(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Shl || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let val = node_operands[0];
if let Some(shift_amt) = self.get_constant_value(dag, node_operands[1]) {
if shift_amt > 0 && shift_amt <= 3 {
let multiplier = 1u64 << shift_amt;
let mul_c = dag.get_constant(multiplier, node_ty.clone());
let mul = dag.add_node(SDOpcode::Mul, vec![node_ty], vec![val, mul_c]);
self.replace_node(dag, node_id, mul);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_bitcast_chain(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Bitcast || node.operands.is_empty() {
return false;
}
(node.operands.clone(), node.value_types.first().cloned())
};
let src = node_operands[0];
let (src_opcode, src_operands) = {
let sn = dag.get_node(src);
(sn.opcode, sn.operands.clone())
};
if src_opcode == SDOpcode::Bitcast && src_operands.len() >= 1 {
let inner = src_operands[0];
let inner_ty = inner.get_value_type(dag);
if let Some(dst_ty) = node_ty {
if inner_ty == dst_ty {
self.replace_node(dag, node_id, inner);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_freeze_undef(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Freeze || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
if dag.get_node(src).opcode == SDOpcode::Undef {
let undef = dag.get_undef(node_ty);
self.replace_node(dag, node_id, undef);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_freeze_const(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node_operands = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Freeze || node.operands.is_empty() {
return false;
}
node.operands.clone()
};
let src = node_operands[0];
if dag.get_node(src).opcode == SDOpcode::Constant
|| dag.get_node(src).opcode == SDOpcode::ConstantFP
{
self.replace_node(dag, node_id, src);
self.num_combined += 1;
return true;
}
false
}
pub fn combine_shift_pair(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::Sra | SDOpcode::Srl) || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let val = node_operands[0];
if let Some(shift_out) = self.get_constant_value(dag, node_operands[1]) {
let (val_opcode, val_operands) = {
let vn = dag.get_node(val);
(vn.opcode, vn.operands.clone())
};
if val_opcode == SDOpcode::Shl && val_operands.len() >= 2 {
if let Some(shift_in) = self.get_constant_value(dag, val_operands[1]) {
if shift_in == shift_out {
let bits = self.get_type_bits(&node_ty);
if shift_out < bits as u64 {
let trunc_bits = bits - shift_out as u32;
let trunc = dag.add_node(
SDOpcode::Trunc,
vec![Type::int(trunc_bits)],
vec![val_operands[0]],
);
let ext = dag.add_node(
if node_operands.is_empty() {
SDOpcode::ZExt
} else {
SDOpcode::ZExt
},
vec![node_ty],
vec![trunc],
);
self.replace_node(dag, node_id, ext);
self.num_combined += 1;
return true;
}
}
}
}
}
false
}
pub fn combine_and_not(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if rhs_opcode == SDOpcode::Xor && rhs_operands.len() >= 2 {
if let Some(mask) = self.get_constant_value(dag, rhs_operands[1]) {
let all_ones = self.get_all_ones(&node_ty);
if mask == all_ones {
}
}
}
false
}
pub fn combine_or_of_shifts(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (lhs_opcode, lhs_ops) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
let (rhs_opcode, rhs_ops) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if lhs_opcode == SDOpcode::Shl
&& rhs_opcode == SDOpcode::Shl
&& lhs_ops.len() >= 2
&& rhs_ops.len() >= 2
{
if let (Some(lhs_base), Some(rhs_base)) = (
self.get_constant_value(dag, lhs_ops[0]),
self.get_constant_value(dag, rhs_ops[0]),
) {
if lhs_base == 1 && rhs_base == 1 && lhs_ops[1] != rhs_ops[1] {
let combined = (1u64 << lhs_base) | (1u64 << rhs_base);
let c = dag.get_constant(combined, node_ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_srl_of_shl(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Srl || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let val = node_operands[0];
if let Some(outer_shift) = self.get_constant_value(dag, node_operands[1]) {
let (val_opcode, val_operands) = {
let vn = dag.get_node(val);
(vn.opcode, vn.operands.clone())
};
if val_opcode == SDOpcode::Shl && val_operands.len() >= 2 {
if let Some(inner_shift) = self.get_constant_value(dag, val_operands[1]) {
let bits = self.get_type_bits(&node_ty) as u64;
if inner_shift >= outer_shift {
let new_shift = inner_shift - outer_shift;
if new_shift < bits {
let shift_c = dag.get_constant(new_shift, node_ty.clone());
let new_shl = dag.add_node(
SDOpcode::Shl,
vec![node_ty],
vec![val_operands[0], shift_c],
);
self.replace_node(dag, node_id, new_shl);
self.num_combined += 1;
return true;
}
}
}
}
}
false
}
pub fn combine_and_of_setcc(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i1()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (lhs_opcode, lhs_operands) = {
let ln = dag.get_node(lhs);
(ln.opcode, ln.operands.clone())
};
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if lhs_opcode == SDOpcode::SetCC
&& rhs_opcode == SDOpcode::SetCC
&& lhs_operands.len() >= 2
&& rhs_operands.len() >= 2
{
if let (Some(l_c), Some(r_c)) = (
self.get_constant_value(dag, lhs_operands[1]),
self.get_constant_value(dag, rhs_operands[1]),
) {
if l_c == 0 && r_c == 0 {
let or_val = dag.add_node(
SDOpcode::Or,
vec![node_ty.clone()],
vec![lhs_operands[0], rhs_operands[0]],
);
let zero = dag.get_constant(0, node_ty);
let setcc = dag.add_node(SDOpcode::SetCC, vec![Type::i1()], vec![or_val, zero]);
self.replace_node(dag, node_id, setcc);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_nested_xor(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if rhs_opcode == SDOpcode::Xor && rhs_operands.len() >= 2 {
if let (Some(c1), Some(c2)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs_operands[1]),
) {
let bits = self.get_type_bits(&node_ty);
let mask = if bits < 64 {
(1u64 << bits) - 1
} else {
u64::MAX
};
let combined = (c1 ^ c2) & mask;
let c = dag.get_constant(combined, node_ty.clone());
let new_xor = dag.add_node(SDOpcode::Xor, vec![node_ty], vec![rhs_operands[0], c]);
self.replace_node(dag, node_id, new_xor);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_extract_subvec_of_concat(
&mut self,
dag: &mut SelectionDAG,
node_id: usize,
) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ExtractSubvector || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let idx = node_operands[1];
let (src_opcode, src_operands) = {
let sn = dag.get_node(src);
(sn.opcode, sn.operands.clone())
};
if src_opcode == SDOpcode::ConcatVectors && src_operands.len() >= 2 {
if let Some(idx_val) = self.get_constant_value(dag, idx) {
if idx_val == 0 {
self.replace_node(dag, node_id, src_operands[0]);
self.num_combined += 1;
return true;
} else if idx_val == 1 && src_operands.len() >= 3 {
self.replace_node(dag, node_id, src_operands[1]);
self.num_combined += 1;
return true;
}
}
}
false
}
pub fn combine_mul_neg_one(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Mul || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
let all_ones = self.get_all_ones(&node_ty);
if rhs_val == all_ones {
let zero = dag.get_constant(0, node_ty.clone());
let sub_val = dag.add_node(SDOpcode::Sub, vec![node_ty], vec![zero, lhs]);
self.replace_node(dag, node_id, sub_val);
self.num_combined += 1;
return true;
}
}
false
}
pub fn combine_add_shl_twice(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let (rhs_opcode, rhs_operands) = {
let rn = dag.get_node(rhs);
(rn.opcode, rn.operands.clone())
};
if rhs_opcode == SDOpcode::Shl && rhs_operands.len() >= 2 && rhs_operands[0] == lhs {
if let Some(amt) = self.get_constant_value(dag, rhs_operands[1]) {
let multiplier = (1u64 << amt) + 1;
let c = dag.get_constant(multiplier, node_ty.clone());
let mul = dag.add_node(SDOpcode::Mul, vec![node_ty], vec![lhs, c]);
self.replace_node(dag, node_id, mul);
self.num_combined += 1;
return true;
}
}
false
}
}
impl DAGCombiner {
pub fn visit_fadd(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(fp_val) = self.get_fp_value(dag, rhs.node_id) {
if fp_val == 0.0 || fp_val == -0.0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if let Some(fp_val) = self.get_fp_value(dag, lhs.node_id) {
if fp_val == 0.0 || fp_val == -0.0 {
self.replace_node(dag, node_id, rhs);
self.num_combined += 1;
return true;
}
}
if dag.nodes[lhs.node_id].opcode == SDOpcode::FNeg {
let inner = dag.nodes[lhs.node_id].operands[0];
let ty = dag.nodes[node_id].value_types[0].clone();
let fsub_node = dag.get_binary_op(SDOpcode::FSub, ty, rhs, inner);
self.replace_node(dag, node_id, fsub_node);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fsub(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(fp_val) = self.get_fp_value(dag, rhs.node_id) {
if fp_val == 0.0 || fp_val == -0.0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
let ty = dag.nodes[node_id].value_types[0].clone();
let zero = dag.get_constant_fp(0.0f64.to_bits(), ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
if let Some(fp_val) = self.get_fp_value(dag, lhs.node_id) {
if fp_val == 0.0 || fp_val == -0.0 {
let ty = dag.nodes[node_id].value_types[0].clone();
let fneg = dag.add_node(SDOpcode::FNeg, vec![ty], vec![rhs]);
self.replace_node(dag, node_id, fneg);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_fmul(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(fp_val) = self.get_fp_value(dag, rhs.node_id) {
if fp_val == 1.0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
if fp_val == 0.0 || fp_val == -0.0 {
let ty = dag.nodes[node_id].value_types[0].clone();
let zero = dag.get_constant_fp(0.0f64.to_bits(), ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
if fp_val == 2.0 {
let ty = dag.nodes[node_id].value_types[0].clone();
let fadd = dag.get_binary_op(SDOpcode::FAdd, ty, lhs, lhs);
self.replace_node(dag, node_id, fadd);
self.num_combined += 1;
return true;
}
if fp_val == -1.0 {
let ty = dag.nodes[node_id].value_types[0].clone();
let fneg = dag.add_node(SDOpcode::FNeg, vec![ty], vec![lhs]);
self.replace_node(dag, node_id, fneg);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_fdiv(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(fp_val) = self.get_fp_value(dag, rhs.node_id) {
if fp_val == 1.0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
if fp_val == -1.0 {
let ty = dag.nodes[node_id].value_types[0].clone();
let fneg = dag.add_node(SDOpcode::FNeg, vec![ty], vec![lhs]);
self.replace_node(dag, node_id, fneg);
self.num_combined += 1;
return true;
}
}
if let Some(fp_val) = self.get_fp_value(dag, lhs.node_id) {
if fp_val == 0.0 {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if lhs == rhs {
let ty = dag.nodes[node_id].value_types[0].clone();
let one = dag.get_constant_fp(1.0f64.to_bits(), ty);
self.replace_node(dag, node_id, one);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fabs(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let inner_id = node.operands[0].node_id;
let inner_opcode = dag.nodes[inner_id].opcode;
if inner_opcode == SDOpcode::FAbs {
self.replace_node(dag, node_id, node.operands[0]);
self.num_combined += 1;
return true;
}
if inner_opcode == SDOpcode::FNeg {
let x = dag.nodes[inner_id].operands[0];
let ty = dag.nodes[node_id].value_types[0].clone();
let fabs = dag.add_node(SDOpcode::FAbs, vec![ty], vec![x]);
self.replace_node(dag, node_id, fabs);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fneg(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let inner_id = node.operands[0].node_id;
let inner_opcode = dag.nodes[inner_id].opcode;
if inner_opcode == SDOpcode::FNeg {
let x = dag.nodes[inner_id].operands[0];
self.replace_node(dag, node_id, x);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fma(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 3 {
return false;
}
let a = node.operands[0];
let b = node.operands[1];
let c = node.operands[2];
let ty = dag.nodes[node_id].value_types[0].clone();
if let Some(fp_val) = self.get_fp_value(dag, c.node_id) {
if fp_val == 0.0 || fp_val == -0.0 {
let fmul = dag.get_binary_op(SDOpcode::FMul, ty, a, b);
self.replace_node(dag, node_id, fmul);
self.num_combined += 1;
return true;
}
}
if let Some(fp_val) = self.get_fp_value(dag, b.node_id) {
if fp_val == 1.0 {
let fadd = dag.get_binary_op(SDOpcode::FAdd, ty, a, c);
self.replace_node(dag, node_id, fadd);
self.num_combined += 1;
return true;
}
}
if let Some(fp_val) = self.get_fp_value(dag, a.node_id) {
if fp_val == 1.0 {
let fadd = dag.get_binary_op(SDOpcode::FAdd, ty, b, c);
self.replace_node(dag, node_id, fadd);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_int_to_fp(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let src_sd = node.operands[0];
if let Some(int_val) = self.get_constant_value(dag, src_sd) {
let ty = dag.nodes[node_id].value_types[0].clone();
let is_signed = dag.nodes[node_id].opcode == SDOpcode::SIToFP;
let fp_val = if is_signed {
int_val as i64 as f64
} else {
int_val as f64
};
let fp_node = dag.get_constant_fp(fp_val.to_bits(), ty);
self.replace_node(dag, node_id, fp_node);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fp_to_int(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let src_sd = node.operands[0];
if let Some(fp_val) = self.get_fp_constant_value(dag, src_sd) {
let ty = dag.nodes[node_id].value_types[0].clone();
let int_val = fp_val as i64 as u64;
let int_node = dag.get_constant(int_val, ty);
self.replace_node(dag, node_id, int_node);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fp_round_extend(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let inner_id = node.operands[0].node_id;
let my_opcode = dag.nodes[node_id].opcode;
let inner_opcode = dag.nodes[inner_id].opcode;
if my_opcode == SDOpcode::FpRound && inner_opcode == SDOpcode::FpExtend {
let x = dag.nodes[inner_id].operands[0];
self.replace_node(dag, node_id, x);
self.num_combined += 1;
return true;
}
if my_opcode == SDOpcode::FpExtend && inner_opcode == SDOpcode::FpRound {
let x = dag.nodes[inner_id].operands[0];
let x_ty = dag.nodes[x.node_id].value_types[0].clone();
let my_ty = dag.nodes[node_id].value_types[0].clone();
if x_ty == my_ty {
self.replace_node(dag, node_id, x);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_fpowi(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.len() < 2 {
return false;
}
let base = node.operands[0];
let exp_sd = node.operands[1];
let ty = dag.nodes[node_id].value_types[0].clone();
if let Some(exp_val) = self.get_constant_value(dag, exp_sd) {
let exp = exp_val as i32;
match exp {
0 => {
let one = dag.get_constant_fp(1.0f64.to_bits(), ty);
self.replace_node(dag, node_id, one);
self.num_combined += 1;
return true;
}
1 => {
self.replace_node(dag, node_id, base);
self.num_combined += 1;
return true;
}
2 => {
let sq = dag.get_binary_op(SDOpcode::FMul, ty, base, base);
self.replace_node(dag, node_id, sq);
self.num_combined += 1;
return true;
}
3 => {
let sq = dag.get_binary_op(SDOpcode::FMul, ty.clone(), base, base);
let cube = dag.get_binary_op(SDOpcode::FMul, ty, sq, base);
self.replace_node(dag, node_id, cube);
self.num_combined += 1;
return true;
}
_ => {
if exp < 0 {
}
}
}
}
false
}
pub fn visit_vecreduce(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let vec_id = node.operands[0].node_id;
if dag.nodes[vec_id].opcode != SDOpcode::BuildVector {
return false;
}
let build_vec = &dag.nodes[vec_id];
let mut all_const = true;
let mut sum: i64 = 0;
for operand in &build_vec.operands {
if let Some(c) = self.get_constant_value(dag, *operand) {
sum = sum.wrapping_add(c as i64);
} else {
all_const = false;
break;
}
}
if all_const {
let ty = dag.nodes[node_id].value_types[0].clone();
let result = dag.get_constant(sum as u64, ty);
self.replace_node(dag, node_id, result);
self.num_combined += 1;
return true;
}
false
}
fn get_fp_value(&self, dag: &SelectionDAG, node_id: usize) -> Option<f64> {
let val = SDValue::new(node_id, 0);
self.get_fp_constant_value(dag, val)
}
pub fn reassociate_add_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Add || node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::Add && lhs_node.operands.len() >= 2 {
let inner_lhs = lhs_node.operands[0];
let inner_rhs = lhs_node.operands[1];
if let Some(c1) = self.get_constant_value(dag, inner_rhs) {
let sum = c1.wrapping_add(c2);
let new_c = dag.get_constant(sum, ty.clone());
let new_add = dag.get_binary_op(SDOpcode::Add, ty, inner_lhs, new_c);
self.replace_node(dag, node_id, new_add);
return true;
}
}
}
false
}
pub fn reassociate_sub_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Sub || node.operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::Sub && lhs_node.operands.len() >= 2 {
let inner_x = lhs_node.operands[0];
let inner_rhs = lhs_node.operands[1];
if let Some(c1) = self.get_constant_value(dag, inner_rhs) {
let sum = c1.wrapping_add(c2);
let new_c = dag.get_constant(sum, ty.clone());
let new_sub = dag.get_binary_op(SDOpcode::Sub, ty, inner_x, new_c);
self.replace_node(dag, node_id, new_sub);
return true;
}
}
}
false
}
pub fn reassociate_and_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And || node.operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::And && lhs_node.operands.len() >= 2 {
let inner_x = lhs_node.operands[0];
let inner_rhs = lhs_node.operands[1];
if let Some(c1) = self.get_constant_value(dag, inner_rhs) {
let combined = c1 & c2;
let new_c = dag.get_constant(combined, ty.clone());
let new_and = dag.get_binary_op(SDOpcode::And, ty, inner_x, new_c);
self.replace_node(dag, node_id, new_and);
return true;
}
}
}
false
}
pub fn reassociate_or_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or || node.operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::Or && lhs_node.operands.len() >= 2 {
let inner_x = lhs_node.operands[0];
let inner_rhs = lhs_node.operands[1];
if let Some(c1) = self.get_constant_value(dag, inner_rhs) {
let combined = c1 | c2;
let new_c = dag.get_constant(combined, ty.clone());
let new_or = dag.get_binary_op(SDOpcode::Or, ty, inner_x, new_c);
self.replace_node(dag, node_id, new_or);
return true;
}
}
}
false
}
pub fn reassociate_xor_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Xor || node.operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::Xor && lhs_node.operands.len() >= 2 {
let inner_x = lhs_node.operands[0];
let inner_rhs = lhs_node.operands[1];
if let Some(c1) = self.get_constant_value(dag, inner_rhs) {
let combined = c1 ^ c2;
let new_c = dag.get_constant(combined, ty.clone());
let new_xor = dag.get_binary_op(SDOpcode::Xor, ty, inner_x, new_c);
self.replace_node(dag, node_id, new_xor);
return true;
}
}
}
false
}
pub fn reassociate_shift_constants(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node_opcode;
let node_operands;
let ty;
{
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Shl
&& node.opcode != SDOpcode::Srl
&& node.opcode != SDOpcode::Sra
{
return false;
}
if node.operands.len() < 2 {
return false;
}
ty = node.value_types.first().cloned().unwrap_or(Type::i32());
node_opcode = node.opcode;
node_operands = node.operands.clone();
}
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(c2) = self.get_constant_value(dag, rhs) {
let lhs_node_id = lhs.node_id;
let (lhs_opcode, lhs_ops): (SDOpcode, Vec<SDValue>);
{
let lhs_node = &dag.nodes[lhs_node_id];
lhs_opcode = lhs_node.opcode;
lhs_ops = lhs_node.operands.clone();
}
if lhs_opcode == node_opcode && lhs_ops.len() >= 2 {
let inner_x = lhs_ops[0];
let inner_count = lhs_ops[1];
if let Some(c1) = self.get_constant_value(dag, inner_count) {
let total = c1 + c2;
let total_c = dag.get_constant(total, ty.clone());
let new_shift = dag.get_binary_op(node_opcode, ty, inner_x, total_c);
self.replace_node(dag, node_id, new_shift);
return true;
}
}
}
false
}
pub fn fold_cmp_and_one(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::And || node.operands.len() < 2 {
return false;
}
let ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let lhs = node.operands[0];
let rhs = node.operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 1 {
let lhs_node = dag.get_node(lhs);
if lhs_node.opcode == SDOpcode::SetCC && !lhs_node.operands.is_empty() {
let zext = dag.add_node(SDOpcode::ZExt, vec![ty], vec![lhs]);
self.replace_node(dag, node_id, zext);
return true;
}
}
}
false
}
pub fn fold_or_shl_one(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Or || node.operands.len() < 2 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let lhs_node = dag.get_node(lhs);
let rhs_node = dag.get_node(rhs);
if lhs_node.opcode == SDOpcode::Shl
&& rhs_node.opcode == SDOpcode::Shl
&& lhs_node.operands.len() >= 2
&& rhs_node.operands.len() >= 2
{
if let (Some(lc), Some(rc)) = (
self.get_constant_value(dag, lhs_node.operands[0]),
self.get_constant_value(dag, rhs_node.operands[0]),
) {
if lc == 1 && rc == 1 {
}
}
}
false
}
pub fn visit_load(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Load || node.operands.len() < 2 {
return false;
}
let chain = node.operands[0];
let ptr = node.operands[1];
if let Some(addr_val) = self.get_constant_value(dag, ptr) {
if addr_val == 0 {
return false; }
}
if let Some(store_val) = self.find_forwarding_store(dag, chain, ptr) {
let load_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let store_node = dag.get_node(store_val);
let store_ty = store_node
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
if load_ty == store_ty {
self.replace_node(dag, node_id, store_val);
return true;
}
}
false
}
fn find_forwarding_store(
&self,
dag: &SelectionDAG,
chain: SDValue,
ptr: SDValue,
) -> Option<SDValue> {
let mut current = chain;
for _ in 0..8 {
if current.node_id >= dag.nodes.len() {
break;
}
let cn = &dag.nodes[current.node_id];
if cn.opcode == SDOpcode::Store && cn.operands.len() >= 3 && cn.operands[2] == ptr {
return Some(cn.operands[1]);
}
if cn.operands.is_empty() {
break;
}
current = cn.operands[0];
}
None
}
pub fn visit_store(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Store || node.operands.len() < 3 {
return false;
}
let chain = node.operands[0];
let stored_val = node.operands[1];
let ptr = node.operands[2];
if dag.nodes[stored_val.node_id].opcode == SDOpcode::Undef {
dag.replace_all_uses_with(SDValue::new(node_id, 0), chain);
self.num_combined += 1;
return true;
}
if let Some(prev_val) = self.find_previous_store_value(dag, chain, ptr) {
if prev_val == stored_val {
dag.replace_all_uses_with(SDValue::new(node_id, 0), chain);
self.num_combined += 1;
return true;
}
}
false
}
fn find_previous_store_value(
&self,
dag: &SelectionDAG,
chain: SDValue,
ptr: SDValue,
) -> Option<SDValue> {
let mut current = chain;
for _ in 0..6 {
if current.node_id >= dag.nodes.len() {
break;
}
let cn = &dag.nodes[current.node_id];
if cn.opcode == SDOpcode::Store && cn.operands.len() >= 3 && cn.operands[2] == ptr {
return Some(cn.operands[1]);
}
if cn.operands.is_empty() {
break;
}
current = cn.operands[0];
}
None
}
pub fn visit_atomic(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
match node.opcode {
SDOpcode::AtomicRMW => {
let ops = node.operands.clone();
if ops.len() >= 3 {
if let Some(_val) = self.get_constant_value(dag, ops[2]) {
}
}
false
}
SDOpcode::AtomicCmpSwap => {
let ops = node.operands.clone();
if ops.len() >= 4 && ops[2] == ops[3] {
}
false
}
_ => false,
}
}
pub fn visit_build_vector(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::BuildVector {
return false;
}
let ops = node.operands.clone();
if ops.is_empty() {
return false;
}
let all_zero = ops
.iter()
.all(|&op| self.get_constant_value(dag, op).map_or(false, |v| v == 0));
if all_zero {
let vec_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let zero = dag.get_constant(0, vec_ty);
self.replace_node(dag, node_id, zero);
return true;
}
let first = ops[0];
if ops.iter().all(|&op| op == first) {
}
let mut src_vec: Option<SDValue> = None;
let mut indices: Vec<usize> = Vec::new();
let all_extracts = ops.iter().all(|&op| {
let on = &dag.nodes[op.node_id];
if on.opcode == SDOpcode::ExtractElement && on.operands.len() >= 2 {
let v = on.operands[0];
if let Some(prev) = src_vec {
if v != prev {
return false;
}
} else {
src_vec = Some(v);
}
if let Some(idx) = self.get_constant_value(dag, on.operands[1]) {
indices.push(idx as usize);
true
} else {
false
}
} else {
false
}
});
if all_extracts && indices.iter().enumerate().all(|(i, &idx)| i == idx) {
if let Some(src) = src_vec {
self.replace_node(dag, node_id, src);
return true;
}
}
false
}
pub fn visit_vector_shuffle(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::VectorShuffle || node.operands.len() < 3 {
return false;
}
let lhs = node.operands[0];
let rhs = node.operands[1];
let mask = node.operands[2];
if lhs == rhs {
if let Some(mask_c) = self.get_constant_value(dag, mask) {
let num = self.get_vector_num_elements(dag, lhs);
let mut identity = true;
for i in 0..num.min(16) {
if ((mask_c >> (i * 4)) & 0xF) as usize != i {
identity = false;
break;
}
}
if identity {
self.replace_node(dag, node_id, lhs);
return true;
}
}
}
let lhs_node = &dag.nodes[lhs.node_id];
if lhs_node.opcode == SDOpcode::VectorShuffle && lhs_node.operands.len() >= 3 {
}
false
}
pub fn visit_concat_vectors(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ConcatVectors {
return false;
}
let ops = node.operands.clone();
if ops.len() == 2 {
let lo = ops[0];
let hi = ops[1];
let lo_n = &dag.nodes[lo.node_id];
let hi_n = &dag.nodes[hi.node_id];
if lo_n.opcode == SDOpcode::ExtractSubvector
&& hi_n.opcode == SDOpcode::ExtractSubvector
&& lo_n.operands.len() >= 2
&& hi_n.operands.len() >= 2
{
if lo_n.operands[0] == hi_n.operands[0] {
let lo_idx = self
.get_constant_value(dag, lo_n.operands[1])
.unwrap_or(999);
let hi_idx = self
.get_constant_value(dag, hi_n.operands[1])
.unwrap_or(999);
let lo_len = self.get_vector_num_elements(dag, lo);
if lo_idx == 0 && hi_idx == lo_len as u64 {
self.replace_node(dag, node_id, lo_n.operands[0]);
return true;
}
}
}
}
false
}
pub fn visit_extract_vector_elt(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ExtractElement || node.operands.len() < 2 {
return false;
}
let vec = node.operands[0];
let idx = node.operands[1];
let vec_n = &dag.nodes[vec.node_id];
if vec_n.opcode == SDOpcode::BuildVector {
if let Some(i) = self.get_constant_value(dag, idx) {
let i = i as usize;
if i < vec_n.operands.len() {
self.replace_node(dag, node_id, vec_n.operands[i]);
return true;
}
}
}
if vec_n.opcode == SDOpcode::InsertElement && vec_n.operands.len() >= 3 {
let insert_val = vec_n.operands[1];
let insert_idx = vec_n.operands[2];
if let Some(oi) = self.get_constant_value(dag, idx) {
if let Some(ti) = self.get_constant_value(dag, insert_idx) {
if oi == ti {
self.replace_node(dag, node_id, insert_val);
return true;
} else {
let new_ex = dag.add_node(
SDOpcode::ExtractElement,
node.value_types.clone(),
vec![vec_n.operands[0], idx],
);
self.replace_node(dag, node_id, new_ex);
return true;
}
}
}
}
false
}
pub fn visit_insert_vector_elt(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::InsertElement || node.operands.len() < 3 {
return false;
}
let vec = node.operands[0];
let val = node.operands[1];
let idx = node.operands[2];
let vec_n = &dag.nodes[vec.node_id];
if vec_n.opcode == SDOpcode::BuildVector {
if let Some(i) = self.get_constant_value(dag, idx) {
let i = i as usize;
if i < vec_n.operands.len() {
let mut new_elts = vec_n.operands.clone();
new_elts[i] = val;
let new_bv =
dag.add_node(SDOpcode::BuildVector, node.value_types.clone(), new_elts);
self.replace_node(dag, node_id, new_bv);
return true;
}
}
}
false
}
pub fn visit_scalar_to_vector(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ScalarToVector {
return false;
}
if !node.operands.is_empty() {
let scalar = node.operands[0];
if dag.nodes[scalar.node_id].opcode == SDOpcode::Undef {
let vt = node.value_types.first().cloned().unwrap_or(Type::i32());
let undef_vec = dag.get_undef(vt);
self.replace_node(dag, node_id, undef_vec);
return true;
}
}
false
}
pub fn visit_extend(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
let opcode = node.opcode;
if opcode != SDOpcode::ZExt && opcode != SDOpcode::SExt && opcode != SDOpcode::AnyExt {
return false;
}
if node.operands.is_empty() {
return false;
}
let src = node.operands[0];
let src_n = &dag.nodes[src.node_id];
if src_n.opcode == opcode && !src_n.operands.is_empty() {
let inner = src_n.operands[0];
let dest_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let inner_ty = dag.nodes[inner.node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
if self.get_type_bits(&inner_ty) < self.get_type_bits(&dest_ty) {
let new_ext = dag.add_node(opcode, node.value_types.clone(), vec![inner]);
self.replace_node(dag, node_id, new_ext);
return true;
}
}
if opcode == SDOpcode::SExt && src_n.opcode == SDOpcode::Trunc && !src_n.operands.is_empty()
{
let trunc_src = src_n.operands[0];
let dest_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let trunc_ty = dag.nodes[trunc_src.node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
if trunc_ty == dest_ty {
self.replace_node(dag, node_id, trunc_src);
return true;
}
}
if src_n.opcode == SDOpcode::Load && src_n.operands.len() >= 2 {
let chain = src_n.operands[0];
let ptr = src_n.operands[1];
let dest_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
let ext_load_op = match opcode {
SDOpcode::SExt => SDOpcode::SExtLoad,
SDOpcode::ZExt => SDOpcode::ZExtLoad,
_ => return false,
};
let ext_load = dag.add_node(ext_load_op, vec![dest_ty], vec![chain, ptr]);
self.replace_node(dag, node_id, ext_load);
return true;
}
false
}
fn get_vector_num_elements(&self, dag: &SelectionDAG, val: SDValue) -> usize {
let node = dag.get_node(val);
if let Some(ty) = node.value_types.first() {
match &ty.kind {
TypeKind::FixedVector { len, .. } => *len as usize,
_ => 4,
}
} else {
4
}
}
pub fn visit_truncate(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, dst_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Trunc || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops, src_ty) = {
let src_node = dag.get_node(src);
(
src_node.opcode,
src_node.operands.clone(),
src_node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if (src_opcode == SDOpcode::And
|| src_opcode == SDOpcode::Or
|| src_opcode == SDOpcode::Xor
|| src_opcode == SDOpcode::Add
|| src_opcode == SDOpcode::Sub)
&& src_ops.len() >= 2
{
let trunc_lhs = dag.add_node(SDOpcode::Trunc, vec![dst_ty.clone()], vec![src_ops[0]]);
let trunc_rhs = dag.add_node(SDOpcode::Trunc, vec![dst_ty.clone()], vec![src_ops[1]]);
let new_binop = dag.get_binary_op(src_opcode, dst_ty, trunc_lhs, trunc_rhs);
self.replace_node(dag, node_id, new_binop);
self.num_combined += 1;
return true;
}
if (src_opcode == SDOpcode::Shl
|| src_opcode == SDOpcode::Srl
|| src_opcode == SDOpcode::Sra)
&& src_ops.len() >= 2
{
if let Some(shift_amt) = self.get_constant_value(dag, src_ops[1]) {
let dst_bits = self.get_type_bits(&dst_ty) as u64;
let src_bits = self.get_type_bits(&src_ty) as u64;
if shift_amt < dst_bits {
let trunc_x =
dag.add_node(SDOpcode::Trunc, vec![dst_ty.clone()], vec![src_ops[0]]);
let trunc_shift =
dag.add_node(SDOpcode::Trunc, vec![dst_ty.clone()], vec![src_ops[1]]);
let new_shift = dag.get_binary_op(src_opcode, dst_ty, trunc_x, trunc_shift);
self.replace_node(dag, node_id, new_shift);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::Srl || src_opcode == SDOpcode::Shl {
if shift_amt >= src_bits {
let zero = dag.get_constant(0, dst_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
}
}
}
if src_opcode == SDOpcode::ExtractElement && src_ops.len() >= 2 {
let (vec_opcode, vec_operands) = {
let vec_node = dag.get_node(src_ops[0]);
(vec_node.opcode, vec_node.operands.clone())
};
if vec_opcode == SDOpcode::BuildVector {
if let Some(idx_val) = self.get_constant_value(dag, src_ops[1]) {
let idx = idx_val as usize;
if idx < vec_operands.len() {
let elem = vec_operands[idx];
let trunc = dag.add_node(SDOpcode::Trunc, vec![dst_ty], vec![elem]);
self.replace_node(dag, node_id, trunc);
self.num_combined += 1;
return true;
}
}
}
}
false
}
pub fn visit_zero_extend(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, dst_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::ZExt || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops) = {
let src_node = dag.get_node(src);
(src_node.opcode, src_node.operands.clone())
};
if src_opcode == SDOpcode::Trunc && !src_ops.is_empty() {
let inner = src_ops[0];
let inner_ty = dag.nodes[inner.node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
let inner_bits = self.get_type_bits(&inner_ty);
let dst_bits = self.get_type_bits(&dst_ty);
if inner_bits == dst_bits {
self.replace_node(dag, node_id, inner);
self.num_combined += 1;
return true;
} else if inner_bits < dst_bits {
let new_zext = dag.add_node(SDOpcode::ZExt, vec![dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_zext);
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::Load && src_ops.len() >= 2 {
let chain = src_ops[0];
let ptr = src_ops[1];
let zext_load = dag.add_node(SDOpcode::ZExtLoad, vec![dst_ty], vec![chain, ptr]);
self.replace_node(dag, node_id, zext_load);
self.num_combined += 1;
return true;
}
if (src_opcode == SDOpcode::And
|| src_opcode == SDOpcode::Or
|| src_opcode == SDOpcode::Xor)
&& src_ops.len() >= 2
{
let lhs = src_ops[0];
let rhs = src_ops[1];
let zext_lhs = dag.add_node(SDOpcode::ZExt, vec![dst_ty.clone()], vec![lhs]);
let zext_rhs = dag.add_node(SDOpcode::ZExt, vec![dst_ty.clone()], vec![rhs]);
let new_binop = dag.get_binary_op(src_opcode, dst_ty, zext_lhs, zext_rhs);
self.replace_node(dag, node_id, new_binop);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_sign_extend(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, dst_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::SExt || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops, src_ty) = {
let src_node = dag.get_node(src);
(
src_node.opcode,
src_node.operands.clone(),
src_node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
if src_opcode == SDOpcode::Trunc && !src_ops.is_empty() {
let inner = src_ops[0];
let inner_ty = dag.nodes[inner.node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
let inner_bits = self.get_type_bits(&inner_ty);
let dst_bits = self.get_type_bits(&dst_ty);
let src_bits = self.get_type_bits(&src_ty);
if inner_bits == dst_bits && src_bits < inner_bits {
let new_sext = dag.add_node(SDOpcode::SExt, vec![dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_sext);
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::Load && src_ops.len() >= 2 {
let chain = src_ops[0];
let ptr = src_ops[1];
let sext_load = dag.add_node(SDOpcode::SExtLoad, vec![dst_ty], vec![chain, ptr]);
self.replace_node(dag, node_id, sext_load);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::AssertSExt && !src_ops.is_empty() {
let inner = src_ops[0];
let new_sext = dag.add_node(SDOpcode::SExt, vec![dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_sext);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_any_extend(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, dst_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::AnyExt || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops) = {
let src_node = dag.get_node(src);
(src_node.opcode, src_node.operands.clone())
};
if src_opcode == SDOpcode::Trunc && !src_ops.is_empty() {
let inner = src_ops[0];
let inner_ty = dag.nodes[inner.node_id]
.value_types
.first()
.cloned()
.unwrap_or(Type::i32());
if inner_ty == dst_ty {
self.replace_node(dag, node_id, inner);
self.num_combined += 1;
return true;
}
}
if src_opcode == SDOpcode::Load && src_ops.len() >= 2 {
let chain = src_ops[0];
let ptr = src_ops[1];
let wide_load = dag.add_node_with_values(
SDOpcode::Load,
vec![dst_ty, Type::token()],
vec![chain, ptr],
2,
);
self.replace_node(dag, node_id, wide_load);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::AnyExt && !src_ops.is_empty() {
let inner = src_ops[0];
let new_anyext = dag.add_node(SDOpcode::AnyExt, vec![dst_ty], vec![inner]);
self.replace_node(dag, node_id, new_anyext);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_freeze_combine(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::Freeze || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops) = {
let src_node = dag.get_node(src);
(src_node.opcode, src_node.operands.clone())
};
if src_opcode == SDOpcode::Freeze && !src_ops.is_empty() {
let inner = src_ops[0];
let new_freeze = dag.add_node(SDOpcode::Freeze, vec![node_ty], vec![inner]);
self.replace_node(dag, node_id, new_freeze);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::Undef {
let zero = dag.get_constant(0, node_ty);
self.replace_node(dag, node_id, zero);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fcopysign(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::FCopySign || node.operands.len() < 2 {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::float()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
if lhs == rhs {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
{
let (lhs_opcode, lhs_ops) = {
let lhs_node = dag.get_node(lhs);
(lhs_node.opcode, lhs_node.operands.clone())
};
if lhs_opcode == SDOpcode::FCopySign && lhs_ops.len() >= 2 {
let inner_x = lhs_ops[0];
let new_copysign =
dag.add_node(SDOpcode::FCopySign, vec![node_ty], vec![inner_x, rhs]);
self.replace_node(dag, node_id, new_copysign);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_fcanonicalize(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::FCanonicalize || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::float()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops) = {
let src_node = dag.get_node(src);
(src_node.opcode, src_node.operands.clone())
};
if src_opcode == SDOpcode::ConstantFP {
self.replace_node(dag, node_id, src);
self.num_combined += 1;
return true;
}
if src_opcode == SDOpcode::FCanonicalize && !src_ops.is_empty() {
let inner = src_ops[0];
let new_canon = dag.add_node(SDOpcode::FCanonicalize, vec![node_ty], vec![inner]);
self.replace_node(dag, node_id, new_canon);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_saturating_arith(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if !matches!(
node.opcode,
SDOpcode::UAddSat | SDOpcode::USubSat | SDOpcode::SAddSat | SDOpcode::SSubSat
) || node.operands.len() < 2
{
return false;
}
(
node.opcode,
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
if rhs_val == 0 {
match node_opcode {
SDOpcode::UAddSat
| SDOpcode::USubSat
| SDOpcode::SAddSat
| SDOpcode::SSubSat => {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
_ => {}
}
}
}
if let (Some(lhs_val), Some(rhs_val)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
let bits = self.get_type_bits(&node_ty);
let result: Option<u64> = match node_opcode {
SDOpcode::UAddSat => {
let sum = lhs_val.wrapping_add(rhs_val);
if sum < lhs_val || sum < rhs_val {
Some((1u64 << bits) - 1)
} else {
Some(sum)
}
}
SDOpcode::USubSat => {
if rhs_val > lhs_val {
Some(0)
} else {
Some(lhs_val - rhs_val)
}
}
SDOpcode::SAddSat => {
let max = (1u64 << (bits - 1)) - 1;
let min = 1u64 << (bits - 1);
let lhs_signed = lhs_val as i64;
let rhs_signed = rhs_val as i64;
let sum = lhs_signed.wrapping_add(rhs_signed);
if sum > max as i64 {
Some(max)
} else if sum < -(min as i64) {
Some(min)
} else {
Some(sum as u64)
}
}
SDOpcode::SSubSat => {
let max = (1u64 << (bits - 1)) - 1;
let min = 1u64 << (bits - 1);
let lhs_signed = lhs_val as i64;
let rhs_signed = rhs_val as i64;
let diff = lhs_signed.wrapping_sub(rhs_signed);
if diff > max as i64 {
Some(max)
} else if diff < -(min as i64) {
Some(min)
} else {
Some(diff as u64)
}
}
_ => None,
};
if let Some(val) = result {
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let c = dag.get_constant(val & mask, node_ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_overflow_arith(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if !matches!(
node.opcode,
SDOpcode::UAddO
| SDOpcode::USubO
| SDOpcode::SAddO
| SDOpcode::SSubO
| SDOpcode::SMulo
| SDOpcode::UMulo
) || node.operands.len() < 2
{
return false;
}
(
node.opcode,
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let lhs = node_operands[0];
let rhs = node_operands[1];
let bits = self.get_type_bits(&node_ty);
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
if let Some(rhs_val) = self.get_constant_value(dag, rhs) {
let is_identity = match node_opcode {
SDOpcode::UAddO | SDOpcode::USubO | SDOpcode::SAddO | SDOpcode::SSubO => {
rhs_val == 0
}
SDOpcode::SMulo | SDOpcode::UMulo => rhs_val == 1,
_ => false,
};
if is_identity {
self.replace_node(dag, node_id, lhs);
self.num_combined += 1;
return true;
}
}
if let (Some(lhs_val), Some(rhs_val)) = (
self.get_constant_value(dag, lhs),
self.get_constant_value(dag, rhs),
) {
let result = match node_opcode {
SDOpcode::UAddO => {
let sum = (lhs_val & mask).wrapping_add(rhs_val & mask);
let overflow = sum < (lhs_val & mask);
Some((sum & mask, overflow))
}
SDOpcode::USubO => {
let overflow = (rhs_val & mask) > (lhs_val & mask);
Some((
(lhs_val & mask).wrapping_sub(rhs_val & mask) & mask,
overflow,
))
}
SDOpcode::SAddO => {
let l = lhs_val as i64;
let r = rhs_val as i64;
let s = l.wrapping_add(r);
let overflow = (l >= 0 && r >= 0 && s < 0) || (l < 0 && r < 0 && s >= 0);
Some((s as u64 & mask, overflow))
}
SDOpcode::SSubO => {
let l = lhs_val as i64;
let r = rhs_val as i64;
let d = l.wrapping_sub(r);
let overflow = (l >= 0 && r < 0 && d < 0) || (l < 0 && r >= 0 && d >= 0);
Some((d as u64 & mask, overflow))
}
SDOpcode::SMulo => {
let l = (lhs_val & mask) as i64;
let r = (rhs_val & mask) as i64;
if let Some(m) = l.checked_mul(r) {
Some((m as u64 & mask, false))
} else {
Some((0, true))
}
}
SDOpcode::UMulo => {
let l = lhs_val & mask;
let r = rhs_val & mask;
if let Some(m) = l.checked_mul(r) {
Some((m & mask, false))
} else {
Some((0, true))
}
}
_ => None,
};
if let Some((val, _overflow)) = result {
let c = dag.get_constant(val, node_ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
}
false
}
pub fn visit_vecreduce_extended(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let node = &dag.nodes[node_id];
if node.operands.is_empty() {
return false;
}
let vec_id = node.operands[0].node_id;
let vec_node = &dag.nodes[vec_id];
let result_ty = node.value_types.first().cloned().unwrap_or(Type::i32());
if vec_node.opcode == SDOpcode::BuildVector && vec_node.operands.len() == 1 {
self.replace_node(dag, node_id, vec_node.operands[0]);
self.num_combined += 1;
return true;
}
if vec_node.opcode == SDOpcode::BuildVector && !vec_node.operands.is_empty() {
let mut all_const = true;
let mut values: Vec<u64> = Vec::new();
for op in &vec_node.operands {
if let Some(c) = self.get_constant_value(dag, *op) {
values.push(c);
} else {
all_const = false;
break;
}
}
if all_const && !values.is_empty() {
let sum: u64 = values.iter().fold(0u64, |a, &b| a.wrapping_add(b));
let bits = self.get_type_bits(&result_ty);
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let result = dag.get_constant(sum & mask, result_ty);
self.replace_node(dag, node_id, result);
self.num_combined += 1;
return true;
}
}
if vec_node.opcode == SDOpcode::ScalarToVector && !vec_node.operands.is_empty() {
self.replace_node(dag, node_id, vec_node.operands[0]);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_fp_to_int_sat(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_opcode, node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if !matches!(node.opcode, SDOpcode::FpToSIntSat | SDOpcode::FpToUIntSat)
|| node.operands.is_empty()
{
return false;
}
(
node.opcode,
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
if let Some(fp_val) = self.get_fp_value(dag, src.node_id) {
let bits = self.get_type_bits(&node_ty);
let result: u64 = match node_opcode {
SDOpcode::FpToSIntSat => {
let min = -((1i64 << (bits - 1)) as i64);
let max = (1i64 << (bits - 1)) - 1;
if fp_val.is_nan() {
0
} else if fp_val >= max as f64 {
max as u64
} else if fp_val <= min as f64 {
min as u64
} else {
fp_val as i64 as u64
}
}
SDOpcode::FpToUIntSat => {
let max = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
if fp_val.is_nan() || fp_val <= 0.0 {
0
} else if fp_val >= max as f64 {
max
} else {
fp_val as u64
}
}
_ => return false,
};
let c = dag.get_constant(result, node_ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
false
}
pub fn visit_bitreverse(&mut self, dag: &mut SelectionDAG, node_id: usize) -> bool {
let (node_operands, node_ty) = {
let node = &dag.nodes[node_id];
if node.opcode != SDOpcode::BitReverse || node.operands.is_empty() {
return false;
}
(
node.operands.clone(),
node.value_types.first().cloned().unwrap_or(Type::i32()),
)
};
let src = node_operands[0];
let (src_opcode, src_ops) = {
let src_node = dag.get_node(src);
(src_node.opcode, src_node.operands.clone())
};
if src_opcode == SDOpcode::BitReverse && !src_ops.is_empty() {
let inner = src_ops[0];
self.replace_node(dag, node_id, inner);
self.num_combined += 1;
return true;
}
if let Some(val) = self.get_constant_value(dag, src) {
let bits = self.get_type_bits(&node_ty);
let reversed = val.reverse_bits();
let shift = 64 - bits as u32;
let adjusted = reversed >> shift;
let c = dag.get_constant(adjusted, node_ty);
self.replace_node(dag, node_id, c);
self.num_combined += 1;
return true;
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_test_dag() -> SelectionDAG {
SelectionDAG::new()
}
#[test]
fn test_constant_fold_add() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(10, i32.clone());
let c2 = dag.get_constant(20, i32.clone());
let add_node = dag.get_binary_op(SDOpcode::Add, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, add_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_sub() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(50, i32.clone());
let c2 = dag.get_constant(20, i32.clone());
let sub_node = dag.get_binary_op(SDOpcode::Sub, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sub_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_mul() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(6, i32.clone());
let c2 = dag.get_constant(7, i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_and() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(0xFF, i32.clone());
let c2 = dag.get_constant(0x0F, i32.clone());
let and_node = dag.get_binary_op(SDOpcode::And, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, and_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_or() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(0xF0, i32.clone());
let c2 = dag.get_constant(0x0F, i32.clone());
let or_node = dag.get_binary_op(SDOpcode::Or, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, or_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_xor() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(0xFF, i32.clone());
let c2 = dag.get_constant(0x0F, i32.clone());
let xor_node = dag.get_binary_op(SDOpcode::Xor, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, xor_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_shl() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(1, i32.clone());
let c2 = dag.get_constant(3, i32.clone());
let shl_node = dag.get_binary_op(SDOpcode::Shl, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, shl_node.node_id);
assert!(changed);
}
#[test]
fn test_constant_fold_srl() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c1 = dag.get_constant(16, i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let srl_node = dag.get_binary_op(SDOpcode::Srl, i32.clone(), c1, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, srl_node.node_id);
assert!(changed);
}
#[test]
fn test_add_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let add_node = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, add_node.node_id);
assert!(changed);
}
#[test]
fn test_mul_one() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c1 = dag.get_constant(1, i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), x, c1);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_mul_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_and_all_ones() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let all_ones = dag.get_constant(0xFFFFFFFF, i32.clone());
let and_node = dag.get_binary_op(SDOpcode::And, i32.clone(), x, all_ones);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, and_node.node_id);
assert!(changed);
}
#[test]
fn test_and_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let and_node = dag.get_binary_op(SDOpcode::And, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, and_node.node_id);
assert!(changed);
}
#[test]
fn test_or_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let or_node = dag.get_binary_op(SDOpcode::Or, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, or_node.node_id);
assert!(changed);
}
#[test]
fn test_or_all_ones() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let all_ones = dag.get_constant(0xFFFFFFFF, i32.clone());
let or_node = dag.get_binary_op(SDOpcode::Or, i32.clone(), x, all_ones);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, or_node.node_id);
assert!(changed);
}
#[test]
fn test_xor_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let xor_node = dag.get_binary_op(SDOpcode::Xor, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, xor_node.node_id);
assert!(changed);
}
#[test]
fn test_xor_same() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let xor_node = dag.get_binary_op(SDOpcode::Xor, i32.clone(), x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, xor_node.node_id);
assert!(changed);
}
#[test]
fn test_sub_same() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let sub_node = dag.get_binary_op(SDOpcode::Sub, i32.clone(), x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sub_node.node_id);
assert!(changed);
}
#[test]
fn test_and_same() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let and_node = dag.get_binary_op(SDOpcode::And, i32.clone(), x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, and_node.node_id);
assert!(changed);
}
#[test]
fn test_shl_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let shl_node = dag.get_binary_op(SDOpcode::Shl, i32.clone(), x, c0);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, shl_node.node_id);
assert!(changed);
}
#[test]
fn test_mul_by_2_to_shl() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), x, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_mul_by_8_to_shl() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c8 = dag.get_constant(8, i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), x, c8);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_udiv_by_power_of_2() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c4 = dag.get_constant(4, i32.clone());
let div_node = dag.get_binary_op(SDOpcode::UDiv, i32.clone(), x, c4);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, div_node.node_id);
assert!(changed);
}
#[test]
fn test_urem_by_power_of_2() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c8 = dag.get_constant(8, i32.clone());
let rem_node = dag.get_binary_op(SDOpcode::URem, i32.clone(), x, c8);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, rem_node.node_id);
assert!(changed);
}
#[test]
fn test_add_same_as_shl() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let add_node = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, add_node.node_id);
assert!(changed);
}
#[test]
fn test_fmul_by_1() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let one = dag.get_constant_fp(1.0f64.to_bits(), f32.clone());
let mul_node = dag.get_binary_op(SDOpcode::FMul, f32.clone(), x, one);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_fadd_zero() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let zero = dag.get_constant_fp(0.0f64.to_bits(), f32.clone());
let add_node = dag.get_binary_op(SDOpcode::FAdd, f32.clone(), x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, add_node.node_id);
assert!(changed);
}
#[test]
fn test_fsub_zero() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let zero = dag.get_constant_fp(0.0f64.to_bits(), f32.clone());
let sub_node = dag.get_binary_op(SDOpcode::FSub, f32.clone(), x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sub_node.node_id);
assert!(changed);
}
#[test]
fn test_fmul_by_2_to_fadd() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let two = dag.get_constant_fp(2.0f64.to_bits(), f32.clone());
let mul_node = dag.get_binary_op(SDOpcode::FMul, f32.clone(), x, two);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_fdiv_by_1() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let one = dag.get_constant_fp(1.0f64.to_bits(), f32.clone());
let div_node = dag.get_binary_op(SDOpcode::FDiv, f32.clone(), x, one);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, div_node.node_id);
assert!(changed);
}
#[test]
fn test_fneg_fneg() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let zero = dag.get_constant_fp(0.0f64.to_bits(), f32.clone());
let neg1 = dag.get_binary_op(SDOpcode::FSub, f32.clone(), zero, x);
let zero2 = dag.get_constant_fp(0.0f64.to_bits(), f32.clone());
let neg2 = dag.get_binary_op(SDOpcode::FSub, f32.clone(), zero2, neg1);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, neg2.node_id);
assert!(changed);
}
#[test]
fn test_select_cond_true() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let cond = dag.get_constant(1, Type::i1());
let tval = dag.get_constant(42, i32.clone());
let fval = dag.get_constant(0, i32.clone());
let sel = dag.get_select(cond, tval, fval, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sel.node_id);
assert!(changed);
}
#[test]
fn test_select_cond_false() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let cond = dag.get_constant(0, Type::i1());
let tval = dag.get_constant(42, i32.clone());
let fval = dag.get_constant(99, i32.clone());
let sel = dag.get_select(cond, tval, fval, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sel.node_id);
assert!(changed);
}
#[test]
fn test_select_same_values() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let cond = dag.get_register(Type::i1());
let val = dag.get_constant(7, i32.clone());
let sel = dag.get_select(cond, val, val, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sel.node_id);
assert!(changed);
}
#[test]
fn test_select_undef_cond() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let cond = dag.get_undef(Type::i1());
let tval = dag.get_constant(1, i32.clone());
let fval = dag.get_constant(0, i32.clone());
let sel = dag.get_select(cond, tval, fval, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, sel.node_id);
assert!(changed);
}
#[test]
fn test_setcc_same_operands() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let setcc = dag.add_node(SDOpcode::SetCC, vec![Type::i1()], vec![x, x]);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, setcc.node_id);
assert!(changed);
}
#[test]
fn test_reassociate_add_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c1 = dag.get_constant(10, i32.clone());
let inner_add = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, c1);
let c2 = dag.get_constant(20, i32.clone());
let outer_add = dag.get_binary_op(SDOpcode::Add, i32.clone(), inner_add, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, outer_add.node_id);
assert!(changed);
}
#[test]
fn test_reassociate_mul_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c1 = dag.get_constant(3, i32.clone());
let inner_mul = dag.get_binary_op(SDOpcode::Mul, i32.clone(), x, c1);
let c2 = dag.get_constant(5, i32.clone());
let outer_mul = dag.get_binary_op(SDOpcode::Mul, i32.clone(), inner_mul, c2);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, outer_mul.node_id);
assert!(changed);
}
#[test]
fn test_undef_propagation_add() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let undef = dag.get_undef(i32.clone());
let add_node = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, undef);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, add_node.node_id);
assert!(changed);
}
#[test]
fn test_undef_propagation_mul() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let undef = dag.get_undef(i32.clone());
let x = dag.get_register(i32.clone());
let mul_node = dag.get_binary_op(SDOpcode::Mul, i32.clone(), undef, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, mul_node.node_id);
assert!(changed);
}
#[test]
fn test_load_undef_ptr() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let chain = dag.entry_token;
let undef = dag.get_undef(Type::pointer(0));
let load_val = dag.get_load(chain, undef, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, load_val.node_id);
assert!(changed);
}
#[test]
fn test_multiple_combine_iterations() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let c1 = dag.get_constant(1, i32.clone());
let add1 = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, c0);
let add2 = dag.get_binary_op(SDOpcode::Add, i32.clone(), add1, c1);
let add3 = dag.get_binary_op(SDOpcode::Add, i32.clone(), add2, c0);
dag.set_root(add3);
let mut combiner = DAGCombiner::new();
let changes = combiner.combine(&mut dag);
assert!(changes > 0);
}
#[test]
fn test_combine_no_infinite_loop() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0 = dag.get_constant(0, i32.clone());
let add_node = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, c0);
let mut combiner = DAGCombiner::with_max_iterations(10);
let changes = combiner.combine(&mut dag);
assert!(changes > 0);
assert!(combiner.num_combined > 0);
}
#[test]
fn test_combiner_default() {
let combiner = DAGCombiner::new();
assert_eq!(combiner.num_combined, 0);
assert!(!combiner.modified);
}
#[test]
fn test_combiner_with_max_iterations() {
let combiner = DAGCombiner::with_max_iterations(5);
assert_eq!(combiner.max_iterations, 5);
}
#[test]
fn test_store_undef() {
let mut dag = make_test_dag();
let chain = dag.entry_token;
let undef = dag.get_undef(Type::i32());
let ptr = dag.get_register(Type::pointer(0));
let store_node = dag.get_store(chain, undef, ptr);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, store_node.node_id);
assert!(changed);
}
#[test]
fn test_redundant_load_elimination() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let ptr = dag.get_register(Type::pointer(0));
let val = dag.get_constant(42, i32.clone());
let chain = dag.entry_token;
let store_node = dag.get_store(chain, val, ptr);
let load_val = dag.get_load(store_node, ptr, i32.clone());
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, load_val.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_zext() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let i64 = Type::i64();
let x = dag.get_register(i32.clone());
let zext = dag.add_node(SDOpcode::ZExt, vec![i64.clone()], vec![x]);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32.clone()], vec![zext]);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_extract_from_build_vector() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.id);
let e0 = dag.get_constant(0, i32.clone());
let e1 = dag.get_constant(1, i32.clone());
let e2 = dag.get_constant(2, i32.clone());
let e3 = dag.get_constant(3, i32.clone());
let bv = dag.add_node(
SDOpcode::BuildVector,
vec![v4i32.clone()],
vec![e0, e1, e2, e3],
);
let idx = dag.get_constant(1, Type::i32());
let extract = dag.add_node(SDOpcode::ExtractElement, vec![i32.clone()], vec![bv, idx]);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, extract.node_id);
assert!(changed);
}
#[test]
fn test_build_vector_from_extracts() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.id);
let src_vec = dag.get_undef(v4i32.clone());
let idx0 = dag.get_constant(0, Type::i32());
let idx1 = dag.get_constant(1, Type::i32());
let idx2 = dag.get_constant(2, Type::i32());
let idx3 = dag.get_constant(3, Type::i32());
let e0 = dag.add_node(
SDOpcode::ExtractElement,
vec![i32.clone()],
vec![src_vec, idx0],
);
let e1 = dag.add_node(
SDOpcode::ExtractElement,
vec![i32.clone()],
vec![src_vec, idx1],
);
let e2 = dag.add_node(
SDOpcode::ExtractElement,
vec![i32.clone()],
vec![src_vec, idx2],
);
let e3 = dag.add_node(
SDOpcode::ExtractElement,
vec![i32.clone()],
vec![src_vec, idx3],
);
let bv = dag.add_node(
SDOpcode::BuildVector,
vec![v4i32.clone()],
vec![e0, e1, e2, e3],
);
let mut combiner = DAGCombiner::new();
let changed = combiner.combine_node(&mut dag, bv.node_id);
assert!(changed);
}
#[test]
fn test_fadd_zero_v2() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let zero = dag.get_constant_fp(0.0f64.to_bits(), f64.clone());
let fadd = dag.get_binary_op(SDOpcode::FAdd, f64, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fadd(&mut dag, fadd.node_id);
assert!(changed);
}
#[test]
fn test_fsub_x_minus_x() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let fsub = dag.get_binary_op(SDOpcode::FSub, f64, x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fsub(&mut dag, fsub.node_id);
assert!(changed);
}
#[test]
fn test_fmul_one() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let one = dag.get_constant_fp(1.0f64.to_bits(), f64.clone());
let fmul = dag.get_binary_op(SDOpcode::FMul, f64, x, one);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fmul(&mut dag, fmul.node_id);
assert!(changed);
}
#[test]
fn test_fdiv_x_div_x() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let fdiv = dag.get_binary_op(SDOpcode::FDiv, f64, x, x);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fdiv(&mut dag, fdiv.node_id);
assert!(changed);
}
#[test]
fn test_fabs_fabs() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let fabs1 = dag.add_node(SDOpcode::FAbs, vec![f64.clone()], vec![x]);
let fabs2 = dag.add_node(SDOpcode::FAbs, vec![f64], vec![fabs1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fabs(&mut dag, fabs2.node_id);
assert!(changed);
}
#[test]
fn test_fneg_fneg_v2() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let fneg1 = dag.add_node(SDOpcode::FNeg, vec![f64.clone()], vec![x]);
let fneg2 = dag.add_node(SDOpcode::FNeg, vec![f64], vec![fneg1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fneg(&mut dag, fneg2.node_id);
assert!(changed);
}
#[test]
fn test_fma_zero_accum() {
let mut dag = make_test_dag();
let f64 = Type::double();
let a = dag.get_register(f64.clone());
let b = dag.get_register(f64.clone());
let zero = dag.get_constant_fp(0.0f64.to_bits(), f64.clone());
let fma = dag.add_node(SDOpcode::FMA, vec![f64], vec![a, b, zero]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fma(&mut dag, fma.node_id);
assert!(changed);
}
#[test]
fn test_extract_from_build_vector_direct() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.id);
let e0 = dag.get_constant(0, i32.clone());
let e1 = dag.get_constant(1, i32.clone());
let e2 = dag.get_constant(2, i32.clone());
let e3 = dag.get_constant(3, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v4i32], vec![e0, e1, e2, e3]);
let idx = dag.get_constant(2, Type::i32());
let extract = dag.add_node(SDOpcode::ExtractElement, vec![i32], vec![bv, idx]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_extract_vector_elt(&mut dag, extract.node_id);
assert!(changed);
}
#[test]
fn test_fp_round_extend_canonicalize() {
let mut dag = make_test_dag();
let f64 = Type::double();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let extend = dag.add_node(SDOpcode::FpExtend, vec![f64.clone()], vec![x]);
let round = dag.add_node(SDOpcode::FpRound, vec![f32], vec![extend]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fp_round_extend(&mut dag, round.node_id);
assert!(changed);
}
#[test]
fn test_visit_load_basic() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let ptr = dag.get_register(Type::ptr());
let chain = dag.entry_token;
let load = dag.add_node(SDOpcode::Load, vec![i32], vec![chain, ptr]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_load(&mut dag, load.node_id);
assert!(!changed);
}
#[test]
fn test_visit_store_undef() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let undef = dag.get_undef(i32.clone());
let ptr = dag.get_register(Type::ptr());
let chain = dag.entry_token;
let store = dag.add_node(SDOpcode::Store, vec![], vec![chain, undef, ptr]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_store(&mut dag, store.node_id);
assert!(changed); }
#[test]
fn test_visit_atomic_basic() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let ptr = dag.get_register(Type::ptr());
let chain = dag.entry_token;
let val = dag.get_constant(42, i32.clone());
let rmw = dag.add_node(SDOpcode::AtomicRMW, vec![i32], vec![chain, ptr, val]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_atomic(&mut dag, rmw.node_id);
assert!(!changed); }
#[test]
fn test_visit_build_vector_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.clone());
let z = dag.get_constant(0, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v4i32], vec![z, z, z, z]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_build_vector(&mut dag, bv.node_id);
assert!(changed); }
#[test]
fn test_visit_build_vector_nonzero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v2i32 = Type::fixed_vector_with(2, i32.clone());
let c1 = dag.get_constant(1, i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v2i32], vec![c1, c2]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_build_vector(&mut dag, bv.node_id);
assert!(!changed); }
#[test]
fn test_visit_vector_shuffle_basic() {
let mut dag = make_test_dag();
let v4i32 = Type::fixed_vector_with(4, Type::i32());
let v = dag.get_register(v4i32.clone());
let mask = dag.get_constant(0x3210, Type::i32()); let shuffle = dag.add_node(SDOpcode::VectorShuffle, vec![v4i32], vec![v, v, mask]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_vector_shuffle(&mut dag, shuffle.node_id);
assert!(changed); }
#[test]
fn test_visit_extract_vector_elt_from_build() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v2i32 = Type::fixed_vector_with(2, i32.clone());
let c1 = dag.get_constant(10, i32.clone());
let c2 = dag.get_constant(20, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v2i32], vec![c1, c2]);
let idx = dag.get_constant(0, i32.clone());
let extract = dag.add_node(SDOpcode::ExtractElement, vec![i32], vec![bv, idx]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_extract_vector_elt(&mut dag, extract.node_id);
assert!(changed); }
#[test]
fn test_visit_insert_vector_elt_to_build() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v2i32 = Type::fixed_vector_with(2, i32.clone());
let c1 = dag.get_constant(1, i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v2i32.clone()], vec![c1, c2]);
let new_val = dag.get_constant(99, i32.clone());
let idx = dag.get_constant(0, i32.clone());
let insert = dag.add_node(SDOpcode::InsertElement, vec![v2i32], vec![bv, new_val, idx]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_insert_vector_elt(&mut dag, insert.node_id);
assert!(changed); }
#[test]
fn test_visit_scalar_to_vector_undef() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.clone());
let undef = dag.get_undef(i32);
let s2v = dag.add_node(SDOpcode::ScalarToVector, vec![v4i32], vec![undef]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_scalar_to_vector(&mut dag, s2v.node_id);
assert!(changed);
}
#[test]
fn test_visit_extend_zext_of_zext() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i16 = Type::i16();
let i32 = Type::i32();
let x = dag.get_register(i8.clone());
let zext1 = dag.add_node(SDOpcode::ZExt, vec![i16], vec![x]);
let zext2 = dag.add_node(SDOpcode::ZExt, vec![i32], vec![zext1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_extend(&mut dag, zext2.node_id);
assert!(changed); }
#[test]
fn test_visit_extend_sext_of_trunc() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let i16 = Type::i16();
let x = dag.get_register(i32.clone());
let trunc = dag.add_node(SDOpcode::Trunc, vec![i16], vec![x]);
let sext = dag.add_node(SDOpcode::SExt, vec![i32], vec![trunc]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_extend(&mut dag, sext.node_id);
assert!(changed);
}
#[test]
fn test_visit_concat_vectors_basic() {
let mut dag = make_test_dag();
let v2i32 = Type::fixed_vector_with(2, Type::i32());
let v4i32 = Type::fixed_vector_with(4, Type::i32());
let a = dag.get_register(v2i32.clone());
let b = dag.get_register(v2i32.clone());
let concat = dag.add_node(SDOpcode::ConcatVectors, vec![v4i32], vec![a, b]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_concat_vectors(&mut dag, concat.node_id);
assert!(!changed); }
#[test]
fn test_reassociate_add_constants_v2() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c5 = dag.get_constant(5, i32.clone());
let c10 = dag.get_constant(10, i32.clone());
let inner = dag.get_binary_op(SDOpcode::Add, i32.clone(), x, c5);
let outer = dag.get_binary_op(SDOpcode::Add, i32, inner, c10);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_add_constants(&mut dag, outer.node_id);
assert!(changed); }
#[test]
fn test_reassociate_sub_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c3 = dag.get_constant(3, i32.clone());
let c7 = dag.get_constant(7, i32.clone());
let inner = dag.get_binary_op(SDOpcode::Sub, i32.clone(), x, c3);
let outer = dag.get_binary_op(SDOpcode::Sub, i32, inner, c7);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_sub_constants(&mut dag, outer.node_id);
assert!(changed);
}
#[test]
fn test_reassociate_and_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0xf0 = dag.get_constant(0xF0, i32.clone());
let c0x0f = dag.get_constant(0x0F, i32.clone());
let inner = dag.get_binary_op(SDOpcode::And, i32.clone(), x, c0xf0);
let outer = dag.get_binary_op(SDOpcode::And, i32, inner, c0x0f);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_and_constants(&mut dag, outer.node_id);
assert!(changed); }
#[test]
fn test_reassociate_or_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c0x10 = dag.get_constant(0x10, i32.clone());
let c0x01 = dag.get_constant(0x01, i32.clone());
let inner = dag.get_binary_op(SDOpcode::Or, i32.clone(), x, c0x10);
let outer = dag.get_binary_op(SDOpcode::Or, i32, inner, c0x01);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_or_constants(&mut dag, outer.node_id);
assert!(changed);
}
#[test]
fn test_reassociate_xor_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c_aa = dag.get_constant(0xAA, i32.clone());
let c_55 = dag.get_constant(0x55, i32.clone());
let inner = dag.get_binary_op(SDOpcode::Xor, i32.clone(), x, c_aa);
let outer = dag.get_binary_op(SDOpcode::Xor, i32, inner, c_55);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_xor_constants(&mut dag, outer.node_id);
assert!(changed);
}
#[test]
fn test_fold_cmp_and_one() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let a = dag.get_register(i32.clone());
let b = dag.get_register(i32.clone());
let cmp = dag.add_node(SDOpcode::SetCC, vec![Type::i1()], vec![a, b]);
let one = dag.get_constant(1, i32.clone());
let and = dag.get_binary_op(SDOpcode::And, i32, cmp, one);
let mut combiner = DAGCombiner::new();
let changed = combiner.fold_cmp_and_one(&mut dag, and.node_id);
assert!(changed);
}
#[test]
fn test_reassociate_shift_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let c3 = dag.get_constant(3, i32.clone());
let inner = dag.get_binary_op(SDOpcode::Shl, i32.clone(), x, c2);
let outer = dag.get_binary_op(SDOpcode::Shl, i32, inner, c3);
let mut combiner = DAGCombiner::new();
let changed = combiner.reassociate_shift_constants(&mut dag, outer.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_binop_and() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let mask = dag.get_constant(0xFFFF, i64.clone());
let and = dag.get_binary_op(SDOpcode::And, i64.clone(), x, mask);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![and]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_binop_or() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let y = dag.get_register(i64.clone());
let or = dag.get_binary_op(SDOpcode::Or, i64.clone(), x, y);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![or]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_binop_xor() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let y = dag.get_register(i64.clone());
let xor = dag.get_binary_op(SDOpcode::Xor, i64.clone(), x, y);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![xor]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_binop_add() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let y = dag.get_register(i64.clone());
let add = dag.get_binary_op(SDOpcode::Add, i64, x, y);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![add]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_shift_small_amount() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let c2 = dag.get_constant(2, i64.clone());
let shl = dag.get_binary_op(SDOpcode::Shl, i64, x, c2);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![shl]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_trunc_of_large_shift_to_zero() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let c64 = dag.get_constant(64, i64.clone());
let shl = dag.get_binary_op(SDOpcode::Shl, i64, x, c64);
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32], vec![shl]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_truncate(&mut dag, trunc.node_id);
assert!(changed);
}
#[test]
fn test_zext_of_trunc_matching_types() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let i32b = Type::i32();
let x = dag.get_register(i32.clone());
let trunc = dag.add_node(SDOpcode::Trunc, vec![i32b.clone()], vec![x]);
let zext = dag.add_node(SDOpcode::ZExt, vec![i32b], vec![trunc]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_zero_extend(&mut dag, zext.node_id);
assert!(changed);
}
#[test]
fn test_zext_of_load_to_zextload() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i32 = Type::i32();
let chain = dag.entry_token;
let ptr = dag.get_register(Type::pointer(0));
let load = dag.get_load(chain, ptr, i8);
let zext = dag.add_node(SDOpcode::ZExt, vec![i32], vec![load]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_zero_extend(&mut dag, zext.node_id);
assert!(changed);
}
#[test]
fn test_zext_of_binop_and() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i32 = Type::i32();
let x = dag.get_register(i8.clone());
let y = dag.get_register(i8.clone());
let and = dag.get_binary_op(SDOpcode::And, i8, x, y);
let zext = dag.add_node(SDOpcode::ZExt, vec![i32], vec![and]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_zero_extend(&mut dag, zext.node_id);
assert!(changed);
}
#[test]
fn test_sext_of_trunc_canonicalize() {
let mut dag = make_test_dag();
let i64 = Type::i64();
let i16 = Type::i16();
let i32 = Type::i32();
let x = dag.get_register(i64.clone());
let trunc = dag.add_node(SDOpcode::Trunc, vec![i16], vec![x]);
let sext = dag.add_node(SDOpcode::SExt, vec![i32], vec![trunc]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_sign_extend(&mut dag, sext.node_id);
assert!(changed);
}
#[test]
fn test_sext_of_load_to_sextload() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i32 = Type::i32();
let chain = dag.entry_token;
let ptr = dag.get_register(Type::pointer(0));
let load = dag.get_load(chain, ptr, i8);
let sext = dag.add_node(SDOpcode::SExt, vec![i32], vec![load]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_sign_extend(&mut dag, sext.node_id);
assert!(changed);
}
#[test]
fn test_sext_of_assert_sext() {
let mut dag = make_test_dag();
let i16 = Type::i16();
let i32 = Type::i32();
let x = dag.get_register(i16.clone());
let assert = dag.add_node(SDOpcode::AssertSExt, vec![i16], vec![x]);
let sext = dag.add_node(SDOpcode::SExt, vec![i32], vec![assert]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_sign_extend(&mut dag, sext.node_id);
assert!(changed);
}
#[test]
fn test_anyext_of_trunc_elimination() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let i16 = Type::i16();
let x = dag.get_register(i32.clone());
let trunc = dag.add_node(SDOpcode::Trunc, vec![i16], vec![x]);
let anyext = dag.add_node(SDOpcode::AnyExt, vec![i32], vec![trunc]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_any_extend(&mut dag, anyext.node_id);
assert!(changed);
}
#[test]
fn test_anyext_of_load_promotion() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i32 = Type::i32();
let chain = dag.entry_token;
let ptr = dag.get_register(Type::pointer(0));
let load = dag.get_load(chain, ptr, i8);
let anyext = dag.add_node(SDOpcode::AnyExt, vec![i32], vec![load]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_any_extend(&mut dag, anyext.node_id);
assert!(changed);
}
#[test]
fn test_anyext_of_anyext() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let i16 = Type::i16();
let i32 = Type::i32();
let x = dag.get_register(i8.clone());
let anyext1 = dag.add_node(SDOpcode::AnyExt, vec![i16], vec![x]);
let anyext2 = dag.add_node(SDOpcode::AnyExt, vec![i32], vec![anyext1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_any_extend(&mut dag, anyext2.node_id);
assert!(changed);
}
#[test]
fn test_freeze_of_freeze() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let f1 = dag.add_node(SDOpcode::Freeze, vec![i32.clone()], vec![x]);
let f2 = dag.add_node(SDOpcode::Freeze, vec![i32], vec![f1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_freeze_combine(&mut dag, f2.node_id);
assert!(changed);
}
#[test]
fn test_freeze_of_undef() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let undef = dag.get_undef(i32.clone());
let freeze = dag.add_node(SDOpcode::Freeze, vec![i32], vec![undef]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_freeze_combine(&mut dag, freeze.node_id);
assert!(changed);
}
#[test]
fn test_fcopysign_same_operands() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let copysign = dag.add_node(SDOpcode::FCopySign, vec![f64], vec![x, x]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fcopysign(&mut dag, copysign.node_id);
assert!(changed);
}
#[test]
fn test_fcopysign_nested() {
let mut dag = make_test_dag();
let f64 = Type::double();
let x = dag.get_register(f64.clone());
let y = dag.get_register(f64.clone());
let z = dag.get_register(f64.clone());
let inner = dag.add_node(SDOpcode::FCopySign, vec![f64.clone()], vec![x, y]);
let outer = dag.add_node(SDOpcode::FCopySign, vec![f64], vec![inner, z]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fcopysign(&mut dag, outer.node_id);
assert!(changed);
}
#[test]
fn test_fcanonicalize_of_constant() {
let mut dag = make_test_dag();
let f32 = Type::float();
let c = dag.get_constant_fp(1.5f64.to_bits(), f32.clone());
let canon = dag.add_node(SDOpcode::FCanonicalize, vec![f32], vec![c]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fcanonicalize(&mut dag, canon.node_id);
assert!(changed);
}
#[test]
fn test_fcanonicalize_nested() {
let mut dag = make_test_dag();
let f32 = Type::float();
let x = dag.get_register(f32.clone());
let c1 = dag.add_node(SDOpcode::FCanonicalize, vec![f32.clone()], vec![x]);
let c2 = dag.add_node(SDOpcode::FCanonicalize, vec![f32], vec![c1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fcanonicalize(&mut dag, c2.node_id);
assert!(changed);
}
#[test]
fn test_uaddsat_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let zero = dag.get_constant(0, i32.clone());
let sat = dag.get_binary_op(SDOpcode::UAddSat, i32, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_saturating_arith(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_usubsat_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let zero = dag.get_constant(0, i32.clone());
let sat = dag.get_binary_op(SDOpcode::USubSat, i32, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_saturating_arith(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_saddsat_zero() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let zero = dag.get_constant(0, i32.clone());
let sat = dag.get_binary_op(SDOpcode::SAddSat, i32, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_saturating_arith(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_uaddsat_constants() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let c200 = dag.get_constant(200, i8.clone());
let c100 = dag.get_constant(100, i8.clone());
let sat = dag.get_binary_op(SDOpcode::UAddSat, i8, c200, c100);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_saturating_arith(&mut dag, sat.node_id);
assert!(changed); }
#[test]
fn test_usubsat_constants_underflow() {
let mut dag = make_test_dag();
let i8 = Type::i8();
let c50 = dag.get_constant(50, i8.clone());
let c100 = dag.get_constant(100, i8.clone());
let sat = dag.get_binary_op(SDOpcode::USubSat, i8, c50, c100);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_saturating_arith(&mut dag, sat.node_id);
assert!(changed); }
#[test]
fn test_uaddo_identity() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let zero = dag.get_constant(0, i32.clone());
let uaddo = dag.get_binary_op(SDOpcode::UAddO, i32, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_overflow_arith(&mut dag, uaddo.node_id);
assert!(changed);
}
#[test]
fn test_usubo_identity() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let zero = dag.get_constant(0, i32.clone());
let usubo = dag.get_binary_op(SDOpcode::USubO, i32, x, zero);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_overflow_arith(&mut dag, usubo.node_id);
assert!(changed);
}
#[test]
fn test_umulo_identity() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let one = dag.get_constant(1, i32.clone());
let umulo = dag.get_binary_op(SDOpcode::UMulo, i32, x, one);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_overflow_arith(&mut dag, umulo.node_id);
assert!(changed);
}
#[test]
fn test_uaddo_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c10 = dag.get_constant(10, i32.clone());
let c20 = dag.get_constant(20, i32.clone());
let uaddo = dag.get_binary_op(SDOpcode::UAddO, i32, c10, c20);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_overflow_arith(&mut dag, uaddo.node_id);
assert!(changed);
}
#[test]
fn test_smulo_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c3 = dag.get_constant(3, i32.clone());
let c5 = dag.get_constant(5, i32.clone());
let smulo = dag.get_binary_op(SDOpcode::SMulo, i32, c3, c5);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_overflow_arith(&mut dag, smulo.node_id);
assert!(changed);
}
#[test]
fn test_vecreduce_one_element() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v1i32 = Type::fixed_vector_with(1, i32.clone());
let elem = dag.get_constant(42, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v1i32], vec![elem]);
let reduce = dag.add_node(SDOpcode::TokenFactor, vec![i32], vec![bv]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_vecreduce_extended(&mut dag, reduce.node_id);
assert!(changed);
}
#[test]
fn test_vecreduce_constants() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.clone());
let c1 = dag.get_constant(1, i32.clone());
let c2 = dag.get_constant(2, i32.clone());
let c3 = dag.get_constant(3, i32.clone());
let c4 = dag.get_constant(4, i32.clone());
let bv = dag.add_node(SDOpcode::BuildVector, vec![v4i32], vec![c1, c2, c3, c4]);
let reduce = dag.add_node(SDOpcode::TokenFactor, vec![i32], vec![bv]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_vecreduce_extended(&mut dag, reduce.node_id);
assert!(changed);
}
#[test]
fn test_vecreduce_splat() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let v4i32 = Type::fixed_vector_with(4, i32.clone());
let x = dag.get_register(i32.clone());
let s2v = dag.add_node(SDOpcode::ScalarToVector, vec![v4i32], vec![x]);
let reduce = dag.add_node(SDOpcode::TokenFactor, vec![i32], vec![s2v]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_vecreduce_extended(&mut dag, reduce.node_id);
assert!(changed);
}
#[test]
fn test_fp_to_sint_sat_constant() {
let mut dag = make_test_dag();
let f64 = Type::double();
let i32 = Type::i32();
let c = dag.get_constant_fp(3.14f64.to_bits(), f64.clone());
let sat = dag.add_node(SDOpcode::FpToSIntSat, vec![i32], vec![c]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fp_to_int_sat(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_fp_to_uint_sat_constant() {
let mut dag = make_test_dag();
let f64 = Type::double();
let i8 = Type::i8();
let c = dag.get_constant_fp(42.0f64.to_bits(), f64.clone());
let sat = dag.add_node(SDOpcode::FpToUIntSat, vec![i8], vec![c]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fp_to_int_sat(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_fp_to_sint_sat_nan() {
let mut dag = make_test_dag();
let f64 = Type::double();
let i32 = Type::i32();
let nan_bits = f64::NAN.to_bits();
let c = dag.get_constant_fp(nan_bits, f64);
let sat = dag.add_node(SDOpcode::FpToSIntSat, vec![i32], vec![c]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_fp_to_int_sat(&mut dag, sat.node_id);
assert!(changed);
}
#[test]
fn test_bitreverse_of_bitreverse() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let x = dag.get_register(i32.clone());
let br1 = dag.add_node(SDOpcode::BitReverse, vec![i32.clone()], vec![x]);
let br2 = dag.add_node(SDOpcode::BitReverse, vec![i32], vec![br1]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_bitreverse(&mut dag, br2.node_id);
assert!(changed);
}
#[test]
fn test_bitreverse_constant() {
let mut dag = make_test_dag();
let i32 = Type::i32();
let c = dag.get_constant(0x12345678, i32.clone());
let br = dag.add_node(SDOpcode::BitReverse, vec![i32], vec![c]);
let mut combiner = DAGCombiner::new();
let changed = combiner.visit_bitreverse(&mut dag, br.node_id);
assert!(changed);
}
}