Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR] Initial implementation of lowering CIR to MLIR #127835

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/CIR/CIRGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class CIRGenerator : public clang::ASTConsumer {
void Initialize(clang::ASTContext &astContext) override;
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
mlir::ModuleOp getModule() const;
mlir::MLIRContext &getContext() { return *mlirContext; }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mlir::MLIRContext &getContext() { return *mlirContext; }
mlir::MLIRContext &getMLIRContext() { return *mlirContext; }

MINOR preference? We have all sorts of contexts all over the place that these are incredibly confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's reasonable. I frequently find myself wondering which context things are returning (recently it's been the AST context coming up during IR generation).

Copy link
Contributor

Choose a reason for hiding this comment

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

A little while back I changed all the variable, parameter, and field names in the ClangIR code to be astContext or mlirContext, getting rid of all generic context or cxt names. I didn't do the same for function names. But it would be good if those were also changed.

};

} // namespace cir
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/CIR/FrontendAction/CIRGenAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class CIRGenAction : public clang::ASTFrontendAction {
EmitCIR,
EmitLLVM,
EmitBC,
EmitMLIR,
EmitObj,
};

Expand Down Expand Up @@ -59,6 +60,13 @@ class EmitCIRAction : public CIRGenAction {
EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr);
};

class EmitMLIRAction : public CIRGenAction {
virtual void anchor();

public:
EmitMLIRAction(mlir::MLIRContext *MLIRCtx = nullptr);
};

class EmitLLVMAction : public CIRGenAction {
virtual void anchor();

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/CIR/LowerToLLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Module;
} // namespace llvm

namespace mlir {
class MLIRContext;
class ModuleOp;
} // namespace mlir

Expand All @@ -30,6 +31,9 @@ std::unique_ptr<llvm::Module>
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule,
llvm::LLVMContext &llvmCtx);
} // namespace direct

mlir::ModuleOp lowerFromCIRToMLIR(mlir::ModuleOp mlirModule,
mlir::MLIRContext &mlirCtx);
} // namespace cir

#endif // CLANG_CIR_LOWERTOLLVM_H
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2958,6 +2958,8 @@ defm clangir : BoolFOption<"clangir",
BothFlags<[], [ClangOption, CC1Option], "">>;
def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>,
Group<Action_Group>, HelpText<"Build ASTs and then lower to ClangIR">;
def emit_cir_mlir : Flag<["-"], "emit-cir-mlir">, Visibility<[CC1Option]>, Group<Action_Group>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the incubator this is just "-emit-mlir" but that's claimed below by flang as an alias for "-emit-fir". The incubator just has that line commented out. I find it a bit disturbing that flang is affecting the namespace for clang command-line options, but I guess it's good to have some alignment between the two drivers.

I'm not entirely happy with the name I used here, as it seems to imply the opposite of what it is. It's emitting MLIR with no ClangIR dialect, but I'm not sure how to represent that without a very long name. Suggestions?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This IS a shame... IMO, we should bring in the Fortran folks (@clementval for suggestions?) and see if we can make this arrange a way to make this useful in both compilers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. One problem with aligning the two is that in flang, currently, -emit-mlir means to emit the fir dialect, whereas here we wanted it to mean, emit MLIR but not the cir dialect. This highlights the problem that "emit MLIR" isn't very specific. Even the interpretation used in the ClangIR incubator is emitting a handful of different dialects (affine, scf, arith, memref, vector, etc.).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah we have currently two options. -emit-fir and -emit-hlfir. These are dump the IR at different level. But as Andy mentioned, it is with the fir or hlfir dialect.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a perhaps a term that MEANS 'generic dialects' or something that we could use here instead?

perhaps emit-std-mlir ? And point out th at this is intending to lower to the standard dialects?

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't have a lowering to core dialects at the moment. At least two attempts have been made for that but nothing is upstream so far.

Copy link
Contributor

Choose a reason for hiding this comment

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

I double checked in flang and we have a couple of tests (7 files) using that option but we can switch these to use -emit-fir or -emit-hlfir.

@banach-space Since you are probably the initial author of this option, do you have any objection to make -emit-mlir identical for clang and flang?

Copy link
Contributor

Choose a reason for hiding this comment

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

No objections. In fact, -emit-mlir is a misnomer and emit_mlir_EQ would make more sense to me.

Does flang have the ability to lower to MLIR core dialects? I think that's what we're saying we'd like -emit-mlir with no argument to do in clang.

The semantics in Flang and Clang can differ. Whenever that's the case, please use HelpTextForVariant in Options.td to document that.

That said,-emit-mlir is very ambiguous - there's no single MLIR representation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we add emit_mlir_EQ will that peacefully coexist with the def emit_mlir : Flag<["-"], "emit-mlir">, Alias<emit_fir>; definition until we can update flang to align the behavior of the option with no argument?

Answering my own question, yes, it will peacefully coexist. At least, the clang part of it worked. I can't see a reason that flang would be impacted (functionally) by what I did here, but I didn't build and test flang.

I think it makes sense to put emit_mlir_EQ in here and then have a separate PR to have flang make use of this option, adding the additional flang dialects, and flang-specific help text.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it makes sense to put emit_mlir_EQ in here and then have a separate PR to have flang make use of this option, adding the additional flang dialects, and flang-specific help text.

Yeah we can address that in a follow up PR if it doesn't break flang with this one.

HelpText<"Build ASTs and then lower through ClangIR to MLIR, emit the .milr file">;
/// ClangIR-specific options - END

def flto_EQ : Joined<["-"], "flto=">,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ enum ActionKind {
/// Emit a .cir file
EmitCIR,

/// Emit a .mlir file
EmitMLIR,

/// Emit a .ll file.
EmitLLVM,

Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CIR/FrontendAction/CIRGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static BackendAction
getBackendActionFromOutputType(CIRGenAction::OutputType Action) {
switch (Action) {
case CIRGenAction::OutputType::EmitCIR:
case CIRGenAction::OutputType::EmitMLIR:
assert(false &&
"Unsupported output type for getBackendActionFromOutputType!");
break; // Unreachable, but fall through to report that
Expand Down Expand Up @@ -82,6 +83,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
void HandleTranslationUnit(ASTContext &C) override {
Gen->HandleTranslationUnit(C);
mlir::ModuleOp MlirModule = Gen->getModule();
mlir::MLIRContext &MlirCtx = Gen->getContext();
switch (Action) {
case CIRGenAction::OutputType::EmitCIR:
if (OutputStream && MlirModule) {
Expand All @@ -90,6 +92,15 @@ class CIRGenConsumer : public clang::ASTConsumer {
MlirModule->print(*OutputStream, Flags);
}
break;
case CIRGenAction::OutputType::EmitMLIR: {
auto LoweredMlirModule = lowerFromCIRToMLIR(MlirModule, MlirCtx);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please spell out the type.

assert(OutputStream && "No output stream when lowering to MLIR!");
// FIXME: we cannot roundtrip prettyForm=true right now.
mlir::OpPrintingFlags Flags;
Flags.enableDebugInfo(/*enable=*/true, /*prettyForm=*/false);
LoweredMlirModule->print(*OutputStream, Flags);
break;
}
case CIRGenAction::OutputType::EmitLLVM:
case CIRGenAction::OutputType::EmitBC:
case CIRGenAction::OutputType::EmitObj:
Expand Down Expand Up @@ -124,6 +135,8 @@ getOutputStream(CompilerInstance &CI, StringRef InFile,
return CI.createDefaultOutputFile(false, InFile, "s");
case CIRGenAction::OutputType::EmitCIR:
return CI.createDefaultOutputFile(false, InFile, "cir");
case CIRGenAction::OutputType::EmitMLIR:
return CI.createDefaultOutputFile(false, InFile, "mlir");
case CIRGenAction::OutputType::EmitLLVM:
return CI.createDefaultOutputFile(false, InFile, "ll");
case CIRGenAction::OutputType::EmitBC:
Expand Down Expand Up @@ -155,6 +168,10 @@ void EmitCIRAction::anchor() {}
EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
: CIRGenAction(OutputType::EmitCIR, MLIRCtx) {}

void EmitMLIRAction::anchor() {}
EmitMLIRAction::EmitMLIRAction(mlir::MLIRContext *MLIRCtx)
: CIRGenAction(OutputType::EmitMLIR, MLIRCtx) {}

void EmitLLVMAction::anchor() {}
EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx)
: CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/FrontendAction/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_clang_library(clangCIRFrontendAction
clangFrontend
clangCIR
clangCIRLoweringDirectToLLVM
clangCIRLoweringThroughMLIR
clangCodeGen
MLIRCIR
MLIRIR
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/Lowering/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(DirectToLLVM)
add_subdirectory(ThroughMLIR)
16 changes: 16 additions & 0 deletions clang/lib/CIR/Lowering/ThroughMLIR/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
set(LLVM_LINK_COMPONENTS
Core
Support
)

get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

add_clang_library(clangCIRLoweringThroughMLIR
LowerCIRToMLIR.cpp

DEPENDS
LINK_LIBS
MLIRIR
${dialect_libs}
MLIRCIR
)
201 changes: 201 additions & 0 deletions clang/lib/CIR/Lowering/ThroughMLIR/LowerCIRToMLIR.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//====- LowerCIRToMLIR.cpp - Lowering from CIR to MLIR --------------------===//
//
// 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 operations to MLIR.
//
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Transforms/DialectConversion.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/LowerToLLVM.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/TimeProfiler.h"

using namespace cir;
using namespace llvm;

namespace cir {

struct ConvertCIRToMLIRPass
: public mlir::PassWrapper<ConvertCIRToMLIRPass,
mlir::OperationPass<mlir::ModuleOp>> {
void getDependentDialects(mlir::DialectRegistry &registry) const override {
registry.insert<mlir::BuiltinDialect, mlir::memref::MemRefDialect>();
}
void runOnOperation() final;

StringRef getDescription() const override {
return "Convert the CIR dialect module to MLIR standard dialects";
}

StringRef getArgument() const override { return "cir-to-mlir"; }
};

class CIRGlobalOpLowering : public mlir::OpConversionPattern<cir::GlobalOp> {
public:
using OpConversionPattern<cir::GlobalOp>::OpConversionPattern;
mlir::LogicalResult
matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
auto moduleOp = op->getParentOfType<mlir::ModuleOp>();
if (!moduleOp)
return mlir::failure();

mlir::OpBuilder b(moduleOp.getContext());

const auto cirSymType = op.getSymType();
assert(!cir::MissingFeatures::convertTypeForMemory());
auto convertedType = getTypeConverter()->convertType(cirSymType);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please spell out the types...

if (!convertedType)
return mlir::failure();
auto memrefType = dyn_cast<mlir::MemRefType>(convertedType);
if (!memrefType)
memrefType = mlir::MemRefType::get({}, convertedType);
// Add an optional alignment to the global memref.
assert(!cir::MissingFeatures::opGlobalAlignment());
mlir::IntegerAttr memrefAlignment = mlir::IntegerAttr();
// Add an optional initial value to the global memref.
mlir::Attribute initialValue = mlir::Attribute();
std::optional<mlir::Attribute> init = op.getInitialValue();
if (init.has_value()) {
initialValue =
llvm::TypeSwitch<mlir::Attribute, mlir::Attribute>(init.value())
.Case<cir::IntAttr>([&](cir::IntAttr attr) {
auto rtt = mlir::RankedTensorType::get({}, convertedType);
return mlir::DenseIntElementsAttr::get(rtt, attr.getValue());
})
.Case<cir::FPAttr>([&](cir::FPAttr attr) {
auto rtt = mlir::RankedTensorType::get({}, convertedType);
return mlir::DenseFPElementsAttr::get(rtt, attr.getValue());
})
.Default([&](mlir::Attribute attr) {
llvm_unreachable("GlobalOp lowering with initial value is not "
"fully supported yet");
return mlir::Attribute();
});
}

// Add symbol visibility
assert(!cir::MissingFeatures::opGlobalLinkage());
std::string symVisibility = "public";

assert(!cir::MissingFeatures::opGlobalConstant());
bool isConstant = false;

rewriter.replaceOpWithNewOp<mlir::memref::GlobalOp>(
op, b.getStringAttr(op.getSymName()),
/*sym_visibility=*/b.getStringAttr(symVisibility),
/*type=*/memrefType, initialValue,
/*constant=*/isConstant,
/*alignment=*/memrefAlignment);

return mlir::success();
}
};

void populateCIRToMLIRConversionPatterns(mlir::RewritePatternSet &patterns,
mlir::TypeConverter &converter) {
patterns.add<CIRGlobalOpLowering>(converter, patterns.getContext());
}

static mlir::TypeConverter prepareTypeConverter() {
mlir::TypeConverter converter;
converter.addConversion([&](cir::PointerType type) -> mlir::Type {
assert(!cir::MissingFeatures::convertTypeForMemory());
mlir::Type ty = converter.convertType(type.getPointee());
// FIXME: The pointee type might not be converted (e.g. struct)
if (!ty)
return nullptr;
return mlir::MemRefType::get({}, ty);
});
converter.addConversion(
[&](mlir::IntegerType type) -> mlir::Type { return type; });
converter.addConversion(
[&](mlir::FloatType type) -> mlir::Type { return type; });
converter.addConversion([&](cir::VoidType type) -> mlir::Type { return {}; });
converter.addConversion([&](cir::IntType type) -> mlir::Type {
// arith dialect ops doesn't take signed integer -- drop cir sign here
return mlir::IntegerType::get(
type.getContext(), type.getWidth(),
mlir::IntegerType::SignednessSemantics::Signless);
});
converter.addConversion([&](cir::SingleType type) -> mlir::Type {
return mlir::Float32Type::get(type.getContext());
});
converter.addConversion([&](cir::DoubleType type) -> mlir::Type {
return mlir::Float64Type::get(type.getContext());
});
converter.addConversion([&](cir::FP80Type type) -> mlir::Type {
return mlir::Float80Type::get(type.getContext());
});
converter.addConversion([&](cir::LongDoubleType type) -> mlir::Type {
return converter.convertType(type.getUnderlying());
});
converter.addConversion([&](cir::FP128Type type) -> mlir::Type {
return mlir::Float128Type::get(type.getContext());
});
converter.addConversion([&](cir::FP16Type type) -> mlir::Type {
return mlir::Float16Type::get(type.getContext());
});
converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
return mlir::BFloat16Type::get(type.getContext());
});

return converter;
}

void ConvertCIRToMLIRPass::runOnOperation() {
auto module = getOperation();

auto converter = prepareTypeConverter();

mlir::RewritePatternSet patterns(&getContext());

populateCIRToMLIRConversionPatterns(patterns, converter);

mlir::ConversionTarget target(getContext());
target.addLegalOp<mlir::ModuleOp>();
target.addLegalDialect<mlir::memref::MemRefDialect>();
target.addIllegalDialect<cir::CIRDialect>();

if (failed(applyPartialConversion(module, target, std::move(patterns))))
signalPassFailure();
}

std::unique_ptr<mlir::Pass> createConvertCIRToMLIRPass() {
return std::make_unique<ConvertCIRToMLIRPass>();
}

mlir::ModuleOp lowerFromCIRToMLIR(mlir::ModuleOp mlirModule,
mlir::MLIRContext &mlirCtx) {
llvm::TimeTraceScope scope("Lower CIR To MLIR");

mlir::PassManager pm(&mlirCtx);

pm.addPass(createConvertCIRToMLIRPass());

auto result = !mlir::failed(pm.run(mlirModule));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please spell out the type.

if (!result)
llvm::report_fatal_error(
"The pass manager failed to lower CIR to MLIR standard dialects!");

// Now that we ran all the lowering passes, verify the final output.
if (mlirModule.verify().failed())
llvm::report_fatal_error(
"Verification of the final MLIR in standard dialects failed!");

return mlirModule;
}

} // namespace cir
2 changes: 2 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2740,6 +2740,7 @@ static const auto &getFrontendActionTable() {
{frontend::EmitAssembly, OPT_S},
{frontend::EmitBC, OPT_emit_llvm_bc},
{frontend::EmitCIR, OPT_emit_cir},
{frontend::EmitMLIR, OPT_emit_cir_mlir},
{frontend::EmitHTML, OPT_emit_html},
{frontend::EmitLLVM, OPT_emit_llvm},
{frontend::EmitLLVMOnly, OPT_emit_llvm_only},
Expand Down Expand Up @@ -4631,6 +4632,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
case frontend::EmitAssembly:
case frontend::EmitBC:
case frontend::EmitCIR:
case frontend::EmitMLIR:
case frontend::EmitHTML:
case frontend::EmitLLVM:
case frontend::EmitLLVMOnly:
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
return std::make_unique<cir::EmitCIRAction>();
#else
llvm_unreachable("CIR suppport not built into clang");
#endif
case EmitMLIR:
#if CLANG_ENABLE_CIR
return std::make_unique<cir::EmitMLIRAction>();
#else
llvm_unreachable("CIR suppport not built into clang");
#endif
case EmitHTML: return std::make_unique<HTMLPrintAction>();
case EmitLLVM: {
Expand Down
Loading