[clang] 701148f - [CIR] Upstream initial support for CIR flattening (#130648)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 11 15:47:06 PDT 2025
Author: Andy Kaylor
Date: 2025-03-11T15:47:02-07:00
New Revision: 701148f05a7b90ed6b8c4fc38db4c2b0c9241ffc
URL: https://github.com/llvm/llvm-project/commit/701148f05a7b90ed6b8c4fc38db4c2b0c9241ffc
DIFF: https://github.com/llvm/llvm-project/commit/701148f05a7b90ed6b8c4fc38db4c2b0c9241ffc.diff
LOG: [CIR] Upstream initial support for CIR flattening (#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.
Added:
clang/include/clang/CIR/Dialect/Passes.h
clang/include/clang/CIR/Dialect/Passes.td
clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
clang/lib/CIR/Dialect/Transforms/PassDetail.h
clang/lib/CIR/Lowering/CIRPasses.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
Modified:
clang/include/clang/CIR/Dialect/CMakeLists.txt
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/Dialect/CMakeLists.txt
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/IR/CMakeLists.txt
clang/lib/CIR/Lowering/CMakeLists.txt
clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
clang/test/CIR/Lowering/func-simple.cpp
Removed:
################################################################################
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..84b7ecba2630a
--- /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 by inlining all the nested regions. Thus,
+ the following conditions are true after the pass applied:
+ - there are no nested regions in any 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..b01d3d4d43e65
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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. MLIR canonicalizer is too aggressive and we
+ // need to either (a) make sure all our ops model all side-effects and/or
+ // (b) have more options in the canonicalizer in MLIR to temper
+ // aggressiveness level.
+ 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..79ec0696eb180 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,11 +27,13 @@
#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"
#include "llvm/IR/Module.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TimeProfiler.h"
using namespace cir;
@@ -135,6 +139,7 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
case CIR::WeakODRLinkage:
return LLVM::WeakODR;
};
+ llvm_unreachable("Unknown CIR linkage type");
}
class CIRAttrToValue {
@@ -603,6 +608,69 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
});
}
+// The applyPartialConversion function traverses blocks in the dominance order,
+// so it does not lower and operations that are not reachachable from the
+// operations passed in as arguments. Since we do need to lower such code in
+// order to avoid verification errors occur, we cannot just pass the module op
+// to applyPartialConversion. We must build a set of unreachable ops and
+// explicitly add them, along with the module, to the vector we pass to
+// applyPartialConversion.
+//
+// For instance, this 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. 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 a DCE pass or
+// something similar. But for 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 +698,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 +714,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 +751,7 @@ std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
}
void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
+ mlir::populateCIRPreLoweringPasses(pm);
pm.addPass(createConvertCIRToLLVMPass());
}
@@ -670,6 +772,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..30b9eaaca2d37
--- /dev/null
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+
+ /// 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