Fix an assertion failure with an empty Switch (#5650)

Fix an error introduced in #5644, where an unsigned subtraction from zero was possible with an empty Switch structure. Additionally, missing the empty case caused us to not emit a branch to the default block. This PR fixes the issue by detecting the empty Switch case early, and emitting a jump.
This commit is contained in:
Trevor Elliott
2023-01-27 17:46:11 -08:00
committed by GitHub
parent ffbcc67eb3
commit 20a216923b

View File

@@ -114,6 +114,12 @@ impl Switch {
otherwise: Block, otherwise: Block,
contiguous_case_ranges: &'a [ContiguousCaseRange], contiguous_case_ranges: &'a [ContiguousCaseRange],
) { ) {
// If no switch cases were added to begin with, we can just emit `jump otherwise`.
if contiguous_case_ranges.is_empty() {
bx.ins().jump(otherwise, &[]);
return;
}
// Avoid allocation in the common case // Avoid allocation in the common case
if contiguous_case_ranges.len() <= 3 { if contiguous_case_ranges.len() <= 3 {
Self::build_search_branches(bx, val, otherwise, contiguous_case_ranges); Self::build_search_branches(bx, val, otherwise, contiguous_case_ranges);
@@ -159,9 +165,8 @@ impl Switch {
otherwise: Block, otherwise: Block,
contiguous_case_ranges: &'a [ContiguousCaseRange], contiguous_case_ranges: &'a [ContiguousCaseRange],
) { ) {
let last_ix = contiguous_case_ranges.len() - 1; for (ix, range) in contiguous_case_ranges.iter().enumerate().rev() {
for (ix, range) in contiguous_case_ranges.iter().rev().enumerate() { let alternate = if ix == 0 {
let alternate = if ix == last_ix {
otherwise otherwise
} else { } else {
bx.create_block() bx.create_block()
@@ -343,6 +348,7 @@ mod tests {
let block = bx.create_block(); let block = bx.create_block();
bx.switch_to_block(block); bx.switch_to_block(block);
let val = bx.ins().iconst(types::I8, 0); let val = bx.ins().iconst(types::I8, 0);
#[allow(unused_mut)]
let mut switch = Switch::new(); let mut switch = Switch::new();
$( $(
let block = bx.create_block(); let block = bx.create_block();
@@ -360,18 +366,28 @@ mod tests {
macro_rules! assert_eq_output { macro_rules! assert_eq_output {
($actual:ident, $expected:literal) => { ($actual:ident, $expected:literal) => {
if $actual != $expected { assert_eq!(
assert!( $actual,
false, $expected,
"\n{}", "\n{}",
similar::TextDiff::from_lines($expected, &$actual) similar::TextDiff::from_lines($expected, &$actual)
.unified_diff() .unified_diff()
.header("expected", "actual") .header("expected", "actual")
); )
}
}; };
} }
#[test]
fn switch_empty() {
let func = setup!(42, []);
assert_eq_output!(
func,
"block0:
v0 = iconst.i8 0
jump block42"
);
}
#[test] #[test]
fn switch_zero() { fn switch_zero() {
let func = setup!(0, [0,]); let func = setup!(0, [0,]);