https://github.com/Andres-Salamanca created https://github.com/llvm/llvm-project/pull/140425
This PR adds support for lowering the `cir.switch` operation to LLVM. It includes tests for lowering from `.cir` as well as end-to-end source code tests. >From 384ec2904b91cbed8757720373b451389f5f1a1a Mon Sep 17 00:00:00 2001 From: Andres Salamanca <andrealebarbari...@gmail.com> Date: Sat, 17 May 2025 19:36:05 -0500 Subject: [PATCH] Added lowering logic for switch and appropriate tests --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 32 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 + clang/test/CIR/CodeGen/switch.cpp | 424 +++++++++++++++++- clang/test/CIR/Lowering/switch.cir | 190 ++++++++ 4 files changed, 655 insertions(+), 1 deletion(-) create mode 100644 clang/test/CIR/Lowering/switch.cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index c8eac87f6cdff..05c772e7c1a53 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1038,6 +1038,37 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite( + cir::SwitchFlatOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + + llvm::SmallVector<mlir::APInt, 8> caseValues; + if (op.getCaseValues()) { + for (mlir::Attribute val : op.getCaseValues()) { + auto intAttr = dyn_cast<cir::IntAttr>(val); + caseValues.push_back(intAttr.getValue()); + } + } + + llvm::SmallVector<mlir::Block *, 8> caseDestinations; + llvm::SmallVector<mlir::ValueRange, 8> caseOperands; + + for (mlir::Block *x : op.getCaseDestinations()) { + caseDestinations.push_back(x); + } + + for (mlir::OperandRange x : op.getCaseOperands()) { + caseOperands.push_back(x); + } + + // Set switch op to branch to the newly created blocks. + rewriter.setInsertionPoint(op); + rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( + op, adaptor.getCondition(), op.getDefaultDestination(), + op.getDefaultOperands(), caseValues, caseDestinations, caseOperands); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMUnaryOpLowering::matchAndRewrite( cir::UnaryOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -1641,6 +1672,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMGetGlobalOpLowering, CIRToLLVMGetMemberOpLowering, CIRToLLVMSelectOpLowering, + CIRToLLVMSwitchFlatOpLowering, CIRToLLVMShiftOpLowering, CIRToLLVMStackSaveOpLowering, CIRToLLVMStackRestoreOpLowering, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index bd077e3d1d1e0..dde0cfcabe395 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -149,6 +149,16 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMSwitchFlatOpLowering + : public mlir::OpConversionPattern<cir::SwitchFlatOp> { +public: + using mlir::OpConversionPattern<cir::SwitchFlatOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::SwitchFlatOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + class CIRToLLVMGetGlobalOpLowering : public mlir::OpConversionPattern<cir::GetGlobalOp> { public: diff --git a/clang/test/CIR/CodeGen/switch.cpp b/clang/test/CIR/CodeGen/switch.cpp index 0bd4e0759e634..a3dbac89fb856 100644 --- a/clang/test/CIR/CodeGen/switch.cpp +++ b/clang/test/CIR/CodeGen/switch.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir /// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG void sw1(int a) { @@ -28,6 +30,36 @@ void sw1(int a) { // CIR: cir.alloca !s32i, !cir.ptr<!s32i>, ["yolo", init] // CIR: cir.break +// LLVM: define void @_Z3sw1i +// LLVM: store i32 1, ptr %[[B_ADDR:.*]], align 4 +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR:.*]], align 4 +// LLVM: br label %[[BB7:.*]] +// LLVM: [[BB7]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 0, label %[[CASE0:.*]] +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM: ] +// LLVM: [[CASE0]]: +// LLVM: %[[B:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[INC0:.*]] = add nsw i32 %[[B]], 1 +// LLVM: store i32 %[[INC0]], ptr %[[B_ADDR]], align 4 +// LLVM: br label %[[EXIT]] +// LLVM: [[CASE1]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[CASE2]]: +// LLVM: br label %[[BB14:.*]] +// LLVM: [[BB14]]: +// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[INC2:.*]] = add nsw i32 %[[B2]], 1 +// LLVM: store i32 %[[INC2]], ptr %[[B_ADDR]], align 4 +// LLVM: store i32 100, ptr %[[YOLO:.*]], align 4 +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[DEFAULT:.*]] +// LLVM: [[DEFAULT]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw1i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -74,6 +106,26 @@ void sw2(int a) { // CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: cir.store %[[ZERO]], %[[FOMO]] : !s32i, !cir.ptr<!s32i> +// LLVM: define void @_Z3sw2i +// LLVM: store i32 2, ptr %[[YOLO_ADDR:.*]], align 4 +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR:.*]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM: i32 3, label %[[CASE3:.*]] +// LLVM: ] +// LLVM: [[CASE3]]: +// LLVM: store i32 0, ptr %[[FOMO_ADDR:.*]], align 4 +// LLVM: %[[YOLO_VAL:.*]] = load i32, ptr %[[YOLO_ADDR]], align 4 +// LLVM: %[[FOMO_VAL:.*]] = load i32, ptr %[[FOMO_ADDR]], align 4 +// LLVM: %[[YOLO_PLUS_FOMO:.*]] = add nsw i32 %[[YOLO_VAL]], %[[FOMO_VAL]] +// LLVM: store i32 %[[YOLO_PLUS_FOMO]], ptr %[[YOLO_ADDR]], align 4 +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw2i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -109,6 +161,19 @@ void sw3(int a) { // CIR-NEXT: cir.yield // CIR-NEXT: } +// LLVM-LABEL: define void @_Z3sw3i +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR:.*]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[DEFAULT:.*]] [ +// LLVM: ] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT:.*]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw3i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -150,6 +215,32 @@ int sw4(int a) { // CIR-NEXT: cir.yield // CIR-NEXT: } +// LLVM: define i32 @_Z3sw4i +// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: %[[RET_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: br label %[[ENTRY:.*]] +// LLVM: [[ENTRY]]: +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 42, label %[[CASE42:.*]] +// LLVM: ] +// LLVM: [[CASE42]]: +// LLVM: br label %[[CASE42_BODY:.*]] +// LLVM: [[CASE42_BODY]]: +// LLVM: store i32 3, ptr %[[RET_ADDR]], align 4 +// LLVM: %[[RET3:.*]] = load i32, ptr %[[RET_ADDR]], align 4 +// LLVM: ret i32 %[[RET3]] +// LLVM: [[DEFAULT]]: +// LLVM: store i32 2, ptr %[[RET_ADDR]], align 4 +// LLVM: %[[RET2:.*]] = load i32, ptr %[[RET_ADDR]], align 4 +// LLVM: ret i32 %[[RET2]] +// LLVM: [[EXIT_UNRE:.*]]: +// LLVM: store i32 0, ptr %[[RET_ADDR]], align 4 +// LLVM: %[[RET0:.*]] = load i32, ptr %[[RET_ADDR]], align 4 +// LLVM: ret i32 %[[RET0]] + // OGCG: define dso_local noundef i32 @_Z3sw4i // OGCG: entry: // OGCG: %[[RETVAL:.*]] = alloca i32, align 4 @@ -180,6 +271,23 @@ void sw5(int a) { // CIR-NEXT: cir.yield // CIR-NEXT: } +// LLVM-LABEL: define void @_Z3sw5i +// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: br label %[[ENTRY:.*]] +// LLVM: [[ENTRY]]: +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM: ] +// LLVM: [[CASE1]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw5i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -226,6 +334,42 @@ void sw6(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z3sw6i +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR:.*]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 0, label %[[CASE0:.*]] +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM-DAG: i32 3, label %[[CASE3:.*]] +// LLVM-DAG: i32 4, label %[[CASE4:.*]] +// LLVM-DAG: i32 5, label %[[CASE5:.*]] +// LLVM: ] +// LLVM: [[CASE0]]: +// LLVM: br label %[[CASE0_CONT:.*]] +// LLVM: [[CASE0_CONT]]: +// LLVM: br label %[[CASE1]] +// LLVM: [[CASE1]]: +// LLVM: br label %[[CASE1_CONT:.*]] +// LLVM: [[CASE1_CONT]]: +// LLVM: br label %[[CASE2]] +// LLVM: [[CASE2]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[CASE3]]: +// LLVM: br label %[[CASE3_CONT:.*]] +// LLVM: [[CASE3_CONT]]: +// LLVM: br label %[[CASE4]] +// LLVM: [[CASE4]]: +// LLVM: br label %[[CASE4_CONT:.*]] +// LLVM: [[CASE4_CONT]]: +// LLVM: br label %[[CASE5]] +// LLVM: [[CASE5]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void // OGCG: define dso_local void @_Z3sw6i // OGCG: entry: @@ -284,6 +428,45 @@ void sw7(int a) { // CIR-NEXT: cir.yield // CIR: } +// LLVM: define void @_Z3sw7i +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR:.*]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 0, label %[[CASE0:.*]] +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM-DAG: i32 3, label %[[CASE3:.*]] +// LLVM-DAG: i32 4, label %[[CASE4:.*]] +// LLVM-DAG: i32 5, label %[[CASE5:.*]] +// LLVM: ] +// LLVM: [[CASE0]]: +// LLVM: br label %[[CASE0_CONT:.*]] +// LLVM: [[CASE0_CONT]]: +// LLVM: br label %[[CASE1]] +// LLVM: [[CASE1]]: +// LLVM: br label %[[CASE1_CONT:.*]] +// LLVM: [[CASE1_CONT]]: +// LLVM: br label %[[CASE2]] +// LLVM: [[CASE2]]: +// LLVM: br label %[[CASE2_CONT:.*]] +// LLVM: [[CASE2_CONT]]: +// LLVM: br label %[[CASE3]] +// LLVM: [[CASE3]]: +// LLVM: br label %[[CASE3_CONT:.*]] +// LLVM: [[CASE3_CONT]]: +// LLVM: br label %[[CASE4]] +// LLVM: [[CASE4]]: +// LLVM: br label %[[CASE4_CONT:.*]] +// LLVM: [[CASE4_CONT]]: +// LLVM: br label %[[CASE5]] +// LLVM: [[CASE5]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw7i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -327,6 +510,23 @@ void sw8(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z3sw8i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 3, label %[[CASE3:.*]] +// LLVM-DAG: i32 4, label %[[CASE4:.*]] +// LLVM: ] +// LLVM: [[CASE3]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[CASE4]]: +// LLVM: br label %[[CASE4_CONT:.*]] +// LLVM: [[CASE4_CONT]]: +// LLVM: br label %[[DEFAULT]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void // OGCG: define dso_local void @_Z3sw8i // OGCG: entry: @@ -368,6 +568,24 @@ void sw9(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z3sw9i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 3, label %[[CASE3:.*]] +// LLVM-DAG: i32 4, label %[[CASE4:.*]] +// LLVM: ] +// LLVM: [[CASE3]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[DEFAULT_CONT:.*]] +// LLVM: [[DEFAULT_CONT]]: +// LLVM: br label %[[CASE4]] +// LLVM: [[CASE4]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z3sw9i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -412,6 +630,29 @@ void sw10(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z4sw10i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 3, label %[[CASE_3:.*]] +// LLVM-DAG: i32 4, label %[[CASE_4:.*]] +// LLVM-DAG: i32 5, label %[[CASE_5:.*]] +// LLVM: ] +// LLVM: [[CASE_3]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[CASE_4]]: +// LLVM: br label %[[CASE4_CONT:.*]] +// LLVM: [[CASE4_CONT]]: +// LLVM: br label %[[DEFAULT]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[DEFAULT_CONT:.*]] +// LLVM: [[DEFAULT_CONT]]: +// LLVM: br label %[[CASE_5]] +// LLVM: [[CASE_5]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw10i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -467,6 +708,39 @@ void sw11(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z4sw11i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 3, label %[[CASE_3:.*]] +// LLVM-DAG: i32 4, label %[[CASE_4:.*]] +// LLVM-DAG: i32 5, label %[[CASE_5:.*]] +// LLVM-DAG: i32 6, label %[[CASE_6:.*]] +// LLVM-DAG: i32 7, label %[[CASE_7:.*]] +// LLVM: ] +// LLVM: [[CASE_3]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[CASE_4]]: +// LLVM: br label %[[CASE4_CONT:.*]] +// LLVM: [[CASE4_CONT]]: +// LLVM: br label %[[CASE_5]] +// LLVM: [[CASE_5]]: +// LLVM: br label %[[CASE5_CONT:.*]] +// LLVM: [[CASE5_CONT]]: +// LLVM: br label %[[DEFAULT]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[DEFAULT_CONT:.*]] +// LLVM: [[DEFAULT_CONT]]: +// LLVM: br label %[[CASE_6]] +// LLVM: [[CASE_6]]: +// LLVM: br label %[[CASE6_CONT:.*]] +// LLVM: [[CASE6_CONT]]: +// LLVM: br label %[[CASE_7]] +// LLVM: [[CASE_7]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw11i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -507,6 +781,19 @@ void sw12(int a) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z4sw12i +// LLVM: switch i32 %[[COND:.*]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 3, label %[[CASE_3:.*]] +// LLVM: ] +// LLVM: [[CASE_3]]: +// LLVM: ret void +// LLVM: [[UNREACHABLE:.*]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw12i // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -545,6 +832,32 @@ void sw13(int a, int b) { // CIR: } // CIR: cir.return +// LLVM: define void @_Z4sw13ii +// LLVM: switch i32 %[[COND:.*]], label %[[OUTER_EXIT:.*]] [ +// LLVM-DAG: i32 1, label %[[CASE_A_1:.*]] +// LLVM: ] +// LLVM: [[CASE_A_1]]: +// LLVM: br label %[[LOAD_B:.*]] +// LLVM: [[LOAD_B]]: +// LLVM: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR:.*]], align 4 +// LLVM: br label %[[INNER_SWITCH:.*]] +// LLVM: [[INNER_SWITCH]]: +// LLVM: switch i32 %[[B_VAL]], label %[[INNER_EXIT:.*]] [ +// LLVM-DAG: i32 2, label %[[CASE_B_2:.*]] +// LLVM: ] +// LLVM: [[CASE_B_2]]: +// LLVM: br label %[[INNER_EXIT]] +// LLVM: [[INNER_EXIT]]: +// LLVM: br label %[[INNER_EXIT_CONT:.*]] +// LLVM: [[INNER_EXIT_CONT]]: +// LLVM: br label %[[MERGE:.*]] +// LLVM: [[MERGE]]: +// LLVM: br label %[[OUTER_EXIT]] +// LLVM: [[OUTER_EXIT]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[EXIT]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw13ii // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 @@ -595,12 +908,42 @@ void sw14(int x) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z4sw14i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM-DAG: i32 3, label %[[CASE3_TO_6:.*]] +// LLVM-DAG: i32 4, label %[[CASE3_TO_6]] +// LLVM-DAG: i32 5, label %[[CASE3_TO_6]] +// LLVM-DAG: i32 6, label %[[CASE3_TO_6]] +// LLVM-DAG: i32 7, label %[[CASE7:.*]] +// LLVM: ] +// LLVM: [[CASE1]]: +// LLVM: br label %[[AFTER1:.*]] +// LLVM: [[AFTER1]]: +// LLVM: br label %[[CASE2]] +// LLVM: [[CASE2]]: +// LLVM: br label %[[AFTER2:.*]] +// LLVM: [[AFTER2]]: +// LLVM: br label %[[CASE3_TO_6]] +// LLVM: [[CASE3_TO_6]]: +// LLVM: br label %[[AFTER3_6:.*]] +// LLVM: [[AFTER3_6]]: +// LLVM: br label %[[CASE7]] +// LLVM: [[CASE7]]: +// LLVM: br label %[[EXIT1:.*]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[EXIT1]] +// LLVM: [[EXIT1]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw14i // OGCG: entry: // OGCG: %[[X_ADDR:.*]] = alloca i32, align 4 // OGCG: store i32 %x, ptr %[[X_ADDR]], align 4 // OGCG: %[[X_VAL:.*]] = load i32, ptr %[[X_ADDR]], align 4 - // OGCG: switch i32 %[[X_VAL]], label %[[DEFAULT:.*]] [ // OGCG-DAG: i32 1, label %[[BB1:.*]] // OGCG-DAG: i32 2, label %[[BB1]] @@ -652,6 +995,30 @@ void sw15(int x) { // CIR-NEXT: cir.break // CIR-NEXT: } +// LLVM: define void @_Z4sw15i +// LLVM: switch i32 %[[COND:.*]], label %[[DEFAULT:.*]] [ +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM-DAG: i32 3, label %[[CASE3:.*]] +// LLVM: ] +// LLVM: [[CASE1]]: +// LLVM: br label %[[CASE1_CONT:.*]] +// LLVM: [[CASE1_CONT]]: +// LLVM: br label %[[CASE2]] +// LLVM: [[CASE2]]: +// LLVM: store i32 0, ptr %[[Y_ADDR:.*]], align 4 +// LLVM: br label %[[CASE2_CONT:.*]] +// LLVM: [[CASE2_CONT]]: +// LLVM: br label %[[CASE3]] +// LLVM: [[CASE3]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[DEFAULT]]: +// LLVM: br label %[[EXIT]] +// LLVM: [[EXIT]]: +// LLVM: br label %[[RET:.*]] +// LLVM: [[RET]]: +// LLVM: ret void + // OGCG: define dso_local void @_Z4sw15i // OGCG: entry: // OGCG: %[[X_ADDR:.*]] = alloca i32, align 4 @@ -714,6 +1081,61 @@ int nested_switch(int a) { // CIR: cir.case(equal, [#cir.int<7> : !s32i]) { // CIR: cir.return +// LLVM: define i32 @_Z13nested_switchi +// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: %[[RES_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: store i32 %[[ARG:.*]], ptr %[[A_ADDR]], align 4 +// LLVM: br label %[[ENTRY:.*]] +// LLVM: [[ENTRY]]: +// LLVM: store i32 1, ptr %[[B_ADDR]], align 4 +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]], align 4 +// LLVM: br label %[[SWITCH:.*]] +// LLVM: [[SWITCH]]: +// LLVM: switch i32 %[[A_VAL]], label %[[EXIT:.*]] [ +// LLVM-DAG: i32 0, label %[[CASE0:.*]] +// LLVM-DAG: i32 1, label %[[CASE1:.*]] +// LLVM-DAG: i32 2, label %[[CASE2:.*]] +// LLVM-DAG: i32 9, label %[[CASE9:.*]] +// LLVM-DAG: i32 7, label %[[CASE7:.*]] +// LLVM: ] +// LLVM: [[CASE0]]: +// LLVM: %[[B0:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[B1:.*]] = add nsw i32 %[[B0]], 1 +// LLVM: store i32 %[[B1]], ptr %[[B_ADDR]], align 4 +// LLVM: br label %[[CASE0_CONT:.*]] +// LLVM: [[CASE0_CONT]]: +// LLVM: br label %[[CASE1]] +// LLVM: [[CASE1]]: +// LLVM: %[[B1a:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: store i32 %[[B1a]], ptr %[[RES_ADDR]], align 4 +// LLVM: %[[RET1:.*]] = load i32, ptr %[[RES_ADDR]], align 4 +// LLVM: ret i32 %[[RET1]] +// LLVM: [[CASE2]]: +// LLVM: br label %[[CASE2_BODY:.*]] +// LLVM: [[CASE2_BODY]]: +// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[B3:.*]] = add nsw i32 %[[B2]], 1 +// LLVM: store i32 %[[B3]], ptr %[[B_ADDR]], align 4 +// LLVM: br label %[[CASE2_CONT:.*]] +// LLVM: [[CASE9]]: +// LLVM: %[[A9:.*]] = load i32, ptr %[[A_ADDR]], align 4 +// LLVM: %[[B4:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[SUM9:.*]] = add nsw i32 %[[A9]], %[[B4]] +// LLVM: store i32 %[[SUM9]], ptr %[[B_ADDR]], align 4 +// LLVM: br label %[[CASE2_CONT1:.*]] +// LLVM: [[CASE7]]: +// LLVM: %[[A7:.*]] = load i32, ptr %[[A_ADDR]], align 4 +// LLVM: %[[B5:.*]] = load i32, ptr %[[B_ADDR]], align 4 +// LLVM: %[[SUM7:.*]] = add nsw i32 %[[A7]], %[[B5]] +// LLVM: store i32 %[[SUM7]], ptr %[[RES_ADDR]], align 4 +// LLVM: %[[RET7:.*]] = load i32, ptr %[[RES_ADDR]], align 4 +// LLVM: ret i32 %[[RET7]] +// LLVM: [[EXIT]]: +// LLVM: store i32 0, ptr %[[RES_ADDR]], align 4 +// LLVM: %[[RET0:.*]] = load i32, ptr %[[RES_ADDR]], align 4 +// LLVM: ret i32 %[[RET0]] + // OGCG: define dso_local noundef i32 @_Z13nested_switchi // OGCG: entry: // OGCG: %[[RETVAL:.*]] = alloca i32, align 4 diff --git a/clang/test/CIR/Lowering/switch.cir b/clang/test/CIR/Lowering/switch.cir new file mode 100644 index 0000000000000..9434b7337f7ed --- /dev/null +++ b/clang/test/CIR/Lowering/switch.cir @@ -0,0 +1,190 @@ +// RUN: cir-opt %s -cir-to-llvm -o %t.mlir +// RUN: FileCheck --input-file=%t.mlir %s + +!s8i = !cir.int<s, 8> +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> + +module { + cir.func @shouldLowerSwitchWithDefault(%arg0: !s8i) { + cir.switch (%arg0 : !s8i) { + // CHECK: llvm.switch %arg0 : i8, ^bb[[#DEFAULT:]] [ + // CHECK: 1: ^bb[[#CASE1:]] + // CHECK: ] + cir.case (equal, [#cir.int<1> : !s8i]) { + cir.break + } + // CHECK: ^bb[[#CASE1]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + cir.case (default, []) { + cir.break + } + // CHECK: ^bb[[#DEFAULT]]: + // CHECK: llvm.br ^bb[[#EXIT]] + cir.yield + } + // CHECK: ^bb[[#EXIT]]: + cir.return + } + + + cir.func @shouldLowerSwitchWithoutDefault(%arg0: !s32i) { + cir.switch (%arg0 : !s32i) { + // Default block is the exit block: + // CHECK: llvm.switch %arg0 : i32, ^bb[[#EXIT:]] [ + // CHECK: 1: ^bb[[#CASE1:]] + // CHECK: ] + cir.case (equal, [#cir.int<1> : !s32i]) { + cir.break + } + // CHECK: ^bb[[#CASE1]]: + // CHECK: llvm.br ^bb[[#EXIT]] + cir.yield + } + // CHECK: ^bb[[#EXIT]]: + cir.return + } + + + cir.func @shouldLowerSwitchWithImplicitFallthrough(%arg0: !s64i) { + cir.switch (%arg0 : !s64i) { + // CHECK: llvm.switch %arg0 : i64, ^bb[[#EXIT:]] [ + // CHECK: 1: ^bb[[#CASE1N2:]], + // CHECK: 2: ^bb[[#CASE1N2]] + // CHECK: ] + cir.case (anyof, [#cir.int<1> : !s64i, #cir.int<2> : !s64i]) { // case 1 and 2 use same region + cir.break + } + // CHECK: ^bb[[#CASE1N2]]: + // CHECK: llvm.br ^bb[[#EXIT]] + cir.yield + } + // CHECK: ^bb[[#EXIT]]: + cir.return + } + + + cir.func @shouldLowerSwitchWithExplicitFallthrough(%arg0: !s64i) { + cir.switch (%arg0 : !s64i) { + // CHECK: llvm.switch %arg0 : i64, ^bb[[#EXIT:]] [ + // CHECK: 1: ^bb[[#CASE1:]], + // CHECK: 2: ^bb[[#CASE2:]] + // CHECK: ] + cir.case (equal, [#cir.int<1> : !s64i]) { // case 1 has its own region + cir.yield // fallthrough to case 2 + } + // CHECK: ^bb[[#CASE1]]: + // CHECK: llvm.br ^bb[[#CASE2]] + cir.case (equal, [#cir.int<2> : !s64i]) { + cir.break + } + // CHECK: ^bb[[#CASE2]]: + // CHECK: llvm.br ^bb[[#EXIT]] + cir.yield + } + // CHECK: ^bb[[#EXIT]]: + cir.return + } + + + cir.func @shouldLowerSwitchWithFallthroughToExit(%arg0: !s64i) { + cir.switch (%arg0 : !s64i) { + // CHECK: llvm.switch %arg0 : i64, ^bb[[#EXIT:]] [ + // CHECK: 1: ^bb[[#CASE1:]] + // CHECK: ] + cir.case (equal, [#cir.int<1> : !s64i]) { + cir.yield // fallthrough to exit + } + // CHECK: ^bb[[#CASE1]]: + // CHECK: llvm.br ^bb[[#EXIT]] + cir.yield + } + // CHECK: ^bb[[#EXIT]]: + cir.return + } + + + cir.func @shouldDropEmptySwitch(%arg0: !s64i) { + cir.switch (%arg0 : !s64i) { + cir.yield + } + // CHECK-NOT: llvm.switch + cir.return + } + + cir.func @shouldLowerMultiBlockCase(%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: llvm.func @shouldLowerMultiBlockCase + // CHECK: ^bb1: // pred: ^bb0 + // CHECK: llvm.switch {{.*}} : i32, ^[[DEFAULT_BB:.+]] [ + // CHECK: 3: ^[[DIRECTLY_RET_BB:.+]] + // CHECK: ] + // CHECK: ^[[DIRECTLY_RET_BB]]: + // CHECK: llvm.return + // CHECK: ^[[DEFAULT_BB:.+]]: + // CHECK: llvm.br ^[[RET_BB:.+]] + // CHECK: ^[[RET_BB:.+]]: // pred: ^[[DEFAULT_BB:.+]] + // CHECK: llvm.return + // CHECK: } + + cir.func @shouldLowerNestedBreak(%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: llvm.func @shouldLowerNestedBreak + // CHECK: llvm.switch %6 : i32, ^[[DEFAULT_BB:.+]] [ + // CHECK: 0: ^[[ZERO_BB:.+]] + // CHECK: ] + // CHECK: ^[[ZERO_BB]]: + // CHECK: llvm.br ^[[ZERO_BB_SUCC:.+]] + // CHECK: ^[[ZERO_BB_SUCC]]: // pred: ^[[ZERO_BB:]] + // CHECK: llvm.cond_br {{%.*}}, ^[[DEFAULT_BB_PRED1:.+]], ^[[DEFAULT_BB_PRED12:.+]] + // CHECK: ^[[DEFAULT_BB_PRED1]]: // pred: ^[[ZERO_BB_SUCC]] + // CHECK: llvm.br ^[[DEFAULT_BB]] + // CHECK: ^[[DEFAULT_BB_PRED12]]: // pred: ^[[ZERO_BB_SUCC]] + // CHECK: llvm.br ^[[DEFAULT_BB_PRED1:.+]] + // CHECK: ^[[DEFAULT_BB_PRED1]]: // pred: ^[[DEFAULT_BB_PRED12]] + // CHECK: llvm.br ^[[DEFAULT_BB]] + // CHECK: ^[[DEFAULT_BB]]: + // CHECK: llvm.br ^[[RET_BB:.+]] + // CHECK: ^[[RET_BB]]: // pred: ^[[DEFAULT_BB]] + // CHECK: llvm.return +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits