[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.
This commit is contained in:
Andy Kaylor
2025-02-19 09:08:37 -08:00
committed by GitHub
parent ddf24086f1
commit 75ea7aed93
8 changed files with 111 additions and 4 deletions

View File

@@ -28,6 +28,8 @@ def CIR_Dialect : Dialect {
let useDefaultTypePrinterParser = 0;
let extraClassDeclaration = [{
static llvm::StringRef getTripleAttrName() { return "cir.triple"; }
void registerAttributes();
void registerTypes();

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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<llvm::Module> LLVMModule =
lowerFromCIRToLLVMIR(MlirModule, LLVMCtx);
@@ -111,10 +120,16 @@ static std::unique_ptr<raw_pwrite_stream>
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) {}

View File

@@ -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<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
processCIRAttrs(module);
mlir::ConversionTarget target(getContext());
target.addLegalOp<mlir::ModuleOp>();
target.addLegalDialect<mlir::LLVM::LLVMDialect>();

View File

@@ -62,8 +62,18 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
return std::make_unique<DumpCompilerOptionsAction>();
case DumpRawTokens: return std::make_unique<DumpRawTokensAction>();
case DumpTokens: return std::make_unique<DumpTokensAction>();
case EmitAssembly: return std::make_unique<EmitAssemblyAction>();
case EmitBC: return std::make_unique<EmitBCAction>();
case EmitAssembly:
#if CLANG_ENABLE_CIR
if (UseCIR)
return std::make_unique<cir::EmitAssemblyAction>();
#endif
return std::make_unique<EmitAssemblyAction>();
case EmitBC:
#if CLANG_ENABLE_CIR
if (UseCIR)
return std::make_unique<cir::EmitBCAction>();
#endif
return std::make_unique<EmitBCAction>();
case EmitCIR:
#if CLANG_ENABLE_CIR
return std::make_unique<cir::EmitCIRAction>();
@@ -80,7 +90,12 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
}
case EmitLLVMOnly: return std::make_unique<EmitLLVMOnlyAction>();
case EmitCodeGenOnly: return std::make_unique<EmitCodeGenOnlyAction>();
case EmitObj: return std::make_unique<EmitObjAction>();
case EmitObj:
#if CLANG_ENABLE_CIR
if (UseCIR)
return std::make_unique<cir::EmitObjAction>();
#endif
return std::make_unique<EmitObjAction>();
case ExtractAPI:
return std::make_unique<ExtractAPIAction>();
case FixIt: return std::make_unique<FixItAction>();

View File

@@ -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