| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -jump-table-to-switch-size-threshold=0 -verify-dom-info -S | FileCheck %s --check-prefix=THRESHOLD-0 | ||
|
|
||
| @func_array = constant [2 x ptr] [ptr @func0, ptr @func1] | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @func1() { | ||
| ret i32 2 | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = call i32 @func1() | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[TMP1]], [[CALL_0]] ], [ [[TMP2]], [[CALL_1]] ] | ||
| ; CHECK-NEXT: ret i32 [[TMP3]] | ||
| ; | ||
| ; THRESHOLD-0-LABEL: define i32 @function_with_jump_table( | ||
| ; THRESHOLD-0-SAME: i32 [[INDEX:%.*]]) { | ||
| ; THRESHOLD-0-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; THRESHOLD-0-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]]() | ||
| ; THRESHOLD-0-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| %result = call i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|
||
| define i32 @basic_block_splitted_twice(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @basic_block_splitted_twice( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR1:%.*]] = load ptr, ptr [[GEP1]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = call i32 @func1() | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[TMP1]], [[CALL_0]] ], [ [[TMP2]], [[CALL_1]] ] | ||
| ; CHECK-NEXT: [[GEP2:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR2:%.*]] = load ptr, ptr [[GEP2]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE1:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_02:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_13:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable1: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.02: | ||
| ; CHECK-NEXT: [[TMP4:%.*]] = call i32 @func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL_TAIL:%.*]] | ||
| ; CHECK: call.13: | ||
| ; CHECK-NEXT: [[TMP5:%.*]] = call i32 @func1() | ||
| ; CHECK-NEXT: br label [[DOTTAIL_TAIL]] | ||
| ; CHECK: .tail.tail: | ||
| ; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[CALL_02]] ], [ [[TMP5]], [[CALL_13]] ] | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = add i32 [[TMP3]], [[TMP6]] | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| ; THRESHOLD-0-LABEL: define i32 @basic_block_splitted_twice( | ||
| ; THRESHOLD-0-SAME: i32 [[INDEX:%.*]]) { | ||
| ; THRESHOLD-0-NEXT: [[GEP1:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR1:%.*]] = load ptr, ptr [[GEP1]], align 8 | ||
| ; THRESHOLD-0-NEXT: [[RESULT1:%.*]] = call i32 [[FUNC_PTR1]]() | ||
| ; THRESHOLD-0-NEXT: [[GEP2:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR2:%.*]] = load ptr, ptr [[GEP2]], align 8 | ||
| ; THRESHOLD-0-NEXT: [[RESULT2:%.*]] = call i32 [[FUNC_PTR2]]() | ||
| ; THRESHOLD-0-NEXT: [[RESULT:%.*]] = add i32 [[RESULT1]], [[RESULT2]] | ||
| ; THRESHOLD-0-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep1 = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr1 = load ptr, ptr %gep1 | ||
| %result1 = call i32 %func_ptr1() | ||
| %gep2 = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr2 = load ptr, ptr %gep2 | ||
| %result2 = call i32 %func_ptr2() | ||
| %result = add i32 %result1, %result2 | ||
| ret i32 %result | ||
| } | ||
|
|
||
| define void @void_func0() { | ||
| ret void | ||
| } | ||
|
|
||
| define void @void_func1() { | ||
| ret void | ||
| } | ||
|
|
||
| @void_func_array = constant [2 x ptr] [ptr @void_func0, ptr @void_func1] | ||
|
|
||
| define void @void_function_with_jump_table(i32 %index) { | ||
| ; CHECK-LABEL: define void @void_function_with_jump_table( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: call void @void_func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: call void @void_func1() | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: ret void | ||
| ; | ||
| ; THRESHOLD-0-LABEL: define void @void_function_with_jump_table( | ||
| ; THRESHOLD-0-SAME: i32 [[INDEX:%.*]]) { | ||
| ; THRESHOLD-0-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; THRESHOLD-0-NEXT: call void [[FUNC_PTR]]() | ||
| ; THRESHOLD-0-NEXT: ret void | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| call void %func_ptr() | ||
| ret void | ||
| } | ||
|
|
||
| define void @void_function_with_jump_table_and_call_site_attr(i32 %index) { | ||
| ; CHECK-LABEL: define void @void_function_with_jump_table_and_call_site_attr( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: call void @void_func0() #[[ATTR0:[0-9]+]] | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: call void @void_func1() #[[ATTR0]] | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: ret void | ||
| ; | ||
| ; THRESHOLD-0-LABEL: define void @void_function_with_jump_table_and_call_site_attr( | ||
| ; THRESHOLD-0-SAME: i32 [[INDEX:%.*]]) { | ||
| ; THRESHOLD-0-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; THRESHOLD-0-NEXT: call void [[FUNC_PTR]]() #[[ATTR0:[0-9]+]] | ||
| ; THRESHOLD-0-NEXT: ret void | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| call void %func_ptr() nounwind | ||
| ret void | ||
| } | ||
|
|
||
|
|
||
| define i32 @func0_addrspace_42() addrspace(42) { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @func1_addrspace_42() addrspace(42) { | ||
| ret i32 2 | ||
| } | ||
|
|
||
| @func_array_addrspace_42 = addrspace(42) constant [2 x ptr addrspace(42)] [ptr addrspace(42) @func0_addrspace_42, ptr addrspace(42) @func1_addrspace_42] | ||
|
|
||
| define i32 @function_with_jump_table_addrspace_42(i32 %index) addrspace(42) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_addrspace_42( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) addrspace(42) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr addrspace(42)], ptr addrspace(42) @func_array_addrspace_42, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr addrspace(42), ptr addrspace(42) [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call addrspace(42) i32 @func0_addrspace_42() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = call addrspace(42) i32 @func1_addrspace_42() | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[TMP1]], [[CALL_0]] ], [ [[TMP2]], [[CALL_1]] ] | ||
| ; CHECK-NEXT: ret i32 [[TMP3]] | ||
| ; | ||
| ; THRESHOLD-0-LABEL: define i32 @function_with_jump_table_addrspace_42( | ||
| ; THRESHOLD-0-SAME: i32 [[INDEX:%.*]]) addrspace(42) { | ||
| ; THRESHOLD-0-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr addrspace(42)], ptr addrspace(42) @func_array_addrspace_42, i32 0, i32 [[INDEX]] | ||
| ; THRESHOLD-0-NEXT: [[FUNC_PTR:%.*]] = load ptr addrspace(42), ptr addrspace(42) [[GEP]], align 8 | ||
| ; THRESHOLD-0-NEXT: [[RESULT:%.*]] = call addrspace(42) i32 [[FUNC_PTR]]() | ||
| ; THRESHOLD-0-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr addrspace(42)], ptr addrspace(42) @func_array_addrspace_42, i32 0, i32 %index | ||
| %func_ptr = load ptr addrspace(42), ptr addrspace(42) %gep, align 8 | ||
| %result = call addrspace(42) i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -jump-table-to-switch-function-size-threshold=1 -verify-dom-info -S | FileCheck %s | ||
|
|
||
| @func_array0 = constant [2 x ptr] [ptr @func0, ptr @large_func] | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @large_func() { | ||
| %x = add i32 1, 2 | ||
| ret i32 %x | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table_with_large_func(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_with_large_func( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]]() | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = call i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -pass-remarks=jump-table-to-switch -S -o /dev/null 2>&1 | FileCheck %s | ||
|
|
||
| ; CHECK: remark: /tmp/tmp.cc:2:20: expanded indirect call into switch | ||
|
|
||
| @func_array = constant [2 x ptr] [ptr @func0, ptr @func1] | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @func1() { | ||
| ret i32 2 | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table(i32 %index) { | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| %result = call i32 %func_ptr(), !dbg !8 | ||
| ret i32 %result | ||
| } | ||
|
|
||
| !llvm.dbg.cu = !{!0} | ||
| !llvm.module.flags = !{!3, !4} | ||
| !llvm.ident = !{!5} | ||
|
|
||
| !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 18.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: NoDebug, enums: !2) | ||
| !1 = !DIFile(filename: "/tmp/tmp.cc", directory: "/tmp") | ||
| !2 = !{} | ||
| !3 = !{i32 2, !"Debug Info Version", i32 3} | ||
| !4 = !{i32 1, !"PIC Level", i32 2} | ||
| !5 = !{!"clang version 18.0.0 "} | ||
| !6 = distinct !DISubprogram(name: "success", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2) | ||
| !7 = !DISubroutineType(types: !2) | ||
| !8 = !DILocation(line: 2, column: 20, scope: !6) | ||
| !9 = !DILocation(line: 2, column: 21, scope: !6) | ||
| !10 = !DILocation(line: 2, column: 22, scope: !6) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s | ||
|
|
||
| @func_array0 = constant [2 x ptr] [ptr @func0, ptr @declared_only_func1] | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| declare i32 @declared_only_func1() | ||
|
|
||
| define i32 @function_with_jump_table_with_a_declared_only_func(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_with_a_declared_only_func( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]]() | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = call i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|
||
| declare i32 @__gxx_personality_v0(...) | ||
|
|
||
| define i32 @function_with_jump_table_invoke(i32 %index) personality ptr @__gxx_personality_v0 { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_invoke( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) personality ptr @__gxx_personality_v0 { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = invoke i32 [[FUNC_PTR]]() | ||
| ; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTIONAL:%.*]] | ||
| ; CHECK: normal: | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; CHECK: exceptional: | ||
| ; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad { ptr, i32 } | ||
| ; CHECK-NEXT: catch ptr null | ||
| ; CHECK-NEXT: resume { ptr, i32 } [[LANDING_PAD]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = invoke i32 %func_ptr() to label %normal unwind label %exceptional | ||
| normal: | ||
| ret i32 %result | ||
| exceptional: | ||
| %landing_pad = landingpad { ptr, i32 } catch ptr null | ||
| resume { ptr, i32 } %landing_pad | ||
| } | ||
|
|
||
| @func_array1 = constant [1 x ptr] [ptr @func2] | ||
|
|
||
| define i32 @func2(i32 %arg) { | ||
| ret i32 %arg | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table_musttail_call(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_musttail_call( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = musttail call i32 [[FUNC_PTR]](i32 [[INDEX]]) | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = musttail call i32 %func_ptr(i32 %index) | ||
| ret i32 %result | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table_and_volatile_load(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_and_volatile_load( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load volatile ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]](i32 [[INDEX]]) | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 %index | ||
| %func_ptr = load volatile ptr, ptr %gep, align 8 | ||
| %result = call i32 %func_ptr(i32 %index) | ||
| ret i32 %result | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table_and_atomic_load(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table_and_atomic_load( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load atomic ptr, ptr [[GEP]] monotonic, align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]](i32 [[INDEX]]) | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 %index | ||
| %func_ptr = load atomic ptr, ptr %gep monotonic, align 8 | ||
| %result = call i32 %func_ptr(i32 %index) | ||
| ret i32 %result | ||
| } | ||
|
|
||
| @func_array2 = global [1 x ptr] [ptr @func2] | ||
|
|
||
| define i32 @function_with_nonconstant_jump_table(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_nonconstant_jump_table( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [1 x ptr], ptr @func_array2, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]](i32 [[INDEX]]) | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [1 x ptr], ptr @func_array2, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = call i32 %func_ptr(i32 %index) | ||
| ret i32 %result | ||
| } | ||
|
|
||
| @func_array3 = weak constant [1 x ptr] [ptr @func2] | ||
|
|
||
| define i32 @function_with_constant_weak_jump_table(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_constant_weak_jump_table( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [1 x ptr], ptr @func_array3, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: [[RESULT:%.*]] = call i32 [[FUNC_PTR]](i32 [[INDEX]]) | ||
| ; CHECK-NEXT: ret i32 [[RESULT]] | ||
| ; | ||
| %gep = getelementptr inbounds [1 x ptr], ptr @func_array3, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep, align 8 | ||
| %result = call i32 %func_ptr(i32 %index) | ||
| ret i32 %result | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s | ||
|
|
||
| @func_array = constant [2 x ptr] [ptr @func0, ptr @func1] | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @func1() { | ||
| ret i32 2 | ||
| } | ||
|
|
||
| define i32 @check_stride(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @check_stride( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x { ptr, ptr }], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[CALL_0]] ] | ||
| ; CHECK-NEXT: ret i32 [[TMP2]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x { ptr, ptr }], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| %result = call i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 | ||
| ; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s | ||
|
|
||
| %"struct_ty" = type { [2 x ptr] } | ||
|
|
||
| @func_array = constant %"struct_ty" { [2 x ptr] [ptr @func0, ptr @func1] } | ||
|
|
||
| define i32 @func0() { | ||
| ret i32 1 | ||
| } | ||
|
|
||
| define i32 @func1() { | ||
| ret i32 2 | ||
| } | ||
|
|
||
| define i32 @function_with_jump_table(i32 %index) { | ||
| ; CHECK-LABEL: define i32 @function_with_jump_table( | ||
| ; CHECK-SAME: i32 [[INDEX:%.*]]) { | ||
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 [[INDEX]] | ||
| ; CHECK-NEXT: [[FUNC_PTR:%.*]] = load ptr, ptr [[GEP]], align 8 | ||
| ; CHECK-NEXT: switch i32 [[INDEX]], label [[DEFAULT_SWITCH_CASE_UNREACHABLE:%.*]] [ | ||
| ; CHECK-NEXT: i32 0, label [[CALL_0:%.*]] | ||
| ; CHECK-NEXT: i32 1, label [[CALL_1:%.*]] | ||
| ; CHECK-NEXT: ] | ||
| ; CHECK: default.switch.case.unreachable: | ||
| ; CHECK-NEXT: unreachable | ||
| ; CHECK: call.0: | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @func0() | ||
| ; CHECK-NEXT: br label [[DOTTAIL:%.*]] | ||
| ; CHECK: call.1: | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = call i32 @func1() | ||
| ; CHECK-NEXT: br label [[DOTTAIL]] | ||
| ; CHECK: .tail: | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[TMP1]], [[CALL_0]] ], [ [[TMP2]], [[CALL_1]] ] | ||
| ; CHECK-NEXT: ret i32 [[TMP3]] | ||
| ; | ||
| %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index | ||
| %func_ptr = load ptr, ptr %gep | ||
| %result = call i32 %func_ptr() | ||
| ret i32 %result | ||
| } | ||
|
|