-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LangRef] Disallow accessing byval arguments from tail-called functions #110093
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
Conversation
We already disallow accessing the callee's allocas from a tail-called function, because their stack memory will have been de-allocated before the tail call. I think this should apply to byval arguments too, as they also occupy space in the celler's stack frame.
@llvm/pr-subscribers-backend-arm @llvm/pr-subscribers-llvm-ir Author: Oliver Stannard (ostannard) ChangesWe already disallow accessing the callee's allocas from a tail-called function, because their stack memory will have been de-allocated before the tail call. I think this should apply to byval arguments too, as they also occupy space in the caller's stack frame. This was originally part of #109943, spilt out for separate review. Full diff: https://github.com/llvm/llvm-project/pull/110093.diff 2 Files Affected:
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 91c3e60bb0acb1..4cb2ddf9a01ac7 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -12658,10 +12658,55 @@ This instruction requires several arguments:
the return value of the callee is returned to the caller's caller, even
if a void return type is in use.
- Both markers imply that the callee does not access allocas from the caller.
- The ``tail`` marker additionally implies that the callee does not access
- varargs from the caller. Calls marked ``musttail`` must obey the following
- additional rules:
+ Both markers imply that the callee does not access allocas or varargs from
+ the caller. They also imply that the callee does not access the memory
+ pointed to by a byval argument to the caller, unless that pointer is passed
+ to the callee as a byval argument. For example:
+
+.. code-block:: llvm
+
+ declare void @take_byval(ptr byval(i64))
+ declare void @take_ptr(ptr)
+
+ ; Invalid, %local may be de-allocated before call to @take_ptr.
+ define void @invalid_alloca() {
+ entry:
+ %local = alloca i64
+ tail call void @take_ptr(ptr %local)
+ ret void
+ }
+
+ ; Invalid, because @use_global_va_list uses the variadic arguments from @invalid_va_list.
+ %struct.va_list = type { ptr }
+ @va_list = external global %struct.va_list
+ define void @use_global_va_list() {
+ entry:
+ %arg = va_arg ptr @va_list, i64
+ ret void
+ }
+ define void @invalid_va_list(i32 %a, ...) {
+ entry:
+ call void @llvm.va_start.p0(ptr @va_list)
+ tail call void @use_global_va_list()
+ ret void
+ }
+
+ ; Valid, byval argument forwarded to tail call as another byval argument.
+ define void @forward_byval(ptr byval(i64) %x) {
+ entry:
+ tail call void @take_byval(ptr byval(i64) %x)
+ ret void
+ }
+
+ ; Invalid, byval argument passed to tail callee as non-byval ptr.
+ define void @invalid_byval(ptr byval(i64) %x) {
+ entry:
+ tail call void @take_ptr(ptr %x)
+ ret void
+ }
+
+
+ Calls marked ``musttail`` must obey the following additional rules:
- The call must immediately precede a :ref:`ret <i_ret>` instruction,
or a pointer bitcast followed by a ret instruction.
diff --git a/llvm/test/CodeGen/ARM/struct_byval.ll b/llvm/test/CodeGen/ARM/struct_byval.ll
index 73a1b5ee33bca9..2bc4f9c816d539 100644
--- a/llvm/test/CodeGen/ARM/struct_byval.ll
+++ b/llvm/test/CodeGen/ARM/struct_byval.ll
@@ -63,25 +63,6 @@ declare i32 @e1(ptr nocapture byval(%struct.SmallStruct) %in) nounwind
declare i32 @e2(ptr nocapture byval(%struct.LargeStruct) %in) nounwind
declare i32 @e3(ptr nocapture byval(%struct.LargeStruct) align 16 %in) nounwind
-; rdar://12442472
-; We can't do tail call since address of s is passed to the callee and part of
-; s is in caller's local frame.
-define void @f3(ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
-; CHECK-LABEL: f3
-; CHECK: bl _consumestruct
-entry:
- tail call void @consumestruct(ptr %s, i32 80) optsize
- ret void
-}
-
-define void @f4(ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
-; CHECK-LABEL: f4
-; CHECK: bl _consumestruct
-entry:
- tail call void @consumestruct(ptr %s, i32 80) optsize
- ret void
-}
-
; We can do tail call here since s is in the incoming argument area.
define void @f5(i32 %a, i32 %b, i32 %c, i32 %d, ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
; CHECK-LABEL: f5
|
; CHECK-LABEL: f3 | ||
; CHECK: bl _consumestruct | ||
entry: | ||
tail call void @consumestruct(ptr %s, i32 80) optsize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to delete these tests; they have well-defined behavior. Maybe add a comment if you're concerned someone reading the test will get confused. (If @consumestruct
tried to access memory through the pointer, that would be wrong, but it doesn't, as far as the compiler can tell.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to delete them, because the comment above says they are testing something not guaranteed by the LangRef, and with #109943 they will actually be tail-called.
Both markers imply that the callee does not access allocas or varargs from | ||
the caller. They also imply that the callee does not access the memory | ||
pointed to by a byval argument to the caller, unless that pointer is passed | ||
to the callee as a byval argument. For example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phrasing here is a little unclear; the "unless that pointer is passed to the callee as a byval argument" part applies to allocas as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, and added an example of passing an alloca as a byval argument.
declare void @take_byval(ptr byval(i64)) | ||
declare void @take_ptr(ptr) | ||
|
||
; Invalid, %local may be de-allocated before call to @take_ptr. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe say "invalid to access"; passing around the raw pointer bits of a dead pointer is fine, although probably not useful in most cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…ns (llvm#110093) We already disallow accessing the callee's allocas from a tail-called function, because their stack memory will have been de-allocated before the tail call. I think this should apply to byval arguments too, as they also occupy space in the caller's stack frame. This was originally part of llvm#109943, spilt out for separate review.
…8ca4d0875 Local branch amd-gfx 1928ca4 Merged main:631bcbe9de13e160d427ad7452a7ef2ca67911ab into amd-gfx:c94dc53f6a77 Remote branch main 8dd817b [LangRef] Disallow accessing byval arguments from tail-called functions (llvm#110093)
We already disallow accessing the callee's allocas from a tail-called function, because their stack memory will have been de-allocated before the tail call. I think this should apply to byval arguments too, as they also occupy space in the caller's stack frame.
This was originally part of #109943, spilt out for separate review.