This patch adds typeid lowering, which uses a lot of the infrastructure from dynamic_cast. However, this adds a `get_type_info` operation that gets the type info out of a vtable pointer as well, which lets the offset be handled by the ABI specific lowering code.
1518 lines
55 KiB
C++
1518 lines
55 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CIRGenFunction.h"
|
|
|
|
#include "CIRGenCXXABI.h"
|
|
#include "CIRGenCall.h"
|
|
#include "CIRGenValue.h"
|
|
#include "mlir/IR/Location.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/GlobalDecl.h"
|
|
#include "clang/CIR/MissingFeatures.h"
|
|
#include "llvm/IR/FPEnv.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace clang::CIRGen {
|
|
|
|
CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder,
|
|
bool suppressNewContext)
|
|
: CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) {
|
|
ehStack.setCGF(this);
|
|
}
|
|
|
|
CIRGenFunction::~CIRGenFunction() {}
|
|
|
|
// This is copied from clang/lib/CodeGen/CodeGenFunction.cpp
|
|
cir::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::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:
|
|
case Type::OverflowBehavior:
|
|
case Type::HLSLAttributedResource:
|
|
case Type::HLSLInlineSpirv:
|
|
return cir::TEK_Scalar;
|
|
|
|
// Complexes.
|
|
case Type::Complex:
|
|
return cir::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:
|
|
case Type::ArrayParameter:
|
|
return cir::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 srcLoc) {
|
|
// Some AST nodes might contain invalid source locations (e.g.
|
|
// CXXDefaultArgExpr), workaround that to still get something out.
|
|
if (srcLoc.isValid()) {
|
|
const SourceManager &sm = getContext().getSourceManager();
|
|
PresumedLoc pLoc = sm.getPresumedLoc(srcLoc);
|
|
StringRef filename = pLoc.getFilename();
|
|
return mlir::FileLineColLoc::get(builder.getStringAttr(filename),
|
|
pLoc.getLine(), pLoc.getColumn());
|
|
}
|
|
// Do our best...
|
|
assert(currSrcLoc && "expected to inherit some source location");
|
|
return *currSrcLoc;
|
|
}
|
|
|
|
mlir::Location CIRGenFunction::getLoc(SourceRange srcLoc) {
|
|
// Some AST nodes might contain invalid source locations (e.g.
|
|
// CXXDefaultArgExpr), workaround that to still get something out.
|
|
if (srcLoc.isValid()) {
|
|
mlir::Location beg = getLoc(srcLoc.getBegin());
|
|
mlir::Location end = getLoc(srcLoc.getEnd());
|
|
SmallVector<mlir::Location, 2> locs = {beg, end};
|
|
mlir::Attribute metadata;
|
|
return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
|
|
}
|
|
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, &getMLIRContext());
|
|
}
|
|
|
|
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 case statements when we
|
|
// recursively process the sub-statements of the switch. If we haven't
|
|
// encountered a switch statement, we treat case statements like labels, but
|
|
// if we are processing a switch statement, case statements are expected.
|
|
if (isa<SwitchStmt>(s))
|
|
ignoreCaseStmts = true;
|
|
|
|
// Scan subexpressions for verboten labels.
|
|
return std::any_of(s->child_begin(), s->child_end(),
|
|
[=](const Stmt *subStmt) {
|
|
return containsLabel(subStmt, ignoreCaseStmts);
|
|
});
|
|
}
|
|
|
|
/// 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 CIRGenFunction::constantFoldsToBool(const Expr *cond, bool &resultBool,
|
|
bool allowLabels) {
|
|
llvm::APSInt resultInt;
|
|
if (!constantFoldsToSimpleInteger(cond, resultInt, allowLabels))
|
|
return false;
|
|
|
|
resultBool = resultInt.getBoolValue();
|
|
return true;
|
|
}
|
|
|
|
/// If the specified expression does not fold to a constant, or if it does
|
|
/// fold 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 intValue = result.Val.getInt();
|
|
if (!allowLabels && containsLabel(cond))
|
|
return false; // Contains a label.
|
|
|
|
resultInt = intValue;
|
|
return true;
|
|
}
|
|
|
|
void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
|
|
CharUnits alignment) {
|
|
if (!type->isVoidType()) {
|
|
mlir::Value addr = emitAlloca("__retval", convertType(type), loc, alignment,
|
|
/*insertIntoFnEntryBlock=*/false);
|
|
fnRetAlloca = addr;
|
|
returnValue = Address(addr, alignment);
|
|
}
|
|
}
|
|
|
|
void CIRGenFunction::declare(mlir::Value addrVal, const Decl *var, QualType ty,
|
|
mlir::Location loc, CharUnits alignment,
|
|
bool isParam) {
|
|
assert(isa<NamedDecl>(var) && "Needs a named decl");
|
|
assert(!symbolTable.count(var) && "not supposed to be available just yet");
|
|
|
|
auto allocaOp = addrVal.getDefiningOp<cir::AllocaOp>();
|
|
assert(allocaOp && "expected cir::AllocaOp");
|
|
|
|
if (isParam)
|
|
allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
|
|
if (ty->isReferenceType() || ty.isConstQualified())
|
|
allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext()));
|
|
|
|
symbolTable.insert(var, allocaOp);
|
|
}
|
|
|
|
void CIRGenFunction::LexicalScope::cleanup() {
|
|
CIRGenBuilderTy &builder = cgf.builder;
|
|
LexicalScope *localScope = cgf.curLexScope;
|
|
|
|
auto applyCleanup = [&]() {
|
|
if (performCleanup) {
|
|
// ApplyDebugLocation
|
|
assert(!cir::MissingFeatures::generateDebugInfo());
|
|
forceCleanup();
|
|
}
|
|
};
|
|
|
|
// Cleanup are done right before codegen resumes a scope. This is where
|
|
// objects are destroyed. Process all return blocks.
|
|
// TODO(cir): Handle returning from a switch statement through a cleanup
|
|
// block. We can't simply jump to the cleanup block, because the cleanup block
|
|
// is not part of the case region. Either reemit all cleanups in the return
|
|
// block or wait for MLIR structured control flow to support early exits.
|
|
llvm::SmallVector<mlir::Block *> retBlocks;
|
|
for (mlir::Block *retBlock : localScope->getRetBlocks()) {
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
builder.setInsertionPointToEnd(retBlock);
|
|
retBlocks.push_back(retBlock);
|
|
mlir::Location retLoc = localScope->getRetLoc(retBlock);
|
|
emitReturn(retLoc);
|
|
}
|
|
|
|
auto insertCleanupAndLeave = [&](mlir::Block *insPt) {
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
builder.setInsertionPointToEnd(insPt);
|
|
|
|
// If we still don't have a cleanup block, it means that `applyCleanup`
|
|
// below might be able to get us one.
|
|
mlir::Block *cleanupBlock = localScope->getCleanupBlock(builder);
|
|
|
|
// Leverage and defers to RunCleanupsScope's dtor and scope handling.
|
|
applyCleanup();
|
|
|
|
mlir::Block *currentBlock = builder.getBlock();
|
|
|
|
// If we now have one after `applyCleanup`, hook it up properly.
|
|
if (!cleanupBlock && localScope->getCleanupBlock(builder)) {
|
|
cleanupBlock = localScope->getCleanupBlock(builder);
|
|
cir::BrOp::create(builder, insPt->back().getLoc(), cleanupBlock);
|
|
if (!cleanupBlock->mightHaveTerminator()) {
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
builder.setInsertionPointToEnd(cleanupBlock);
|
|
cir::YieldOp::create(builder, localScope->endLoc);
|
|
}
|
|
}
|
|
|
|
if (localScope->depth == 0) {
|
|
// Reached the end of the function.
|
|
// Special handling only for single return block case
|
|
if (localScope->getRetBlocks().size() == 1) {
|
|
mlir::Block *retBlock = localScope->getRetBlocks()[0];
|
|
mlir::Location retLoc = localScope->getRetLoc(retBlock);
|
|
if (retBlock->getUses().empty()) {
|
|
retBlock->erase();
|
|
} else {
|
|
// Thread return block via cleanup block.
|
|
if (cleanupBlock) {
|
|
for (mlir::BlockOperand &blockUse : retBlock->getUses()) {
|
|
cir::BrOp brOp = mlir::cast<cir::BrOp>(blockUse.getOwner());
|
|
brOp.setSuccessor(cleanupBlock);
|
|
}
|
|
}
|
|
|
|
cir::BrOp::create(builder, retLoc, retBlock);
|
|
return;
|
|
}
|
|
}
|
|
emitImplicitReturn();
|
|
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() && !currentBlock->mightHaveTerminator()) {
|
|
!retVal ? cir::YieldOp::create(builder, localScope->endLoc)
|
|
: cir::YieldOp::create(builder, 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.
|
|
mlir::Block *cleanupBlock = localScope->getCleanupBlock(builder);
|
|
if (cleanupBlock)
|
|
insertCleanupAndLeave(cleanupBlock);
|
|
|
|
// Now deal with any pending block wrap up like implicit end of
|
|
// scope.
|
|
|
|
mlir::Block *curBlock = builder.getBlock();
|
|
if (isGlobalInit() && !curBlock)
|
|
return;
|
|
if (curBlock->mightHaveTerminator() && curBlock->getTerminator())
|
|
return;
|
|
|
|
// Get rid of any empty block at the end of the scope.
|
|
bool entryBlock = builder.getInsertionBlock()->isEntryBlock();
|
|
if (!entryBlock && curBlock->empty()) {
|
|
curBlock->erase();
|
|
for (mlir::Block *retBlock : retBlocks) {
|
|
if (retBlock->getUses().empty())
|
|
retBlock->erase();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If there's a cleanup block, branch to it, nothing else to do.
|
|
if (cleanupBlock) {
|
|
cir::BrOp::create(builder, curBlock->back().getLoc(), cleanupBlock);
|
|
return;
|
|
}
|
|
|
|
// No pre-existent cleanup block, emit cleanup code and yield/return.
|
|
insertCleanupAndLeave(curBlock);
|
|
}
|
|
|
|
cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
|
|
CIRGenBuilderTy &builder = cgf.getBuilder();
|
|
|
|
auto fn = dyn_cast<cir::FuncOp>(cgf.curFn);
|
|
assert(fn && "emitReturn from non-function");
|
|
|
|
// If we are on a coroutine, add the coro_end builtin call.
|
|
if (fn.getCoroutine())
|
|
cgf.emitCoroEndBuiltinCall(loc,
|
|
builder.getNullPtr(builder.getVoidPtrTy(), loc));
|
|
if (!fn.getFunctionType().hasVoidReturn()) {
|
|
// Load the value from `__retval` and return it via the `cir.return` op.
|
|
auto value = cir::LoadOp::create(
|
|
builder, loc, fn.getFunctionType().getReturnType(), *cgf.fnRetAlloca);
|
|
return cir::ReturnOp::create(builder, loc,
|
|
llvm::ArrayRef(value.getResult()));
|
|
}
|
|
return cir::ReturnOp::create(builder, loc);
|
|
}
|
|
|
|
// This is copied from CodeGenModule::MayDropFunctionReturn. This is a
|
|
// candidate for sharing between CIRGen and CodeGen.
|
|
static bool mayDropFunctionReturn(const ASTContext &astContext,
|
|
QualType returnType) {
|
|
// We can't just discard the return value for a record type with a complex
|
|
// destructor or a non-trivially copyable type.
|
|
if (const auto *classDecl = returnType->getAsCXXRecordDecl())
|
|
return classDecl->hasTrivialDestructor();
|
|
return returnType.isTriviallyCopyableType(astContext);
|
|
}
|
|
|
|
static bool previousOpIsNonYieldingCleanup(mlir::Block *block) {
|
|
if (block->empty())
|
|
return false;
|
|
mlir::Operation *op = &block->back();
|
|
auto cleanupScopeOp = mlir::dyn_cast<cir::CleanupScopeOp>(op);
|
|
if (!cleanupScopeOp)
|
|
return false;
|
|
|
|
// Check whether the body region of the cleanup scope exits via cir.yield.
|
|
// Exits via cir.return or cir.goto do not fall through to the operation
|
|
// following the cleanup scope, and exits via break, continue, and resume
|
|
// are not expected here.
|
|
for (mlir::Block &bodyBlock : cleanupScopeOp.getBodyRegion()) {
|
|
if (bodyBlock.mightHaveTerminator()) {
|
|
if (mlir::isa<cir::YieldOp>(bodyBlock.getTerminator()))
|
|
return false;
|
|
assert(!mlir::isa<cir::BreakOp>(bodyBlock.getTerminator()) &&
|
|
!mlir::isa<cir::ContinueOp>(bodyBlock.getTerminator()) &&
|
|
!mlir::isa<cir::ResumeOp>(bodyBlock.getTerminator()));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CIRGenFunction::LexicalScope::emitImplicitReturn() {
|
|
CIRGenBuilderTy &builder = cgf.getBuilder();
|
|
LexicalScope *localScope = cgf.curLexScope;
|
|
|
|
const auto *fd = cast<clang::FunctionDecl>(cgf.curGD.getDecl());
|
|
|
|
// In C++, flowing off the end of a non-void function is always undefined
|
|
// behavior. In C, flowing off the end of a non-void function is undefined
|
|
// behavior only if the non-existent return value is used by the caller.
|
|
// That influences whether the terminating op is trap, unreachable, or
|
|
// return.
|
|
if (cgf.getLangOpts().CPlusPlus && !fd->hasImplicitReturnZero() &&
|
|
!cgf.sawAsmBlock && !fd->getReturnType()->isVoidType() &&
|
|
builder.getInsertionBlock() &&
|
|
!previousOpIsNonYieldingCleanup(builder.getInsertionBlock())) {
|
|
bool shouldEmitUnreachable =
|
|
cgf.cgm.getCodeGenOpts().StrictReturn ||
|
|
!mayDropFunctionReturn(fd->getASTContext(), fd->getReturnType());
|
|
|
|
if (shouldEmitUnreachable) {
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
if (cgf.cgm.getCodeGenOpts().OptimizationLevel == 0)
|
|
cir::TrapOp::create(builder, localScope->endLoc);
|
|
else
|
|
cir::UnreachableOp::create(builder, localScope->endLoc);
|
|
builder.clearInsertionPoint();
|
|
return;
|
|
}
|
|
}
|
|
|
|
(void)emitReturn(localScope->endLoc);
|
|
}
|
|
|
|
cir::TryOp CIRGenFunction::LexicalScope::getClosestTryParent() {
|
|
LexicalScope *scope = this;
|
|
while (scope) {
|
|
if (scope->isTry())
|
|
return scope->getTry();
|
|
scope = scope->parentScope;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// An argument came in as a promoted argument; demote it back to its
|
|
/// declared type.
|
|
static mlir::Value emitArgumentDemotion(CIRGenFunction &cgf, const VarDecl *var,
|
|
mlir::Value value) {
|
|
mlir::Type ty = cgf.convertType(var->getType());
|
|
|
|
// This can happen with promotions that actually don't change the
|
|
// underlying type, like the enum promotions.
|
|
if (value.getType() == ty)
|
|
return value;
|
|
|
|
assert((mlir::isa<cir::IntType>(ty) || cir::isAnyFloatingPointType(ty)) &&
|
|
"unexpected promotion type");
|
|
|
|
if (mlir::isa<cir::IntType>(ty))
|
|
return cgf.getBuilder().CIRBaseBuilderTy::createIntCast(value, ty);
|
|
|
|
return cgf.getBuilder().createFloatingCast(value, ty);
|
|
}
|
|
|
|
void CIRGenFunction::emitFunctionProlog(const FunctionArgList &args,
|
|
mlir::Block *entryBB,
|
|
const FunctionDecl *fd,
|
|
SourceLocation bodyBeginLoc) {
|
|
// Naked functions don't have prologues.
|
|
if (fd && fd->hasAttr<NakedAttr>()) {
|
|
cgm.errorNYI(bodyBeginLoc, "naked function decl");
|
|
}
|
|
|
|
// Declare all the function arguments in the symbol table.
|
|
for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) {
|
|
const VarDecl *paramVar = std::get<0>(nameValue);
|
|
mlir::Value paramVal = std::get<1>(nameValue);
|
|
CharUnits alignment = getContext().getDeclAlign(paramVar);
|
|
mlir::Location paramLoc = getLoc(paramVar->getSourceRange());
|
|
paramVal.setLoc(paramLoc);
|
|
|
|
mlir::Value addrVal =
|
|
emitAlloca(cast<NamedDecl>(paramVar)->getName(),
|
|
convertType(paramVar->getType()), paramLoc, alignment,
|
|
/*insertIntoFnEntryBlock=*/true);
|
|
|
|
declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
|
|
/*isParam=*/true);
|
|
|
|
setAddrOfLocalVar(paramVar, Address(addrVal, alignment));
|
|
|
|
bool isPromoted = isa<ParmVarDecl>(paramVar) &&
|
|
cast<ParmVarDecl>(paramVar)->isKNRPromoted();
|
|
assert(!cir::MissingFeatures::constructABIArgDirectExtend());
|
|
if (isPromoted)
|
|
paramVal = emitArgumentDemotion(*this, paramVar, paramVal);
|
|
|
|
// Location of the store to the param storage tracked as beginning of
|
|
// the function body.
|
|
mlir::Location fnBodyBegin = getLoc(bodyBeginLoc);
|
|
builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addrVal);
|
|
}
|
|
assert(builder.getInsertionBlock() && "Should be valid");
|
|
}
|
|
|
|
void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
|
|
cir::FuncOp fn, cir::FuncType funcType,
|
|
FunctionArgList args, SourceLocation loc,
|
|
SourceLocation startLoc) {
|
|
assert(!curFn &&
|
|
"CIRGenFunction can only be used for one function at a time");
|
|
|
|
curFn = fn;
|
|
|
|
const Decl *d = gd.getDecl();
|
|
|
|
didCallStackSave = false;
|
|
curCodeDecl = d;
|
|
const auto *fd = dyn_cast_or_null<FunctionDecl>(d);
|
|
curFuncDecl = (d ? d->getNonClosureContext() : nullptr);
|
|
|
|
prologueCleanupDepth = ehStack.stable_begin();
|
|
|
|
mlir::Block *entryBB = &fn.getBlocks().front();
|
|
builder.setInsertionPointToStart(entryBB);
|
|
|
|
// Determine the function body begin location for the prolog.
|
|
// If fd is null or has no body, use startLoc as fallback.
|
|
SourceLocation bodyBeginLoc = startLoc;
|
|
if (fd) {
|
|
if (Stmt *body = fd->getBody())
|
|
bodyBeginLoc = body->getBeginLoc();
|
|
else
|
|
bodyBeginLoc = fd->getLocation();
|
|
}
|
|
|
|
emitFunctionProlog(args, entryBB, fd, bodyBeginLoc);
|
|
|
|
// When the current function is not void, create an address to store the
|
|
// result value.
|
|
if (!returnType->isVoidType()) {
|
|
// Determine the function body end location.
|
|
// If fd is null or has no body, use loc as fallback.
|
|
SourceLocation bodyEndLoc = loc;
|
|
if (fd) {
|
|
if (Stmt *body = fd->getBody())
|
|
bodyEndLoc = body->getEndLoc();
|
|
else
|
|
bodyEndLoc = fd->getLocation();
|
|
}
|
|
emitAndUpdateRetAlloca(returnType, getLoc(bodyEndLoc),
|
|
getContext().getTypeAlignInChars(returnType));
|
|
}
|
|
|
|
if (isa_and_nonnull<CXXMethodDecl>(d) &&
|
|
cast<CXXMethodDecl>(d)->isInstance()) {
|
|
cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);
|
|
|
|
const auto *md = cast<CXXMethodDecl>(d);
|
|
if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) {
|
|
// We're in a lambda.
|
|
auto fn = dyn_cast<cir::FuncOp>(curFn);
|
|
assert(fn && "lambda in non-function region");
|
|
fn.setLambda(true);
|
|
|
|
// Figure out the captures.
|
|
md->getParent()->getCaptureFields(lambdaCaptureFields,
|
|
lambdaThisCaptureField);
|
|
if (lambdaThisCaptureField) {
|
|
// If the lambda captures the object referred to by '*this' - either by
|
|
// value or by reference, make sure CXXThisValue points to the correct
|
|
// object.
|
|
|
|
// Get the lvalue for the field (which is a copy of the enclosing object
|
|
// or contains the address of the enclosing object).
|
|
LValue thisFieldLValue =
|
|
emitLValueForLambdaField(lambdaThisCaptureField);
|
|
if (!lambdaThisCaptureField->getType()->isPointerType()) {
|
|
// If the enclosing object was captured by value, just use its
|
|
// address. Sign this pointer.
|
|
cxxThisValue = thisFieldLValue.getPointer();
|
|
} else {
|
|
// Load the lvalue pointed to by the field, since '*this' was captured
|
|
// by reference.
|
|
cxxThisValue =
|
|
emitLoadOfLValue(thisFieldLValue, SourceLocation()).getValue();
|
|
}
|
|
}
|
|
for (auto *fd : md->getParent()->fields()) {
|
|
if (fd->hasCapturedVLAType())
|
|
cgm.errorNYI(loc, "lambda captured VLA type");
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
assert(!cir::MissingFeatures::emitTypeCheck());
|
|
}
|
|
}
|
|
|
|
void CIRGenFunction::resolveBlockAddresses() {
|
|
for (cir::BlockAddressOp &blockAddress : cgm.unresolvedBlockAddressToLabel) {
|
|
cir::LabelOp labelOp =
|
|
cgm.lookupBlockAddressInfo(blockAddress.getBlockAddrInfo());
|
|
assert(labelOp && "expected cir.labelOp to already be emitted");
|
|
cgm.updateResolvedBlockAddress(blockAddress, labelOp);
|
|
}
|
|
cgm.unresolvedBlockAddressToLabel.clear();
|
|
}
|
|
|
|
void CIRGenFunction::finishIndirectBranch() {
|
|
if (!indirectGotoBlock)
|
|
return;
|
|
llvm::SmallVector<mlir::Block *> succesors;
|
|
llvm::SmallVector<mlir::ValueRange> rangeOperands;
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
builder.setInsertionPointToEnd(indirectGotoBlock);
|
|
for (auto &[blockAdd, labelOp] : cgm.blockAddressToLabel) {
|
|
succesors.push_back(labelOp->getBlock());
|
|
rangeOperands.push_back(labelOp->getBlock()->getArguments());
|
|
}
|
|
cir::IndirectBrOp::create(builder, builder.getUnknownLoc(),
|
|
indirectGotoBlock->getArgument(0), false,
|
|
rangeOperands, succesors);
|
|
cgm.blockAddressToLabel.clear();
|
|
}
|
|
|
|
void CIRGenFunction::finishFunction(SourceLocation endLoc) {
|
|
// Resolve block address-to-label mappings, then emit the indirect branch
|
|
// with the corresponding targets.
|
|
resolveBlockAddresses();
|
|
finishIndirectBranch();
|
|
|
|
// If a label address was taken but no indirect goto was used, we can't remove
|
|
// the block argument here. Instead, we mark the 'indirectbr' op
|
|
// as poison so that the cleanup can be deferred to lowering, since the
|
|
// verifier doesn't allow the 'indirectbr' target address to be null.
|
|
if (indirectGotoBlock && indirectGotoBlock->hasNoPredecessors()) {
|
|
auto indrBr = cast<cir::IndirectBrOp>(indirectGotoBlock->front());
|
|
indrBr.setPoison(true);
|
|
}
|
|
|
|
// 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.
|
|
// TODO(cir): Use prologueCleanupDepth here.
|
|
bool hasCleanups = ehStack.stable_begin() != prologueCleanupDepth;
|
|
if (hasCleanups) {
|
|
assert(!cir::MissingFeatures::generateDebugInfo());
|
|
// FIXME(cir): should we clearInsertionPoint? breaks many testcases
|
|
popCleanupBlocks(prologueCleanupDepth);
|
|
}
|
|
}
|
|
|
|
mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
|
|
// We start with function level scope for variables.
|
|
SymTableScopeTy varScope(symbolTable);
|
|
|
|
if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body))
|
|
return emitCompoundStmtWithoutScope(*block);
|
|
|
|
return emitStmt(body, /*useCurrentScope=*/true);
|
|
}
|
|
|
|
static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
|
|
// Remove any leftover blocks that are unreachable and empty, since they do
|
|
// not represent unreachable code useful for warnings nor anything deemed
|
|
// useful in general.
|
|
SmallVector<mlir::Block *> blocksToDelete;
|
|
for (mlir::Block &block : func.getBlocks()) {
|
|
if (block.empty() && block.getUses().empty())
|
|
blocksToDelete.push_back(&block);
|
|
}
|
|
for (mlir::Block *block : blocksToDelete)
|
|
block->erase();
|
|
}
|
|
|
|
cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
|
|
cir::FuncType funcType) {
|
|
const auto *funcDecl = cast<FunctionDecl>(gd.getDecl());
|
|
curGD = gd;
|
|
|
|
if (funcDecl->isInlineBuiltinDeclaration()) {
|
|
// When generating code for a builtin with an inline declaration, use a
|
|
// mangled name to hold the actual body, while keeping an external
|
|
// declaration in case the function pointer is referenced somewhere.
|
|
std::string fdInlineName = (cgm.getMangledName(funcDecl) + ".inline").str();
|
|
cir::FuncOp clone =
|
|
mlir::cast_or_null<cir::FuncOp>(cgm.getGlobalValue(fdInlineName));
|
|
if (!clone) {
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
builder.setInsertionPoint(fn);
|
|
clone = cir::FuncOp::create(builder, fn.getLoc(), fdInlineName,
|
|
fn.getFunctionType());
|
|
clone.setLinkage(cir::GlobalLinkageKind::InternalLinkage);
|
|
clone.setSymVisibility("private");
|
|
clone.setInlineKind(cir::InlineKind::AlwaysInline);
|
|
}
|
|
fn.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
|
|
fn.setSymVisibility("private");
|
|
fn = clone;
|
|
} 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.
|
|
for (const FunctionDecl *pd = funcDecl->getPreviousDecl(); pd;
|
|
pd = pd->getPreviousDecl()) {
|
|
if (LLVM_UNLIKELY(pd->isInlineBuiltinDeclaration())) {
|
|
std::string inlineName = funcDecl->getName().str() + ".inline";
|
|
if (auto inlineFn = mlir::cast_or_null<cir::FuncOp>(
|
|
cgm.getGlobalValue(inlineName))) {
|
|
// Replace all uses of the .inline function with the regular function
|
|
// FIXME: This performs a linear walk over the module. Introduce some
|
|
// caching here.
|
|
if (inlineFn
|
|
.replaceAllSymbolUses(fn.getSymNameAttr(), cgm.getModule())
|
|
.failed())
|
|
llvm_unreachable("Failed to replace inline builtin symbol uses");
|
|
inlineFn.erase();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SourceLocation loc = funcDecl->getLocation();
|
|
Stmt *body = funcDecl->getBody();
|
|
SourceRange bodyRange =
|
|
body ? body->getSourceRange() : funcDecl->getLocation();
|
|
|
|
SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc)
|
|
: builder.getUnknownLoc()};
|
|
|
|
auto validMLIRLoc = [&](clang::SourceLocation clangLoc) {
|
|
return clangLoc.isValid() ? getLoc(clangLoc) : builder.getUnknownLoc();
|
|
};
|
|
const mlir::Location fusedLoc = mlir::FusedLoc::get(
|
|
&getMLIRContext(),
|
|
{validMLIRLoc(bodyRange.getBegin()), validMLIRLoc(bodyRange.getEnd())});
|
|
mlir::Block *entryBB = fn.addEntryBlock();
|
|
|
|
FunctionArgList args;
|
|
QualType retTy = buildFunctionArgList(gd, args);
|
|
|
|
// Create a scope in the symbol table to hold variable declarations.
|
|
SymTableScopeTy varScope(symbolTable);
|
|
{
|
|
LexicalScope lexScope(*this, fusedLoc, entryBB);
|
|
|
|
// Emit the standard function prologue.
|
|
startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());
|
|
|
|
// Save parameters for coroutine function.
|
|
if (body && isa_and_nonnull<CoroutineBodyStmt>(body))
|
|
llvm::append_range(fnArgs, funcDecl->parameters());
|
|
|
|
if (isa<CXXDestructorDecl>(funcDecl)) {
|
|
emitDestructorBody(args);
|
|
} else if (isa<CXXConstructorDecl>(funcDecl)) {
|
|
emitConstructorBody(args);
|
|
} else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice &&
|
|
funcDecl->hasAttr<CUDAGlobalAttr>()) {
|
|
cgm.getCUDARuntime().emitDeviceStub(*this, fn, args);
|
|
} else if (isa<CXXMethodDecl>(funcDecl) &&
|
|
cast<CXXMethodDecl>(funcDecl)->isLambdaStaticInvoker()) {
|
|
// The lambda static invoker function is special, because it forwards or
|
|
// clones the body of the function call operator (but is actually
|
|
// static).
|
|
emitLambdaStaticInvokeBody(cast<CXXMethodDecl>(funcDecl));
|
|
} else if (funcDecl->isDefaulted() && isa<CXXMethodDecl>(funcDecl) &&
|
|
(cast<CXXMethodDecl>(funcDecl)->isCopyAssignmentOperator() ||
|
|
cast<CXXMethodDecl>(funcDecl)->isMoveAssignmentOperator())) {
|
|
// Implicit copy-assignment gets the same special treatment as implicit
|
|
// copy-constructors.
|
|
emitImplicitAssignmentOperatorBody(args);
|
|
} else if (body) {
|
|
// Emit standard function body.
|
|
if (mlir::failed(emitFunctionBody(body))) {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
// Anything without a body should have been handled above.
|
|
llvm_unreachable("no definition for normal function");
|
|
}
|
|
|
|
if (mlir::failed(fn.verifyBody()))
|
|
return nullptr;
|
|
|
|
finishFunction(bodyRange.getEnd());
|
|
}
|
|
|
|
eraseEmptyAndUnusedBlocks(fn);
|
|
return fn;
|
|
}
|
|
|
|
void CIRGenFunction::emitConstructorBody(FunctionArgList &args) {
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
const auto *ctor = cast<CXXConstructorDecl>(curGD.getDecl());
|
|
CXXCtorType ctorType = curGD.getCtorType();
|
|
|
|
assert((cgm.getTarget().getCXXABI().hasConstructorVariants() ||
|
|
ctorType == Ctor_Complete) &&
|
|
"can only generate complete ctor for this ABI");
|
|
|
|
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor);
|
|
|
|
if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
|
|
cgm.getTarget().getCXXABI().hasConstructorVariants()) {
|
|
emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc());
|
|
return;
|
|
}
|
|
|
|
const FunctionDecl *definition = nullptr;
|
|
Stmt *body = ctor->getBody(definition);
|
|
assert(definition == ctor && "emitting wrong constructor body");
|
|
|
|
if (isa_and_nonnull<CXXTryStmt>(body)) {
|
|
cgm.errorNYI(ctor->getSourceRange(), "emitConstructorBody: try body");
|
|
return;
|
|
}
|
|
|
|
assert(!cir::MissingFeatures::incrementProfileCounter());
|
|
assert(!cir::MissingFeatures::runCleanupsScope());
|
|
|
|
// TODO: in restricted cases, we can emit the vbase initializers of a
|
|
// complete ctor and then delegate to the base ctor.
|
|
|
|
// Emit the constructor prologue, i.e. the base and member initializers.
|
|
emitCtorPrologue(ctor, ctorType, args);
|
|
|
|
// TODO(cir): propagate this result via mlir::logical result. Just unreachable
|
|
// now just to have it handled.
|
|
if (mlir::failed(emitStmt(body, true))) {
|
|
cgm.errorNYI(ctor->getSourceRange(),
|
|
"emitConstructorBody: emit body statement failed.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// Emits the body of the current destructor.
|
|
void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
|
|
const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl());
|
|
CXXDtorType dtorType = curGD.getDtorType();
|
|
|
|
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), dtor);
|
|
|
|
// For an abstract class, non-base destructors are never used (and can't
|
|
// be emitted in general, because vbase dtors may not have been validated
|
|
// by Sema), but the Itanium ABI doesn't make them optional and Clang may
|
|
// in fact emit references to them from other compilations, so emit them
|
|
// as functions containing a trap instruction.
|
|
if (dtorType != Dtor_Base && dtor->getParent()->isAbstract()) {
|
|
SourceLocation loc =
|
|
dtor->hasBody() ? dtor->getBody()->getBeginLoc() : dtor->getLocation();
|
|
emitTrap(getLoc(loc), true);
|
|
return;
|
|
}
|
|
|
|
Stmt *body = dtor->getBody();
|
|
assert(body && !cir::MissingFeatures::incrementProfileCounter());
|
|
|
|
// The call to operator delete in a deleting destructor happens
|
|
// outside of the function-try-block, which means it's always
|
|
// possible to delegate the destructor body to the complete
|
|
// destructor. Do so.
|
|
if (dtorType == Dtor_Deleting || dtorType == Dtor_VectorDeleting) {
|
|
if (cxxStructorImplicitParamValue && dtorType == Dtor_VectorDeleting)
|
|
cgm.errorNYI(dtor->getSourceRange(), "emitConditionalArrayDtorCall");
|
|
RunCleanupsScope dtorEpilogue(*this);
|
|
enterDtorCleanups(dtor, Dtor_Deleting);
|
|
if (haveInsertPoint()) {
|
|
QualType thisTy = dtor->getFunctionObjectParameterType();
|
|
emitCXXDestructorCall(dtor, Dtor_Complete, /*forVirtualBase=*/false,
|
|
/*delegating=*/false, loadCXXThisAddress(), thisTy);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If the body is a function-try-block, enter the try before
|
|
// anything else.
|
|
const bool isTryBody = isa_and_nonnull<CXXTryStmt>(body);
|
|
if (isTryBody)
|
|
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");
|
|
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
|
|
// Enter the epilogue cleanups.
|
|
RunCleanupsScope dtorEpilogue(*this);
|
|
|
|
// If this is the complete variant, just invoke the base variant;
|
|
// the epilogue will destruct the virtual bases. But we can't do
|
|
// this optimization if the body is a function-try-block, because
|
|
// we'd introduce *two* handler blocks. In the Microsoft ABI, we
|
|
// always delegate because we might not have a definition in this TU.
|
|
switch (dtorType) {
|
|
case Dtor_Unified:
|
|
llvm_unreachable("not expecting a unified dtor");
|
|
case Dtor_Comdat:
|
|
llvm_unreachable("not expecting a COMDAT");
|
|
case Dtor_Deleting:
|
|
case Dtor_VectorDeleting:
|
|
llvm_unreachable("already handled deleting case");
|
|
|
|
case Dtor_Complete:
|
|
assert((body || getTarget().getCXXABI().isMicrosoft()) &&
|
|
"can't emit a dtor without a body for non-Microsoft ABIs");
|
|
|
|
// Enter the cleanup scopes for virtual bases.
|
|
enterDtorCleanups(dtor, Dtor_Complete);
|
|
|
|
if (!isTryBody) {
|
|
QualType thisTy = dtor->getFunctionObjectParameterType();
|
|
emitCXXDestructorCall(dtor, Dtor_Base, /*forVirtualBase=*/false,
|
|
/*delegating=*/false, loadCXXThisAddress(), thisTy);
|
|
break;
|
|
}
|
|
|
|
// Fallthrough: act like we're in the base variant.
|
|
[[fallthrough]];
|
|
|
|
case Dtor_Base:
|
|
assert(body);
|
|
|
|
// Enter the cleanup scopes for fields and non-virtual bases.
|
|
enterDtorCleanups(dtor, Dtor_Base);
|
|
|
|
assert(!cir::MissingFeatures::vtableInitialization());
|
|
|
|
if (isTryBody) {
|
|
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");
|
|
} else if (body) {
|
|
(void)emitStmt(body, /*useCurrentScope=*/true);
|
|
} else {
|
|
assert(dtor->isImplicit() && "bodyless dtor not implicit");
|
|
// nothing to do besides what's in the epilogue
|
|
}
|
|
// -fapple-kext must inline any call to this dtor into
|
|
// the caller's body.
|
|
assert(!cir::MissingFeatures::appleKext());
|
|
|
|
break;
|
|
}
|
|
|
|
// Jump out through the epilogue cleanups.
|
|
dtorEpilogue.forceCleanup();
|
|
|
|
// Exit the try if applicable.
|
|
if (isTryBody)
|
|
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");
|
|
}
|
|
|
|
/// Given a value of type T* that may not be to a complete object, construct
|
|
/// an l-vlaue withi the natural pointee alignment of T.
|
|
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
|
|
QualType ty) {
|
|
// FIXME(cir): is it safe to assume Op->getResult(0) is valid? Perhaps
|
|
// assert on the result type first.
|
|
LValueBaseInfo baseInfo;
|
|
assert(!cir::MissingFeatures::opTBAA());
|
|
CharUnits align = cgm.getNaturalTypeAlignment(ty, &baseInfo);
|
|
return makeAddrLValue(Address(val, align), ty, baseInfo);
|
|
}
|
|
|
|
LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val,
|
|
QualType ty) {
|
|
LValueBaseInfo baseInfo;
|
|
CharUnits alignment = cgm.getNaturalTypeAlignment(ty, &baseInfo);
|
|
Address addr(val, convertTypeForMem(ty), alignment);
|
|
assert(!cir::MissingFeatures::opTBAA());
|
|
return makeAddrLValue(addr, ty, baseInfo);
|
|
}
|
|
|
|
// Map the LangOption for exception behavior into the corresponding enum in
|
|
// the IR.
|
|
static llvm::fp::ExceptionBehavior
|
|
toConstrainedExceptMd(LangOptions::FPExceptionModeKind kind) {
|
|
switch (kind) {
|
|
case LangOptions::FPE_Ignore:
|
|
return llvm::fp::ebIgnore;
|
|
case LangOptions::FPE_MayTrap:
|
|
return llvm::fp::ebMayTrap;
|
|
case LangOptions::FPE_Strict:
|
|
return llvm::fp::ebStrict;
|
|
case LangOptions::FPE_Default:
|
|
llvm_unreachable("expected explicitly initialized exception behavior");
|
|
}
|
|
llvm_unreachable("unsupported FP exception behavior");
|
|
}
|
|
|
|
clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
|
|
FunctionArgList &args) {
|
|
const auto *fd = cast<FunctionDecl>(gd.getDecl());
|
|
QualType retTy = fd->getReturnType();
|
|
|
|
const auto *md = dyn_cast<CXXMethodDecl>(fd);
|
|
if (md && md->isInstance()) {
|
|
if (cgm.getCXXABI().hasThisReturn(gd))
|
|
cgm.errorNYI(fd->getSourceRange(), "this return");
|
|
else if (cgm.getCXXABI().hasMostDerivedReturn(gd))
|
|
cgm.errorNYI(fd->getSourceRange(), "most derived return");
|
|
cgm.getCXXABI().buildThisParam(*this, args);
|
|
}
|
|
|
|
if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd))
|
|
if (cd->getInheritedConstructor())
|
|
cgm.errorNYI(fd->getSourceRange(),
|
|
"buildFunctionArgList: inherited constructor");
|
|
|
|
for (auto *param : fd->parameters())
|
|
args.push_back(param);
|
|
|
|
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
|
|
cgm.getCXXABI().addImplicitStructorParams(*this, retTy, args);
|
|
|
|
return retTy;
|
|
}
|
|
|
|
/// Emit code to compute a designator that specifies the location
|
|
/// of the expression.
|
|
/// FIXME: document this function better.
|
|
LValue CIRGenFunction::emitLValue(const Expr *e) {
|
|
// FIXME: ApplyDebugLocation DL(*this, e);
|
|
switch (e->getStmtClass()) {
|
|
default:
|
|
getCIRGenModule().errorNYI(e->getSourceRange(),
|
|
std::string("l-value not implemented for '") +
|
|
e->getStmtClassName() + "'");
|
|
return LValue();
|
|
case Expr::ConditionalOperatorClass:
|
|
return emitConditionalOperatorLValue(cast<ConditionalOperator>(e));
|
|
case Expr::BinaryConditionalOperatorClass:
|
|
return emitConditionalOperatorLValue(cast<BinaryConditionalOperator>(e));
|
|
case Expr::ArraySubscriptExprClass:
|
|
return emitArraySubscriptExpr(cast<ArraySubscriptExpr>(e));
|
|
case Expr::ExtVectorElementExprClass:
|
|
return emitExtVectorElementExpr(cast<ExtVectorElementExpr>(e));
|
|
case Expr::UnaryOperatorClass:
|
|
return emitUnaryOpLValue(cast<UnaryOperator>(e));
|
|
case Expr::StringLiteralClass:
|
|
return emitStringLiteralLValue(cast<StringLiteral>(e));
|
|
case Expr::MemberExprClass:
|
|
return emitMemberExpr(cast<MemberExpr>(e));
|
|
case Expr::CompoundLiteralExprClass:
|
|
return emitCompoundLiteralLValue(cast<CompoundLiteralExpr>(e));
|
|
case Expr::PredefinedExprClass:
|
|
return emitPredefinedLValue(cast<PredefinedExpr>(e));
|
|
case Expr::BinaryOperatorClass:
|
|
return emitBinaryOperatorLValue(cast<BinaryOperator>(e));
|
|
case Expr::CompoundAssignOperatorClass: {
|
|
QualType ty = e->getType();
|
|
if (ty->getAs<AtomicType>()) {
|
|
cgm.errorNYI(e->getSourceRange(),
|
|
"CompoundAssignOperator with AtomicType");
|
|
return LValue();
|
|
}
|
|
if (!ty->isAnyComplexType())
|
|
return emitCompoundAssignmentLValue(cast<CompoundAssignOperator>(e));
|
|
|
|
return emitComplexCompoundAssignmentLValue(cast<CompoundAssignOperator>(e));
|
|
}
|
|
case Expr::CallExprClass:
|
|
case Expr::CXXMemberCallExprClass:
|
|
case Expr::CXXOperatorCallExprClass:
|
|
case Expr::UserDefinedLiteralClass:
|
|
return emitCallExprLValue(cast<CallExpr>(e));
|
|
case Expr::ExprWithCleanupsClass: {
|
|
const auto *cleanups = cast<ExprWithCleanups>(e);
|
|
RunCleanupsScope scope(*this);
|
|
LValue lv = emitLValue(cleanups->getSubExpr());
|
|
assert(!cir::MissingFeatures::cleanupWithPreservedValues());
|
|
return lv;
|
|
}
|
|
case Expr::CXXDefaultArgExprClass: {
|
|
auto *dae = cast<CXXDefaultArgExpr>(e);
|
|
CXXDefaultArgExprScope scope(*this, dae);
|
|
return emitLValue(dae->getExpr());
|
|
}
|
|
case Expr::CXXTypeidExprClass:
|
|
return emitCXXTypeidLValue(cast<CXXTypeidExpr>(e));
|
|
case Expr::ParenExprClass:
|
|
return emitLValue(cast<ParenExpr>(e)->getSubExpr());
|
|
case Expr::GenericSelectionExprClass:
|
|
return emitLValue(cast<GenericSelectionExpr>(e)->getResultExpr());
|
|
case Expr::DeclRefExprClass:
|
|
return emitDeclRefLValue(cast<DeclRefExpr>(e));
|
|
case Expr::ImplicitCastExprClass:
|
|
case Expr::CStyleCastExprClass:
|
|
case Expr::CXXStaticCastExprClass:
|
|
case Expr::CXXDynamicCastExprClass:
|
|
case Expr::CXXReinterpretCastExprClass:
|
|
case Expr::CXXConstCastExprClass:
|
|
// TODO(cir): The above list is missing CXXFunctionalCastExprClass,
|
|
// CXXAddrSpaceCastExprClass, and ObjCBridgedCastExprClass.
|
|
return emitCastLValue(cast<CastExpr>(e));
|
|
case Expr::MaterializeTemporaryExprClass:
|
|
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
|
|
case Expr::OpaqueValueExprClass:
|
|
return emitOpaqueValueLValue(cast<OpaqueValueExpr>(e));
|
|
case Expr::ChooseExprClass:
|
|
return emitLValue(cast<ChooseExpr>(e)->getChosenSubExpr());
|
|
case Expr::SubstNonTypeTemplateParmExprClass:
|
|
return emitLValue(cast<SubstNonTypeTemplateParmExpr>(e)->getReplacement());
|
|
}
|
|
}
|
|
|
|
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::getCounterRefTmpAsString() {
|
|
return getVersionedTmpName("ref.tmp", counterRefTmp++);
|
|
}
|
|
|
|
std::string CIRGenFunction::getCounterAggTmpAsString() {
|
|
return getVersionedTmpName("agg.tmp", counterAggTmp++);
|
|
}
|
|
|
|
void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
|
|
QualType ty) {
|
|
// Ignore empty classes in C++.
|
|
if (getLangOpts().CPlusPlus)
|
|
if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->isEmpty())
|
|
return;
|
|
|
|
// Cast the dest ptr to the appropriate i8 pointer type.
|
|
if (builder.isInt8Ty(destPtr.getElementType())) {
|
|
cgm.errorNYI(loc, "Cast the dest ptr to the appropriate i8 pointer type");
|
|
}
|
|
|
|
// Get size and alignment info for this aggregate.
|
|
const CharUnits size = getContext().getTypeSizeInChars(ty);
|
|
if (size.isZero()) {
|
|
// But note that getTypeInfo returns 0 for a VLA.
|
|
if (isa<VariableArrayType>(getContext().getAsArrayType(ty))) {
|
|
cgm.errorNYI(loc,
|
|
"emitNullInitialization for zero size VariableArrayType");
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 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)) {
|
|
cgm.errorNYI(loc, "type is not zero initializable");
|
|
}
|
|
|
|
// 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);
|
|
const mlir::Value zeroValue = builder.getNullValue(convertType(ty), loc);
|
|
builder.createStore(loc, zeroValue, 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(!cir::MissingFeatures::fastMathGuard());
|
|
|
|
[[maybe_unused]] llvm::RoundingMode newRoundingBehavior =
|
|
fpFeatures.getRoundingMode();
|
|
// TODO(cir): override rounding behaviour once FM configs are guarded.
|
|
[[maybe_unused]] llvm::fp::ExceptionBehavior 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(!cir::MissingFeatures::fastMathFlags());
|
|
|
|
assert((cgf.curFuncDecl == nullptr || cgf.builder.getIsFPConstrained() ||
|
|
isa<CXXConstructorDecl>(cgf.curFuncDecl) ||
|
|
isa<CXXDestructorDecl>(cgf.curFuncDecl) ||
|
|
(newExceptionBehavior == llvm::fp::ebIgnore &&
|
|
newRoundingBehavior == llvm::RoundingMode::NearestTiesToEven)) &&
|
|
"FPConstrained should be enabled on entire function");
|
|
|
|
// TODO(cir): mark CIR function with fast math attributes.
|
|
assert(!cir::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;
|
|
}
|
|
|
|
/// 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::emitArrayLength(const clang::ArrayType *origArrayType,
|
|
QualType &baseType, Address &addr) {
|
|
const clang::ArrayType *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.
|
|
if (isa<VariableArrayType>(arrayType)) {
|
|
assert(cir::MissingFeatures::vlas());
|
|
cgm.errorNYI(*currSrcLoc, "VLAs");
|
|
return builder.getConstInt(*currSrcLoc, sizeTy, 0);
|
|
}
|
|
|
|
uint64_t countFromCLAs = 1;
|
|
QualType eltType;
|
|
|
|
auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType());
|
|
|
|
while (cirArrayType) {
|
|
assert(isa<ConstantArrayType>(arrayType));
|
|
countFromCLAs *= cirArrayType.getSize();
|
|
eltType = arrayType->getElementType();
|
|
|
|
cirArrayType =
|
|
mlir::dyn_cast<cir::ArrayType>(cirArrayType.getElementType());
|
|
|
|
arrayType = getContext().getAsArrayType(arrayType->getElementType());
|
|
assert((!cirArrayType || arrayType) &&
|
|
"CIR and Clang types are out-of-sync");
|
|
}
|
|
|
|
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.
|
|
cgm.errorNYI(*currSrcLoc, "length for non-array underlying types");
|
|
}
|
|
|
|
baseType = eltType;
|
|
return builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
|
|
}
|
|
|
|
void CIRGenFunction::instantiateIndirectGotoBlock() {
|
|
// If we already made the indirect branch for indirect goto, return its block.
|
|
if (indirectGotoBlock)
|
|
return;
|
|
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
indirectGotoBlock =
|
|
builder.createBlock(builder.getBlock()->getParent(), {}, {voidPtrTy},
|
|
{builder.getUnknownLoc()});
|
|
}
|
|
|
|
mlir::Value CIRGenFunction::emitAlignmentAssumption(
|
|
mlir::Value ptrValue, QualType ty, SourceLocation loc,
|
|
SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue) {
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
return cir::AssumeAlignedOp::create(builder, getLoc(assumptionLoc), ptrValue,
|
|
alignment, offsetValue);
|
|
}
|
|
|
|
mlir::Value CIRGenFunction::emitAlignmentAssumption(
|
|
mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc,
|
|
int64_t alignment, mlir::Value offsetValue) {
|
|
QualType ty = expr->getType();
|
|
SourceLocation loc = expr->getExprLoc();
|
|
return emitAlignmentAssumption(ptrValue, ty, loc, assumptionLoc, alignment,
|
|
offsetValue);
|
|
}
|
|
|
|
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.getLoc(), numElements, vlaSize,
|
|
cir::OverflowBehavior::NoUnsignedWrap);
|
|
}
|
|
} while ((type = getContext().getAsVariableArrayType(elementType)));
|
|
|
|
assert(numElements && "Undefined elements number");
|
|
return {numElements, elementType};
|
|
}
|
|
|
|
CIRGenFunction::VlaSizePair
|
|
CIRGenFunction::getVLAElements1D(const VariableArrayType *vla) {
|
|
mlir::Value vlaSize = vlaSizeMap[vla->getSizeExpr()];
|
|
assert(vlaSize && "no size for VLA!");
|
|
assert(vlaSize.getType() == sizeTy);
|
|
return {vlaSize, vla->getElementType()};
|
|
}
|
|
|
|
// TODO(cir): Most of this function can be shared between CIRGen
|
|
// and traditional LLVM codegen
|
|
void CIRGenFunction::emitVariablyModifiedType(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 Type::CountAttributed:
|
|
case Type::PackIndexing:
|
|
case Type::ArrayParameter:
|
|
case Type::HLSLAttributedResource:
|
|
case Type::HLSLInlineSpirv:
|
|
case Type::PredefinedSugar:
|
|
cgm.errorNYI("CIRGenFunction::emitVariablyModifiedType");
|
|
break;
|
|
|
|
#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(
|
|
"dependent type must be resolved before the CIR codegen");
|
|
|
|
// 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:
|
|
case Type::OverflowBehavior:
|
|
llvm_unreachable("type class is never variably-modified!");
|
|
|
|
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 = emitScalarExpr(sizeExpr);
|
|
assert(!cir::MissingFeatures::sanitizers());
|
|
|
|
// 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.
|
|
emitIgnoredExpr(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());
|
|
}
|
|
|
|
Address CIRGenFunction::emitVAListRef(const Expr *e) {
|
|
if (getContext().getBuiltinVaListType()->isArrayType())
|
|
return emitPointerWithAlignment(e);
|
|
return emitLValue(e).getAddress();
|
|
}
|
|
|
|
} // namespace clang::CIRGen
|