Skip to content

Commit 10a6422

Browse files
committed
Update on "[CIR][CIRGen] Support mutable and recursive named records"
This allows a named StructType to be mutated after it has been created, if it is identified and incomplete. The motivation for this is to improve the codegen of CIR in certain scenarios where an incomplete type is used and later completed. These usually leave the IR in an inconsistent state, where there are two records types with the same identifier but different definitions (one complete the other incomplete). For example: ```c++ struct Node { Node *next; }; void test(struct Node n) {} ``` Generates: ```mlir !temp_struct = !cir.struct<struct "Node" incomplete> !full_struct = !cir.struct<struct "Node" {!cir.ptr<!temp_struct>}> ``` To generate the `Node` struct type, its members must be created first. However, the `next` member is a recursive reference, so it can only be completed after its parent. This generates a temporary incomplete definition of the `Node` type that remains in the code even after the type to which it refers is completed. As a consequence, accessing the `next` member of a `Node` value fetches the old incomplete version of the type which affects CIR's type-checking capabilities. This patch ensures that, once the parent is fully visited, the `next` member can be completed in place, automatically updating any references to it at a low cost. To represent recursive types, the StructType now is equipped with self-references. These are represented by a `cir.struct` type with just the name of the parent struct that it refers to. The same snippet of code will not generate the following CIR IR: ```mlir !full_struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct "Node">>}> ``` Summary of the changes made: - Named records are now uniquely identified by their name. An attempt to create a new record with the same will fail. - Anonymous records are uniquely identified by members and other relevant attributes. - StructType has a new `mutate` method that allows it to be mutated after it has been created. Each type can only be mutated if it is identified and incomplete, rendering further changes impossible. - When building a new name StructType, the builder will try to first create, then complete the type, ensuring that: - Inexistent types are created - Existing incomplete types are completed - Existing complete types with matching attributes are reused - Existing complete types with different attributes raise errors - StructType now uses the CyclicParser/Printer guard to avoid infinite recursion and identify when it should print/parse a self-reference. [ghstack-poisoned]
2 parents 4f588a0 + b1dfd63 commit 10a6422

File tree

4 files changed

+20
-10
lines changed

4 files changed

+20
-10
lines changed

clang/include/clang/CIR/LowerToLLVM.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ namespace cir {
3131
namespace direct {
3232
std::unique_ptr<llvm::Module>
3333
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp theModule,
34-
llvm::LLVMContext &llvmCtx);
34+
llvm::LLVMContext &llvmCtx,
35+
bool disableVerifier = false);
3536
}
3637

3738
// Lower directly from pristine CIR to LLVMIR.

clang/lib/CIR/FrontendAction/CIRGenAction.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,14 @@ static std::string sanitizePassOptions(llvm::StringRef o) {
7272

7373
namespace cir {
7474

75-
static std::unique_ptr<llvm::Module> lowerFromCIRToLLVMIR(
76-
const clang::FrontendOptions &feOptions, mlir::ModuleOp mlirMod,
77-
std::unique_ptr<mlir::MLIRContext> mlirCtx, llvm::LLVMContext &llvmCtx) {
75+
static std::unique_ptr<llvm::Module>
76+
lowerFromCIRToLLVMIR(const clang::FrontendOptions &feOptions,
77+
mlir::ModuleOp mlirMod,
78+
std::unique_ptr<mlir::MLIRContext> mlirCtx,
79+
llvm::LLVMContext &llvmCtx, bool disableVerifier = false) {
7880
if (feOptions.ClangIRDirectLowering)
79-
return direct::lowerDirectlyFromCIRToLLVMIR(mlirMod, llvmCtx);
81+
return direct::lowerDirectlyFromCIRToLLVMIR(mlirMod, llvmCtx,
82+
disableVerifier);
8083
else
8184
return lowerFromCIRToMLIRToLLVMIR(mlirMod, std::move(mlirCtx), llvmCtx);
8285
}
@@ -245,7 +248,8 @@ class CIRGenConsumer : public clang::ASTConsumer {
245248
case CIRGenAction::OutputType::EmitLLVM: {
246249
llvm::LLVMContext llvmCtx;
247250
auto llvmModule =
248-
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx);
251+
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx,
252+
feOptions.ClangIRDisableCIRVerifier);
249253

250254
llvmModule->setTargetTriple(targetOptions.Triple);
251255

@@ -259,7 +263,8 @@ class CIRGenConsumer : public clang::ASTConsumer {
259263
case CIRGenAction::OutputType::EmitObj: {
260264
llvm::LLVMContext llvmCtx;
261265
auto llvmModule =
262-
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx);
266+
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx,
267+
feOptions.ClangIRDisableCIRVerifier);
263268

264269
llvmModule->setTargetTriple(targetOptions.Triple);
265270
EmitBackendOutput(diagnosticsEngine, headerSearchOptions, codeGenOptions,
@@ -272,7 +277,8 @@ class CIRGenConsumer : public clang::ASTConsumer {
272277
case CIRGenAction::OutputType::EmitAssembly: {
273278
llvm::LLVMContext llvmCtx;
274279
auto llvmModule =
275-
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx);
280+
lowerFromCIRToLLVMIR(feOptions, mlirMod, std::move(mlirCtx), llvmCtx,
281+
feOptions.ClangIRDisableCIRVerifier);
276282

277283
llvmModule->setTargetTriple(targetOptions.Triple);
278284
EmitBackendOutput(diagnosticsEngine, headerSearchOptions, codeGenOptions,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2160,7 +2160,8 @@ std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
21602160
extern void registerCIRDialectTranslation(mlir::MLIRContext &context);
21612161

21622162
std::unique_ptr<llvm::Module>
2163-
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp theModule, LLVMContext &llvmCtx) {
2163+
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp theModule, LLVMContext &llvmCtx,
2164+
bool disableVerifier) {
21642165
mlir::MLIRContext *mlirCtx = theModule.getContext();
21652166
mlir::PassManager pm(mlirCtx);
21662167

@@ -2177,6 +2178,7 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp theModule, LLVMContext &llvmCtx) {
21772178
// emmited and how to properly avoid them.
21782179
pm.addPass(mlir::createReconcileUnrealizedCastsPass());
21792180

2181+
pm.enableVerifier(!disableVerifier);
21802182
(void)mlir::applyPassManagerCLOptions(pm);
21812183

21822184
auto result = !mlir::failed(pm.run(theModule));

clang/tools/cir-translate/cir-translate.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace direct {
2626
extern void registerCIRDialectTranslation(mlir::DialectRegistry &registry);
2727
extern std::unique_ptr<llvm::Module>
2828
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp theModule,
29-
llvm::LLVMContext &llvmCtx);
29+
llvm::LLVMContext &llvmCtx,
30+
bool disableVerifier = false);
3031
} // namespace direct
3132
}
3233

0 commit comments

Comments
 (0)