cranelift: Remove brz and brnz (#5630)
Remove the brz and brnz instructions, as their behavior is now redundant with brif.
This commit is contained in:
@@ -8,9 +8,8 @@ pub(crate) struct Formats {
|
||||
pub(crate) binary: Rc<InstructionFormat>,
|
||||
pub(crate) binary_imm8: Rc<InstructionFormat>,
|
||||
pub(crate) binary_imm64: Rc<InstructionFormat>,
|
||||
pub(crate) brif: Rc<InstructionFormat>,
|
||||
pub(crate) branch: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table: Rc<InstructionFormat>,
|
||||
pub(crate) brif: Rc<InstructionFormat>,
|
||||
pub(crate) call: Rc<InstructionFormat>,
|
||||
pub(crate) call_indirect: Rc<InstructionFormat>,
|
||||
pub(crate) cond_trap: Rc<InstructionFormat>,
|
||||
@@ -114,8 +113,6 @@ impl Formats {
|
||||
|
||||
jump: Builder::new("Jump").block().build(),
|
||||
|
||||
branch: Builder::new("Branch").value().block().build(),
|
||||
|
||||
brif: Builder::new("Brif").value().block().block().build(),
|
||||
|
||||
branch_table: Builder::new("BranchTable")
|
||||
|
||||
@@ -65,38 +65,6 @@ fn define_control_flow(
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"brz",
|
||||
r#"
|
||||
Branch when zero.
|
||||
|
||||
Take the branch when ``c = 0``.
|
||||
"#,
|
||||
&formats.branch,
|
||||
)
|
||||
.operands_in(vec![c, block_call])
|
||||
.is_branch(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"brnz",
|
||||
r#"
|
||||
Branch when non-zero.
|
||||
|
||||
Take the branch when ``c != 0``.
|
||||
"#,
|
||||
&formats.branch,
|
||||
)
|
||||
.operands_in(vec![c, block_call])
|
||||
.is_branch(true),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let _i32 = &TypeVar::new(
|
||||
"i32",
|
||||
|
||||
@@ -53,7 +53,7 @@ impl<'a> CFGPrinter<'a> {
|
||||
write!(w, " {} [shape=record, label=\"{{", block)?;
|
||||
crate::write::write_block_header(w, self.func, block, 4)?;
|
||||
// Add all outgoing branch instructions to the label.
|
||||
for inst in self.func.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = self.func.layout.last_inst(block) {
|
||||
write!(w, " | <{}>", inst)?;
|
||||
PlainWriter.write_instruction(w, self.func, &aliases, inst, 0)?;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ impl Context {
|
||||
|
||||
/// Perform pre-legalization rewrites on the function.
|
||||
pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
|
||||
do_preopt(&mut self.func, &mut self.cfg, isa);
|
||||
do_preopt(&mut self.func, isa);
|
||||
self.verify_if(isa)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ impl DominatorTree {
|
||||
/// post-order. Split-invariant means that if a block is split in two, we get the same
|
||||
/// post-order except for the insertion of the new block header at the split point.
|
||||
fn push_successors(&mut self, func: &Function, block: Block) {
|
||||
for inst in func.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = func.layout.last_inst(block) {
|
||||
match func.dfg.analyze_branch(inst) {
|
||||
BranchInfo::SingleDest(succ) => {
|
||||
self.push_if_unseen(succ.block(&func.dfg.value_lists))
|
||||
@@ -653,11 +653,14 @@ mod tests {
|
||||
let v0 = func.dfg.append_block_param(block0, I32);
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
let trap_block = func.dfg.make_block();
|
||||
|
||||
let mut cur = FuncCursor::new(&mut func);
|
||||
|
||||
cur.insert_block(block0);
|
||||
cur.ins().brnz(v0, block2, &[]);
|
||||
cur.ins().brif(v0, block2, &[], trap_block, &[]);
|
||||
|
||||
cur.insert_block(trap_block);
|
||||
cur.ins().trap(TrapCode::User(0));
|
||||
|
||||
cur.insert_block(block1);
|
||||
@@ -674,13 +677,13 @@ mod tests {
|
||||
// Fall-through-first, prune-at-source DFT:
|
||||
//
|
||||
// block0 {
|
||||
// brnz block2 {
|
||||
// brif block2 {
|
||||
// trap
|
||||
// block2 {
|
||||
// return
|
||||
// } block2
|
||||
// } block0
|
||||
assert_eq!(dt.cfg_postorder(), &[block2, block0]);
|
||||
assert_eq!(dt.cfg_postorder(), &[trap_block, block2, block0]);
|
||||
|
||||
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
|
||||
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
|
||||
@@ -714,8 +717,7 @@ mod tests {
|
||||
let jmp_block3_block1 = cur.ins().jump(block1, &[]);
|
||||
|
||||
cur.insert_block(block1);
|
||||
let br_block1_block0 = cur.ins().brnz(cond, block0, &[]);
|
||||
let jmp_block1_block2 = cur.ins().jump(block2, &[]);
|
||||
let br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
|
||||
|
||||
cur.insert_block(block2);
|
||||
cur.ins().jump(block0, &[]);
|
||||
@@ -730,7 +732,7 @@ mod tests {
|
||||
// block3 {
|
||||
// block3:jump block1 {
|
||||
// block1 {
|
||||
// block1:brnz block0 {
|
||||
// block1:brif block0 {
|
||||
// block1:jump block2 {
|
||||
// block2 {
|
||||
// block2:jump block0 (seen)
|
||||
@@ -738,7 +740,7 @@ mod tests {
|
||||
// } block1:jump block2
|
||||
// block0 {
|
||||
// } block0
|
||||
// } block1:brnz block0
|
||||
// } block1:brif block0
|
||||
// } block1
|
||||
// } block3:jump block1
|
||||
// } block3
|
||||
@@ -748,12 +750,16 @@ mod tests {
|
||||
assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
|
||||
assert_eq!(dt.idom(block3), None);
|
||||
assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1);
|
||||
assert_eq!(dt.idom(block2).unwrap(), jmp_block1_block2);
|
||||
assert_eq!(dt.idom(block0).unwrap(), br_block1_block0);
|
||||
assert_eq!(dt.idom(block2).unwrap(), br_block1_block0_block2);
|
||||
assert_eq!(dt.idom(block0).unwrap(), br_block1_block0_block2);
|
||||
|
||||
assert!(dt.dominates(br_block1_block0, br_block1_block0, &cur.func.layout));
|
||||
assert!(!dt.dominates(br_block1_block0, jmp_block3_block1, &cur.func.layout));
|
||||
assert!(dt.dominates(jmp_block3_block1, br_block1_block0, &cur.func.layout));
|
||||
assert!(dt.dominates(
|
||||
br_block1_block0_block2,
|
||||
br_block1_block0_block2,
|
||||
&cur.func.layout
|
||||
));
|
||||
assert!(!dt.dominates(br_block1_block0_block2, jmp_block3_block1, &cur.func.layout));
|
||||
assert!(dt.dominates(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout));
|
||||
|
||||
assert_eq!(
|
||||
dt.rpo_cmp(block3, block3, &cur.func.layout),
|
||||
@@ -765,7 +771,7 @@ mod tests {
|
||||
Ordering::Less
|
||||
);
|
||||
assert_eq!(
|
||||
dt.rpo_cmp(jmp_block3_block1, jmp_block1_block2, &cur.func.layout),
|
||||
dt.rpo_cmp(jmp_block3_block1, br_block1_block0_block2, &cur.func.layout),
|
||||
Ordering::Less
|
||||
);
|
||||
}
|
||||
|
||||
@@ -465,11 +465,7 @@ impl<'a> Elaborator<'a> {
|
||||
));
|
||||
// Determine the instruction at which we
|
||||
// insert in `data.hoist_block`.
|
||||
let before = self
|
||||
.func
|
||||
.layout
|
||||
.canonical_branch_inst(&self.func.dfg, data.hoist_block)
|
||||
.unwrap();
|
||||
let before = self.func.layout.last_inst(data.hoist_block).unwrap();
|
||||
(data.scope_depth as usize, before, data.hoist_block)
|
||||
};
|
||||
|
||||
|
||||
@@ -11,17 +11,14 @@
|
||||
//!
|
||||
//! ...
|
||||
//!
|
||||
//! brz vx, Block1 ; end of basic block
|
||||
//! brif vx, Block1, Block2 ; end of basic block
|
||||
//!
|
||||
//! ... ; beginning of basic block
|
||||
//!
|
||||
//! ...
|
||||
//!
|
||||
//! jmp Block2 ; end of basic block
|
||||
//! Block1:
|
||||
//! jump block3
|
||||
//! ```
|
||||
//!
|
||||
//! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brz)`
|
||||
//! and `(Block0, jmp Block2)` respectively.
|
||||
//! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brif)`,
|
||||
//! while `Block3` would have a single predecessor denoted as `(Block1, jump block3)`.
|
||||
|
||||
use crate::bforest;
|
||||
use crate::entity::SecondaryMap;
|
||||
@@ -120,7 +117,7 @@ impl ControlFlowGraph {
|
||||
}
|
||||
|
||||
fn compute_block(&mut self, func: &Function, block: Block) {
|
||||
for inst in func.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = func.layout.last_inst(block) {
|
||||
match func.dfg.analyze_branch(inst) {
|
||||
BranchInfo::SingleDest(dest) => {
|
||||
self.add_edge(block, inst, dest.block(&func.dfg.value_lists));
|
||||
@@ -253,21 +250,17 @@ mod tests {
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
|
||||
let br_block0_block2;
|
||||
let br_block1_block1;
|
||||
let jmp_block0_block1;
|
||||
let jmp_block1_block2;
|
||||
let br_block0_block2_block1;
|
||||
let br_block1_block1_block2;
|
||||
|
||||
{
|
||||
let mut cur = FuncCursor::new(&mut func);
|
||||
|
||||
cur.insert_block(block0);
|
||||
br_block0_block2 = cur.ins().brnz(cond, block2, &[]);
|
||||
jmp_block0_block1 = cur.ins().jump(block1, &[]);
|
||||
br_block0_block2_block1 = cur.ins().brif(cond, block2, &[], block1, &[]);
|
||||
|
||||
cur.insert_block(block1);
|
||||
br_block1_block1 = cur.ins().brnz(cond, block1, &[]);
|
||||
jmp_block1_block2 = cur.ins().jump(block2, &[]);
|
||||
br_block1_block1_block2 = cur.ins().brif(cond, block1, &[], block2, &[]);
|
||||
|
||||
cur.insert_block(block2);
|
||||
}
|
||||
@@ -288,19 +281,23 @@ mod tests {
|
||||
assert_eq!(block2_predecessors.len(), 2);
|
||||
|
||||
assert_eq!(
|
||||
block1_predecessors.contains(&BlockPredecessor::new(block0, jmp_block0_block1)),
|
||||
block1_predecessors
|
||||
.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
block1_predecessors.contains(&BlockPredecessor::new(block1, br_block1_block1)),
|
||||
block1_predecessors
|
||||
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
block2_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block2)),
|
||||
block2_predecessors
|
||||
.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
block2_predecessors.contains(&BlockPredecessor::new(block1, jmp_block1_block2)),
|
||||
block2_predecessors
|
||||
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -309,11 +306,22 @@ mod tests {
|
||||
assert_eq!(block2_successors, []);
|
||||
}
|
||||
|
||||
// Change some instructions and recompute block0
|
||||
func.dfg.replace(br_block0_block2).brnz(cond, block1, &[]);
|
||||
func.dfg.replace(jmp_block0_block1).return_(&[]);
|
||||
// Add a new block to hold a return instruction
|
||||
let ret_block = func.dfg.make_block();
|
||||
|
||||
{
|
||||
let mut cur = FuncCursor::new(&mut func);
|
||||
cur.insert_block(ret_block);
|
||||
cur.ins().return_(&[]);
|
||||
}
|
||||
|
||||
// Change some instructions and recompute block0 and ret_block
|
||||
func.dfg
|
||||
.replace(br_block0_block2_block1)
|
||||
.brif(cond, block1, &[], ret_block, &[]);
|
||||
cfg.recompute_block(&mut func, block0);
|
||||
let br_block0_block1 = br_block0_block2;
|
||||
cfg.recompute_block(&mut func, ret_block);
|
||||
let br_block0_block1_ret_block = br_block0_block2_block1;
|
||||
|
||||
{
|
||||
let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
|
||||
@@ -329,23 +337,27 @@ mod tests {
|
||||
assert_eq!(block2_predecessors.len(), 1);
|
||||
|
||||
assert_eq!(
|
||||
block1_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block1)),
|
||||
block1_predecessors
|
||||
.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
block1_predecessors.contains(&BlockPredecessor::new(block1, br_block1_block1)),
|
||||
block1_predecessors
|
||||
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
block2_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block2)),
|
||||
block2_predecessors
|
||||
.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
block2_predecessors.contains(&BlockPredecessor::new(block1, jmp_block1_block2)),
|
||||
block2_predecessors
|
||||
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
|
||||
true
|
||||
);
|
||||
|
||||
assert_eq!(block0_successors.collect::<Vec<_>>(), [block1]);
|
||||
assert_eq!(block0_successors.collect::<Vec<_>>(), [block1, ret_block]);
|
||||
assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]);
|
||||
assert_eq!(block2_successors.collect::<Vec<_>>(), []);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ pub(crate) fn visit_block_succs<F: FnMut(Inst, Block, bool)>(
|
||||
block: Block,
|
||||
mut visit: F,
|
||||
) {
|
||||
for inst in f.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = f.layout.last_inst(block) {
|
||||
match f.dfg.insts[inst].analyze_branch() {
|
||||
BranchInfo::NotABranch => {}
|
||||
BranchInfo::SingleDest(dest) => {
|
||||
|
||||
@@ -270,7 +270,6 @@ impl InstructionData {
|
||||
pub fn analyze_branch(&self) -> BranchInfo {
|
||||
match *self {
|
||||
Self::Jump { destination, .. } => BranchInfo::SingleDest(destination),
|
||||
Self::Branch { destination, .. } => BranchInfo::SingleDest(destination),
|
||||
Self::Brif {
|
||||
blocks: [block_then, block_else],
|
||||
..
|
||||
@@ -292,9 +291,6 @@ impl InstructionData {
|
||||
match self {
|
||||
Self::Jump {
|
||||
ref destination, ..
|
||||
}
|
||||
| Self::Branch {
|
||||
ref destination, ..
|
||||
} => std::slice::from_ref(destination),
|
||||
Self::Brif { blocks, .. } => blocks,
|
||||
Self::BranchTable { .. } => &[],
|
||||
@@ -313,10 +309,6 @@ impl InstructionData {
|
||||
Self::Jump {
|
||||
ref mut destination,
|
||||
..
|
||||
}
|
||||
| Self::Branch {
|
||||
ref mut destination,
|
||||
..
|
||||
} => std::slice::from_mut(destination),
|
||||
Self::Brif { blocks, .. } => blocks,
|
||||
Self::BranchTable { .. } => &mut [],
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//! determined by the `Layout` data structure defined in this module.
|
||||
|
||||
use crate::entity::SecondaryMap;
|
||||
use crate::ir::dfg::DataFlowGraph;
|
||||
use crate::ir::progpoint::{ExpandedProgramPoint, ProgramOrder};
|
||||
use crate::ir::{Block, Inst};
|
||||
use crate::packed_option::PackedOption;
|
||||
@@ -594,19 +593,6 @@ impl Layout {
|
||||
self.insts[inst].prev.expand()
|
||||
}
|
||||
|
||||
/// Fetch the first instruction in a block's terminal branch group.
|
||||
pub fn canonical_branch_inst(&self, dfg: &DataFlowGraph, block: Block) -> Option<Inst> {
|
||||
// Basic blocks permit at most two terminal branch instructions.
|
||||
// If two, the former is conditional and the latter is unconditional.
|
||||
let last = self.last_inst(block)?;
|
||||
if let Some(prev) = self.prev_inst(last) {
|
||||
if dfg.insts[prev].opcode().is_branch() {
|
||||
return Some(prev);
|
||||
}
|
||||
}
|
||||
Some(last)
|
||||
}
|
||||
|
||||
/// Insert `inst` before the instruction `before` in the same block.
|
||||
pub fn insert_inst(&mut self, inst: Inst, before: Inst) {
|
||||
debug_assert_eq!(self.inst_block(inst), None);
|
||||
@@ -662,24 +648,6 @@ impl Layout {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over a limited set of instruction which are likely the branches of `block` in layout
|
||||
/// order. Any instruction not visited by this iterator is not a branch, but an instruction visited by this may not be a branch.
|
||||
pub fn block_likely_branches(&self, block: Block) -> Insts {
|
||||
// Note: Checking whether an instruction is a branch or not while walking backward might add
|
||||
// extra overhead. However, we know that the number of branches is limited to 2 at the end of
|
||||
// each block, and therefore we can just iterate over the last 2 instructions.
|
||||
let mut iter = self.block_insts(block);
|
||||
let head = iter.head;
|
||||
let tail = iter.tail;
|
||||
iter.next_back();
|
||||
let head = iter.next_back().or(head);
|
||||
Insts {
|
||||
layout: self,
|
||||
head,
|
||||
tail,
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the block containing `before` in two.
|
||||
///
|
||||
/// Insert `new_block` after the old block and move `before` and the following instructions to
|
||||
|
||||
@@ -160,8 +160,7 @@ mod tests {
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
pos.ins().brif(v0, block2, &[], block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
@@ -2507,93 +2507,6 @@
|
||||
(with_flags_side_effect flags
|
||||
(cond_br taken not_taken (cond_br_not_zero rt))))))
|
||||
|
||||
;;; Rules for `brz`/`brnz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; `brz` following `icmp`
|
||||
(rule (lower_branch (brz (maybe_uextend (icmp cc x @ (value_type ty) y)) _) targets)
|
||||
(let ((comparison FlagsAndCC (lower_icmp_into_flags cc x y ty))
|
||||
;; Negate the condition for `brz`.
|
||||
(cond Cond (invert_cond (cond_code (flags_and_cc_cc comparison))))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (flags_and_cc_flags comparison)
|
||||
(cond_br taken
|
||||
not_taken
|
||||
(cond_br_cond cond))))))
|
||||
;; `brnz` following `icmp`
|
||||
(rule (lower_branch (brnz (maybe_uextend (icmp cc x @ (value_type ty) y)) _) targets)
|
||||
(let ((comparison FlagsAndCC (lower_icmp_into_flags cc x y ty))
|
||||
(cond Cond (cond_code (flags_and_cc_cc comparison)))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (flags_and_cc_flags comparison)
|
||||
(cond_br taken
|
||||
not_taken
|
||||
(cond_br_cond cond))))))
|
||||
;; `brz` following `fcmp`
|
||||
(rule (lower_branch (brz (maybe_uextend (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) _) targets)
|
||||
(let ((cond Cond (fp_cond_code cc))
|
||||
(cond Cond (invert_cond cond)) ;; negate for `brz`
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (fpu_cmp (scalar_size ty) x y)
|
||||
(cond_br taken not_taken
|
||||
(cond_br_cond cond))))))
|
||||
;; `brnz` following `fcmp`
|
||||
(rule (lower_branch (brnz (maybe_uextend (fcmp cc x @ (value_type (ty_scalar_float ty)) y)) _) targets)
|
||||
(let ((cond Cond (fp_cond_code cc))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (fpu_cmp (scalar_size ty) x y)
|
||||
(cond_br taken not_taken
|
||||
(cond_br_cond cond))))))
|
||||
;; standard `brz`
|
||||
(rule -1 (lower_branch (brz c @ (value_type $I128) _) targets)
|
||||
(let ((flags ProducesFlags (flags_to_producesflags c))
|
||||
(c ValueRegs (put_in_regs c))
|
||||
(c_lo Reg (value_regs_get c 0))
|
||||
(c_hi Reg (value_regs_get c 1))
|
||||
(rt Reg (orr $I64 c_lo c_hi))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect flags
|
||||
(cond_br taken not_taken (cond_br_zero rt))))))
|
||||
(rule -2 (lower_branch (brz c @ (value_type ty) _) targets)
|
||||
(if (ty_int_ref_scalar_64 ty))
|
||||
(let ((flags ProducesFlags (flags_to_producesflags c))
|
||||
(rt Reg (put_in_reg_zext64 c))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect flags
|
||||
(cond_br taken not_taken (cond_br_zero rt))))))
|
||||
;; standard `brnz`
|
||||
(rule -1 (lower_branch (brnz c @ (value_type $I128) _) targets)
|
||||
(let ((flags ProducesFlags (flags_to_producesflags c))
|
||||
(c ValueRegs (put_in_regs c))
|
||||
(c_lo Reg (value_regs_get c 0))
|
||||
(c_hi Reg (value_regs_get c 1))
|
||||
(rt Reg (orr $I64 c_lo c_hi))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect flags
|
||||
(cond_br taken not_taken (cond_br_not_zero rt))))))
|
||||
(rule -2 (lower_branch (brnz c @ (value_type ty) _) targets)
|
||||
(if (ty_int_ref_scalar_64 ty))
|
||||
(let ((flags ProducesFlags (flags_to_producesflags c))
|
||||
(rt Reg (put_in_reg_zext64 c))
|
||||
(taken BranchTarget (branch_target targets 0))
|
||||
(not_taken BranchTarget (branch_target targets 1)))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect flags
|
||||
(cond_br taken not_taken (cond_br_not_zero rt))))))
|
||||
|
||||
;;; Rules for `jump` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower_branch (jump _) targets)
|
||||
|
||||
@@ -293,15 +293,12 @@ mod test {
|
||||
pos.insert_block(bb0);
|
||||
let v0 = pos.ins().iconst(I32, 0x1234);
|
||||
let v1 = pos.ins().iadd(arg0, v0);
|
||||
pos.ins().brnz(v1, bb1, &[]);
|
||||
pos.ins().jump(bb2, &[]);
|
||||
pos.ins().brif(v1, bb1, &[], bb2, &[]);
|
||||
pos.insert_block(bb1);
|
||||
pos.ins().brnz(v1, bb2, &[]);
|
||||
pos.ins().jump(bb3, &[]);
|
||||
pos.ins().brif(v1, bb2, &[], bb3, &[]);
|
||||
pos.insert_block(bb2);
|
||||
let v2 = pos.ins().iadd(v1, v0);
|
||||
pos.ins().brnz(v2, bb2, &[]);
|
||||
pos.ins().jump(bb1, &[]);
|
||||
pos.ins().brif(v2, bb2, &[], bb1, &[]);
|
||||
pos.insert_block(bb3);
|
||||
let v3 = pos.ins().isub(v1, v0);
|
||||
pos.ins().return_(&[v3]);
|
||||
|
||||
@@ -1868,8 +1868,8 @@
|
||||
(decl int_zero_reg (Type) ValueRegs)
|
||||
(extern constructor int_zero_reg int_zero_reg)
|
||||
|
||||
(decl lower_brz_or_nz (IntCC ValueRegs VecMachLabel Type) Unit)
|
||||
(extern constructor lower_brz_or_nz lower_brz_or_nz)
|
||||
(decl lower_cond_br (IntCC ValueRegs VecMachLabel Type) Unit)
|
||||
(extern constructor lower_cond_br lower_cond_br)
|
||||
|
||||
;; Normalize a value for comparision.
|
||||
;;
|
||||
@@ -1901,14 +1901,14 @@
|
||||
;; Default behavior for branching based on an input value.
|
||||
(rule
|
||||
(lower_branch (brif v @ (value_type ty) _ _) targets)
|
||||
(lower_brz_or_nz (IntCC.NotEqual) (normalize_cmp_value ty v) targets ty))
|
||||
(lower_cond_br (IntCC.NotEqual) (normalize_cmp_value ty v) targets ty))
|
||||
|
||||
;; Special case for SI128 to reify the comparison value and branch on it.
|
||||
(rule 2
|
||||
(lower_branch (brif v @ (value_type $I128) _ _) targets)
|
||||
(let ((zero ValueRegs (value_regs (zero_reg) (zero_reg)))
|
||||
(cmp Reg (gen_icmp (IntCC.NotEqual) v zero $I128)))
|
||||
(lower_brz_or_nz (IntCC.NotEqual) cmp targets $I64)))
|
||||
(lower_cond_br (IntCC.NotEqual) cmp targets $I64)))
|
||||
|
||||
;; Branching on the result of an icmp
|
||||
(rule 1
|
||||
@@ -1930,66 +1930,6 @@
|
||||
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
|
||||
(emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))))
|
||||
|
||||
;;;;;
|
||||
(rule
|
||||
(lower_branch (brz v @ (value_type ty) _) targets)
|
||||
(lower_brz_or_nz (IntCC.Equal) (normalize_cmp_value ty v) targets ty))
|
||||
|
||||
;; Special case for SI128 to reify the comparison value and branch on it.
|
||||
(rule 2
|
||||
(lower_branch (brz v @ (value_type $I128) _) targets)
|
||||
(let ((zero ValueRegs (value_regs (zero_reg) (zero_reg)))
|
||||
(cmp Reg (gen_icmp (IntCC.Equal) v zero $I128)))
|
||||
(lower_brz_or_nz (IntCC.NotEqual) cmp targets $I64)))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brz (maybe_uextend (icmp cc a @ (value_type ty) b)) _) targets)
|
||||
(lower_br_icmp (intcc_inverse cc) a b targets ty))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brz (maybe_uextend (fcmp cc a @ (value_type ty) b)) _) targets)
|
||||
(if-let $true (floatcc_unordered cc))
|
||||
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
|
||||
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
|
||||
(emit_side_effect (cond_br (emit_fcmp (floatcc_inverse cc) ty a b) then else))))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brz (maybe_uextend (fcmp cc a @ (value_type ty) b)) _) targets)
|
||||
(if-let $false (floatcc_unordered cc))
|
||||
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
|
||||
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
|
||||
(emit_side_effect (cond_br (emit_fcmp cc ty a b) else then))))
|
||||
|
||||
;;;;
|
||||
(rule
|
||||
(lower_branch (brnz v @ (value_type ty) _) targets)
|
||||
(lower_brz_or_nz (IntCC.NotEqual) (normalize_cmp_value ty v) targets ty))
|
||||
|
||||
;; Special case for SI128 to reify the comparison value and branch on it.
|
||||
(rule 2
|
||||
(lower_branch (brnz v @ (value_type $I128) _) targets)
|
||||
(let ((zero ValueRegs (value_regs (zero_reg) (zero_reg)))
|
||||
(cmp Reg (gen_icmp (IntCC.NotEqual) v zero $I128)))
|
||||
(lower_brz_or_nz (IntCC.NotEqual) cmp targets $I64)))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brnz (maybe_uextend (icmp cc a @ (value_type ty) b)) _) targets)
|
||||
(lower_br_icmp cc a b targets ty))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brnz (maybe_uextend (fcmp cc a @ (value_type ty) b)) _) targets)
|
||||
(if-let $true (floatcc_unordered cc))
|
||||
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
|
||||
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
|
||||
(emit_side_effect (cond_br (emit_fcmp (floatcc_inverse cc) ty a b) else then))))
|
||||
|
||||
(rule 1
|
||||
(lower_branch (brnz (maybe_uextend (fcmp cc a @ (value_type ty) b)) _) targets)
|
||||
(if-let $false (floatcc_unordered cc))
|
||||
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
|
||||
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
|
||||
(emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))))
|
||||
|
||||
;;;
|
||||
(decl lower_br_table (Reg VecMachLabel) Unit)
|
||||
(extern constructor lower_br_table lower_br_table)
|
||||
|
||||
@@ -118,7 +118,6 @@ impl Into<AMode> for StackAMode {
|
||||
}
|
||||
|
||||
/// risc-v always take two register to compare
|
||||
/// brz can be compare with zero register which has the value 0
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct IntegerCompare {
|
||||
pub(crate) kind: IntCC,
|
||||
|
||||
@@ -159,8 +159,7 @@ mod tests {
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
pos.ins().brif(v0, block2, &[], block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
@@ -61,7 +61,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Riscv64Backend> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_brz_or_nz(
|
||||
fn lower_cond_br(
|
||||
&mut self,
|
||||
cc: &IntCC,
|
||||
a: ValueRegs,
|
||||
|
||||
@@ -194,8 +194,7 @@ mod tests {
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
pos.ins().brif(v0, block2, &[], block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
@@ -3761,28 +3761,6 @@
|
||||
(vec_element targets 1))))
|
||||
|
||||
|
||||
;;;; Rules for `brz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Two-way conditional branch on zero. `targets` contains:
|
||||
;; - element 0: target if the condition is true (i.e. value is zero)
|
||||
;; - element 1: target if the condition is false (i.e. value is nonzero)
|
||||
(rule (lower_branch (brz val_cond _) targets)
|
||||
(emit_side_effect (cond_br_bool (invert_bool (value_nonzero val_cond))
|
||||
(vec_element targets 0)
|
||||
(vec_element targets 1))))
|
||||
|
||||
|
||||
;;;; Rules for `brnz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Two-way conditional branch on nonzero. `targets` contains:
|
||||
;; - element 0: target if the condition is true (i.e. value is nonzero)
|
||||
;; - element 1: target if the condition is false (i.e. value is zero)
|
||||
(rule (lower_branch (brnz val_cond _) targets)
|
||||
(emit_side_effect (cond_br_bool (value_nonzero val_cond)
|
||||
(vec_element targets 0)
|
||||
(vec_element targets 1))))
|
||||
|
||||
|
||||
;;;; Rules for `trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (trap trap_code))
|
||||
|
||||
@@ -265,15 +265,12 @@ mod test {
|
||||
pos.insert_block(bb0);
|
||||
let v0 = pos.ins().iconst(I32, 0x1234);
|
||||
let v1 = pos.ins().iadd(arg0, v0);
|
||||
pos.ins().brnz(v1, bb1, &[]);
|
||||
pos.ins().jump(bb2, &[]);
|
||||
pos.ins().brif(v1, bb1, &[], bb2, &[]);
|
||||
pos.insert_block(bb1);
|
||||
pos.ins().brnz(v1, bb2, &[]);
|
||||
pos.ins().jump(bb3, &[]);
|
||||
pos.ins().brif(v1, bb2, &[], bb3, &[]);
|
||||
pos.insert_block(bb2);
|
||||
let v2 = pos.ins().iadd(v1, v0);
|
||||
pos.ins().brnz(v2, bb2, &[]);
|
||||
pos.ins().jump(bb1, &[]);
|
||||
pos.ins().brif(v2, bb2, &[], bb1, &[]);
|
||||
pos.insert_block(bb3);
|
||||
let v3 = pos.ins().isub(v1, v0);
|
||||
pos.ins().return_(&[v3]);
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
;; One-way conditional branch: jcond cond target.
|
||||
;;
|
||||
;; This instruction is useful when we have conditional jumps depending on
|
||||
;; more than two conditions, see for instance the lowering of Brz/brnz
|
||||
;; more than two conditions, see for instance the lowering of Brif
|
||||
;; with Fcmp inputs.
|
||||
;;
|
||||
;; A note of caution: in contexts where the branch target is another
|
||||
|
||||
@@ -184,8 +184,7 @@ mod tests {
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
pos.ins().brif(v0, block2, &[], block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
@@ -2904,41 +2904,6 @@
|
||||
(jmp_cond (CC.NZ) then else))))
|
||||
|
||||
|
||||
;; Rules for `brz` and `brnz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule 2 (lower_branch (brz (maybe_uextend (icmp cc a b)) _) (two_targets taken not_taken))
|
||||
(let ((cmp IcmpCondResult (invert_icmp_cond_result (emit_cmp cc a b))))
|
||||
(emit_side_effect (jmp_cond_icmp cmp taken not_taken))))
|
||||
|
||||
(rule 2 (lower_branch (brz (maybe_uextend (fcmp cc a b)) _) (two_targets taken not_taken))
|
||||
(let ((cmp FcmpCondResult (emit_fcmp (floatcc_inverse cc) a b)))
|
||||
(emit_side_effect (jmp_cond_fcmp cmp taken not_taken))))
|
||||
|
||||
(rule 1 (lower_branch (brz val @ (value_type $I128) _) (two_targets taken not_taken))
|
||||
(emit_side_effect (jmp_cond_icmp (cmp_zero_i128 (CC.NZ) val) taken not_taken)))
|
||||
|
||||
(rule 0 (lower_branch (brz val @ (value_type (ty_int_bool_or_ref)) _) (two_targets taken not_taken))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (cmp_zero_int_bool_ref val)
|
||||
(jmp_cond (CC.Z) taken not_taken))))
|
||||
|
||||
|
||||
(rule 2 (lower_branch (brnz (maybe_uextend (icmp cc a b)) _) (two_targets taken not_taken))
|
||||
(emit_side_effect (jmp_cond_icmp (emit_cmp cc a b) taken not_taken)))
|
||||
|
||||
(rule 2 (lower_branch (brnz (maybe_uextend (fcmp cc a b)) _) (two_targets taken not_taken))
|
||||
(let ((cmp FcmpCondResult (emit_fcmp cc a b)))
|
||||
(emit_side_effect (jmp_cond_fcmp cmp taken not_taken))))
|
||||
|
||||
(rule 1 (lower_branch (brnz val @ (value_type $I128) _) (two_targets taken not_taken))
|
||||
(emit_side_effect (jmp_cond_icmp (cmp_zero_i128 (CC.Z) val) taken not_taken)))
|
||||
|
||||
(rule 0 (lower_branch (brnz val @ (value_type (ty_int_bool_or_ref)) _) (two_targets taken not_taken))
|
||||
(emit_side_effect
|
||||
(with_flags_side_effect (cmp_zero_int_bool_ref val)
|
||||
(jmp_cond (CC.NZ) taken not_taken))))
|
||||
|
||||
|
||||
;; Compare an I128 value to zero, returning a flags result suitable for making a
|
||||
;; jump decision. The comparison is implemented as `(hi == 0) && (low == 0)`,
|
||||
;; and the result can be interpreted as follows
|
||||
|
||||
@@ -261,23 +261,20 @@ mod test {
|
||||
let v0 = pos.ins().iconst(I32, 0x1234);
|
||||
pos.set_srcloc(SourceLoc::new(2));
|
||||
let v1 = pos.ins().iadd(arg0, v0);
|
||||
pos.ins().brnz(v1, bb1, &[v1]);
|
||||
pos.ins().jump(bb2, &[]);
|
||||
pos.ins().brif(v1, bb1, &[v1], bb2, &[]);
|
||||
|
||||
pos.insert_block(bb1);
|
||||
pos.set_srcloc(SourceLoc::new(3));
|
||||
let v2 = pos.ins().isub(v1, v0);
|
||||
pos.set_srcloc(SourceLoc::new(4));
|
||||
let v3 = pos.ins().iadd(v2, bb1_param);
|
||||
pos.ins().brnz(v1, bb2, &[]);
|
||||
pos.ins().jump(bb3, &[v3]);
|
||||
pos.ins().brif(v1, bb2, &[], bb3, &[v3]);
|
||||
|
||||
pos.func.layout.set_cold(bb2);
|
||||
pos.insert_block(bb2);
|
||||
pos.set_srcloc(SourceLoc::new(5));
|
||||
let v4 = pos.ins().iadd(v1, v0);
|
||||
pos.ins().brnz(v4, bb2, &[]);
|
||||
pos.ins().jump(bb1, &[v4]);
|
||||
pos.ins().brif(v4, bb2, &[], bb1, &[v4]);
|
||||
|
||||
pos.insert_block(bb3);
|
||||
pos.set_srcloc(SourceLoc::new(6));
|
||||
|
||||
@@ -268,8 +268,7 @@ fn expand_cond_trap(
|
||||
//
|
||||
// Becomes:
|
||||
//
|
||||
// brz arg, new_block_resume
|
||||
// jump new_block_trap
|
||||
// brif arg, new_block_trap, new_block_resume
|
||||
//
|
||||
// new_block_trap:
|
||||
// trap
|
||||
@@ -285,17 +284,18 @@ fn expand_cond_trap(
|
||||
|
||||
// Replace trap instruction by the inverted condition.
|
||||
if trapz {
|
||||
func.dfg.replace(inst).brnz(arg, new_block_resume, &[]);
|
||||
func.dfg
|
||||
.replace(inst)
|
||||
.brif(arg, new_block_resume, &[], new_block_trap, &[]);
|
||||
} else {
|
||||
func.dfg.replace(inst).brz(arg, new_block_resume, &[]);
|
||||
func.dfg
|
||||
.replace(inst)
|
||||
.brif(arg, new_block_trap, &[], new_block_resume, &[]);
|
||||
}
|
||||
|
||||
// Add jump instruction after the inverted branch.
|
||||
// Insert the new label and the unconditional trap terminator.
|
||||
let mut pos = FuncCursor::new(func).after_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
pos.ins().jump(new_block_trap, &[]);
|
||||
|
||||
// Insert the new label and the unconditional trap terminator.
|
||||
pos.insert_block(new_block_trap);
|
||||
|
||||
match opcode {
|
||||
|
||||
@@ -330,6 +330,7 @@ mod tests {
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
let block3 = func.dfg.make_block();
|
||||
let block4 = func.dfg.make_block();
|
||||
let cond = func.dfg.append_block_param(block0, types::I32);
|
||||
|
||||
{
|
||||
@@ -342,11 +343,13 @@ mod tests {
|
||||
cur.ins().jump(block2, &[]);
|
||||
|
||||
cur.insert_block(block2);
|
||||
cur.ins().brnz(cond, block1, &[]);
|
||||
cur.ins().jump(block3, &[]);
|
||||
cur.ins().brif(cond, block1, &[], block3, &[]);
|
||||
|
||||
cur.insert_block(block3);
|
||||
cur.ins().brnz(cond, block0, &[]);
|
||||
cur.ins().brif(cond, block0, &[], block4, &[]);
|
||||
|
||||
cur.insert_block(block4);
|
||||
cur.ins().return_(&[]);
|
||||
}
|
||||
|
||||
let mut loop_analysis = LoopAnalysis::new();
|
||||
@@ -385,31 +388,32 @@ mod tests {
|
||||
let block3 = func.dfg.make_block();
|
||||
let block4 = func.dfg.make_block();
|
||||
let block5 = func.dfg.make_block();
|
||||
let block6 = func.dfg.make_block();
|
||||
let cond = func.dfg.append_block_param(block0, types::I32);
|
||||
|
||||
{
|
||||
let mut cur = FuncCursor::new(&mut func);
|
||||
|
||||
cur.insert_block(block0);
|
||||
cur.ins().brnz(cond, block1, &[]);
|
||||
cur.ins().jump(block3, &[]);
|
||||
cur.ins().brif(cond, block1, &[], block3, &[]);
|
||||
|
||||
cur.insert_block(block1);
|
||||
cur.ins().jump(block2, &[]);
|
||||
|
||||
cur.insert_block(block2);
|
||||
cur.ins().brnz(cond, block1, &[]);
|
||||
cur.ins().jump(block5, &[]);
|
||||
cur.ins().brif(cond, block1, &[], block5, &[]);
|
||||
|
||||
cur.insert_block(block3);
|
||||
cur.ins().jump(block4, &[]);
|
||||
|
||||
cur.insert_block(block4);
|
||||
cur.ins().brnz(cond, block3, &[]);
|
||||
cur.ins().jump(block5, &[]);
|
||||
cur.ins().brif(cond, block3, &[], block5, &[]);
|
||||
|
||||
cur.insert_block(block5);
|
||||
cur.ins().brnz(cond, block0, &[]);
|
||||
cur.ins().brif(cond, block0, &[], block6, &[]);
|
||||
|
||||
cur.insert_block(block6);
|
||||
cur.ins().return_(&[]);
|
||||
}
|
||||
|
||||
let mut loop_analysis = LoopAnalysis::new();
|
||||
|
||||
@@ -250,7 +250,7 @@ impl BlockLoweringOrder {
|
||||
let block_succ_end = block_succs.len();
|
||||
block_succ_range[block] = (block_succ_start, block_succ_end);
|
||||
|
||||
for inst in f.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = f.layout.last_inst(block) {
|
||||
if f.dfg.insts[inst].opcode() == Opcode::Return {
|
||||
// Implicit output edge for any return.
|
||||
block_out_count[block] += 1;
|
||||
@@ -276,7 +276,7 @@ impl BlockLoweringOrder {
|
||||
// could not be, in cases of br_table with no table and just a
|
||||
// default label, for example.)
|
||||
for block in f.layout.blocks() {
|
||||
for inst in f.layout.block_likely_branches(block) {
|
||||
if let Some(inst) = f.layout.last_inst(block) {
|
||||
// If the block has a branch with any "fixed args"
|
||||
// (not blockparam args) ...
|
||||
if f.dfg.insts[inst].opcode().is_branch() && f.dfg.inst_fixed_args(inst).len() > 0 {
|
||||
@@ -561,8 +561,8 @@ mod test {
|
||||
} else if succs.len() == 1 {
|
||||
pos.ins().jump(blocks[succs[0]], &[]);
|
||||
} else if succs.len() == 2 {
|
||||
pos.ins().brnz(arg0, blocks[succs[0]], &[]);
|
||||
pos.ins().jump(blocks[succs[1]], &[]);
|
||||
pos.ins()
|
||||
.brif(arg0, blocks[succs[0]], &[], blocks[succs[1]], &[]);
|
||||
} else {
|
||||
panic!("Too many successors");
|
||||
}
|
||||
|
||||
@@ -16,10 +16,9 @@ use smallvec::SmallVec;
|
||||
// "formal parameters" to mean the `Value`s listed in the block head, and
|
||||
// "actual parameters" to mean the `Value`s passed in a branch or a jump:
|
||||
//
|
||||
// block4(v16: i32, v18: i32): <-- formal parameters
|
||||
// block4(v16: i32, v18: i32): <-- formal parameters
|
||||
// ...
|
||||
// brnz v27, block7(v22, v24) <-- actual parameters
|
||||
// jump block6
|
||||
// brif v27, block7(v22, v24), block6 <-- actual parameters
|
||||
|
||||
// This transformation pass (conceptually) partitions all values in the
|
||||
// function into two groups:
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::divconst_magic_numbers::{magic_s32, magic_s64, magic_u32, magic_u64};
|
||||
use crate::divconst_magic_numbers::{MS32, MS64, MU32, MU64};
|
||||
use crate::flowgraph::ControlFlowGraph;
|
||||
use crate::ir::{
|
||||
condcodes::{CondCode, IntCC},
|
||||
condcodes::IntCC,
|
||||
instructions::Opcode,
|
||||
types::{I128, I32, I64, INVALID},
|
||||
Block, DataFlowGraph, Function, Inst, InstBuilder, InstructionData, Type, Value,
|
||||
types::{I128, I32, I64},
|
||||
DataFlowGraph, Function, Inst, InstBuilder, InstructionData, Type, Value,
|
||||
};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::timing;
|
||||
@@ -465,71 +464,6 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
||||
}
|
||||
}
|
||||
|
||||
/// Reorder branches to encourage fallthroughs.
|
||||
///
|
||||
/// When a block ends with a conditional branch followed by an unconditional
|
||||
/// branch, this will reorder them if one of them is branching to the next Block
|
||||
/// layout-wise. The unconditional jump can then become a fallthrough.
|
||||
fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, block: Block, inst: Inst) {
|
||||
let (term_inst, term_dest, cond_inst, cond_dest, kind, cond_arg) =
|
||||
match pos.func.dfg.insts[inst] {
|
||||
InstructionData::Jump {
|
||||
opcode: Opcode::Jump,
|
||||
destination,
|
||||
} => {
|
||||
let next_block = if let Some(next_block) = pos.func.layout.next_block(block) {
|
||||
next_block
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if destination.block(&pos.func.dfg.value_lists) == next_block {
|
||||
return;
|
||||
}
|
||||
|
||||
let prev_inst = if let Some(prev_inst) = pos.func.layout.prev_inst(inst) {
|
||||
prev_inst
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
match &pos.func.dfg.insts[prev_inst] {
|
||||
&InstructionData::Branch {
|
||||
opcode,
|
||||
arg,
|
||||
destination: cond_dest,
|
||||
} => {
|
||||
if cond_dest.block(&pos.func.dfg.value_lists) != next_block {
|
||||
return;
|
||||
}
|
||||
|
||||
let kind = match opcode {
|
||||
Opcode::Brz => Opcode::Brnz,
|
||||
Opcode::Brnz => Opcode::Brz,
|
||||
_ => panic!("unexpected opcode"),
|
||||
};
|
||||
|
||||
(inst, destination, prev_inst, cond_dest, kind, arg)
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
pos.func
|
||||
.dfg
|
||||
.replace(term_inst)
|
||||
.Jump(Opcode::Jump, INVALID, cond_dest);
|
||||
pos.func
|
||||
.dfg
|
||||
.replace(cond_inst)
|
||||
.Branch(kind, INVALID, term_dest, cond_arg);
|
||||
|
||||
cfg.recompute_block(pos.func, block);
|
||||
}
|
||||
|
||||
mod simplify {
|
||||
use super::*;
|
||||
use crate::ir::{
|
||||
@@ -789,12 +723,11 @@ mod simplify {
|
||||
/// Fold comparisons into branch operations when possible.
|
||||
///
|
||||
/// This matches against operations which compare against zero, then use the
|
||||
/// result in a `brz` or `brnz` branch. It folds those two operations into a
|
||||
/// single `brz` or `brnz`.
|
||||
/// result in a conditional branch.
|
||||
fn branch_opt(pos: &mut FuncCursor, inst: Inst) {
|
||||
let (cmp_arg, new_opcode) = if let InstructionData::Branch {
|
||||
opcode: br_opcode,
|
||||
let (cmp_arg, new_then, new_else) = if let InstructionData::Brif {
|
||||
arg: first_arg,
|
||||
blocks: [block_then, block_else],
|
||||
..
|
||||
} = pos.func.dfg.insts[inst]
|
||||
{
|
||||
@@ -817,21 +750,13 @@ mod simplify {
|
||||
return;
|
||||
}
|
||||
|
||||
// icmp_imm returns non-zero when the comparison is true. So, if
|
||||
// we're branching on zero, we need to invert the condition.
|
||||
let cond = match br_opcode {
|
||||
Opcode::Brz => cmp_cond.inverse(),
|
||||
Opcode::Brnz => cmp_cond,
|
||||
let (new_then, new_else) = match cmp_cond {
|
||||
IntCC::Equal => (block_else, block_then),
|
||||
IntCC::NotEqual => (block_then, block_else),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let new_opcode = match cond {
|
||||
IntCC::Equal => Opcode::Brz,
|
||||
IntCC::NotEqual => Opcode::Brnz,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
(cmp_arg, new_opcode)
|
||||
(cmp_arg, new_then, new_else)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -839,9 +764,10 @@ mod simplify {
|
||||
return;
|
||||
};
|
||||
|
||||
if let InstructionData::Branch { opcode, arg, .. } = &mut pos.func.dfg.insts[inst] {
|
||||
*opcode = new_opcode;
|
||||
if let InstructionData::Brif { arg, blocks, .. } = &mut pos.func.dfg.insts[inst] {
|
||||
*arg = cmp_arg;
|
||||
blocks[0] = new_then;
|
||||
blocks[1] = new_else;
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
@@ -849,14 +775,14 @@ mod simplify {
|
||||
}
|
||||
|
||||
/// The main pre-opt pass.
|
||||
pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
|
||||
pub fn do_preopt(func: &mut Function, isa: &dyn TargetIsa) {
|
||||
let _tt = timing::preopt();
|
||||
|
||||
let mut pos = FuncCursor::new(func);
|
||||
let native_word_width = isa.pointer_bytes() as u32;
|
||||
let mut optimizer = simplify::peephole_optimizer(isa);
|
||||
|
||||
while let Some(block) = pos.next_block() {
|
||||
while let Some(_) = pos.next_block() {
|
||||
while let Some(inst) = pos.next_inst() {
|
||||
simplify::apply_all(&mut optimizer, &mut pos, inst, native_word_width);
|
||||
|
||||
@@ -865,8 +791,6 @@ pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn Targ
|
||||
do_divrem_transformation(&divrem_info, &mut pos, inst);
|
||||
continue;
|
||||
}
|
||||
|
||||
branch_order(&mut pos, cfg, block, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,7 +584,7 @@ impl<'a> Verifier<'a> {
|
||||
MultiAry { ref args, .. } => {
|
||||
self.verify_value_list(inst, args, errors)?;
|
||||
}
|
||||
Jump { destination, .. } | Branch { destination, .. } => {
|
||||
Jump { destination, .. } => {
|
||||
self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
|
||||
}
|
||||
Brif {
|
||||
|
||||
@@ -428,12 +428,6 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
|
||||
write!(w, ", {}", block_else.block(pool))?;
|
||||
write_block_args(w, block_else.args_slice(pool))
|
||||
}
|
||||
Branch {
|
||||
arg, destination, ..
|
||||
} => {
|
||||
write!(w, " {}, {}", arg, destination.block(pool))?;
|
||||
write_block_args(w, destination.args_slice(pool))
|
||||
}
|
||||
BranchTable {
|
||||
arg,
|
||||
destination,
|
||||
|
||||
Reference in New Issue
Block a user