Skip to content

Commit e56a6a2

Browse files
authored
Reapply [CaptureTracking][FunctionAttrs] Add support for CaptureInfo (#125880) (#128020)
Relative to the previous attempt this includes two fixes: * Adjust callCapturesBefore() to not skip captures(ret: address, provenance) arguments, as these will not count as a capture at the call-site. * When visiting uses during stack slot optimization, don't skip the ModRef check for passthru captures. Calls can both modref and be passthru for captures. ------ This extends CaptureTracking to support inferring non-trivial CaptureInfos. The focus of this patch is to only support FunctionAttrs, other users of CaptureTracking will be updated in followups. The key API changes here are: * DetermineUseCaptureKind() now returns a UseCaptureInfo where the UseCC component specifies what is captured at that Use and the ResultCC component specifies what may be captured via the return value of the User. Usually only one or the other will be used (corresponding to previous MAY_CAPTURE or PASSTHROUGH results), but both may be set for call captures. * The CaptureTracking::captures() extension point is passed this UseCaptureInfo as well and then can decide what to do with it by returning an Action, which is one of: Stop: stop traversal. ContinueIgnoringReturn: continue traversal but don't follow the instruction return value. Continue: continue traversal and follow the instruction return value if it has additional CaptureComponents. For now, this patch retains the (unsound) special logic for comparison of null with a dereferenceable pointer. I'd like to switch key code to take advantage of address/address_is_null before dropping it. This PR mainly intends to introduce necessary API changes and basic inference support, there are various possible improvements marked with TODOs.
1 parent 78aa61d commit e56a6a2

28 files changed

+514
-227
lines changed

clang/test/CodeGen/allow-ubsan-check.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ int div(int x, int y) {
8686
}
8787

8888
// CHECK-LABEL: define dso_local i32 @null(
89-
// CHECK-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
89+
// CHECK-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
9090
// CHECK-NEXT: [[ENTRY:.*:]]
9191
// CHECK-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
9292
//
@@ -102,7 +102,7 @@ int div(int x, int y) {
102102
// CHECK-NEXT: ret i32 [[TMP2]]
103103
//
104104
// TR-LABEL: define dso_local i32 @null(
105-
// TR-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
105+
// TR-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
106106
// TR-NEXT: [[ENTRY:.*:]]
107107
// TR-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
108108
// TR-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]
@@ -116,7 +116,7 @@ int div(int x, int y) {
116116
// TR-NEXT: ret i32 [[TMP2]]
117117
//
118118
// REC-LABEL: define dso_local i32 @null(
119-
// REC-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
119+
// REC-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
120120
// REC-NEXT: [[ENTRY:.*:]]
121121
// REC-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
122122
// REC-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]

clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O3 -o - -emit-llvm | FileCheck %s
55

6-
// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
6+
// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
77
// CHECK-NEXT: entry:
88
// CHECK-NEXT: ret ptr %b
99
// CHECK-NEXT: }
@@ -22,12 +22,12 @@
2222

2323
// CHECK: declare ptr @__dynamic_cast(ptr, ptr, ptr, i64) local_unnamed_addr
2424

25-
// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
25+
// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
2626
// CHECK-NEXT: entry
2727
// CHECK-NEXT: ret ptr %b
2828
// CHECK-NEXT: }
2929

30-
// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly %b) local_unnamed_addr
30+
// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly captures(address_is_null, ret: address, provenance) %b) local_unnamed_addr
3131
// CHECK-NEXT: entry:
3232
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %b, null
3333
// CHECK-NEXT: br i1 [[isnull]], label %[[dynamic_cast_end:[a-z0-9._]+]], label %[[dynamic_cast_notnull:[a-z0-9._]+]]

clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
// CHECK-NEXT: ret ptr @_ZTS1A
2525
// CHECK-NEXT: }
2626

27-
// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly %a) local_unnamed_addr
27+
// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly captures(address_is_null) %a) local_unnamed_addr
2828
// CHECK-NEXT: entry:
2929
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %a, null
3030
// CHECK-NEXT: br i1 [[isnull]], label %[[bad_typeid:[a-z0-9._]+]], label %[[end:[a-z0-9.+]+]]

clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ __amdgpu_buffer_rsrc_t getBuffer(void *p) {
2222
}
2323

2424
// CHECK-LABEL: define {{[^@]+}}@consumeBufferPtr
25-
// CHECK-SAME: (ptr addrspace(5) noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
25+
// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
2626
// CHECK-NEXT: entry:
2727
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr addrspace(5) [[P]], addrspacecast (ptr null to ptr addrspace(5))
2828
// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
@@ -39,7 +39,7 @@ void consumeBufferPtr(__amdgpu_buffer_rsrc_t *p) {
3939
}
4040

4141
// CHECK-LABEL: define {{[^@]+}}@test
42-
// CHECK-SAME: (ptr addrspace(5) noundef readonly [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
42+
// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
4343
// CHECK-NEXT: entry:
4444
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(5) [[A]], align 16, !tbaa [[TBAA8:![0-9]+]]
4545
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP0]], 0

clang/test/CodeGenOpenCL/as_type.cl

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ int3 f8(char16 x) {
6767
return __builtin_astype(x, int3);
6868
}
6969

70-
//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone %[[x:.*]])
70+
//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone captures(ret: address, provenance) %[[x:.*]])
7171
//CHECK: %[[cast:.*]] ={{.*}} addrspacecast ptr %[[x]] to ptr addrspace(1)
7272
//CHECK: ret ptr addrspace(1) %[[cast]]
7373
global int* addr_cast(int *x) {

llvm/include/llvm/Analysis/CaptureTracking.h

+53-15
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
#define LLVM_ANALYSIS_CAPTURETRACKING_H
1515

1616
#include "llvm/ADT/DenseMap.h"
17+
#include "llvm/Support/ModRef.h"
1718

1819
namespace llvm {
1920

2021
class Value;
2122
class Use;
23+
class CaptureInfo;
2224
class DataLayout;
2325
class Instruction;
2426
class DominatorTree;
@@ -71,10 +73,47 @@ namespace llvm {
7173
bool ReturnCaptures, const DominatorTree &DT,
7274
unsigned MaxUsesToExplore = 0);
7375

76+
/// Capture information for a specific Use.
77+
struct UseCaptureInfo {
78+
/// Components captured by this use.
79+
CaptureComponents UseCC;
80+
/// Components captured by the return value of the user of this Use.
81+
CaptureComponents ResultCC;
82+
83+
UseCaptureInfo(CaptureComponents UseCC,
84+
CaptureComponents ResultCC = CaptureComponents::None)
85+
: UseCC(UseCC), ResultCC(ResultCC) {}
86+
87+
static UseCaptureInfo passthrough() {
88+
return UseCaptureInfo(CaptureComponents::None, CaptureComponents::All);
89+
}
90+
91+
bool isPassthrough() const {
92+
return capturesNothing(UseCC) && capturesAnything(ResultCC);
93+
}
94+
95+
operator CaptureComponents() const { return UseCC | ResultCC; }
96+
};
97+
7498
/// This callback is used in conjunction with PointerMayBeCaptured. In
7599
/// addition to the interface here, you'll need to provide your own getters
76100
/// to see whether anything was captured.
77101
struct CaptureTracker {
102+
/// Action returned from captures().
103+
enum Action {
104+
/// Stop the traversal.
105+
Stop,
106+
/// Continue traversal, and also follow the return value of the user if
107+
/// it has additional capture components (that is, if it has capture
108+
/// components in Ret that are not part of Other).
109+
Continue,
110+
/// Continue traversal, but do not follow the return value of the user,
111+
/// even if it has additional capture components. Should only be used if
112+
/// captures() has already taken the potential return captures into
113+
/// account.
114+
ContinueIgnoringReturn,
115+
};
116+
78117
virtual ~CaptureTracker();
79118

80119
/// tooManyUses - The depth of traversal has breached a limit. There may be
@@ -88,32 +127,31 @@ namespace llvm {
88127
/// U->getUser() is always an Instruction.
89128
virtual bool shouldExplore(const Use *U);
90129

91-
/// captured - Information about the pointer was captured by the user of
92-
/// use U. Return true to stop the traversal or false to continue looking
93-
/// for more capturing instructions.
94-
virtual bool captured(const Use *U) = 0;
130+
/// Use U directly captures CI.UseCC and additionally CI.ResultCC
131+
/// through the return value of the user of U.
132+
///
133+
/// Return one of Stop, Continue or ContinueIgnoringReturn to control
134+
/// further traversal.
135+
virtual Action captured(const Use *U, UseCaptureInfo CI) = 0;
95136

96137
/// isDereferenceableOrNull - Overload to allow clients with additional
97138
/// knowledge about pointer dereferenceability to provide it and thereby
98139
/// avoid conservative responses when a pointer is compared to null.
99140
virtual bool isDereferenceableOrNull(Value *O, const DataLayout &DL);
100141
};
101142

102-
/// Types of use capture kinds, see \p DetermineUseCaptureKind.
103-
enum class UseCaptureKind {
104-
NO_CAPTURE,
105-
MAY_CAPTURE,
106-
PASSTHROUGH,
107-
};
108-
109143
/// Determine what kind of capture behaviour \p U may exhibit.
110144
///
111-
/// A use can be no-capture, a use can potentially capture, or a use can be
112-
/// passthrough such that the uses of the user or \p U should be inspected.
145+
/// The returned UseCaptureInfo contains the components captured directly
146+
/// by the use (UseCC) and the components captured through the return value
147+
/// of the user (ResultCC).
148+
///
149+
/// \p Base is the starting value of the capture analysis, which is
150+
/// relevant for address_is_null captures.
113151
/// The \p IsDereferenceableOrNull callback is used to rule out capturing for
114152
/// certain comparisons.
115-
UseCaptureKind
116-
DetermineUseCaptureKind(const Use &U,
153+
UseCaptureInfo
154+
DetermineUseCaptureKind(const Use &U, const Value *Base,
117155
llvm::function_ref<bool(Value *, const DataLayout &)>
118156
IsDereferenceableOrNull);
119157

llvm/include/llvm/IR/InstrTypes.h

+5
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,11 @@ class CallBase : public Instruction {
16921692
return capturesNothing(getCaptureInfo(OpNo));
16931693
}
16941694

1695+
/// Returns whether the call has an argument that has an attribute like
1696+
/// captures(ret: address, provenance), where the return capture components
1697+
/// are not a subset of the other capture components.
1698+
bool hasArgumentWithAdditionalReturnCaptureComponents() const;
1699+
16951700
/// Determine whether this argument is passed by value.
16961701
bool isByValArgument(unsigned ArgNo) const {
16971702
return paramHasAttr(ArgNo, Attribute::ByVal);

llvm/include/llvm/Support/ModRef.h

+13
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ inline bool capturesFullProvenance(CaptureComponents CC) {
326326
return (CC & CaptureComponents::Provenance) == CaptureComponents::Provenance;
327327
}
328328

329+
inline bool capturesAll(CaptureComponents CC) {
330+
return CC == CaptureComponents::All;
331+
}
332+
329333
raw_ostream &operator<<(raw_ostream &OS, CaptureComponents CC);
330334

331335
/// Represents which components of the pointer may be captured in which
@@ -350,6 +354,15 @@ class CaptureInfo {
350354
/// Create CaptureInfo that may capture all components of the pointer.
351355
static CaptureInfo all() { return CaptureInfo(CaptureComponents::All); }
352356

357+
/// Create CaptureInfo that may only capture via the return value.
358+
static CaptureInfo
359+
retOnly(CaptureComponents RetComponents = CaptureComponents::All) {
360+
return CaptureInfo(CaptureComponents::None, RetComponents);
361+
}
362+
363+
/// Whether the pointer is only captured via the return value.
364+
bool isRetOnly() const { return capturesNothing(OtherComponents); }
365+
353366
/// Get components potentially captured by the return value.
354367
CaptureComponents getRetComponents() const { return RetComponents; }
355368

llvm/lib/Analysis/AliasAnalysis.cpp

+16-4
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,13 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
635635
// Only look at the no-capture or byval pointer arguments. If this
636636
// pointer were passed to arguments that were neither of these, then it
637637
// couldn't be no-capture.
638-
if (!(*CI)->getType()->isPointerTy() || !Call->doesNotCapture(ArgNo))
638+
if (!(*CI)->getType()->isPointerTy())
639+
continue;
640+
641+
// Make sure we still check captures(ret: address, provenance) arguments,
642+
// as these wouldn't be treated as a capture at the call-site.
643+
CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
644+
if (!capturesNothing(Captures.getOtherComponents()))
639645
continue;
640646

641647
AliasResult AR =
@@ -834,9 +840,15 @@ bool llvm::isBaseOfObject(const Value *V) {
834840
}
835841

836842
bool llvm::isEscapeSource(const Value *V) {
837-
if (auto *CB = dyn_cast<CallBase>(V))
838-
return !isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB,
839-
true);
843+
if (auto *CB = dyn_cast<CallBase>(V)) {
844+
if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB, true))
845+
return false;
846+
847+
// The return value of a function with a captures(ret: address, provenance)
848+
// attribute is not necessarily an escape source. The return value may
849+
// alias with a non-escaping object.
850+
return !CB->hasArgumentWithAdditionalReturnCaptureComponents();
851+
}
840852

841853
// The load case works because isNonEscapingLocalObject considers all
842854
// stores to be escapes (it passes true for the StoreCaptures argument

0 commit comments

Comments
 (0)