diff options
author | Andy Kaylor <akaylor@nvidia.com> | 2025-06-12 09:24:26 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-12 09:24:26 -0700 |
commit | 77834a40cf350d2fe63fac26222c3918f5f348fd (patch) | |
tree | 55dff8e127a38bda8efa47ee82c28365eee3ece9 /clang/lib/CIR | |
parent | 4039fdb7ba5a0d9ead5bdc0404f036063a4ca95d (diff) | |
download | llvm-77834a40cf350d2fe63fac26222c3918f5f348fd.zip llvm-77834a40cf350d2fe63fac26222c3918f5f348fd.tar.gz llvm-77834a40cf350d2fe63fac26222c3918f5f348fd.tar.bz2 |
[CIR] Upstream support for emitting constructors (#143639)
This change upstreams the code to emit simple constructor defintions.
Diffstat (limited to 'clang/lib/CIR')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 40 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 11 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCall.cpp | 41 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenClass.cpp | 81 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 60 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 18 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 65 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 15 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 5 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CMakeLists.txt | 1 |
10 files changed, 323 insertions, 14 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp new file mode 100644 index 0000000..5175148 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 contains code dealing with C++ code generation. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" +#include "CIRGenModule.h" + +#include "clang/AST/GlobalDecl.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) { + const CIRGenFunctionInfo &fnInfo = + getTypes().arrangeCXXStructorDeclaration(gd); + cir::FuncType funcType = getTypes().getFunctionType(fnInfo); + cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr, + /*DontDefer=*/true, ForDefinition); + assert(!cir::MissingFeatures::opFuncLinkage()); + CIRGenFunction cgf{*this, builder}; + curCGF = &cgf; + { + mlir::OpBuilder::InsertionGuard guard(builder); + cgf.generateCode(gd, fn, funcType); + } + curCGF = nullptr; + + setNonAliasAttributes(gd, fn); + assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); + return fn; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 107535e..2d967fd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -37,6 +37,10 @@ public: void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr); + /// Emit a single constructor/destructor with the gen type from a C++ + /// constructor/destructor Decl. + virtual void emitCXXStructor(clang::GlobalDecl gd) = 0; + public: clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) { return cgf.cxxabiThisDecl; @@ -55,12 +59,19 @@ public: return md->getParent(); } + /// Return whether the given global decl needs a VTT (virtual table table) + /// parameter. + virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; } + /// Build a parameter variable suitable for 'this'. void buildThisParam(CIRGenFunction &cgf, FunctionArgList ¶ms); /// Loads the incoming C++ this pointer as it was passed by the caller. mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf); + /// Emit constructor variants required by this ABI. + virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0; + /// Returns true if the given constructor or destructor is one of the kinds /// that the ABI says returns 'this' (only applies when called non-virtually /// for destructors). diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 9d25eea..da754e0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -162,6 +162,47 @@ arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix, return cgt.arrangeCIRFunctionInfo(resultType, prefix, required); } +void CIRGenFunction::emitDelegateCallArg(CallArgList &args, + const VarDecl *param, + SourceLocation loc) { + // StartFunction converted the ABI-lowered parameter(s) into a local alloca. + // We need to turn that into an r-value suitable for emitCall + Address local = getAddrOfLocalVar(param); + + QualType type = param->getType(); + + if (const auto *rd = type->getAsCXXRecordDecl()) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: record argument"); + return; + } + + // GetAddrOfLocalVar returns a pointer-to-pointer for references, but the + // argument needs to be the original pointer. + if (type->isReferenceType()) { + args.add( + RValue::get(builder.createLoad(getLoc(param->getSourceRange()), local)), + type); + } else if (getLangOpts().ObjCAutoRefCount) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: ObjCAutoRefCount"); + // For the most part, we just need to load the alloca, except that aggregate + // r-values are actually pointers to temporaries. + } else { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: convertTempToRValue"); + } + + // Deactivate the cleanup for the callee-destructed param that was pushed. + assert(!cir::MissingFeatures::thunks()); + if (type->isRecordType() && + type->castAs<RecordType>()->getDecl()->isParamDestroyedInCallee() && + param->needsDestruction(getContext())) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: callee-destructed param"); + } +} + static const CIRGenFunctionInfo & arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, const CallArgList &args, diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 8491a66..bb4b451 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -21,6 +21,87 @@ using namespace clang; using namespace clang::CIRGen; +/// Checks whether the given constructor is a valid subject for the +/// complete-to-base constructor delegation optimization, i.e. emitting the +/// complete constructor as a simple call to the base constructor. +bool CIRGenFunction::isConstructorDelegationValid( + const CXXConstructorDecl *ctor) { + // Currently we disable the optimization for classes with virtual bases + // because (1) the address of parameter variables need to be consistent across + // all initializers but (2) the delegate function call necessarily creates a + // second copy of the parameter variable. + // + // The limiting example (purely theoretical AFAIK): + // struct A { A(int &c) { c++; } }; + // struct A : virtual A { + // B(int count) : A(count) { printf("%d\n", count); } + // }; + // ...although even this example could in principle be emitted as a delegation + // since the address of the parameter doesn't escape. + if (ctor->getParent()->getNumVBases()) + return false; + + // We also disable the optimization for variadic functions because it's + // impossible to "re-pass" varargs. + if (ctor->getType()->castAs<FunctionProtoType>()->isVariadic()) + return false; + + // FIXME: Decide if we can do a delegation of a delegating constructor. + if (ctor->isDelegatingConstructor()) + return false; + + return true; +} + +Address CIRGenFunction::loadCXXThisAddress() { + assert(curFuncDecl && "loading 'this' without a func declaration?"); + assert(isa<CXXMethodDecl>(curFuncDecl)); + + // Lazily compute CXXThisAlignment. + if (cxxThisAlignment.isZero()) { + // Just use the best known alignment for the parent. + // TODO: if we're currently emitting a complete-object ctor/dtor, we can + // always use the complete-object alignment. + auto rd = cast<CXXMethodDecl>(curFuncDecl)->getParent(); + cxxThisAlignment = cgm.getClassPointerAlignment(rd); + } + + return Address(loadCXXThis(), cxxThisAlignment); +} + +void CIRGenFunction::emitDelegateCXXConstructorCall( + const CXXConstructorDecl *ctor, CXXCtorType ctorType, + const FunctionArgList &args, SourceLocation loc) { + CallArgList delegateArgs; + + FunctionArgList::const_iterator i = args.begin(), e = args.end(); + assert(i != e && "no parameters to constructor"); + + // this + Address thisAddr = loadCXXThisAddress(); + delegateArgs.add(RValue::get(thisAddr.getPointer()), (*i)->getType()); + ++i; + + // FIXME: The location of the VTT parameter in the parameter list is specific + // to the Itanium ABI and shouldn't be hardcoded here. + if (cgm.getCXXABI().needsVTTParameter(curGD)) { + cgm.errorNYI(loc, "emitDelegateCXXConstructorCall: VTT parameter"); + return; + } + + // Explicit arguments. + for (; i != e; ++i) { + const VarDecl *param = *i; + // FIXME: per-argument source location + emitDelegateCallArg(delegateArgs, param, loc); + } + + assert(!cir::MissingFeatures::sanitizers()); + + emitCXXConstructorCall(ctor, ctorType, /*ForVirtualBase=*/false, + /*Delegating=*/true, thisAddr, delegateArgs, loc); +} + Address CIRGenFunction::getAddressOfBaseClass( Address value, const CXXRecordDecl *derived, llvm::iterator_range<CastExpr::path_const_iterator> path, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index e32a5c8..53c44c6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -465,7 +465,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, if (isa<CXXDestructorDecl>(funcDecl)) getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); else if (isa<CXXConstructorDecl>(funcDecl)) - getCIRGenModule().errorNYI(bodyRange, "C++ constructor definition"); + emitConstructorBody(args); else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && funcDecl->hasAttr<CUDAGlobalAttr>()) getCIRGenModule().errorNYI(bodyRange, "CUDA kernel"); @@ -496,6 +496,54 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, return fn; } +void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { + assert(!cir::MissingFeatures::sanitizers()); + const auto *ctor = cast<CXXConstructorDecl>(curGD.getDecl()); + CXXCtorType ctorType = curGD.getCtorType(); + + assert((cgm.getTarget().getCXXABI().hasConstructorVariants() || + ctorType == Ctor_Complete) && + "can only generate complete ctor for this ABI"); + + if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) && + cgm.getTarget().getCXXABI().hasConstructorVariants()) { + emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc()); + return; + } + + const FunctionDecl *definition = nullptr; + Stmt *body = ctor->getBody(definition); + assert(definition == ctor && "emitting wrong constructor body"); + + if (isa_and_nonnull<CXXTryStmt>(body)) { + cgm.errorNYI(ctor->getSourceRange(), "emitConstructorBody: try body"); + return; + } + + assert(!cir::MissingFeatures::incrementProfileCounter()); + assert(!cir::MissingFeatures::runCleanupsScope()); + + // TODO: in restricted cases, we can emit the vbase initializers of a + // complete ctor and then delegate to the base ctor. + + assert(!cir::MissingFeatures::emitCtorPrologue()); + if (ctor->isDelegatingConstructor()) { + // This will be handled in emitCtorPrologue, but we should emit a diagnostic + // rather than silently fail to delegate. + cgm.errorNYI(ctor->getSourceRange(), + "emitConstructorBody: delegating ctor"); + return; + } + + // TODO(cir): propagate this result via mlir::logical result. Just unreachable + // now just to have it handled. + if (mlir::failed(emitStmt(body, true))) { + cgm.errorNYI(ctor->getSourceRange(), + "emitConstructorBody: emit body statement failed."); + return; + } +} + /// Given a value of type T* that may not be to a complete object, construct /// an l-vlaue withi the natural pointee alignment of T. LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val, @@ -522,16 +570,16 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, cgm.getCXXABI().buildThisParam(*this, args); } - if (isa<CXXConstructorDecl>(fd)) - cgm.errorNYI(fd->getSourceRange(), - "buildFunctionArgList: CXXConstructorDecl"); + if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd)) + if (cd->getInheritedConstructor()) + cgm.errorNYI(fd->getSourceRange(), + "buildFunctionArgList: inherited constructor"); for (auto *param : fd->parameters()) args.push_back(param); if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md))) - cgm.errorNYI(fd->getSourceRange(), - "buildFunctionArgList: implicit structor params"); + assert(!cir::MissingFeatures::cxxabiStructorImplicitParam()); return retTy; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 682d59d..361dcd5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -66,6 +66,7 @@ public: ImplicitParamDecl *cxxabiThisDecl = nullptr; mlir::Value cxxabiThisValue = nullptr; mlir::Value cxxThisValue = nullptr; + clang::CharUnits cxxThisAlignment; // Holds the Decl for the current outermost non-closure context const clang::Decl *curFuncDecl = nullptr; @@ -473,6 +474,9 @@ public: bool shouldNullCheckClassCastValue(const CastExpr *ce); + static bool + isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); + LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t); /// Construct an address with the natural alignment of T. If a pointer to T @@ -517,6 +521,7 @@ public: assert(cxxThisValue && "no 'this' value for this function"); return cxxThisValue; } + Address loadCXXThisAddress(); /// Get an appropriate 'undef' rvalue for the given type. /// TODO: What's the equivalent for MLIR? Currently we're only using this for @@ -753,6 +758,8 @@ public: LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e); + void emitConstructorBody(FunctionArgList &args); + mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); void emitCXXConstructExpr(const clang::CXXConstructExpr *e, @@ -841,6 +848,17 @@ public: mlir::Type condType, bool buildingTopLevelCase); + void emitDelegateCXXConstructorCall(const clang::CXXConstructorDecl *ctor, + clang::CXXCtorType ctorType, + const FunctionArgList &args, + clang::SourceLocation loc); + + /// We are performing a delegate call; that is, the current function is + /// delegating to another one. Produce a r-value suitable for passing the + /// given parameter. + void emitDelegateCallArg(CallArgList &args, const clang::VarDecl *param, + clang::SourceLocation loc); + /// Emit an `if` on a boolean condition to the specified blocks. /// FIXME: Based on the condition, this might try to simplify the codegen of /// the conditional based on the branch. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index fdd8b63..cd9096a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -20,7 +20,9 @@ #include "CIRGenCXXABI.h" #include "CIRGenFunction.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CIR/MissingFeatures.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; @@ -35,8 +37,13 @@ public: assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI()); } - void emitInstanceFunctionProlog(SourceLocation Loc, - CIRGenFunction &CGF) override; + bool needsVTTParameter(clang::GlobalDecl gd) override; + + void emitInstanceFunctionProlog(SourceLocation loc, + CIRGenFunction &cgf) override; + + void emitCXXConstructors(const clang::CXXConstructorDecl *d) override; + void emitCXXStructor(clang::GlobalDecl gd) override; }; } // namespace @@ -72,6 +79,60 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc, } } +void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) { + auto *md = cast<CXXMethodDecl>(gd.getDecl()); + auto *cd = dyn_cast<CXXConstructorDecl>(md); + + if (!cd) { + cgm.errorNYI(md->getSourceRange(), "CXCABI emit destructor"); + return; + } + + if (cgm.getCodeGenOpts().CXXCtorDtorAliases) + cgm.errorNYI(md->getSourceRange(), "Ctor/Dtor aliases"); + + auto fn = cgm.codegenCXXStructor(gd); + + cgm.maybeSetTrivialComdat(*md, fn); +} + +void CIRGenItaniumCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) { + // Just make sure we're in sync with TargetCXXABI. + assert(cgm.getTarget().getCXXABI().hasConstructorVariants()); + + // The constructor used for constructing this as a base class; + // ignores virtual bases. + cgm.emitGlobal(GlobalDecl(d, Ctor_Base)); + + // The constructor used for constructing this as a complete class; + // constructs the virtual bases, then calls the base constructor. + if (!d->getParent()->isAbstract()) { + // We don't need to emit the complete ctro if the class is abstract. + cgm.emitGlobal(GlobalDecl(d, Ctor_Complete)); + } +} + +/// Return whether the given global decl needs a VTT (virtual table table) +/// parameter, which it does if it's a base constructor or destructor with +/// virtual bases. +bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) { + auto *md = cast<CXXMethodDecl>(gd.getDecl()); + + // We don't have any virtual bases, just return early. + if (!md->getParent()->getNumVBases()) + return false; + + // Check if we have a base constructor. + if (isa<CXXConstructorDecl>(md) && gd.getCtorType() == Ctor_Base) + return true; + + // Check if we have a base destructor. + if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base) + return true; + + return false; +} + CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { switch (cgm.getASTContext().getCXXABIKind()) { case TargetCXXABI::GenericItanium: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 8407f8f..434dd37 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -226,11 +226,9 @@ mlir::Operation * CIRGenModule::getAddrOfGlobal(GlobalDecl gd, ForDefinition_t isForDefinition) { const Decl *d = gd.getDecl(); - if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) { - errorNYI(d->getSourceRange(), - "getAddrOfGlobal: C++ constructor/destructor"); - return nullptr; - } + if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) + return getAddrOfCXXStructor(gd, /*FnInfo=*/nullptr, /*FnType=*/nullptr, + /*DontDefer=*/false, isForDefinition); if (isa<CXXMethodDecl>(d)) { const CIRGenFunctionInfo &fi = @@ -411,6 +409,7 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, cgf.generateCode(gd, funcOp, funcType); } curCGF = nullptr; + assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); } mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) { @@ -771,7 +770,7 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, // Make sure to emit the definition(s) before we emit the thunks. This is // necessary for the generation of certain thunks. if (isa<CXXConstructorDecl>(method) || isa<CXXDestructorDecl>(method)) - errorNYI(method->getSourceRange(), "C++ ctor/dtor"); + abi->emitCXXStructor(gd); else if (fd->isMultiVersion()) errorNYI(method->getSourceRange(), "multiversion functions"); else @@ -1173,6 +1172,10 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::Empty: break; + case Decl::CXXConstructor: + getCXXABI().emitCXXConstructors(cast<CXXConstructorDecl>(decl)); + break; + // C++ Decls case Decl::LinkageSpec: case Decl::Namespace: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 9748c0b..f76fd8e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -267,6 +267,11 @@ public: // Make sure that this type is translated. void updateCompletedType(const clang::TagDecl *td); + // Produce code for this constructor/destructor. This method doesn't try to + // apply any ABI rules about which other constructors/destructors are needed + // or if they are alias to each other. + cir::FuncOp codegenCXXStructor(clang::GlobalDecl gd); + bool supportsCOMDAT() const; void maybeSetTrivialComdat(const clang::Decl &d, mlir::Operation *op); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index beaa9af..21760968 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangCIR CIRGenBuilder.cpp CIRGenCall.cpp CIRGenClass.cpp + CIRGenCXX.cpp CIRGenCXXABI.cpp CIRGenCXXExpr.cpp CIRGenBuiltin.cpp |