From db2e245d6be4283c725dcdcdbed42494af6128df Mon Sep 17 00:00:00 2001 From: Kirill Yansitov <36601354+YazZz1k@users.noreply.github.com> Date: Thu, 8 Feb 2024 23:02:17 +0300 Subject: [PATCH] [CIR][CIRGen] Add codegen for global compound literals (#454) This PR adds support for global compound literals. The implementation is almost the same as in original codegen. But the original codegen can reuse the value of emitted compound literal global variable in case then the init expression of new variable and this variable are the same. It's easy to implement this feature. But I can't find any test-case then this feature will be applied. So I decided to ignore this optimization opportunity to avoid mistakes. --- clang/lib/CIR/CodeGen/CIRGenCstEmitter.h | 7 +++ clang/lib/CIR/CodeGen/CIRGenExprConst.cpp | 56 +++++++++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenModule.h | 6 +++ clang/test/CIR/CodeGen/compound-literal.c | 29 ++++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/CodeGen/compound-literal.c diff --git a/clang/lib/CIR/CodeGen/CIRGenCstEmitter.h b/clang/lib/CIR/CodeGen/CIRGenCstEmitter.h index 5c9e545f227f..086c68baec9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCstEmitter.h +++ b/clang/lib/CIR/CodeGen/CIRGenCstEmitter.h @@ -67,9 +67,14 @@ class ConstantEmitter { /// Is the current emission context abstract? bool isAbstract() const { return Abstract; } + bool isInConstantContext() const { return InConstantContext; } + void setInConstantContext(bool var) { InConstantContext = var; } + /// Try to emit the initiaizer of the given declaration as an abstract /// constant. If this succeeds, the emission must be finalized. mlir::Attribute tryEmitForInitializer(const VarDecl &D); + mlir::Attribute tryEmitForInitializer(const Expr *E, LangAS destAddrSpace, + QualType destType); void finalize(mlir::cir::GlobalOp global); @@ -106,6 +111,8 @@ class ConstantEmitter { mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value, QualType T); + mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE); + // These are private helper routines of the constant emitter that // can't actually be private because things are split out into helper // functions and classes. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp index 3ed5e60f84f9..fac38ab1bb84 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp @@ -840,8 +840,9 @@ class ConstExprEmitter mlir::Attribute VisitStmt(Stmt *S, QualType T) { return nullptr; } mlir::Attribute VisitConstantExpr(ConstantExpr *CE, QualType T) { - assert(0 && "unimplemented"); - return {}; + if (mlir::Attribute Result = Emitter.tryEmitConstantExpr(CE)) + return Result; + return Visit(CE->getSubExpr(), T); } mlir::Attribute VisitParenExpr(ParenExpr *PE, QualType T) { @@ -1368,6 +1369,34 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { return Visit(base.get()); } +static ConstantLValue +tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter, + const CompoundLiteralExpr *E) { + CIRGenModule &CGM = emitter.CGM; + + LangAS addressSpace = E->getType().getAddressSpace(); + mlir::Attribute C = emitter.tryEmitForInitializer(E->getInitializer(), + addressSpace, E->getType()); + if (!C) { + assert(!E->isFileScope() && + "file-scope compound literal did not have constant initializer!"); + return nullptr; + } + + auto GV = CIRGenModule::createGlobalOp( + CGM, CGM.getLoc(E->getSourceRange()), + CGM.createGlobalCompoundLiteralName(), + CGM.getTypes().convertTypeForMem(E->getType()), + E->getType().isConstantStorage(CGM.getASTContext(), false, false)); + GV.setInitialValueAttr(C); + GV.setLinkage(mlir::cir::GlobalLinkageKind::InternalLinkage); + CharUnits Align = CGM.getASTContext().getTypeAlignInChars(E->getType()); + GV.setAlignment(Align.getAsAlign().value()); + + emitter.finalize(GV); + return CGM.getBuilder().getGlobalViewAttr(GV); +} + ConstantLValue ConstantLValueEmitter::VisitConstantExpr(const ConstantExpr *E) { assert(0 && "NYI"); return Visit(E->getSubExpr()); @@ -1375,8 +1404,9 @@ ConstantLValue ConstantLValueEmitter::VisitConstantExpr(const ConstantExpr *E) { ConstantLValue ConstantLValueEmitter::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { - assert(0 && "NYI"); - return nullptr; + ConstantEmitter CompoundLiteralEmitter(CGM, Emitter.CGF); + CompoundLiteralEmitter.setInConstantContext(Emitter.isInConstantContext()); + return tryEmitGlobalCompoundLiteral(CompoundLiteralEmitter, E); } ConstantLValue @@ -1457,6 +1487,13 @@ mlir::Attribute ConstantEmitter::tryEmitForInitializer(const VarDecl &D) { return markIfFailed(tryEmitPrivateForVarInit(D)); } +mlir::Attribute ConstantEmitter::tryEmitForInitializer(const Expr *E, + LangAS destAddrSpace, + QualType destType) { + initializeNonAbstract(destAddrSpace); + return markIfFailed(tryEmitPrivateForMemory(E, destType)); +} + void ConstantEmitter::finalize(mlir::cir::GlobalOp global) { assert(InitializedNonAbstract && "finalizing emitter that was used for abstract emission?"); @@ -1549,6 +1586,17 @@ mlir::Attribute ConstantEmitter::tryEmitAbstract(const APValue &value, return validateAndPopAbstract(C, state); } +mlir::Attribute ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *CE) { + if (!CE->hasAPValueResult()) + return nullptr; + + QualType RetType = CE->getType(); + if (CE->isGLValue()) + RetType = CGM.getASTContext().getLValueReferenceType(RetType); + + return emitAbstract(CE->getBeginLoc(), CE->getAPValueResult(), RetType); +} + mlir::Attribute ConstantEmitter::tryEmitAbstractForMemory(const Expr *E, QualType destType) { auto nonMemoryDestType = getNonMemoryType(CGM, destType); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index b200b210b888..a598400dd80c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -314,6 +314,12 @@ class CIRGenModule : public CIRGenTypeCache { StringRef Name = ".str"); unsigned StringLiteralCnt = 0; + unsigned CompoundLitaralCnt = 0; + /// Return the unique name for global compound literal + std::string createGlobalCompoundLiteralName() { + return (Twine(".compoundLiteral.") + Twine(CompoundLitaralCnt++)).str(); + } + /// Return the AST address space of constant literal, which is used to emit /// the constant literal as global variable in LLVM IR. /// Note: This is not necessarily the address space of the constant literal diff --git a/clang/test/CIR/CodeGen/compound-literal.c b/clang/test/CIR/CodeGen/compound-literal.c new file mode 100644 index 000000000000..327774c74f9f --- /dev/null +++ b/clang/test/CIR/CodeGen/compound-literal.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -S -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + + +typedef struct { + int *arr; +} S; + +S a = { + .arr = (int[]){} +}; + +// CIR: cir.global "private" internal @".compoundLiteral.0" = #cir.zero : !cir.array {alignment = 4 : i64} +// CIR: cir.global external @a = #cir.const_struct<{#cir.global_view<@".compoundLiteral.0"> : !cir.ptr}> : !ty_22S22 + +// LLVM: @.compoundLiteral.0 = internal global [0 x i32] zeroinitializer +// LLVM: @a = global %struct.S { ptr @.compoundLiteral.0 } + +S b = { + .arr = (int[]){1} +}; + +// CIR: cir.global "private" internal @".compoundLiteral.1" = #cir.const_array<[#cir.int<1> : !s32i]> : !cir.array {alignment = 4 : i64} +// CIR: cir.global external @b = #cir.const_struct<{#cir.global_view<@".compoundLiteral.1"> : !cir.ptr}> : !ty_22S22 + +// LLVM: @.compoundLiteral.1 = internal global [1 x i32] [i32 1] +// LLVM: @b = global %struct.S { ptr @.compoundLiteral.1 }