Remove fallthrough instruction
This commit is contained in:
@@ -38,26 +38,6 @@ fn define_control_flow(
|
|||||||
.is_branch(true),
|
.is_branch(true),
|
||||||
);
|
);
|
||||||
|
|
||||||
ig.push(
|
|
||||||
Inst::new(
|
|
||||||
"fallthrough",
|
|
||||||
r#"
|
|
||||||
Fall through to the next block.
|
|
||||||
|
|
||||||
This is the same as `jump`, except the destination block must be
|
|
||||||
the next one in the layout.
|
|
||||||
|
|
||||||
Jumps are turned into fall-through instructions by the branch
|
|
||||||
relaxation pass. There is no reason to use this instruction outside
|
|
||||||
that pass.
|
|
||||||
"#,
|
|
||||||
&formats.jump,
|
|
||||||
)
|
|
||||||
.operands_in(vec![block, args])
|
|
||||||
.is_terminator(true)
|
|
||||||
.is_branch(true),
|
|
||||||
);
|
|
||||||
|
|
||||||
let Testable = &TypeVar::new(
|
let Testable = &TypeVar::new(
|
||||||
"Testable",
|
"Testable",
|
||||||
"A scalar boolean or integer type",
|
"A scalar boolean or integer type",
|
||||||
|
|||||||
@@ -274,12 +274,12 @@ impl Function {
|
|||||||
let mut inst_iter = inst_iter.skip_while(|&inst| !dfg[inst].opcode().is_branch());
|
let mut inst_iter = inst_iter.skip_while(|&inst| !dfg[inst].opcode().is_branch());
|
||||||
|
|
||||||
// A conditional branch is permitted in a basic block only when followed
|
// A conditional branch is permitted in a basic block only when followed
|
||||||
// by a terminal jump or fallthrough instruction.
|
// by a terminal jump instruction.
|
||||||
if let Some(_branch) = inst_iter.next() {
|
if let Some(_branch) = inst_iter.next() {
|
||||||
if let Some(next) = inst_iter.next() {
|
if let Some(next) = inst_iter.next() {
|
||||||
match dfg[next].opcode() {
|
match dfg[next].opcode() {
|
||||||
Opcode::Fallthrough | Opcode::Jump => (),
|
Opcode::Jump => (),
|
||||||
_ => return Err((next, "post-branch instruction not fallthrough or jump")),
|
_ => return Err((next, "post-branch instruction not jump")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2158,19 +2158,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
ctx.emit(Inst::gen_move(writable_xreg(PINNED_REG), rm, I64));
|
ctx.emit(Inst::gen_move(writable_xreg(PINNED_REG), rm, I64));
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Spill
|
|
||||||
| Opcode::Fill
|
|
||||||
| Opcode::FillNop
|
|
||||||
| Opcode::CopyNop
|
|
||||||
| Opcode::AdjustSpDown
|
|
||||||
| Opcode::AdjustSpUpImm
|
|
||||||
| Opcode::AdjustSpDownImm
|
|
||||||
| Opcode::IfcmpSp => {
|
|
||||||
panic!("Unused opcode should not be encountered.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Opcode::Jump
|
Opcode::Jump
|
||||||
| Opcode::Fallthrough
|
|
||||||
| Opcode::Brz
|
| Opcode::Brz
|
||||||
| Opcode::Brnz
|
| Opcode::Brnz
|
||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
@@ -3789,7 +3777,7 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
let op0 = ctx.data(branches[0]).opcode();
|
let op0 = ctx.data(branches[0]).opcode();
|
||||||
let op1 = ctx.data(branches[1]).opcode();
|
let op1 = ctx.data(branches[1]).opcode();
|
||||||
|
|
||||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
assert!(op1 == Opcode::Jump);
|
||||||
let taken = BranchTarget::Label(targets[0]);
|
let taken = BranchTarget::Label(targets[0]);
|
||||||
// not_taken target is the target of the second branch, even if it is a Fallthrough
|
// not_taken target is the target of the second branch, even if it is a Fallthrough
|
||||||
// instruction: because we reorder blocks while we lower, the fallthrough in the new
|
// instruction: because we reorder blocks while we lower, the fallthrough in the new
|
||||||
@@ -3932,11 +3920,8 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
// Must be an unconditional branch or an indirect branch.
|
// Must be an unconditional branch or an indirect branch.
|
||||||
let op = ctx.data(branches[0]).opcode();
|
let op = ctx.data(branches[0]).opcode();
|
||||||
match op {
|
match op {
|
||||||
Opcode::Jump | Opcode::Fallthrough => {
|
Opcode::Jump => {
|
||||||
assert!(branches.len() == 1);
|
assert!(branches.len() == 1);
|
||||||
// In the Fallthrough case, the machine-independent driver
|
|
||||||
// fills in `targets[0]` with our fallthrough block, so this
|
|
||||||
// is valid for both Jump and Fallthrough.
|
|
||||||
ctx.emit(Inst::Jump {
|
ctx.emit(Inst::Jump {
|
||||||
dest: BranchTarget::Label(targets[0]),
|
dest: BranchTarget::Label(targets[0]),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -542,10 +542,9 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
) -> CodegenResult<()> {
|
) -> CodegenResult<()> {
|
||||||
// A block should end with at most two branches. The first may be a
|
// A block should end with at most two branches. The first may be a
|
||||||
// conditional branch; a conditional branch can be followed only by an
|
// conditional branch; a conditional branch can be followed only by an
|
||||||
// unconditional branch or fallthrough. Otherwise, if only one branch,
|
// unconditional branch. Otherwise, if only one branch, it may be an
|
||||||
// it may be an unconditional branch, a fallthrough, a return, or a
|
// unconditional branch, a return, or a trap. These conditions are verified
|
||||||
// trap. These conditions are verified by `is_ebb_basic()` during the
|
// by `is_ebb_basic()` during the verifier pass.
|
||||||
// verifier pass.
|
|
||||||
assert!(branches.len() <= 2);
|
assert!(branches.len() <= 2);
|
||||||
|
|
||||||
if branches.len() == 2 {
|
if branches.len() == 2 {
|
||||||
@@ -553,7 +552,7 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
let op0 = ctx.data(branches[0]).opcode();
|
let op0 = ctx.data(branches[0]).opcode();
|
||||||
let op1 = ctx.data(branches[1]).opcode();
|
let op1 = ctx.data(branches[1]).opcode();
|
||||||
|
|
||||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
assert!(op1 == Opcode::Jump);
|
||||||
let taken = BranchTarget::Label(targets[0]);
|
let taken = BranchTarget::Label(targets[0]);
|
||||||
let not_taken = BranchTarget::Label(targets[1]);
|
let not_taken = BranchTarget::Label(targets[1]);
|
||||||
|
|
||||||
@@ -586,11 +585,8 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
// Must be an unconditional branch or an indirect branch.
|
// Must be an unconditional branch or an indirect branch.
|
||||||
let op = ctx.data(branches[0]).opcode();
|
let op = ctx.data(branches[0]).opcode();
|
||||||
match op {
|
match op {
|
||||||
Opcode::Jump | Opcode::Fallthrough => {
|
Opcode::Jump => {
|
||||||
assert_eq!(branches.len(), 1);
|
assert_eq!(branches.len(), 1);
|
||||||
// In the Fallthrough case, the machine-independent driver
|
|
||||||
// fills in `targets[0]` with our fallthrough block, so this
|
|
||||||
// is valid for both Jump and Fallthrough.
|
|
||||||
ctx.emit(Inst::Jump {
|
ctx.emit(Inst::Jump {
|
||||||
dest: BranchTarget::Label(targets[0]),
|
dest: BranchTarget::Label(targets[0]),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2909,7 +2909,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Jump
|
Opcode::Jump
|
||||||
| Opcode::Fallthrough
|
|
||||||
| Opcode::Brz
|
| Opcode::Brz
|
||||||
| Opcode::Brnz
|
| Opcode::Brnz
|
||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
@@ -2980,7 +2979,7 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
let op0 = ctx.data(branches[0]).opcode();
|
let op0 = ctx.data(branches[0]).opcode();
|
||||||
let op1 = ctx.data(branches[1]).opcode();
|
let op1 = ctx.data(branches[1]).opcode();
|
||||||
|
|
||||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
assert!(op1 == Opcode::Jump);
|
||||||
let taken = BranchTarget::Label(targets[0]);
|
let taken = BranchTarget::Label(targets[0]);
|
||||||
let not_taken = BranchTarget::Label(targets[1]);
|
let not_taken = BranchTarget::Label(targets[1]);
|
||||||
|
|
||||||
@@ -3029,11 +3028,8 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
|
|||||||
// Must be an unconditional branch or an indirect branch.
|
// Must be an unconditional branch or an indirect branch.
|
||||||
let op = ctx.data(branches[0]).opcode();
|
let op = ctx.data(branches[0]).opcode();
|
||||||
match op {
|
match op {
|
||||||
Opcode::Jump | Opcode::Fallthrough => {
|
Opcode::Jump => {
|
||||||
assert!(branches.len() == 1);
|
assert!(branches.len() == 1);
|
||||||
// In the Fallthrough case, the machine-independent driver
|
|
||||||
// fills in `targets[0]` with our fallthrough block, so this
|
|
||||||
// is valid for both Jump and Fallthrough.
|
|
||||||
ctx.emit(Inst::Jump {
|
ctx.emit(Inst::Jump {
|
||||||
dest: BranchTarget::Label(targets[0]),
|
dest: BranchTarget::Label(targets[0]),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6879,7 +6879,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Jump
|
Opcode::Jump
|
||||||
| Opcode::Fallthrough
|
|
||||||
| Opcode::Brz
|
| Opcode::Brz
|
||||||
| Opcode::Brnz
|
| Opcode::Brnz
|
||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
@@ -6931,13 +6930,10 @@ impl LowerBackend for X64Backend {
|
|||||||
op0,
|
op0,
|
||||||
op1
|
op1
|
||||||
);
|
);
|
||||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
assert!(op1 == Opcode::Jump);
|
||||||
|
|
||||||
let taken = targets[0];
|
let taken = targets[0];
|
||||||
// not_taken target is the target of the second branch, even if it is a Fallthrough
|
// not_taken target is the target of the second branch.
|
||||||
// instruction: because we reorder blocks while we lower, the fallthrough in the new
|
|
||||||
// order is not (necessarily) the same as the fallthrough in CLIF. So we use the
|
|
||||||
// explicitly-provided target.
|
|
||||||
let not_taken = targets[1];
|
let not_taken = targets[1];
|
||||||
|
|
||||||
match op0 {
|
match op0 {
|
||||||
@@ -7147,7 +7143,7 @@ impl LowerBackend for X64Backend {
|
|||||||
// Must be an unconditional branch or trap.
|
// Must be an unconditional branch or trap.
|
||||||
let op = ctx.data(branches[0]).opcode();
|
let op = ctx.data(branches[0]).opcode();
|
||||||
match op {
|
match op {
|
||||||
Opcode::Jump | Opcode::Fallthrough => {
|
Opcode::Jump => {
|
||||||
ctx.emit(Inst::jmp_known(targets[0]));
|
ctx.emit(Inst::jmp_known(targets[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1295,32 +1295,23 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
|||||||
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) {
|
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) {
|
||||||
for inst in f.layout.block_likely_branches(block) {
|
for inst in f.layout.block_likely_branches(block) {
|
||||||
if f.dfg[inst].opcode().is_branch() {
|
if f.dfg[inst].opcode().is_branch() {
|
||||||
visit_branch_targets(f, block, inst, &mut visit);
|
visit_branch_targets(f, inst, &mut visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_branch_targets<F: FnMut(Inst, Block)>(
|
fn visit_branch_targets<F: FnMut(Inst, Block)>(f: &Function, inst: Inst, visit: &mut F) {
|
||||||
f: &Function,
|
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
|
||||||
block: Block,
|
BranchInfo::NotABranch => {}
|
||||||
inst: Inst,
|
BranchInfo::SingleDest(dest, _) => {
|
||||||
visit: &mut F,
|
visit(inst, dest);
|
||||||
) {
|
}
|
||||||
if f.dfg[inst].opcode() == Opcode::Fallthrough {
|
BranchInfo::Table(table, maybe_dest) => {
|
||||||
visit(inst, f.layout.next_block(block).unwrap());
|
if let Some(dest) = maybe_dest {
|
||||||
} else {
|
|
||||||
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
|
|
||||||
BranchInfo::NotABranch => {}
|
|
||||||
BranchInfo::SingleDest(dest, _) => {
|
|
||||||
visit(inst, dest);
|
visit(inst, dest);
|
||||||
}
|
}
|
||||||
BranchInfo::Table(table, maybe_dest) => {
|
for &dest in f.jump_tables[table].as_slice() {
|
||||||
if let Some(dest) = maybe_dest {
|
visit(inst, dest);
|
||||||
visit(inst, dest);
|
|
||||||
}
|
|
||||||
for &dest in f.jump_tables[table].as_slice() {
|
|
||||||
visit(inst, dest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ block0(v0: i32):
|
|||||||
v1 = icmp_imm ule v0, 2
|
v1 = icmp_imm ule v0, 2
|
||||||
v2 = iconst.i32 1
|
v2 = iconst.i32 1
|
||||||
brnz v1, block3(v2) ; handle base case, n <= 2
|
brnz v1, block3(v2) ; handle base case, n <= 2
|
||||||
fallthrough block1(v0, v2)
|
jump block1(v0, v2)
|
||||||
|
|
||||||
block1(v4: i32, v5:i32):
|
block1(v4: i32, v5:i32):
|
||||||
v6 = iconst.i32 1
|
v6 = iconst.i32 1
|
||||||
v7 = iadd_imm v4, -2
|
v7 = iadd_imm v4, -2
|
||||||
fallthrough block2(v7, v5, v6)
|
jump block2(v7, v5, v6)
|
||||||
|
|
||||||
block2(v10: i32, v11: i32, v12: i32): ; params: n, fib(n-1), fib(n-2)
|
block2(v10: i32, v11: i32, v12: i32): ; params: n, fib(n-1), fib(n-2)
|
||||||
v13 = iadd v11, v12
|
v13 = iadd v11, v12
|
||||||
@@ -40,7 +40,7 @@ function %fibonacci_recursive(i32) -> i32 {
|
|||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
v1 = icmp_imm ule v0, 2
|
v1 = icmp_imm ule v0, 2
|
||||||
brnz v1, block2
|
brnz v1, block2
|
||||||
fallthrough block1(v0)
|
jump block1(v0)
|
||||||
|
|
||||||
block1(v10: i32):
|
block1(v10: i32):
|
||||||
v11 = iadd_imm v10, -1
|
v11 = iadd_imm v10, -1
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ where
|
|||||||
|
|
||||||
// Interpret a Cranelift instruction.
|
// Interpret a Cranelift instruction.
|
||||||
Ok(match inst.opcode() {
|
Ok(match inst.opcode() {
|
||||||
Opcode::Jump | Opcode::Fallthrough => ControlFlow::ContinueAt(branch(), args()?),
|
Opcode::Jump => ControlFlow::ContinueAt(branch(), args()?),
|
||||||
Opcode::Brz => branch_when(
|
Opcode::Brz => branch_when(
|
||||||
!arg(0)?
|
!arg(0)?
|
||||||
.convert(ValueConversionKind::ToBoolean)?
|
.convert(ValueConversionKind::ToBoolean)?
|
||||||
|
|||||||
Reference in New Issue
Block a user