aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CIR/CodeGen/CIRGenFunction.cpp')
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.cpp219
1 files changed, 178 insertions, 41 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 2338ec9..16547f2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -135,6 +135,13 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
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) {
@@ -149,6 +156,118 @@ void CIRGenFunction::declare(mlir::Value addrVal, const Decl *var, QualType ty,
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,
@@ -156,7 +275,6 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
assert(!curFn &&
"CIRGenFunction can only be used for one function at a time");
- fnRetTy = returnType;
curFn = fn;
const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
@@ -194,6 +312,12 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
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) {}
@@ -208,9 +332,24 @@ mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
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 =
@@ -219,55 +358,53 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc)
: builder.getUnknownLoc()};
- // This will be used once more code is upstreamed.
- [[maybe_unused]] mlir::Block *entryBB = fn.addEntryBlock();
+ 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);
- 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
- llvm_unreachable("no definition for normal function");
-
- // This code to insert a cir.return or cir.trap at the end of the function is
- // temporary until the function return code, including
- // CIRGenFunction::LexicalScope::emitImplicitReturn(), is upstreamed.
- mlir::Block &lastBlock = fn.getRegion().back();
- if (lastBlock.empty() || !lastBlock.mightHaveTerminator() ||
- !lastBlock.getTerminator()->hasTrait<mlir::OpTrait::IsTerminator>()) {
- builder.setInsertionPointToEnd(&lastBlock);
- if (mlir::isa<cir::VoidType>(funcType.getReturnType())) {
- builder.create<cir::ReturnOp>(getLoc(bodyRange.getEnd()));
+ {
+ 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 {
- builder.create<cir::TrapOp>(getLoc(bodyRange.getEnd()));
+ // Anything without a body should have been handled above.
+ llvm_unreachable("no definition for normal function");
}
- }
- if (mlir::failed(fn.verifyBody()))
- return nullptr;
+ if (mlir::failed(fn.verifyBody()))
+ return nullptr;
- finishFunction(bodyRange.getEnd());
+ finishFunction(bodyRange.getEnd());
+ }
+ eraseEmptyAndUnusedBlocks(fn);
return fn;
}