Skip to content

Commit c6c864d

Browse files
authored
[FunctionAttrs] Treat byval calls as only reading ptrs (llvm#122618)
Since byval arguments are passed via a hidden copy of the pointee, they do not have the same semantics as normal pointer arguments. The callee cannot capture or write to the pointer and the copy is a read of the pointer.
1 parent 22a280d commit c6c864d

File tree

5 files changed

+36
-15
lines changed

5 files changed

+36
-15
lines changed

llvm/include/llvm/IR/InstrTypes.h

+5
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,11 @@ class CallBase : public Instruction {
16671667
// FIXME: Once this API is no longer duplicated in `CallSite`, rename this to
16681668
// better indicate that this may return a conservative answer.
16691669
bool doesNotCapture(unsigned OpNo) const {
1670+
// If the argument is passed byval, the callee does not have access to the
1671+
// original pointer and thus cannot capture it.
1672+
if (OpNo < arg_size() && isByValArgument(OpNo))
1673+
return true;
1674+
16701675
return dataOperandHasImpliedAttr(OpNo, Attribute::NoCapture);
16711676
}
16721677

llvm/lib/Analysis/AliasAnalysis.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,7 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
636636
// Only look at the no-capture or byval pointer arguments. If this
637637
// pointer were passed to arguments that were neither of these, then it
638638
// couldn't be no-capture.
639-
if (!(*CI)->getType()->isPointerTy() ||
640-
(!Call->doesNotCapture(ArgNo) && ArgNo < Call->arg_size() &&
641-
!Call->isByValArgument(ArgNo)))
639+
if (!(*CI)->getType()->isPointerTy() || !Call->doesNotCapture(ArgNo))
642640
continue;
643641

644642
AliasResult AR =

llvm/lib/Transforms/IPO/FunctionAttrs.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@ determinePointerAccessAttrs(Argument *A,
852852
continue;
853853
}
854854

855-
// Given we've explictily handled the callee operand above, what's left
855+
// Given we've explicitly handled the callee operand above, what's left
856856
// must be a data operand (e.g. argument or operand bundle)
857857
const unsigned UseIndex = CB.getDataOperandNo(U);
858858

@@ -890,11 +890,14 @@ determinePointerAccessAttrs(Argument *A,
890890
// can participate in the speculation.
891891
break;
892892

893+
const bool IsByVal =
894+
CB.isArgOperand(U) && CB.isByValArgument(CB.getArgOperandNo(U));
895+
893896
// The accessors used on call site here do the right thing for calls and
894897
// invokes with operand bundles.
895898
if (CB.doesNotAccessMemory(UseIndex)) {
896899
/* nop */
897-
} else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex)) {
900+
} else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex) || IsByVal) {
898901
IsRead = true;
899902
} else if (!isRefSet(ArgMR) ||
900903
CB.dataOperandHasImpliedAttr(UseIndex, Attribute::WriteOnly)) {

llvm/test/Transforms/FunctionAttrs/readattrs.ll

+22
Original file line numberDiff line numberDiff line change
@@ -762,5 +762,27 @@ define void @writable_readnone(ptr writable dereferenceable(4) %p) {
762762
ret void
763763
}
764764

765+
declare void @byval_param(ptr byval(i32) %p)
766+
767+
define void @call_byval_param(ptr %p) {
768+
; FNATTRS-LABEL: define {{[^@]+}}@call_byval_param
769+
; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) {
770+
; FNATTRS-NEXT: call void @byval_param(ptr byval(i32) [[P]])
771+
; FNATTRS-NEXT: ret void
772+
;
773+
; ATTRIBUTOR-LABEL: define {{[^@]+}}@call_byval_param
774+
; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) {
775+
; ATTRIBUTOR-NEXT: call void @byval_param(ptr nocapture readonly byval(i32) [[P]])
776+
; ATTRIBUTOR-NEXT: ret void
777+
;
778+
; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@call_byval_param
779+
; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture readonly [[P:%.*]]) {
780+
; ATTRIBUTOR-CGSCC-NEXT: call void @byval_param(ptr nocapture readonly byval(i32) [[P]])
781+
; ATTRIBUTOR-CGSCC-NEXT: ret void
782+
;
783+
call void @byval_param(ptr byval(i32) %p)
784+
ret void
785+
}
786+
765787
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
766788
; COMMON: {{.*}}

llvm/test/Transforms/MemCpyOpt/memcpy-byval-forwarding-clobbers.ll

+3-10
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,15 @@ declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
1111
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
1212
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
1313

14-
; %a.2's lifetime ends before the call to @check. Cannot replace
15-
; %a.1 with %a.2 in the call to @check.
14+
; %a.2's lifetime ends before the call to @check. We must remove the call to
15+
; @llvm.lifetime.end in order to replace %a.1 with %a.2 in the call to @check.
1616
define i1 @alloca_forwarding_lifetime_end_clobber() {
1717
; CHECK-LABEL: @alloca_forwarding_lifetime_end_clobber(
1818
; CHECK-NEXT: entry:
19-
; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8
2019
; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8
21-
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]])
2220
; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]])
2321
; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1
24-
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false)
25-
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[A_2]])
26-
; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_1]])
22+
; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_2]])
2723
; CHECK-NEXT: ret i1 [[CALL]]
2824
;
2925
entry:
@@ -94,13 +90,10 @@ entry:
9490
define i1 @alloca_forwarding_unrelated_call_noclobber() {
9591
; CHECK-LABEL: @alloca_forwarding_unrelated_call_noclobber(
9692
; CHECK-NEXT: entry:
97-
; CHECK-NEXT: [[A_1:%.*]] = alloca i64, align 8
9893
; CHECK-NEXT: [[A_2:%.*]] = alloca i64, align 8
9994
; CHECK-NEXT: [[A_3:%.*]] = alloca i64, align 8
100-
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A_2]])
10195
; CHECK-NEXT: call void @init(ptr sret(i64) align 8 [[A_2]])
10296
; CHECK-NEXT: store i8 0, ptr [[A_2]], align 1
103-
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[A_1]], ptr [[A_2]], i64 8, i1 false)
10497
; CHECK-NEXT: call void @clobber(ptr [[A_3]])
10598
; CHECK-NEXT: [[CALL:%.*]] = call i1 @check(ptr byval(i64) align 8 [[A_2]])
10699
; CHECK-NEXT: ret i1 [[CALL]]

0 commit comments

Comments
 (0)