Skip to content

Commit da198e8

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 0869e81 commit da198e8

File tree

4 files changed

+90
-51
lines changed

4 files changed

+90
-51
lines changed

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

+70-50
Original file line numberDiff line numberDiff line change
@@ -320,22 +320,6 @@ void CIRGenFunction::LexicalScope::cleanup() {
320320
auto &builder = CGF.builder;
321321
auto *localScope = CGF.currLexScope;
322322

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

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

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

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

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

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

clang/lib/CIR/CodeGen/CIRGenFunction.h

+3
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

+2-1
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

+15
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)