blob: 16547f240129250c0bb6560c5742d58d724d6af4 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 "CIRGenCall.h"
#include "CIRGenValue.h"
#include "mlir/IR/Location.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/MissingFeatures.h"
#include <cassert>
namespace clang::CIRGen {
CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder,
bool suppressNewContext)
: CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) {}
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::ArrayParameter:
case Type::HLSLAttributedResource:
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 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:
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());
}
void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
CharUnits alignment) {
if (!type->isVoidType()) {
fnRetAlloca = emitAlloca("__retval", convertType(type), loc, alignment);
}
}
void CIRGenFunction::declare(mlir::Value addrVal, const Decl *var, QualType ty,
mlir::Location loc, CharUnits alignment,
bool isParam) {
const auto *namedVar = dyn_cast_or_null<NamedDecl>(var);
assert(namedVar && "Needs a named decl");
assert(!cir::MissingFeatures::cgfSymbolTable());
auto allocaOp = cast<cir::AllocaOp>(addrVal.getDefiningOp());
if (isParam)
allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
if (ty->isReferenceType() || ty.isConstQualified())
allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext()));
}
void CIRGenFunction::LexicalScope::cleanup() {
CIRGenBuilderTy &builder = cgf.builder;
LexicalScope *localScope = cgf.curLexScope;
if (returnBlock != nullptr) {
// Write out the return block, which loads the value from `__retval` and
// issues the `cir.return`.
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(returnBlock);
(void)emitReturn(*returnLoc);
}
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();
if (returnBlock != nullptr && returnBlock->getUses().empty())
returnBlock->erase();
return;
}
// Reached the end of the scope.
{
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(curBlock);
if (localScope->depth == 0) {
// Reached the end of the function.
if (returnBlock != nullptr) {
if (returnBlock->getUses().empty())
returnBlock->erase();
else {
builder.create<cir::BrOp>(*returnLoc, returnBlock);
return;
}
}
emitImplicitReturn();
return;
}
// Reached the end of a non-function scope. Some scopes, such as those
// used with the ?: operator, can return a value.
if (!localScope->isTernary() && !curBlock->mightHaveTerminator()) {
!retVal ? builder.create<cir::YieldOp>(localScope->endLoc)
: builder.create<cir::YieldOp>(localScope->endLoc, retVal);
}
}
}
cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
CIRGenBuilderTy &builder = cgf.getBuilder();
if (!cgf.curFn.getFunctionType().hasVoidReturn()) {
// Load the value from `__retval` and return it via the `cir.return` op.
auto value = builder.create<cir::LoadOp>(
loc, cgf.curFn.getFunctionType().getReturnType(), *cgf.fnRetAlloca);
return builder.create<cir::ReturnOp>(loc,
llvm::ArrayRef(value.getResult()));
}
return builder.create<cir::ReturnOp>(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 RecordType *recordType =
returnType.getCanonicalType()->getAs<RecordType>()) {
if (const auto *classDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl()))
return classDecl->hasTrivialDestructor();
}
return returnType.isTriviallyCopyableType(astContext);
}
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()) {
bool shouldEmitUnreachable =
cgf.cgm.getCodeGenOpts().StrictReturn ||
!mayDropFunctionReturn(fd->getASTContext(), fd->getReturnType());
if (shouldEmitUnreachable) {
if (cgf.cgm.getCodeGenOpts().OptimizationLevel == 0)
builder.create<cir::TrapOp>(localScope->endLoc);
else
builder.create<cir::UnreachableOp>(localScope->endLoc);
builder.clearInsertionPoint();
return;
}
}
(void)emitReturn(localScope->endLoc);
}
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 auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
mlir::Block *entryBB = &fn.getBlocks().front();
builder.setInsertionPointToStart(entryBB);
// TODO(cir): this should live in `emitFunctionProlog
// 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);
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)
cgm.errorNYI(fd->getSourceRange(), "Function argument demotion");
// Location of the store to the param storage tracked as beginning of
// the function body.
mlir::Location fnBodyBegin = getLoc(fd->getBody()->getBeginLoc());
builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addrVal);
}
assert(builder.getInsertionBlock() && "Should be valid");
// When the current function is not void, create an address to store the
// result value.
if (!returnType->isVoidType())
emitAndUpdateRetAlloca(returnType, getLoc(fd->getBody()->getEndLoc()),
getContext().getTypeAlignInChars(returnType));
}
void CIRGenFunction::finishFunction(SourceLocation endLoc) {}
mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
auto result = mlir::LogicalResult::success();
if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body))
emitCompoundStmtWithoutScope(*block);
else
result = emitStmt(body, /*useCurrentScope=*/true);
return result;
}
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;
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);
{
LexicalScope lexScope(*this, fusedLoc, entryBB);
startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());
if (isa<CXXDestructorDecl>(funcDecl))
getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition");
else if (isa<CXXConstructorDecl>(funcDecl))
getCIRGenModule().errorNYI(bodyRange, "C++ constructor definition");
else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice &&
funcDecl->hasAttr<CUDAGlobalAttr>())
getCIRGenModule().errorNYI(bodyRange, "CUDA kernel");
else if (isa<CXXMethodDecl>(funcDecl) &&
cast<CXXMethodDecl>(funcDecl)->isLambdaStaticInvoker())
getCIRGenModule().errorNYI(bodyRange, "Lambda static invoker");
else if (funcDecl->isDefaulted() && isa<CXXMethodDecl>(funcDecl) &&
(cast<CXXMethodDecl>(funcDecl)->isCopyAssignmentOperator() ||
cast<CXXMethodDecl>(funcDecl)->isMoveAssignmentOperator()))
getCIRGenModule().errorNYI(bodyRange, "Default assignment operator");
else if (body) {
if (mlir::failed(emitFunctionBody(body))) {
fn.erase();
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;
}
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())
cgm.errorNYI(fd->getSourceRange(), "buildFunctionArgList: CXXMethodDecl");
if (isa<CXXConstructorDecl>(fd))
cgm.errorNYI(fd->getSourceRange(),
"buildFunctionArgList: CXXConstructorDecl");
for (auto *param : fd->parameters())
args.push_back(param);
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
cgm.errorNYI(fd->getSourceRange(),
"buildFunctionArgList: implicit structor params");
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::UnaryOperatorClass:
return emitUnaryOpLValue(cast<UnaryOperator>(e));
case Expr::DeclRefExprClass:
return emitDeclRefLValue(cast<DeclRefExpr>(e));
}
}
} // namespace clang::CIRGen