//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // Internal per-function state used for AST-to-ClangIR code gen // //===----------------------------------------------------------------------===// #ifndef CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H #define CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H #include "CIRGenBuilder.h" #include "CIRGenCall.h" #include "CIRGenModule.h" #include "CIRGenTypeCache.h" #include "CIRGenValue.h" #include "EHScopeStack.h" #include "Address.h" #include "clang/AST/ASTContext.h" #include "clang/AST/BaseSubobject.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" #include "clang/AST/Decl.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/TypeEvaluationKind.h" #include "llvm/ADT/ScopedHashTable.h" namespace { class ScalarExprEmitter; } // namespace namespace mlir { namespace acc { class LoopOp; } // namespace acc } // namespace mlir namespace clang::CIRGen { class CIRGenFunction : public CIRGenTypeCache { public: CIRGenModule &cgm; private: friend class ::ScalarExprEmitter; /// The builder is a helper class to create IR inside a function. The /// builder is stateful, in particular it keeps an "insertion point": this /// is where the next operations will be introduced. CIRGenBuilderTy &builder; public: /// The GlobalDecl for the current function being compiled or the global /// variable currently being initialized. clang::GlobalDecl curGD; /// The compiler-generated variable that holds the return value. std::optional fnRetAlloca; /// The temporary alloca to hold the return value. This is /// invalid iff the function has no return value. Address returnValue = Address::invalid(); /// Tracks function scope overall cleanup handling. EHScopeStack ehStack; llvm::DenseMap lambdaCaptureFields; clang::FieldDecl *lambdaThisCaptureField = nullptr; /// CXXThisDecl - When generating code for a C++ member function, /// this will hold the implicit 'this' declaration. ImplicitParamDecl *cxxabiThisDecl = nullptr; mlir::Value cxxabiThisValue = nullptr; mlir::Value cxxThisValue = nullptr; clang::CharUnits cxxThisAlignment; /// When generating code for a constructor or destructor, this will hold the /// implicit argument (e.g. VTT). ImplicitParamDecl *cxxStructorImplicitParamDecl{}; mlir::Value cxxStructorImplicitParamValue{}; /// The value of 'this' to sue when evaluating CXXDefaultInitExprs within this /// expression. Address cxxDefaultInitExprThis = Address::invalid(); // Holds the Decl for the current outermost non-closure context const clang::Decl *curFuncDecl = nullptr; /// This is the inner-most code context, which includes blocks. const clang::Decl *curCodeDecl = nullptr; /// The function for which code is currently being generated. cir::FuncOp curFn; using DeclMapTy = llvm::DenseMap; /// This keeps track of the CIR allocas or globals for local C /// declarations. DeclMapTy localDeclMap; /// The type of the condition for the emitting switch statement. llvm::SmallVector condTypeStack; clang::ASTContext &getContext() const { return cgm.getASTContext(); } CIRGenBuilderTy &getBuilder() { return builder; } CIRGenModule &getCIRGenModule() { return cgm; } const CIRGenModule &getCIRGenModule() const { return cgm; } mlir::Block *getCurFunctionEntryBlock() { return &curFn.getRegion().front(); } /// Sanitizers enabled for this function. clang::SanitizerSet sanOpts; /// The symbol table maps a variable name to a value in the current scope. /// Entering a function creates a new scope, and the function arguments are /// added to the mapping. When the processing of a function is terminated, /// the scope is destroyed and the mappings created in this scope are /// dropped. using SymTableTy = llvm::ScopedHashTable; SymTableTy symbolTable; /// Whether or not a Microsoft-style asm block has been processed within /// this fuction. These can potentially set the return value. bool sawAsmBlock = false; mlir::Type convertTypeForMem(QualType t); mlir::Type convertType(clang::QualType t); mlir::Type convertType(const TypeDecl *t) { return convertType(getContext().getTypeDeclType(t)); } /// Return the cir::TypeEvaluationKind of QualType \c type. static cir::TypeEvaluationKind getEvaluationKind(clang::QualType type); static bool hasScalarEvaluationKind(clang::QualType type) { return getEvaluationKind(type) == cir::TEK_Scalar; } static bool hasAggregateEvaluationKind(clang::QualType type) { return getEvaluationKind(type) == cir::TEK_Aggregate; } CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext = false); ~CIRGenFunction(); CIRGenTypes &getTypes() const { return cgm.getTypes(); } const TargetInfo &getTarget() const { return cgm.getTarget(); } mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); } // --------------------- // Opaque value handling // --------------------- /// Keeps track of the current set of opaque value expressions. llvm::DenseMap opaqueLValues; llvm::DenseMap opaqueRValues; public: /// A non-RAII class containing all the information about a bound /// opaque value. OpaqueValueMapping, below, is a RAII wrapper for /// this which makes individual mappings very simple; using this /// class directly is useful when you have a variable number of /// opaque values or don't want the RAII functionality for some /// reason. class OpaqueValueMappingData { const OpaqueValueExpr *opaqueValue; bool boundLValue; OpaqueValueMappingData(const OpaqueValueExpr *ov, bool boundLValue) : opaqueValue(ov), boundLValue(boundLValue) {} public: OpaqueValueMappingData() : opaqueValue(nullptr) {} static bool shouldBindAsLValue(const Expr *expr) { // gl-values should be bound as l-values for obvious reasons. // Records should be bound as l-values because IR generation // always keeps them in memory. Expressions of function type // act exactly like l-values but are formally required to be // r-values in C. return expr->isGLValue() || expr->getType()->isFunctionType() || hasAggregateEvaluationKind(expr->getType()); } static OpaqueValueMappingData bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const Expr *e) { if (shouldBindAsLValue(ov)) return bind(cgf, ov, cgf.emitLValue(e)); return bind(cgf, ov, cgf.emitAnyExpr(e)); } static OpaqueValueMappingData bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const LValue &lv) { assert(shouldBindAsLValue(ov)); cgf.opaqueLValues.insert(std::make_pair(ov, lv)); return OpaqueValueMappingData(ov, true); } static OpaqueValueMappingData bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const RValue &rv) { assert(!shouldBindAsLValue(ov)); cgf.opaqueRValues.insert(std::make_pair(ov, rv)); OpaqueValueMappingData data(ov, false); // Work around an extremely aggressive peephole optimization in // EmitScalarConversion which assumes that all other uses of a // value are extant. assert(!cir::MissingFeatures::peepholeProtection() && "NYI"); return data; } bool isValid() const { return opaqueValue != nullptr; } void clear() { opaqueValue = nullptr; } void unbind(CIRGenFunction &cgf) { assert(opaqueValue && "no data to unbind!"); if (boundLValue) { cgf.opaqueLValues.erase(opaqueValue); } else { cgf.opaqueRValues.erase(opaqueValue); assert(!cir::MissingFeatures::peepholeProtection() && "NYI"); } } }; /// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr. class OpaqueValueMapping { CIRGenFunction &cgf; OpaqueValueMappingData data; public: static bool shouldBindAsLValue(const Expr *expr) { return OpaqueValueMappingData::shouldBindAsLValue(expr); } /// Build the opaque value mapping for the given conditional /// operator if it's the GNU ?: extension. This is a common /// enough pattern that the convenience operator is really /// helpful. /// OpaqueValueMapping(CIRGenFunction &cgf, const AbstractConditionalOperator *op) : cgf(cgf) { if (mlir::isa(op)) // Leave Data empty. return; const BinaryConditionalOperator *e = mlir::cast(op); data = OpaqueValueMappingData::bind(cgf, e->getOpaqueValue(), e->getCommon()); } /// Build the opaque value mapping for an OpaqueValueExpr whose source /// expression is set to the expression the OVE represents. OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *ov) : cgf(cgf) { if (ov) { assert(ov->getSourceExpr() && "wrong form of OpaqueValueMapping used " "for OVE with no source expression"); data = OpaqueValueMappingData::bind(cgf, ov, ov->getSourceExpr()); } } OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue, LValue lvalue) : cgf(cgf), data(OpaqueValueMappingData::bind(cgf, opaqueValue, lvalue)) {} OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue, RValue rvalue) : cgf(cgf), data(OpaqueValueMappingData::bind(cgf, opaqueValue, rvalue)) {} void pop() { data.unbind(cgf); data.clear(); } ~OpaqueValueMapping() { if (data.isValid()) data.unbind(cgf); } }; private: /// Declare a variable in the current scope, return success if the variable /// wasn't declared yet. void declare(mlir::Value addrVal, const clang::Decl *var, clang::QualType ty, mlir::Location loc, clang::CharUnits alignment, bool isParam = false); public: mlir::Value createDummyValue(mlir::Location loc, clang::QualType qt); void emitNullInitialization(mlir::Location loc, Address destPtr, QualType ty); private: // Track current variable initialization (if there's one) const clang::VarDecl *currVarDecl = nullptr; class VarDeclContext { CIRGenFunction &p; const clang::VarDecl *oldVal = nullptr; public: VarDeclContext(CIRGenFunction &p, const VarDecl *value) : p(p) { if (p.currVarDecl) oldVal = p.currVarDecl; p.currVarDecl = value; } /// Can be used to restore the state early, before the dtor /// is run. void restore() { p.currVarDecl = oldVal; } ~VarDeclContext() { restore(); } }; public: /// Use to track source locations across nested visitor traversals. /// Always use a `SourceLocRAIIObject` to change currSrcLoc. std::optional currSrcLoc; class SourceLocRAIIObject { CIRGenFunction &cgf; std::optional oldLoc; public: SourceLocRAIIObject(CIRGenFunction &cgf, mlir::Location value) : cgf(cgf) { if (cgf.currSrcLoc) oldLoc = cgf.currSrcLoc; cgf.currSrcLoc = value; } /// Can be used to restore the state early, before the dtor /// is run. void restore() { cgf.currSrcLoc = oldLoc; } ~SourceLocRAIIObject() { restore(); } }; using SymTableScopeTy = llvm::ScopedHashTableScope; /// Hold counters for incrementally naming temporaries unsigned counterRefTmp = 0; unsigned counterAggTmp = 0; std::string getCounterRefTmpAsString(); std::string getCounterAggTmpAsString(); /// Helpers to convert Clang's SourceLocation to a MLIR Location. mlir::Location getLoc(clang::SourceLocation srcLoc); mlir::Location getLoc(clang::SourceRange srcLoc); mlir::Location getLoc(mlir::Location lhs, mlir::Location rhs); const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); } /// True if an insertion point is defined. If not, this indicates that the /// current code being emitted is unreachable. /// FIXME(cir): we need to inspect this and perhaps use a cleaner mechanism /// since we don't yet force null insertion point to designate behavior (like /// LLVM's codegen does) and we probably shouldn't. bool haveInsertPoint() const { return builder.getInsertionBlock() != nullptr; } // Wrapper for function prototype sources. Wraps either a FunctionProtoType or // an ObjCMethodDecl. struct PrototypeWrapper { llvm::PointerUnion p; PrototypeWrapper(const clang::FunctionProtoType *ft) : p(ft) {} PrototypeWrapper(const clang::ObjCMethodDecl *md) : p(md) {} }; bool isLValueSuitableForInlineAtomic(LValue lv); /// An abstract representation of regular/ObjC call/message targets. class AbstractCallee { /// The function declaration of the callee. [[maybe_unused]] const clang::Decl *calleeDecl; public: AbstractCallee() : calleeDecl(nullptr) {} AbstractCallee(const clang::FunctionDecl *fd) : calleeDecl(fd) {} bool hasFunctionDecl() const { return llvm::isa_and_nonnull(calleeDecl); } unsigned getNumParams() const { if (const auto *fd = llvm::dyn_cast(calleeDecl)) return fd->getNumParams(); return llvm::cast(calleeDecl)->param_size(); } const clang::ParmVarDecl *getParamDecl(unsigned I) const { if (const auto *fd = llvm::dyn_cast(calleeDecl)) return fd->getParamDecl(I); return *(llvm::cast(calleeDecl)->param_begin() + I); } }; void finishFunction(SourceLocation endLoc); /// Determine whether the given initializer is trivial in the sense /// that it requires no code to be generated. bool isTrivialInitializer(const Expr *init); /// If the specified expression does not fold to a constant, or if it does but /// contains a label, return false. If it constant folds return true and set /// the boolean result in Result. bool constantFoldsToBool(const clang::Expr *cond, bool &resultBool, bool allowLabels = false); bool constantFoldsToSimpleInteger(const clang::Expr *cond, llvm::APSInt &resultInt, bool allowLabels = false); /// Return true if the statement contains a label in it. If /// this statement is not executed normally, it not containing a label means /// that we can just remove the code. bool containsLabel(const clang::Stmt *s, bool ignoreCaseStmts = false); class ConstantEmission { // Cannot use mlir::TypedAttr directly here because of bit availability. llvm::PointerIntPair valueAndIsReference; ConstantEmission(mlir::TypedAttr c, bool isReference) : valueAndIsReference(c, isReference) {} public: ConstantEmission() {} static ConstantEmission forReference(mlir::TypedAttr c) { return ConstantEmission(c, true); } static ConstantEmission forValue(mlir::TypedAttr c) { return ConstantEmission(c, false); } explicit operator bool() const { return valueAndIsReference.getOpaqueValue() != nullptr; } bool isReference() const { return valueAndIsReference.getInt(); } LValue getReferenceLValue(CIRGenFunction &cgf, Expr *refExpr) const { assert(isReference()); cgf.cgm.errorNYI(refExpr->getSourceRange(), "ConstantEmission::getReferenceLValue"); return {}; } mlir::TypedAttr getValue() const { assert(!isReference()); return mlir::cast(valueAndIsReference.getPointer()); } }; ConstantEmission tryEmitAsConstant(const DeclRefExpr *refExpr); ConstantEmission tryEmitAsConstant(const MemberExpr *me); struct AutoVarEmission { const clang::VarDecl *Variable; /// The address of the alloca for languages with explicit address space /// (e.g. OpenCL) or alloca casted to generic pointer for address space /// agnostic languages (e.g. C++). Invalid if the variable was emitted /// as a global constant. Address Addr; /// True if the variable is of aggregate type and has a constant /// initializer. bool IsConstantAggregate = false; /// True if the variable is a __block variable that is captured by an /// escaping block. bool IsEscapingByRef = false; /// True if the variable was emitted as an offload recipe, and thus doesn't /// have the same sort of alloca initialization. bool EmittedAsOffload = false; mlir::Value NRVOFlag{}; struct Invalid {}; AutoVarEmission(Invalid) : Variable(nullptr), Addr(Address::invalid()) {} AutoVarEmission(const clang::VarDecl &variable) : Variable(&variable), Addr(Address::invalid()) {} static AutoVarEmission invalid() { return AutoVarEmission(Invalid()); } bool wasEmittedAsGlobal() const { return !Addr.isValid(); } bool wasEmittedAsOffloadClause() const { return EmittedAsOffload; } /// Returns the raw, allocated address, which is not necessarily /// the address of the object itself. It is casted to default /// address space for address space agnostic languages. Address getAllocatedAddress() const { return Addr; } // Changes the stored address for the emission. This function should only // be used in extreme cases, and isn't required to model normal AST // initialization/variables. void setAllocatedAddress(Address A) { Addr = A; } /// Returns the address of the object within this declaration. /// Note that this does not chase the forwarding pointer for /// __block decls. Address getObjectAddress(CIRGenFunction &cgf) const { if (!IsEscapingByRef) return Addr; assert(!cir::MissingFeatures::opAllocaEscapeByReference()); return Address::invalid(); } }; /// Perform the usual unary conversions on the specified expression and /// compare the result against zero, returning an Int1Ty value. mlir::Value evaluateExprAsBool(const clang::Expr *e); cir::GlobalOp addInitializerToStaticVarDecl(const VarDecl &d, cir::GlobalOp gv, cir::GetGlobalOp gvAddr); /// Set the address of a local variable. void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) { assert(!localDeclMap.count(vd) && "Decl already exists in LocalDeclMap!"); localDeclMap.insert({vd, addr}); // Add to the symbol table if not there already. if (symbolTable.count(vd)) return; symbolTable.insert(vd, addr.getPointer()); } // A class to allow reverting changes to a var-decl's registration to the // localDeclMap. This is used in cases where things are being inserted into // the variable list but don't follow normal lookup/search rules, like in // OpenACC recipe generation. class DeclMapRevertingRAII { CIRGenFunction &cgf; const VarDecl *vd; bool shouldDelete = false; Address oldAddr = Address::invalid(); public: DeclMapRevertingRAII(CIRGenFunction &cgf, const VarDecl *vd) : cgf(cgf), vd(vd) { auto mapItr = cgf.localDeclMap.find(vd); if (mapItr != cgf.localDeclMap.end()) oldAddr = mapItr->second; else shouldDelete = true; } ~DeclMapRevertingRAII() { if (shouldDelete) cgf.localDeclMap.erase(vd); else cgf.localDeclMap.insert_or_assign(vd, oldAddr); } }; bool shouldNullCheckClassCastValue(const CastExpr *ce); RValue convertTempToRValue(Address addr, clang::QualType type, clang::SourceLocation loc); static bool isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); struct VPtr { clang::BaseSubobject base; const clang::CXXRecordDecl *nearestVBase; clang::CharUnits offsetFromNearestVBase; const clang::CXXRecordDecl *vtableClass; }; using VisitedVirtualBasesSetTy = llvm::SmallPtrSet; using VPtrsVector = llvm::SmallVector; VPtrsVector getVTablePointers(const clang::CXXRecordDecl *vtableClass); void getVTablePointers(clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase, clang::CharUnits offsetFromNearestVBase, bool baseIsNonVirtualPrimaryBase, const clang::CXXRecordDecl *vtableClass, VisitedVirtualBasesSetTy &vbases, VPtrsVector &vptrs); /// Return the Value of the vtable pointer member pointed to by thisAddr. mlir::Value getVTablePtr(mlir::Location loc, Address thisAddr, const clang::CXXRecordDecl *vtableClass); /// Returns whether we should perform a type checked load when loading a /// virtual function for virtual calls to members of RD. This is generally /// true when both vcall CFI and whole-program-vtables are enabled. bool shouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *rd); /// Source location information about the default argument or member /// initializer expression we're evaluating, if any. clang::CurrentSourceLocExprScope curSourceLocExprScope; using SourceLocExprScopeGuard = clang::CurrentSourceLocExprScope::SourceLocExprScopeGuard; /// A scope within which we are constructing the fields of an object which /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if /// we need to evaluate the CXXDefaultInitExpr within the evaluation. class FieldConstructionScope { public: FieldConstructionScope(CIRGenFunction &cgf, Address thisAddr) : cgf(cgf), oldCXXDefaultInitExprThis(cgf.cxxDefaultInitExprThis) { cgf.cxxDefaultInitExprThis = thisAddr; } ~FieldConstructionScope() { cgf.cxxDefaultInitExprThis = oldCXXDefaultInitExprThis; } private: CIRGenFunction &cgf; Address oldCXXDefaultInitExprThis; }; /// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this' /// is overridden to be the object under construction. class CXXDefaultInitExprScope { public: CXXDefaultInitExprScope(CIRGenFunction &cgf, const CXXDefaultInitExpr *e) : cgf{cgf}, oldCXXThisValue(cgf.cxxThisValue), oldCXXThisAlignment(cgf.cxxThisAlignment), sourceLocScope(e, cgf.curSourceLocExprScope) { cgf.cxxThisValue = cgf.cxxDefaultInitExprThis.getPointer(); cgf.cxxThisAlignment = cgf.cxxDefaultInitExprThis.getAlignment(); } ~CXXDefaultInitExprScope() { cgf.cxxThisValue = oldCXXThisValue; cgf.cxxThisAlignment = oldCXXThisAlignment; } public: CIRGenFunction &cgf; mlir::Value oldCXXThisValue; clang::CharUnits oldCXXThisAlignment; SourceLocExprScopeGuard sourceLocScope; }; LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t); LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty); /// Construct an address with the natural alignment of T. If a pointer to T /// is expected to be signed, the pointer passed to this function must have /// been signed, and the returned Address will have the pointer authentication /// information needed to authenticate the signed pointer. Address makeNaturalAddressForPointer(mlir::Value ptr, QualType t, CharUnits alignment, bool forPointeeType = false, LValueBaseInfo *baseInfo = nullptr) { if (alignment.isZero()) alignment = cgm.getNaturalTypeAlignment(t, baseInfo); return Address(ptr, convertTypeForMem(t), alignment); } Address getAddressOfBaseClass( Address value, const CXXRecordDecl *derived, llvm::iterator_range path, bool nullCheckValue, SourceLocation loc); /// Return the VTT parameter that should be passed to a base /// constructor/destructor with virtual bases. /// FIXME: VTTs are Itanium ABI-specific, so the definition should move /// to ItaniumCXXABI.cpp together with all the references to VTT. mlir::Value getVTTParameter(GlobalDecl gd, bool forVirtualBase, bool delegating); LValue makeAddrLValue(Address addr, QualType ty, AlignmentSource source = AlignmentSource::Type) { return makeAddrLValue(addr, ty, LValueBaseInfo(source)); } LValue makeAddrLValue(Address addr, QualType ty, LValueBaseInfo baseInfo) { return LValue::makeAddr(addr, ty, baseInfo); } void initializeVTablePointers(mlir::Location loc, const clang::CXXRecordDecl *rd); void initializeVTablePointer(mlir::Location loc, const VPtr &vptr); AggValueSlot::Overlap_t getOverlapForFieldInit(const FieldDecl *fd); /// Return the address of a local variable. Address getAddrOfLocalVar(const clang::VarDecl *vd) { auto it = localDeclMap.find(vd); assert(it != localDeclMap.end() && "Invalid argument to getAddrOfLocalVar(), no decl!"); return it->second; } Address getAddrOfBitFieldStorage(LValue base, const clang::FieldDecl *field, mlir::Type fieldType, unsigned index); /// Given an opaque value expression, return its LValue mapping if it exists, /// otherwise create one. LValue getOrCreateOpaqueLValueMapping(const OpaqueValueExpr *e); /// Given an opaque value expression, return its RValue mapping if it exists, /// otherwise create one. RValue getOrCreateOpaqueRValueMapping(const OpaqueValueExpr *e); /// Load the value for 'this'. This function is only valid while generating /// code for an C++ member function. /// FIXME(cir): this should return a mlir::Value! mlir::Value loadCXXThis() { assert(cxxThisValue && "no 'this' value for this function"); return cxxThisValue; } Address loadCXXThisAddress(); /// Load the VTT parameter to base constructors/destructors have virtual /// bases. FIXME: Every place that calls LoadCXXVTT is something that needs to /// be abstracted properly. mlir::Value loadCXXVTT() { assert(cxxStructorImplicitParamValue && "no VTT value for this function"); return cxxStructorImplicitParamValue; } /// Convert the given pointer to a complete class to the given direct base. Address getAddressOfDirectBaseInCompleteClass(mlir::Location loc, Address value, const CXXRecordDecl *derived, const CXXRecordDecl *base, bool baseIsVirtual); /// Determine whether a return value slot may overlap some other object. AggValueSlot::Overlap_t getOverlapForReturnValue() { // FIXME: Assuming no overlap here breaks guaranteed copy elision for base // class subobjects. These cases may need to be revisited depending on the // resolution of the relevant core issue. return AggValueSlot::DoesNotOverlap; } /// Determine whether a base class initialization may overlap some other /// object. AggValueSlot::Overlap_t getOverlapForBaseInit(const CXXRecordDecl *rd, const CXXRecordDecl *baseRD, bool isVirtual); /// Get an appropriate 'undef' rvalue for the given type. /// TODO: What's the equivalent for MLIR? Currently we're only using this for /// void types so it just returns RValue::get(nullptr) but it'll need /// addressed later. RValue getUndefRValue(clang::QualType ty); cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType); clang::QualType buildFunctionArgList(clang::GlobalDecl gd, FunctionArgList &args); /// Emit code for the start of a function. /// \param loc The location to be associated with the function. /// \param startLoc The location of the function body. void startFunction(clang::GlobalDecl gd, clang::QualType returnType, cir::FuncOp fn, cir::FuncType funcType, FunctionArgList args, clang::SourceLocation loc, clang::SourceLocation startLoc); /// The cleanup depth enclosing all the cleanups associated with the /// parameters. EHScopeStack::stable_iterator prologueCleanupDepth; /// Takes the old cleanup stack size and emits the cleanup blocks /// that have been added. void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth); void popCleanupBlock(); /// Push a cleanup to be run at the end of the current full-expression. Safe /// against the possibility that we're currently inside a /// conditionally-evaluated expression. template void pushFullExprCleanup(CleanupKind kind, As... a) { // If we're not in a conditional branch, or if none of the // arguments requires saving, then use the unconditional cleanup. if (!isInConditionalBranch()) return ehStack.pushCleanup(kind, a...); cgm.errorNYI("pushFullExprCleanup in conditional branch"); } /// Enters a new scope for capturing cleanups, all of which /// will be executed once the scope is exited. class RunCleanupsScope { EHScopeStack::stable_iterator cleanupStackDepth, oldCleanupStackDepth; protected: bool performCleanup; private: RunCleanupsScope(const RunCleanupsScope &) = delete; void operator=(const RunCleanupsScope &) = delete; protected: CIRGenFunction &cgf; public: /// Enter a new cleanup scope. explicit RunCleanupsScope(CIRGenFunction &cgf) : performCleanup(true), cgf(cgf) { cleanupStackDepth = cgf.ehStack.stable_begin(); oldCleanupStackDepth = cgf.currentCleanupStackDepth; cgf.currentCleanupStackDepth = cleanupStackDepth; } /// Exit this cleanup scope, emitting any accumulated cleanups. ~RunCleanupsScope() { if (performCleanup) forceCleanup(); } /// Force the emission of cleanups now, instead of waiting /// until this object is destroyed. void forceCleanup() { assert(performCleanup && "Already forced cleanup"); { mlir::OpBuilder::InsertionGuard guard(cgf.getBuilder()); cgf.popCleanupBlocks(cleanupStackDepth); performCleanup = false; cgf.currentCleanupStackDepth = oldCleanupStackDepth; } } }; // Cleanup stack depth of the RunCleanupsScope that was pushed most recently. EHScopeStack::stable_iterator currentCleanupStackDepth = ehStack.stable_end(); public: /// Represents a scope, including function bodies, compound statements, and /// the substatements of if/while/do/for/switch/try statements. This class /// handles any automatic cleanup, along with the return value. struct LexicalScope : public RunCleanupsScope { private: // Block containing cleanup code for things initialized in this // lexical context (scope). mlir::Block *cleanupBlock = nullptr; // Points to the scope entry block. This is useful, for instance, for // helping to insert allocas before finalizing any recursive CodeGen from // switches. mlir::Block *entryBlock; LexicalScope *parentScope = nullptr; // Only Regular is used at the moment. Support for other kinds will be // added as the relevant statements/expressions are upstreamed. enum Kind { Regular, // cir.if, cir.scope, if_regions Ternary, // cir.ternary Switch, // cir.switch Try, // cir.try GlobalInit // cir.global initialization code }; Kind scopeKind = Kind::Regular; // The scope return value. mlir::Value retVal = nullptr; mlir::Location beginLoc; mlir::Location endLoc; public: unsigned depth = 0; LexicalScope(CIRGenFunction &cgf, mlir::Location loc, mlir::Block *eb) : RunCleanupsScope(cgf), entryBlock(eb), parentScope(cgf.curLexScope), beginLoc(loc), endLoc(loc) { assert(entryBlock && "LexicalScope requires an entry block"); cgf.curLexScope = this; if (parentScope) ++depth; if (const auto fusedLoc = mlir::dyn_cast(loc)) { assert(fusedLoc.getLocations().size() == 2 && "too many locations"); beginLoc = fusedLoc.getLocations()[0]; endLoc = fusedLoc.getLocations()[1]; } } void setRetVal(mlir::Value v) { retVal = v; } void cleanup(); void restore() { cgf.curLexScope = parentScope; } ~LexicalScope() { assert(!cir::MissingFeatures::generateDebugInfo()); cleanup(); restore(); } // --- // Kind // --- bool isGlobalInit() { return scopeKind == Kind::GlobalInit; } bool isRegular() { return scopeKind == Kind::Regular; } bool isSwitch() { return scopeKind == Kind::Switch; } bool isTernary() { return scopeKind == Kind::Ternary; } bool isTry() { return scopeKind == Kind::Try; } void setAsGlobalInit() { scopeKind = Kind::GlobalInit; } void setAsSwitch() { scopeKind = Kind::Switch; } void setAsTernary() { scopeKind = Kind::Ternary; } // Lazy create cleanup block or return what's available. mlir::Block *getOrCreateCleanupBlock(mlir::OpBuilder &builder) { if (cleanupBlock) return cleanupBlock; cleanupBlock = createCleanupBlock(builder); return cleanupBlock; } mlir::Block *getCleanupBlock(mlir::OpBuilder &builder) { return cleanupBlock; } mlir::Block *createCleanupBlock(mlir::OpBuilder &builder) { // Create the cleanup block but dont hook it up around just yet. mlir::OpBuilder::InsertionGuard guard(builder); mlir::Region *r = builder.getBlock() ? builder.getBlock()->getParent() : &cgf.curFn->getRegion(0); cleanupBlock = builder.createBlock(r); return cleanupBlock; } // --- // Return handling. // --- private: // `returnBlock`, `returnLoc`, and all the functions that deal with them // will change and become more complicated when `switch` statements are // upstreamed. `case` statements within the `switch` are in the same scope // but have their own regions. Therefore the LexicalScope will need to // keep track of multiple return blocks. mlir::Block *returnBlock = nullptr; std::optional returnLoc; // See the comment on `getOrCreateRetBlock`. mlir::Block *createRetBlock(CIRGenFunction &cgf, mlir::Location loc) { assert(returnBlock == nullptr && "only one return block per scope"); // Create the cleanup block but don't hook it up just yet. mlir::OpBuilder::InsertionGuard guard(cgf.builder); returnBlock = cgf.builder.createBlock(cgf.builder.getBlock()->getParent()); updateRetLoc(returnBlock, loc); return returnBlock; } cir::ReturnOp emitReturn(mlir::Location loc); void emitImplicitReturn(); public: mlir::Block *getRetBlock() { return returnBlock; } mlir::Location getRetLoc(mlir::Block *b) { return *returnLoc; } void updateRetLoc(mlir::Block *b, mlir::Location loc) { returnLoc = loc; } // Create the return block for this scope, or return the existing one. // This get-or-create logic is necessary to handle multiple return // statements within the same scope, which can happen if some of them are // dead code or if there is a `goto` into the middle of the scope. mlir::Block *getOrCreateRetBlock(CIRGenFunction &cgf, mlir::Location loc) { if (returnBlock == nullptr) { returnBlock = createRetBlock(cgf, loc); return returnBlock; } updateRetLoc(returnBlock, loc); return returnBlock; } mlir::Block *getEntryBlock() { return entryBlock; } }; LexicalScope *curLexScope = nullptr; typedef void Destroyer(CIRGenFunction &cgf, Address addr, QualType ty); static Destroyer destroyCXXObject; void pushDestroy(CleanupKind kind, Address addr, QualType type, Destroyer *destroyer); Destroyer *getDestroyer(clang::QualType::DestructionKind kind); /// ---------------------- /// CIR emit functions /// ---------------------- public: mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, QualType ty, SourceLocation loc, SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue = nullptr); mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue = nullptr); private: void emitAndUpdateRetAlloca(clang::QualType type, mlir::Location loc, clang::CharUnits alignment); CIRGenCallee emitDirectCallee(const GlobalDecl &gd); public: Address emitAddrOfFieldStorage(Address base, const FieldDecl *field, llvm::StringRef fieldName, unsigned fieldIndex); mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment, bool insertIntoFnEntryBlock, mlir::Value arraySize = nullptr); mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment, mlir::OpBuilder::InsertPoint ip, mlir::Value arraySize = nullptr); void emitAggregateStore(mlir::Value value, Address dest); void emitAggExpr(const clang::Expr *e, AggValueSlot slot); LValue emitAggExprToLValue(const Expr *e); /// Emit an aggregate copy. /// /// \param isVolatile \c true iff either the source or the destination is /// volatile. /// \param MayOverlap Whether the tail padding of the destination might be /// occupied by some other object. More efficient code can often be /// generated if not. void emitAggregateCopy(LValue dest, LValue src, QualType eltTy, AggValueSlot::Overlap_t mayOverlap); /// Emit code to compute the specified expression which can have any type. The /// result is returned as an RValue struct. If this is an aggregate /// expression, the aggloc/agglocvolatile arguments indicate where the result /// should be returned. RValue emitAnyExpr(const clang::Expr *e, AggValueSlot aggSlot = AggValueSlot::ignored()); /// Emits the code necessary to evaluate an arbitrary expression into the /// given memory location. void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, bool isInitializer); /// Similarly to emitAnyExpr(), however, the result will always be accessible /// even if no aggregate location is provided. RValue emitAnyExprToTemp(const clang::Expr *e); void emitArrayDestroy(mlir::Value begin, mlir::Value numElements, QualType elementType, CharUnits elementAlign, Destroyer *destroyer); mlir::Value emitArrayLength(const clang::ArrayType *arrayType, QualType &baseType, Address &addr); LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e); Address emitArrayToPointerDecay(const Expr *e, LValueBaseInfo *baseInfo = nullptr); mlir::LogicalResult emitAsmStmt(const clang::AsmStmt &s); RValue emitAtomicExpr(AtomicExpr *e); void emitAtomicInit(Expr *init, LValue dest); AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d, mlir::OpBuilder::InsertPoint ip = {}); /// Emit code and set up symbol table for a variable declaration with auto, /// register, or no storage class specifier. These turn into simple stack /// objects, globals depending on target. void emitAutoVarDecl(const clang::VarDecl &d); void emitAutoVarCleanups(const AutoVarEmission &emission); /// Emit the initializer for an allocated variable. If this call is not /// associated with the call to emitAutoVarAlloca (as the address of the /// emission is not directly an alloca), the allocatedSeparately parameter can /// be used to suppress the assertions. However, this should only be used in /// extreme cases, as it doesn't properly reflect the language/AST. void emitAutoVarInit(const AutoVarEmission &emission); void emitAutoVarTypeCleanup(const AutoVarEmission &emission, clang::QualType::DestructionKind dtorKind); void maybeEmitDeferredVarDeclInit(const VarDecl *vd); void emitBaseInitializer(mlir::Location loc, const CXXRecordDecl *classDecl, CXXCtorInitializer *baseInit); LValue emitBinaryOperatorLValue(const BinaryOperator *e); mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s); RValue emitBuiltinExpr(const clang::GlobalDecl &gd, unsigned builtinID, const clang::CallExpr *e, ReturnValueSlot returnValue); RValue emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, const CallArgList &args, cir::CIRCallOpInterface *callOp, mlir::Location loc); RValue emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, const CallArgList &args, cir::CIRCallOpInterface *callOrTryCall = nullptr) { assert(currSrcLoc && "source location must have been set"); return emitCall(funcInfo, callee, returnValue, args, callOrTryCall, *currSrcLoc); } RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee, const clang::CallExpr *e, ReturnValueSlot returnValue); void emitCallArg(CallArgList &args, const clang::Expr *e, clang::QualType argType); void emitCallArgs( CallArgList &args, PrototypeWrapper prototype, llvm::iterator_range argRange, AbstractCallee callee = AbstractCallee(), unsigned paramsToSkip = 0); RValue emitCallExpr(const clang::CallExpr *e, ReturnValueSlot returnValue = ReturnValueSlot()); LValue emitCallExprLValue(const clang::CallExpr *e); CIRGenCallee emitCallee(const clang::Expr *e); template mlir::LogicalResult emitCaseDefaultCascade(const T *stmt, mlir::Type condType, mlir::ArrayAttr value, cir::CaseOpKind kind, bool buildingTopLevelCase); mlir::LogicalResult emitCaseStmt(const clang::CaseStmt &s, mlir::Type condType, bool buildingTopLevelCase); LValue emitCastLValue(const CastExpr *e); /// Emits an argument for a call to a `__builtin_assume`. If the builtin /// sanitizer is enabled, a runtime check is also emitted. mlir::Value emitCheckedArgForAssume(const Expr *e); /// Emit a conversion from the specified complex type to the specified /// destination type, where the destination type is an LLVM scalar type. mlir::Value emitComplexToScalarConversion(mlir::Value src, QualType srcTy, QualType dstTy, SourceLocation loc); LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e); LValue emitCompoundLiteralLValue(const CompoundLiteralExpr *e); void emitConstructorBody(FunctionArgList &args); void emitDestroy(Address addr, QualType type, Destroyer *destroyer); void emitDestructorBody(FunctionArgList &args); mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); void emitCXXConstructExpr(const clang::CXXConstructExpr *e, AggValueSlot dest); void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, const clang::ArrayType *arrayType, Address arrayBegin, const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize = false); void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, mlir::Value numElements, Address arrayBase, const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize); void emitCXXConstructorCall(const clang::CXXConstructorDecl *d, clang::CXXCtorType type, bool forVirtualBase, bool delegating, AggValueSlot thisAVS, const clang::CXXConstructExpr *e); void emitCXXConstructorCall(const clang::CXXConstructorDecl *d, clang::CXXCtorType type, bool forVirtualBase, bool delegating, Address thisAddr, CallArgList &args, clang::SourceLocation loc); void emitCXXDestructorCall(const CXXDestructorDecl *dd, CXXDtorType type, bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy); RValue emitCXXDestructorCall(GlobalDecl dtor, const CIRGenCallee &callee, mlir::Value thisVal, QualType thisTy, mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *e); mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s, llvm::ArrayRef attrs); RValue emitCXXMemberCallExpr(const clang::CXXMemberCallExpr *e, ReturnValueSlot returnValue); RValue emitCXXMemberOrOperatorCall( const clang::CXXMethodDecl *md, const CIRGenCallee &callee, ReturnValueSlot returnValue, mlir::Value thisPtr, mlir::Value implicitParam, clang::QualType implicitParamTy, const clang::CallExpr *ce, CallArgList *rtlArgs); RValue emitCXXMemberOrOperatorMemberCallExpr( const clang::CallExpr *ce, const clang::CXXMethodDecl *md, ReturnValueSlot returnValue, bool hasQualifier, clang::NestedNameSpecifier qualifier, bool isArrow, const clang::Expr *base); mlir::Value emitCXXNewExpr(const CXXNewExpr *e); RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e, const CXXMethodDecl *md, ReturnValueSlot returnValue); RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr); void emitCXXThrowExpr(const CXXThrowExpr *e); void emitCtorPrologue(const clang::CXXConstructorDecl *ctor, clang::CXXCtorType ctorType, FunctionArgList &args); // It's important not to confuse this and emitDelegateCXXConstructorCall. // Delegating constructors are the C++11 feature. The constructor delegate // optimization is used to reduce duplication in the base and complete // constructors where they are substantially the same. void emitDelegatingCXXConstructorCall(const CXXConstructorDecl *ctor, const FunctionArgList &args); mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); /// Emit an expression as an initializer for an object (variable, field, etc.) /// at the given location. The expression is not necessarily the normal /// initializer for the object, and the address is not necessarily /// its normal location. /// /// \param init the initializing expression /// \param d the object to act as if we're initializing /// \param lvalue the lvalue to initialize /// \param capturedByInit true if \p d is a __block variable whose address is /// potentially changed by the initializer void emitExprAsInit(const clang::Expr *init, const clang::ValueDecl *d, LValue lvalue, bool capturedByInit = false); mlir::LogicalResult emitFunctionBody(const clang::Stmt *body); mlir::LogicalResult emitGotoStmt(const clang::GotoStmt &s); void emitImplicitAssignmentOperatorBody(FunctionArgList &args); void emitInitializerForField(clang::FieldDecl *field, LValue lhs, clang::Expr *init); mlir::Value emitPromotedComplexExpr(const Expr *e, QualType promotionType); mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType); mlir::Value emitPromotedValue(mlir::Value result, QualType promotionType); void emitReturnOfRValue(mlir::Location loc, RValue rv, QualType ty); /// Emit the computation of the specified expression of scalar type. mlir::Value emitScalarExpr(const clang::Expr *e); mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv, cir::UnaryOpKind kind, bool isPre); /// Build a debug stoppoint if we are emitting debug info. void emitStopPoint(const Stmt *s); // Build CIR for a statement. useCurrentScope should be true if no // new scopes need be created when finding a compound statement. mlir::LogicalResult emitStmt(const clang::Stmt *s, bool useCurrentScope, llvm::ArrayRef attrs = {}); mlir::LogicalResult emitSimpleStmt(const clang::Stmt *s, bool useCurrentScope); mlir::LogicalResult emitForStmt(const clang::ForStmt &s); void emitForwardingCallToLambda(const CXXMethodDecl *lambdaCallOperator, CallArgList &callArgs); /// Emit the computation of the specified expression of complex type, /// returning the result. mlir::Value emitComplexExpr(const Expr *e); void emitComplexExprIntoLValue(const Expr *e, LValue dest, bool isInit); mlir::Value emitComplexPrePostIncDec(const UnaryOperator *e, LValue lv, cir::UnaryOpKind op, bool isPre); LValue emitComplexAssignmentLValue(const BinaryOperator *e); LValue emitComplexCompoundAssignmentLValue(const CompoundAssignOperator *e); LValue emitScalarCompoundAssignWithComplex(const CompoundAssignOperator *e, mlir::Value &result); mlir::LogicalResult emitCompoundStmt(const clang::CompoundStmt &s, Address *lastValue = nullptr, AggValueSlot slot = AggValueSlot::ignored()); mlir::LogicalResult emitCompoundStmtWithoutScope(const clang::CompoundStmt &s, Address *lastValue = nullptr, AggValueSlot slot = AggValueSlot::ignored()); void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false); mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s); LValue emitDeclRefLValue(const clang::DeclRefExpr *e); mlir::LogicalResult emitDefaultStmt(const clang::DefaultStmt &s, 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. /// In the future, we may apply code generation simplifications here, /// similar to those used in classic LLVM codegen /// See `EmitBranchOnBoolExpr` for inspiration. mlir::LogicalResult emitIfOnBoolExpr(const clang::Expr *cond, const clang::Stmt *thenS, const clang::Stmt *elseS); cir::IfOp emitIfOnBoolExpr(const clang::Expr *cond, BuilderCallbackRef thenBuilder, mlir::Location thenLoc, BuilderCallbackRef elseBuilder, std::optional elseLoc = {}); mlir::Value emitOpOnBoolExpr(mlir::Location loc, const clang::Expr *cond); mlir::LogicalResult emitLabel(const clang::LabelDecl &d); mlir::LogicalResult emitLabelStmt(const clang::LabelStmt &s); void emitLambdaDelegatingInvokeBody(const CXXMethodDecl *md); void emitLambdaStaticInvokeBody(const CXXMethodDecl *md); mlir::LogicalResult emitIfStmt(const clang::IfStmt &s); /// Emit code to compute the specified expression, /// ignoring the result. void emitIgnoredExpr(const clang::Expr *e); RValue emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc); /// Load a complex number from the specified l-value. mlir::Value emitLoadOfComplex(LValue src, SourceLocation loc); /// Given an expression that represents a value lvalue, this method emits /// the address of the lvalue, then loads the result as an rvalue, /// returning the rvalue. RValue emitLoadOfLValue(LValue lv, SourceLocation loc); Address emitLoadOfReference(LValue refLVal, mlir::Location loc, LValueBaseInfo *pointeeBaseInfo); LValue emitLoadOfReferenceLValue(Address refAddr, mlir::Location loc, QualType refTy, AlignmentSource source); /// EmitLoadOfScalar - Load a scalar value from an address, taking /// care to appropriately convert from the memory representation to /// the LLVM value representation. The l-value must be a simple /// l-value. mlir::Value emitLoadOfScalar(LValue lvalue, SourceLocation loc); mlir::Value emitLoadOfScalar(Address addr, bool isVolatile, QualType ty, SourceLocation loc, LValueBaseInfo baseInfo); /// Emit code to compute a designator that specifies the location /// of the expression. /// FIXME: document this function better. LValue emitLValue(const clang::Expr *e); LValue emitLValueForBitField(LValue base, const FieldDecl *field); LValue emitLValueForField(LValue base, const clang::FieldDecl *field); LValue emitLValueForLambdaField(const FieldDecl *field); LValue emitLValueForLambdaField(const FieldDecl *field, mlir::Value thisValue); /// Like emitLValueForField, excpet that if the Field is a reference, this /// will return the address of the reference and not the address of the value /// stored in the reference. LValue emitLValueForFieldInitialization(LValue base, const clang::FieldDecl *field, llvm::StringRef fieldName); LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e); LValue emitMemberExpr(const MemberExpr *e); /// Given an expression with a pointer type, emit the value and compute our /// best estimate of the alignment of the pointee. /// /// One reasonable way to use this information is when there's a language /// guarantee that the pointer must be aligned to some stricter value, and /// we're simply trying to ensure that sufficiently obvious uses of under- /// aligned objects don't get miscompiled; for example, a placement new /// into the address of a local variable. In such a case, it's quite /// reasonable to just ignore the returned alignment when it isn't from an /// explicit source. Address emitPointerWithAlignment(const clang::Expr *expr, LValueBaseInfo *baseInfo = nullptr); /// Emits a reference binding to the passed in expression. RValue emitReferenceBindingToExpr(const Expr *e); mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s); RValue emitRotate(const CallExpr *e, bool isRotateLeft); mlir::Value emitScalarConstant(const ConstantEmission &constant, Expr *e); /// Emit a conversion from the specified type to the specified destination /// type, both of which are CIR scalar types. mlir::Value emitScalarConversion(mlir::Value src, clang::QualType srcType, clang::QualType dstType, clang::SourceLocation loc); void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit = false); void emitStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage); void emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, bool isInit); void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, bool isInit = false, bool isNontemporal = false); void emitStoreOfScalar(mlir::Value value, LValue lvalue, bool isInit); /// Store the specified rvalue into the specified /// lvalue, where both are guaranteed to the have the same type, and that type /// is 'Ty'. void emitStoreThroughLValue(RValue src, LValue dst, bool isInit = false); mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult); LValue emitStringLiteralLValue(const StringLiteral *e); mlir::LogicalResult emitSwitchBody(const clang::Stmt *s); mlir::LogicalResult emitSwitchCase(const clang::SwitchCase &s, bool buildingTopLevelCase); mlir::LogicalResult emitSwitchStmt(const clang::SwitchStmt &s); /// Given a value and its clang type, returns the value casted to its memory /// representation. /// Note: CIR defers most of the special casting to the final lowering passes /// to conserve the high level information. mlir::Value emitToMemory(mlir::Value value, clang::QualType ty); /// Emit a trap instruction, which is used to abort the program in an abnormal /// way, usually for debugging purposes. /// \p createNewBlock indicates whether to create a new block for the IR /// builder. Since the `cir.trap` operation is a terminator, operations that /// follow a trap cannot be emitted after `cir.trap` in the same block. To /// ensure these operations get emitted successfully, you need to create a new /// dummy block and set the insertion point there before continuing from the /// trap operation. void emitTrap(mlir::Location loc, bool createNewBlock); LValue emitUnaryOpLValue(const clang::UnaryOperator *e); mlir::Value emitUnPromotedValue(mlir::Value result, QualType unPromotionType); /// Emit a reached-unreachable diagnostic if \p loc is valid and runtime /// checking is enabled. Otherwise, just emit an unreachable instruction. /// \p createNewBlock indicates whether to create a new block for the IR /// builder. Since the `cir.unreachable` operation is a terminator, operations /// that follow an unreachable point cannot be emitted after `cir.unreachable` /// in the same block. To ensure these operations get emitted successfully, /// you need to create a dummy block and set the insertion point there before /// continuing from the unreachable point. void emitUnreachable(clang::SourceLocation loc, bool createNewBlock); /// This method handles emission of any variable declaration /// inside a function, including static vars etc. void emitVarDecl(const clang::VarDecl &d); void emitVariablyModifiedType(QualType ty); mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s); /// Given an assignment `*lhs = rhs`, emit a test that checks if \p rhs is /// nonnull, if 1\p LHS is marked _Nonnull. void emitNullabilityCheck(LValue lhs, mlir::Value rhs, clang::SourceLocation loc); /// An object to manage conditionally-evaluated expressions. class ConditionalEvaluation { CIRGenFunction &cgf; mlir::OpBuilder::InsertPoint insertPt; public: ConditionalEvaluation(CIRGenFunction &cgf) : cgf(cgf), insertPt(cgf.builder.saveInsertionPoint()) {} ConditionalEvaluation(CIRGenFunction &cgf, mlir::OpBuilder::InsertPoint ip) : cgf(cgf), insertPt(ip) {} void beginEvaluation() { assert(cgf.outermostConditional != this); if (!cgf.outermostConditional) cgf.outermostConditional = this; } void endEvaluation() { assert(cgf.outermostConditional != nullptr); if (cgf.outermostConditional == this) cgf.outermostConditional = nullptr; } /// Returns the insertion point which will be executed prior to each /// evaluation of the conditional code. In LLVM OG, this method /// is called getStartingBlock. mlir::OpBuilder::InsertPoint getInsertPoint() const { return insertPt; } }; struct ConditionalInfo { std::optional lhs{}, rhs{}; mlir::Value result{}; }; // Return true if we're currently emitting one branch or the other of a // conditional expression. bool isInConditionalBranch() const { return outermostConditional != nullptr; } void setBeforeOutermostConditional(mlir::Value value, Address addr) { assert(isInConditionalBranch()); { mlir::OpBuilder::InsertionGuard guard(builder); builder.restoreInsertionPoint(outermostConditional->getInsertPoint()); builder.createStore( value.getLoc(), value, addr, /*isVolatile=*/false, mlir::IntegerAttr::get( mlir::IntegerType::get(value.getContext(), 64), (uint64_t)addr.getAlignment().getAsAlign().value())); } } // Points to the outermost active conditional control. This is used so that // we know if a temporary should be destroyed conditionally. ConditionalEvaluation *outermostConditional = nullptr; /// An RAII object to record that we're evaluating a statement /// expression. class StmtExprEvaluation { CIRGenFunction &cgf; /// We have to save the outermost conditional: cleanups in a /// statement expression aren't conditional just because the /// StmtExpr is. ConditionalEvaluation *savedOutermostConditional; public: StmtExprEvaluation(CIRGenFunction &cgf) : cgf(cgf), savedOutermostConditional(cgf.outermostConditional) { cgf.outermostConditional = nullptr; } ~StmtExprEvaluation() { cgf.outermostConditional = savedOutermostConditional; } }; template ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e, const FuncTy &branchGenFunc); mlir::Value emitTernaryOnBoolExpr(const clang::Expr *cond, mlir::Location loc, const clang::Stmt *thenS, const clang::Stmt *elseS); /// Build a "reference" to a va_list; this is either the address or the value /// of the expression, depending on how va_list is defined. Address emitVAListRef(const Expr *e); /// Emits the start of a CIR variable-argument operation (`cir.va_start`) /// /// \param vaList A reference to the \c va_list as emitted by either /// \c emitVAListRef or \c emitMSVAListRef. /// /// \param count The number of arguments in \c vaList void emitVAStart(mlir::Value vaList, mlir::Value count); /// Emits the end of a CIR variable-argument operation (`cir.va_start`) /// /// \param vaList A reference to the \c va_list as emitted by either /// \c emitVAListRef or \c emitMSVAListRef. void emitVAEnd(mlir::Value vaList); /// Generate code to get an argument from the passed in pointer /// and update it accordingly. /// /// \param ve The \c VAArgExpr for which to generate code. /// /// \param vaListAddr Receives a reference to the \c va_list as emitted by /// either \c emitVAListRef or \c emitMSVAListRef. /// /// \returns SSA value with the argument. mlir::Value emitVAArg(VAArgExpr *ve); /// ---------------------- /// CIR build helpers /// ----------------- public: cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, const Twine &name = "tmp", mlir::Value arraySize = nullptr, bool insertIntoFnEntryBlock = false); cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, const Twine &name = "tmp", mlir::OpBuilder::InsertPoint ip = {}, mlir::Value arraySize = nullptr); Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name = "tmp", mlir::Value arraySize = nullptr, Address *alloca = nullptr, mlir::OpBuilder::InsertPoint ip = {}); Address createTempAllocaWithoutCast(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name = "tmp", mlir::Value arraySize = nullptr, mlir::OpBuilder::InsertPoint ip = {}); /// Create a temporary memory object of the given type, with /// appropriate alignmen and cast it to the default address space. Returns /// the original alloca instruction by \p Alloca if it is not nullptr. Address createMemTemp(QualType t, mlir::Location loc, const Twine &name = "tmp", Address *alloca = nullptr, mlir::OpBuilder::InsertPoint ip = {}); Address createMemTemp(QualType t, CharUnits align, mlir::Location loc, const Twine &name = "tmp", Address *alloca = nullptr, mlir::OpBuilder::InsertPoint ip = {}); //===--------------------------------------------------------------------===// // OpenACC Emission //===--------------------------------------------------------------------===// private: template Op emitOpenACCOp(mlir::Location start, OpenACCDirectiveKind dirKind, SourceLocation dirLoc, llvm::ArrayRef clauses); // Function to do the basic implementation of an operation with an Associated // Statement. Models AssociatedStmtConstruct. template mlir::LogicalResult emitOpenACCOpAssociatedStmt( mlir::Location start, mlir::Location end, OpenACCDirectiveKind dirKind, SourceLocation dirLoc, llvm::ArrayRef clauses, const Stmt *associatedStmt); template mlir::LogicalResult emitOpenACCOpCombinedConstruct( mlir::Location start, mlir::Location end, OpenACCDirectiveKind dirKind, SourceLocation dirLoc, llvm::ArrayRef clauses, const Stmt *loopStmt); template void emitOpenACCClauses(Op &op, OpenACCDirectiveKind dirKind, SourceLocation dirLoc, ArrayRef clauses); // The second template argument doesn't need to be a template, since it should // always be an mlir::acc::LoopOp, but as this is a template anyway, we make // it a template argument as this way we can avoid including the OpenACC MLIR // headers here. We will count on linker failures/explicit instantiation to // ensure we don't mess this up, but it is only called from 1 place, and // instantiated 3x. template void emitOpenACCClauses(ComputeOp &op, LoopOp &loopOp, OpenACCDirectiveKind dirKind, SourceLocation dirLoc, ArrayRef clauses); // The OpenACC LoopOp requires that we have auto, seq, or independent on all // LoopOp operations for the 'none' device type case. This function checks if // the LoopOp has one, else it updates it to have one. void updateLoopOpParallelism(mlir::acc::LoopOp &op, bool isOrphan, OpenACCDirectiveKind dk); // The OpenACC 'cache' construct actually applies to the 'loop' if present. So // keep track of the 'loop' so that we can add the cache vars to it correctly. mlir::acc::LoopOp *activeLoopOp = nullptr; struct ActiveOpenACCLoopRAII { CIRGenFunction &cgf; mlir::acc::LoopOp *oldLoopOp; ActiveOpenACCLoopRAII(CIRGenFunction &cgf, mlir::acc::LoopOp *newOp) : cgf(cgf), oldLoopOp(cgf.activeLoopOp) { cgf.activeLoopOp = newOp; } ~ActiveOpenACCLoopRAII() { cgf.activeLoopOp = oldLoopOp; } }; // Keep track of the last place we inserted a 'recipe' so that we can insert // the next one in lexical order. mlir::OpBuilder::InsertPoint lastRecipeLocation; public: // Helper type used to store the list of important information for a 'data' // clause variable, or a 'cache' variable reference. struct OpenACCDataOperandInfo { mlir::Location beginLoc; mlir::Value varValue; std::string name; // The type of the original variable reference: that is, after 'bounds' have // removed pointers/array types/etc. So in the case of int arr[5], and a // private(arr[1]), 'origType' is 'int', but 'baseType' is 'int[5]'. QualType origType; QualType baseType; llvm::SmallVector bounds; // The list of types that we found when going through the bounds, which we // can use to properly set the alloca section. llvm::SmallVector boundTypes; }; // Gets the collection of info required to lower and OpenACC clause or cache // construct variable reference. OpenACCDataOperandInfo getOpenACCDataOperandInfo(const Expr *e); // Helper function to emit the integer expressions as required by an OpenACC // clause/construct. mlir::Value emitOpenACCIntExpr(const Expr *intExpr); // Helper function to emit an integer constant as an mlir int type, used for // constants in OpenACC constructs/clauses. mlir::Value createOpenACCConstantInt(mlir::Location loc, unsigned width, int64_t value); mlir::LogicalResult emitOpenACCComputeConstruct(const OpenACCComputeConstruct &s); mlir::LogicalResult emitOpenACCLoopConstruct(const OpenACCLoopConstruct &s); mlir::LogicalResult emitOpenACCCombinedConstruct(const OpenACCCombinedConstruct &s); mlir::LogicalResult emitOpenACCDataConstruct(const OpenACCDataConstruct &s); mlir::LogicalResult emitOpenACCEnterDataConstruct(const OpenACCEnterDataConstruct &s); mlir::LogicalResult emitOpenACCExitDataConstruct(const OpenACCExitDataConstruct &s); mlir::LogicalResult emitOpenACCHostDataConstruct(const OpenACCHostDataConstruct &s); mlir::LogicalResult emitOpenACCWaitConstruct(const OpenACCWaitConstruct &s); mlir::LogicalResult emitOpenACCInitConstruct(const OpenACCInitConstruct &s); mlir::LogicalResult emitOpenACCShutdownConstruct(const OpenACCShutdownConstruct &s); mlir::LogicalResult emitOpenACCSetConstruct(const OpenACCSetConstruct &s); mlir::LogicalResult emitOpenACCUpdateConstruct(const OpenACCUpdateConstruct &s); mlir::LogicalResult emitOpenACCAtomicConstruct(const OpenACCAtomicConstruct &s); mlir::LogicalResult emitOpenACCCacheConstruct(const OpenACCCacheConstruct &s); void emitOpenACCDeclare(const OpenACCDeclareDecl &d); void emitOpenACCRoutine(const OpenACCRoutineDecl &d); /// Create a temporary memory object for the given aggregate type. AggValueSlot createAggTemp(QualType ty, mlir::Location loc, const Twine &name = "tmp", Address *alloca = nullptr) { assert(!cir::MissingFeatures::aggValueSlot()); return AggValueSlot::forAddr( createMemTemp(ty, loc, name, alloca), ty.getQualifiers(), AggValueSlot::IsNotDestructed, AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap); } private: QualType getVarArgType(const Expr *arg); }; } // namespace clang::CIRGen #endif