-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Reapply [CaptureTracking][FunctionAttrs] Add support for CaptureInfo (#125880) #128020
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
Conversation
864874f
to
39a18a1
Compare
…lvm#125880) Relative to the previous attempt, this adjusts isEscapeSource() to not treat calls with captures(ret: address, provenance) or similar arguments as escape sources. This addresses the miscompile reported at: llvm#125880 (comment) The implementation uses a helper function on CallBase to make this check a bit more efficient (e.g. by skipping the byval checks) as checking attributes on all arguments if fairly expensive. ------ 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.
Previously passthrus never had effects. Now they do, if it it's a call with a ret-only capture. Always run the modref handling code, but wrap it in a quick read/write check to avoid wasting time for cases that definitely don't modref.
39a18a1
to
a267645
Compare
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: Nikita Popov (nikic) ChangesRelative to the previous attempt this includes two fixes:
This extends CaptureTracking to support inferring non-trivial The key API changes here are:
For now, this patch retains the (unsound) special logic for comparison This PR mainly intends to introduce necessary API changes and basic Patch is 79.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128020.diff 28 Files Affected:
diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c
index 0cd81a77f5cc5..c116604288546 100644
--- a/clang/test/CodeGen/allow-ubsan-check.c
+++ b/clang/test/CodeGen/allow-ubsan-check.c
@@ -86,7 +86,7 @@ int div(int x, int y) {
}
// CHECK-LABEL: define dso_local i32 @null(
-// CHECK-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
//
@@ -102,7 +102,7 @@ int div(int x, int y) {
// CHECK-NEXT: ret i32 [[TMP2]]
//
// TR-LABEL: define dso_local i32 @null(
-// TR-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// TR-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// TR-NEXT: [[ENTRY:.*:]]
// TR-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
// TR-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]
@@ -116,7 +116,7 @@ int div(int x, int y) {
// TR-NEXT: ret i32 [[TMP2]]
//
// REC-LABEL: define dso_local i32 @null(
-// REC-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// REC-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// REC-NEXT: [[ENTRY:.*:]]
// REC-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
// REC-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]
diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
index 83daf57be22ff..3662a270713b6 100644
--- a/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
+++ b/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
@@ -3,7 +3,7 @@
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O3 -o - -emit-llvm | FileCheck %s
-// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: ret ptr %b
// CHECK-NEXT: }
@@ -22,12 +22,12 @@
// CHECK: declare ptr @__dynamic_cast(ptr, ptr, ptr, i64) local_unnamed_addr
-// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry
// CHECK-NEXT: ret ptr %b
// CHECK-NEXT: }
-// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly captures(address_is_null, ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %b, null
// CHECK-NEXT: br i1 [[isnull]], label %[[dynamic_cast_end:[a-z0-9._]+]], label %[[dynamic_cast_notnull:[a-z0-9._]+]]
diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
index c471e5dbd7b33..2a838708ca231 100644
--- a/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
+++ b/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
@@ -24,7 +24,7 @@
// CHECK-NEXT: ret ptr @_ZTS1A
// CHECK-NEXT: }
-// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly %a) local_unnamed_addr
+// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly captures(address_is_null) %a) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %a, null
// CHECK-NEXT: br i1 [[isnull]], label %[[bad_typeid:[a-z0-9._]+]], label %[[end:[a-z0-9.+]+]]
diff --git a/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl b/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
index 0aadaad2dca5c..62fd20c4d1414 100644
--- a/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
+++ b/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
@@ -22,7 +22,7 @@ __amdgpu_buffer_rsrc_t getBuffer(void *p) {
}
// CHECK-LABEL: define {{[^@]+}}@consumeBufferPtr
-// CHECK-SAME: (ptr addrspace(5) noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr addrspace(5) [[P]], addrspacecast (ptr null to ptr addrspace(5))
// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
@@ -39,7 +39,7 @@ void consumeBufferPtr(__amdgpu_buffer_rsrc_t *p) {
}
// CHECK-LABEL: define {{[^@]+}}@test
-// CHECK-SAME: (ptr addrspace(5) noundef readonly [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(5) [[A]], align 16, !tbaa [[TBAA8:![0-9]+]]
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP0]], 0
diff --git a/clang/test/CodeGenOpenCL/as_type.cl b/clang/test/CodeGenOpenCL/as_type.cl
index 1fe26fbeafdb4..2c6cdc3810b4d 100644
--- a/clang/test/CodeGenOpenCL/as_type.cl
+++ b/clang/test/CodeGenOpenCL/as_type.cl
@@ -67,7 +67,7 @@ int3 f8(char16 x) {
return __builtin_astype(x, int3);
}
-//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone %[[x:.*]])
+//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone captures(ret: address, provenance) %[[x:.*]])
//CHECK: %[[cast:.*]] ={{.*}} addrspacecast ptr %[[x]] to ptr addrspace(1)
//CHECK: ret ptr addrspace(1) %[[cast]]
global int* addr_cast(int *x) {
diff --git a/llvm/include/llvm/Analysis/CaptureTracking.h b/llvm/include/llvm/Analysis/CaptureTracking.h
index ef69d40aac18a..1f4aae31c62a8 100644
--- a/llvm/include/llvm/Analysis/CaptureTracking.h
+++ b/llvm/include/llvm/Analysis/CaptureTracking.h
@@ -14,11 +14,13 @@
#define LLVM_ANALYSIS_CAPTURETRACKING_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/ModRef.h"
namespace llvm {
class Value;
class Use;
+ class CaptureInfo;
class DataLayout;
class Instruction;
class DominatorTree;
@@ -71,10 +73,47 @@ namespace llvm {
bool ReturnCaptures, const DominatorTree &DT,
unsigned MaxUsesToExplore = 0);
+ /// Capture information for a specific Use.
+ struct UseCaptureInfo {
+ /// Components captured by this use.
+ CaptureComponents UseCC;
+ /// Components captured by the return value of the user of this Use.
+ CaptureComponents ResultCC;
+
+ UseCaptureInfo(CaptureComponents UseCC,
+ CaptureComponents ResultCC = CaptureComponents::None)
+ : UseCC(UseCC), ResultCC(ResultCC) {}
+
+ static UseCaptureInfo passthrough() {
+ return UseCaptureInfo(CaptureComponents::None, CaptureComponents::All);
+ }
+
+ bool isPassthrough() const {
+ return capturesNothing(UseCC) && capturesAnything(ResultCC);
+ }
+
+ operator CaptureComponents() const { return UseCC | ResultCC; }
+ };
+
/// This callback is used in conjunction with PointerMayBeCaptured. In
/// addition to the interface here, you'll need to provide your own getters
/// to see whether anything was captured.
struct CaptureTracker {
+ /// Action returned from captures().
+ enum Action {
+ /// Stop the traversal.
+ Stop,
+ /// Continue traversal, and also follow the return value of the user if
+ /// it has additional capture components (that is, if it has capture
+ /// components in Ret that are not part of Other).
+ Continue,
+ /// Continue traversal, but do not follow the return value of the user,
+ /// even if it has additional capture components. Should only be used if
+ /// captures() has already taken the potential return captures into
+ /// account.
+ ContinueIgnoringReturn,
+ };
+
virtual ~CaptureTracker();
/// tooManyUses - The depth of traversal has breached a limit. There may be
@@ -88,10 +127,12 @@ namespace llvm {
/// U->getUser() is always an Instruction.
virtual bool shouldExplore(const Use *U);
- /// captured - Information about the pointer was captured by the user of
- /// use U. Return true to stop the traversal or false to continue looking
- /// for more capturing instructions.
- virtual bool captured(const Use *U) = 0;
+ /// Use U directly captures CI.UseCC and additionally CI.ResultCC
+ /// through the return value of the user of U.
+ ///
+ /// Return one of Stop, Continue or ContinueIgnoringReturn to control
+ /// further traversal.
+ virtual Action captured(const Use *U, UseCaptureInfo CI) = 0;
/// isDereferenceableOrNull - Overload to allow clients with additional
/// knowledge about pointer dereferenceability to provide it and thereby
@@ -99,21 +140,18 @@ namespace llvm {
virtual bool isDereferenceableOrNull(Value *O, const DataLayout &DL);
};
- /// Types of use capture kinds, see \p DetermineUseCaptureKind.
- enum class UseCaptureKind {
- NO_CAPTURE,
- MAY_CAPTURE,
- PASSTHROUGH,
- };
-
/// Determine what kind of capture behaviour \p U may exhibit.
///
- /// A use can be no-capture, a use can potentially capture, or a use can be
- /// passthrough such that the uses of the user or \p U should be inspected.
+ /// The returned UseCaptureInfo contains the components captured directly
+ /// by the use (UseCC) and the components captured through the return value
+ /// of the user (ResultCC).
+ ///
+ /// \p Base is the starting value of the capture analysis, which is
+ /// relevant for address_is_null captures.
/// The \p IsDereferenceableOrNull callback is used to rule out capturing for
/// certain comparisons.
- UseCaptureKind
- DetermineUseCaptureKind(const Use &U,
+ UseCaptureInfo
+ DetermineUseCaptureKind(const Use &U, const Value *Base,
llvm::function_ref<bool(Value *, const DataLayout &)>
IsDereferenceableOrNull);
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 90fe864d4ae71..8e47e3c7b3a7c 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1692,6 +1692,11 @@ class CallBase : public Instruction {
return capturesNothing(getCaptureInfo(OpNo));
}
+ /// Returns whether the call has an argument that has an attribute like
+ /// captures(ret: address, provenance), where the return capture components
+ /// are not a subset of the other capture components.
+ bool hasArgumentWithAdditionalReturnCaptureComponents() const;
+
/// Determine whether this argument is passed by value.
bool isByValArgument(unsigned ArgNo) const {
return paramHasAttr(ArgNo, Attribute::ByVal);
diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h
index eb660844b0b3a..7f58f5236aedd 100644
--- a/llvm/include/llvm/Support/ModRef.h
+++ b/llvm/include/llvm/Support/ModRef.h
@@ -326,6 +326,10 @@ inline bool capturesFullProvenance(CaptureComponents CC) {
return (CC & CaptureComponents::Provenance) == CaptureComponents::Provenance;
}
+inline bool capturesAll(CaptureComponents CC) {
+ return CC == CaptureComponents::All;
+}
+
raw_ostream &operator<<(raw_ostream &OS, CaptureComponents CC);
/// Represents which components of the pointer may be captured in which
@@ -350,6 +354,15 @@ class CaptureInfo {
/// Create CaptureInfo that may capture all components of the pointer.
static CaptureInfo all() { return CaptureInfo(CaptureComponents::All); }
+ /// Create CaptureInfo that may only capture via the return value.
+ static CaptureInfo
+ retOnly(CaptureComponents RetComponents = CaptureComponents::All) {
+ return CaptureInfo(CaptureComponents::None, RetComponents);
+ }
+
+ /// Whether the pointer is only captured via the return value.
+ bool isRetOnly() const { return capturesNothing(OtherComponents); }
+
/// Get components potentially captured by the return value.
CaptureComponents getRetComponents() const { return RetComponents; }
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 969993c94a6e8..3365349922078 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -635,7 +635,13 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
// Only look at the no-capture or byval pointer arguments. If this
// pointer were passed to arguments that were neither of these, then it
// couldn't be no-capture.
- if (!(*CI)->getType()->isPointerTy() || !Call->doesNotCapture(ArgNo))
+ if (!(*CI)->getType()->isPointerTy())
+ continue;
+
+ // Make sure we still check captures(ret: address, provenance) arguments,
+ // as these wouldn't be treated as a capture at the call-site.
+ CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
+ if (!capturesNothing(Captures.getOtherComponents()))
continue;
AliasResult AR =
@@ -834,9 +840,15 @@ bool llvm::isBaseOfObject(const Value *V) {
}
bool llvm::isEscapeSource(const Value *V) {
- if (auto *CB = dyn_cast<CallBase>(V))
- return !isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB,
- true);
+ if (auto *CB = dyn_cast<CallBase>(V)) {
+ if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB, true))
+ return false;
+
+ // The return value of a function with a captures(ret: address, provenance)
+ // attribute is not necessarily an escape source. The return value may
+ // alias with a non-escaping object.
+ return !CB->hasArgumentWithAdditionalReturnCaptureComponents();
+ }
// The load case works because isNonEscapingLocalObject considers all
// stores to be escapes (it passes true for the StoreCaptures argument
diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp
index 9cb3c092768e4..6e5748c233240 100644
--- a/llvm/lib/Analysis/CaptureTracking.cpp
+++ b/llvm/lib/Analysis/CaptureTracking.cpp
@@ -81,14 +81,15 @@ struct SimpleCaptureTracker : public CaptureTracker {
Captured = true;
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
Captured = true;
- return true;
+ return Stop;
}
bool ReturnCaptures;
@@ -122,19 +123,21 @@ struct CapturesBefore : public CaptureTracker {
return !isPotentiallyReachable(I, BeforeHere, nullptr, DT, LI);
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
// Check isSafeToPrune() here rather than in shouldExplore() to avoid
// an expensive reachability query for every instruction we look at.
// Instead we only do one for actual capturing candidates.
if (isSafeToPrune(I))
- return false;
+ // If the use is not reachable, the instruction result isn't either.
+ return ContinueIgnoringReturn;
Captured = true;
- return true;
+ return Stop;
}
const Instruction *BeforeHere;
@@ -166,10 +169,11 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = &*F.getEntryBlock().begin();
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
if (!EarliestCapture)
EarliestCapture = I;
@@ -177,9 +181,10 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
Captured = true;
- // Return false to continue analysis; we need to see all potential
- // captures.
- return false;
+ // Continue analysis, as we need to see all potential captures. However,
+ // we do not need to follow the instruction result, as this use will
+ // dominate any captures made through the instruction result.
+ return ContinueIgnoringReturn;
}
Instruction *EarliestCapture = nullptr;
@@ -260,14 +265,14 @@ Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
return CB.EarliestCapture;
}
-UseCaptureKind llvm::DetermineUseCaptureKind(
- const Use &U,
+UseCaptureInfo llvm::DetermineUseCaptureKind(
+ const Use &U, const Value *Base,
function_ref<bool(Value *, const DataLayout &)> IsDereferenceableOrNull) {
Instruction *I = dyn_cast<Instruction>(U.getUser());
// TODO: Investigate non-instruction uses.
if (!I)
- return UseCaptureKind::MAY_CAPTURE;
+ return CaptureComponents::All;
switch (I->getOpcode()) {
case Instruction::Call:
@@ -278,7 +283,7 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// by throwing an exception or not depending on the input value).
if (Call->onlyReadsMemory() && Call->doesNotThrow() &&
Call->getType()->isVoidTy())
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
// The pointer is not captured if returned pointer is not captured.
// NOTE: CaptureTracking users should not assume that only functions
@@ -286,13 +291,13 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// getUnderlyingObject in ValueTracking or DecomposeGEPExpression
// in BasicAA also need to know about this property.
if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(Call, true))
- return UseCaptureKind::PASSTHROUGH;
+ return UseCaptureInfo::passthrough();
// Volatile operations effectively capture the memory location that they
// load and store to.
if (auto *MI = dyn_cast<MemIntrinsic>(Call))
if (MI->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
+ return CaptureComponents::All;
// Calling a function pointer does not in itself cause the pointer to
// be captured. This is a subtle point considering that (for example)
@@ -301,30 +306,27 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// captured, even though the loaded value might be the pointer itself
// (think of self-referential objects).
if (Call->isCallee(&U))
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
// Not captured if only passed via 'nocapture' arguments.
assert(Call->isDataOperand(&U) && "Non-callee must be data operand");
- if (!Call->doesNotCapture(Call->getDataOperandNo(&U))) {
- // The parameter is not marked 'nocapture' - captured.
- return UseCaptureKind::MAY_CAPTURE;
- }
- return UseCaptureKind::NO_CAPTURE;
+ CaptureInfo CI = Call->getCaptureInfo(Call->getDataOperandNo(&U));
+ return UseCaptureInfo(CI.getOtherComponents(), CI.getRetComponents());
}
case Instruction::Load:
// Volatile loads make the address observable.
if (cast<LoadInst>(I)->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::All;
+ return CaptureComponents::None;
case Instruction::VAArg:
// "va-arg" from a pointer does not cause it to be captured.
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
case Instruction::Store:
// Stored the pointer - conservatively assume it may be captured.
// Volatile stores make the address observable.
if (U.getOperandNo() == 0 || cast<StoreInst>(I)->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
- return UseCaptu...
[truncated]
|
@llvm/pr-subscribers-clang Author: Nikita Popov (nikic) ChangesRelative to the previous attempt this includes two fixes:
This extends CaptureTracking to support inferring non-trivial The key API changes here are:
For now, this patch retains the (unsound) special logic for comparison This PR mainly intends to introduce necessary API changes and basic Patch is 79.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128020.diff 28 Files Affected:
diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c
index 0cd81a77f5cc5..c116604288546 100644
--- a/clang/test/CodeGen/allow-ubsan-check.c
+++ b/clang/test/CodeGen/allow-ubsan-check.c
@@ -86,7 +86,7 @@ int div(int x, int y) {
}
// CHECK-LABEL: define dso_local i32 @null(
-// CHECK-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
//
@@ -102,7 +102,7 @@ int div(int x, int y) {
// CHECK-NEXT: ret i32 [[TMP2]]
//
// TR-LABEL: define dso_local i32 @null(
-// TR-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// TR-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// TR-NEXT: [[ENTRY:.*:]]
// TR-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
// TR-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]
@@ -116,7 +116,7 @@ int div(int x, int y) {
// TR-NEXT: ret i32 [[TMP2]]
//
// REC-LABEL: define dso_local i32 @null(
-// REC-SAME: ptr noundef readonly [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// REC-SAME: ptr noundef readonly captures(address_is_null) [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
// REC-NEXT: [[ENTRY:.*:]]
// REC-NEXT: [[TMP0:%.*]] = icmp eq ptr [[X]], null, !nosanitize [[META2]]
// REC-NEXT: [[TMP1:%.*]] = tail call i1 @llvm.allow.ubsan.check(i8 29), !nosanitize [[META2]]
diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
index 83daf57be22ff..3662a270713b6 100644
--- a/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
+++ b/clang/test/CodeGenCXX/RelativeVTablesABI/dynamic-cast.cpp
@@ -3,7 +3,7 @@
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O3 -o - -emit-llvm | FileCheck %s
-// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z6upcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: ret ptr %b
// CHECK-NEXT: }
@@ -22,12 +22,12 @@
// CHECK: declare ptr @__dynamic_cast(ptr, ptr, ptr, i64) local_unnamed_addr
-// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z8selfcastP1B(ptr noundef readnone returned captures(ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry
// CHECK-NEXT: ret ptr %b
// CHECK-NEXT: }
-// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly %b) local_unnamed_addr
+// CHECK: define{{.*}} ptr @_Z9void_castP1B(ptr noundef readonly captures(address_is_null, ret: address, provenance) %b) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %b, null
// CHECK-NEXT: br i1 [[isnull]], label %[[dynamic_cast_end:[a-z0-9._]+]], label %[[dynamic_cast_notnull:[a-z0-9._]+]]
diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
index c471e5dbd7b33..2a838708ca231 100644
--- a/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
+++ b/clang/test/CodeGenCXX/RelativeVTablesABI/type-info.cpp
@@ -24,7 +24,7 @@
// CHECK-NEXT: ret ptr @_ZTS1A
// CHECK-NEXT: }
-// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly %a) local_unnamed_addr
+// CHECK: define{{.*}} i1 @_Z5equalP1A(ptr noundef readonly captures(address_is_null) %a) local_unnamed_addr
// CHECK-NEXT: entry:
// CHECK-NEXT: [[isnull:%[0-9]+]] = icmp eq ptr %a, null
// CHECK-NEXT: br i1 [[isnull]], label %[[bad_typeid:[a-z0-9._]+]], label %[[end:[a-z0-9.+]+]]
diff --git a/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl b/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
index 0aadaad2dca5c..62fd20c4d1414 100644
--- a/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
+++ b/clang/test/CodeGenOpenCL/amdgcn-buffer-rsrc-type.cl
@@ -22,7 +22,7 @@ __amdgpu_buffer_rsrc_t getBuffer(void *p) {
}
// CHECK-LABEL: define {{[^@]+}}@consumeBufferPtr
-// CHECK-SAME: (ptr addrspace(5) noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr addrspace(5) [[P]], addrspacecast (ptr null to ptr addrspace(5))
// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
@@ -39,7 +39,7 @@ void consumeBufferPtr(__amdgpu_buffer_rsrc_t *p) {
}
// CHECK-LABEL: define {{[^@]+}}@test
-// CHECK-SAME: (ptr addrspace(5) noundef readonly [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-SAME: (ptr addrspace(5) noundef readonly captures(address) [[A:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(5) [[A]], align 16, !tbaa [[TBAA8:![0-9]+]]
// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP0]], 0
diff --git a/clang/test/CodeGenOpenCL/as_type.cl b/clang/test/CodeGenOpenCL/as_type.cl
index 1fe26fbeafdb4..2c6cdc3810b4d 100644
--- a/clang/test/CodeGenOpenCL/as_type.cl
+++ b/clang/test/CodeGenOpenCL/as_type.cl
@@ -67,7 +67,7 @@ int3 f8(char16 x) {
return __builtin_astype(x, int3);
}
-//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone %[[x:.*]])
+//CHECK: define{{.*}} spir_func noundef ptr addrspace(1) @addr_cast(ptr noundef readnone captures(ret: address, provenance) %[[x:.*]])
//CHECK: %[[cast:.*]] ={{.*}} addrspacecast ptr %[[x]] to ptr addrspace(1)
//CHECK: ret ptr addrspace(1) %[[cast]]
global int* addr_cast(int *x) {
diff --git a/llvm/include/llvm/Analysis/CaptureTracking.h b/llvm/include/llvm/Analysis/CaptureTracking.h
index ef69d40aac18a..1f4aae31c62a8 100644
--- a/llvm/include/llvm/Analysis/CaptureTracking.h
+++ b/llvm/include/llvm/Analysis/CaptureTracking.h
@@ -14,11 +14,13 @@
#define LLVM_ANALYSIS_CAPTURETRACKING_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/ModRef.h"
namespace llvm {
class Value;
class Use;
+ class CaptureInfo;
class DataLayout;
class Instruction;
class DominatorTree;
@@ -71,10 +73,47 @@ namespace llvm {
bool ReturnCaptures, const DominatorTree &DT,
unsigned MaxUsesToExplore = 0);
+ /// Capture information for a specific Use.
+ struct UseCaptureInfo {
+ /// Components captured by this use.
+ CaptureComponents UseCC;
+ /// Components captured by the return value of the user of this Use.
+ CaptureComponents ResultCC;
+
+ UseCaptureInfo(CaptureComponents UseCC,
+ CaptureComponents ResultCC = CaptureComponents::None)
+ : UseCC(UseCC), ResultCC(ResultCC) {}
+
+ static UseCaptureInfo passthrough() {
+ return UseCaptureInfo(CaptureComponents::None, CaptureComponents::All);
+ }
+
+ bool isPassthrough() const {
+ return capturesNothing(UseCC) && capturesAnything(ResultCC);
+ }
+
+ operator CaptureComponents() const { return UseCC | ResultCC; }
+ };
+
/// This callback is used in conjunction with PointerMayBeCaptured. In
/// addition to the interface here, you'll need to provide your own getters
/// to see whether anything was captured.
struct CaptureTracker {
+ /// Action returned from captures().
+ enum Action {
+ /// Stop the traversal.
+ Stop,
+ /// Continue traversal, and also follow the return value of the user if
+ /// it has additional capture components (that is, if it has capture
+ /// components in Ret that are not part of Other).
+ Continue,
+ /// Continue traversal, but do not follow the return value of the user,
+ /// even if it has additional capture components. Should only be used if
+ /// captures() has already taken the potential return captures into
+ /// account.
+ ContinueIgnoringReturn,
+ };
+
virtual ~CaptureTracker();
/// tooManyUses - The depth of traversal has breached a limit. There may be
@@ -88,10 +127,12 @@ namespace llvm {
/// U->getUser() is always an Instruction.
virtual bool shouldExplore(const Use *U);
- /// captured - Information about the pointer was captured by the user of
- /// use U. Return true to stop the traversal or false to continue looking
- /// for more capturing instructions.
- virtual bool captured(const Use *U) = 0;
+ /// Use U directly captures CI.UseCC and additionally CI.ResultCC
+ /// through the return value of the user of U.
+ ///
+ /// Return one of Stop, Continue or ContinueIgnoringReturn to control
+ /// further traversal.
+ virtual Action captured(const Use *U, UseCaptureInfo CI) = 0;
/// isDereferenceableOrNull - Overload to allow clients with additional
/// knowledge about pointer dereferenceability to provide it and thereby
@@ -99,21 +140,18 @@ namespace llvm {
virtual bool isDereferenceableOrNull(Value *O, const DataLayout &DL);
};
- /// Types of use capture kinds, see \p DetermineUseCaptureKind.
- enum class UseCaptureKind {
- NO_CAPTURE,
- MAY_CAPTURE,
- PASSTHROUGH,
- };
-
/// Determine what kind of capture behaviour \p U may exhibit.
///
- /// A use can be no-capture, a use can potentially capture, or a use can be
- /// passthrough such that the uses of the user or \p U should be inspected.
+ /// The returned UseCaptureInfo contains the components captured directly
+ /// by the use (UseCC) and the components captured through the return value
+ /// of the user (ResultCC).
+ ///
+ /// \p Base is the starting value of the capture analysis, which is
+ /// relevant for address_is_null captures.
/// The \p IsDereferenceableOrNull callback is used to rule out capturing for
/// certain comparisons.
- UseCaptureKind
- DetermineUseCaptureKind(const Use &U,
+ UseCaptureInfo
+ DetermineUseCaptureKind(const Use &U, const Value *Base,
llvm::function_ref<bool(Value *, const DataLayout &)>
IsDereferenceableOrNull);
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 90fe864d4ae71..8e47e3c7b3a7c 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1692,6 +1692,11 @@ class CallBase : public Instruction {
return capturesNothing(getCaptureInfo(OpNo));
}
+ /// Returns whether the call has an argument that has an attribute like
+ /// captures(ret: address, provenance), where the return capture components
+ /// are not a subset of the other capture components.
+ bool hasArgumentWithAdditionalReturnCaptureComponents() const;
+
/// Determine whether this argument is passed by value.
bool isByValArgument(unsigned ArgNo) const {
return paramHasAttr(ArgNo, Attribute::ByVal);
diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h
index eb660844b0b3a..7f58f5236aedd 100644
--- a/llvm/include/llvm/Support/ModRef.h
+++ b/llvm/include/llvm/Support/ModRef.h
@@ -326,6 +326,10 @@ inline bool capturesFullProvenance(CaptureComponents CC) {
return (CC & CaptureComponents::Provenance) == CaptureComponents::Provenance;
}
+inline bool capturesAll(CaptureComponents CC) {
+ return CC == CaptureComponents::All;
+}
+
raw_ostream &operator<<(raw_ostream &OS, CaptureComponents CC);
/// Represents which components of the pointer may be captured in which
@@ -350,6 +354,15 @@ class CaptureInfo {
/// Create CaptureInfo that may capture all components of the pointer.
static CaptureInfo all() { return CaptureInfo(CaptureComponents::All); }
+ /// Create CaptureInfo that may only capture via the return value.
+ static CaptureInfo
+ retOnly(CaptureComponents RetComponents = CaptureComponents::All) {
+ return CaptureInfo(CaptureComponents::None, RetComponents);
+ }
+
+ /// Whether the pointer is only captured via the return value.
+ bool isRetOnly() const { return capturesNothing(OtherComponents); }
+
/// Get components potentially captured by the return value.
CaptureComponents getRetComponents() const { return RetComponents; }
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 969993c94a6e8..3365349922078 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -635,7 +635,13 @@ ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
// Only look at the no-capture or byval pointer arguments. If this
// pointer were passed to arguments that were neither of these, then it
// couldn't be no-capture.
- if (!(*CI)->getType()->isPointerTy() || !Call->doesNotCapture(ArgNo))
+ if (!(*CI)->getType()->isPointerTy())
+ continue;
+
+ // Make sure we still check captures(ret: address, provenance) arguments,
+ // as these wouldn't be treated as a capture at the call-site.
+ CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
+ if (!capturesNothing(Captures.getOtherComponents()))
continue;
AliasResult AR =
@@ -834,9 +840,15 @@ bool llvm::isBaseOfObject(const Value *V) {
}
bool llvm::isEscapeSource(const Value *V) {
- if (auto *CB = dyn_cast<CallBase>(V))
- return !isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB,
- true);
+ if (auto *CB = dyn_cast<CallBase>(V)) {
+ if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(CB, true))
+ return false;
+
+ // The return value of a function with a captures(ret: address, provenance)
+ // attribute is not necessarily an escape source. The return value may
+ // alias with a non-escaping object.
+ return !CB->hasArgumentWithAdditionalReturnCaptureComponents();
+ }
// The load case works because isNonEscapingLocalObject considers all
// stores to be escapes (it passes true for the StoreCaptures argument
diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp
index 9cb3c092768e4..6e5748c233240 100644
--- a/llvm/lib/Analysis/CaptureTracking.cpp
+++ b/llvm/lib/Analysis/CaptureTracking.cpp
@@ -81,14 +81,15 @@ struct SimpleCaptureTracker : public CaptureTracker {
Captured = true;
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
LLVM_DEBUG(dbgs() << "Captured by: " << *U->getUser() << "\n");
Captured = true;
- return true;
+ return Stop;
}
bool ReturnCaptures;
@@ -122,19 +123,21 @@ struct CapturesBefore : public CaptureTracker {
return !isPotentiallyReachable(I, BeforeHere, nullptr, DT, LI);
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
// Check isSafeToPrune() here rather than in shouldExplore() to avoid
// an expensive reachability query for every instruction we look at.
// Instead we only do one for actual capturing candidates.
if (isSafeToPrune(I))
- return false;
+ // If the use is not reachable, the instruction result isn't either.
+ return ContinueIgnoringReturn;
Captured = true;
- return true;
+ return Stop;
}
const Instruction *BeforeHere;
@@ -166,10 +169,11 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = &*F.getEntryBlock().begin();
}
- bool captured(const Use *U) override {
+ Action captured(const Use *U, UseCaptureInfo CI) override {
+ // TODO(captures): Use UseCaptureInfo.
Instruction *I = cast<Instruction>(U->getUser());
if (isa<ReturnInst>(I) && !ReturnCaptures)
- return false;
+ return ContinueIgnoringReturn;
if (!EarliestCapture)
EarliestCapture = I;
@@ -177,9 +181,10 @@ struct EarliestCaptures : public CaptureTracker {
EarliestCapture = DT.findNearestCommonDominator(EarliestCapture, I);
Captured = true;
- // Return false to continue analysis; we need to see all potential
- // captures.
- return false;
+ // Continue analysis, as we need to see all potential captures. However,
+ // we do not need to follow the instruction result, as this use will
+ // dominate any captures made through the instruction result.
+ return ContinueIgnoringReturn;
}
Instruction *EarliestCapture = nullptr;
@@ -260,14 +265,14 @@ Instruction *llvm::FindEarliestCapture(const Value *V, Function &F,
return CB.EarliestCapture;
}
-UseCaptureKind llvm::DetermineUseCaptureKind(
- const Use &U,
+UseCaptureInfo llvm::DetermineUseCaptureKind(
+ const Use &U, const Value *Base,
function_ref<bool(Value *, const DataLayout &)> IsDereferenceableOrNull) {
Instruction *I = dyn_cast<Instruction>(U.getUser());
// TODO: Investigate non-instruction uses.
if (!I)
- return UseCaptureKind::MAY_CAPTURE;
+ return CaptureComponents::All;
switch (I->getOpcode()) {
case Instruction::Call:
@@ -278,7 +283,7 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// by throwing an exception or not depending on the input value).
if (Call->onlyReadsMemory() && Call->doesNotThrow() &&
Call->getType()->isVoidTy())
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
// The pointer is not captured if returned pointer is not captured.
// NOTE: CaptureTracking users should not assume that only functions
@@ -286,13 +291,13 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// getUnderlyingObject in ValueTracking or DecomposeGEPExpression
// in BasicAA also need to know about this property.
if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(Call, true))
- return UseCaptureKind::PASSTHROUGH;
+ return UseCaptureInfo::passthrough();
// Volatile operations effectively capture the memory location that they
// load and store to.
if (auto *MI = dyn_cast<MemIntrinsic>(Call))
if (MI->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
+ return CaptureComponents::All;
// Calling a function pointer does not in itself cause the pointer to
// be captured. This is a subtle point considering that (for example)
@@ -301,30 +306,27 @@ UseCaptureKind llvm::DetermineUseCaptureKind(
// captured, even though the loaded value might be the pointer itself
// (think of self-referential objects).
if (Call->isCallee(&U))
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
// Not captured if only passed via 'nocapture' arguments.
assert(Call->isDataOperand(&U) && "Non-callee must be data operand");
- if (!Call->doesNotCapture(Call->getDataOperandNo(&U))) {
- // The parameter is not marked 'nocapture' - captured.
- return UseCaptureKind::MAY_CAPTURE;
- }
- return UseCaptureKind::NO_CAPTURE;
+ CaptureInfo CI = Call->getCaptureInfo(Call->getDataOperandNo(&U));
+ return UseCaptureInfo(CI.getOtherComponents(), CI.getRetComponents());
}
case Instruction::Load:
// Volatile loads make the address observable.
if (cast<LoadInst>(I)->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::All;
+ return CaptureComponents::None;
case Instruction::VAArg:
// "va-arg" from a pointer does not cause it to be captured.
- return UseCaptureKind::NO_CAPTURE;
+ return CaptureComponents::None;
case Instruction::Store:
// Stored the pointer - conservatively assume it may be captured.
// Volatile stores make the address observable.
if (U.getOperandNo() == 0 || cast<StoreInst>(I)->isVolatile())
- return UseCaptureKind::MAY_CAPTURE;
- return UseCaptu...
[truncated]
|
This reverts commit 07f057b. In upstream this landed ``` commit e56a6a2 error: cannot run gpg: No such file or directory Author: Nikita Popov <npopov@redhat.com> Date: Thu Feb 27 09:38:29 2025 +0100 Reapply [CaptureTracking][FunctionAttrs] Add support for CaptureInfo (llvm#125880) (llvm#128020) ``` which is another attempt at landing llvm#125880. The `-fbounds-safety` tests were already adjusted when this was landed before but we had to revert these changes when upstream reverted $125880. So now this PR reverts the revert of the test changes. Hopefully the upstream PR doesn't get reverted again. rdar://145796299
This reverts commit 07f057b. In upstream this landed ``` commit e56a6a2 error: cannot run gpg: No such file or directory Author: Nikita Popov <npopov@redhat.com> Date: Thu Feb 27 09:38:29 2025 +0100 Reapply [CaptureTracking][FunctionAttrs] Add support for CaptureInfo (llvm#125880) (llvm#128020) ``` which is another attempt at landing llvm#125880. The `-fbounds-safety` tests were already adjusted when this was landed before but we had to revert these changes when upstream reverted $125880. So now this PR reverts the revert of the test changes. Hopefully the upstream PR doesn't get reverted again. rdar://145796299
…lvm#125880) (llvm#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.
Relative to the previous attempt this includes two fixes:
provenance) arguments, as these will not count as a capture
at the call-site.
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:
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.
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.