Skip to content

Commit 8dd817b

Browse files
authored
[LangRef] Disallow accessing byval arguments from tail-called functions (#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.
1 parent 3c09843 commit 8dd817b

File tree

2 files changed

+61
-23
lines changed

2 files changed

+61
-23
lines changed

llvm/docs/LangRef.rst

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12658,10 +12658,67 @@ This instruction requires several arguments:
1265812658
the return value of the callee is returned to the caller's caller, even
1265912659
if a void return type is in use.
1266012660

12661-
Both markers imply that the callee does not access allocas from the caller.
12662-
The ``tail`` marker additionally implies that the callee does not access
12663-
varargs from the caller. Calls marked ``musttail`` must obey the following
12664-
additional rules:
12661+
Both markers imply that the callee does not access allocas, va_args, or
12662+
byval arguments from the caller. As an exception to that, an alloca or byval
12663+
argument may be passed to the callee as a byval argument, which can be
12664+
dereferenced inside the callee. For example:
12665+
12666+
.. code-block:: llvm
12667+
12668+
declare void @take_byval(ptr byval(i64))
12669+
declare void @take_ptr(ptr)
12670+
12671+
; Invalid (assuming @take_ptr dereferences the pointer), because %local
12672+
; may be de-allocated before the call to @take_ptr.
12673+
define void @invalid_alloca() {
12674+
entry:
12675+
%local = alloca i64
12676+
tail call void @take_ptr(ptr %local)
12677+
ret void
12678+
}
12679+
12680+
; Valid, the byval attribute causes the memory allocated by %local to be
12681+
; copied into @take_byval's stack frame.
12682+
define void @byval_alloca() {
12683+
entry:
12684+
%local = alloca i64
12685+
tail call void @take_byval(ptr byval(i64) %local)
12686+
ret void
12687+
}
12688+
12689+
; Invalid, because @use_global_va_list uses the variadic arguments from
12690+
; @invalid_va_list.
12691+
%struct.va_list = type { ptr }
12692+
@va_list = external global %struct.va_list
12693+
define void @use_global_va_list() {
12694+
entry:
12695+
%arg = va_arg ptr @va_list, i64
12696+
ret void
12697+
}
12698+
define void @invalid_va_list(i32 %a, ...) {
12699+
entry:
12700+
call void @llvm.va_start.p0(ptr @va_list)
12701+
tail call void @use_global_va_list()
12702+
ret void
12703+
}
12704+
12705+
; Valid, byval argument forwarded to tail call as another byval argument.
12706+
define void @forward_byval(ptr byval(i64) %x) {
12707+
entry:
12708+
tail call void @take_byval(ptr byval(i64) %x)
12709+
ret void
12710+
}
12711+
12712+
; Invalid (assuming @take_ptr dereferences the pointer), byval argument
12713+
; passed to tail callee as non-byval ptr.
12714+
define void @invalid_byval(ptr byval(i64) %x) {
12715+
entry:
12716+
tail call void @take_ptr(ptr %x)
12717+
ret void
12718+
}
12719+
12720+
12721+
Calls marked ``musttail`` must obey the following additional rules:
1266512722

1266612723
- The call must immediately precede a :ref:`ret <i_ret>` instruction,
1266712724
or a pointer bitcast followed by a ret instruction.

llvm/test/CodeGen/ARM/struct_byval.ll

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,6 @@ declare i32 @e1(ptr nocapture byval(%struct.SmallStruct) %in) nounwind
6363
declare i32 @e2(ptr nocapture byval(%struct.LargeStruct) %in) nounwind
6464
declare i32 @e3(ptr nocapture byval(%struct.LargeStruct) align 16 %in) nounwind
6565

66-
; rdar://12442472
67-
; We can't do tail call since address of s is passed to the callee and part of
68-
; s is in caller's local frame.
69-
define void @f3(ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
70-
; CHECK-LABEL: f3
71-
; CHECK: bl _consumestruct
72-
entry:
73-
tail call void @consumestruct(ptr %s, i32 80) optsize
74-
ret void
75-
}
76-
77-
define void @f4(ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
78-
; CHECK-LABEL: f4
79-
; CHECK: bl _consumestruct
80-
entry:
81-
tail call void @consumestruct(ptr %s, i32 80) optsize
82-
ret void
83-
}
84-
8566
; We can do tail call here since s is in the incoming argument area.
8667
define void @f5(i32 %a, i32 %b, i32 %c, i32 %d, ptr nocapture byval(%struct.SmallStruct) %s) nounwind optsize {
8768
; CHECK-LABEL: f5

0 commit comments

Comments
 (0)