[clang] [CIR] Upstream initial support for CIR flattening (PR #130648)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 10 10:59:47 PDT 2025
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/130648
The ClangIR CFG has to be flat before it can be lowered to LLVM IR. That is, there can be no nested regions and all blocks in a region must belong to the parent region. Currently only cir.scope operations violate these rules, so the initial implementation of the cir-flatten-cfg pass only has to transform scope operations.
>From f75c9fdbd68dc1a240cb28ab3e4de4492992d5fc Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 7 Mar 2025 16:41:48 -0800
Subject: [PATCH] [CIR] Upstream initial support for CIR flattening
The ClangIR CFG has to be flat before it can be lowered to LLVM IR. That is,
there can be no nested regions and all blocks in a region must belong to the
parent region. Currently only cir.scope operations violate these rules, so
the initial implementation of the cir-flatten-cfg pass only has to transform
scope operations.
---
.../include/clang/CIR/Dialect/CMakeLists.txt | 6 +
clang/include/clang/CIR/Dialect/IR/CIROps.td | 40 ++++++
clang/include/clang/CIR/Dialect/Passes.h | 39 ++++++
clang/include/clang/CIR/Dialect/Passes.td | 28 +++++
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/Dialect/CMakeLists.txt | 1 +
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 13 ++
clang/lib/CIR/Dialect/IR/CMakeLists.txt | 1 +
.../lib/CIR/Dialect/Transforms/CMakeLists.txt | 18 +++
.../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 114 ++++++++++++++++++
clang/lib/CIR/Dialect/Transforms/PassDetail.h | 29 +++++
clang/lib/CIR/Lowering/CIRPasses.cpp | 24 ++++
clang/lib/CIR/Lowering/CMakeLists.txt | 19 +++
.../CIR/Lowering/DirectToLLVM/CMakeLists.txt | 4 +-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 102 +++++++++++++++-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 18 +++
.../Lowering/DirectToLLVM/LowerToLLVMIR.cpp | 77 ++++++++++++
clang/test/CIR/Lowering/func-simple.cpp | 20 +++
18 files changed, 550 insertions(+), 4 deletions(-)
create mode 100644 clang/include/clang/CIR/Dialect/Passes.h
create mode 100644 clang/include/clang/CIR/Dialect/Passes.td
create mode 100644 clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
create mode 100644 clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
create mode 100644 clang/lib/CIR/Dialect/Transforms/PassDetail.h
create mode 100644 clang/lib/CIR/Lowering/CIRPasses.cpp
create mode 100644 clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
diff --git a/clang/include/clang/CIR/Dialect/CMakeLists.txt b/clang/include/clang/CIR/Dialect/CMakeLists.txt
index f33061b2d87cf..3d4e6586e1c62 100644
--- a/clang/include/clang/CIR/Dialect/CMakeLists.txt
+++ b/clang/include/clang/CIR/Dialect/CMakeLists.txt
@@ -1 +1,7 @@
add_subdirectory(IR)
+
+set(LLVM_TARGET_DEFINITIONS Passes.td)
+mlir_tablegen(Passes.h.inc -gen-pass-decls -name CIR)
+mlir_tablegen(Passes.capi.h.inc -gen-pass-capi-header --prefix CIR)
+mlir_tablegen(Passes.capi.cpp.inc -gen-pass-capi-impl --prefix CIR)
+add_public_tablegen_target(MLIRCIRPassIncGen)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e2ab50c78ec2d..77c43e5ace64a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -428,6 +428,46 @@ def ScopeOp : CIR_Op<"scope", [
];
}
+//===----------------------------------------------------------------------===//
+// BrOp
+//===----------------------------------------------------------------------===//
+
+def BrOp : CIR_Op<"br",
+ [DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
+ Pure, Terminator]> {
+ let summary = "Unconditional branch";
+ let description = [{
+ The `cir.br` branches unconditionally to a block. Used to represent C/C++
+ goto's and general block branching.
+
+ Note that for source level `goto`'s crossing scope boundaries, those are
+ usually represented with the "symbolic" `cir.goto` operation.
+
+ Example:
+
+ ```mlir
+ ...
+ cir.br ^bb3
+ ^bb3:
+ cir.return
+ ```
+ }];
+
+ let builders = [
+ OpBuilder<(ins "mlir::Block *":$dest,
+ CArg<"mlir::ValueRange", "{}">:$destOperands), [{
+ $_state.addSuccessors(dest);
+ $_state.addOperands(destOperands);
+ }]>
+ ];
+
+ let arguments = (ins Variadic<CIR_AnyType>:$destOperands);
+ let successors = (successor AnySuccessor:$dest);
+ let assemblyFormat = [{
+ $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// GlobalOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
new file mode 100644
index 0000000000000..b691849dfc563
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -0,0 +1,39 @@
+//===- Passes.h - CIR pass entry points -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header file defines prototypes that expose pass constructors.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_PASSES_H
+#define CLANG_CIR_DIALECT_PASSES_H
+
+#include "mlir/Pass/Pass.h"
+
+namespace clang {
+class ASTContext;
+}
+namespace mlir {
+
+std::unique_ptr<Pass> createCIRFlattenCFGPass();
+
+void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
+
+//===----------------------------------------------------------------------===//
+// Registration
+//===----------------------------------------------------------------------===//
+
+void registerCIRDialectTranslation(mlir::MLIRContext &context);
+
+/// Generate the code for registering passes.
+#define GEN_PASS_REGISTRATION
+#include "clang/CIR/Dialect/Passes.h.inc"
+
+} // namespace mlir
+
+#endif // CLANG_CIR_DIALECT_PASSES_H
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
new file mode 100644
index 0000000000000..260bbd51062ad
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_PASSES_TD
+#define CLANG_CIR_DIALECT_PASSES_TD
+
+include "mlir/Pass/PassBase.td"
+
+def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
+ let summary = "Produces flatten CFG";
+ let description = [{
+ This pass transforms CIR and inline all the nested regions. Thus,
+ the next post condtions are met after the pass applied:
+ - there is not any nested region in a function body
+ - all the blocks in a function belong to the parent region
+ In other words, this pass removes such CIR operations like IfOp, LoopOp,
+ ScopeOp and etc. and produces a flat CIR.
+ }];
+ let constructor = "mlir::createCIRFlattenCFGPass()";
+ let dependentDialects = ["cir::CIRDialect"];
+}
+
+#endif // CLANG_CIR_DIALECT_PASSES_TD
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d20cd0560a7c1..ddfe654009644 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -82,6 +82,7 @@ struct MissingFeatures {
static bool objCLifetime() { return false; }
static bool emitNullabilityCheck() { return false; }
static bool astVarDeclInterface() { return false; }
+ static bool stackSaveOp() { return false; }
};
} // namespace cir
diff --git a/clang/lib/CIR/Dialect/CMakeLists.txt b/clang/lib/CIR/Dialect/CMakeLists.txt
index f33061b2d87cf..9f57627c321fb 100644
--- a/clang/lib/CIR/Dialect/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/CMakeLists.txt
@@ -1 +1,2 @@
add_subdirectory(IR)
+add_subdirectory(Transforms)
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 5ad369b40cda1..d041791770d82 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -268,6 +268,19 @@ LogicalResult cir::ScopeOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// BrOp
+//===----------------------------------------------------------------------===//
+
+mlir::SuccessorOperands cir::BrOp::getSuccessorOperands(unsigned index) {
+ assert(index == 0 && "invalid successor index");
+ return mlir::SuccessorOperands(getDestOperandsMutable());
+}
+
+Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
+ return getDest();
+}
+
//===----------------------------------------------------------------------===//
// GlobalOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index e3a6fc6e80ecc..aa5ea52a5e93f 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -11,6 +11,7 @@ add_clang_library(MLIRCIR
LINK_LIBS PUBLIC
MLIRIR
+ MLIRCIRInterfaces
MLIRDLTIDialect
MLIRDataLayoutInterfaces
MLIRFuncDialect
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
new file mode 100644
index 0000000000000..aa27074cc6131
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_clang_library(MLIRCIRTransforms
+ FlattenCFG.cpp
+
+ DEPENDS
+ MLIRCIRPassIncGen
+
+ LINK_LIBS PUBLIC
+ clangAST
+ clangBasic
+
+ MLIRAnalysis
+ MLIRIR
+ MLIRPass
+ MLIRTransformUtils
+
+ MLIRCIR
+ MLIRCIRInterfaces
+)
diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
new file mode 100644
index 0000000000000..dfe33beb0ae1a
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements pass that inlines CIR operations regions into the parent
+// function region.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassDetail.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/Support/LogicalResult.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/Passes.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace mlir;
+using namespace cir;
+
+namespace {
+
+struct CIRFlattenCFGPass : public CIRFlattenCFGBase<CIRFlattenCFGPass> {
+
+ CIRFlattenCFGPass() = default;
+ void runOnOperation() override;
+};
+
+class CIRScopeOpFlattening : public mlir::OpRewritePattern<cir::ScopeOp> {
+public:
+ using OpRewritePattern<cir::ScopeOp>::OpRewritePattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::ScopeOp scopeOp,
+ mlir::PatternRewriter &rewriter) const override {
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ mlir::Location loc = scopeOp.getLoc();
+
+ // Empty scope: just remove it.
+ // TODO: Remove this logic once CIR uses MLIR infrastructure to remove
+ // trivially dead operations
+ if (scopeOp.isEmpty()) {
+ rewriter.eraseOp(scopeOp);
+ return mlir::success();
+ }
+
+ // Split the current block before the ScopeOp to create the inlining
+ // point.
+ mlir::Block *currentBlock = rewriter.getInsertionBlock();
+ mlir::Block *continueBlock =
+ rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
+ if (scopeOp.getNumResults() > 0)
+ continueBlock->addArguments(scopeOp.getResultTypes(), loc);
+
+ // Inline body region.
+ mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
+ mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
+ rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
+
+ // Save stack and then branch into the body of the region.
+ rewriter.setInsertionPointToEnd(currentBlock);
+ assert(!cir::MissingFeatures::stackSaveOp());
+ rewriter.create<cir::BrOp>(loc, mlir::ValueRange(), beforeBody);
+
+ // Replace the scopeop return with a branch that jumps out of the body.
+ // Stack restore before leaving the body region.
+ rewriter.setInsertionPointToEnd(afterBody);
+ if (auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
+ rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getArgs(),
+ continueBlock);
+ }
+
+ // Replace the op with values return from the body region.
+ rewriter.replaceOp(scopeOp, continueBlock->getArguments());
+
+ return mlir::success();
+ }
+};
+
+void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
+ patterns.add<CIRScopeOpFlattening>(patterns.getContext());
+}
+
+void CIRFlattenCFGPass::runOnOperation() {
+ RewritePatternSet patterns(&getContext());
+ populateFlattenCFGPatterns(patterns);
+
+ // Collect operations to apply patterns.
+ llvm::SmallVector<Operation *, 16> ops;
+ getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
+ if (isa<ScopeOp>(op))
+ ops.push_back(op);
+ });
+
+ // Apply patterns.
+ if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
+ signalPassFailure();
+}
+
+} // namespace
+
+namespace mlir {
+
+std::unique_ptr<Pass> createCIRFlattenCFGPass() {
+ return std::make_unique<CIRFlattenCFGPass>();
+}
+
+} // namespace mlir
diff --git a/clang/lib/CIR/Dialect/Transforms/PassDetail.h b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
new file mode 100644
index 0000000000000..600dde56d679f
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CIR_DIALECT_TRANSFORMS_PASSDETAIL_H
+#define CIR_DIALECT_TRANSFORMS_PASSDETAIL_H
+
+#include "mlir/IR/Dialect.h"
+#include "mlir/Pass/Pass.h"
+
+namespace cir {
+class CIRDialect;
+} // namespace cir
+
+namespace mlir {
+// Forward declaration from Dialect.h
+template <typename ConcreteDialect>
+void registerDialect(DialectRegistry ®istry);
+
+#define GEN_PASS_CLASSES
+#include "clang/CIR/Dialect/Passes.h.inc"
+
+} // namespace mlir
+
+#endif // CIR_DIALECT_TRANSFORMS_PASSDETAIL_H
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
new file mode 100644
index 0000000000000..235acbfee8967
--- /dev/null
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements machinery for any CIR <-> CIR passes used by clang.
+//
+//===----------------------------------------------------------------------===//
+
+// #include "clang/AST/ASTContext.h"
+#include "clang/CIR/Dialect/Passes.h"
+
+#include "mlir/Pass/PassManager.h"
+
+namespace mlir {
+
+void populateCIRPreLoweringPasses(OpPassManager &pm) {
+ pm.addPass(createCIRFlattenCFGPass());
+}
+
+} // namespace mlir
diff --git a/clang/lib/CIR/Lowering/CMakeLists.txt b/clang/lib/CIR/Lowering/CMakeLists.txt
index 95c304ded9183..09e48862df63c 100644
--- a/clang/lib/CIR/Lowering/CMakeLists.txt
+++ b/clang/lib/CIR/Lowering/CMakeLists.txt
@@ -1 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+ Core
+ Support
+ )
+
+get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+
+add_clang_library(clangCIRLoweringCommon
+ CIRPasses.cpp
+
+ LINK_LIBS
+ clangCIR
+ ${dialect_libs}
+ MLIRCIR
+ MLIRCIRTransforms
+ MLIRTransforms
+ MLIRSupport
+ )
+
add_subdirectory(DirectToLLVM)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
index c11ecb82183d0..7baff3412a84e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
@@ -7,6 +7,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
add_clang_library(clangCIRLoweringDirectToLLVM
LowerToLLVM.cpp
+ LowerToLLVMIR.cpp
DEPENDS
MLIRCIREnumsGen
@@ -14,9 +15,10 @@ add_clang_library(clangCIRLoweringDirectToLLVM
MLIRCIROpInterfacesIncGen
LINK_LIBS
- MLIRIR
+ clangCIRLoweringCommon
${dialect_libs}
MLIRCIR
MLIRBuiltinToLLVMIRTranslation
MLIRLLVMToLLVMIRTranslation
+ MLIRIR
)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3200527bd03af..b320dae4c1b9f 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -12,6 +12,8 @@
#include "LowerToLLVM.h"
+#include <deque>
+
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
#include "mlir/Dialect/DLTI/DLTI.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
@@ -25,6 +27,7 @@
#include "mlir/Target/LLVMIR/Export.h"
#include "mlir/Transforms/DialectConversion.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/Passes.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/Passes.h"
#include "llvm/ADT/TypeSwitch.h"
@@ -603,6 +606,64 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
});
}
+// The unreachable code is not lowered by applyPartialConversion function
+// since it traverses blocks in the dominance order. At the same time we
+// do need to lower such code - otherwise verification errors occur.
+// For instance, the next CIR code:
+//
+// cir.func @foo(%arg0: !s32i) -> !s32i {
+// %4 = cir.cast(int_to_bool, %arg0 : !s32i), !cir.bool
+// cir.if %4 {
+// %5 = cir.const #cir.int<1> : !s32i
+// cir.return %5 : !s32i
+// } else {
+// %5 = cir.const #cir.int<0> : !s32i
+// cir.return %5 : !s32i
+// }
+// cir.return %arg0 : !s32i
+// }
+//
+// contains an unreachable return operation (the last one). After the flattening
+// pass it will be placed into the unreachable block. And the possible error
+// after the lowering pass is: error: 'cir.return' op expects parent op to be
+// one of 'cir.func, cir.scope, cir.if ... The reason that this operation was
+// not lowered and the new parent is llvm.func.
+//
+// In the future we may want to get rid of this function and use DCE pass or
+// something similar. But now we need to guarantee the absence of the dialect
+// verification errors.
+static void collectUnreachable(mlir::Operation *parent,
+ llvm::SmallVector<mlir::Operation *> &ops) {
+
+ llvm::SmallVector<mlir::Block *> unreachableBlocks;
+ parent->walk([&](mlir::Block *blk) { // check
+ if (blk->hasNoPredecessors() && !blk->isEntryBlock())
+ unreachableBlocks.push_back(blk);
+ });
+
+ std::set<mlir::Block *> visited;
+ for (mlir::Block *root : unreachableBlocks) {
+ // We create a work list for each unreachable block.
+ // Thus we traverse operations in some order.
+ std::deque<mlir::Block *> workList;
+ workList.push_back(root);
+
+ while (!workList.empty()) {
+ mlir::Block *blk = workList.back();
+ workList.pop_back();
+ if (visited.count(blk))
+ continue;
+ visited.emplace(blk);
+
+ for (mlir::Operation &op : *blk)
+ ops.push_back(&op);
+
+ for (mlir::Block *succ : blk->getSuccessors())
+ workList.push_back(succ);
+ }
+ }
+}
+
void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) {
// Lower the module attributes to LLVM equivalents.
if (mlir::Attribute tripleAttr =
@@ -630,7 +691,13 @@ void ConvertCIRToLLVMPass::runOnOperation() {
patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
patterns.add<CIRToLLVMConstantOpLowering>(converter, patterns.getContext(),
dl);
- patterns.add<CIRToLLVMFuncOpLowering>(converter, patterns.getContext());
+ patterns.add<
+ // clang-format off
+ CIRToLLVMBrOpLowering,
+ CIRToLLVMFuncOpLowering,
+ CIRToLLVMTrapOpLowering
+ // clang-format on
+ >(converter, patterns.getContext());
processCIRAttrs(module);
@@ -640,9 +707,36 @@ void ConvertCIRToLLVMPass::runOnOperation() {
target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
mlir::func::FuncDialect>();
- if (failed(applyPartialConversion(module, target, std::move(patterns)))) {
+ llvm::SmallVector<mlir::Operation *> ops;
+ ops.push_back(module);
+ collectUnreachable(module, ops);
+
+ if (failed(applyPartialConversion(ops, target, std::move(patterns))))
signalPassFailure();
- }
+}
+
+mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
+ cir::BrOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(op, adaptor.getOperands(),
+ op.getDest());
+ return mlir::LogicalResult::success();
+}
+
+mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
+ cir::TrapOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Location loc = op->getLoc();
+ rewriter.eraseOp(op);
+
+ rewriter.create<mlir::LLVM::Trap>(loc);
+
+ // Note that the call to llvm.trap is not a terminator in LLVM dialect.
+ // So we must emit an additional llvm.unreachable to terminate the current
+ // block.
+ rewriter.create<mlir::LLVM::UnreachableOp>(loc);
+
+ return mlir::success();
}
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
@@ -650,6 +744,7 @@ std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
}
void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
+ mlir::populateCIRPreLoweringPasses(pm);
pm.addPass(createConvertCIRToLLVMPass());
}
@@ -670,6 +765,7 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) {
mlir::registerBuiltinDialectTranslation(*mlirCtx);
mlir::registerLLVMDialectTranslation(*mlirCtx);
+ mlir::registerCIRDialectTranslation(*mlirCtx);
llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index a694047e3616b..d090bbe4f2e10 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -136,6 +136,24 @@ class CIRToLLVMGlobalOpLowering
cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const;
};
+class CIRToLLVMBrOpLowering : public mlir::OpConversionPattern<cir::BrOp> {
+public:
+ using mlir::OpConversionPattern<cir::BrOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::BrOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
+class CIRToLLVMTrapOpLowering : public mlir::OpConversionPattern<cir::TrapOp> {
+public:
+ using mlir::OpConversionPattern<cir::TrapOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::TrapOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
} // namespace direct
} // namespace cir
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
new file mode 100644
index 0000000000000..97077e3c3eaa0
--- /dev/null
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements lowering of CIR attributes and operations directly to
+// LLVMIR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/DialectRegistry.h"
+#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h"
+#include "mlir/Target/LLVMIR/ModuleTranslation.h"
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/IR/Constant.h"
+#include "llvm/IR/GlobalVariable.h"
+
+using namespace llvm;
+
+namespace cir {
+namespace direct {
+
+/// Implementation of the dialect interface that converts CIR attributes to LLVM
+/// IR metadata.
+class CIRDialectLLVMIRTranslationInterface
+ : public mlir::LLVMTranslationDialectInterface {
+public:
+ using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
+#if 0
+ /// Any named attribute in the CIR dialect, i.e, with name started with
+ /// "cir.", will be handled here.
+ mlir::LogicalResult amendOperation(
+ mlir::Operation *op, llvm::ArrayRef<llvm::Instruction *> instructions,
+ mlir::NamedAttribute attribute,
+ mlir::LLVM::ModuleTranslation &moduleTranslation) const override {
+ return mlir::success();
+ }
+#endif
+ /// Translates the given operation to LLVM IR using the provided IR builder
+ /// and saving the state in `moduleTranslation`.
+ mlir::LogicalResult convertOperation(
+ mlir::Operation *op, llvm::IRBuilderBase &builder,
+ mlir::LLVM::ModuleTranslation &moduleTranslation) const final {
+
+ if (auto cirOp = llvm::dyn_cast<mlir::LLVM::ZeroOp>(op))
+ moduleTranslation.mapValue(cirOp.getResult()) =
+ llvm::Constant::getNullValue(
+ moduleTranslation.convertType(cirOp.getType()));
+
+ return mlir::success();
+ }
+};
+
+void registerCIRDialectTranslation(mlir::DialectRegistry ®istry) {
+ registry.insert<cir::CIRDialect>();
+ registry.addExtension(+[](mlir::MLIRContext *ctx, cir::CIRDialect *dialect) {
+ dialect->addInterfaces<CIRDialectLLVMIRTranslationInterface>();
+ });
+}
+
+} // namespace direct
+} // namespace cir
+
+namespace mlir {
+void registerCIRDialectTranslation(mlir::MLIRContext &context) {
+ mlir::DialectRegistry registry;
+ cir::direct::registerCIRDialectTranslation(registry);
+ context.appendDialectRegistry(registry);
+}
+} // namespace mlir
diff --git a/clang/test/CIR/Lowering/func-simple.cpp b/clang/test/CIR/Lowering/func-simple.cpp
index 1429ec270774b..32d75cdd2c15d 100644
--- a/clang/test/CIR/Lowering/func-simple.cpp
+++ b/clang/test/CIR/Lowering/func-simple.cpp
@@ -13,6 +13,26 @@ int intfunc() { return 42; }
// CHECK: define{{.*}} i32 @intfunc()
// CHECK: ret i32 42
+int scopes() {
+ {
+ {
+ return 99;
+ }
+ }
+}
+// CHECK: define{{.*}} i32 @scopes() {
+// CHECK: br label %[[LABEL1:.*]]
+// CHECK: [[LABEL1]]:
+// CHECK: br label %[[LABEL2:.*]]
+// CHECK: [[LABEL2]]:
+// CHECK: ret i32 99
+// CHECK: [[LABEL3:.*]]:
+// CHECK: br label %[[LABEL4:.*]]
+// CHECK: [[LABEL4]]:
+// CHECK: call void @llvm.trap()
+// CHECK: unreachable
+// CHECK: }
+
long longfunc() { return 42l; }
// CHECK: define{{.*}} i64 @longfunc() {
// CHECK: ret i64 42
More information about the cfe-commits
mailing list