return indicatePessimisticFixpoint();
// Once returned values "directly" present in the code are handled we try to
- // resolve returned calls.
+ // resolve returned calls. To avoid modifications to the ReturnedValues map
+ // while we iterate over it we kept record of potential new entries in a copy
+ // map, NewRVsMap.
decltype(ReturnedValues) NewRVsMap;
- for (auto &It : ReturnedValues) {
- LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned value: " << *It.first
- << " by #" << It.second.size() << " RIs\n");
- CallBase *CB = dyn_cast<CallBase>(It.first);
+
+ auto HandleReturnValue = [&](Value *RV, SmallSetVector<ReturnInst *, 4> &RIs) {
+ LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned value: " << *RV
+ << " by #" << RIs.size() << " RIs\n");
+ CallBase *CB = dyn_cast<CallBase>(RV);
if (!CB || UnresolvedCalls.count(CB))
- continue;
+ return;
if (!CB->getCalledFunction()) {
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Unresolved call: " << *CB
<< "\n");
UnresolvedCalls.insert(CB);
- continue;
+ return;
}
// TODO: use the function scope once we have call site AAReturnedValues.
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Unresolved call: " << *CB
<< "\n");
UnresolvedCalls.insert(CB);
- continue;
+ return;
}
// Do not try to learn partial information. If the callee has unresolved
auto &RetValAAUnresolvedCalls = RetValAA.getUnresolvedCalls();
if (!RetValAAUnresolvedCalls.empty()) {
UnresolvedCalls.insert(CB);
- continue;
+ return;
}
// Now check if we can track transitively returned values. If possible, thus
}
if (Unresolved)
- continue;
+ return;
// Now track transitively returned values.
unsigned &NumRetAA = NumReturnedValuesPerKnownAA[CB];
if (NumRetAA == RetValAA.getNumReturnValues()) {
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Skip call as it has not "
"changed since it was seen last\n");
- continue;
+ return;
}
NumRetAA = RetValAA.getNumReturnValues();
continue;
} else if (isa<Constant>(RetVal)) {
// Constants are valid everywhere, we can simply take them.
- NewRVsMap[RetVal].insert(It.second.begin(), It.second.end());
+ NewRVsMap[RetVal].insert(RIs.begin(), RIs.end());
continue;
}
}
- }
+ };
+
+ for (auto &It : ReturnedValues)
+ HandleReturnValue(It.first, It.second);
+
+ // Because processing the new information can again lead to new return values
+ // we have to be careful and iterate until this iteration is complete. The
+ // idea is that we are in a stable state at the end of an update. All return
+ // values have been handled and properly categorized. We might not update
+ // again if we have not requested a non-fix attribute so we cannot "wait" for
+ // the next update to analyze a new return value.
+ while (!NewRVsMap.empty()) {
+ auto It = std::move(NewRVsMap.back());
+ NewRVsMap.pop_back();
- // To avoid modifications to the ReturnedValues map while we iterate over it
- // we kept record of potential new entries in a copy map, NewRVsMap.
- for (auto &It : NewRVsMap) {
assert(!It.second.empty() && "Entry does not add anything.");
auto &ReturnInsts = ReturnedValues[It.first];
for (ReturnInst *RI : It.second)
if (ReturnInsts.insert(RI)) {
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Add new returned value "
<< *It.first << " => " << *RI << "\n");
+ HandleReturnValue(It.first, ReturnInsts);
Changed = true;
}
}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
-; RUN: opt -attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=15 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=15 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
+; RUN: opt -attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=14 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=14 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
;
ret i32* %c
}
+; UTC_ARGS: --disable
+;
+; Verify we do not derive constraints for @_Z3fooP1X as if it was returning `null`.
+;
+; CHEKC-NOT: noalias
+; CHECK-NOT: align 536870912
+
+%struct.Y = type { %struct.X }
+%struct.X = type { i32 (...)** }
+
+@_ZTI1X = external dso_local constant { i8*, i8* }, align 8
+@_ZTI1Y = external dso_local constant { i8*, i8*, i8* }, align 8
+
+define internal i8* @_ZN1Y3barEv(%struct.Y* %this) align 2 {
+entry:
+ %0 = bitcast %struct.Y* %this to i8*
+ ret i8* %0
+}
+
+define dso_local i8* @_Z3fooP1X(%struct.X* %x) {
+entry:
+ %0 = icmp eq %struct.X* %x, null
+ br i1 %0, label %dynamic_cast.null, label %dynamic_cast.notnull
+
+dynamic_cast.notnull: ; preds = %entry
+ %1 = bitcast %struct.X* %x to i8*
+ %2 = call i8* @__dynamic_cast(i8* %1, i8* bitcast ({ i8*, i8* }* @_ZTI1X to i8*), i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1Y to i8*), i64 0) #2
+ %3 = bitcast i8* %2 to %struct.Y*
+ br label %dynamic_cast.end
+
+dynamic_cast.null: ; preds = %entry
+ br label %dynamic_cast.end
+
+dynamic_cast.end: ; preds = %dynamic_cast.null, %dynamic_cast.notnull
+ %QQ5 = phi %struct.Y* [ %3, %dynamic_cast.notnull ], [ null, %dynamic_cast.null ]
+ %call = call i8* @_ZN1Y3barEv(%struct.Y* %QQ5)
+ ret i8* %call
+}
+
+declare dso_local i8* @__dynamic_cast(i8*, i8*, i8*, i64)
+
+; UTC_ARGS: --enable
+
attributes #0 = { noinline nounwind uwtable }