[CIR] Implement most of 'musttail' statement attr (#213154)
This implements the basics, and leaves NYI in a few places (particularly those that require cleanup calculations). There IS some additional work that needs to be done when the ABI work is put in place as well, and the thunk 'tail' hint isn't implemented either, and is left as a missing-feature. This is necessary for compiling the bytecode interpreter in Clang in a few places during self-build.
This commit is contained in:
parent
a1d93f19be
commit
06361fb2ef
11 changed files with 312 additions and 24 deletions
|
|
@ -91,7 +91,7 @@ struct MissingFeatures {
|
|||
static bool opCallReturn() { return false; }
|
||||
static bool opCallArgEvaluationOrder() { return false; }
|
||||
static bool opCallCallConv() { return false; }
|
||||
static bool opCallMustTail() { return false; }
|
||||
static bool opCallThunkTailHint() { return false; }
|
||||
static bool opCallInAlloca() { return false; }
|
||||
static bool opCallAttrs() { return false; }
|
||||
static bool opCallSurroundingTry() { return false; }
|
||||
|
|
|
|||
|
|
@ -1170,7 +1170,8 @@ static RValue emitAtomicLibCall(CIRGenFunction &cgf, llvm::StringRef funcName,
|
|||
|
||||
cir::FuncOp fn = cgf.cgm.createRuntimeFunction(fnTy, funcName, fnAttrs);
|
||||
auto callee = CIRGenCallee::forDirect(fn);
|
||||
return cgf.emitCall(fnInfo, callee, ReturnValueSlot(), args);
|
||||
return cgf.emitCall(fnInfo, callee, ReturnValueSlot(), args,
|
||||
/*isMustTail=*/false);
|
||||
}
|
||||
|
||||
static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e,
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ void CIRGenNVCUDARuntime::emitDeviceStubBodyNew(CIRGenFunction &cgf,
|
|||
const CIRGenFunctionInfo &callInfo =
|
||||
cgm.getTypes().arrangeFunctionDeclaration(cudaLaunchKernelFD);
|
||||
cgf.emitCall(callInfo, CIRGenCallee::forDirect(cudaKernelLauncherFn),
|
||||
ReturnValueSlot(), launchArgs);
|
||||
ReturnValueSlot(), launchArgs, /*isMustTail=*/false);
|
||||
|
||||
if (cgm.getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
|
||||
!cgf.getLangOpts().HIP)
|
||||
|
|
|
|||
|
|
@ -1173,7 +1173,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
|
|||
ReturnValueSlot returnValue,
|
||||
const CallArgList &args,
|
||||
cir::CIRCallOpInterface *callOp,
|
||||
mlir::Location loc) {
|
||||
bool isMustTail, mlir::Location loc) {
|
||||
QualType retTy = funcInfo.getReturnType();
|
||||
cir::FuncType cirFuncTy = getTypes().getFunctionType(funcInfo);
|
||||
|
||||
|
|
@ -1350,7 +1350,41 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
|
|||
if (callOp)
|
||||
*callOp = theCall;
|
||||
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
if (isMustTail) {
|
||||
// PPC/MIPS have some diagnostics for classic-codegen, but we don't support
|
||||
// them yet.
|
||||
const llvm::Triple &triple = getTarget().getTriple();
|
||||
if (triple.isPPC() || triple.isMIPS()) {
|
||||
cgm.errorNYI(mustTailCall->getBeginLoc(),
|
||||
"musttail call target legality checks");
|
||||
return getUndefRValue(retTy);
|
||||
}
|
||||
|
||||
// Musttail is required to return immediately. Classic codegen does some
|
||||
// work here to go through the exception handling scopes to put them before
|
||||
// the call (it seems?) since musttail must be the last op before the
|
||||
// return. For now, skip this so we an do it later.
|
||||
if (ehStack.stable_begin() != prologueCleanupDepth) {
|
||||
cgm.errorNYI(mustTailCall->getBeginLoc(),
|
||||
"musttail call that skips cleanups");
|
||||
return getUndefRValue(retTy);
|
||||
}
|
||||
|
||||
theCall->setAttr(cir::CIRDialect::getMustTailAttrName(),
|
||||
builder.getUnitAttr());
|
||||
|
||||
if (isa<cir::VoidType>(convertType(retTy)))
|
||||
cir::ReturnOp::create(builder, loc);
|
||||
else
|
||||
cir::ReturnOp::create(builder, loc, theCall->getResult(0));
|
||||
|
||||
// Musttail must return immediately, so we just do that. All of the below
|
||||
// stuff is effectively UB if this is a musttail, so just do a return
|
||||
// immediately.
|
||||
builder.createBlock(builder.getBlock()->getParent());
|
||||
return getUndefRValue(retTy);
|
||||
}
|
||||
|
||||
assert(!cir::MissingFeatures::opCallReturn());
|
||||
|
||||
mlir::Type retCIRTy = convertType(retTy);
|
||||
|
|
|
|||
|
|
@ -958,7 +958,8 @@ void CIRGenFunction::emitForwardingCallToLambda(
|
|||
// Now emit our call.
|
||||
CIRGenCallee callee =
|
||||
CIRGenCallee::forDirect(calleePtr, GlobalDecl(callOperator));
|
||||
RValue rv = emitCall(calleeFnInfo, callee, returnSlot, callArgs);
|
||||
RValue rv = emitCall(calleeFnInfo, callee, returnSlot, callArgs,
|
||||
/*isMustTail=*/false);
|
||||
|
||||
// Forward the returned value through the function's return slot.
|
||||
if (!resultType->isVoidType()) {
|
||||
|
|
@ -1555,7 +1556,8 @@ void CIRGenFunction::emitCXXConstructorCall(
|
|||
args, d, type, extraArgs.prefix, extraArgs.suffix, passPrototypeArgs);
|
||||
CIRGenCallee callee = CIRGenCallee::forDirect(calleePtr, GlobalDecl(d, type));
|
||||
cir::CIRCallOpInterface c;
|
||||
emitCall(info, callee, ReturnValueSlot(), args, &c, getLoc(loc));
|
||||
emitCall(info, callee, ReturnValueSlot(), args, &c, /*isMustTail=*/false,
|
||||
getLoc(loc));
|
||||
|
||||
if (cgm.getCodeGenOpts().OptimizationLevel != 0 && !crd->isDynamicClass() &&
|
||||
type != Ctor_Base && cgm.getCodeGenOpts().StrictVTablePointers)
|
||||
|
|
|
|||
|
|
@ -2426,11 +2426,10 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
|
|||
|
||||
assert(!cir::MissingFeatures::opCallFnInfoOpts());
|
||||
assert(!cir::MissingFeatures::hip());
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
|
||||
cir::CIRCallOpInterface callOp;
|
||||
RValue callResult = emitCall(funcInfo, callee, returnValue, args, &callOp,
|
||||
getLoc(e->getExprLoc()));
|
||||
e == mustTailCall, getLoc(e->getExprLoc()));
|
||||
|
||||
assert(!cir::MissingFeatures::generateDebugInfo());
|
||||
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ CIRGenFunction::emitCXXMemberPointerCallExpr(const CXXMemberCallExpr *ce,
|
|||
|
||||
// Build the call.
|
||||
CIRGenCallee callee(fpt, calleePtr.getDefiningOp());
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
return emitCall(cgm.getTypes().arrangeCXXMethodCall(argsList, fpt, required,
|
||||
/*PrefixSize=*/0),
|
||||
callee, returnValue, argsList, nullptr, loc);
|
||||
callee, returnValue, argsList, nullptr, ce == mustTailCall,
|
||||
loc);
|
||||
}
|
||||
|
||||
RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
|
||||
|
|
@ -322,8 +322,8 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
|
|||
args, fpt, callInfo.reqArgs, callInfo.prefixSize);
|
||||
assert((ce || currSrcLoc) && "expected source location");
|
||||
mlir::Location loc = ce ? getLoc(ce->getExprLoc()) : *currSrcLoc;
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
|
||||
return emitCall(fnInfo, callee, returnValue, args, nullptr,
|
||||
ce && ce == mustTailCall, loc);
|
||||
}
|
||||
|
||||
static void emitNullBaseClassInitialization(CIRGenFunction &cgf,
|
||||
|
|
@ -756,9 +756,9 @@ static RValue emitNewDeleteCall(CIRGenFunction &cgf,
|
|||
cir::FuncOp calleePtr = cgf.cgm.getAddrOfFunction(calleeDecl);
|
||||
CIRGenCallee callee =
|
||||
CIRGenCallee::forDirect(calleePtr, GlobalDecl(calleeDecl));
|
||||
RValue rv =
|
||||
cgf.emitCall(cgf.cgm.getTypes().arrangeFreeFunctionCall(args, calleeType),
|
||||
callee, ReturnValueSlot(), args, &callOrTryCall);
|
||||
RValue rv = cgf.emitCall(
|
||||
cgf.cgm.getTypes().arrangeFreeFunctionCall(args, calleeType), callee,
|
||||
ReturnValueSlot(), args, /*isMustTail=*/false, &callOrTryCall);
|
||||
|
||||
/// C++1y [expr.new]p10:
|
||||
/// [In a new-expression,] an implementation is allowed to omit a call
|
||||
|
|
@ -1367,9 +1367,8 @@ RValue CIRGenFunction::emitCXXDestructorCall(
|
|||
commonBuildCXXMemberOrOperatorCall(*this, dtorDecl, thisVal, implicitParam,
|
||||
implicitParamTy, ce, args, nullptr);
|
||||
assert((ce || dtor.getDecl()) && "expected source location provider");
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
return emitCall(cgm.getTypes().arrangeCXXStructorDeclaration(dtor), callee,
|
||||
ReturnValueSlot(), args, nullptr,
|
||||
ReturnValueSlot(), args, nullptr, ce && ce == mustTailCall,
|
||||
ce ? getLoc(ce->getExprLoc())
|
||||
: getLoc(dtor.getDecl()->getSourceRange()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -586,6 +586,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// The CallExpr within the current statement that the musttail attribute
|
||||
// applies to. nullptr if there is no 'musttail' on the current statement.
|
||||
const CallExpr *mustTailCall = nullptr;
|
||||
|
||||
struct VlaSizePair {
|
||||
mlir::Value numElts;
|
||||
QualType type;
|
||||
|
|
@ -1786,14 +1790,14 @@ public:
|
|||
RValue emitCall(const CIRGenFunctionInfo &funcInfo,
|
||||
const CIRGenCallee &callee, ReturnValueSlot returnValue,
|
||||
const CallArgList &args, cir::CIRCallOpInterface *callOp,
|
||||
mlir::Location loc);
|
||||
bool isMustTail, mlir::Location loc);
|
||||
RValue emitCall(const CIRGenFunctionInfo &funcInfo,
|
||||
const CIRGenCallee &callee, ReturnValueSlot returnValue,
|
||||
const CallArgList &args,
|
||||
const CallArgList &args, bool isMustTail,
|
||||
cir::CIRCallOpInterface *callOrTryCall = nullptr) {
|
||||
assert(currSrcLoc && "source location must have been set");
|
||||
return emitCall(funcInfo, callee, returnValue, args, callOrTryCall,
|
||||
*currSrcLoc);
|
||||
isMustTail, *currSrcLoc);
|
||||
}
|
||||
|
||||
RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "clang/AST/StmtOpenACC.h"
|
||||
#include "clang/AST/StmtOpenMP.h"
|
||||
#include "clang/CIR/MissingFeatures.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::CIRGen;
|
||||
|
|
@ -87,6 +88,9 @@ mlir::LogicalResult CIRGenFunction::emitCompoundStmtWithoutScope(
|
|||
|
||||
mlir::LogicalResult
|
||||
CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
|
||||
|
||||
const CallExpr *musttail = nullptr;
|
||||
|
||||
for (const Attr *attr : s.getAttrs()) {
|
||||
switch (attr->getKind()) {
|
||||
default:
|
||||
|
|
@ -95,12 +99,17 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
|
|||
case attr::NoInline:
|
||||
case attr::AlwaysInline:
|
||||
case attr::NoConvergent:
|
||||
case attr::MustTail:
|
||||
case attr::Atomic:
|
||||
case attr::HLSLControlFlowHint:
|
||||
cgm.errorNYI(s.getSourceRange(),
|
||||
"Unimplemented statement attribute: ", attr->getKind());
|
||||
break;
|
||||
case attr::MustTail: {
|
||||
const Stmt *sub = s.getSubStmt();
|
||||
const ReturnStmt *ret = cast<ReturnStmt>(sub);
|
||||
musttail = cast<CallExpr>(ret->getRetValue()->IgnoreParens());
|
||||
break;
|
||||
}
|
||||
case attr::CXXAssume: {
|
||||
const Expr *assumptionExpr = cast<CXXAssumeAttr>(attr)->getAssumption();
|
||||
if (getLangOpts().CXXAssumptions && builder.getInsertionBlock() &&
|
||||
|
|
@ -114,6 +123,8 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
|
|||
}
|
||||
}
|
||||
|
||||
SaveAndRestore save_musttail(mustTailCall, musttail);
|
||||
|
||||
return emitStmt(s.getSubStmt(), /*useCurrentScope=*/true, s.getAttrs());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -744,13 +744,13 @@ void CIRGenFunction::emitCallAndReturnForThunk(cir::FuncOp callee,
|
|||
CIRGenCallee cirCallee = CIRGenCallee::forDirect(callee, curGD);
|
||||
mlir::Location loc = builder.getUnknownLoc();
|
||||
RValue rv = emitCall(*curFnInfo, cirCallee, slot, callArgs,
|
||||
/*callOrTryCall=*/nullptr, loc);
|
||||
/*callOrTryCall=*/nullptr, /*isMustTail=*/false, loc);
|
||||
|
||||
// Consider return adjustment if we have ThunkInfo.
|
||||
if (thunk && !thunk->Return.isEmpty())
|
||||
rv = performReturnAdjustment(*this, resultType, rv, *thunk);
|
||||
else
|
||||
assert(!cir::MissingFeatures::opCallMustTail());
|
||||
assert(!cir::MissingFeatures::opCallThunkTailHint());
|
||||
|
||||
// Emit return. For aggregate returns the call has already written the
|
||||
// result through the slot bound to returnValue above; emit the
|
||||
|
|
|
|||
238
clang/test/CIR/CodeGen/attr-musttail.cpp
Normal file
238
clang/test/CIR/CodeGen/attr-musttail.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
|
||||
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
|
||||
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
|
||||
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
|
||||
|
||||
int Bar(int);
|
||||
int Baz(int);
|
||||
|
||||
int Func1(int x) {
|
||||
if (x)
|
||||
[[clang::musttail]] return Bar(x);
|
||||
else
|
||||
[[clang::musttail]] return Baz(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z5Func1i
|
||||
// CIR: %[[R1:.+]] = cir.call @_Z3Bari(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R1]] : !s32i
|
||||
// CIR: %[[R2:.+]] = cir.call @_Z3Bazi(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R2]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z5Func1i
|
||||
// LLVM: %[[C1:.+]] = musttail call{{.*}} i32 @_Z3Bari(i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[C1]]
|
||||
// LLVM: %[[C2:.+]] = musttail call{{.*}} i32 @_Z3Bazi(i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[C2]]
|
||||
|
||||
int Nested(int x) {
|
||||
[[clang::musttail]] return Bar(Bar(x));
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z6Nestedi
|
||||
// CIR: %[[INNER:.+]] = cir.call @_Z3Bari(%{{.+}}) : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: %[[OUTER:.+]] = cir.call @_Z3Bari(%[[INNER]]) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[OUTER]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z6Nestedi
|
||||
// LLVM: %[[INNER:.+]] = call{{.*}} i32 @_Z3Bari
|
||||
// LLVM: %[[OUTER:.+]] = musttail call{{.*}} i32 @_Z3Bari(i32{{.*}} %[[INNER]])
|
||||
// LLVM-NEXT: ret i32 %[[OUTER]]
|
||||
|
||||
int Scoped(int x) {
|
||||
{ [[clang::musttail]] return Bar(x); }
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z6Scopedi
|
||||
// CIR: %[[R:.+]] = cir.call @_Z3Bari(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z6Scopedi
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_Z3Bari
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
void ReturnsVoid();
|
||||
void VoidTail() {
|
||||
[[clang::musttail]] return ReturnsVoid();
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z8VoidTailv
|
||||
// CIR: cir.call @_Z11ReturnsVoidv() musttail : () -> ()
|
||||
// CIR-NEXT: cir.return
|
||||
|
||||
// LLVM-LABEL: define{{.*}} void @_Z8VoidTailv
|
||||
// LLVM: musttail call void @_Z11ReturnsVoidv()
|
||||
// LLVM-NEXT: ret void
|
||||
|
||||
void IndirectLocal(int x) {
|
||||
void (*p)(int) = nullptr;
|
||||
[[clang::musttail]] return p(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z13IndirectLocali
|
||||
// CIR: cir.call %{{.+}}(%{{.+}}) musttail : (!cir.ptr<!cir.func<(!s32i)>>, !s32i{{.*}}) -> ()
|
||||
// CIR-NEXT: cir.return
|
||||
|
||||
// LLVM-LABEL: define{{.*}} void @_Z13IndirectLocali
|
||||
// LLVM: musttail call void %{{.+}}(i32{{.*}})
|
||||
// LLVM-NEXT: ret void
|
||||
|
||||
struct Data {
|
||||
int (*fptr)(Data *);
|
||||
};
|
||||
int IndirectField(Data *data) {
|
||||
[[clang::musttail]] return data->fptr(data);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z13IndirectFieldP4Data
|
||||
// CIR: %[[R:.+]] = cir.call %{{.+}}(%{{.+}}) musttail : (!cir.ptr<{{.*}}>, !cir.ptr<!rec_Data>{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z13IndirectFieldP4Data
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 %{{.+}}(ptr{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
struct Foo {
|
||||
int MemberFunction(int x);
|
||||
static int StaticMethod(int x);
|
||||
int TailFrom(int x);
|
||||
int TailFrom2(int x);
|
||||
};
|
||||
|
||||
int Foo::TailFrom(int x) {
|
||||
[[clang::musttail]] return MemberFunction(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_ZN3Foo8TailFromEi
|
||||
// CIR: %[[R:.+]] = cir.call @_ZN3Foo14MemberFunctionEi(%{{.+}}, %{{.+}}) musttail : (!cir.ptr<!rec_Foo>{{.*}}, !s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_ZN3Foo8TailFromEi
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_ZN3Foo14MemberFunctionEi(ptr{{.*}}, i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
int StaticViaClass(int x) {
|
||||
[[clang::musttail]] return Foo::StaticMethod(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z14StaticViaClassi
|
||||
// CIR: %[[R:.+]] = cir.call @_ZN3Foo12StaticMethodEi(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z14StaticViaClassi
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_ZN3Foo12StaticMethodEi(i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
int StaticViaObject(int x) {
|
||||
Foo foo;
|
||||
[[clang::musttail]] return foo.StaticMethod(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z15StaticViaObjecti
|
||||
// CIR: %[[R:.+]] = cir.call @_ZN3Foo12StaticMethodEi(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z15StaticViaObjecti
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_ZN3Foo12StaticMethodEi(i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
template <class T> int TplBody(int x) {
|
||||
T t;
|
||||
[[clang::musttail]] return Bar(x);
|
||||
}
|
||||
int InstBody(int x) { return TplBody<int>(x); }
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z7TplBodyIiEii
|
||||
// CIR: %[[R:.+]] = cir.call @_Z3Bari(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z7TplBodyIiEii
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_Z3Bari
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
template <class T> T TplQualified(T x) {
|
||||
[[clang::musttail]] return ::Bar(x);
|
||||
}
|
||||
int InstQualified(int x) { return TplQualified<int>(x); }
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z12TplQualifiedIiET_S0_
|
||||
// CIR: %[[R:.+]] = cir.call @_Z3Bari(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z12TplQualifiedIiET_S0_
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_Z3Bari
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
struct TrivialDtor {};
|
||||
int ReturnsInt(int x);
|
||||
int TrivialLocal(int x) {
|
||||
TrivialDtor foo;
|
||||
[[clang::musttail]] return ReturnsInt(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z12TrivialLocali
|
||||
// CIR: %[[R:.+]] = cir.call @_Z10ReturnsInti(%{{.+}}) musttail : (!s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z12TrivialLocali
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 @_Z10ReturnsInti
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
// FIXME(cir): This requires a cleanup scope of some sort, but is NYI.
|
||||
// int VlaScope(int x) {
|
||||
// int vla[x];
|
||||
// [[clang::musttail]] return Bar(x);
|
||||
// }
|
||||
|
||||
int (Foo::*pmf)(int);
|
||||
int Foo::TailFrom2(int x) {
|
||||
[[clang::musttail]] return (this->*pmf)(x);
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_ZN3Foo9TailFrom2Ei
|
||||
// CIR: %[[R:.+]] = cir.call %{{.+}}(%{{.+}}, %{{.+}}) musttail : (!cir.ptr<{{.*}}>, !cir.ptr<!void>{{.*}}, !s32i{{.*}}) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_ZN3Foo9TailFrom2Ei
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 %{{.+}}(ptr{{.*}}, i32{{.*}})
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
struct V {
|
||||
virtual void t();
|
||||
virtual void f();
|
||||
};
|
||||
void V::f() {
|
||||
[[clang::musttail]] return t();
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_ZN1V1fEv
|
||||
// CIR: cir.call %{{.+}}(%{{.+}}) musttail : (!cir.ptr<{{.*}}>, !cir.ptr<!rec_V>{{.*}}) -> ()
|
||||
// CIR-NEXT: cir.return
|
||||
|
||||
// LLVM-LABEL: define{{.*}} void @_ZN1V1fEv
|
||||
// LLVM: musttail call void %{{.+}}(ptr{{.*}})
|
||||
// LLVM-NEXT: ret void
|
||||
|
||||
// FIXME(cir): Another one that requires cleanups.
|
||||
// struct NT { NT(const NT &); char d[32]; };
|
||||
// NT RCBV();
|
||||
// NT SretTail() {
|
||||
// [[clang::musttail]] return RCBV();
|
||||
// }
|
||||
|
||||
int Lam() {
|
||||
auto l = []() { return 12; };
|
||||
[[clang::musttail]] return (+l)();
|
||||
}
|
||||
|
||||
// CIR-LABEL: cir.func{{.*}} @_Z3Lamv
|
||||
// CIR: %{{.+}} = cir.call @_ZZ3LamvENK3$_0cvPFivEEv(%{{.+}}){{.*}} : (!cir.ptr<{{.*}}>{{.*}}) -> (!cir.ptr<!cir.func<() -> !s32i>>{{.*}})
|
||||
// CIR: %[[R:.+]] = cir.call %{{.+}}() musttail : (!cir.ptr<!cir.func<() -> !s32i>>) -> (!s32i{{.*}})
|
||||
// CIR-NEXT: cir.return %[[R]] : !s32i
|
||||
|
||||
// LLVM-LABEL: define{{.*}} i32 @_Z3Lamv
|
||||
// LLVM: %[[R:.+]] = musttail call{{.*}} i32 %{{.+}}()
|
||||
// LLVM-NEXT: ret i32 %[[R]]
|
||||
|
||||
Loading…
Reference in a new issue