| //===- CIRGenFunction.cpp - Emit CIR from ASTs for a Function -------------===// |
| // |
| // 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 coordinates the per-function state used while generating code |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CIRGenFunction.h" |
| #include "CIRGenCXXABI.h" |
| #include "CIRGenModule.h" |
| #include "CIRGenOpenMPRuntime.h" |
| #include "clang/CIR/MissingFeatures.h" |
| |
| #include "clang/AST/ASTLambda.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/Basic/Builtins.h" |
| #include "clang/Basic/DiagnosticCategories.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/CIR/Dialect/IR/CIRDialect.h" |
| #include "clang/CIR/Dialect/IR/FPEnv.h" |
| #include "clang/Frontend/FrontendDiagnostic.h" |
| |
| #include "mlir/Dialect/Func/IR/FuncOps.h" |
| #include "mlir/Support/LogicalResult.h" |
| |
| using namespace cir; |
| using namespace clang; |
| using namespace mlir::cir; |
| |
| CIRGenFunction::CIRGenFunction(CIRGenModule &CGM, CIRGenBuilderTy &builder, |
| bool suppressNewContext) |
| : CIRGenTypeCache(CGM), CGM{CGM}, builder(builder), |
| SanOpts(CGM.getLangOpts().Sanitize), CurFPFeatures(CGM.getLangOpts()), |
| ShouldEmitLifetimeMarkers(false) { |
| if (!suppressNewContext) |
| CGM.getCXXABI().getMangleContext().startNewFunction(); |
| EHStack.setCGF(this); |
| |
| // TODO(CIR): SetFastMathFlags(CurFPFeatures); |
| } |
| |
| CIRGenFunction::~CIRGenFunction() { |
| assert(LifetimeExtendedCleanupStack.empty() && "failed to emit a cleanup"); |
| assert(DeferredDeactivationCleanupStack.empty() && |
| "missed to deactivate a cleanup"); |
| |
| // TODO(cir): set function is finished. |
| assert(!MissingFeatures::openMPRuntime()); |
| |
| // If we have an OpenMPIRBuilder we want to finalize functions (incl. |
| // outlining etc) at some point. Doing it once the function codegen is done |
| // seems to be a reasonable spot. We do it here, as opposed to the deletion |
| // time of the CodeGenModule, because we have to ensure the IR has not yet |
| // been "emitted" to the outside, thus, modifications are still sensible. |
| assert(!MissingFeatures::openMPRuntime()); |
| } |
| |
| clang::ASTContext &CIRGenFunction::getContext() const { |
| return CGM.getASTContext(); |
| } |
| |
| mlir::Type CIRGenFunction::ConvertType(QualType T) { |
| return CGM.getTypes().ConvertType(T); |
| } |
| |
| TypeEvaluationKind CIRGenFunction::getEvaluationKind(QualType type) { |
| type = type.getCanonicalType(); |
| while (true) { |
| switch (type->getTypeClass()) { |
| #define TYPE(name, parent) |
| #define ABSTRACT_TYPE(name, parent) |
| #define NON_CANONICAL_TYPE(name, parent) case Type::name: |
| #define DEPENDENT_TYPE(name, parent) case Type::name: |
| #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(name, parent) case Type::name: |
| #include "clang/AST/TypeNodes.inc" |
| llvm_unreachable("non-canonical or dependent type in IR-generation"); |
| |
| case Type::ArrayParameter: |
| llvm_unreachable("NYI"); |
| |
| case Type::Auto: |
| case Type::DeducedTemplateSpecialization: |
| llvm_unreachable("undeduced type in IR-generation"); |
| |
| // Various scalar types. |
| case Type::Builtin: |
| case Type::Pointer: |
| case Type::BlockPointer: |
| case Type::LValueReference: |
| case Type::RValueReference: |
| case Type::MemberPointer: |
| case Type::Vector: |
| case Type::ExtVector: |
| case Type::ConstantMatrix: |
| case Type::FunctionProto: |
| case Type::FunctionNoProto: |
| case Type::Enum: |
| case Type::ObjCObjectPointer: |
| case Type::Pipe: |
| case Type::BitInt: |
| return TEK_Scalar; |
| |
| // Complexes. |
| case Type::Complex: |
| return TEK_Complex; |
| |
| // Arrays, records, and Objective-C objects. |
| case Type::ConstantArray: |
| case Type::IncompleteArray: |
| case Type::VariableArray: |
| case Type::Record: |
| case Type::ObjCObject: |
| case Type::ObjCInterface: |
| return TEK_Aggregate; |
| |
| // We operate on atomic values according to their underlying type. |
| case Type::Atomic: |
| type = cast<AtomicType>(type)->getValueType(); |
| continue; |
| } |
| llvm_unreachable("unknown type kind!"); |
| } |
| } |
| |
| mlir::Type CIRGenFunction::convertTypeForMem(QualType T) { |
| return CGM.getTypes().convertTypeForMem(T); |
| } |
| |
| mlir::Type CIRGenFunction::convertType(QualType T) { |
| return CGM.getTypes().ConvertType(T); |
| } |
| |
| mlir::Location CIRGenFunction::getLoc(SourceLocation SLoc) { |
| // Some AST nodes might contain invalid source locations (e.g. |
| // CXXDefaultArgExpr), workaround that to still get something out. |
| if (SLoc.isValid()) { |
| const SourceManager &SM = getContext().getSourceManager(); |
| PresumedLoc PLoc = SM.getPresumedLoc(SLoc); |
| StringRef Filename = PLoc.getFilename(); |
| return mlir::FileLineColLoc::get(builder.getStringAttr(Filename), |
| PLoc.getLine(), PLoc.getColumn()); |
| } else { |
| // Do our best... |
| assert(currSrcLoc && "expected to inherit some source location"); |
| return *currSrcLoc; |
| } |
| } |
| |
| mlir::Location CIRGenFunction::getLoc(SourceRange SLoc) { |
| // Some AST nodes might contain invalid source locations (e.g. |
| // CXXDefaultArgExpr), workaround that to still get something out. |
| if (SLoc.isValid()) { |
| mlir::Location B = getLoc(SLoc.getBegin()); |
| mlir::Location E = getLoc(SLoc.getEnd()); |
| SmallVector<mlir::Location, 2> locs = {B, E}; |
| mlir::Attribute metadata; |
| return mlir::FusedLoc::get(locs, metadata, builder.getContext()); |
| } else if (currSrcLoc) { |
| return *currSrcLoc; |
| } |
| |
| // We're brave, but time to give up. |
| return builder.getUnknownLoc(); |
| } |
| |
| mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) { |
| SmallVector<mlir::Location, 2> locs = {lhs, rhs}; |
| mlir::Attribute metadata; |
| return mlir::FusedLoc::get(locs, metadata, builder.getContext()); |
| } |
| |
| /// 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 CIRGenFunction::ContainsLabel(const Stmt *S, bool IgnoreCaseStmts) { |
| // Null statement, not a label! |
| if (!S) |
| return false; |
| |
| // If this is a label, we have to emit the code, consider something like: |
| // if (0) { ... foo: bar(); } goto foo; |
| // |
| // TODO: If anyone cared, we could track __label__'s, since we know that you |
| // can't jump to one from outside their declared region. |
| if (isa<LabelStmt>(S)) |
| return true; |
| |
| // If this is a case/default statement, and we haven't seen a switch, we |
| // have to emit the code. |
| if (isa<SwitchCase>(S) && !IgnoreCaseStmts) |
| return true; |
| |
| // If this is a switch statement, we want to ignore cases below it. |
| if (isa<SwitchStmt>(S)) |
| IgnoreCaseStmts = true; |
| |
| // Scan subexpressions for verboten labels. |
| for (const Stmt *SubStmt : S->children()) |
| if (ContainsLabel(SubStmt, IgnoreCaseStmts)) |
| return true; |
| |
| return false; |
| } |
| |
| bool CIRGenFunction::sanitizePerformTypeCheck() const { |
| return SanOpts.has(SanitizerKind::Null) || |
| SanOpts.has(SanitizerKind::Alignment) || |
| SanOpts.has(SanitizerKind::ObjectSize) || |
| SanOpts.has(SanitizerKind::Vptr); |
| } |
| |
| void CIRGenFunction::buildTypeCheck(TypeCheckKind TCK, |
| clang::SourceLocation Loc, mlir::Value V, |
| clang::QualType Type, |
| clang::CharUnits Alignment, |
| clang::SanitizerSet SkippedChecks, |
| std::optional<mlir::Value> ArraySize) { |
| if (!sanitizePerformTypeCheck()) |
| return; |
| |
| assert(false && "type check NYI"); |
| } |
| |
| /// 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 folded value. |
| bool CIRGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond, |
| llvm::APSInt &ResultInt, |
| bool AllowLabels) { |
| // FIXME: Rename and handle conversion of other evaluatable things |
| // to bool. |
| Expr::EvalResult Result; |
| if (!Cond->EvaluateAsInt(Result, getContext())) |
| return false; // Not foldable, not integer or not fully evaluatable. |
| |
| llvm::APSInt Int = Result.Val.getInt(); |
| if (!AllowLabels && ContainsLabel(Cond)) |
| return false; // Contains a label. |
| |
| ResultInt = Int; |
| return true; |
| } |
| |
| mlir::Type CIRGenFunction::getCIRType(const QualType &type) { |
| return CGM.getCIRType(type); |
| } |
| |
| /// Determine whether the function F ends with a return stmt. |
| static bool endsWithReturn(const Decl *F) { |
| const Stmt *Body = nullptr; |
| if (auto *FD = dyn_cast_or_null<FunctionDecl>(F)) |
| Body = FD->getBody(); |
| else if (auto *OMD = dyn_cast_or_null<ObjCMethodDecl>(F)) |
| llvm_unreachable("NYI"); |
| |
| if (auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) { |
| auto LastStmt = CS->body_rbegin(); |
| if (LastStmt != CS->body_rend()) |
| return isa<ReturnStmt>(*LastStmt); |
| } |
| return false; |
| } |
| |
| void CIRGenFunction::buildAndUpdateRetAlloca(QualType ty, mlir::Location loc, |
| CharUnits alignment) { |
| |
| if (ty->isVoidType()) { |
| // Void type; nothing to return. |
| ReturnValue = Address::invalid(); |
| |
| // Count the implicit return. |
| if (!endsWithReturn(CurFuncDecl)) |
| ++NumReturnExprs; |
| } else if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect) { |
| // TODO(CIR): Consider this implementation in CIRtoLLVM |
| llvm_unreachable("NYI"); |
| // TODO(CIR): Consider this implementation in CIRtoLLVM |
| } else if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::InAlloca) { |
| llvm_unreachable("NYI"); |
| } else { |
| auto addr = buildAlloca("__retval", ty, loc, alignment); |
| FnRetAlloca = addr; |
| ReturnValue = Address(addr, alignment); |
| |
| // Tell the epilog emitter to autorelease the result. We do this now so |
| // that various specialized functions can suppress it during their IR - |
| // generation |
| if (getLangOpts().ObjCAutoRefCount) |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| mlir::LogicalResult CIRGenFunction::declare(const Decl *var, QualType ty, |
| mlir::Location loc, |
| CharUnits alignment, |
| mlir::Value &addr, bool isParam) { |
| const auto *namedVar = dyn_cast_or_null<NamedDecl>(var); |
| assert(namedVar && "Needs a named decl"); |
| assert(!symbolTable.count(var) && "not supposed to be available just yet"); |
| |
| addr = buildAlloca(namedVar->getName(), ty, loc, alignment); |
| if (isParam) { |
| auto allocaOp = cast<mlir::cir::AllocaOp>(addr.getDefiningOp()); |
| allocaOp.setInitAttr(mlir::UnitAttr::get(builder.getContext())); |
| } |
| |
| symbolTable.insert(var, addr); |
| return mlir::success(); |
| } |
| |
| mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var, |
| QualType ty, mlir::Location loc, |
| CharUnits alignment, |
| mlir::Value &addrVal, |
| bool isParam) { |
| const auto *namedVar = dyn_cast_or_null<NamedDecl>(var); |
| assert(namedVar && "Needs a named decl"); |
| assert(!symbolTable.count(var) && "not supposed to be available just yet"); |
| |
| addrVal = addr.getPointer(); |
| if (isParam) { |
| auto allocaOp = cast<mlir::cir::AllocaOp>(addrVal.getDefiningOp()); |
| allocaOp.setInitAttr(mlir::UnitAttr::get(builder.getContext())); |
| } |
| |
| symbolTable.insert(var, addrVal); |
| return mlir::success(); |
| } |
| |
| /// All scope related cleanup needed: |
| /// - Patching up unsolved goto's. |
| /// - Build all cleanup code and insert yield/returns. |
| void CIRGenFunction::LexicalScope::cleanup() { |
| auto &builder = CGF.builder; |
| auto *localScope = CGF.currLexScope; |
| |
| auto applyCleanup = [&]() { |
| if (PerformCleanup) { |
| // ApplyDebugLocation |
| assert(!MissingFeatures::generateDebugInfo()); |
| ForceCleanup(); |
| } |
| }; |
| |
| // Cleanup are done right before codegen resume a scope. This is where |
| // objects are destroyed. |
| unsigned curLoc = 0; |
| for (auto *retBlock : localScope->getRetBlocks()) { |
| mlir::OpBuilder::InsertionGuard guard(builder); |
| builder.setInsertionPointToEnd(retBlock); |
| mlir::Location retLoc = *localScope->getRetLocs()[curLoc]; |
| curLoc++; |
| (void)buildReturn(retLoc); |
| } |
| |
| auto insertCleanupAndLeave = [&](mlir::Block *InsPt) { |
| mlir::OpBuilder::InsertionGuard guard(builder); |
| builder.setInsertionPointToEnd(InsPt); |
| |
| // Leverage and defers to RunCleanupsScope's dtor and scope handling. |
| applyCleanup(); |
| |
| if (localScope->Depth == 0) { |
| buildImplicitReturn(); |
| return; |
| } |
| |
| // End of any local scope != function |
| // Ternary ops have to deal with matching arms for yielding types |
| // and do return a value, it must do its own cir.yield insertion. |
| if (!localScope->isTernary()) { |
| !retVal ? builder.create<YieldOp>(localScope->EndLoc) |
| : builder.create<YieldOp>(localScope->EndLoc, retVal); |
| } |
| }; |
| |
| // If a cleanup block has been created at some point, branch to it |
| // and set the insertion point to continue at the cleanup block. |
| // Terminators are then inserted either in the cleanup block or |
| // inline in this current block. |
| auto *cleanupBlock = localScope->getCleanupBlock(builder); |
| if (cleanupBlock) |
| insertCleanupAndLeave(cleanupBlock); |
| |
| // Now deal with any pending block wrap up like implicit end of |
| // scope. |
| |
| // If a terminator is already present in the current block, nothing |
| // else to do here. |
| auto *currBlock = builder.getBlock(); |
| if (currBlock->mightHaveTerminator() && currBlock->getTerminator()) |
| return; |
| |
| // An empty non-entry block has nothing to offer, and since this is |
| // synthetic, losing information does not affect anything. |
| bool entryBlock = builder.getInsertionBlock()->isEntryBlock(); |
| if (!entryBlock && currBlock->empty()) { |
| currBlock->erase(); |
| // Remove unused cleanup blocks. |
| if (cleanupBlock && cleanupBlock->hasNoPredecessors()) |
| cleanupBlock->erase(); |
| // FIXME(cir): ideally we should call applyCleanup() before we |
| // get into this condition and emit the proper cleanup. This is |
| // needed to get nrvo to interop with dtor logic. |
| PerformCleanup = false; |
| return; |
| } |
| |
| // If there's a cleanup block, branch to it, nothing else to do. |
| if (cleanupBlock) { |
| builder.create<BrOp>(currBlock->back().getLoc(), cleanupBlock); |
| return; |
| } |
| |
| // No pre-existent cleanup block, emit cleanup code and yield/return. |
| insertCleanupAndLeave(currBlock); |
| } |
| |
| mlir::cir::ReturnOp |
| CIRGenFunction::LexicalScope::buildReturn(mlir::Location loc) { |
| auto &builder = CGF.getBuilder(); |
| |
| // If we are on a coroutine, add the coro_end builtin call. |
| auto Fn = dyn_cast<mlir::cir::FuncOp>(CGF.CurFn); |
| assert(Fn && "other callables NYI"); |
| if (Fn.getCoroutine()) |
| CGF.buildCoroEndBuiltinCall( |
| loc, builder.getNullPtr(builder.getVoidPtrTy(), loc)); |
| |
| if (CGF.FnRetCIRTy.has_value()) { |
| // If there's anything to return, load it first. |
| auto val = builder.create<LoadOp>(loc, *CGF.FnRetCIRTy, *CGF.FnRetAlloca); |
| return builder.create<ReturnOp>(loc, llvm::ArrayRef(val.getResult())); |
| } |
| return builder.create<ReturnOp>(loc); |
| } |
| |
| void CIRGenFunction::LexicalScope::buildImplicitReturn() { |
| auto &builder = CGF.getBuilder(); |
| auto *localScope = CGF.currLexScope; |
| |
| const auto *FD = cast<clang::FunctionDecl>(CGF.CurGD.getDecl()); |
| |
| // C++11 [stmt.return]p2: |
| // Flowing off the end of a function [...] results in undefined behavior |
| // in a value-returning function. |
| // C11 6.9.1p12: |
| // If the '}' that terminates a function is reached, and the value of the |
| // function call is used by the caller, the behavior is undefined. |
| if (CGF.getLangOpts().CPlusPlus && !FD->hasImplicitReturnZero() && |
| !CGF.SawAsmBlock && !FD->getReturnType()->isVoidType() && |
| builder.getInsertionBlock()) { |
| bool shouldEmitUnreachable = CGF.CGM.getCodeGenOpts().StrictReturn || |
| !CGF.CGM.MayDropFunctionReturn( |
| FD->getASTContext(), FD->getReturnType()); |
| |
| if (CGF.SanOpts.has(SanitizerKind::Return)) { |
| assert(!MissingFeatures::sanitizerReturn()); |
| llvm_unreachable("NYI"); |
| } else if (shouldEmitUnreachable) { |
| if (CGF.CGM.getCodeGenOpts().OptimizationLevel == 0) { |
| builder.create<mlir::cir::TrapOp>(localScope->EndLoc); |
| builder.clearInsertionPoint(); |
| return; |
| } |
| } |
| |
| if (CGF.SanOpts.has(SanitizerKind::Return) || shouldEmitUnreachable) { |
| builder.create<mlir::cir::UnreachableOp>(localScope->EndLoc); |
| builder.clearInsertionPoint(); |
| return; |
| } |
| } |
| |
| (void)buildReturn(localScope->EndLoc); |
| } |
| |
| mlir::cir::TryOp CIRGenFunction::LexicalScope::getClosestTryParent() { |
| auto *scope = this; |
| while (scope) { |
| if (scope->isTry()) |
| return scope->getTry(); |
| scope = scope->ParentScope; |
| } |
| return nullptr; |
| } |
| |
| void CIRGenFunction::finishFunction(SourceLocation EndLoc) { |
| // CIRGen doesn't use a BreakContinueStack or evaluates OnlySimpleReturnStmts. |
| |
| // Usually the return expression is evaluated before the cleanup |
| // code. If the function contains only a simple return statement, |
| // such as a constant, the location before the cleanup code becomes |
| // the last useful breakpoint in the function, because the simple |
| // return expression will be evaluated after the cleanup code. To be |
| // safe, set the debug location for cleanup code to the location of |
| // the return statement. Otherwise the cleanup code should be at the |
| // end of the function's lexical scope. |
| // |
| // If there are multiple branches to the return block, the branch |
| // instructions will get the location of the return statements and |
| // all will be fine. |
| if (auto *DI = getDebugInfo()) |
| assert(!MissingFeatures::generateDebugInfo() && "NYI"); |
| |
| // Pop any cleanups that might have been associated with the |
| // parameters. Do this in whatever block we're currently in; it's |
| // important to do this before we enter the return block or return |
| // edges will be *really* confused. |
| bool HasCleanups = EHStack.stable_begin() != PrologueCleanupDepth; |
| if (HasCleanups) { |
| // Make sure the line table doesn't jump back into the body for |
| // the ret after it's been at EndLoc. |
| if (auto *DI = getDebugInfo()) |
| assert(!MissingFeatures::generateDebugInfo() && "NYI"); |
| // FIXME(cir): vla.c test currently crashes here. |
| // PopCleanupBlocks(PrologueCleanupDepth); |
| } |
| |
| // Emit function epilog (to return). |
| |
| // Original LLVM codegen does EmitReturnBlock() here, CIRGen handles |
| // this as part of LexicalScope instead, given CIR might have multiple |
| // blocks with `cir.return`. |
| if (ShouldInstrumentFunction()) { |
| assert(!MissingFeatures::shouldInstrumentFunction() && "NYI"); |
| } |
| |
| // Emit debug descriptor for function end. |
| if (auto *DI = getDebugInfo()) |
| assert(!MissingFeatures::generateDebugInfo() && "NYI"); |
| |
| // Reset the debug location to that of the simple 'return' expression, if any |
| // rather than that of the end of the function's scope '}'. |
| assert(!MissingFeatures::generateDebugInfo() && "NYI"); |
| |
| assert(!MissingFeatures::emitFunctionEpilog() && "NYI"); |
| assert(!MissingFeatures::emitEndEHSpec() && "NYI"); |
| |
| // FIXME(cir): vla.c test currently crashes here. |
| // assert(EHStack.empty() && "did not remove all scopes from cleanup stack!"); |
| |
| // If someone did an indirect goto, emit the indirect goto block at the end of |
| // the function. |
| assert(!MissingFeatures::indirectBranch() && "NYI"); |
| |
| // If some of our locals escaped, insert a call to llvm.localescape in the |
| // entry block. |
| assert(!MissingFeatures::escapedLocals() && "NYI"); |
| |
| // If someone took the address of a label but never did an indirect goto, we |
| // made a zero entry PHI node, which is illegal, zap it now. |
| assert(!MissingFeatures::indirectBranch() && "NYI"); |
| |
| // CIRGen doesn't need to emit EHResumeBlock, TerminateLandingPad, |
| // TerminateHandler, UnreachableBlock, TerminateFunclets, NormalCleanupDest |
| // here because the basic blocks aren't shared. |
| |
| assert(!MissingFeatures::emitDeclMetadata() && "NYI"); |
| assert(!MissingFeatures::deferredReplacements() && "NYI"); |
| |
| // Add the min-legal-vector-width attribute. This contains the max width from: |
| // 1. min-vector-width attribute used in the source program. |
| // 2. Any builtins used that have a vector width specified. |
| // 3. Values passed in and out of inline assembly. |
| // 4. Width of vector arguments and return types for this function. |
| // 5. Width of vector arguments and return types for functions called by |
| // this function. |
| assert(!MissingFeatures::minLegalVectorWidthAttr() && "NYI"); |
| |
| // Add vscale_range attribute if appropriate. |
| assert(!MissingFeatures::vscaleRangeAttr() && "NYI"); |
| |
| // In traditional LLVM codegen, if clang generated an unreachable return |
| // block, it'd be deleted now. Same for unused ret allocas from ReturnValue |
| } |
| |
| mlir::cir::FuncOp |
| CIRGenFunction::generateCode(clang::GlobalDecl GD, mlir::cir::FuncOp Fn, |
| const CIRGenFunctionInfo &FnInfo) { |
| assert(Fn && "generating code for a null function"); |
| const auto FD = cast<FunctionDecl>(GD.getDecl()); |
| CurGD = GD; |
| |
| FnRetQualTy = FD->getReturnType(); |
| if (!FnRetQualTy->isVoidType()) |
| FnRetCIRTy = getCIRType(FnRetQualTy); |
| |
| FunctionArgList Args; |
| QualType ResTy = buildFunctionArgList(GD, Args); |
| |
| if (FD->isInlineBuiltinDeclaration()) { |
| llvm_unreachable("NYI"); |
| } else { |
| // Detect the unusual situation where an inline version is shadowed by a |
| // non-inline version. In that case we should pick the external one |
| // everywhere. That's GCC behavior too. Unfortunately, I cannot find a way |
| // to detect that situation before we reach codegen, so do some late |
| // replacement. |
| for (const auto *PD = FD->getPreviousDecl(); PD; |
| PD = PD->getPreviousDecl()) { |
| if (LLVM_UNLIKELY(PD->isInlineBuiltinDeclaration())) { |
| llvm_unreachable("NYI"); |
| } |
| } |
| } |
| |
| // Check if we should generate debug info for this function. |
| if (FD->hasAttr<NoDebugAttr>()) { |
| assert(!MissingFeatures::noDebugInfo()); |
| } |
| |
| // The function might not have a body if we're generating thunks for a |
| // function declaration. |
| SourceRange BodyRange; |
| if (Stmt *Body = FD->getBody()) |
| BodyRange = Body->getSourceRange(); |
| else |
| BodyRange = FD->getLocation(); |
| // TODO: CurEHLocation |
| |
| // Use the location of the start of the function to determine where the |
| // function definition is located. By default we use the location of the |
| // declaration as the location for the subprogram. A function may lack a |
| // declaration in the source code if it is created by code gen. (examples: |
| // _GLOBAL__I_a, __cxx_global_array_dtor, thunk). |
| SourceLocation Loc = FD->getLocation(); |
| |
| // If this is a function specialization then use the pattern body as the |
| // location for the function. |
| if (const auto *SpecDecl = FD->getTemplateInstantiationPattern()) |
| if (SpecDecl->hasBody(SpecDecl)) |
| Loc = SpecDecl->getLocation(); |
| |
| Stmt *Body = FD->getBody(); |
| |
| if (Body) { |
| // LLVM codegen: Coroutines always emit lifetime markers |
| // Hide this under request for lifetime emission so that we can write |
| // tests when the time comes, but CIR should be intrinsically scope |
| // accurate, so no need to tie coroutines to such markers. |
| if (isa<CoroutineBodyStmt>(Body)) |
| assert(!MissingFeatures::shouldEmitLifetimeMarkers() && "NYI"); |
| |
| // Initialize helper which will detect jumps which can cause invalid |
| // lifetime markers. |
| if (ShouldEmitLifetimeMarkers) |
| assert(!MissingFeatures::shouldEmitLifetimeMarkers() && "NYI"); |
| } |
| |
| // Create a scope in the symbol table to hold variable declarations. |
| SymTableScopeTy varScope(symbolTable); |
| // Compiler synthetized functions might have invalid slocs... |
| auto bSrcLoc = FD->getBody()->getBeginLoc(); |
| auto eSrcLoc = FD->getBody()->getEndLoc(); |
| auto unknownLoc = builder.getUnknownLoc(); |
| |
| auto FnBeginLoc = bSrcLoc.isValid() ? getLoc(bSrcLoc) : unknownLoc; |
| auto FnEndLoc = eSrcLoc.isValid() ? getLoc(eSrcLoc) : unknownLoc; |
| const auto fusedLoc = |
| mlir::FusedLoc::get(builder.getContext(), {FnBeginLoc, FnEndLoc}); |
| SourceLocRAIIObject fnLoc{*this, Loc.isValid() ? getLoc(Loc) : unknownLoc}; |
| |
| assert(Fn.isDeclaration() && "Function already has body?"); |
| mlir::Block *EntryBB = Fn.addEntryBlock(); |
| builder.setInsertionPointToStart(EntryBB); |
| |
| { |
| // Initialize lexical scope information. |
| LexicalScope lexScope{*this, fusedLoc, EntryBB}; |
| |
| // Emit the standard function prologue. |
| StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin()); |
| |
| // Save parameters for coroutine function. |
| if (Body && isa_and_nonnull<CoroutineBodyStmt>(Body)) |
| llvm::append_range(FnArgs, FD->parameters()); |
| |
| // Generate the body of the function. |
| // TODO: PGO.assignRegionCounters |
| if (isa<CXXDestructorDecl>(FD)) |
| buildDestructorBody(Args); |
| else if (isa<CXXConstructorDecl>(FD)) |
| buildConstructorBody(Args); |
| else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && |
| FD->hasAttr<CUDAGlobalAttr>()) |
| llvm_unreachable("NYI"); |
| else if (isa<CXXMethodDecl>(FD) && |
| cast<CXXMethodDecl>(FD)->isLambdaStaticInvoker()) { |
| // The lambda static invoker function is special, because it forwards or |
| // clones the body of the function call operator (but is actually |
| // static). |
| buildLambdaStaticInvokeBody(cast<CXXMethodDecl>(FD)); |
| } else if (FD->isDefaulted() && isa<CXXMethodDecl>(FD) && |
| (cast<CXXMethodDecl>(FD)->isCopyAssignmentOperator() || |
| cast<CXXMethodDecl>(FD)->isMoveAssignmentOperator())) { |
| // Implicit copy-assignment gets the same special treatment as implicit |
| // copy-constructors. |
| buildImplicitAssignmentOperatorBody(Args); |
| } else if (Body) { |
| if (mlir::failed(buildFunctionBody(Body))) { |
| Fn.erase(); |
| return nullptr; |
| } |
| } else |
| llvm_unreachable("no definition for emitted function"); |
| |
| assert(builder.getInsertionBlock() && "Should be valid"); |
| } |
| |
| if (mlir::failed(Fn.verifyBody())) |
| return nullptr; |
| |
| // Emit the standard function epilogue. |
| finishFunction(BodyRange.getEnd()); |
| |
| // If we haven't marked the function nothrow through other means, do a quick |
| // pass now to see if we can. |
| assert(!MissingFeatures::tryMarkNoThrow()); |
| |
| return Fn; |
| } |
| |
| mlir::Value CIRGenFunction::createLoad(const VarDecl *VD, const char *Name) { |
| auto addr = GetAddrOfLocalVar(VD); |
| return builder.create<LoadOp>(getLoc(VD->getLocation()), |
| addr.getElementType(), addr.getPointer()); |
| } |
| |
| static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) { |
| auto *CD = llvm::dyn_cast<CXXConstructorDecl>(D); |
| if (!(CD && CD->isCopyOrMoveConstructor()) && |
| !D->isCopyAssignmentOperator() && !D->isMoveAssignmentOperator()) |
| return false; |
| |
| // We can emit a memcpy for a trivial copy or move constructor/assignment |
| if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding()) |
| return true; |
| |
| if (D->getParent()->isUnion() && D->isDefaulted()) |
| return true; |
| |
| return false; |
| } |
| |
| void CIRGenFunction::buildCXXConstructorCall(const clang::CXXConstructorDecl *D, |
| clang::CXXCtorType Type, |
| bool ForVirtualBase, |
| bool Delegating, |
| AggValueSlot ThisAVS, |
| const clang::CXXConstructExpr *E) { |
| CallArgList Args; |
| Address This = ThisAVS.getAddress(); |
| LangAS SlotAS = ThisAVS.getQualifiers().getAddressSpace(); |
| QualType ThisType = D->getThisType(); |
| LangAS ThisAS = ThisType.getTypePtr()->getPointeeType().getAddressSpace(); |
| mlir::Value ThisPtr = This.getPointer(); |
| |
| assert(SlotAS == ThisAS && "This edge case NYI"); |
| |
| Args.add(RValue::get(ThisPtr), D->getThisType()); |
| |
| // In LLVM Codegen: If this is a trivial constructor, just emit what's needed. |
| // If this is a union copy constructor, we must emit a memcpy, because the AST |
| // does not model that copy. |
| if (isMemcpyEquivalentSpecialMember(D)) { |
| assert(!MissingFeatures::isMemcpyEquivalentSpecialMember()); |
| } |
| |
| const FunctionProtoType *FPT = D->getType()->castAs<FunctionProtoType>(); |
| EvaluationOrder Order = E->isListInitialization() |
| ? EvaluationOrder::ForceLeftToRight |
| : EvaluationOrder::Default; |
| |
| buildCallArgs(Args, FPT, E->arguments(), E->getConstructor(), |
| /*ParamsToSkip*/ 0, Order); |
| |
| buildCXXConstructorCall(D, Type, ForVirtualBase, Delegating, This, Args, |
| ThisAVS.mayOverlap(), E->getExprLoc(), |
| ThisAVS.isSanitizerChecked()); |
| } |
| |
| void CIRGenFunction::buildCXXConstructorCall( |
| const CXXConstructorDecl *D, CXXCtorType Type, bool ForVirtualBase, |
| bool Delegating, Address This, CallArgList &Args, |
| AggValueSlot::Overlap_t Overlap, SourceLocation Loc, |
| bool NewPointerIsChecked) { |
| |
| const auto *ClassDecl = D->getParent(); |
| |
| if (!NewPointerIsChecked) |
| buildTypeCheck(CIRGenFunction::TCK_ConstructorCall, Loc, This.getPointer(), |
| getContext().getRecordType(ClassDecl), CharUnits::Zero()); |
| |
| // If this is a call to a trivial default constructor: |
| // In LLVM: do nothing. |
| // In CIR: emit as a regular call, other later passes should lower the |
| // ctor call into trivial initialization. |
| assert(!MissingFeatures::isTrivialAndisDefaultConstructor()); |
| |
| if (isMemcpyEquivalentSpecialMember(D)) { |
| assert(!MissingFeatures::isMemcpyEquivalentSpecialMember()); |
| } |
| |
| bool PassPrototypeArgs = true; |
| |
| assert(!D->getInheritedConstructor() && "inheritance NYI"); |
| |
| // Insert any ABI-specific implicit constructor arguments. |
| CIRGenCXXABI::AddedStructorArgCounts ExtraArgs = |
| CGM.getCXXABI().addImplicitConstructorArgs(*this, D, Type, ForVirtualBase, |
| Delegating, Args); |
| |
| // Emit the call. |
| auto CalleePtr = CGM.getAddrOfCXXStructor(GlobalDecl(D, Type)); |
| const CIRGenFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall( |
| Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix, PassPrototypeArgs); |
| CIRGenCallee Callee = CIRGenCallee::forDirect(CalleePtr, GlobalDecl(D, Type)); |
| mlir::cir::CIRCallOpInterface C; |
| buildCall(Info, Callee, ReturnValueSlot(), Args, &C, false, getLoc(Loc)); |
| |
| assert(CGM.getCodeGenOpts().OptimizationLevel == 0 || |
| ClassDecl->isDynamicClass() || Type == Ctor_Base || |
| !CGM.getCodeGenOpts().StrictVTablePointers && |
| "vtable assumption loads NYI"); |
| } |
| |
| void CIRGenFunction::buildConstructorBody(FunctionArgList &Args) { |
| // TODO: EmitAsanPrologueOrEpilogue(true); |
| const auto *Ctor = cast<CXXConstructorDecl>(CurGD.getDecl()); |
| auto CtorType = CurGD.getCtorType(); |
| |
| assert((CGM.getTarget().getCXXABI().hasConstructorVariants() || |
| CtorType == Ctor_Complete) && |
| "can only generate complete ctor for this ABI"); |
| |
| // Before we go any further, try the complete->base constructor delegation |
| // optimization. |
| if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) && |
| CGM.getTarget().getCXXABI().hasConstructorVariants()) { |
| buildDelegateCXXConstructorCall(Ctor, Ctor_Base, Args, Ctor->getEndLoc()); |
| return; |
| } |
| |
| const FunctionDecl *Definition = nullptr; |
| Stmt *Body = Ctor->getBody(Definition); |
| assert(Definition == Ctor && "emitting wrong constructor body"); |
| |
| // Enter the function-try-block before the constructor prologue if |
| // applicable. |
| bool IsTryBody = (Body && isa<CXXTryStmt>(Body)); |
| if (IsTryBody) |
| llvm_unreachable("NYI"); |
| |
| // TODO: incrementProfileCounter |
| |
| // TODO: RunClenaupCcope RunCleanups(*this); |
| |
| // TODO: in restricted cases, we can emit the vbase initializers of a |
| // complete ctor and then delegate to the base ctor. |
| |
| // Emit the constructor prologue, i.e. the base and member initializers. |
| buildCtorPrologue(Ctor, CtorType, Args); |
| |
| // Emit the body of the statement. |
| if (IsTryBody) |
| llvm_unreachable("NYI"); |
| else { |
| // TODO: propagate this result via mlir::logical result. Just unreachable |
| // now just to have it handled. |
| if (mlir::failed(buildStmt(Body, true))) |
| llvm_unreachable("NYI"); |
| } |
| |
| // Emit any cleanup blocks associated with the member or base initializers, |
| // which inlcudes (along the exceptional path) the destructors for those |
| // members and bases that were fully constructed. |
| /// TODO: RunCleanups.ForceCleanup(); |
| |
| if (IsTryBody) |
| llvm_unreachable("NYI"); |
| } |
| |
| /// 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 V, |
| QualType T) { |
| // FIXME(cir): is it safe to assume Op->getResult(0) is valid? Perhaps |
| // assert on the result type first. |
| LValueBaseInfo BaseInfo; |
| CharUnits Align = CGM.getNaturalTypeAlignment(T, &BaseInfo, |
| /* for PointeeType= */ true); |
| return makeAddrLValue(Address(V, Align), T, BaseInfo); |
| } |
| |
| LValue CIRGenFunction::MakeNaturalAlignAddrLValue(mlir::Value V, QualType T) { |
| LValueBaseInfo BaseInfo; |
| assert(!MissingFeatures::tbaa()); |
| CharUnits Alignment = CGM.getNaturalTypeAlignment(T, &BaseInfo); |
| Address Addr(V, getTypes().convertTypeForMem(T), Alignment); |
| return LValue::makeAddr(Addr, T, getContext(), BaseInfo); |
| } |
| |
| // Map the LangOption for exception behavior into the corresponding enum in |
| // the IR. |
| cir::fp::ExceptionBehavior |
| ToConstrainedExceptMD(LangOptions::FPExceptionModeKind Kind) { |
| switch (Kind) { |
| case LangOptions::FPE_Ignore: |
| return cir::fp::ebIgnore; |
| case LangOptions::FPE_MayTrap: |
| return cir::fp::ebMayTrap; |
| case LangOptions::FPE_Strict: |
| return cir::fp::ebStrict; |
| default: |
| llvm_unreachable("Unsupported FP Exception Behavior"); |
| } |
| } |
| |
| bool CIRGenFunction::ShouldSkipSanitizerInstrumentation() { |
| if (!CurFuncDecl) |
| return false; |
| return CurFuncDecl->hasAttr<DisableSanitizerInstrumentationAttr>(); |
| } |
| |
| /// Return true if the current function should be instrumented with XRay nop |
| /// sleds. |
| bool CIRGenFunction::ShouldXRayInstrumentFunction() const { |
| return CGM.getCodeGenOpts().XRayInstrumentFunctions; |
| } |
| |
| static bool matchesStlAllocatorFn(const Decl *D, const ASTContext &Ctx) { |
| auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); |
| if (!MD || !MD->getDeclName().getAsIdentifierInfo() || |
| !MD->getDeclName().getAsIdentifierInfo()->isStr("allocate") || |
| (MD->getNumParams() != 1 && MD->getNumParams() != 2)) |
| return false; |
| |
| if (MD->parameters()[0]->getType().getCanonicalType() != Ctx.getSizeType()) |
| return false; |
| |
| if (MD->getNumParams() == 2) { |
| auto *PT = MD->parameters()[1]->getType()->getAs<clang::PointerType>(); |
| if (!PT || !PT->isVoidPointerType() || |
| !PT->getPointeeType().isConstQualified()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, |
| mlir::cir::FuncOp Fn, |
| const CIRGenFunctionInfo &FnInfo, |
| const FunctionArgList &Args, |
| SourceLocation Loc, |
| SourceLocation StartLoc) { |
| assert(!CurFn && |
| "Do not use a CIRGenFunction object for more than one function"); |
| |
| const auto *D = GD.getDecl(); |
| |
| DidCallStackSave = false; |
| CurCodeDecl = D; |
| const auto *FD = dyn_cast_or_null<FunctionDecl>(D); |
| if (FD && FD->usesSEHTry()) |
| CurSEHParent = GD; |
| CurFuncDecl = (D ? D->getNonClosureContext() : nullptr); |
| FnRetTy = RetTy; |
| CurFn = Fn; |
| CurFnInfo = &FnInfo; |
| |
| // If this function is ignored for any of the enabled sanitizers, disable |
| // the sanitizer for the function. |
| do { |
| #define SANITIZER(NAME, ID) \ |
| if (SanOpts.empty()) \ |
| break; \ |
| if (SanOpts.has(SanitizerKind::ID)) \ |
| if (CGM.isInNoSanitizeList(SanitizerKind::ID, Fn, Loc)) \ |
| SanOpts.set(SanitizerKind::ID, false); |
| |
| #include "clang/Basic/Sanitizers.def" |
| #undef SANITIZER |
| } while (0); |
| |
| if (D) { |
| const bool SanitizeBounds = SanOpts.hasOneOf(SanitizerKind::Bounds); |
| SanitizerMask no_sanitize_mask; |
| bool NoSanitizeCoverage = false; |
| |
| for (auto *Attr : D->specific_attrs<NoSanitizeAttr>()) { |
| no_sanitize_mask |= Attr->getMask(); |
| // SanitizeCoverage is not handled by SanOpts. |
| if (Attr->hasCoverage()) |
| NoSanitizeCoverage = true; |
| } |
| |
| // Apply the no_sanitize* attributes to SanOpts. |
| SanOpts.Mask &= ~no_sanitize_mask; |
| if (no_sanitize_mask & SanitizerKind::Address) |
| SanOpts.set(SanitizerKind::KernelAddress, false); |
| if (no_sanitize_mask & SanitizerKind::KernelAddress) |
| SanOpts.set(SanitizerKind::Address, false); |
| if (no_sanitize_mask & SanitizerKind::HWAddress) |
| SanOpts.set(SanitizerKind::KernelHWAddress, false); |
| if (no_sanitize_mask & SanitizerKind::KernelHWAddress) |
| SanOpts.set(SanitizerKind::HWAddress, false); |
| |
| // TODO(cir): set llvm::Attribute::NoSanitizeBounds |
| if (SanitizeBounds && !SanOpts.hasOneOf(SanitizerKind::Bounds)) |
| assert(!MissingFeatures::sanitizeOther()); |
| |
| // TODO(cir): set llvm::Attribute::NoSanitizeCoverage |
| if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage()) |
| assert(!MissingFeatures::sanitizeOther()); |
| |
| // Some passes need the non-negated no_sanitize attribute. Pass them on. |
| if (CGM.getCodeGenOpts().hasSanitizeBinaryMetadata()) { |
| // TODO(cir): set no_sanitize_thread |
| if (no_sanitize_mask & SanitizerKind::Thread) |
| assert(!MissingFeatures::sanitizeOther()); |
| } |
| } |
| |
| if (ShouldSkipSanitizerInstrumentation()) { |
| assert(!MissingFeatures::sanitizeOther()); |
| } else { |
| // Apply sanitizer attributes to the function. |
| if (SanOpts.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.hasOneOf(SanitizerKind::HWAddress | |
| SanitizerKind::KernelHWAddress)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.has(SanitizerKind::MemtagStack)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.has(SanitizerKind::Thread)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.has(SanitizerKind::NumericalStability)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.hasOneOf(SanitizerKind::Memory | SanitizerKind::KernelMemory)) |
| assert(!MissingFeatures::sanitizeOther()); |
| } |
| if (SanOpts.has(SanitizerKind::SafeStack)) |
| assert(!MissingFeatures::sanitizeOther()); |
| if (SanOpts.has(SanitizerKind::ShadowCallStack)) |
| assert(!MissingFeatures::sanitizeOther()); |
| |
| // Apply fuzzing attribute to the function. |
| if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink)) |
| assert(!MissingFeatures::sanitizeOther()); |
| |
| // Ignore TSan memory acesses from within ObjC/ObjC++ dealloc, initialize, |
| // .cxx_destruct, __destroy_helper_block_ and all of their calees at run time. |
| if (SanOpts.has(SanitizerKind::Thread)) { |
| if (const auto *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) { |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| // Ignore unrelated casts in STL allocate() since the allocator must cast |
| // from void* to T* before object initialization completes. Don't match on the |
| // namespace because not all allocators are in std:: |
| if (D && SanOpts.has(SanitizerKind::CFIUnrelatedCast)) { |
| if (matchesStlAllocatorFn(D, getContext())) |
| SanOpts.Mask &= ~SanitizerKind::CFIUnrelatedCast; |
| } |
| |
| // Ignore null checks in coroutine functions since the coroutines passes |
| // are not aware of how to move the extra UBSan instructions across the split |
| // coroutine boundaries. |
| if (D && SanOpts.has(SanitizerKind::Null)) |
| if (FD && FD->getBody() && |
| FD->getBody()->getStmtClass() == Stmt::CoroutineBodyStmtClass) |
| SanOpts.Mask &= ~SanitizerKind::Null; |
| |
| // Apply xray attributes to the function (as a string, for now) |
| if (const auto *XRayAttr = D ? D->getAttr<XRayInstrumentAttr>() : nullptr) { |
| assert(!MissingFeatures::xray()); |
| } else { |
| assert(!MissingFeatures::xray()); |
| } |
| |
| if (ShouldXRayInstrumentFunction()) { |
| assert(!MissingFeatures::xray()); |
| } |
| |
| if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) { |
| assert(!MissingFeatures::getProfileCount()); |
| } |
| |
| unsigned Count, Offset; |
| if (const auto *Attr = |
| D ? D->getAttr<PatchableFunctionEntryAttr>() : nullptr) { |
| llvm_unreachable("NYI"); |
| } else { |
| Count = CGM.getCodeGenOpts().PatchableFunctionEntryCount; |
| Offset = CGM.getCodeGenOpts().PatchableFunctionEntryOffset; |
| } |
| if (Count && Offset <= Count) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // Add no-jump-tables value. |
| if (CGM.getCodeGenOpts().NoUseJumpTables) |
| llvm_unreachable("NYI"); |
| |
| // Add no-inline-line-tables value. |
| if (CGM.getCodeGenOpts().NoInlineLineTables) |
| llvm_unreachable("NYI"); |
| |
| // Add profile-sample-accurate value. |
| if (CGM.getCodeGenOpts().ProfileSampleAccurate) |
| llvm_unreachable("NYI"); |
| |
| if (!CGM.getCodeGenOpts().SampleProfileFile.empty()) |
| llvm_unreachable("NYI"); |
| |
| if (D && D->hasAttr<CFICanonicalJumpTableAttr>()) |
| llvm_unreachable("NYI"); |
| |
| if (D && D->hasAttr<NoProfileFunctionAttr>()) |
| llvm_unreachable("NYI"); |
| |
| if (FD && getLangOpts().OpenCL) { |
| buildKernelMetadata(FD, Fn); |
| } |
| |
| // If we are checking function types, emit a function type signature as |
| // prologue data. |
| if (FD && getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function)) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // If we're checking nullability, we need to know whether we can check the |
| // return value. Initialize the falg to 'true' and refine it in |
| // buildParmDecl. |
| if (SanOpts.has(SanitizerKind::NullabilityReturn)) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // If we're in C++ mode and the function name is "main", it is guaranteed to |
| // be norecurse by the standard (3.6.1.3 "The function main shall not be |
| // used within a program"). |
| // |
| // OpenCL C 2.0 v2.2-11 s6.9.i: |
| // Recursion is not supported. |
| // |
| // SYCL v1.2.1 s3.10: |
| // kernels cannot include RTTI information, exception cases, recursive |
| // code, virtual functions or make use of C++ libraries that are not |
| // compiled for the device. |
| if (FD && |
| ((getLangOpts().CPlusPlus && FD->isMain()) || getLangOpts().OpenCL || |
| getLangOpts().SYCLIsDevice | |
| (getLangOpts().CUDA && FD->hasAttr<CUDAGlobalAttr>()))) |
| ; // TODO: support norecurse attr |
| |
| llvm::RoundingMode RM = getLangOpts().getDefaultRoundingMode(); |
| cir::fp::ExceptionBehavior FPExceptionBehavior = |
| ToConstrainedExceptMD(getLangOpts().getDefaultExceptionMode()); |
| builder.setDefaultConstrainedRounding(RM); |
| builder.setDefaultConstrainedExcept(FPExceptionBehavior); |
| if ((FD && (FD->UsesFPIntrin() || FD->hasAttr<StrictFPAttr>())) || |
| (!FD && (FPExceptionBehavior != cir::fp::ebIgnore || |
| RM != llvm::RoundingMode::NearestTiesToEven))) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // TODO: stackrealign attr |
| |
| mlir::Block *EntryBB = &Fn.getBlocks().front(); |
| |
| // TODO: allocapt insertion? probably don't need for CIR |
| |
| // TODO: return value checking |
| |
| if (getDebugInfo()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| if (ShouldInstrumentFunction()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // Since emitting the mcount call here impacts optimizations such as |
| // function inlining, we just add an attribute to insert a mcount call in |
| // backend. The attribute "counting-function" is set to mcount function name |
| // which is architecture dependent. |
| if (CGM.getCodeGenOpts().InstrumentForProfiling) { |
| llvm_unreachable("NYI"); |
| } |
| |
| if (CGM.getCodeGenOpts().PackedStack) { |
| llvm_unreachable("NYI"); |
| } |
| |
| if (CGM.getCodeGenOpts().WarnStackSize != UINT_MAX) { |
| llvm_unreachable("NYI"); |
| } |
| |
| assert(!MissingFeatures::emitStartEHSpec() && "NYI"); |
| // FIXME(cir): vla.c test currently crashes here. |
| // PrologueCleanupDepth = EHStack.stable_begin(); |
| |
| if (getLangOpts().OpenMP && CurCodeDecl) |
| CGM.getOpenMPRuntime().emitFunctionProlog(*this, CurCodeDecl); |
| |
| // TODO: buildFunctionProlog |
| |
| { |
| // Set the insertion point in the builder to the beginning of the |
| // function body, it will be used throughout the codegen to create |
| // operations in this function. |
| builder.setInsertionPointToStart(EntryBB); |
| |
| // TODO: this should live in `buildFunctionProlog |
| // Declare all the function arguments in the symbol table. |
| for (const auto nameValue : llvm::zip(Args, EntryBB->getArguments())) { |
| auto *paramVar = std::get<0>(nameValue); |
| auto paramVal = std::get<1>(nameValue); |
| auto alignment = getContext().getDeclAlign(paramVar); |
| auto paramLoc = getLoc(paramVar->getSourceRange()); |
| paramVal.setLoc(paramLoc); |
| |
| mlir::Value addr; |
| if (failed(declare(paramVar, paramVar->getType(), paramLoc, alignment, |
| addr, true /*param*/))) |
| return; |
| |
| auto address = Address(addr, alignment); |
| setAddrOfLocalVar(paramVar, address); |
| |
| // Location of the store to the param storage tracked as beginning of |
| // the function body. |
| auto fnBodyBegin = getLoc(FD->getBody()->getBeginLoc()); |
| builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addr); |
| } |
| assert(builder.getInsertionBlock() && "Should be valid"); |
| |
| auto FnEndLoc = getLoc(FD->getBody()->getEndLoc()); |
| |
| // When the current function is not void, create an address to store the |
| // result value. |
| if (FnRetCIRTy.has_value()) |
| buildAndUpdateRetAlloca(FnRetQualTy, FnEndLoc, |
| CGM.getNaturalTypeAlignment(FnRetQualTy)); |
| } |
| |
| if (D && isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance()) { |
| CGM.getCXXABI().buildInstanceFunctionProlog(*this); |
| |
| const auto *MD = cast<CXXMethodDecl>(D); |
| if (MD->getParent()->isLambda() && MD->getOverloadedOperator() == OO_Call) { |
| // We're in a lambda. |
| auto Fn = dyn_cast<mlir::cir::FuncOp>(CurFn); |
| assert(Fn && "other callables NYI"); |
| Fn.setLambdaAttr(mlir::UnitAttr::get(builder.getContext())); |
| |
| // Figure out the captures. |
| MD->getParent()->getCaptureFields(LambdaCaptureFields, |
| LambdaThisCaptureField); |
| if (LambdaThisCaptureField) { |
| llvm_unreachable("NYI"); |
| } |
| for (auto *FD : MD->getParent()->fields()) { |
| if (FD->hasCapturedVLAType()) { |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| } else { |
| // Not in a lambda; just use 'this' from the method. |
| // FIXME: Should we generate a new load for each use of 'this'? The fast |
| // register allocator would be happier... |
| CXXThisValue = CXXABIThisValue; |
| } |
| |
| // Check the 'this' pointer once per function, if it's available |
| if (CXXABIThisValue) { |
| SanitizerSet SkippedChecks; |
| SkippedChecks.set(SanitizerKind::ObjectSize, true); |
| QualType ThisTy = MD->getThisType(); |
| (void)ThisTy; |
| |
| // If this is the call operator of a lambda with no capture-default, it |
| // may have a staic invoker function, which may call this operator with |
| // a null 'this' pointer. |
| if (isLambdaCallOperator(MD) && |
| MD->getParent()->getLambdaCaptureDefault() == LCD_None) |
| SkippedChecks.set(SanitizerKind::Null, true); |
| |
| assert(!MissingFeatures::buildTypeCheck() && "NYI"); |
| } |
| } |
| |
| // If any of the arguments have a variably modified type, make sure to emit |
| // the type size. |
| for (FunctionArgList::const_iterator i = Args.begin(), e = Args.end(); i != e; |
| ++i) { |
| const VarDecl *VD = *i; |
| |
| // Dig out the type as written from ParmVarDecls; it's unclear whether the |
| // standard (C99 6.9.1p10) requires this, but we're following the |
| // precedent set by gcc. |
| QualType Ty; |
| if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) |
| Ty = PVD->getOriginalType(); |
| else |
| Ty = VD->getType(); |
| |
| if (Ty->isVariablyModifiedType()) |
| buildVariablyModifiedType(Ty); |
| } |
| // Emit a location at the end of the prologue. |
| if (getDebugInfo()) |
| llvm_unreachable("NYI"); |
| |
| // TODO: Do we need to handle this in two places like we do with |
| // target-features/target-cpu? |
| if (CurFuncDecl) |
| if (const auto *VecWidth = CurFuncDecl->getAttr<MinVectorWidthAttr>()) |
| llvm_unreachable("NYI"); |
| } |
| |
| /// Return true if the current function should be instrumented with |
| /// __cyg_profile_func_* calls |
| bool CIRGenFunction::ShouldInstrumentFunction() { |
| if (!CGM.getCodeGenOpts().InstrumentFunctions && |
| !CGM.getCodeGenOpts().InstrumentFunctionsAfterInlining && |
| !CGM.getCodeGenOpts().InstrumentFunctionEntryBare) |
| return false; |
| |
| llvm_unreachable("NYI"); |
| } |
| |
| mlir::LogicalResult CIRGenFunction::buildFunctionBody(const clang::Stmt *Body) { |
| // TODO: incrementProfileCounter(Body); |
| |
| // We start with function level scope for variables. |
| SymTableScopeTy varScope(symbolTable); |
| |
| auto result = mlir::LogicalResult::success(); |
| if (const CompoundStmt *S = dyn_cast<CompoundStmt>(Body)) |
| buildCompoundStmtWithoutScope(*S); |
| else |
| result = buildStmt(Body, /*useCurrentScope*/ true); |
| |
| // This is checked after emitting the function body so we know if there are |
| // any permitted infinite loops. |
| // TODO: if (checkIfFunctionMustProgress()) |
| // CurFn->addFnAttr(llvm::Attribute::MustProgress); |
| return result; |
| } |
| |
| clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl GD, |
| FunctionArgList &Args) { |
| const auto *FD = cast<FunctionDecl>(GD.getDecl()); |
| QualType ResTy = FD->getReturnType(); |
| |
| const auto *MD = dyn_cast<CXXMethodDecl>(FD); |
| if (MD && MD->isInstance()) { |
| if (CGM.getCXXABI().HasThisReturn(GD)) |
| llvm_unreachable("NYI"); |
| else if (CGM.getCXXABI().hasMostDerivedReturn(GD)) |
| llvm_unreachable("NYI"); |
| CGM.getCXXABI().buildThisParam(*this, Args); |
| } |
| |
| // The base version of an inheriting constructor whose constructed base is a |
| // virtual base is not passed any arguments (because it doesn't actually |
| // call the inherited constructor). |
| bool PassedParams = true; |
| if (const auto *CD = dyn_cast<CXXConstructorDecl>(FD)) |
| if (auto Inherited = CD->getInheritedConstructor()) |
| PassedParams = |
| getTypes().inheritingCtorHasParams(Inherited, GD.getCtorType()); |
| |
| if (PassedParams) { |
| for (auto *Param : FD->parameters()) { |
| Args.push_back(Param); |
| if (!Param->hasAttr<PassObjectSizeAttr>()) |
| continue; |
| |
| auto *Implicit = ImplicitParamDecl::Create( |
| getContext(), Param->getDeclContext(), Param->getLocation(), |
| /*Id=*/nullptr, getContext().getSizeType(), ImplicitParamKind::Other); |
| SizeArguments[Param] = Implicit; |
| Args.push_back(Implicit); |
| } |
| } |
| |
| if (MD && (isa<CXXConstructorDecl>(MD) || isa<CXXDestructorDecl>(MD))) |
| CGM.getCXXABI().addImplicitStructorParams(*this, ResTy, Args); |
| |
| return ResTy; |
| } |
| |
| static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) { |
| SmallString<256> Buffer; |
| llvm::raw_svector_ostream Out(Buffer); |
| Out << name << cnt; |
| return std::string(Out.str()); |
| } |
| |
| std::string CIRGenFunction::getCounterAggTmpAsString() { |
| return getVersionedTmpName("agg.tmp", CounterAggTmp++); |
| } |
| |
| std::string CIRGenFunction::getCounterRefTmpAsString() { |
| return getVersionedTmpName("ref.tmp", CounterRefTmp++); |
| } |
| |
| void CIRGenFunction::buildNullInitialization(mlir::Location loc, |
| Address DestPtr, QualType Ty) { |
| // Ignore empty classes in C++. |
| if (getLangOpts().CPlusPlus) { |
| if (const RecordType *RT = Ty->getAs<RecordType>()) { |
| if (cast<CXXRecordDecl>(RT->getDecl())->isEmpty()) |
| return; |
| } |
| } |
| |
| // Cast the dest ptr to the appropriate i8 pointer type. |
| if (builder.isInt8Ty(DestPtr.getElementType())) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // Get size and alignment info for this aggregate. |
| CharUnits size = getContext().getTypeSizeInChars(Ty); |
| [[maybe_unused]] mlir::Attribute SizeVal{}; |
| [[maybe_unused]] const VariableArrayType *vla = nullptr; |
| |
| // Don't bother emitting a zero-byte memset. |
| if (size.isZero()) { |
| // But note that getTypeInfo returns 0 for a VLA. |
| if (const VariableArrayType *vlaType = dyn_cast_or_null<VariableArrayType>( |
| getContext().getAsArrayType(Ty))) { |
| llvm_unreachable("NYI"); |
| } else { |
| return; |
| } |
| } else { |
| SizeVal = CGM.getSize(size); |
| } |
| |
| // If the type contains a pointer to data member we can't memset it to zero. |
| // Instead, create a null constant and copy it to the destination. |
| // TODO: there are other patterns besides zero that we can usefully memset, |
| // like -1, which happens to be the pattern used by member-pointers. |
| if (!CGM.getTypes().isZeroInitializable(Ty)) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // In LLVM Codegen: otherwise, just memset the whole thing to zero using |
| // Builder.CreateMemSet. In CIR just emit a store of #cir.zero to the |
| // respective address. |
| // Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false); |
| builder.createStore(loc, builder.getZero(loc, getTypes().ConvertType(Ty)), |
| DestPtr); |
| } |
| |
| CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &CGF, |
| const clang::Expr *E) |
| : CGF(CGF) { |
| ConstructorHelper(E->getFPFeaturesInEffect(CGF.getLangOpts())); |
| } |
| |
| CIRGenFunction::CIRGenFPOptionsRAII::CIRGenFPOptionsRAII(CIRGenFunction &CGF, |
| FPOptions FPFeatures) |
| : CGF(CGF) { |
| ConstructorHelper(FPFeatures); |
| } |
| |
| void CIRGenFunction::CIRGenFPOptionsRAII::ConstructorHelper( |
| FPOptions FPFeatures) { |
| OldFPFeatures = CGF.CurFPFeatures; |
| CGF.CurFPFeatures = FPFeatures; |
| |
| OldExcept = CGF.builder.getDefaultConstrainedExcept(); |
| OldRounding = CGF.builder.getDefaultConstrainedRounding(); |
| |
| if (OldFPFeatures == FPFeatures) |
| return; |
| |
| // TODO(cir): create guard to restore fast math configurations. |
| assert(!MissingFeatures::fastMathGuard()); |
| |
| llvm::RoundingMode NewRoundingBehavior = FPFeatures.getRoundingMode(); |
| // TODO(cir): override rounding behaviour once FM configs are guarded. |
| auto NewExceptionBehavior = |
| ToConstrainedExceptMD(static_cast<LangOptions::FPExceptionModeKind>( |
| FPFeatures.getExceptionMode())); |
| // TODO(cir): override exception behaviour once FM configs are guarded. |
| |
| // TODO(cir): override FP flags once FM configs are guarded. |
| assert(!MissingFeatures::fastMathFlags()); |
| |
| assert((CGF.CurFuncDecl == nullptr || CGF.builder.getIsFPConstrained() || |
| isa<CXXConstructorDecl>(CGF.CurFuncDecl) || |
| isa<CXXDestructorDecl>(CGF.CurFuncDecl) || |
| (NewExceptionBehavior == fp::ebIgnore && |
| NewRoundingBehavior == llvm::RoundingMode::NearestTiesToEven)) && |
| "FPConstrained should be enabled on entire function"); |
| |
| // TODO(cir): mark CIR function with fast math attributes. |
| assert(!MissingFeatures::fastMathFuncAttributes()); |
| } |
| |
| CIRGenFunction::CIRGenFPOptionsRAII::~CIRGenFPOptionsRAII() { |
| CGF.CurFPFeatures = OldFPFeatures; |
| CGF.builder.setDefaultConstrainedExcept(OldExcept); |
| CGF.builder.setDefaultConstrainedRounding(OldRounding); |
| } |
| |
| // TODO(cir): should be shared with LLVM codegen. |
| bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *CE) { |
| const Expr *E = CE->getSubExpr(); |
| |
| if (CE->getCastKind() == CK_UncheckedDerivedToBase) |
| return false; |
| |
| if (isa<CXXThisExpr>(E->IgnoreParens())) { |
| // We always assume that 'this' is never null. |
| return false; |
| } |
| |
| if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(CE)) { |
| // And that glvalue casts are never null. |
| if (ICE->isGLValue()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void CIRGenFunction::buildDeclRefExprDbgValue(const DeclRefExpr *E, |
| const APValue &Init) { |
| assert(!MissingFeatures::generateDebugInfo()); |
| } |
| |
| Address CIRGenFunction::buildVAListRef(const Expr *E) { |
| if (getContext().getBuiltinVaListType()->isArrayType()) |
| return buildPointerWithAlignment(E); |
| return buildLValue(E).getAddress(); |
| } |
| |
| // Emits an error if we don't have a valid set of target features for the |
| // called function. |
| void CIRGenFunction::checkTargetFeatures(const CallExpr *E, |
| const FunctionDecl *TargetDecl) { |
| return checkTargetFeatures(E->getBeginLoc(), TargetDecl); |
| } |
| |
| // Emits an error if we don't have a valid set of target features for the |
| // called function. |
| void CIRGenFunction::checkTargetFeatures(SourceLocation Loc, |
| const FunctionDecl *TargetDecl) { |
| // Early exit if this is an indirect call. |
| if (!TargetDecl) |
| return; |
| |
| // Get the current enclosing function if it exists. If it doesn't |
| // we can't check the target features anyhow. |
| const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurCodeDecl); |
| if (!FD) |
| return; |
| |
| // Grab the required features for the call. For a builtin this is listed in |
| // the td file with the default cpu, for an always_inline function this is any |
| // listed cpu and any listed features. |
| unsigned BuiltinID = TargetDecl->getBuiltinID(); |
| std::string MissingFeature; |
| llvm::StringMap<bool> CallerFeatureMap; |
| CGM.getASTContext().getFunctionFeatureMap(CallerFeatureMap, FD); |
| if (BuiltinID) { |
| StringRef FeatureList( |
| getContext().BuiltinInfo.getRequiredFeatures(BuiltinID)); |
| if (!Builtin::evaluateRequiredTargetFeatures(FeatureList, |
| CallerFeatureMap)) { |
| CGM.getDiags().Report(Loc, diag::err_builtin_needs_feature) |
| << TargetDecl->getDeclName() << FeatureList; |
| } |
| } else if (!TargetDecl->isMultiVersion() && |
| TargetDecl->hasAttr<TargetAttr>()) { |
| // Get the required features for the callee. |
| |
| const TargetAttr *TD = TargetDecl->getAttr<TargetAttr>(); |
| ParsedTargetAttr ParsedAttr = getContext().filterFunctionTargetAttrs(TD); |
| |
| SmallVector<StringRef, 1> ReqFeatures; |
| llvm::StringMap<bool> CalleeFeatureMap; |
| getContext().getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); |
| |
| for (const auto &F : ParsedAttr.Features) { |
| if (F[0] == '+' && CalleeFeatureMap.lookup(F.substr(1))) |
| ReqFeatures.push_back(StringRef(F).substr(1)); |
| } |
| |
| for (const auto &F : CalleeFeatureMap) { |
| // Only positive features are "required". |
| if (F.getValue()) |
| ReqFeatures.push_back(F.getKey()); |
| } |
| if (!llvm::all_of(ReqFeatures, [&](StringRef Feature) { |
| if (!CallerFeatureMap.lookup(Feature)) { |
| MissingFeature = Feature.str(); |
| return false; |
| } |
| return true; |
| })) |
| CGM.getDiags().Report(Loc, diag::err_function_needs_feature) |
| << FD->getDeclName() << TargetDecl->getDeclName() << MissingFeature; |
| } else if (!FD->isMultiVersion() && FD->hasAttr<TargetAttr>()) { |
| llvm::StringMap<bool> CalleeFeatureMap; |
| getContext().getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); |
| |
| for (const auto &F : CalleeFeatureMap) { |
| if (F.getValue() && (!CallerFeatureMap.lookup(F.getKey()) || |
| !CallerFeatureMap.find(F.getKey())->getValue())) |
| CGM.getDiags().Report(Loc, diag::err_function_needs_feature) |
| << FD->getDeclName() << TargetDecl->getDeclName() << F.getKey(); |
| } |
| } |
| } |
| |
| CIRGenFunction::VlaSizePair CIRGenFunction::getVLASize(QualType type) { |
| const VariableArrayType *vla = |
| CGM.getASTContext().getAsVariableArrayType(type); |
| assert(vla && "type was not a variable array type!"); |
| return getVLASize(vla); |
| } |
| |
| CIRGenFunction::VlaSizePair |
| CIRGenFunction::getVLASize(const VariableArrayType *type) { |
| // The number of elements so far; always size_t. |
| mlir::Value numElements; |
| |
| QualType elementType; |
| do { |
| elementType = type->getElementType(); |
| mlir::Value vlaSize = VLASizeMap[type->getSizeExpr()]; |
| assert(vlaSize && "no size for VLA!"); |
| assert(vlaSize.getType() == SizeTy); |
| |
| if (!numElements) { |
| numElements = vlaSize; |
| } else { |
| // It's undefined behavior if this wraps around, so mark it that way. |
| // FIXME: Teach -fsanitize=undefined to trap this. |
| |
| numElements = builder.createMul(numElements, vlaSize); |
| } |
| } while ((type = getContext().getAsVariableArrayType(elementType))); |
| |
| assert(numElements && "Undefined elements number"); |
| return {numElements, elementType}; |
| } |
| |
| // TODO(cir): most part of this function can be shared between CIRGen |
| // and traditional LLVM codegen |
| void CIRGenFunction::buildVariablyModifiedType(QualType type) { |
| assert(type->isVariablyModifiedType() && |
| "Must pass variably modified type to EmitVLASizes!"); |
| |
| // We're going to walk down into the type and look for VLA |
| // expressions. |
| do { |
| assert(type->isVariablyModifiedType()); |
| |
| const Type *ty = type.getTypePtr(); |
| switch (ty->getTypeClass()) { |
| case clang::Type::CountAttributed: |
| case clang::Type::PackIndexing: |
| case clang::Type::ArrayParameter: |
| llvm_unreachable("NYI"); |
| |
| #define TYPE(Class, Base) |
| #define ABSTRACT_TYPE(Class, Base) |
| #define NON_CANONICAL_TYPE(Class, Base) |
| #define DEPENDENT_TYPE(Class, Base) case Type::Class: |
| #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) |
| #include "clang/AST/TypeNodes.inc" |
| llvm_unreachable("unexpected dependent type!"); |
| |
| // These types are never variably-modified. |
| case Type::Builtin: |
| case Type::Complex: |
| case Type::Vector: |
| case Type::ExtVector: |
| case Type::ConstantMatrix: |
| case Type::Record: |
| case Type::Enum: |
| case Type::Using: |
| case Type::TemplateSpecialization: |
| case Type::ObjCTypeParam: |
| case Type::ObjCObject: |
| case Type::ObjCInterface: |
| case Type::ObjCObjectPointer: |
| case Type::BitInt: |
| llvm_unreachable("type class is never variably-modified!"); |
| |
| case Type::Elaborated: |
| type = cast<clang::ElaboratedType>(ty)->getNamedType(); |
| break; |
| |
| case Type::Adjusted: |
| type = cast<clang::AdjustedType>(ty)->getAdjustedType(); |
| break; |
| |
| case Type::Decayed: |
| type = cast<clang::DecayedType>(ty)->getPointeeType(); |
| break; |
| |
| case Type::Pointer: |
| type = cast<clang::PointerType>(ty)->getPointeeType(); |
| break; |
| |
| case Type::BlockPointer: |
| type = cast<clang::BlockPointerType>(ty)->getPointeeType(); |
| break; |
| |
| case Type::LValueReference: |
| case Type::RValueReference: |
| type = cast<clang::ReferenceType>(ty)->getPointeeType(); |
| break; |
| |
| case Type::MemberPointer: |
| type = cast<clang::MemberPointerType>(ty)->getPointeeType(); |
| break; |
| |
| case Type::ConstantArray: |
| case Type::IncompleteArray: |
| // Losing element qualification here is fine. |
| type = cast<clang::ArrayType>(ty)->getElementType(); |
| break; |
| |
| case Type::VariableArray: { |
| // Losing element qualification here is fine. |
| const VariableArrayType *vat = cast<clang::VariableArrayType>(ty); |
| |
| // Unknown size indication requires no size computation. |
| // Otherwise, evaluate and record it. |
| if (const Expr *sizeExpr = vat->getSizeExpr()) { |
| // It's possible that we might have emitted this already, |
| // e.g. with a typedef and a pointer to it. |
| mlir::Value &entry = VLASizeMap[sizeExpr]; |
| if (!entry) { |
| mlir::Value size = buildScalarExpr(sizeExpr); |
| assert(!MissingFeatures::sanitizeVLABound()); |
| |
| // Always zexting here would be wrong if it weren't |
| // undefined behavior to have a negative bound. |
| // FIXME: What about when size's type is larger than size_t? |
| entry = builder.createIntCast(size, SizeTy); |
| } |
| } |
| type = vat->getElementType(); |
| break; |
| } |
| |
| case Type::FunctionProto: |
| case Type::FunctionNoProto: |
| type = cast<clang::FunctionType>(ty)->getReturnType(); |
| break; |
| |
| case Type::Paren: |
| case Type::TypeOf: |
| case Type::UnaryTransform: |
| case Type::Attributed: |
| case Type::BTFTagAttributed: |
| case Type::SubstTemplateTypeParm: |
| case Type::MacroQualified: |
| // Keep walking after single level desugaring. |
| type = type.getSingleStepDesugaredType(getContext()); |
| break; |
| |
| case Type::Typedef: |
| case Type::Decltype: |
| case Type::Auto: |
| case Type::DeducedTemplateSpecialization: |
| // Stop walking: nothing to do. |
| return; |
| |
| case Type::TypeOfExpr: |
| // Stop walking: emit typeof expression. |
| buildIgnoredExpr(cast<clang::TypeOfExprType>(ty)->getUnderlyingExpr()); |
| return; |
| |
| case Type::Atomic: |
| type = cast<clang::AtomicType>(ty)->getValueType(); |
| break; |
| |
| case Type::Pipe: |
| type = cast<clang::PipeType>(ty)->getElementType(); |
| break; |
| } |
| } while (type->isVariablyModifiedType()); |
| } |
| |
| /// Computes the length of an array in elements, as well as the base |
| /// element type and a properly-typed first element pointer. |
| mlir::Value |
| CIRGenFunction::buildArrayLength(const clang::ArrayType *origArrayType, |
| QualType &baseType, Address &addr) { |
| const auto *arrayType = origArrayType; |
| |
| // If it's a VLA, we have to load the stored size. Note that |
| // this is the size of the VLA in bytes, not its size in elements. |
| mlir::Value numVLAElements{}; |
| if (isa<VariableArrayType>(arrayType)) { |
| llvm_unreachable("NYI"); |
| } |
| |
| uint64_t countFromCLAs = 1; |
| QualType eltType; |
| |
| // llvm::ArrayType *llvmArrayType = |
| // dyn_cast<llvm::ArrayType>(addr.getElementType()); |
| auto cirArrayType = |
| mlir::dyn_cast<mlir::cir::ArrayType>(addr.getElementType()); |
| |
| while (cirArrayType) { |
| assert(isa<ConstantArrayType>(arrayType)); |
| countFromCLAs *= cirArrayType.getSize(); |
| eltType = arrayType->getElementType(); |
| |
| cirArrayType = |
| mlir::dyn_cast<mlir::cir::ArrayType>(cirArrayType.getEltType()); |
| |
| arrayType = getContext().getAsArrayType(arrayType->getElementType()); |
| assert((!cirArrayType || arrayType) && |
| "CIR and Clang types are out-of-synch"); |
| } |
| |
| if (arrayType) { |
| // From this point onwards, the Clang array type has been emitted |
| // as some other type (probably a packed struct). Compute the array |
| // size, and just emit the 'begin' expression as a bitcast. |
| llvm_unreachable("NYI"); |
| } |
| |
| baseType = eltType; |
| auto numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs); |
| |
| // If we had any VLA dimensions, factor them in. |
| if (numVLAElements) |
| llvm_unreachable("NYI"); |
| |
| return numElements; |
| } |