-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12658,10 +12658,67 @@ 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, va_args, or | ||
byval arguments from the caller. As an exception to that, an alloca or byval | ||
argument may be passed to the callee as a byval argument, which can be | ||
dereferenced inside the callee. For example: | ||
|
||
.. code-block:: llvm | ||
|
||
declare void @take_byval(ptr byval(i64)) | ||
declare void @take_ptr(ptr) | ||
|
||
; Invalid (assuming @take_ptr dereferences the pointer), because %local | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
; may be de-allocated before the call to @take_ptr. | ||
define void @invalid_alloca() { | ||
entry: | ||
%local = alloca i64 | ||
tail call void @take_ptr(ptr %local) | ||
ret void | ||
} | ||
|
||
; Valid, the byval attribute causes the memory allocated by %local to be | ||
; copied into @take_byval's stack frame. | ||
define void @byval_alloca() { | ||
entry: | ||
%local = alloca i64 | ||
tail call void @take_byval(ptr byval(i64) %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 (assuming @take_ptr dereferences the pointer), 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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
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 | ||
|
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.