[LangRef] Disallow accessing byval arguments from tail-called functions#110093
[LangRef] Disallow accessing byval arguments from tail-called functions#110093
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. 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.
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.
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.
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.
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.
Maybe say "invalid to access"; passing around the raw pointer bits of a dead pointer is fine, although probably not useful in most cases.
…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.