Skip to content

Commit 266b004

Browse files
Lancernlanza
authored andcommitted
[CIR][CIRGen] Emit cir.unreachable on implicit returns (#486)
This patch changes the emission of implicit returns from functions whose return type is not `void`. Instead of emitting `cir.return`, this PR aligns to the original clang CodeGen and emits a `cir.unreachable` operation. Related issue: #457 .
1 parent f9f7776 commit 266b004

File tree

4 files changed

+90
-51
lines changed

4 files changed

+90
-51
lines changed

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 70 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -323,22 +323,6 @@ void CIRGenFunction::LexicalScope::cleanup() {
323323
auto &builder = CGF.builder;
324324
auto *localScope = CGF.currLexScope;
325325

326-
auto buildReturn = [&](mlir::Location loc) {
327-
// If we are on a coroutine, add the coro_end builtin call.
328-
auto Fn = dyn_cast<mlir::cir::FuncOp>(CGF.CurFn);
329-
assert(Fn && "other callables NYI");
330-
if (Fn.getCoroutine())
331-
CGF.buildCoroEndBuiltinCall(
332-
loc, builder.getNullPtr(builder.getVoidPtrTy(), loc));
333-
334-
if (CGF.FnRetCIRTy.has_value()) {
335-
// If there's anything to return, load it first.
336-
auto val = builder.create<LoadOp>(loc, *CGF.FnRetCIRTy, *CGF.FnRetAlloca);
337-
return builder.create<ReturnOp>(loc, llvm::ArrayRef(val.getResult()));
338-
}
339-
return builder.create<ReturnOp>(loc);
340-
};
341-
342326
// Handle pending gotos and the solved labels in this scope.
343327
while (!localScope->PendingGotos.empty()) {
344328
auto gotoInfo = localScope->PendingGotos.back();
@@ -381,15 +365,18 @@ void CIRGenFunction::LexicalScope::cleanup() {
381365
// Leverage and defers to RunCleanupsScope's dtor and scope handling.
382366
applyCleanup();
383367

384-
if (localScope->Depth != 0) { // end of any local scope != function
385-
// Ternary ops have to deal with matching arms for yielding types
386-
// and do return a value, it must do its own cir.yield insertion.
387-
if (!localScope->isTernary()) {
388-
!retVal ? builder.create<YieldOp>(localScope->EndLoc)
389-
: builder.create<YieldOp>(localScope->EndLoc, retVal);
390-
}
391-
} else
392-
(void)buildReturn(localScope->EndLoc);
368+
if (localScope->Depth == 0) {
369+
buildImplicitReturn();
370+
return;
371+
}
372+
373+
// End of any local scope != function
374+
// Ternary ops have to deal with matching arms for yielding types
375+
// and do return a value, it must do its own cir.yield insertion.
376+
if (!localScope->isTernary()) {
377+
!retVal ? builder.create<YieldOp>(localScope->EndLoc)
378+
: builder.create<YieldOp>(localScope->EndLoc, retVal);
379+
}
393380
};
394381

395382
// If a cleanup block has been created at some point, branch to it
@@ -434,6 +421,64 @@ void CIRGenFunction::LexicalScope::cleanup() {
434421
insertCleanupAndLeave(currBlock);
435422
}
436423

424+
mlir::cir::ReturnOp
425+
CIRGenFunction::LexicalScope::buildReturn(mlir::Location loc) {
426+
auto &builder = CGF.getBuilder();
427+
428+
// If we are on a coroutine, add the coro_end builtin call.
429+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CGF.CurFn);
430+
assert(Fn && "other callables NYI");
431+
if (Fn.getCoroutine())
432+
CGF.buildCoroEndBuiltinCall(
433+
loc, builder.getNullPtr(builder.getVoidPtrTy(), loc));
434+
435+
if (CGF.FnRetCIRTy.has_value()) {
436+
// If there's anything to return, load it first.
437+
auto val = builder.create<LoadOp>(loc, *CGF.FnRetCIRTy, *CGF.FnRetAlloca);
438+
return builder.create<ReturnOp>(loc, llvm::ArrayRef(val.getResult()));
439+
}
440+
return builder.create<ReturnOp>(loc);
441+
}
442+
443+
void CIRGenFunction::LexicalScope::buildImplicitReturn() {
444+
auto &builder = CGF.getBuilder();
445+
auto *localScope = CGF.currLexScope;
446+
447+
const auto *FD = cast<clang::FunctionDecl>(CGF.CurGD.getDecl());
448+
449+
// C++11 [stmt.return]p2:
450+
// Flowing off the end of a function [...] results in undefined behavior
451+
// in a value-returning function.
452+
// C11 6.9.1p12:
453+
// If the '}' that terminates a function is reached, and the value of the
454+
// function call is used by the caller, the behavior is undefined.
455+
if (CGF.getLangOpts().CPlusPlus && !FD->hasImplicitReturnZero() &&
456+
!CGF.SawAsmBlock && !FD->getReturnType()->isVoidType() &&
457+
builder.getInsertionBlock()) {
458+
bool shouldEmitUnreachable = CGF.CGM.getCodeGenOpts().StrictReturn ||
459+
!CGF.CGM.MayDropFunctionReturn(
460+
FD->getASTContext(), FD->getReturnType());
461+
462+
if (CGF.SanOpts.has(SanitizerKind::Return)) {
463+
assert(!UnimplementedFeature::sanitizerReturn());
464+
llvm_unreachable("NYI");
465+
} else if (shouldEmitUnreachable) {
466+
if (CGF.CGM.getCodeGenOpts().OptimizationLevel == 0) {
467+
// TODO: buildTrapCall(llvm::Intrinsic::trap);
468+
assert(!UnimplementedFeature::trap());
469+
}
470+
}
471+
472+
if (CGF.SanOpts.has(SanitizerKind::Return) || shouldEmitUnreachable) {
473+
builder.create<mlir::cir::UnreachableOp>(localScope->EndLoc);
474+
builder.clearInsertionPoint();
475+
return;
476+
}
477+
}
478+
479+
(void)buildReturn(localScope->EndLoc);
480+
}
481+
437482
void CIRGenFunction::finishFunction(SourceLocation EndLoc) {
438483
// CIRGen doesn't use a BreakContinueStack or evaluates OnlySimpleReturnStmts.
439484

@@ -661,31 +706,6 @@ CIRGenFunction::generateCode(clang::GlobalDecl GD, mlir::cir::FuncOp Fn,
661706
if (mlir::failed(Fn.verifyBody()))
662707
return nullptr;
663708

664-
// C++11 [stmt.return]p2:
665-
// Flowing off the end of a function [...] results in undefined behavior
666-
// in a value-returning function.
667-
// C11 6.9.1p12:
668-
// If the '}' that terminates a function is reached, and the value of the
669-
// function call is used by the caller, the behavior is undefined.
670-
if (getLangOpts().CPlusPlus && !FD->hasImplicitReturnZero() && !SawAsmBlock &&
671-
!FD->getReturnType()->isVoidType() && builder.getInsertionBlock()) {
672-
bool shouldEmitUnreachable =
673-
CGM.getCodeGenOpts().StrictReturn ||
674-
!CGM.MayDropFunctionReturn(FD->getASTContext(), FD->getReturnType());
675-
676-
if (SanOpts.has(SanitizerKind::Return)) {
677-
llvm_unreachable("NYI");
678-
} else if (shouldEmitUnreachable) {
679-
if (CGM.getCodeGenOpts().OptimizationLevel == 0)
680-
; // TODO: buildTrapCall(llvm::Intrinsic::trap);
681-
}
682-
if (SanOpts.has(SanitizerKind::Return) || shouldEmitUnreachable) {
683-
// TODO: builder.createUnreachable();
684-
assert(!UnimplementedFeature::unreachableOp());
685-
builder.clearInsertionPoint();
686-
}
687-
}
688-
689709
// Emit the standard function epilogue.
690710
finishFunction(BodyRange.getEnd());
691711

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,6 +1918,9 @@ class CIRGenFunction : public CIRGenTypeCache {
19181918
return b;
19191919
}
19201920

1921+
mlir::cir::ReturnOp buildReturn(mlir::Location loc);
1922+
void buildImplicitReturn();
1923+
19211924
public:
19221925
void updateCurrentSwitchCaseRegion() { CurrentSwitchRegionIdx++; }
19231926
llvm::ArrayRef<mlir::Block *> getRetBlocks() { return RetBlocks; }

clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct UnimplementedFeature {
5858
static bool pointerOverflowSanitizer() { return false; }
5959
static bool sanitizeDtor() { return false; }
6060
static bool sanitizeVLABound() { return false; }
61+
static bool sanitizerReturn() { return false; }
6162

6263
// ObjC
6364
static bool setObjCGCLValueClass() { return false; }
@@ -160,12 +161,12 @@ struct UnimplementedFeature {
160161
static bool emitScalarRangeCheck() { return false; }
161162
static bool stmtExprEvaluation() { return false; }
162163
static bool setCallingConv() { return false; }
163-
static bool unreachableOp() { return false; }
164164
static bool tryMarkNoThrow() { return false; }
165165
static bool indirectBranch() { return false; }
166166
static bool escapedLocals() { return false; }
167167
static bool deferredReplacements() { return false; }
168168
static bool shouldInstrumentFunction() { return false; }
169+
static bool trap() { return false; }
169170
};
170171
} // namespace cir
171172

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
void ret_void() {}
5+
6+
// CHECK: cir.func @_Z8ret_voidv()
7+
// CHECK-NEXT: cir.return
8+
// CHECK-NEXT: }
9+
10+
int ret_non_void() {}
11+
12+
// CHECK: cir.func @_Z12ret_non_voidv() -> !s32i
13+
// CHECK-NEXT: %0 = cir.alloca !s32i, cir.ptr <!s32i>, ["__retval"]
14+
// CHECK-NEXT: cir.unreachable
15+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)