1
1
use super :: HashMap ;
2
2
use crate :: frontend:: FunctionBuilder ;
3
3
use alloc:: vec:: Vec ;
4
+ use core:: convert:: TryFrom ;
4
5
use cranelift_codegen:: ir:: condcodes:: IntCC ;
5
6
use cranelift_codegen:: ir:: * ;
6
7
use log:: debug;
7
8
8
- type EntryIndex = u64 ;
9
+ type EntryIndex = u128 ;
9
10
10
11
/// Unlike with `br_table`, `Switch` cases may be sparse or non-0-based.
11
12
/// They emit efficient code using branches, jump tables, or a combination of both.
@@ -152,11 +153,9 @@ impl Switch {
152
153
let left_block = bx. create_block ( ) ;
153
154
let right_block = bx. create_block ( ) ;
154
155
155
- let should_take_right_side = bx. ins ( ) . icmp_imm (
156
- IntCC :: UnsignedGreaterThanOrEqual ,
157
- val,
158
- right[ 0 ] . first_index as i64 ,
159
- ) ;
156
+ let first_index = right[ 0 ] . first_index ;
157
+ let should_take_right_side =
158
+ icmp_imm_u128 ( bx, IntCC :: UnsignedGreaterThanOrEqual , val, first_index) ;
160
159
bx. ins ( ) . brnz ( should_take_right_side, right_block, & [ ] ) ;
161
160
bx. ins ( ) . jump ( left_block, & [ ] ) ;
162
161
@@ -200,7 +199,7 @@ impl Switch {
200
199
}
201
200
( 1 , _) => {
202
201
ins_fallthrough_jump ( was_branch, bx) ;
203
- let is_good_val = bx . ins ( ) . icmp_imm ( IntCC :: Equal , val, first_index as i64 ) ;
202
+ let is_good_val = icmp_imm_u128 ( bx , IntCC :: Equal , val, first_index) ;
204
203
bx. ins ( ) . brnz ( is_good_val, blocks[ 0 ] , & [ ] ) ;
205
204
}
206
205
( _, 0 ) => {
@@ -217,11 +216,8 @@ impl Switch {
217
216
( _, _) => {
218
217
ins_fallthrough_jump ( was_branch, bx) ;
219
218
let jt_block = bx. create_block ( ) ;
220
- let is_good_val = bx. ins ( ) . icmp_imm (
221
- IntCC :: UnsignedGreaterThanOrEqual ,
222
- val,
223
- first_index as i64 ,
224
- ) ;
219
+ let is_good_val =
220
+ icmp_imm_u128 ( bx, IntCC :: UnsignedGreaterThanOrEqual , val, first_index) ;
225
221
bx. ins ( ) . brnz ( is_good_val, jt_block, & [ ] ) ;
226
222
bx. seal_block ( jt_block) ;
227
223
cases_and_jt_blocks. push ( ( first_index, jt_block, blocks) ) ;
@@ -241,6 +237,13 @@ impl Switch {
241
237
cases_and_jt_blocks : Vec < ( EntryIndex , Block , Vec < Block > ) > ,
242
238
) {
243
239
for ( first_index, jt_block, blocks) in cases_and_jt_blocks. into_iter ( ) . rev ( ) {
240
+ // There are currently no 128bit systems supported by rustc, but once we do ensure that
241
+ // we don't silently ignore a part of the jump table for 128bit integers on 128bit systems.
242
+ assert ! (
243
+ u64 :: try_from( blocks. len( ) ) . is_ok( ) ,
244
+ "Jump tables bigger than 2^64-1 are not yet supported"
245
+ ) ;
246
+
244
247
let mut jt_data = JumpTableData :: new ( ) ;
245
248
for block in blocks {
246
249
jt_data. push_entry ( block) ;
@@ -251,8 +254,33 @@ impl Switch {
251
254
let discr = if first_index == 0 {
252
255
val
253
256
} else {
254
- bx. ins ( ) . iadd_imm ( val, ( first_index as i64 ) . wrapping_neg ( ) )
257
+ if let Ok ( first_index) = u64:: try_from ( first_index) {
258
+ bx. ins ( ) . iadd_imm ( val, ( first_index as i64 ) . wrapping_neg ( ) )
259
+ } else {
260
+ let ( lsb, msb) = ( first_index as u64 , ( first_index >> 64 ) as u64 ) ;
261
+ let lsb = bx. ins ( ) . iconst ( types:: I64 , lsb as i64 ) ;
262
+ let msb = bx. ins ( ) . iconst ( types:: I64 , msb as i64 ) ;
263
+ let index = bx. ins ( ) . iconcat ( lsb, msb) ;
264
+ bx. ins ( ) . isub ( val, index)
265
+ }
266
+ } ;
267
+
268
+ let discr = if bx. func . dfg . value_type ( discr) . bits ( ) > 64 {
269
+ // Check for overflow of cast to u64.
270
+ let new_block = bx. create_block ( ) ;
271
+ let bigger_than_u64 =
272
+ bx. ins ( )
273
+ . icmp_imm ( IntCC :: UnsignedGreaterThan , discr, u64:: max_value ( ) as i64 ) ;
274
+ bx. ins ( ) . brnz ( bigger_than_u64, otherwise, & [ ] ) ;
275
+ bx. ins ( ) . jump ( new_block, & [ ] ) ;
276
+ bx. switch_to_block ( new_block) ;
277
+
278
+ // Cast to u64, as br_table is not implemented for integers bigger than 64bits.
279
+ bx. ins ( ) . ireduce ( types:: I64 , discr)
280
+ } else {
281
+ discr
255
282
} ;
283
+
256
284
bx. ins ( ) . br_table ( discr, otherwise, jump_table) ;
257
285
}
258
286
}
@@ -278,6 +306,18 @@ impl Switch {
278
306
}
279
307
}
280
308
309
+ fn icmp_imm_u128 ( bx : & mut FunctionBuilder , cond : IntCC , x : Value , y : u128 ) -> Value {
310
+ if let Ok ( index) = u64:: try_from ( y) {
311
+ bx. ins ( ) . icmp_imm ( cond, x, index as i64 )
312
+ } else {
313
+ let ( lsb, msb) = ( y as u64 , ( y >> 64 ) as u64 ) ;
314
+ let lsb = bx. ins ( ) . iconst ( types:: I64 , lsb as i64 ) ;
315
+ let msb = bx. ins ( ) . iconst ( types:: I64 , msb as i64 ) ;
316
+ let index = bx. ins ( ) . iconcat ( lsb, msb) ;
317
+ bx. ins ( ) . icmp ( cond, x, index)
318
+ }
319
+ }
320
+
281
321
/// This represents a contiguous range of cases to switch on.
282
322
///
283
323
/// For example 10 => block1, 11 => block2, 12 => block7 will be represented as:
@@ -440,7 +480,7 @@ block10:
440
480
441
481
#[ test]
442
482
fn switch_min_index_value ( ) {
443
- let func = setup ! ( 0 , [ :: core:: i64 :: MIN as u64 , 1 , ] ) ;
483
+ let func = setup ! ( 0 , [ :: core:: i64 :: MIN as u64 as u128 , 1 , ] ) ;
444
484
assert_eq ! (
445
485
func,
446
486
"block0:
@@ -459,7 +499,7 @@ block3:
459
499
460
500
#[ test]
461
501
fn switch_max_index_value ( ) {
462
- let func = setup ! ( 0 , [ :: core:: i64 :: MAX as u64 , 1 , ] ) ;
502
+ let func = setup ! ( 0 , [ :: core:: i64 :: MAX as u64 as u128 , 1 , ] ) ;
463
503
assert_eq ! (
464
504
func,
465
505
"block0:
@@ -478,7 +518,7 @@ block3:
478
518
479
519
#[ test]
480
520
fn switch_optimal_codegen ( ) {
481
- let func = setup ! ( 0 , [ -1i64 as u64 , 0 , 1 , ] ) ;
521
+ let func = setup ! ( 0 , [ -1i64 as u64 as u128 , 0 , 1 , ] ) ;
482
522
assert_eq ! (
483
523
func,
484
524
" jt0 = jump_table [block2, block3]
@@ -530,4 +570,45 @@ block4:
530
570
531
571
builder. finalize ( ) ; // Will panic if some blocks are not sealed
532
572
}
573
+
574
+ #[ test]
575
+ fn switch_128bit ( ) {
576
+ let mut func = Function :: new ( ) ;
577
+ let mut func_ctx = FunctionBuilderContext :: new ( ) ;
578
+ {
579
+ let mut bx = FunctionBuilder :: new ( & mut func, & mut func_ctx) ;
580
+ let block0 = bx. create_block ( ) ;
581
+ bx. switch_to_block ( block0) ;
582
+ let val = bx. ins ( ) . iconst ( types:: I128 , 0 ) ;
583
+ let mut switch = Switch :: new ( ) ;
584
+ let block1 = bx. create_block ( ) ;
585
+ switch. set_entry ( 1 , block1) ;
586
+ let block2 = bx. create_block ( ) ;
587
+ switch. set_entry ( 0 , block2) ;
588
+ let block3 = bx. create_block ( ) ;
589
+ switch. emit ( & mut bx, val, block3) ;
590
+ }
591
+ let func = func
592
+ . to_string ( )
593
+ . trim_start_matches ( "function u0:0() fast {\n " )
594
+ . trim_end_matches ( "\n }\n " )
595
+ . to_string ( ) ;
596
+ assert_eq ! (
597
+ func,
598
+ " jt0 = jump_table [block2, block1]
599
+
600
+ block0:
601
+ v0 = iconst.i128 0
602
+ jump block4
603
+
604
+ block4:
605
+ v1 = icmp_imm.i128 ugt v0, -1
606
+ brnz v1, block3
607
+ jump block5
608
+
609
+ block5:
610
+ v2 = ireduce.i64 v0
611
+ br_table v2, block3, jt0"
612
+ ) ;
613
+ }
533
614
}
0 commit comments