
This change adds support for function linkage and visibility and related attributes. Most of the test changes are generalizations to allow 'dso_local' to be accepted where we aren't specifically testing for it. Some tests based on CIR inputs have been updated to add 'private' to function declarations where required by newly supported interfaces. The dso-local.c test has been updated to add specific tests for dso_local being set correctly, and a new test, func-linkage.cpp tests other linkage settings. This change sets `comdat` correctly in CIR, but it is not yet applied to functions when lowering to LLVM IR. That will be handled in a later change.
319 lines
10 KiB
Plaintext
319 lines
10 KiB
Plaintext
// RUN: cir-opt %s -cir-flatten-cfg -o - | FileCheck %s
|
|
|
|
!s8i = !cir.int<s, 8>
|
|
!s32i = !cir.int<s, 32>
|
|
!s64i = !cir.int<s, 64>
|
|
|
|
module {
|
|
cir.func @shouldFlatSwitchWithDefault(%arg0: !s8i) {
|
|
cir.switch (%arg0 : !s8i) {
|
|
cir.case (equal, [#cir.int<1> : !s8i]) {
|
|
cir.break
|
|
}
|
|
cir.case (default, []) {
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatSwitchWithDefault(%arg0: !s8i) {
|
|
// CHECK: cir.switch.flat %arg0 : !s8i, ^bb[[#DEFAULT:]] [
|
|
// CHECK: 1: ^bb[[#CASE1:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#CASE1]]:
|
|
// CHECK: cir.br ^bb[[#EXIT:]]
|
|
// CHECK: ^bb[[#DEFAULT]]:
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
cir.func @shouldFlatSwitchWithoutDefault(%arg0: !s32i) {
|
|
cir.switch (%arg0 : !s32i) {
|
|
cir.case (equal, [#cir.int<1> : !s32i]) {
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatSwitchWithoutDefault(%arg0: !s32i) {
|
|
// CHECK: cir.switch.flat %arg0 : !s32i, ^bb[[#EXIT:]] [
|
|
// CHECK: 1: ^bb[[#CASE1:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#CASE1]]:
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
|
|
cir.func @shouldFlatSwitchWithImplicitFallthrough(%arg0: !s64i) {
|
|
cir.switch (%arg0 : !s64i) {
|
|
cir.case (anyof, [#cir.int<1> : !s64i, #cir.int<2> : !s64i]) {
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatSwitchWithImplicitFallthrough(%arg0: !s64i) {
|
|
// CHECK: cir.switch.flat %arg0 : !s64i, ^bb[[#EXIT:]] [
|
|
// CHECK: 1: ^bb[[#CASE1N2:]],
|
|
// CHECK: 2: ^bb[[#CASE1N2]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#CASE1N2]]:
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
|
|
|
|
cir.func @shouldFlatSwitchWithExplicitFallthrough(%arg0: !s64i) {
|
|
cir.switch (%arg0 : !s64i) {
|
|
cir.case (equal, [#cir.int<1> : !s64i]) { // case 1 has its own region
|
|
cir.yield // fallthrough to case 2
|
|
}
|
|
cir.case (equal, [#cir.int<2> : !s64i]) {
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatSwitchWithExplicitFallthrough(%arg0: !s64i) {
|
|
// CHECK: cir.switch.flat %arg0 : !s64i, ^bb[[#EXIT:]] [
|
|
// CHECK: 1: ^bb[[#CASE1:]],
|
|
// CHECK: 2: ^bb[[#CASE2:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#CASE1]]:
|
|
// CHECK: cir.br ^bb[[#CASE2]]
|
|
// CHECK: ^bb[[#CASE2]]:
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
cir.func @shouldFlatSwitchWithFallthroughToExit(%arg0: !s64i) {
|
|
cir.switch (%arg0 : !s64i) {
|
|
cir.case (equal, [#cir.int<1> : !s64i]) {
|
|
cir.yield // fallthrough to exit
|
|
}
|
|
cir.yield
|
|
}
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatSwitchWithFallthroughToExit(%arg0: !s64i) {
|
|
// CHECK: cir.switch.flat %arg0 : !s64i, ^bb[[#EXIT:]] [
|
|
// CHECK: 1: ^bb[[#CASE1:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#CASE1]]:
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
cir.func @shouldDropEmptySwitch(%arg0: !s64i) {
|
|
cir.switch (%arg0 : !s64i) {
|
|
cir.yield
|
|
}
|
|
// CHECK-NOT: llvm.switch
|
|
cir.return
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldDropEmptySwitch(%arg0: !s64i)
|
|
// CHECK-NOT: cir.switch.flat
|
|
|
|
|
|
cir.func @shouldFlatMultiBlockCase(%arg0: !s32i) {
|
|
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] {alignment = 4 : i64}
|
|
cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i>
|
|
cir.scope {
|
|
%1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
|
cir.switch (%1 : !s32i) {
|
|
cir.case (equal, [#cir.int<3> : !s32i]) {
|
|
cir.return
|
|
^bb1: // no predecessors
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
}
|
|
cir.return
|
|
}
|
|
|
|
// CHECK: cir.func{{.*}} @shouldFlatMultiBlockCase(%arg0: !s32i) {
|
|
// CHECK: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] {alignment = 4 : i64}
|
|
// CHECK: cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i>
|
|
// CHECK: cir.br ^bb1
|
|
// CHECK: ^bb1: // pred: ^bb0
|
|
// CHECK: %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
|
// CHECK: cir.switch.flat %1 : !s32i, ^bb[[#DEFAULT:]] [
|
|
// CHECK: 3: ^bb[[#BB1:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#BB1]]:
|
|
// CHECK: cir.return
|
|
// CHECK: ^bb[[#DEFAULT]]:
|
|
// CHECK: cir.br ^bb[[#RET_BB:]]
|
|
// CHECK: ^bb[[#RET_BB]]: // pred: ^bb[[#DEFAULT]]
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
|
|
cir.func @shouldFlatNestedBreak(%arg0: !s32i, %arg1: !s32i) -> !s32i {
|
|
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
|
|
%1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init] {alignment = 4 : i64}
|
|
%2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
|
|
cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i>
|
|
cir.store %arg1, %1 : !s32i, !cir.ptr<!s32i>
|
|
cir.scope {
|
|
%5 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
|
cir.switch (%5 : !s32i) {
|
|
cir.case (equal, [#cir.int<0> : !s32i]) {
|
|
cir.scope {
|
|
%6 = cir.load %1 : !cir.ptr<!s32i>, !s32i
|
|
%7 = cir.const #cir.int<0> : !s32i
|
|
%8 = cir.cmp(ge, %6, %7) : !s32i, !cir.bool
|
|
cir.if %8 {
|
|
cir.break
|
|
}
|
|
}
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
}
|
|
%3 = cir.const #cir.int<3> : !s32i
|
|
cir.store %3, %2 : !s32i, !cir.ptr<!s32i>
|
|
%4 = cir.load %2 : !cir.ptr<!s32i>, !s32i
|
|
cir.return %4 : !s32i
|
|
}
|
|
// CHECK: cir.func{{.*}} @shouldFlatNestedBreak(%arg0: !s32i, %arg1: !s32i) -> !s32i {
|
|
// CHECK: cir.switch.flat %[[COND:.*]] : !s32i, ^bb[[#DEFAULT_BB:]] [
|
|
// CHECK: 0: ^bb[[#BB1:]]
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#BB1]]:
|
|
// CHECK: cir.br ^bb[[#COND_BB:]]
|
|
// CHECK: ^bb[[#COND_BB]]:
|
|
// CHECK: cir.brcond {{%.*}} ^bb[[#TRUE_BB:]], ^bb[[#FALSE_BB:]]
|
|
// CHECK: ^bb[[#TRUE_BB]]:
|
|
// CHECK: cir.br ^bb[[#DEFAULT_BB]]
|
|
// CHECK: ^bb[[#FALSE_BB]]:
|
|
// CHECK: cir.br ^bb[[#PRED_BB:]]
|
|
// CHECK: ^bb[[#PRED_BB]]:
|
|
// CHECK: cir.br ^bb[[#DEFAULT_BB]]
|
|
// CHECK: ^bb[[#DEFAULT_BB]]:
|
|
// CHECK: cir.br ^bb[[#RET_BB:]]
|
|
// CHECK: ^bb[[#RET_BB]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
|
|
cir.func @flatCaseRange(%arg0: !s32i) -> !s32i {
|
|
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
|
|
%1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
|
|
%2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init] {alignment = 4 : i64}
|
|
cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i>
|
|
%3 = cir.const #cir.int<0> : !s32i
|
|
cir.store %3, %2 : !s32i, !cir.ptr<!s32i>
|
|
cir.scope {
|
|
%6 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
|
cir.switch (%6 : !s32i) {
|
|
cir.case (equal, [#cir.int<-100> : !s32i]) {
|
|
%7 = cir.const #cir.int<1> : !s32i
|
|
cir.store %7, %2 : !s32i, !cir.ptr<!s32i>
|
|
cir.break
|
|
}
|
|
cir.case (range, [#cir.int<1> : !s32i, #cir.int<100> : !s32i]) {
|
|
%7 = cir.const #cir.int<2> : !s32i
|
|
cir.store %7, %2 : !s32i, !cir.ptr<!s32i>
|
|
cir.break
|
|
}
|
|
cir.case (default, []) {
|
|
%7 = cir.const #cir.int<3> : !s32i
|
|
cir.store %7, %2 : !s32i, !cir.ptr<!s32i>
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
}
|
|
%4 = cir.load %2 : !cir.ptr<!s32i>, !s32i
|
|
cir.store %4, %1 : !s32i, !cir.ptr<!s32i>
|
|
%5 = cir.load %1 : !cir.ptr<!s32i>, !s32i
|
|
cir.return %5 : !s32i
|
|
}
|
|
// CHECK: cir.func{{.*}} @flatCaseRange(%arg0: !s32i) -> !s32i {
|
|
// CHECK: cir.switch.flat %[[X:[0-9]+]] : !s32i, ^[[JUDGE_RANGE:bb[0-9]+]] [
|
|
// CHECK-NEXT: -100: ^[[CASE_EQUAL:bb[0-9]+]]
|
|
// CHECK-NEXT: ]
|
|
// CHECK-NEXT: ^[[UNRACHABLE_BB:.+]]: // no predecessors
|
|
// CHECK-NEXT: cir.br ^[[CASE_EQUAL]]
|
|
// CHECK-NEXT: ^[[CASE_EQUAL]]:
|
|
// CHECK-NEXT: cir.int<1>
|
|
// CHECK-NEXT: cir.store
|
|
// CHECK-NEXT: cir.br ^[[EPILOG:bb[0-9]+]]
|
|
// CHECK-NEXT: ^[[CASE_RANGE:bb[0-9]+]]:
|
|
// CHECK-NEXT: cir.int<2>
|
|
// CHECK-NEXT: cir.store
|
|
// CHECK-NEXT: cir.br ^[[EPILOG]]
|
|
// CHECK-NEXT: ^[[JUDGE_RANGE]]:
|
|
// CHECK-NEXT: %[[RANGE:[0-9]+]] = cir.const #cir.int<99>
|
|
// CHECK-NEXT: %[[LOWER_BOUND:[0-9]+]] = cir.const #cir.int<1>
|
|
// CHECK-NEXT: %[[DIFF:[0-9]+]] = cir.binop(sub, %[[X]], %[[LOWER_BOUND]])
|
|
// CHECK-NEXT: %[[U_DIFF:[0-9]+]] = cir.cast(integral, %[[DIFF]] : !s32i), !u32i
|
|
// CHECK-NEXT: %[[U_RANGE:[0-9]+]] = cir.cast(integral, %[[RANGE]] : !s32i), !u32i
|
|
// CHECK-NEXT: %[[CMP_RESULT:[0-9]+]] = cir.cmp(le, %[[U_DIFF]], %[[U_RANGE]])
|
|
// CHECK-NEXT: cir.brcond %[[CMP_RESULT]] ^[[CASE_RANGE]], ^[[CASE_DEFAULT:bb[0-9]+]]
|
|
// CHECK-NEXT: ^[[CASE_DEFAULT]]:
|
|
// CHECK-NEXT: cir.int<3>
|
|
// CHECK-NEXT: cir.store
|
|
// CHECK-NEXT: cir.br ^[[EPILOG]]
|
|
// CHECK-NEXT: ^[[EPILOG]]:
|
|
// CHECK-NEXT: cir.br ^[[EPILOG_END:bb[0-9]+]]
|
|
// CHECK-NEXT: ^[[EPILOG_END]]:
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
|
|
cir.func @_Z8bigRangei(%arg0: !s32i) {
|
|
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] {alignment = 4 : i64}
|
|
cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i>
|
|
cir.scope {
|
|
%1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
|
cir.switch (%1 : !s32i) {
|
|
cir.case(range, [#cir.int<3> : !s32i, #cir.int<100> : !s32i]) {
|
|
cir.break
|
|
}
|
|
cir.case(default, []) {
|
|
cir.break
|
|
}
|
|
cir.yield
|
|
}
|
|
}
|
|
cir.return
|
|
}
|
|
|
|
// CHECK: cir.func{{.*}} @_Z8bigRangei(%arg0: !s32i) {
|
|
// CHECK: cir.switch.flat %[[COND:.*]] : !s32i, ^bb[[#RANGE_BR:]] [
|
|
// CHECK: ]
|
|
// CHECK: ^bb[[#NO_PRED_BB:]]: // no predecessors
|
|
// CHECK: cir.br ^bb[[#DEFAULT_BB:]]
|
|
// CHECK: ^bb[[#DEFAULT_BB]]: // 2 preds: ^bb[[#NO_PRED_BB]], ^bb[[#RANGE_BR]]
|
|
// CHECK: cir.br ^bb[[#EXIT:]]
|
|
// CHECK: ^bb[[#RANGE_BR]]: // pred: ^bb[[#BB2:]]
|
|
// CHECK: %[[CONST97:.*]] = cir.const #cir.int<97> : !s32i
|
|
// CHECK: %[[CONST3:.*]] = cir.const #cir.int<3> : !s32i
|
|
// CHECK: %[[SUB:.*]] = cir.binop(sub, %[[COND]], %[[CONST3]]) : !s32i
|
|
// CHECK: %[[CAST1:.*]] = cir.cast(integral, %[[SUB]] : !s32i), !u32i
|
|
// CHECK: %[[CAST2:.*]] = cir.cast(integral, %[[CONST97]] : !s32i), !u32i
|
|
// CHECK: %[[CMP:.*]] = cir.cmp(le, %[[CAST1]], %[[CAST2]]) : !u32i, !cir.bool
|
|
// CHECK: cir.brcond %7 ^bb[[#DEFAULT_BB]], ^bb[[#RANGE_BB:]]
|
|
// CHECK: ^bb[[#RANGE_BB]]: // pred: ^bb[[#RANGE_BR]]
|
|
// CHECK: cir.br ^bb[[#EXIT]]
|
|
// CHECK: ^bb[[#EXIT]]: // 2 preds: ^bb[[#DEFAULT_BB]], ^bb[[#RANGE_BB]]
|
|
// CHECK: cir.br ^bb[[#RET_BB:]]
|
|
// CHECK: ^bb[[#RET_BB]]: // pred: ^bb[[#EXIT]]
|
|
// CHECK: cir.return
|
|
// CHECK: }
|
|
}
|