Skip to content

Commit 22388c1

Browse files
authored
[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 b3d0d3c commit 22388c1

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
@@ -319,22 +319,6 @@ void CIRGenFunction::LexicalScope::cleanup() {
319319
auto &builder = CGF.builder;
320320
auto *localScope = CGF.currLexScope;
321321

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

380-
if (localScope->Depth != 0) { // end of any local scope != function
381-
// Ternary ops have to deal with matching arms for yielding types
382-
// and do return a value, it must do its own cir.yield insertion.
383-
if (!localScope->isTernary()) {
384-
!retVal ? builder.create<YieldOp>(localScope->EndLoc)
385-
: builder.create<YieldOp>(localScope->EndLoc, retVal);
386-
}
387-
} else
388-
(void)buildReturn(localScope->EndLoc);
364+
if (localScope->Depth == 0) {
365+
buildImplicitReturn();
366+
return;
367+
}
368+
369+
// End of any local scope != function
370+
// Ternary ops have to deal with matching arms for yielding types
371+
// and do return a value, it must do its own cir.yield insertion.
372+
if (!localScope->isTernary()) {
373+
!retVal ? builder.create<YieldOp>(localScope->EndLoc)
374+
: builder.create<YieldOp>(localScope->EndLoc, retVal);
375+
}
389376
};
390377

391378
// If a cleanup block has been created at some point, branch to it
@@ -430,6 +417,64 @@ void CIRGenFunction::LexicalScope::cleanup() {
430417
insertCleanupAndLeave(currBlock);
431418
}
432419

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

@@ -657,31 +702,6 @@ CIRGenFunction::generateCode(clang::GlobalDecl GD, mlir::cir::FuncOp Fn,
657702
if (mlir::failed(Fn.verifyBody()))
658703
return nullptr;
659704

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

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-enable -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)