Skip to content

Commit 3b0f3a0

Browse files
committed
Optimize SwitchInt for bools
1 parent a8e3f59 commit 3b0f3a0

File tree

3 files changed

+119
-7
lines changed

3 files changed

+119
-7
lines changed

src/base.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ pub(crate) fn trans_fn<'tcx, B: Backend + 'static>(
110110
context.compute_cfg();
111111
context.compute_domtree();
112112
context.eliminate_unreachable_code(cx.module.isa()).unwrap();
113+
context.dce(cx.module.isa()).unwrap();
113114

114115
// Define function
115116
let module = &mut cx.module;
@@ -315,18 +316,45 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Backend>) {
315316

316317
TerminatorKind::SwitchInt {
317318
discr,
318-
switch_ty: _,
319+
switch_ty,
319320
values,
320321
targets,
321322
} => {
322323
let discr = trans_operand(fx, discr).load_scalar(fx);
323-
let mut switch = ::cranelift_frontend::Switch::new();
324-
for (i, value) in values.iter().enumerate() {
325-
let block = fx.get_block(targets[i]);
326-
switch.set_entry(*value, block);
324+
325+
if switch_ty.kind == fx.tcx.types.bool.kind {
326+
assert_eq!(targets.len(), 2);
327+
let then_block = fx.get_block(targets[0]);
328+
let else_block = fx.get_block(targets[1]);
329+
let test_zero = match **values {
330+
[0] => true,
331+
[1] => false,
332+
_ => unreachable!("{:?}", values),
333+
};
334+
335+
let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr);
336+
let (discr, is_inverted) =
337+
crate::optimize::peephole::maybe_unwrap_bool_not(&mut fx.bcx, discr);
338+
let test_zero = if is_inverted { !test_zero } else { test_zero };
339+
let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr);
340+
let discr =
341+
crate::optimize::peephole::make_branchable_value(&mut fx.bcx, discr);
342+
if test_zero {
343+
fx.bcx.ins().brz(discr, then_block, &[]);
344+
fx.bcx.ins().jump(else_block, &[]);
345+
} else {
346+
fx.bcx.ins().brnz(discr, then_block, &[]);
347+
fx.bcx.ins().jump(else_block, &[]);
348+
}
349+
} else {
350+
let mut switch = ::cranelift_frontend::Switch::new();
351+
for (i, value) in values.iter().enumerate() {
352+
let block = fx.get_block(targets[i]);
353+
switch.set_entry(*value, block);
354+
}
355+
let otherwise_block = fx.get_block(targets[targets.len() - 1]);
356+
switch.emit(&mut fx.bcx, discr, otherwise_block);
327357
}
328-
let otherwise_block = fx.get_block(targets[targets.len() - 1]);
329-
switch.emit(&mut fx.bcx, discr, otherwise_block);
330358
}
331359
TerminatorKind::Call {
332360
func,

src/optimize/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::prelude::*;
22

33
mod code_layout;
4+
pub(crate) mod peephole;
45
mod stack2reg;
56

67
pub(crate) fn optimize_function<'tcx>(

src/optimize/peephole.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//! Peephole optimizations that can be performed while creating clif ir.
2+
3+
use cranelift_codegen::ir::{
4+
condcodes::IntCC, types, InstBuilder, InstructionData, Opcode, Value, ValueDef,
5+
};
6+
use cranelift_frontend::FunctionBuilder;
7+
8+
/// If the given value was produced by a `bint` instruction, return it's input, otherwise return the
9+
/// given value.
10+
pub(crate) fn maybe_unwrap_bint(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value {
11+
if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) {
12+
match bcx.func.dfg[arg_inst] {
13+
InstructionData::Unary {
14+
opcode: Opcode::Bint,
15+
arg,
16+
} => arg,
17+
_ => arg,
18+
}
19+
} else {
20+
arg
21+
}
22+
}
23+
24+
/// If the given value was produced by the lowering of `Rvalue::Not` return the input and true,
25+
/// otherwise return the given value and false.
26+
pub(crate) fn maybe_unwrap_bool_not(bcx: &mut FunctionBuilder<'_>, arg: Value) -> (Value, bool) {
27+
if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) {
28+
match bcx.func.dfg[arg_inst] {
29+
// This is the lowering of `Rvalue::Not`
30+
InstructionData::IntCompareImm {
31+
opcode: Opcode::IcmpImm,
32+
cond: IntCC::Equal,
33+
arg,
34+
imm,
35+
} if imm.bits() == 0 => (arg, true),
36+
_ => (arg, false),
37+
}
38+
} else {
39+
(arg, false)
40+
}
41+
}
42+
43+
pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value {
44+
if bcx.func.dfg.value_type(arg).is_bool() {
45+
return arg;
46+
}
47+
48+
(|| {
49+
let arg_inst = if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) {
50+
arg_inst
51+
} else {
52+
return None;
53+
};
54+
55+
match bcx.func.dfg[arg_inst] {
56+
// This is the lowering of Rvalue::Not
57+
InstructionData::Load {
58+
opcode: Opcode::Load,
59+
arg: ptr,
60+
flags,
61+
offset,
62+
} => {
63+
// Using `load.i8 + uextend.i32` would legalize to `uload8 + ireduce.i8 +
64+
// uextend.i32`. Just `uload8` is much faster.
65+
match bcx.func.dfg.ctrl_typevar(arg_inst) {
66+
types::I8 => Some(bcx.ins().uload8(types::I32, flags, ptr, offset)),
67+
types::I16 => Some(bcx.ins().uload16(types::I32, flags, ptr, offset)),
68+
_ => None,
69+
}
70+
}
71+
_ => None,
72+
}
73+
})()
74+
.unwrap_or_else(|| {
75+
match bcx.func.dfg.value_type(arg) {
76+
types::I8 | types::I32 => {
77+
// WORKAROUND for brz.i8 and brnz.i8 not yet being implemented
78+
bcx.ins().uextend(types::I32, arg)
79+
}
80+
_ => arg,
81+
}
82+
})
83+
}

0 commit comments

Comments
 (0)