Skip to content

[CIR] Build out AST consumer patterns to reach the entry point into CIRGen #91007

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

Merged
Show file tree
Hide file tree
Changes from all commits
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
60 changes: 60 additions & 0 deletions clang/include/clang/CIR/CIRGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===- CIRGenerator.h - CIR Generation from Clang AST ---------------------===//
//
// 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 a simple interface to perform CIR generation from Clang
// AST
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CIR_CIRGENERATOR_H
#define LLVM_CLANG_CIR_CIRGENERATOR_H

#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/CodeGenOptions.h"

#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/VirtualFileSystem.h"

#include <memory>

namespace clang {
class DeclGroupRef;
class DiagnosticsEngine;
} // namespace clang

namespace mlir {
class MLIRContext;
} // namespace mlir
namespace cir {
class CIRGenModule;

class CIRGenerator : public clang::ASTConsumer {
virtual void anchor();
clang::DiagnosticsEngine &diags;
clang::ASTContext *astCtx;
// Only used for debug info.
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs;

const clang::CodeGenOptions &codeGenOpts;

protected:
std::unique_ptr<mlir::MLIRContext> mlirCtx;
std::unique_ptr<CIRGenModule> cgm;

public:
CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const clang::CodeGenOptions &cgo);
~CIRGenerator() override;
void Initialize(clang::ASTContext &astCtx) override;
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
};

} // namespace cir

#endif // LLVM_CLANG_CIR_CIRGENERATOR_H
60 changes: 60 additions & 0 deletions clang/include/clang/CIR/FrontendAction/CIRGenAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===---- CIRGenAction.h - CIR Code Generation Frontend Action -*- C++ -*--===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CIR_CIRGENACTION_H
#define LLVM_CLANG_CIR_CIRGENACTION_H

#include "clang/Frontend/FrontendAction.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/OwningOpRef.h"

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

namespace cir {
class CIRGenConsumer;

class CIRGenAction : public clang::ASTFrontendAction {
public:
enum class OutputType {
EmitCIR,
};

private:
friend class CIRGenConsumer;

mlir::OwningOpRef<mlir::ModuleOp> MLIRMod;

mlir::MLIRContext *MLIRCtx;

protected:
CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr);

std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &CI,
llvm::StringRef InFile) override;

public:
~CIRGenAction() override;

OutputType Action;
};

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

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

} // namespace cir

#endif
2 changes: 1 addition & 1 deletion clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2996,7 +2996,7 @@ defm clangir : BoolFOption<"clangir",
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
NegFlag<SetFalse, [], [ClangOption, CC1Option], "Use the AST -> LLVM pipeline to compile">,
BothFlags<[], [ClangOption, CC1Option], "">>;
def emit_cir : Flag<["-"], "emit-cir">, Visibility<[CC1Option]>,
def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>,
Copy link
Member Author

Choose a reason for hiding this comment

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

@erichkeane @AaronBallman @MaskRay

I had to re-add the ClangOption here. lib/Driver/Driver.cpp builds the Action framework out based on input to the driver. So to properly orchestrate the outputs you need a driver flag to tell it that, e.g., we are emitting a .cir textual output. e.g. -fsyntax-only exists to chop off the frontend at the right point and -emit-llvm seems intentionally to have been made a driver flag to orchestrate .ll or .bc output.

Notably, -emit-llvm-bc does the wrong thing in all cases. clang -Xclang -emit-llvm-bc hello.c does the wrong thing and emits an executable. clang -Xclang -emit-llvm -S hello.c also does the wrong thing and emits a file named hello.s that is actually llvm bitcode.

Copy link
Member

@MaskRay MaskRay May 14, 2024

Choose a reason for hiding this comment

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

Notably, -emit-llvm-bc does the wrong thing in all cases. clang -Xclang -emit-llvm-bc hello.c does the wrong thing and emits an executable. clang -Xclang -emit-llvm -S hello.c also does the wrong thing and emits a file named hello.s that is actually llvm bitcode.

The description of #91140 might be useful.

You need -S or -c to disable linking. Then -c -Xclang -emit-cir can be used to override the assumed action passed by clangDriver to cc1.

-flto modifies -c to emit a bitcode file and -S to emit a textual IR file. -fcir can be modeled like -flto.

Copy link
Member Author

Choose a reason for hiding this comment

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

Then -c -Xclang -emit-cir can be used to override the assumed action passed by clangDriver to cc1.

It would be incorrect to only use -Xclang -emit-cir -c as it would output a .o file for a textual .cir file. I've plugged in ClangIR to the action building system. -c requests actions through the assembler. We're stopping with ClangIR via -emit-cir after the Compile phase.

-flto modifies -c to emit a bitcode file and -S to emit a textual IR file. -fcir can be modeled like -flto.

That's an inaccurate comparison. -fcir is supposed to only change from using codegen to cirgen&lowering. The output shouldn't change. -emit-cir is supposed to emit a file with the suffix .cir. The driver has to be told the preferred output type to tell the action system to emit a .cir suffixed file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@MaskRay are you okay with this approach? Also, if this is exposed as a driver flag, should we have a test in clang/test/Driver that it emits the expected -cc1 option?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Fangrui is out for a few weeks: #104899 (review)

Copy link
Member Author

Choose a reason for hiding this comment

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

To restate the argument here in favor of using a -emit-cir frontend flag:

  • This line shows the chunk of driver code responsible for mapping the requested emission to the output file. Without -emit-cir as a frontend flag we instead get .o, .s or some other incorrect output based on the -c, -S flags you pass to the frontend.
  • @MaskRay previously argued that we should use -fclangir to change the output. But this flag is orthogonal to the output. It merely enabled the ClangIR pipeline and, by design, does not change the output. We need some flag to tell the driver what the output kind is supposed to be.

Group<Action_Group>, HelpText<"Build ASTs and then lower to ClangIR">;
/// ClangIR-specific options - END

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include)
include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include)

add_subdirectory(Dialect)
add_subdirectory(CodeGen)
add_subdirectory(FrontendAction)
32 changes: 32 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===//
//
// 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 is the internal per-translation-unit state used for CIR translation.
//
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclBase.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"

using namespace cir;
CIRGenModule::CIRGenModule(mlir::MLIRContext &context,
clang::ASTContext &astctx,
const clang::CodeGenOptions &cgo,
DiagnosticsEngine &diags)
: astCtx(astctx), langOpts(astctx.getLangOpts()),
theModule{mlir::ModuleOp::create(mlir::UnknownLoc())},
target(astCtx.getTargetInfo()) {}

// Emit code for a single top level declaration.
void CIRGenModule::buildTopLevelDecl(Decl *decl) {}
62 changes: 62 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- C++ -*-===//
//
// 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 is the internal per-translation-unit state used for CIR translation.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H

#include "CIRGenTypeCache.h"

#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h"

namespace clang {
class ASTContext;
class CodeGenOptions;
class Decl;
class DiagnosticsEngine;
class LangOptions;
class TargetInfo;
} // namespace clang

using namespace clang;
namespace cir {

/// This class organizes the cross-function state that is used while generating
/// CIR code.
class CIRGenModule : public CIRGenTypeCache {
CIRGenModule(CIRGenModule &) = delete;
CIRGenModule &operator=(CIRGenModule &) = delete;

public:
CIRGenModule(mlir::MLIRContext &context, clang::ASTContext &astctx,
const clang::CodeGenOptions &cgo,
clang::DiagnosticsEngine &diags);

~CIRGenModule() = default;

private:
/// Hold Clang AST information.
clang::ASTContext &astCtx;

const clang::LangOptions &langOpts;

/// A "module" matches a c/cpp source file: containing a list of functions.
mlir::ModuleOp theModule;

const clang::TargetInfo &target;

public:
void buildTopLevelDecl(clang::Decl *decl);
};
} // namespace cir

#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
27 changes: 27 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===--- CIRGenTypeCache.h - Commonly used LLVM types and info -*- C++ --*-===//
//
// 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 structure provides a set of common types useful during CIR emission.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H
#define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H

namespace cir {

/// This structure provides a set of types that are commonly used
/// during IR emission. It's initialized once in CodeGenModule's
/// constructor and then copied around into new CIRGenFunction's.
struct CIRGenTypeCache {
CIRGenTypeCache() = default;
};

} // namespace cir

#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENTYPECACHE_H
43 changes: 43 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===//
//
// 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 builds an AST and converts it to CIR.
//
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"

#include "clang/AST/DeclGroup.h"
#include "clang/CIR/CIRGenerator.h"

using namespace cir;
using namespace clang;

void CIRGenerator::anchor() {}

CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
const CodeGenOptions &cgo)
: diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {}
CIRGenerator::~CIRGenerator() = default;

void CIRGenerator::Initialize(ASTContext &astCtx) {
using namespace llvm;

this->astCtx = &astCtx;

cgm = std::make_unique<CIRGenModule>(*mlirCtx, astCtx, codeGenOpts, diags);
}

bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {

for (Decl *decl : group)
cgm->buildTopLevelDecl(decl);

return true;
}
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set(
LLVM_LINK_COMPONENTS
Core
Support
)

get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

add_clang_library(clangCIR
CIRGenerator.cpp
CIRGenModule.cpp

DEPENDS
MLIRCIR
${dialect_libs}

LINK_LIBS
clangAST
clangBasic
clangLex
${dialect_libs}
MLIRCIR
)
Loading
Loading