Skip to content

[AArch64][GlobalISel] Fix incorrect ABI when tail call not supported #70215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 30, 2023

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Oct 25, 2023

The check for whether a tail call is supported calls determineAssignments(), which may modify argument flags. As such, even though the check fails and a non-tail call will be emitted, it will not have a different (incorrect) ABI.

Fix this by operating on a separate copy of the arguments.

Fixes #70207.

The check for whether a tail call is supported calls
determineAssignments(), which may modify argument flags. As such,
even though the check fails and a non-tail call will be emitted,
it will not have a different (incorrect) ABI.

Fix this by operating on a separate copy of the arguments.
@llvmbot
Copy link
Member

llvmbot commented Oct 25, 2023

@llvm/pr-subscribers-backend-aarch64

@llvm/pr-subscribers-llvm-globalisel

Author: Nikita Popov (nikic)

Changes

The check for whether a tail call is supported calls determineAssignments(), which may modify argument flags. As such, even though the check fails and a non-tail call will be emitted, it will not have a different (incorrect) ABI.

Fix this by operating on a separate copy of the arguments.

Fixes #70207.


Full diff: https://github.com/llvm/llvm-project/pull/70215.diff

2 Files Affected:

  • (modified) llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp (+4-1)
  • (modified) llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-tail-call-fallback.ll (+7-11)
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp
index 2d6cc870f98e77f..02a0c58c8fd0f64 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp
@@ -854,7 +854,10 @@ bool AArch64CallLowering::areCalleeOutgoingArgsTailCallable(
 
   AArch64OutgoingValueAssigner CalleeAssigner(AssignFnFixed, AssignFnVarArg,
                                               Subtarget, /*IsReturn*/ false);
-  if (!determineAssignments(CalleeAssigner, OutArgs, OutInfo)) {
+  // determineAssignments() may modify argument flags, so make a copy.
+  SmallVector<ArgInfo, 8> OutArgsCopy;
+  append_range(OutArgsCopy, OutArgs);
+  if (!determineAssignments(CalleeAssigner, OutArgsCopy, OutInfo)) {
     LLVM_DEBUG(dbgs() << "... Could not analyze call operands.\n");
     return false;
   }
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-tail-call-fallback.ll b/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-tail-call-fallback.ll
index fc6eefb4016b66d..ebd2beca6781050 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-tail-call-fallback.ll
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-tail-call-fallback.ll
@@ -3,30 +3,26 @@
 
 declare void @func(i64, i64, i64, i64, i64, i128, i128)
 
-; FIXME: This is a miscompile.
 ; Make sure the check for whether a tail call is allowed does not affect the
 ; calling convention if it fails.
 ; The first i128 argument should be passed in registers, not on the stack.
 define void @pr70207(i128 %arg1, i128 %arg2) nounwind {
 ; CHECK-LABEL: pr70207:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    sub sp, sp, #64
+; CHECK-NEXT:    mov x8, x2
 ; CHECK-NEXT:    mov x6, x0
-; CHECK-NEXT:    mov x8, x1
-; CHECK-NEXT:    mov x9, x2
-; CHECK-NEXT:    mov x10, x3
+; CHECK-NEXT:    mov x7, x1
+; CHECK-NEXT:    mov x9, x3
 ; CHECK-NEXT:    mov x0, xzr
 ; CHECK-NEXT:    mov x1, xzr
 ; CHECK-NEXT:    mov x2, xzr
 ; CHECK-NEXT:    mov x3, xzr
 ; CHECK-NEXT:    mov x4, xzr
-; CHECK-NEXT:    str x30, [sp, #48] // 8-byte Folded Spill
-; CHECK-NEXT:    str x8, [sp]
-; CHECK-NEXT:    str x9, [sp, #16]
-; CHECK-NEXT:    str x10, [sp, #32]
+; CHECK-NEXT:    str x8, [sp, #-32]!
+; CHECK-NEXT:    stp x9, x30, [sp, #8] // 8-byte Folded Spill
 ; CHECK-NEXT:    bl func
-; CHECK-NEXT:    ldr x30, [sp, #48] // 8-byte Folded Reload
-; CHECK-NEXT:    add sp, sp, #64
+; CHECK-NEXT:    ldr x30, [sp, #16] // 8-byte Folded Reload
+; CHECK-NEXT:    add sp, sp, #32
 ; CHECK-NEXT:    ret
   tail call void @func(i64 0, i64 0, i64 0, i64 0, i64 0, i128 %arg1, i128 %arg2)
   ret void

@tschuett
Copy link

The documentation of determineAssignments states that it manipulates its arguments. This looks hacky. Probably determineAssignments should return an optional instead. Mutable arguments are an anti pattern.

@aemerson
Copy link
Contributor

The documentation of determineAssignments states that it manipulates its arguments. This looks hacky. Probably determineAssignments should return an optional instead. Mutable arguments are an anti pattern.

I don't think we have to be too dogmatic about that. Unnecessary array copies also (minutely) impact compile time which we'd be paying the cost of on every call to determineAssignments if we returned an optional. An alternative would be to introduce a new wrapper that's only used for querying and doesn't mutate its arguments, hiding the copying of the input from the caller. Something like canDetermineAssignments()?

@nikic
Copy link
Contributor Author

nikic commented Oct 26, 2023

Just to clarify, determineAssignments() does succeed here, it's just that following checks fail and the function has already changed state. Just a boolean canDetermineAssignments() query wouldn't be enough for use in this function, we do need the result as well.

// determineAssignments() may modify argument flags, so make a copy.
SmallVector<ArgInfo, 8> OutArgsCopy;
append_range(OutArgsCopy, OutArgs);
if (!determineAssignments(CalleeAssigner, OutArgsCopy, OutInfo)) {
LLVM_DEBUG(dbgs() << "... Could not analyze call operands.\n");
return false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a boolean canDetermineAssignments() query wouldn't be enough for use in this function, we do need the result as well.

I interpret this to mean that the mutations performed by determineAssignments are necessary when this function succeeds.

But in that case, I would expect to see *OutArgs = OutArgsCopy; or similar at the end of this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I was referring to here is that OutInfo is used by further checks in this function. Though after your comment I saw that OutArgs is also used, so I've tweaked the code to make sure it gets the modified version, not the original one (I'm not sure whether this actually matters here, but better safe than sorry).

We don't need the result of determineAssignments() to persist outside this function (that is, the areCalleeOutgoingArgsTailCallable function). The actual tail call emission will call determineAssignments() again.

Copy link
Contributor

@aemerson aemerson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's take this change for now and leave any refactoring to another patch.

@nikic nikic merged commit 292f34b into llvm:main Oct 30, 2023
tru pushed a commit that referenced this pull request Oct 31, 2023
…70215)

The check for whether a tail call is supported calls
determineAssignments(), which may modify argument flags. As such, even
though the check fails and a non-tail call will be emitted, it will not
have a different (incorrect) ABI.

Fix this by operating on a separate copy of the arguments.

Fixes #70207.

(cherry picked from commit 292f34b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[AArch64][GISel] Incorrect ABI for calls with tail marker
5 participants