Skip to content

Commit 39ce995

Browse files
authored
[CIR] Upstream cir-canonicalize pass (#131891)
This change introduces the cir-canonicalize pass. This is a simple cir-to-cir transformation that eliminates empty scopes and redundant branches. It will be expanded in future changes to simplify other redundant instruction sequences. MLIR verification and mlir-specific command-line option handling is also introduced here.
1 parent cd26dd5 commit 39ce995

25 files changed

+455
-5
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,12 @@ def warn_hlsl_langstd_minimal :
386386
Warning<"support for HLSL language version %0 is incomplete, "
387387
"recommend using %1 instead">,
388388
InGroup<HLSLDXCCompat>;
389+
390+
// ClangIR frontend errors
391+
def err_cir_to_cir_transform_failed : Error<
392+
"CIR-to-CIR transformation failed">, DefaultFatal;
393+
394+
def err_cir_verification_failed_pre_passes : Error<
395+
"CIR module verification error before running CIR-to-CIR passes">,
396+
DefaultFatal;
389397
}

clang/include/clang/CIR/CIRGenerator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class CIRGenerator : public clang::ASTConsumer {
5555
void Initialize(clang::ASTContext &astContext) override;
5656
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
5757
mlir::ModuleOp getModule() const;
58+
mlir::MLIRContext &getMLIRContext() { return *mlirContext; };
59+
const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; };
60+
61+
bool verifyModule() const;
5862
};
5963

6064
} // namespace cir
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file declares an interface for running CIR-to-CIR passes.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef CLANG_CIR_CIRTOCIRPASSES_H
14+
#define CLANG_CIR_CIRTOCIRPASSES_H
15+
16+
#include "mlir/Pass/Pass.h"
17+
18+
#include <memory>
19+
20+
namespace clang {
21+
class ASTContext;
22+
}
23+
24+
namespace mlir {
25+
class MLIRContext;
26+
class ModuleOp;
27+
} // namespace mlir
28+
29+
namespace cir {
30+
31+
// Run set of cleanup/prepare/etc passes CIR <-> CIR.
32+
mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
33+
mlir::MLIRContext &mlirCtx,
34+
clang::ASTContext &astCtx,
35+
bool enableVerifier);
36+
37+
} // namespace cir
38+
39+
#endif // CLANG_CIR_CIRTOCIRPASSES_H_

clang/include/clang/CIR/Dialect/Passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class ASTContext;
2020
}
2121
namespace mlir {
2222

23+
std::unique_ptr<Pass> createCIRCanonicalizePass();
2324
std::unique_ptr<Pass> createCIRFlattenCFGPass();
2425

2526
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);

clang/include/clang/CIR/Dialect/Passes.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@
1111

1212
include "mlir/Pass/PassBase.td"
1313

14+
def CIRCanonicalize : Pass<"cir-canonicalize"> {
15+
let summary = "Performs CIR canonicalization";
16+
let description = [{
17+
Perform canonicalizations on CIR and removes some redundant operations.
18+
19+
This pass performs basic cleanup and canonicalization transformations that
20+
are not intended to affect CIR-to-source fidelity and high-level code
21+
analysis passes. Example transformations performed in this pass include
22+
empty scope cleanup, trivial `try` cleanup, redundant branch cleanup, etc.
23+
Those more "heavyweight" transformations and those transformations that
24+
could significantly affect CIR-to-source fidelity are performed in the
25+
`cir-simplify` pass.
26+
}];
27+
28+
let constructor = "mlir::createCIRCanonicalizePass()";
29+
let dependentDialects = ["cir::CIRDialect"];
30+
}
31+
1432
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
1533
let summary = "Produces flatten CFG";
1634
let description = [{

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ struct MissingFeatures {
103103
static bool scalableVectors() { return false; }
104104
static bool unsizedTypes() { return false; }
105105
static bool vectorType() { return false; }
106+
107+
// Future CIR operations
108+
static bool labelOp() { return false; }
109+
static bool brCondOp() { return false; }
110+
static bool switchOp() { return false; }
111+
static bool tryOp() { return false; }
112+
static bool unaryOp() { return false; }
113+
static bool selectOp() { return false; }
114+
static bool complexCreateOp() { return false; }
115+
static bool complexRealOp() { return false; }
116+
static bool complexImagOp() { return false; }
117+
static bool callOp() { return false; }
106118
};
107119

108120
} // namespace cir

clang/include/clang/Driver/Options.td

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2978,6 +2978,15 @@ def fapple_link_rtlib : Flag<["-"], "fapple-link-rtlib">, Group<f_Group>,
29782978
HelpText<"Force linking the clang builtins runtime library">;
29792979

29802980
/// ClangIR-specific options - BEGIN
2981+
def clangir_disable_passes : Flag<["-"], "clangir-disable-passes">,
2982+
Visibility<[ClangOption, CC1Option]>,
2983+
HelpText<"Disable CIR transformations pipeline">,
2984+
MarshallingInfoFlag<FrontendOpts<"ClangIRDisablePasses">>;
2985+
def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">,
2986+
Visibility<[ClangOption, CC1Option]>,
2987+
HelpText<"ClangIR: Disable MLIR module verifier">,
2988+
MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>;
2989+
29812990
defm clangir : BoolFOption<"clangir",
29822991
FrontendOpts<"UseClangIRPipeline">, DefaultFalse,
29832992
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
@@ -4822,8 +4831,9 @@ def : Joined<["-"], "mllvm=">,
48224831
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Alias<mllvm>,
48234832
HelpText<"Alias for -mllvm">, MetaVarName<"<arg>">;
48244833
def mmlir : Separate<["-"], "mmlir">,
4825-
Visibility<[ClangOption, CLOption, FC1Option, FlangOption]>,
4826-
HelpText<"Additional arguments to forward to MLIR's option processing">;
4834+
Visibility<[ClangOption, CC1Option, FC1Option, FlangOption]>,
4835+
HelpText<"Additional arguments to forward to MLIR's option processing">,
4836+
MarshallingInfoStringVector<FrontendOpts<"MLIRArgs">>;
48274837
def ffuchsia_api_level_EQ : Joined<["-"], "ffuchsia-api-level=">,
48284838
Group<m_Group>, Visibility<[ClangOption, CC1Option]>,
48294839
HelpText<"Set Fuchsia API level">,

clang/include/clang/Frontend/FrontendOptions.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,14 @@ class FrontendOptions {
412412
LLVM_PREFERRED_TYPE(bool)
413413
unsigned UseClangIRPipeline : 1;
414414

415+
/// Disable Clang IR specific (CIR) passes
416+
LLVM_PREFERRED_TYPE(bool)
417+
unsigned ClangIRDisablePasses : 1;
418+
419+
/// Disable Clang IR (CIR) verifier
420+
LLVM_PREFERRED_TYPE(bool)
421+
unsigned ClangIRDisableCIRVerifier : 1;
422+
415423
CodeCompleteOptions CodeCompleteOpts;
416424

417425
/// Specifies the output format of the AST.
@@ -488,6 +496,10 @@ class FrontendOptions {
488496
/// should only be used for debugging and experimental features.
489497
std::vector<std::string> LLVMArgs;
490498

499+
/// A list of arguments to forward to MLIR's option processing; this
500+
/// should only be used for debugging and experimental features.
501+
std::vector<std::string> MLIRArgs;
502+
491503
/// File name of the file that will provide record layouts
492504
/// (in the format produced by -fdump-record-layouts).
493505
std::string OverrideRecordLayoutsFile;
@@ -533,7 +545,8 @@ class FrontendOptions {
533545
EmitExtensionSymbolGraphs(false),
534546
EmitSymbolGraphSymbolLabelsForTesting(false),
535547
EmitPrettySymbolGraphs(false), GenReducedBMI(false),
536-
UseClangIRPipeline(false), TimeTraceGranularity(500),
548+
UseClangIRPipeline(false), ClangIRDisablePasses(false),
549+
ClangIRDisableCIRVerifier(false), TimeTraceGranularity(500),
537550
TimeTraceVerbose(false) {}
538551

539552
/// getInputKindForExtension - Return the appropriate input kind for a file

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "mlir/IR/BuiltinOps.h"
2424
#include "mlir/IR/Location.h"
2525
#include "mlir/IR/MLIRContext.h"
26+
#include "mlir/IR/Verifier.h"
2627

2728
using namespace clang;
2829
using namespace clang::CIRGen;
@@ -488,6 +489,13 @@ mlir::Type CIRGenModule::convertType(QualType type) {
488489
return genTypes.convertType(type);
489490
}
490491

492+
bool CIRGenModule::verifyModule() const {
493+
// Verify the module after we have finished constructing it, this will
494+
// check the structural properties of the IR and invoke any specific
495+
// verifiers we have on the CIR operations.
496+
return mlir::verify(theModule).succeeded();
497+
}
498+
491499
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
492500
llvm::StringRef feature) {
493501
unsigned diagID = diags.getCustomDiagID(

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class CIRGenModule : public CIRGenTypeCache {
9191

9292
void emitTopLevelDecl(clang::Decl *decl);
9393

94+
bool verifyModule() const;
95+
9496
/// Return the address of the given function. If funcType is non-null, then
9597
/// this function will use the specified type if it has to create it.
9698
// TODO: this is a bit weird as `GetAddr` given we give back a FuncOp?

clang/lib/CIR/CodeGen/CIRGenerator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void CIRGenerator::Initialize(ASTContext &astContext) {
4040
*mlirContext.get(), astContext, codeGenOpts, diags);
4141
}
4242

43+
bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); }
44+
4345
mlir::ModuleOp CIRGenerator::getModule() const { return cgm->getModule(); }
4446

4547
bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements pass that canonicalizes CIR operations, eliminating
10+
// redundant branches, empty scopes, and other unnecessary operations.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "PassDetail.h"
15+
#include "mlir/Dialect/Func/IR/FuncOps.h"
16+
#include "mlir/IR/Block.h"
17+
#include "mlir/IR/Operation.h"
18+
#include "mlir/IR/PatternMatch.h"
19+
#include "mlir/IR/Region.h"
20+
#include "mlir/Support/LogicalResult.h"
21+
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
22+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
23+
#include "clang/CIR/Dialect/Passes.h"
24+
#include "clang/CIR/MissingFeatures.h"
25+
26+
using namespace mlir;
27+
using namespace cir;
28+
29+
namespace {
30+
31+
/// Removes branches between two blocks if it is the only branch.
32+
///
33+
/// From:
34+
/// ^bb0:
35+
/// cir.br ^bb1
36+
/// ^bb1: // pred: ^bb0
37+
/// cir.return
38+
///
39+
/// To:
40+
/// ^bb0:
41+
/// cir.return
42+
struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
43+
using OpRewritePattern<BrOp>::OpRewritePattern;
44+
45+
LogicalResult matchAndRewrite(BrOp op,
46+
PatternRewriter &rewriter) const final {
47+
Block *block = op.getOperation()->getBlock();
48+
Block *dest = op.getDest();
49+
50+
assert(!cir::MissingFeatures::labelOp());
51+
52+
// Single edge between blocks: merge it.
53+
if (block->getNumSuccessors() == 1 &&
54+
dest->getSinglePredecessor() == block) {
55+
rewriter.eraseOp(op);
56+
rewriter.mergeBlocks(dest, block);
57+
return success();
58+
}
59+
60+
return failure();
61+
}
62+
};
63+
64+
struct RemoveEmptyScope
65+
: public OpRewritePattern<ScopeOp>::SplitMatchAndRewrite {
66+
using SplitMatchAndRewrite::SplitMatchAndRewrite;
67+
68+
LogicalResult match(ScopeOp op) const final {
69+
// TODO: Remove this logic once CIR uses MLIR infrastructure to remove
70+
// trivially dead operations
71+
if (op.isEmpty())
72+
return success();
73+
74+
Region &region = op.getScopeRegion();
75+
if (region.getBlocks().front().getOperations().size() == 1)
76+
return success(isa<YieldOp>(region.getBlocks().front().front()));
77+
78+
return failure();
79+
}
80+
81+
void rewrite(ScopeOp op, PatternRewriter &rewriter) const final {
82+
rewriter.eraseOp(op);
83+
}
84+
};
85+
86+
//===----------------------------------------------------------------------===//
87+
// CIRCanonicalizePass
88+
//===----------------------------------------------------------------------===//
89+
90+
struct CIRCanonicalizePass : public CIRCanonicalizeBase<CIRCanonicalizePass> {
91+
using CIRCanonicalizeBase::CIRCanonicalizeBase;
92+
93+
// The same operation rewriting done here could have been performed
94+
// by CanonicalizerPass (adding hasCanonicalizer for target Ops and
95+
// implementing the same from above in CIRDialects.cpp). However, it's
96+
// currently too aggressive for static analysis purposes, since it might
97+
// remove things where a diagnostic can be generated.
98+
//
99+
// FIXME: perhaps we can add one more mode to GreedyRewriteConfig to
100+
// disable this behavior.
101+
void runOnOperation() override;
102+
};
103+
104+
void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) {
105+
// clang-format off
106+
patterns.add<
107+
RemoveRedundantBranches,
108+
RemoveEmptyScope
109+
>(patterns.getContext());
110+
// clang-format on
111+
}
112+
113+
void CIRCanonicalizePass::runOnOperation() {
114+
// Collect rewrite patterns.
115+
RewritePatternSet patterns(&getContext());
116+
populateCIRCanonicalizePatterns(patterns);
117+
118+
// Collect operations to apply patterns.
119+
llvm::SmallVector<Operation *, 16> ops;
120+
getOperation()->walk([&](Operation *op) {
121+
assert(!cir::MissingFeatures::brCondOp());
122+
assert(!cir::MissingFeatures::switchOp());
123+
assert(!cir::MissingFeatures::tryOp());
124+
assert(!cir::MissingFeatures::unaryOp());
125+
assert(!cir::MissingFeatures::selectOp());
126+
assert(!cir::MissingFeatures::complexCreateOp());
127+
assert(!cir::MissingFeatures::complexRealOp());
128+
assert(!cir::MissingFeatures::complexImagOp());
129+
assert(!cir::MissingFeatures::callOp());
130+
// CastOp here is to perform a manual `fold` in
131+
// applyOpPatternsGreedily
132+
if (isa<BrOp, ScopeOp, CastOp>(op))
133+
ops.push_back(op);
134+
});
135+
136+
// Apply patterns.
137+
if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
138+
signalPassFailure();
139+
}
140+
141+
} // namespace
142+
143+
std::unique_ptr<Pass> mlir::createCIRCanonicalizePass() {
144+
return std::make_unique<CIRCanonicalizePass>();
145+
}

clang/lib/CIR/Dialect/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_clang_library(MLIRCIRTransforms
2+
CIRCanonicalize.cpp
23
FlattenCFG.cpp
34

45
DEPENDS

0 commit comments

Comments
 (0)