[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.
This commit is contained in:
parent
cd26dd5595
commit
39ce99589b
@ -386,4 +386,12 @@ def warn_hlsl_langstd_minimal :
|
|||||||
Warning<"support for HLSL language version %0 is incomplete, "
|
Warning<"support for HLSL language version %0 is incomplete, "
|
||||||
"recommend using %1 instead">,
|
"recommend using %1 instead">,
|
||||||
InGroup<HLSLDXCCompat>;
|
InGroup<HLSLDXCCompat>;
|
||||||
|
|
||||||
|
// ClangIR frontend errors
|
||||||
|
def err_cir_to_cir_transform_failed : Error<
|
||||||
|
"CIR-to-CIR transformation failed">, DefaultFatal;
|
||||||
|
|
||||||
|
def err_cir_verification_failed_pre_passes : Error<
|
||||||
|
"CIR module verification error before running CIR-to-CIR passes">,
|
||||||
|
DefaultFatal;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,10 @@ public:
|
|||||||
void Initialize(clang::ASTContext &astContext) override;
|
void Initialize(clang::ASTContext &astContext) override;
|
||||||
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
|
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
|
||||||
mlir::ModuleOp getModule() const;
|
mlir::ModuleOp getModule() const;
|
||||||
|
mlir::MLIRContext &getMLIRContext() { return *mlirContext; };
|
||||||
|
const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; };
|
||||||
|
|
||||||
|
bool verifyModule() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cir
|
} // namespace cir
|
||||||
|
39
clang/include/clang/CIR/CIRToCIRPasses.h
Normal file
39
clang/include/clang/CIR/CIRToCIRPasses.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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 declares an interface for running CIR-to-CIR passes.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef CLANG_CIR_CIRTOCIRPASSES_H
|
||||||
|
#define CLANG_CIR_CIRTOCIRPASSES_H
|
||||||
|
|
||||||
|
#include "mlir/Pass/Pass.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
class ASTContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mlir {
|
||||||
|
class MLIRContext;
|
||||||
|
class ModuleOp;
|
||||||
|
} // namespace mlir
|
||||||
|
|
||||||
|
namespace cir {
|
||||||
|
|
||||||
|
// Run set of cleanup/prepare/etc passes CIR <-> CIR.
|
||||||
|
mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
|
||||||
|
mlir::MLIRContext &mlirCtx,
|
||||||
|
clang::ASTContext &astCtx,
|
||||||
|
bool enableVerifier);
|
||||||
|
|
||||||
|
} // namespace cir
|
||||||
|
|
||||||
|
#endif // CLANG_CIR_CIRTOCIRPASSES_H_
|
@ -20,6 +20,7 @@ class ASTContext;
|
|||||||
}
|
}
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
|
std::unique_ptr<Pass> createCIRCanonicalizePass();
|
||||||
std::unique_ptr<Pass> createCIRFlattenCFGPass();
|
std::unique_ptr<Pass> createCIRFlattenCFGPass();
|
||||||
|
|
||||||
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
|
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
|
||||||
|
@ -11,6 +11,24 @@
|
|||||||
|
|
||||||
include "mlir/Pass/PassBase.td"
|
include "mlir/Pass/PassBase.td"
|
||||||
|
|
||||||
|
def CIRCanonicalize : Pass<"cir-canonicalize"> {
|
||||||
|
let summary = "Performs CIR canonicalization";
|
||||||
|
let description = [{
|
||||||
|
Perform canonicalizations on CIR and removes some redundant operations.
|
||||||
|
|
||||||
|
This pass performs basic cleanup and canonicalization transformations that
|
||||||
|
are not intended to affect CIR-to-source fidelity and high-level code
|
||||||
|
analysis passes. Example transformations performed in this pass include
|
||||||
|
empty scope cleanup, trivial `try` cleanup, redundant branch cleanup, etc.
|
||||||
|
Those more "heavyweight" transformations and those transformations that
|
||||||
|
could significantly affect CIR-to-source fidelity are performed in the
|
||||||
|
`cir-simplify` pass.
|
||||||
|
}];
|
||||||
|
|
||||||
|
let constructor = "mlir::createCIRCanonicalizePass()";
|
||||||
|
let dependentDialects = ["cir::CIRDialect"];
|
||||||
|
}
|
||||||
|
|
||||||
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
|
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
|
||||||
let summary = "Produces flatten CFG";
|
let summary = "Produces flatten CFG";
|
||||||
let description = [{
|
let description = [{
|
||||||
|
@ -103,6 +103,18 @@ struct MissingFeatures {
|
|||||||
static bool scalableVectors() { return false; }
|
static bool scalableVectors() { return false; }
|
||||||
static bool unsizedTypes() { return false; }
|
static bool unsizedTypes() { return false; }
|
||||||
static bool vectorType() { return false; }
|
static bool vectorType() { return false; }
|
||||||
|
|
||||||
|
// Future CIR operations
|
||||||
|
static bool labelOp() { return false; }
|
||||||
|
static bool brCondOp() { return false; }
|
||||||
|
static bool switchOp() { return false; }
|
||||||
|
static bool tryOp() { return false; }
|
||||||
|
static bool unaryOp() { return false; }
|
||||||
|
static bool selectOp() { return false; }
|
||||||
|
static bool complexCreateOp() { return false; }
|
||||||
|
static bool complexRealOp() { return false; }
|
||||||
|
static bool complexImagOp() { return false; }
|
||||||
|
static bool callOp() { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cir
|
} // namespace cir
|
||||||
|
@ -2978,6 +2978,15 @@ def fapple_link_rtlib : Flag<["-"], "fapple-link-rtlib">, Group<f_Group>,
|
|||||||
HelpText<"Force linking the clang builtins runtime library">;
|
HelpText<"Force linking the clang builtins runtime library">;
|
||||||
|
|
||||||
/// ClangIR-specific options - BEGIN
|
/// ClangIR-specific options - BEGIN
|
||||||
|
def clangir_disable_passes : Flag<["-"], "clangir-disable-passes">,
|
||||||
|
Visibility<[ClangOption, CC1Option]>,
|
||||||
|
HelpText<"Disable CIR transformations pipeline">,
|
||||||
|
MarshallingInfoFlag<FrontendOpts<"ClangIRDisablePasses">>;
|
||||||
|
def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">,
|
||||||
|
Visibility<[ClangOption, CC1Option]>,
|
||||||
|
HelpText<"ClangIR: Disable MLIR module verifier">,
|
||||||
|
MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>;
|
||||||
|
|
||||||
defm clangir : BoolFOption<"clangir",
|
defm clangir : BoolFOption<"clangir",
|
||||||
FrontendOpts<"UseClangIRPipeline">, DefaultFalse,
|
FrontendOpts<"UseClangIRPipeline">, DefaultFalse,
|
||||||
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
|
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
|
||||||
@ -4822,8 +4831,9 @@ def : Joined<["-"], "mllvm=">,
|
|||||||
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Alias<mllvm>,
|
Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Alias<mllvm>,
|
||||||
HelpText<"Alias for -mllvm">, MetaVarName<"<arg>">;
|
HelpText<"Alias for -mllvm">, MetaVarName<"<arg>">;
|
||||||
def mmlir : Separate<["-"], "mmlir">,
|
def mmlir : Separate<["-"], "mmlir">,
|
||||||
Visibility<[ClangOption, CLOption, FC1Option, FlangOption]>,
|
Visibility<[ClangOption, CC1Option, FC1Option, FlangOption]>,
|
||||||
HelpText<"Additional arguments to forward to MLIR's option processing">;
|
HelpText<"Additional arguments to forward to MLIR's option processing">,
|
||||||
|
MarshallingInfoStringVector<FrontendOpts<"MLIRArgs">>;
|
||||||
def ffuchsia_api_level_EQ : Joined<["-"], "ffuchsia-api-level=">,
|
def ffuchsia_api_level_EQ : Joined<["-"], "ffuchsia-api-level=">,
|
||||||
Group<m_Group>, Visibility<[ClangOption, CC1Option]>,
|
Group<m_Group>, Visibility<[ClangOption, CC1Option]>,
|
||||||
HelpText<"Set Fuchsia API level">,
|
HelpText<"Set Fuchsia API level">,
|
||||||
|
@ -412,6 +412,14 @@ public:
|
|||||||
LLVM_PREFERRED_TYPE(bool)
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned UseClangIRPipeline : 1;
|
unsigned UseClangIRPipeline : 1;
|
||||||
|
|
||||||
|
/// Disable Clang IR specific (CIR) passes
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned ClangIRDisablePasses : 1;
|
||||||
|
|
||||||
|
/// Disable Clang IR (CIR) verifier
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned ClangIRDisableCIRVerifier : 1;
|
||||||
|
|
||||||
CodeCompleteOptions CodeCompleteOpts;
|
CodeCompleteOptions CodeCompleteOpts;
|
||||||
|
|
||||||
/// Specifies the output format of the AST.
|
/// Specifies the output format of the AST.
|
||||||
@ -488,6 +496,10 @@ public:
|
|||||||
/// should only be used for debugging and experimental features.
|
/// should only be used for debugging and experimental features.
|
||||||
std::vector<std::string> LLVMArgs;
|
std::vector<std::string> LLVMArgs;
|
||||||
|
|
||||||
|
/// A list of arguments to forward to MLIR's option processing; this
|
||||||
|
/// should only be used for debugging and experimental features.
|
||||||
|
std::vector<std::string> MLIRArgs;
|
||||||
|
|
||||||
/// File name of the file that will provide record layouts
|
/// File name of the file that will provide record layouts
|
||||||
/// (in the format produced by -fdump-record-layouts).
|
/// (in the format produced by -fdump-record-layouts).
|
||||||
std::string OverrideRecordLayoutsFile;
|
std::string OverrideRecordLayoutsFile;
|
||||||
@ -533,7 +545,8 @@ public:
|
|||||||
EmitExtensionSymbolGraphs(false),
|
EmitExtensionSymbolGraphs(false),
|
||||||
EmitSymbolGraphSymbolLabelsForTesting(false),
|
EmitSymbolGraphSymbolLabelsForTesting(false),
|
||||||
EmitPrettySymbolGraphs(false), GenReducedBMI(false),
|
EmitPrettySymbolGraphs(false), GenReducedBMI(false),
|
||||||
UseClangIRPipeline(false), TimeTraceGranularity(500),
|
UseClangIRPipeline(false), ClangIRDisablePasses(false),
|
||||||
|
ClangIRDisableCIRVerifier(false), TimeTraceGranularity(500),
|
||||||
TimeTraceVerbose(false) {}
|
TimeTraceVerbose(false) {}
|
||||||
|
|
||||||
/// getInputKindForExtension - Return the appropriate input kind for a file
|
/// getInputKindForExtension - Return the appropriate input kind for a file
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "mlir/IR/BuiltinOps.h"
|
#include "mlir/IR/BuiltinOps.h"
|
||||||
#include "mlir/IR/Location.h"
|
#include "mlir/IR/Location.h"
|
||||||
#include "mlir/IR/MLIRContext.h"
|
#include "mlir/IR/MLIRContext.h"
|
||||||
|
#include "mlir/IR/Verifier.h"
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace clang::CIRGen;
|
using namespace clang::CIRGen;
|
||||||
@ -488,6 +489,13 @@ mlir::Type CIRGenModule::convertType(QualType type) {
|
|||||||
return genTypes.convertType(type);
|
return genTypes.convertType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CIRGenModule::verifyModule() const {
|
||||||
|
// Verify the module after we have finished constructing it, this will
|
||||||
|
// check the structural properties of the IR and invoke any specific
|
||||||
|
// verifiers we have on the CIR operations.
|
||||||
|
return mlir::verify(theModule).succeeded();
|
||||||
|
}
|
||||||
|
|
||||||
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
|
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
|
||||||
llvm::StringRef feature) {
|
llvm::StringRef feature) {
|
||||||
unsigned diagID = diags.getCustomDiagID(
|
unsigned diagID = diags.getCustomDiagID(
|
||||||
|
@ -91,6 +91,8 @@ public:
|
|||||||
|
|
||||||
void emitTopLevelDecl(clang::Decl *decl);
|
void emitTopLevelDecl(clang::Decl *decl);
|
||||||
|
|
||||||
|
bool verifyModule() const;
|
||||||
|
|
||||||
/// Return the address of the given function. If funcType is non-null, then
|
/// Return the address of the given function. If funcType is non-null, then
|
||||||
/// this function will use the specified type if it has to create it.
|
/// this function will use the specified type if it has to create it.
|
||||||
// TODO: this is a bit weird as `GetAddr` given we give back a FuncOp?
|
// TODO: this is a bit weird as `GetAddr` given we give back a FuncOp?
|
||||||
|
@ -40,6 +40,8 @@ void CIRGenerator::Initialize(ASTContext &astContext) {
|
|||||||
*mlirContext.get(), astContext, codeGenOpts, diags);
|
*mlirContext.get(), astContext, codeGenOpts, diags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); }
|
||||||
|
|
||||||
mlir::ModuleOp CIRGenerator::getModule() const { return cgm->getModule(); }
|
mlir::ModuleOp CIRGenerator::getModule() const { return cgm->getModule(); }
|
||||||
|
|
||||||
bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {
|
bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {
|
||||||
|
145
clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
Normal file
145
clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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 canonicalizes CIR operations, eliminating
|
||||||
|
// redundant branches, empty scopes, and other unnecessary operations.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "PassDetail.h"
|
||||||
|
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||||
|
#include "mlir/IR/Block.h"
|
||||||
|
#include "mlir/IR/Operation.h"
|
||||||
|
#include "mlir/IR/PatternMatch.h"
|
||||||
|
#include "mlir/IR/Region.h"
|
||||||
|
#include "mlir/Support/LogicalResult.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 {
|
||||||
|
|
||||||
|
/// Removes branches between two blocks if it is the only branch.
|
||||||
|
///
|
||||||
|
/// From:
|
||||||
|
/// ^bb0:
|
||||||
|
/// cir.br ^bb1
|
||||||
|
/// ^bb1: // pred: ^bb0
|
||||||
|
/// cir.return
|
||||||
|
///
|
||||||
|
/// To:
|
||||||
|
/// ^bb0:
|
||||||
|
/// cir.return
|
||||||
|
struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
|
||||||
|
using OpRewritePattern<BrOp>::OpRewritePattern;
|
||||||
|
|
||||||
|
LogicalResult matchAndRewrite(BrOp op,
|
||||||
|
PatternRewriter &rewriter) const final {
|
||||||
|
Block *block = op.getOperation()->getBlock();
|
||||||
|
Block *dest = op.getDest();
|
||||||
|
|
||||||
|
assert(!cir::MissingFeatures::labelOp());
|
||||||
|
|
||||||
|
// Single edge between blocks: merge it.
|
||||||
|
if (block->getNumSuccessors() == 1 &&
|
||||||
|
dest->getSinglePredecessor() == block) {
|
||||||
|
rewriter.eraseOp(op);
|
||||||
|
rewriter.mergeBlocks(dest, block);
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
|
return failure();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RemoveEmptyScope
|
||||||
|
: public OpRewritePattern<ScopeOp>::SplitMatchAndRewrite {
|
||||||
|
using SplitMatchAndRewrite::SplitMatchAndRewrite;
|
||||||
|
|
||||||
|
LogicalResult match(ScopeOp op) const final {
|
||||||
|
// TODO: Remove this logic once CIR uses MLIR infrastructure to remove
|
||||||
|
// trivially dead operations
|
||||||
|
if (op.isEmpty())
|
||||||
|
return success();
|
||||||
|
|
||||||
|
Region ®ion = op.getScopeRegion();
|
||||||
|
if (region.getBlocks().front().getOperations().size() == 1)
|
||||||
|
return success(isa<YieldOp>(region.getBlocks().front().front()));
|
||||||
|
|
||||||
|
return failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rewrite(ScopeOp op, PatternRewriter &rewriter) const final {
|
||||||
|
rewriter.eraseOp(op);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// CIRCanonicalizePass
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
struct CIRCanonicalizePass : public CIRCanonicalizeBase<CIRCanonicalizePass> {
|
||||||
|
using CIRCanonicalizeBase::CIRCanonicalizeBase;
|
||||||
|
|
||||||
|
// The same operation rewriting done here could have been performed
|
||||||
|
// by CanonicalizerPass (adding hasCanonicalizer for target Ops and
|
||||||
|
// implementing the same from above in CIRDialects.cpp). However, it's
|
||||||
|
// currently too aggressive for static analysis purposes, since it might
|
||||||
|
// remove things where a diagnostic can be generated.
|
||||||
|
//
|
||||||
|
// FIXME: perhaps we can add one more mode to GreedyRewriteConfig to
|
||||||
|
// disable this behavior.
|
||||||
|
void runOnOperation() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) {
|
||||||
|
// clang-format off
|
||||||
|
patterns.add<
|
||||||
|
RemoveRedundantBranches,
|
||||||
|
RemoveEmptyScope
|
||||||
|
>(patterns.getContext());
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIRCanonicalizePass::runOnOperation() {
|
||||||
|
// Collect rewrite patterns.
|
||||||
|
RewritePatternSet patterns(&getContext());
|
||||||
|
populateCIRCanonicalizePatterns(patterns);
|
||||||
|
|
||||||
|
// Collect operations to apply patterns.
|
||||||
|
llvm::SmallVector<Operation *, 16> ops;
|
||||||
|
getOperation()->walk([&](Operation *op) {
|
||||||
|
assert(!cir::MissingFeatures::brCondOp());
|
||||||
|
assert(!cir::MissingFeatures::switchOp());
|
||||||
|
assert(!cir::MissingFeatures::tryOp());
|
||||||
|
assert(!cir::MissingFeatures::unaryOp());
|
||||||
|
assert(!cir::MissingFeatures::selectOp());
|
||||||
|
assert(!cir::MissingFeatures::complexCreateOp());
|
||||||
|
assert(!cir::MissingFeatures::complexRealOp());
|
||||||
|
assert(!cir::MissingFeatures::complexImagOp());
|
||||||
|
assert(!cir::MissingFeatures::callOp());
|
||||||
|
// CastOp here is to perform a manual `fold` in
|
||||||
|
// applyOpPatternsGreedily
|
||||||
|
if (isa<BrOp, ScopeOp, CastOp>(op))
|
||||||
|
ops.push_back(op);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply patterns.
|
||||||
|
if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
|
||||||
|
signalPassFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<Pass> mlir::createCIRCanonicalizePass() {
|
||||||
|
return std::make_unique<CIRCanonicalizePass>();
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
add_clang_library(MLIRCIRTransforms
|
add_clang_library(MLIRCIRTransforms
|
||||||
|
CIRCanonicalize.cpp
|
||||||
FlattenCFG.cpp
|
FlattenCFG.cpp
|
||||||
|
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
#include "clang/CIR/FrontendAction/CIRGenAction.h"
|
#include "clang/CIR/FrontendAction/CIRGenAction.h"
|
||||||
#include "mlir/IR/MLIRContext.h"
|
#include "mlir/IR/MLIRContext.h"
|
||||||
#include "mlir/IR/OwningOpRef.h"
|
#include "mlir/IR/OwningOpRef.h"
|
||||||
|
#include "clang/Basic/DiagnosticFrontend.h"
|
||||||
#include "clang/CIR/CIRGenerator.h"
|
#include "clang/CIR/CIRGenerator.h"
|
||||||
|
#include "clang/CIR/CIRToCIRPasses.h"
|
||||||
#include "clang/CIR/LowerToLLVM.h"
|
#include "clang/CIR/LowerToLLVM.h"
|
||||||
#include "clang/CodeGen/BackendUtil.h"
|
#include "clang/CodeGen/BackendUtil.h"
|
||||||
#include "clang/Frontend/CompilerInstance.h"
|
#include "clang/Frontend/CompilerInstance.h"
|
||||||
@ -59,6 +61,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
|
|||||||
ASTContext *Context{nullptr};
|
ASTContext *Context{nullptr};
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
|
||||||
std::unique_ptr<CIRGenerator> Gen;
|
std::unique_ptr<CIRGenerator> Gen;
|
||||||
|
const FrontendOptions &FEOptions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI,
|
CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI,
|
||||||
@ -66,7 +69,8 @@ public:
|
|||||||
: Action(Action), CI(CI), OutputStream(std::move(OS)),
|
: Action(Action), CI(CI), OutputStream(std::move(OS)),
|
||||||
FS(&CI.getVirtualFileSystem()),
|
FS(&CI.getVirtualFileSystem()),
|
||||||
Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS),
|
Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS),
|
||||||
CI.getCodeGenOpts())) {}
|
CI.getCodeGenOpts())),
|
||||||
|
FEOptions(CI.getFrontendOpts()) {}
|
||||||
|
|
||||||
void Initialize(ASTContext &Ctx) override {
|
void Initialize(ASTContext &Ctx) override {
|
||||||
assert(!Context && "initialized multiple times");
|
assert(!Context && "initialized multiple times");
|
||||||
@ -81,7 +85,30 @@ public:
|
|||||||
|
|
||||||
void HandleTranslationUnit(ASTContext &C) override {
|
void HandleTranslationUnit(ASTContext &C) override {
|
||||||
Gen->HandleTranslationUnit(C);
|
Gen->HandleTranslationUnit(C);
|
||||||
|
|
||||||
|
if (!FEOptions.ClangIRDisableCIRVerifier) {
|
||||||
|
if (!Gen->verifyModule()) {
|
||||||
|
CI.getDiagnostics().Report(
|
||||||
|
diag::err_cir_verification_failed_pre_passes);
|
||||||
|
llvm::report_fatal_error(
|
||||||
|
"CIR codegen: module verification error before running CIR passes");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mlir::ModuleOp MlirModule = Gen->getModule();
|
mlir::ModuleOp MlirModule = Gen->getModule();
|
||||||
|
mlir::MLIRContext &MlirCtx = Gen->getMLIRContext();
|
||||||
|
|
||||||
|
if (!FEOptions.ClangIRDisablePasses) {
|
||||||
|
// Setup and run CIR pipeline.
|
||||||
|
if (runCIRToCIRPasses(MlirModule, MlirCtx, C,
|
||||||
|
!FEOptions.ClangIRDisableCIRVerifier)
|
||||||
|
.failed()) {
|
||||||
|
CI.getDiagnostics().Report(diag::err_cir_to_cir_transform_failed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (Action) {
|
switch (Action) {
|
||||||
case CIRGenAction::OutputType::EmitCIR:
|
case CIRGenAction::OutputType::EmitCIR:
|
||||||
if (OutputStream && MlirModule) {
|
if (OutputStream && MlirModule) {
|
||||||
|
@ -15,8 +15,10 @@ add_clang_library(clangCIRFrontendAction
|
|||||||
|
|
||||||
LINK_LIBS
|
LINK_LIBS
|
||||||
clangAST
|
clangAST
|
||||||
|
clangBasic
|
||||||
clangFrontend
|
clangFrontend
|
||||||
clangCIR
|
clangCIR
|
||||||
|
clangCIRLoweringCommon
|
||||||
clangCIRLoweringDirectToLLVM
|
clangCIRLoweringDirectToLLVM
|
||||||
clangCodeGen
|
clangCodeGen
|
||||||
MLIRCIR
|
MLIRCIR
|
||||||
|
@ -11,9 +11,28 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
// #include "clang/AST/ASTContext.h"
|
// #include "clang/AST/ASTContext.h"
|
||||||
#include "clang/CIR/Dialect/Passes.h"
|
#include "mlir/IR/BuiltinOps.h"
|
||||||
|
|
||||||
#include "mlir/Pass/PassManager.h"
|
#include "mlir/Pass/PassManager.h"
|
||||||
|
#include "clang/CIR/Dialect/Passes.h"
|
||||||
|
#include "llvm/Support/TimeProfiler.h"
|
||||||
|
|
||||||
|
namespace cir {
|
||||||
|
mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
|
||||||
|
mlir::MLIRContext &mlirContext,
|
||||||
|
clang::ASTContext &astContext,
|
||||||
|
bool enableVerifier) {
|
||||||
|
|
||||||
|
llvm::TimeTraceScope scope("CIR To CIR Passes");
|
||||||
|
|
||||||
|
mlir::PassManager pm(&mlirContext);
|
||||||
|
pm.addPass(mlir::createCIRCanonicalizePass());
|
||||||
|
|
||||||
|
pm.enableVerifier(enableVerifier);
|
||||||
|
(void)mlir::applyPassManagerCLOptions(pm);
|
||||||
|
return pm.run(theModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cir
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
|
@ -1115,6 +1115,8 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx) {
|
|||||||
mlir::PassManager pm(mlirCtx);
|
mlir::PassManager pm(mlirCtx);
|
||||||
populateCIRToLLVMPasses(pm);
|
populateCIRToLLVMPasses(pm);
|
||||||
|
|
||||||
|
(void)mlir::applyPassManagerCLOptions(pm);
|
||||||
|
|
||||||
if (mlir::failed(pm.run(mlirModule))) {
|
if (mlir::failed(pm.run(mlirModule))) {
|
||||||
// FIXME: Handle any errors where they occurs and return a nullptr here.
|
// FIXME: Handle any errors where they occurs and return a nullptr here.
|
||||||
report_fatal_error(
|
report_fatal_error(
|
||||||
|
@ -5520,6 +5520,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||||||
options::OPT_Wa_COMMA,
|
options::OPT_Wa_COMMA,
|
||||||
options::OPT_Xassembler,
|
options::OPT_Xassembler,
|
||||||
options::OPT_mllvm,
|
options::OPT_mllvm,
|
||||||
|
options::OPT_mmlir,
|
||||||
};
|
};
|
||||||
for (const auto &A : Args)
|
for (const auto &A : Args)
|
||||||
if (llvm::is_contained(kBitcodeOptionIgnorelist, A->getOption().getID()))
|
if (llvm::is_contained(kBitcodeOptionIgnorelist, A->getOption().getID()))
|
||||||
@ -7778,6 +7779,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||||||
// features enabled through -Xclang -target-feature flags.
|
// features enabled through -Xclang -target-feature flags.
|
||||||
SanitizeArgs.addArgs(TC, Args, CmdArgs, InputType);
|
SanitizeArgs.addArgs(TC, Args, CmdArgs, InputType);
|
||||||
|
|
||||||
|
#if CLANG_ENABLE_CIR
|
||||||
|
// Forward -mmlir arguments to to the MLIR option parser.
|
||||||
|
for (const Arg *A : Args.filtered(options::OPT_mmlir)) {
|
||||||
|
A->claim();
|
||||||
|
A->render(Args, CmdArgs);
|
||||||
|
}
|
||||||
|
#endif // CLANG_ENABLE_CIR
|
||||||
|
|
||||||
// With -save-temps, we want to save the unoptimized bitcode output from the
|
// With -save-temps, we want to save the unoptimized bitcode output from the
|
||||||
// CompileJobAction, use -disable-llvm-passes to get pristine IR generated
|
// CompileJobAction, use -disable-llvm-passes to get pristine IR generated
|
||||||
// by the frontend.
|
// by the frontend.
|
||||||
|
@ -3105,6 +3105,14 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
|||||||
if (Args.hasArg(OPT_fclangir) || Args.hasArg(OPT_emit_cir))
|
if (Args.hasArg(OPT_fclangir) || Args.hasArg(OPT_emit_cir))
|
||||||
Opts.UseClangIRPipeline = true;
|
Opts.UseClangIRPipeline = true;
|
||||||
|
|
||||||
|
#if CLANG_ENABLE_CIR
|
||||||
|
if (Args.hasArg(OPT_clangir_disable_passes))
|
||||||
|
Opts.ClangIRDisablePasses = true;
|
||||||
|
|
||||||
|
if (Args.hasArg(OPT_clangir_disable_verifier))
|
||||||
|
Opts.ClangIRDisableCIRVerifier = true;
|
||||||
|
#endif // CLANG_ENABLE_CIR
|
||||||
|
|
||||||
if (Args.hasArg(OPT_aux_target_cpu))
|
if (Args.hasArg(OPT_aux_target_cpu))
|
||||||
Opts.AuxTargetCPU = std::string(Args.getLastArgValue(OPT_aux_target_cpu));
|
Opts.AuxTargetCPU = std::string(Args.getLastArgValue(OPT_aux_target_cpu));
|
||||||
if (Args.hasArg(OPT_aux_target_feature))
|
if (Args.hasArg(OPT_aux_target_feature))
|
||||||
|
@ -20,8 +20,17 @@ if(CLANG_ENABLE_CIR)
|
|||||||
)
|
)
|
||||||
list(APPEND link_libs
|
list(APPEND link_libs
|
||||||
clangCIRFrontendAction
|
clangCIRFrontendAction
|
||||||
|
MLIRCIRTransforms
|
||||||
MLIRIR
|
MLIRIR
|
||||||
|
MLIRPass
|
||||||
)
|
)
|
||||||
|
list(APPEND deps
|
||||||
|
MLIRBuiltinLocationAttributesIncGen
|
||||||
|
MLIRBuiltinTypeInterfacesIncGen
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include)
|
||||||
|
include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CLANG_ENABLE_STATIC_ANALYZER)
|
if(CLANG_ENABLE_STATIC_ANALYZER)
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
|
|
||||||
#if CLANG_ENABLE_CIR
|
#if CLANG_ENABLE_CIR
|
||||||
|
#include "mlir/IR/AsmState.h"
|
||||||
|
#include "mlir/IR/MLIRContext.h"
|
||||||
|
#include "mlir/Pass/PassManager.h"
|
||||||
|
#include "clang/CIR/Dialect/Passes.h"
|
||||||
#include "clang/CIR/FrontendAction/CIRGenAction.h"
|
#include "clang/CIR/FrontendAction/CIRGenAction.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -270,6 +274,22 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CLANG_ENABLE_CIR
|
||||||
|
if (!Clang->getFrontendOpts().MLIRArgs.empty()) {
|
||||||
|
mlir::registerCIRPasses();
|
||||||
|
mlir::registerMLIRContextCLOptions();
|
||||||
|
mlir::registerPassManagerCLOptions();
|
||||||
|
mlir::registerAsmPrinterCLOptions();
|
||||||
|
unsigned NumArgs = Clang->getFrontendOpts().MLIRArgs.size();
|
||||||
|
auto Args = std::make_unique<const char *[]>(NumArgs + 2);
|
||||||
|
Args[0] = "clang (MLIR option parsing)";
|
||||||
|
for (unsigned i = 0; i != NumArgs; ++i)
|
||||||
|
Args[i + 1] = Clang->getFrontendOpts().MLIRArgs[i].c_str();
|
||||||
|
Args[NumArgs + 1] = nullptr;
|
||||||
|
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// If there were errors in processing arguments, don't do anything else.
|
// If there were errors in processing arguments, don't do anything else.
|
||||||
if (Clang->getDiagnostics().hasErrorOccurred())
|
if (Clang->getDiagnostics().hasErrorOccurred())
|
||||||
return false;
|
return false;
|
||||||
|
61
clang/test/CIR/Transforms/canonicalize.cir
Normal file
61
clang/test/CIR/Transforms/canonicalize.cir
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// RUN: cir-opt %s -cir-canonicalize -o - | FileCheck %s
|
||||||
|
|
||||||
|
module {
|
||||||
|
cir.func @redundant_br() {
|
||||||
|
cir.br ^bb1
|
||||||
|
^bb1: // pred: ^bb0
|
||||||
|
%0 = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>, ["a", init] {alignment = 4 : i64}
|
||||||
|
%1 = cir.const #cir.int<4> : !cir.int<u, 32>
|
||||||
|
cir.store %1, %0 : !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>
|
||||||
|
cir.br ^bb2
|
||||||
|
^bb2: // pred: ^bb1
|
||||||
|
cir.return
|
||||||
|
}
|
||||||
|
// CHECK: cir.func @redundant_br() {
|
||||||
|
// CHECK-NEXT: %[[A:.*]] = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>, ["a", init] {alignment = 4 : i64}
|
||||||
|
// CHECK-NEXT: %[[FOUR:.*]] = cir.const #cir.int<4> : !cir.int<u, 32>
|
||||||
|
// CHECK-NEXT: cir.store %[[FOUR]], %[[A]] : !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>
|
||||||
|
// CHECK-NEXT: cir.return
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
|
||||||
|
cir.func @empty_scope() {
|
||||||
|
cir.scope {
|
||||||
|
}
|
||||||
|
cir.return
|
||||||
|
}
|
||||||
|
// CHECK: cir.func @empty_scope() {
|
||||||
|
// CHECK-NEXT: cir.return
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
|
||||||
|
cir.func @cast1(%arg0: !cir.bool) -> !cir.bool {
|
||||||
|
%0 = cir.cast(bool_to_int, %arg0 : !cir.bool), !cir.int<s, 32>
|
||||||
|
%1 = cir.cast(int_to_bool, %0 : !cir.int<s, 32>), !cir.bool
|
||||||
|
cir.return %1 : !cir.bool
|
||||||
|
}
|
||||||
|
// CHECK: cir.func @cast1(%[[ARG0:.*]]: !cir.bool) -> !cir.bool
|
||||||
|
// CHECK-NEXT: cir.return %[[ARG0]] : !cir.bool
|
||||||
|
|
||||||
|
cir.func @cast2(%arg0: !cir.int<s, 32>) -> !cir.bool {
|
||||||
|
%0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool
|
||||||
|
%1 = cir.cast(bool_to_int, %0 : !cir.bool), !cir.int<s, 32>
|
||||||
|
%2 = cir.cast(integral, %1 : !cir.int<s, 32>), !cir.int<s, 64>
|
||||||
|
%3 = cir.cast(int_to_bool, %2 : !cir.int<s, 64>), !cir.bool
|
||||||
|
cir.return %3 : !cir.bool
|
||||||
|
}
|
||||||
|
// CHECK: cir.func @cast2(%[[ARG0:.*]]: !cir.int<s, 32>) -> !cir.bool
|
||||||
|
// CHECK-NEXT: %[[CAST:.*]] = cir.cast(int_to_bool, %[[ARG0]] : !cir.int<s, 32>), !cir.bool
|
||||||
|
// CHECK-NEXT: cir.return %[[CAST]] : !cir.bool
|
||||||
|
|
||||||
|
cir.func @no_fold_cast(%arg0: !cir.int<s, 32>) -> !cir.int<s, 64> {
|
||||||
|
%0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool
|
||||||
|
%1 = cir.cast(bool_to_int, %0 : !cir.bool), !cir.int<s, 32>
|
||||||
|
%2 = cir.cast(integral, %1 : !cir.int<s, 32>), !cir.int<s, 64>
|
||||||
|
cir.return %2 : !cir.int<s, 64>
|
||||||
|
}
|
||||||
|
// CHECK: cir.func @no_fold_cast(%[[ARG0:.*]]: !cir.int<s, 32>) -> !cir.int<s, 64>
|
||||||
|
// CHECK-NEXT: %[[CAST:.*]] = cir.cast(int_to_bool, %[[ARG0]] : !cir.int<s, 32>), !cir.bool
|
||||||
|
// CHECK-NEXT: %[[CAST2:.*]] = cir.cast(bool_to_int, %[[CAST]] : !cir.bool), !cir.int<s, 32>
|
||||||
|
// CHECK-NEXT: %[[CAST3:.*]] = cir.cast(integral, %[[CAST2]] : !cir.int<s, 32>), !cir.int<s, 64>
|
||||||
|
// CHECK-NEXT: cir.return %[[CAST3]] : !cir.int<s, 64>
|
||||||
|
|
||||||
|
}
|
12
clang/test/CIR/mlirargs.c
Normal file
12
clang/test/CIR/mlirargs.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Clang returns 1 when wrong arguments are given.
|
||||||
|
// RUN: not %clang_cc1 -mmlir -mlir-disable-threadingd -mmlir -mlir-print-op-genericd 2>&1 | FileCheck %s --check-prefix=WRONG
|
||||||
|
// Test that the driver can pass mlir args to cc1.
|
||||||
|
// RUN: %clang -### -mmlir -mlir-disable-threading %s 2>&1 | FileCheck %s --check-prefix=CC1
|
||||||
|
|
||||||
|
|
||||||
|
// WRONG: clang (MLIR option parsing): Unknown command line argument '-mlir-disable-threadingd'. Try: 'clang (MLIR option parsing) --help'
|
||||||
|
// WRONG: clang (MLIR option parsing): Did you mean '--mlir-disable-threading'?
|
||||||
|
// WRONG: clang (MLIR option parsing): Unknown command line argument '-mlir-print-op-genericd'. Try: 'clang (MLIR option parsing) --help'
|
||||||
|
// WRONG: clang (MLIR option parsing): Did you mean '--mlir-print-op-generic'?
|
||||||
|
|
||||||
|
// CC1: "-mmlir" "-mlir-disable-threading"
|
14
clang/test/CIR/mlprint.c
Normal file
14
clang/test/CIR/mlprint.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-after-all %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=CIR
|
||||||
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -mmlir --mlir-print-ir-after-all -mllvm -print-after-all %s -o %t.ll 2>&1 | FileCheck %s -check-prefix=CIR -check-prefix=LLVM
|
||||||
|
|
||||||
|
int foo(void) {
|
||||||
|
int i = 3;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIR: IR Dump After CIRCanonicalize (cir-canonicalize)
|
||||||
|
// CIR: cir.func @foo() -> !cir.int<s, 32>
|
||||||
|
// LLVM: IR Dump After cir::direct::ConvertCIRToLLVMPass (cir-flat-to-llvm)
|
||||||
|
// LLVM: llvm.func @foo() -> i32
|
||||||
|
// LLVM: IR Dump After
|
||||||
|
// LLVM: define{{.*}} i32 @foo()
|
@ -34,6 +34,10 @@ int main(int argc, char **argv) {
|
|||||||
registry.insert<mlir::BuiltinDialect, cir::CIRDialect,
|
registry.insert<mlir::BuiltinDialect, cir::CIRDialect,
|
||||||
mlir::memref::MemRefDialect, mlir::LLVM::LLVMDialect>();
|
mlir::memref::MemRefDialect, mlir::LLVM::LLVMDialect>();
|
||||||
|
|
||||||
|
::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||||
|
return mlir::createCIRCanonicalizePass();
|
||||||
|
});
|
||||||
|
|
||||||
mlir::PassPipelineRegistration<CIRToLLVMPipelineOptions> pipeline(
|
mlir::PassPipelineRegistration<CIRToLLVMPipelineOptions> pipeline(
|
||||||
"cir-to-llvm", "",
|
"cir-to-llvm", "",
|
||||||
[](mlir::OpPassManager &pm, const CIRToLLVMPipelineOptions &options) {
|
[](mlir::OpPassManager &pm, const CIRToLLVMPipelineOptions &options) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user