https://github.com/nikic created https://github.com/llvm/llvm-project/pull/201316
The fact that a volatile access was performed on a certain address is an observable effect in the abstract machine, so volatile operations capture the address of the accessed pointer. However, they do not capture the provenance. This matches what we document in LangRef. While I'm pretty sure that this models the semantics correctly, I'm slightly concerned that we might be using the provenance capture here to paper over some other issue, though nothing specific comes to mind (and the test changes don't show anything problematic). >From f9570a12698ad58595bf803ef915115849abc1d4 Mon Sep 17 00:00:00 2001 From: Nikita Popov <[email protected]> Date: Wed, 3 Jun 2026 12:49:20 +0200 Subject: [PATCH] [CaptureTracking] Volatile operations only capture address The fact that a volatile access was performed on a certain address is an observable effect in the abstract machine, so volatile operations capture the address of the accessed pointer. However, they do not capture the provenance. --- clang/test/CodeGen/ms-intrinsics-other.c | 2 +- llvm/lib/Analysis/CaptureTracking.cpp | 31 ++++--- .../AMDGPU/amdgpu-simplify-libcall-sincos.ll | 2 +- llvm/test/Transforms/Attributor/nofpclass.ll | 89 ++++++------------- llvm/test/Transforms/Attributor/nonnull.ll | 2 +- llvm/test/Transforms/Attributor/nosync.ll | 4 +- llvm/test/Transforms/FunctionAttrs/atomic.ll | 4 +- .../Transforms/FunctionAttrs/initializes.ll | 8 +- .../Transforms/FunctionAttrs/nocapture.ll | 2 +- .../Transforms/FunctionAttrs/nofpclass.ll | 26 +++--- llvm/test/Transforms/FunctionAttrs/nonnull.ll | 16 ++-- .../Transforms/FunctionAttrs/readattrs.ll | 2 +- .../Transforms/FunctionAttrs/writeonly.ll | 2 +- .../constraint-elimination-placement.ll | 2 +- .../PhaseOrdering/branch-dom-cond.ll | 2 +- 15 files changed, 86 insertions(+), 108 deletions(-) diff --git a/clang/test/CodeGen/ms-intrinsics-other.c b/clang/test/CodeGen/ms-intrinsics-other.c index 013277cbf6a2d..9f183aee85824 100644 --- a/clang/test/CodeGen/ms-intrinsics-other.c +++ b/clang/test/CodeGen/ms-intrinsics-other.c @@ -159,7 +159,7 @@ LONG test_InterlockedAnd(LONG volatile *value, LONG mask) { LONG test_InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand) { return _InterlockedCompareExchange(Destination, Exchange, Comperand); } -// CHECK: define{{.*}}i32 @test_InterlockedCompareExchange(ptr{{[a-z_ ]*}}%Destination, i32{{[a-z_ ]*}}%Exchange, i32{{[a-z_ ]*}}%Comperand){{.*}}{ +// CHECK: define{{.*}}i32 @test_InterlockedCompareExchange(ptr{{[^%]*}}%Destination, i32{{[a-z_ ]*}}%Exchange, i32{{[a-z_ ]*}}%Comperand){{.*}}{ // CHECK: [[TMP:%[0-9]+]] = cmpxchg volatile ptr %Destination, i32 %Comperand, i32 %Exchange seq_cst seq_cst, align 4 // CHECK: [[RESULT:%[0-9]+]] = extractvalue { i32, i1 } [[TMP]], 0 // CHECK: ret i32 [[RESULT]] diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp index b5ee2430796cf..2af6dbe44dde0 100644 --- a/llvm/lib/Analysis/CaptureTracking.cpp +++ b/llvm/lib/Analysis/CaptureTracking.cpp @@ -284,12 +284,6 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) { Call, /*MustPreserveOffset=*/false)) 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 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) // the callee might return its own address. It is analogous to saying @@ -299,6 +293,13 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) { if (Call->isCallee(&U)) return CaptureComponents::None; + // Volatile operations make the address observable. + // TODO: This should probably get sunk into CallBase::getCaptureInfo(). + CaptureComponents VolatileCC = CaptureComponents::None; + if (auto *MI = dyn_cast<MemIntrinsic>(Call)) + if (MI->isVolatile()) + VolatileCC |= CaptureComponents::Address; + assert(Call->isDataOperand(&U) && "Non-callee must be data operand"); CaptureInfo CI = Call->getCaptureInfo(Call->getDataOperandNo(&U)); @@ -308,13 +309,13 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) { if (Call->onlyReadsMemory() && Call->getType()->isVoidTy()) Mask = CaptureComponents::Address; - return UseCaptureInfo(CI.getOtherComponents() & Mask, + return UseCaptureInfo((CI.getOtherComponents() & Mask) | VolatileCC, CI.getRetComponents()); } case Instruction::Load: // Volatile loads make the address observable. if (cast<LoadInst>(I)->isVolatile()) - return CaptureComponents::All; + return CaptureComponents::Address; return CaptureComponents::None; case Instruction::VAArg: // "va-arg" from a pointer does not cause it to be captured. @@ -327,17 +328,19 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) { // Volatile stores make the address observable. if (cast<StoreInst>(I)->isVolatile()) - return CaptureComponents::All; + return CaptureComponents::Address; return CaptureComponents::None; case Instruction::AtomicRMW: { // atomicrmw conceptually includes both a load and store from // the same location. // As with a store, the location being accessed is not captured, // but the value being stored is. - // Volatile stores make the address observable. auto *ARMWI = cast<AtomicRMWInst>(I); - if (U.getOperandNo() == 1 || ARMWI->isVolatile()) + if (U.getOperandNo() == 1) return CaptureComponents::All; + // Volatile stores make the address observable. + if (ARMWI->isVolatile()) + return CaptureComponents::Address; return CaptureComponents::None; } case Instruction::AtomicCmpXchg: { @@ -345,10 +348,12 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) { // the same location. // As with a store, the location being accessed is not captured, // but the value being stored is. - // Volatile stores make the address observable. auto *ACXI = cast<AtomicCmpXchgInst>(I); - if (U.getOperandNo() == 1 || U.getOperandNo() == 2 || ACXI->isVolatile()) + if (U.getOperandNo() == 1 || U.getOperandNo() == 2) return CaptureComponents::All; + // Volatile stores make the address observable. + if (ACXI->isVolatile()) + return CaptureComponents::Address; return CaptureComponents::None; } case Instruction::GetElementPtr: diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll index 5f14afc017060..0b8ea0a4dd131 100644 --- a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll +++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-sincos.ll @@ -910,7 +910,7 @@ entry: define void @sincos_f32_repeated_uses(float %x, ptr addrspace(1) %sin_out, ptr addrspace(1) %cos_out) { ; CHECK-LABEL: define void @sincos_f32_repeated_uses -; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) [[SIN_OUT:%.*]], ptr addrspace(1) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +; CHECK-SAME: (float [[X:%.*]], ptr addrspace(1) captures(address) [[SIN_OUT:%.*]], ptr addrspace(1) captures(address) [[COS_OUT:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[__SINCOS_:%.*]] = alloca float, align 4, addrspace(5) ; CHECK-NEXT: [[TMP0:%.*]] = call contract float @_Z6sincosfPU3AS5f(float [[X]], ptr addrspace(5) [[__SINCOS_]]) diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll index 028a4eed39dc5..ac0aedc7716d5 100644 --- a/llvm/test/Transforms/Attributor/nofpclass.ll +++ b/llvm/test/Transforms/Attributor/nofpclass.ll @@ -275,7 +275,7 @@ define float @mutually_recursive1(float %arg) { define float @recursive_phi(ptr %ptr) { ; CHECK-LABEL: define nofpclass(nan) float @recursive_phi -; CHECK-SAME: (ptr [[PTR:%.*]]) { +; CHECK-SAME: (ptr nofree [[PTR:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @ret_nofpclass_nan() ; CHECK-NEXT: br label [[LOOP:%.*]] @@ -3655,21 +3655,13 @@ define float @fadd_double_no_zero__output_only_is_dynamic(float noundef nofpclas ; Implies return must be 0 define float @assume_select_condition_not_nan(float noundef %arg) { -; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; TUNIT-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @assume_select_condition_not_nan -; TUNIT-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19:[0-9]+]] { -; TUNIT-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 -; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR28:[0-9]+]] -; TUNIT-NEXT: [[SELECT:%.*]] = select i1 [[ORD]], float 0.000000e+00, float [[ARG]] -; TUNIT-NEXT: ret float [[SELECT]] -; -; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; CGSCC-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @assume_select_condition_not_nan -; CGSCC-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19:[0-9]+]] { -; CGSCC-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 -; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR27:[0-9]+]] -; CGSCC-NEXT: [[SELECT:%.*]] = select i1 [[ORD]], float 0.000000e+00, float [[ARG]] -; CGSCC-NEXT: ret float [[SELECT]] +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) +; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @assume_select_condition_not_nan +; CHECK-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19:[0-9]+]] { +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR22]] +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[ORD]], float 0.000000e+00, float [[ARG]] +; CHECK-NEXT: ret float [[SELECT]] ; %ord = fcmp ord float %arg, 0.0 call void @llvm.assume(i1 %ord) @@ -3678,21 +3670,13 @@ define float @assume_select_condition_not_nan(float noundef %arg) { } define float @assume_select_condition_not_nan_commute(float noundef %arg) { -; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; TUNIT-LABEL: define noundef nofpclass(inf nzero sub norm) float @assume_select_condition_not_nan_commute -; TUNIT-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19]] { -; TUNIT-NEXT: [[UNO:%.*]] = fcmp uno float [[ARG]], 0.000000e+00 -; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[UNO]]) #[[ATTR28]] -; TUNIT-NEXT: [[SELECT:%.*]] = select i1 [[UNO]], float [[ARG]], float 0.000000e+00 -; TUNIT-NEXT: ret float [[SELECT]] -; -; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; CGSCC-LABEL: define noundef nofpclass(inf nzero sub norm) float @assume_select_condition_not_nan_commute -; CGSCC-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19]] { -; CGSCC-NEXT: [[UNO:%.*]] = fcmp uno float [[ARG]], 0.000000e+00 -; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[UNO]]) #[[ATTR27]] -; CGSCC-NEXT: [[SELECT:%.*]] = select i1 [[UNO]], float [[ARG]], float 0.000000e+00 -; CGSCC-NEXT: ret float [[SELECT]] +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) +; CHECK-LABEL: define noundef nofpclass(inf nzero sub norm) float @assume_select_condition_not_nan_commute +; CHECK-SAME: (float noundef [[ARG:%.*]]) #[[ATTR19]] { +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNO]]) #[[ATTR22]] +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[UNO]], float [[ARG]], float 0.000000e+00 +; CHECK-NEXT: ret float [[SELECT]] ; %uno = fcmp uno float %arg, 0.0 call void @llvm.assume(i1 %uno) @@ -3702,21 +3686,13 @@ define float @assume_select_condition_not_nan_commute(float noundef %arg) { ; Implies no return nan define float @assume_load(ptr %ptr) { -; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) -; TUNIT-LABEL: define nofpclass(nan) float @assume_load -; TUNIT-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR20:[0-9]+]] { -; TUNIT-NEXT: [[VAL:%.*]] = load float, ptr [[PTR]], align 4 -; TUNIT-NEXT: [[ORD:%.*]] = fcmp ord float [[VAL]], 0.000000e+00 -; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR28]] -; TUNIT-NEXT: ret float [[VAL]] -; -; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) -; CGSCC-LABEL: define nofpclass(nan) float @assume_load -; CGSCC-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR20:[0-9]+]] { -; CGSCC-NEXT: [[VAL:%.*]] = load float, ptr [[PTR]], align 4 -; CGSCC-NEXT: [[ORD:%.*]] = fcmp ord float [[VAL]], 0.000000e+00 -; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR27]] -; CGSCC-NEXT: ret float [[VAL]] +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) +; CHECK-LABEL: define nofpclass(nan) float @assume_load +; CHECK-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[PTR:%.*]]) #[[ATTR20:[0-9]+]] { +; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[PTR]], align 4 +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[VAL]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR22]] +; CHECK-NEXT: ret float [[VAL]] ; %val = load float, ptr %ptr %ord = fcmp ord float %val, 0.0 @@ -3726,19 +3702,12 @@ define float @assume_load(ptr %ptr) { ; FIXME: Why is this not working? define float @assume_returned_arg(float noundef %arg) { -; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; TUNIT-LABEL: define noundef float @assume_returned_arg -; TUNIT-SAME: (float noundef returned [[ARG:%.*]]) #[[ATTR19]] { -; TUNIT-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 -; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR28]] -; TUNIT-NEXT: ret float [[ARG]] -; -; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) -; CGSCC-LABEL: define noundef float @assume_returned_arg -; CGSCC-SAME: (float noundef returned [[ARG:%.*]]) #[[ATTR19]] { -; CGSCC-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 -; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR27]] -; CGSCC-NEXT: ret float [[ARG]] +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) +; CHECK-LABEL: define noundef float @assume_returned_arg +; CHECK-SAME: (float noundef returned [[ARG:%.*]]) #[[ATTR19]] { +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[ARG]], 0.000000e+00 +; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ORD]]) #[[ATTR22]] +; CHECK-NEXT: ret float [[ARG]] ; %ord = fcmp ord float %arg, 0.0 call void @llvm.assume(i1 %ord) @@ -4012,7 +3981,5 @@ attributes #9 = { denormal_fpenv(ieee|dynamic) } !2 = !{i32 512} ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; CGSCC-CI: {{.*}} ; CGSCC-CV: {{.*}} -; TUNIT-CI: {{.*}} ; TUNIT-CV: {{.*}} diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll index aa0cea33d02d0..0aa0e72d403ab 100644 --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -843,7 +843,7 @@ f: define i8 @parent6(ptr %a, ptr %b) { ; CHECK-LABEL: define {{[^@]+}}@parent6 -; CHECK-SAME: (ptr [[A:%.*]], ptr noundef [[B:%.*]]) { +; CHECK-SAME: (ptr [[A:%.*]], ptr nofree noundef [[B:%.*]]) { ; CHECK-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 ; CHECK-NEXT: call void @use1nonnull(ptr nonnull [[A]]) ; CHECK-NEXT: ret i8 [[C]] diff --git a/llvm/test/Transforms/Attributor/nosync.ll b/llvm/test/Transforms/Attributor/nosync.ll index 49b02beb8d181..bce49fcca913e 100644 --- a/llvm/test/Transforms/Attributor/nosync.ll +++ b/llvm/test/Transforms/Attributor/nosync.ll @@ -105,7 +105,7 @@ define i32 @load_acquire(ptr nocapture readonly %arg) norecurse nounwind uwtable define void @load_release(ptr nocapture %arg) norecurse nounwind uwtable { ; CHECK: Function Attrs: norecurse nounwind memory(argmem: readwrite) uwtable ; CHECK-LABEL: define {{[^@]+}}@load_release -; CHECK-SAME: (ptr noundef writeonly align 4 captures(none) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-SAME: (ptr nofree noundef writeonly align 4 captures(none) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: store atomic volatile i32 10, ptr [[ARG]] release, align 4 ; CHECK-NEXT: ret void ; @@ -118,7 +118,7 @@ define void @load_release(ptr nocapture %arg) norecurse nounwind uwtable { define void @load_volatile_release(ptr nocapture %arg) norecurse nounwind uwtable { ; CHECK: Function Attrs: norecurse nounwind memory(argmem: readwrite) uwtable ; CHECK-LABEL: define {{[^@]+}}@load_volatile_release -; CHECK-SAME: (ptr noundef writeonly align 4 captures(none) [[ARG:%.*]]) #[[ATTR3]] { +; CHECK-SAME: (ptr nofree noundef writeonly align 4 captures(none) [[ARG:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: store atomic volatile i32 10, ptr [[ARG]] release, align 4 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 8d62a8d20388c..2b6a8e8e8ae37 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -112,7 +112,7 @@ define void @atomicrmw_acq_rel_arg(ptr %x) { define void @atomicrmw_monotonic_volatile_arg(ptr %x) { ; CHECK: Function Attrs: norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @atomicrmw_monotonic_volatile_arg( -; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-SAME: ptr captures(address) [[X:%.*]]) #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: [[TMP1:%.*]] = atomicrmw volatile add ptr [[X]], i32 1 monotonic, align 4 ; CHECK-NEXT: ret void ; @@ -145,7 +145,7 @@ define void @cmpxchg_acq_rel_arg(ptr %x) { define void @cmpxchg_monotonic_volatile_arg(ptr %x) { ; CHECK: Function Attrs: norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @cmpxchg_monotonic_volatile_arg( -; CHECK-SAME: ptr [[X:%.*]]) #[[ATTR3]] { +; CHECK-SAME: ptr captures(address) [[X:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: [[TMP1:%.*]] = cmpxchg volatile ptr [[X]], i32 0, i32 1 monotonic monotonic, align 4 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/FunctionAttrs/initializes.ll b/llvm/test/Transforms/FunctionAttrs/initializes.ll index 0e1028c18db33..98dc61ca82b2d 100644 --- a/llvm/test/Transforms/FunctionAttrs/initializes.ll +++ b/llvm/test/Transforms/FunctionAttrs/initializes.ll @@ -130,7 +130,7 @@ define void @store_offset(ptr %p) { define void @store_volatile(ptr %p) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @store_volatile( -; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-SAME: ptr captures(address) [[P:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8 ; CHECK-NEXT: store volatile i32 123, ptr [[G]], align 4 ; CHECK-NEXT: ret void @@ -445,7 +445,7 @@ define void @memset_neg(ptr %p) { define void @memset_volatile(ptr %p) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(argmem: write, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @memset_volatile( -; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] { +; CHECK-SAME: ptr writeonly captures(address) [[P:%.*]]) #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 9, i1 true) ; CHECK-NEXT: ret void ; @@ -480,7 +480,7 @@ define void @memcpy(ptr %p, ptr %p2) { define void @memcpy_volatile(ptr %p, ptr %p2) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @memcpy_volatile( -; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR2]] { +; CHECK-SAME: ptr writeonly captures(address) [[P:%.*]], ptr readonly captures(address) [[P2:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true) ; CHECK-NEXT: ret void ; @@ -543,7 +543,7 @@ define void @memmove(ptr %p, ptr %p2) { define void @memmove_volatile(ptr %p, ptr %p2) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: define void @memmove_volatile( -; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR2]] { +; CHECK-SAME: ptr writeonly captures(address) [[P:%.*]], ptr readonly captures(address) [[P2:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true) ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index aab801a9a7bf7..d291d6d2d387d 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -699,7 +699,7 @@ define void @test_atomicrmw(ptr %p) { define void @test_volatile(ptr %x) { ; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; FNATTRS-LABEL: define void @test_volatile -; FNATTRS-SAME: (ptr [[X:%.*]]) #[[ATTR14:[0-9]+]] { +; FNATTRS-SAME: (ptr captures(address) [[X:%.*]]) #[[ATTR14:[0-9]+]] { ; FNATTRS-NEXT: entry: ; FNATTRS-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1 ; FNATTRS-NEXT: store volatile i32 0, ptr [[GEP]], align 4 diff --git a/llvm/test/Transforms/FunctionAttrs/nofpclass.ll b/llvm/test/Transforms/FunctionAttrs/nofpclass.ll index b8cd0e3b54d7b..c984866979599 100644 --- a/llvm/test/Transforms/FunctionAttrs/nofpclass.ll +++ b/llvm/test/Transforms/FunctionAttrs/nofpclass.ll @@ -3,7 +3,7 @@ define float @return_f32_extern(ptr %ptr) { ; CHECK-LABEL: define float @return_f32_extern( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: ret float [[VAL]] ; @@ -14,7 +14,7 @@ define float @return_f32_extern(ptr %ptr) { ; Deduce nofpclass(inf) on return define internal float @only_noinf_ret_uses(ptr %ptr) { ; CHECK-LABEL: define internal nofpclass(inf) float @only_noinf_ret_uses( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: ret float [[VAL]] ; @@ -24,7 +24,7 @@ define internal float @only_noinf_ret_uses(ptr %ptr) { define float @calls_no_inf_return(ptr %ptr) { ; CHECK-LABEL: define float @calls_no_inf_return( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[CALL0:%.*]] = call float @return_f32_extern(ptr [[PTR]]) ; CHECK-NEXT: [[CALL1:%.*]] = call nofpclass(inf) float @only_noinf_ret_uses(ptr [[PTR]]) ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[CALL0]], [[CALL1]] @@ -39,7 +39,7 @@ define float @calls_no_inf_return(ptr %ptr) { ; Deduce nofpclass(nan) on return, not inf or zero define internal float @merged_ret_uses(ptr %ptr) { ; CHECK-LABEL: define internal nofpclass(nan) float @merged_ret_uses( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: ret float [[VAL]] ; @@ -49,7 +49,7 @@ define internal float @merged_ret_uses(ptr %ptr) { define float @calls_merge_rets(ptr %ptr) { ; CHECK-LABEL: define float @calls_merge_rets( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[CALL0:%.*]] = call nofpclass(nan inf) float @merged_ret_uses(ptr [[PTR]]) ; CHECK-NEXT: [[CALL1:%.*]] = call nofpclass(nan zero) float @merged_ret_uses(ptr [[PTR]]) ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[CALL0]], [[CALL1]] @@ -64,7 +64,7 @@ define float @calls_merge_rets(ptr %ptr) { ; Do not infer nofpclass on return define internal float @called_with_wrong_ret_type(ptr %ptr) { ; CHECK-LABEL: define internal float @called_with_wrong_ret_type( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: ret float [[VAL]] ; @@ -74,7 +74,7 @@ define internal float @called_with_wrong_ret_type(ptr %ptr) { define <2 x half> @wrong_callee_ret_type(ptr %ptr) { ; CHECK-LABEL: define <2 x half> @wrong_callee_ret_type( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) <2 x half> @called_with_wrong_ret_type(ptr [[PTR]]) ; CHECK-NEXT: ret <2 x half> [[RET]] ; @@ -85,7 +85,7 @@ define <2 x half> @wrong_callee_ret_type(ptr %ptr) { ; Do not infer nofpclass on return define internal float @non_callee_use_ret(ptr %ptr) { ; CHECK-LABEL: define internal float @non_callee_use_ret( -; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: ret float [[VAL]] ; @@ -253,7 +253,7 @@ define float @caller_refine_existing_nofpclass(float %arg, ptr %ptr) { ; Do not infer nofpclass define internal float @nofpclass_non_call_user(float %arg, ptr %ptr) { ; CHECK-LABEL: define internal float @nofpclass_non_call_user( -; CHECK-SAME: float [[ARG:%.*]], ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: float [[ARG:%.*]], ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[LOAD:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG]], [[LOAD]] ; CHECK-NEXT: ret float [[ADD]] @@ -265,7 +265,7 @@ define internal float @nofpclass_non_call_user(float %arg, ptr %ptr) { define float @call_nofpclass_non_call_user(float %arg, ptr %ptr, ptr %fptr.ptr) { ; CHECK-LABEL: define float @call_nofpclass_non_call_user( -; CHECK-SAME: float [[ARG:%.*]], ptr [[PTR:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[FPTR_PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: float [[ARG:%.*]], ptr captures(address) [[PTR:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[FPTR_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @nofpclass_non_call_user(float nofpclass(nan) [[ARG]], ptr [[PTR]]) ; CHECK-NEXT: store ptr @nofpclass_non_call_user, ptr [[FPTR_PTR]], align 8 ; CHECK-NEXT: ret float [[RET]] @@ -278,7 +278,7 @@ define float @call_nofpclass_non_call_user(float %arg, ptr %ptr, ptr %fptr.ptr) ; TODO: This case is missed define internal float @transitive_nonan_callee0(float %arg, ptr %ptr) { ; CHECK-LABEL: define internal float @transitive_nonan_callee0( -; CHECK-SAME: float [[ARG:%.*]], ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: float [[ARG:%.*]], ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[LOAD:%.*]] = load volatile float, ptr [[PTR]], align 4 ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG]], [[LOAD]] ; CHECK-NEXT: ret float [[ADD]] @@ -290,7 +290,7 @@ define internal float @transitive_nonan_callee0(float %arg, ptr %ptr) { define internal float @transitive_nonan_callee1(float %arg, ptr %ptr) { ; CHECK-LABEL: define internal nofpclass(nan) float @transitive_nonan_callee1( -; CHECK-SAME: float nofpclass(nan) [[ARG:%.*]], ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: float nofpclass(nan) [[ARG:%.*]], ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[RET:%.*]] = call float @transitive_nonan_callee0(float [[ARG]], ptr [[PTR]]) ; CHECK-NEXT: ret float [[RET]] ; @@ -300,7 +300,7 @@ define internal float @transitive_nonan_callee1(float %arg, ptr %ptr) { define float @caller_transitive_nonan(float %arg, ptr %ptr) { ; CHECK-LABEL: define float @caller_transitive_nonan( -; CHECK-SAME: float [[ARG:%.*]], ptr [[PTR:%.*]]) #[[ATTR0]] { +; CHECK-SAME: float [[ARG:%.*]], ptr captures(address) [[PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @transitive_nonan_callee1(float nofpclass(nan) [[ARG]], ptr [[PTR]]) ; CHECK-NEXT: ret float [[RET]] ; diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll index 869c85e60a8e6..d87987c307c98 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -855,11 +855,17 @@ f: ; The volatile load can trap, so we can't guarantee that we'll get to the call. define i8 @parent6(ptr %a, ptr %b) { -; COMMON-LABEL: define i8 @parent6( -; COMMON-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) { -; COMMON-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 -; COMMON-NEXT: call void @use1nonnull(ptr [[A]]) -; COMMON-NEXT: ret i8 [[C]] +; FNATTRS-LABEL: define i8 @parent6( +; FNATTRS-SAME: ptr [[A:%.*]], ptr captures(address) [[B:%.*]]) { +; FNATTRS-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 +; FNATTRS-NEXT: call void @use1nonnull(ptr [[A]]) +; FNATTRS-NEXT: ret i8 [[C]] +; +; ATTRIBUTOR-LABEL: define i8 @parent6( +; ATTRIBUTOR-SAME: ptr [[A:%.*]], ptr nofree [[B:%.*]]) { +; ATTRIBUTOR-NEXT: [[C:%.*]] = load volatile i8, ptr [[B]], align 1 +; ATTRIBUTOR-NEXT: call void @use1nonnull(ptr [[A]]) +; ATTRIBUTOR-NEXT: ret i8 [[C]] ; %c = load volatile i8, ptr %b diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll index 9704829fd4415..4d6298a2fcca6 100644 --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -348,7 +348,7 @@ define <4 x i32> @test12_2(<4 x ptr> %ptrs) { define i32 @volatile_load(ptr %p) { ; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; FNATTRS-LABEL: define {{[^@]+}}@volatile_load -; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR11:[0-9]+]] { +; FNATTRS-SAME: (ptr captures(address) [[P:%.*]]) #[[ATTR11:[0-9]+]] { ; FNATTRS-NEXT: [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4 ; FNATTRS-NEXT: ret i32 [[LOAD]] ; diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index 9a97dec6df2a9..519af1f39f394 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -147,7 +147,7 @@ define void @test_readwrite(ptr %p) { define void @test_volatile(ptr %p) { ; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; FNATTRS-LABEL: define {{[^@]+}}@test_volatile -; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] { +; FNATTRS-SAME: (ptr captures(address) [[P:%.*]]) #[[ATTR6:[0-9]+]] { ; FNATTRS-NEXT: store volatile i8 0, ptr [[P]], align 1 ; FNATTRS-NEXT: ret void ; diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll index 7e59fecd522d0..c78299687945c 100644 --- a/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll +++ b/llvm/test/Transforms/PhaseOrdering/AArch64/constraint-elimination-placement.ll @@ -6,7 +6,7 @@ target triple = "arm64-apple-macosx" define i1 @test_order_1(ptr %this, ptr noalias %other, i1 %tobool9.not, i32 %call) { ; CHECK-LABEL: define noundef i1 @test_order_1( -; CHECK-SAME: ptr writeonly captures(none) [[THIS:%.*]], ptr noalias [[OTHER:%.*]], i1 [[TOBOOL9_NOT:%.*]], i32 [[CALL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-SAME: ptr writeonly captures(none) [[THIS:%.*]], ptr noalias captures(address) [[OTHER:%.*]], i1 [[TOBOOL9_NOT:%.*]], i32 [[CALL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 [[TOBOOL9_NOT]], label [[EXIT:%.*]], label [[FOR_COND_PREHEADER:%.*]] ; CHECK: for.cond.preheader: diff --git a/llvm/test/Transforms/PhaseOrdering/branch-dom-cond.ll b/llvm/test/Transforms/PhaseOrdering/branch-dom-cond.ll index 904ea6c2d9e73..b4dc5d8e582e0 100644 --- a/llvm/test/Transforms/PhaseOrdering/branch-dom-cond.ll +++ b/llvm/test/Transforms/PhaseOrdering/branch-dom-cond.ll @@ -3,7 +3,7 @@ define void @growTables(ptr %p) { ; CHECK-LABEL: define void @growTables( -; CHECK-SAME: ptr [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-SAME: ptr captures(address) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[CALL:%.*]] = load volatile i32, ptr [[P]], align 4 ; CHECK-NEXT: [[CMP71:%.*]] = icmp sgt i32 [[CALL]], 0 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
