/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override {
- return F<AAType, G<AAType, Base, StateType>, StateType>::updateImpl(A) |
- G<AAType, Base, StateType>::updateImpl(A);
+ ChangeStatus ChangedF = F<AAType, G<AAType, Base, StateType>, StateType>::updateImpl(A);
+ ChangeStatus ChangedG = G<AAType, Base, StateType>::updateImpl(A);
+ return ChangedF | ChangedG;
}
};
static int64_t getKnownNonNullAndDerefBytesForUse(
Attributor &A, AbstractAttribute &QueryingAA, Value &AssociatedValue,
const Use *U, const Instruction *I, bool &IsNonNull, bool &TrackUse) {
- // TODO: Add GEP support
TrackUse = false;
+ const Value *UseV = U->get();
+ if (!UseV->getType()->isPointerTy())
+ return 0;
+
+ Type *PtrTy = UseV->getType();
const Function *F = I->getFunction();
- bool NullPointerIsDefined = F ? F->nullPointerIsDefined() : true;
+ bool NullPointerIsDefined =
+ F ? llvm::NullPointerIsDefined(F, PtrTy->getPointerAddressSpace()) : true;
const DataLayout &DL = A.getInfoCache().getDL();
if (ImmutableCallSite ICS = ImmutableCallSite(I)) {
if (ICS.isBundleOperand(U))
int64_t Offset;
if (const Value *Base = getBasePointerOfAccessPointerOperand(I, Offset, DL)) {
- if (Base == &AssociatedValue) {
+ if (Base == &AssociatedValue && getPointerOperand(I) == UseV) {
int64_t DerefBytes =
- Offset +
- (int64_t)DL.getTypeStoreSize(
- getPointerOperand(I)->getType()->getPointerElementType());
+ Offset + (int64_t)DL.getTypeStoreSize(PtrTy->getPointerElementType());
IsNonNull |= !NullPointerIsDefined;
return DerefBytes;
}
}
+ if (const Value *Base =
+ GetPointerBaseWithConstantOffset(UseV, Offset, DL,
+ /*AllowNonInbounds*/ false)) {
+ auto &DerefAA =
+ A.getAAFor<AADereferenceable>(QueryingAA, IRPosition::value(*Base));
+ IsNonNull |= (!NullPointerIsDefined && DerefAA.isKnownNonNull());
+ IsNonNull |= (!NullPointerIsDefined && (Offset != 0));
+ int64_t DerefBytes = DerefAA.getKnownDereferenceableBytes();
+ return std::max(int64_t(0), DerefBytes - Offset);
+ }
return 0;
}
+
struct AANonNullImpl : AANonNull {
AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {}
// for overflows of the dereferenceable bytes.
int64_t OffsetSExt = Offset.getSExtValue();
if (OffsetSExt < 0)
- Offset = 0;
+ OffsetSExt = 0;
T.takeAssumedDerefBytesMinimum(
std::max(int64_t(0), DerefBytes - OffsetSExt));
;
; Verify the maybe-redefined function is not annotated:
;
-; CHECK: Function Attrs: noinline nounwind uwtable
-; CHECK: define linkonce_odr i32* @maybe_redefined_fn(i32* %r)
+; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: define linkonce_odr i32* @maybe_redefined_fn(i32* %r)
;
-; CHECK: Function Attrs: noinline nounwind uwtable
-; CHECK: define i32* @calls_maybe_redefined_fn(i32* returned %r)
+; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
+; ATTRIBUTOR: define i32* @calls_maybe_redefined_fn(i32* returned %r)
;
; BOTH: Function Attrs: noinline nounwind uwtable
; BOTH-NEXT: define linkonce_odr i32* @maybe_redefined_fn(i32* %r)
%c3 = call i32* @non_exact_3(i32* %a)
; We can use the information of the weak function non_exact_3 because it was
; given to us and not derived (the alignment of the returned argument).
-; CHECK: %c4 = load i32, i32* %c3, align 32
+; ATTRIBUTOR: %c4 = load i32, i32* %c3, align 32
%c4 = load i32, i32* %c3
; FIXME: %c2 and %c3 should be replaced but not %c0 or %c1!
-; CHECK: %add1 = add i32 %c0, %c1
-; CHECK: %add2 = add i32 %add1, %c2
-; CHECK: %add3 = add i32 %add2, %c3
+; ATTRIBUTOR: %add1 = add i32 %c0, %c1
+; ATTRIBUTOR: %add2 = add i32 %add1, %c2
+; ATTRIBUTOR: %add3 = add i32 %add2, %c4
%add1 = add i32 %c0, %c1
%add2 = add i32 %add1, %c2
%add3 = add i32 %add2, %c4
}
define i32* @use_const() #0 {
%c = call i32* @ret_const()
- ; CHECK: ret i32* bitcast (i8* @G to i32*)
+ ; ATTRIBUTOR: ret i32* bitcast (i8* @G to i32*)
ret i32* %c
}
define i32* @dont_use_const() #0 {
%c = musttail call i32* @ret_const()
- ; CHECK: ret i32* %c
+ ; ATTRIBUTOR: ret i32* %c
ret i32* %c
}
-; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
+; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
declare void @deref_phi_user(i32* %a);
for.cond: ; preds = %for.inc, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%a.addr.0 = phi i32* [ %a, %entry ], [ %incdec.ptr, %for.inc ]
-; CHECK: call void @deref_phi_user(i32* dereferenceable(4000) %a.addr.0)
+; ATTRIBUTOR: call void @deref_phi_user(i32* nonnull dereferenceable(4000) %a.addr.0)
call void @deref_phi_user(i32* %a.addr.0)
%tmp = load i32, i32* %a.addr.0, align 4
%cmp = icmp slt i32 %i.0, %tmp
for.cond: ; preds = %for.inc, %entry
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%a.addr.0 = phi i32* [ %a, %entry ], [ %incdec.ptr, %for.inc ]
-; CHECK: call void @deref_phi_user(i32* %a.addr.0)
+; ATTRIBUTOR: call void @deref_phi_user(i32* nonnull %a.addr.0)
call void @deref_phi_user(i32* %a.addr.0)
%tmp = load i32, i32* %a.addr.0, align 4
%cmp = icmp slt i32 %i.0, %tmp
declare void @unknown(i8*)
define void @test_callsite() {
entry:
-; We know that 'null' in AS 0 does not alias anything and cannot be captured
-; CHECK: call void @unknown(i8* noalias nocapture null)
+; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm.
+; ATTRIBUTOR: call void @unknown(i8* noalias null)
call void @unknown(i8* null)
ret void
}