Skip to content

Conversation

@Andres-Salamanca
Copy link
Contributor

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.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels May 18, 2025
@llvmbot
Copy link
Member

llvmbot commented May 18, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: None (Andres-Salamanca)

Changes

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.


Patch is 26.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140425.diff

4 Files Affected:

  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+32)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+10)
  • (modified) clang/test/CIR/CodeGen/switch.cpp (+423-1)
  • (added) clang/test/CIR/Lowering/switch.cir (+190)
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...
[truncated]

@Andres-Salamanca
Copy link
Contributor Author

@andykaylor

@andykaylor andykaylor requested review from andykaylor and mmha May 19, 2025 20:51
Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just a few nits.

mlir::ConversionPatternRewriter &rewriter) const {

llvm::SmallVector<mlir::APInt, 8> caseValues;
if (op.getCaseValues()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this if necessary? I think it there are no case values we just won't enter the for loop below.

llvm::SmallVector<mlir::APInt, 8> caseValues;
if (op.getCaseValues()) {
for (mlir::Attribute val : op.getCaseValues()) {
auto intAttr = dyn_cast<cir::IntAttr>(val);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto intAttr = dyn_cast<cir::IntAttr>(val);
auto intAttr = cast<cir::IntAttr>(val);

Make this assert.

llvm::SmallVector<mlir::Block *, 8> caseDestinations;
llvm::SmallVector<mlir::ValueRange, 8> caseOperands;

for (mlir::Block *x : op.getCaseDestinations()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need braces for loops with single-line bodies.

caseDestinations.push_back(x);
}

for (mlir::OperandRange x : op.getCaseOperands()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No braces

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after you address Andy's nits.

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@andykaylor andykaylor merged commit 35434f2 into llvm:main May 22, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants