[PATCH] D99517: Implemented [[clang::musttail]] attribute for guaranteed tail calls.
Aaron Ballman via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 30 07:11:48 PDT 2021
aaron.ballman added inline comments.
================
Comment at: clang/include/clang/Basic/Attr.td:1356
+def MustTail : StmtAttr {
+ let Spellings = [Clang<"musttail">];
+ let Documentation = [MustTailDocs];
----------------
You should add a `Subjects` list here.
================
Comment at: clang/include/clang/Basic/AttrDocs.td:449
+ let Content = [{
+If a return statement is marked ``musttail``, this indicates that the
+compiler must generate a tail call for the program to be correct, even when
----------------
================
Comment at: clang/include/clang/Basic/AttrDocs.td:454
+
+``clang::musttail`` can only be applied to a return statement whose value is a
+function call (even functions returning void must use 'return', although no
----------------
================
Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:2826-2827
InGroup<IgnoredAttributes>;
+def err_musttail_only_on_return : Error<
+ "%0 attribute can only be applied to a return statement">;
+def err_musttail_needs_call : Error<
----------------
This error should not be necessary if you add the correct subject line to Attr.td
================
Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:2829-2830
+def err_musttail_needs_call : Error<
+ "%0 attribute requires that the return value is a function call, which must "
+ "not create or destroy any temporaries.">;
+def err_musttail_only_from_function : Error<
----------------
================
Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:2832
+def err_musttail_only_from_function : Error<
+ "%0 attribute can only be used from a regular function.">;
+def err_musttail_return_type_mismatch : Error<
----------------
What is a "regular function?"
================
Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:2837
+def err_musttail_no_destruction : Error<
+ "%0 attribute does not allow any variables in scope that require destruction"
+ >;
----------------
All automatic variables require destruction when leaving the scope, so this diagnostic reads oddly to me. Perhaps you mean that the variables must all be trivially destructible?
================
Comment at: clang/include/clang/Sema/ScopeInfo.h:121
+ /// Whether this function contains any statement marked with \c [musttail].
+ bool HasMustTail : 1;
----------------
================
Comment at: clang/lib/CodeGen/CGCall.cpp:5315-5317
+ // TODO(haberman): insert checks/assertions to verify that this early exit
+ // is safe. We tried to verify this in Sema but we should double-check
+ // here.
----------------
Are you planning to handle this TODO in the patch? If not, can you switch to a FIXME without a name associated with it?
================
Comment at: clang/lib/CodeGen/CGCall.cpp:5318-5322
+ if (RetTy->isVoidType()) {
+ Builder.CreateRetVoid();
+ } else {
+ Builder.CreateRet(CI);
+ }
----------------
================
Comment at: clang/lib/CodeGen/CGExpr.cpp:4828
ReturnValueSlot ReturnValue) {
+ SaveAndRestore<bool> save_musttail(InMustTailCallExpr, E == MustTailCall);
+
----------------
================
Comment at: clang/lib/CodeGen/CGStmt.cpp:655-658
+ const ReturnStmt *R = dyn_cast<ReturnStmt>(Sub);
+ assert(R && "musttail should only be on ReturnStmt");
+ musttail = dyn_cast<CallExpr>(R->getRetValue());
+ assert(musttail && "musttail must return CallExpr");
----------------
================
Comment at: clang/lib/Sema/JumpDiagnostics.cpp:1005-1011
+ for (const auto *A : AS->getAttrs()) {
+ if (A->getKind() == attr::MustTail) {
+ return A;
+ }
+ }
+
+ return nullptr;
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:561-568
+ for (const auto *A : Attrs) {
+ if (A->getKind() == attr::MustTail) {
+ if (!checkMustTailAttr(SubStmt, *A)) {
+ return SubStmt;
+ }
+ setFunctionHasMustTail();
+ }
----------------
This functionality belongs in SemaStmtAttr.cpp, I think.
================
Comment at: clang/lib/Sema/SemaStmt.cpp:578-582
+ if (!R) {
+ Diag(St->getBeginLoc(), diag::err_musttail_only_on_return)
+ << MTA.getSpelling();
+ return false;
+ }
----------------
This check should not be necessary once the code moves to SemaStmtAttr.cpp and Attr.td gets a correct subjects line.
================
Comment at: clang/lib/Sema/SemaStmt.cpp:584-591
+ const Expr *Ex = R->getRetValue();
+
+ // We don't actually support tail calling through an implicit cast (we require
+ // the return types to match), but getting the actual function call will let
+ // us give a better error message about the return type mismatch.
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex)) {
+ Ex = ICE->getSubExpr();
----------------
I think you want to ignore parens and implicit casts here. e.g., there's no reason to diagnose code like:
```
int foo();
int bar() {
[[clang::musttail]] return (bar());
}
```
================
Comment at: clang/lib/Sema/SemaStmt.cpp:596
+ if (!CE) {
+ Diag(St->getBeginLoc(), diag::err_musttail_needs_call) << MTA.getSpelling();
+ return false;
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:607
+ Diag(St->getBeginLoc(), diag::err_musttail_only_from_function)
+ << MTA.getSpelling();
+ return false;
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:609-612
+ } else if (CallerDecl->isDependentContext()) {
+ // We have to suspend our check until template instantiation time.
+ return true;
+ }
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:617
+ // type of "this".
+ if (const MemberExpr *mem = dyn_cast<MemberExpr>(Callee)) {
+ // Call is: obj.method() or obj->method()
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:620
+ const CXXMethodDecl *CMD = dyn_cast<CXXMethodDecl>(mem->getMemberDecl());
+ assert(CMD && !CMD->isStatic());
+ CalleeThis = CMD->getThisType()->getPointeeType();
----------------
Is this assertion valid? Consider:
```
struct S {
static int foo();
};
int bar() {
S s;
[[clang::musttail]] return s.foo();
}
```
================
Comment at: clang/lib/Sema/SemaStmt.cpp:640-641
+ }
+ CalleeType = FunctionType->getUnqualifiedDesugaredType()
+ ->castAs<FunctionProtoType>();
+ }
----------------
I think this could assert on K&R C declarations because those don't have a prototype. e.g.,
```
int f(); // Important: compile this as C code, not C++ code
int bar(void) {
[[clang::musttail]] return f();
}
```
================
Comment at: clang/lib/Sema/SemaStmt.cpp:650-656
+ auto TypesMatch = [this](QualType a, QualType b) -> bool {
+ if (a == QualType() || b == QualType()) {
+ return a == b;
+ } else {
+ return Context.hasSimilarType(a, b);
+ }
+ };
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:658
+
+ bool types_match =
+ TypesMatch(CallerDecl->getReturnType(), CalleeType->getReturnType()) &&
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:663-664
+ if (types_match) {
+ ArrayRef<QualType> callee_params = CalleeType->getParamTypes();
+ ArrayRef<ParmVarDecl *> caller_params = CallerDecl->parameters();
+ size_t n = CallerDecl->param_size();
----------------
================
Comment at: clang/lib/Sema/SemaStmt.cpp:665-666
+ ArrayRef<ParmVarDecl *> caller_params = CallerDecl->parameters();
+ size_t n = CallerDecl->param_size();
+ for (size_t i = 0; i < n; i++) {
+ if (!TypesMatch(callee_params[i], caller_params[i]->getType())) {
----------------
How do you want to handle variadic function calls?
================
Comment at: clang/lib/Sema/SemaStmt.cpp:676
+ Diag(St->getBeginLoc(), diag::err_musttail_return_type_mismatch)
+ << MTA.getSpelling();
+ return false;
----------------
================
Comment at: clang/lib/Sema/SemaStmtAttr.cpp:215-217
+ MustTailAttr MTA(S.Context, A);
+ if (S.CheckAttrNoArgs(A))
+ return nullptr;
----------------
None of this should be needed due to the automated diagnostic checking.
================
Comment at: clang/test/CodeGen/attr-musttail.cpp:69
+}
+
+// CHECK: musttail call void @_Z11ReturnsVoidv()
----------------
How about something like:
```
int FuncX() {
[[clang::musttail]] return []() { return 12; }();
}
```
================
Comment at: clang/test/Sema/attr-musttail.cpp:17
+long g(int x);
+int h(long x);
+
----------------
I'd appreciate a test of promotion behavior:
```
int i(int x);
int s(short x);
int whatever1(int x) {
[[clang::musttail]] return s(x);
}
int whatever2(short x) {
[[clang::musttail]] return i(x);
}
```
================
Comment at: clang/test/Sema/attr-musttail.cpp:137
+ [[clang::musttail]] return ObjParam(obj); // expected-error {{musttail attribute requires that the return value is a function call, which must not create or destroy any temporaries}}
+}
----------------
Still to be tested: using a lambda, behavior in C (esp with K&R C functions), and variadic functions.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D99517/new/
https://reviews.llvm.org/D99517
More information about the cfe-commits
mailing list