-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Description
With the following IR:
define void @foo(ptr readonly byval(i64) %x) {
start:
%new_x = alloca i64, align 8
store i64 0, ptr %new_x, align 8
call void @foo(ptr %new_x)
ret void
}
opt -passes="tailcallelim"
results in (https://godbolt.org/z/9arGs5xhx):
define void @foo(ptr readonly byval(i64) %x) {
%new_x1 = alloca i64, align 1
%new_x = alloca i64, align 8
br label %tailrecurse
tailrecurse: ; preds = %tailrecurse, %start
store i64 0, ptr %new_x, align 8
call void @llvm.memcpy.p0.p0.i64(ptr align 1 %new_x1, ptr align 1 %new_x, i64 8, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr align 1 %x, ptr align 1 %new_x1, i64 8, i1 false)
br label %tailrecurse
}
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
%x
is now written to, but it still has its readonly
attribute.
LangRef seems to imply that this is illegal:
The copy is considered to belong to the caller not the callee (for example, readonly functions should not write to byval parameters).
However, Alive2 is fine with writes to readonly byval
arguments (but does flag identical IR without byval
): https://alive2.llvm.org/ce/z/T6yxU-
This leads to an end-to-end miscompile, where the following recursion (always terminating after one call):
define void @foo(ptr noalias byval(i64) %x) {
start:
%new_x = alloca i64, align 8
%x_val = load i64, ptr %x, align 8
%is_zero = icmp eq i64 %x_val, 0
br i1 %is_zero, label %end, label %recurse
recurse:
store i64 0, ptr %new_x, align 8
call void @foo(ptr %new_x)
br label %end
end:
ret void
}
is converted to an infinite loop with opt -O3
(https://godbolt.org/z/9zbn6hno9):
define void @foo(ptr noalias nocapture readonly byval(i64) %x) local_unnamed_addr #0 {
%x_val = load i64, ptr %x, align 8
%is_zero = icmp eq i64 %x_val, 0
br i1 %is_zero, label %end, label %recurse
recurse: ; preds = %start, %recurse
br label %recurse
end: ; preds = %start
ret void
}
If LangRef is right, the bug is in tailcallelim. If Alive2 is right, the bug is in whatever other transforms assume that readonly
is meaningful on byval
arguments.
Upstream issue: rust-lang/rust#114312
Metadata
Metadata
Assignees
Type
Projects
Status