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

Reply via email to