1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals 2; RUN: opt < %s -passes=simplifycfg -switch-to-lookup=true -keep-loops=false -S -mattr=+f | FileCheck %s 3; RUN: opt < %s -passes='simplifycfg<no-keep-loops;switch-to-lookup>' -S -mattr=+f | FileCheck %s 4target datalayout = "e-m:e-p:32:32-i64:64-n32-S128" 5target triple = "riscv32-unknown-elf" 6 7; A simple int-to-int selection switch. 8; It is dense enough to be replaced by table lookup. 9; The result is directly by a ret from an otherwise empty bb, 10; so we return early, directly from the lookup bb. 11 12;. 13; CHECK: @.str = private unnamed_addr constant [4 x i8] c"foo\00", align 1 14; CHECK: @.str1 = private unnamed_addr constant [4 x i8] c"bar\00", align 1 15; CHECK: @.str2 = private unnamed_addr constant [4 x i8] c"baz\00", align 1 16; CHECK: @.str3 = private unnamed_addr constant [4 x i8] c"qux\00", align 1 17; CHECK: @.str4 = private unnamed_addr constant [6 x i8] c"error\00", align 1 18; CHECK: @switch.table.f = private unnamed_addr constant [7 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 27, i32 62, i32 1], align 4 19; CHECK: @switch.table.char = private unnamed_addr constant [9 x i8] c"7{\00\FF\1B>\01!T", align 1 20; CHECK: @switch.table.h = private unnamed_addr constant [4 x float] [float 0x40091EB860000000, float 0x3FF3BE76C0000000, float 0x4012449BA0000000, float 0x4001AE1480000000], align 4 21; CHECK: @switch.table.foostring = private unnamed_addr constant [4 x ptr] [ptr @.str, ptr @.str1, ptr @.str2, ptr @.str3], align 4 22;. 23define i32 @f(i32 %c) { 24; CHECK-LABEL: @f( 25; CHECK-NEXT: entry: 26; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[C:%.*]], 42 27; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 7 28; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] 29; CHECK: switch.lookup: 30; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [7 x i32], ptr @switch.table.f, i32 0, i32 [[SWITCH_TABLEIDX]] 31; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4 32; CHECK-NEXT: br label [[RETURN]] 33; CHECK: return: 34; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 15, [[ENTRY:%.*]] ] 35; CHECK-NEXT: ret i32 [[RETVAL_0]] 36; 37entry: 38 switch i32 %c, label %sw.default [ 39 i32 42, label %return 40 i32 43, label %sw.bb1 41 i32 44, label %sw.bb2 42 i32 45, label %sw.bb3 43 i32 46, label %sw.bb4 44 i32 47, label %sw.bb5 45 i32 48, label %sw.bb6 46 ] 47 48sw.bb1: br label %return 49sw.bb2: br label %return 50sw.bb3: br label %return 51sw.bb4: br label %return 52sw.bb5: br label %return 53sw.bb6: br label %return 54sw.default: br label %return 55return: 56 %retval.0 = phi i32 [ 15, %sw.default ], [ 1, %sw.bb6 ], [ 62, %sw.bb5 ], [ 27, %sw.bb4 ], [ -1, %sw.bb3 ], [ 0, %sw.bb2 ], [ 123, %sw.bb1 ], [ 55, %entry ] 57 ret i32 %retval.0 58 59} 60 61; Same thing, but with i8's 62 63define i8 @char(i32 %c) { 64; CHECK-LABEL: @char( 65; CHECK-NEXT: entry: 66; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[C:%.*]], 42 67; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 9 68; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] 69; CHECK: switch.lookup: 70; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [9 x i8], ptr @switch.table.char, i32 0, i32 [[SWITCH_TABLEIDX]] 71; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i8, ptr [[SWITCH_GEP]], align 1 72; CHECK-NEXT: br label [[RETURN]] 73; CHECK: return: 74; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 15, [[ENTRY:%.*]] ] 75; CHECK-NEXT: ret i8 [[RETVAL_0]] 76; 77entry: 78 switch i32 %c, label %sw.default [ 79 i32 42, label %return 80 i32 43, label %sw.bb1 81 i32 44, label %sw.bb2 82 i32 45, label %sw.bb3 83 i32 46, label %sw.bb4 84 i32 47, label %sw.bb5 85 i32 48, label %sw.bb6 86 i32 49, label %sw.bb7 87 i32 50, label %sw.bb8 88 ] 89 90sw.bb1: br label %return 91sw.bb2: br label %return 92sw.bb3: br label %return 93sw.bb4: br label %return 94sw.bb5: br label %return 95sw.bb6: br label %return 96sw.bb7: br label %return 97sw.bb8: br label %return 98sw.default: br label %return 99return: 100 %retval.0 = phi i8 [ 15, %sw.default ], [ 84, %sw.bb8 ], [ 33, %sw.bb7 ], [ 1, %sw.bb6 ], [ 62, %sw.bb5 ], [ 27, %sw.bb4 ], [ -1, %sw.bb3 ], [ 0, %sw.bb2 ], [ 123, %sw.bb1 ], [ 55, %entry ] 101 ret i8 %retval.0 102 103} 104 105; A switch used to initialize two variables, an i8 and a float. 106 107declare void @dummy(i8 signext, float) 108define void @h(i32 %x) { 109; CHECK-LABEL: @h( 110; CHECK-NEXT: entry: 111; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 4 112; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[SW_EPILOG:%.*]] 113; CHECK: switch.lookup: 114; CHECK-NEXT: [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i32 [[X]], 8 115; CHECK-NEXT: [[SWITCH_DOWNSHIFT:%.*]] = lshr i32 89655594, [[SWITCH_SHIFTAMT]] 116; CHECK-NEXT: [[SWITCH_MASKED:%.*]] = trunc i32 [[SWITCH_DOWNSHIFT]] to i8 117; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x float], ptr @switch.table.h, i32 0, i32 [[X]] 118; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load float, ptr [[SWITCH_GEP]], align 4 119; CHECK-NEXT: br label [[SW_EPILOG]] 120; CHECK: sw.epilog: 121; CHECK-NEXT: [[A_0:%.*]] = phi i8 [ [[SWITCH_MASKED]], [[SWITCH_LOOKUP]] ], [ 7, [[ENTRY:%.*]] ] 122; CHECK-NEXT: [[B_0:%.*]] = phi float [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 0x4023FAE140000000, [[ENTRY]] ] 123; CHECK-NEXT: call void @dummy(i8 signext [[A_0]], float [[B_0]]) 124; CHECK-NEXT: ret void 125; 126entry: 127 switch i32 %x, label %sw.default [ 128 i32 0, label %sw.epilog 129 i32 1, label %sw.bb1 130 i32 2, label %sw.bb2 131 i32 3, label %sw.bb3 132 ] 133 134sw.bb1: br label %sw.epilog 135sw.bb2: br label %sw.epilog 136sw.bb3: br label %sw.epilog 137sw.default: br label %sw.epilog 138 139sw.epilog: 140 %a.0 = phi i8 [ 7, %sw.default ], [ 5, %sw.bb3 ], [ 88, %sw.bb2 ], [ 9, %sw.bb1 ], [ 42, %entry ] 141 %b.0 = phi float [ 0x4023FAE140000000, %sw.default ], [ 0x4001AE1480000000, %sw.bb3 ], [ 0x4012449BA0000000, %sw.bb2 ], [ 0x3FF3BE76C0000000, %sw.bb1 ], [ 0x40091EB860000000, %entry ] 142 call void @dummy(i8 signext %a.0, float %b.0) 143 ret void 144 145} 146 147 148; Switch used to return a string. 149 150@.str = private unnamed_addr constant [4 x i8] c"foo\00", align 1 151@.str1 = private unnamed_addr constant [4 x i8] c"bar\00", align 1 152@.str2 = private unnamed_addr constant [4 x i8] c"baz\00", align 1 153@.str3 = private unnamed_addr constant [4 x i8] c"qux\00", align 1 154@.str4 = private unnamed_addr constant [6 x i8] c"error\00", align 1 155 156define ptr @foostring(i32 %x) { 157; CHECK-LABEL: @foostring( 158; CHECK-NEXT: entry: 159; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 4 160; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] 161; CHECK: switch.lookup: 162; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x ptr], ptr @switch.table.foostring, i32 0, i32 [[X]] 163; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load ptr, ptr [[SWITCH_GEP]], align 4 164; CHECK-NEXT: br label [[RETURN]] 165; CHECK: return: 166; CHECK-NEXT: [[RETVAL_0:%.*]] = phi ptr [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ @.str4, [[ENTRY:%.*]] ] 167; CHECK-NEXT: ret ptr [[RETVAL_0]] 168; 169entry: 170 switch i32 %x, label %sw.default [ 171 i32 0, label %return 172 i32 1, label %sw.bb1 173 i32 2, label %sw.bb2 174 i32 3, label %sw.bb3 175 ] 176 177sw.bb1: br label %return 178sw.bb2: br label %return 179sw.bb3: br label %return 180sw.default: br label %return 181 182return: 183 %retval.0 = phi ptr [ @.str4, %sw.default ], 184 [ @.str3, %sw.bb3 ], 185 [ @.str2, %sw.bb2 ], 186 [ @.str1, %sw.bb1 ], 187 [ @.str, %entry ] 188 ret ptr %retval.0 189 190} 191;. 192; CHECK: attributes #[[ATTR0:[0-9]+]] = { "target-features"="+f" } 193;. 194