From 75ea7aed93ec8afa43634a41c2e94380ba0d671e Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 19 Feb 2025 09:08:37 -0800 Subject: [PATCH] [CIR] Add additional frontend actions (#127249) Add frontend actions to support emitting assembly, bitcode, and object files when compiling with ClangIR. This change also correctly sets and propagates the target triple in the MLIR and LLVM modules, which was a necessary prerequisite for emitting assembly and object files. --- .../clang/CIR/Dialect/IR/CIRDialect.td | 2 ++ .../clang/CIR/FrontendAction/CIRGenAction.h | 24 +++++++++++++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 3 ++ clang/lib/CIR/CodeGen/CIRGenModule.h | 4 +++ clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 29 ++++++++++++++++++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 11 +++++++ .../ExecuteCompilerInvocation.cpp | 21 ++++++++++++-- clang/test/CIR/emit-actions.cpp | 21 ++++++++++++++ 8 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/emit-actions.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 305a06427ed0..73759cfa9c3c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -28,6 +28,8 @@ def CIR_Dialect : Dialect { let useDefaultTypePrinterParser = 0; let extraClassDeclaration = [{ + static llvm::StringRef getTripleAttrName() { return "cir.triple"; } + void registerAttributes(); void registerTypes(); diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h index 5f9110bc83b8..99495f4718c5 100644 --- a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h +++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h @@ -25,8 +25,11 @@ class CIRGenConsumer; class CIRGenAction : public clang::ASTFrontendAction { public: enum class OutputType { + EmitAssembly, EmitCIR, EmitLLVM, + EmitBC, + EmitObj, }; private: @@ -63,6 +66,27 @@ public: EmitLLVMAction(mlir::MLIRContext *MLIRCtx = nullptr); }; +class EmitBCAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitBCAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + +class EmitAssemblyAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitAssemblyAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + +class EmitObjAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitObjAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + } // namespace cir #endif diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2615ae382cb8..cbecdf925aa5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -52,6 +52,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, DoubleTy = cir::DoubleType::get(&getMLIRContext()); FP80Ty = cir::FP80Type::get(&getMLIRContext()); FP128Ty = cir::FP128Type::get(&getMLIRContext()); + + theModule->setAttr(cir::CIRDialect::getTripleAttrName(), + builder.getStringAttr(getTriple().str())); } mlir::Location CIRGenModule::getLoc(SourceLocation cLoc) { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 1c7ed6377390..29bb4036218e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -21,7 +21,9 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringRef.h" +#include "llvm/TargetParser/Triple.h" namespace clang { class ASTContext; @@ -88,6 +90,8 @@ public: void emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative = false); + const llvm::Triple &getTriple() const { return target.getTriple(); } + /// Helpers to emit "not yet implemented" error diagnostics DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef); diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index eab6958ac8f6..0f686a36b982 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -27,8 +27,14 @@ getBackendActionFromOutputType(CIRGenAction::OutputType Action) { assert(false && "Unsupported output type for getBackendActionFromOutputType!"); break; // Unreachable, but fall through to report that + case CIRGenAction::OutputType::EmitAssembly: + return BackendAction::Backend_EmitAssembly; + case CIRGenAction::OutputType::EmitBC: + return BackendAction::Backend_EmitBC; case CIRGenAction::OutputType::EmitLLVM: return BackendAction::Backend_EmitLL; + case CIRGenAction::OutputType::EmitObj: + return BackendAction::Backend_EmitObj; } // We should only get here if a non-enum value is passed in or we went through // the assert(false) case above @@ -84,7 +90,10 @@ public: MlirModule->print(*OutputStream, Flags); } break; - case CIRGenAction::OutputType::EmitLLVM: { + case CIRGenAction::OutputType::EmitLLVM: + case CIRGenAction::OutputType::EmitBC: + case CIRGenAction::OutputType::EmitObj: + case CIRGenAction::OutputType::EmitAssembly: { llvm::LLVMContext LLVMCtx; std::unique_ptr LLVMModule = lowerFromCIRToLLVMIR(MlirModule, LLVMCtx); @@ -111,10 +120,16 @@ static std::unique_ptr getOutputStream(CompilerInstance &CI, StringRef InFile, CIRGenAction::OutputType Action) { switch (Action) { + case CIRGenAction::OutputType::EmitAssembly: + return CI.createDefaultOutputFile(false, InFile, "s"); case CIRGenAction::OutputType::EmitCIR: return CI.createDefaultOutputFile(false, InFile, "cir"); case CIRGenAction::OutputType::EmitLLVM: return CI.createDefaultOutputFile(false, InFile, "ll"); + case CIRGenAction::OutputType::EmitBC: + return CI.createDefaultOutputFile(true, InFile, "bc"); + case CIRGenAction::OutputType::EmitObj: + return CI.createDefaultOutputFile(true, InFile, "o"); } llvm_unreachable("Invalid CIRGenAction::OutputType"); } @@ -132,6 +147,10 @@ CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return Result; } +void EmitAssemblyAction::anchor() {} +EmitAssemblyAction::EmitAssemblyAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitAssembly, MLIRCtx) {} + void EmitCIRAction::anchor() {} EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} @@ -139,3 +158,11 @@ EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) void EmitLLVMAction::anchor() {} EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx) : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {} + +void EmitBCAction::anchor() {} +EmitBCAction::EmitBCAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitBC, MLIRCtx) {} + +void EmitObjAction::anchor() {} +EmitObjAction::EmitObjAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitObj, MLIRCtx) {} diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 74ff89346f3c..235b5a057852 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -114,6 +114,8 @@ struct ConvertCIRToLLVMPass } void runOnOperation() final; + void processCIRAttrs(mlir::ModuleOp module); + StringRef getDescription() const override { return "Convert the prepared CIR dialect module to LLVM dialect"; } @@ -271,6 +273,13 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, }); } +void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) { + // Lower the module attributes to LLVM equivalents. + if (auto tripleAttr = module->getAttr(cir::CIRDialect::getTripleAttrName())) + module->setAttr(mlir::LLVM::LLVMDialect::getTargetTripleAttrName(), + tripleAttr); +} + void ConvertCIRToLLVMPass::runOnOperation() { llvm::TimeTraceScope scope("Convert CIR to LLVM Pass"); @@ -283,6 +292,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add(converter, patterns.getContext(), dl); + processCIRAttrs(module); + mlir::ConversionTarget target(getContext()); target.addLegalOp(); target.addLegalDialect(); diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index c8d004163b96..bb3bb0aac78b 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -62,8 +62,18 @@ CreateFrontendBaseAction(CompilerInstance &CI) { return std::make_unique(); case DumpRawTokens: return std::make_unique(); case DumpTokens: return std::make_unique(); - case EmitAssembly: return std::make_unique(); - case EmitBC: return std::make_unique(); + case EmitAssembly: +#if CLANG_ENABLE_CIR + if (UseCIR) + return std::make_unique(); +#endif + return std::make_unique(); + case EmitBC: +#if CLANG_ENABLE_CIR + if (UseCIR) + return std::make_unique(); +#endif + return std::make_unique(); case EmitCIR: #if CLANG_ENABLE_CIR return std::make_unique(); @@ -80,7 +90,12 @@ CreateFrontendBaseAction(CompilerInstance &CI) { } case EmitLLVMOnly: return std::make_unique(); case EmitCodeGenOnly: return std::make_unique(); - case EmitObj: return std::make_unique(); + case EmitObj: +#if CLANG_ENABLE_CIR + if (UseCIR) + return std::make_unique(); +#endif + return std::make_unique(); case ExtractAPI: return std::make_unique(); case FixIt: return std::make_unique(); diff --git a/clang/test/CIR/emit-actions.cpp b/clang/test/CIR/emit-actions.cpp new file mode 100644 index 000000000000..94ddf23b3475 --- /dev/null +++ b/clang/test/CIR/emit-actions.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S %s -o - | FileCheck %s -check-prefix=ASM + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm-bc %s -o %t.bc +// RUN: llvm-dis %t.bc -o - | FileCheck %s -check-prefix=BC + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-obj %s -o %t.o +// RUN: llvm-objdump -t %t.o | FileCheck %s -check-prefix=OBJ + +// TODO: Make this test target-independent +// REQUIRES: x86-registered-target + +int x = 1; + +// BC: @x = dso_local global i32 1 + +// ASM: x: +// ASM: .long 1 +// ASM: .size x, 4 + +// OBJ: .data +// OBJ-SAME: x