jdoerfert created this revision. jdoerfert added reviewers: homerdin, hfinkel, fedor.sergeev, sanjoy, spatel, nlopes, nicholas, reames. Herald added subscribers: cfe-commits, bollu, hiraditya. Herald added projects: clang, LLVM.
Deduce the "returned" argument attribute by collecting all potentially returned values. Note: Not only the unique return value, if any, can be used by subsequent attributes but also the set of all potentially returned values as well as the mapping from returned values to return instructions that they originate from. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D59919 Files: clang/test/CodeGenOpenCL/as_type.cl llvm/include/llvm/Transforms/IPO/Attributor.h llvm/lib/Transforms/IPO/Attributor.cpp llvm/test/Transforms/FunctionAttrs/SCC1.ll llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll llvm/test/Transforms/FunctionAttrs/arg_returned.ll
Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -1,4 +1,8 @@ -; RUN: opt -functionattrs -attributor -S < %s | FileCheck %s +; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR +; RUN: opt -attributor -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -attributor -functionattrs -S < %s | FileCheck %s --check-prefix=BOTH +; RUN: opt -attributor -attributor-max-iterations=18 -S < %s | FileCheck %s --check-prefix=FEW_IT +; RUN: opt -attributor -attributor-max-iterations=19 -functionattrs -S < %s | FileCheck %s --check-prefix=BOTH ; ; Test cases specifically designed for the "returned" argument attribute. ; We use FIXME's to indicate problems and missing attributes. @@ -16,13 +20,20 @@ ; TEST 1 ; -; CHECK: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]] +; BOTH: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]] +; BOTH: define dso_local i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]] +; BOTH: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 returned %r) [[NoInlineNoUnwindReadnoneUwtable]] +; BOTH: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] ; -; FIXME: returned on %r missing: -; CHECK: define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]] +; FNATTR: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]] +; FNATTR: define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]] +; FNATTR: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] ; -; FIXME: returned on %r missing: -; CHECK: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]] +; ATTRIBUTOR: define dso_local i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]] +; ATTRIBUTOR: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 returned %r) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] ; ; int scc_r1(int a, int b, int r); ; int scc_r2(int a, int b, int r); @@ -157,13 +168,17 @@ ; TEST 2 ; -; CHECK: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; BOTH: define dso_local double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] +; BOTH: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r) [[NoInlineNoUnwindReadnoneUwtable]] ; -; FIXME: returned on %r missing: -; CHECK: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]] ; -; FIXME: returned on %r missing: -; CHECK: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* %b) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) [[NoInlineNoUnwindReadnoneUwtable]] ; ; double* ptr_scc_r1(double* a, double* b, double* r); ; double* ptr_scc_r2(double* a, double* b, double* r); @@ -247,8 +262,9 @@ ; return *a ? a : ret0(ret0(ret0(...ret0(a)...))); ; } ; -; FIXME: returned on %a missing: -; CHECK: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]] +; FEW_IT: define dso_local i32* @ret0(i32* %a) +; FNATTR: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]] +; BOTH: define dso_local i32* @ret0(i32* readonly returned %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]] define dso_local i32* @ret0(i32* %a) #0 { entry: %v = load i32, i32* %a, align 4 @@ -285,9 +301,11 @@ ; return r; ; } ; -; CHECK: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]] +; BOTH: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]] ; -; CHECK: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]] +; FNATTR: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]] +; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]] ; declare void @unknown_fn(i32* (i32*)*) #0 @@ -309,11 +327,12 @@ ; } ; ; Verify the maybe-redefined function is not annotated: -; CHECK: define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]] +; BOTH: define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]] ; FIXME: We should not derive norecurse for potentially redefined functions! ; define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoUnwindUwtable]] ; -; CHECK: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoRecurseNoUnwindUwtable]] +; FNATTR: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]] +; ATTRIBUTOR: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoUnwindUwtable]] ; FIXME: We should not derive norecurse for potentially redefined functions! ; define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoUnwindUwtable]] ; @@ -338,8 +357,8 @@ ; return b == 0? b : x; ; } ; -; FIXME: returned on %b missing: -; CHECK: define dso_local double @select_and_phi(double %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double @select_and_phi(double %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double @select_and_phi(double returned %b) [[NoInlineNoUnwindUwtable]] ; define dso_local double @select_and_phi(double %b) #0 { entry: @@ -366,8 +385,8 @@ ; return b == 0? b : x; ; } ; -; FIXME: returned on %b missing: -; CHECK: define dso_local double @recursion_select_and_phi(i32 %a, double %b) [[NoInlineNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double @recursion_select_and_phi(i32 %a, double %b) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double @recursion_select_and_phi(i32 %a, double returned %b) [[NoInlineNoUnwindUwtable]] ; define dso_local double @recursion_select_and_phi(i32 %a, double %b) #0 { entry: @@ -393,8 +412,9 @@ ; return (double*)b; ; } ; -; FIXME: returned on %b missing: -; CHECK: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned %b) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local double* @bitcast(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] ; define dso_local double* @bitcast(i32* %b) #0 { entry: @@ -412,8 +432,9 @@ ; return b != 0 ? b : x; ; } ; -; FIXME: returned on %b missing: -; CHECK: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; FNATTR: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned %b) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local double* @bitcasts_select_and_phi(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] ; define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 { entry: @@ -438,8 +459,7 @@ attributes #0 = { noinline nounwind uwtable } -; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] = { noinline norecurse nounwind readnone uwtable } -; CHECK-DAG: attributes [[NoInlineNoUnwindReadnoneUwtable]] = { noinline nounwind readnone uwtable } -; CHECK-DAG: attributes [[NoInlineNoUnwindReadonlyUwtable]] = { noinline nounwind readonly uwtable } -; CHECK-DAG: attributes [[NoInlineNoUnwindUwtable]] = { noinline nounwind uwtable } -; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindUwtable]] = { noinline norecurse nounwind uwtable } +; BOTH-DAG: attributes [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] = { noinline norecurse nounwind readnone uwtable } +; BOTH-DAG: attributes [[NoInlineNoUnwindReadnoneUwtable]] = { noinline nounwind readnone uwtable } +; BOTH-DAG: attributes [[NoInlineNoUnwindReadonlyUwtable]] = { noinline nounwind readonly uwtable } +; BOTH-DAG: attributes [[NoInlineNoRecurseNoUnwindUwtable]] = { noinline norecurse nounwind uwtable } Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -127,18 +127,15 @@ ; TEST 5: ; -; FIXME: returned missing for %a ; FIXME: no-capture missing for %a -; CHECK: define dso_local float* @scc_A(i32* readnone %a) +; CHECK: define dso_local float* @scc_A(i32* readnone returned %a) ; -; FIXME: returned missing for %a ; FIXME: no-capture missing for %a -; CHECK: define dso_local i64* @scc_B(double* readnone %a) +; CHECK: define dso_local i64* @scc_B(double* readnone returned %a) ; -; FIXME: returned missing for %a ; FIXME: readnone missing for %s ; FIXME: no-capture missing for %a -; CHECK: define dso_local i8* @scc_C(i16* %a) +; CHECK: define dso_local i8* @scc_C(i16* returned %a) ; ; float *scc_A(int *a) { ; return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a); Index: llvm/test/Transforms/FunctionAttrs/SCC1.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/SCC1.ll +++ llvm/test/Transforms/FunctionAttrs/SCC1.ll @@ -45,7 +45,7 @@ ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND:[0-9]*]] +; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND:[0-9]*]] define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0) @@ -55,7 +55,7 @@ ret i32* %call3 } -; CHECK: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0) #[[NOUNWIND]] define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: %r0 = alloca i32, align 4 @@ -83,7 +83,7 @@ ret i32* %retval.0 } -; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0) #[[NOUNWIND]] define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -132,7 +132,7 @@ ret i32* %w0 } -; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0) #[[NOUNWIND]] define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -157,7 +157,7 @@ ret i32* %retval.0 } -; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND]] +; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND]] define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -38,6 +38,10 @@ STATISTIC(NumFnWithoutExactDefinition, "Number of function without exact definitions"); +STATISTIC(NumFnUniqueReturned, "Number of function with unique return"); +STATISTIC(NumFnArgumentReturned, + "Number of function arguments marked returned"); + // TODO: Determine a good default value. static cl::opt<unsigned> MaxFixpointIterations("attributor-max-iterations", cl::Hidden, @@ -64,11 +68,57 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP, const Attribute &Attr) { switch (Attr.getKindAsEnum()) { + case Attribute::Returned: + NumFnArgumentReturned++; + return; default: return; } } +/// Recursively collect returned values, starting from \p V but look through +/// through select/phi/casts. +static void collectValuesRecursively( + Value *V, SmallPtrSetImpl<ReturnInst *> &ReturnInsts, + DenseMap<Value *, SmallPtrSet<ReturnInst *, 2>> &Values, + SmallPtrSetImpl<Value *> &Visited) { + // TODO: This (looking through casts/phis/selects should be a general + // functionality weith a callback. + V = V->stripPointerCasts(); + + // Explicitly look through calls with a "returned" attribute. + CallSite CS(V); + if (CS && CS.getCalledFunction()) + for (Argument &Arg : CS.getCalledFunction()->args()) + if (Arg.hasReturnedAttr()) + return collectValuesRecursively(CS.getArgOperand(Arg.getArgNo()), + ReturnInsts, Values, Visited); + + if (isa<UndefValue>(V)) + return; + + if (!Visited.insert(V).second) + return; + + // Look through select instructions. + if (auto *SI = dyn_cast<SelectInst>(V)) { + collectValuesRecursively(SI->getTrueValue(), ReturnInsts, Values, Visited); + collectValuesRecursively(SI->getFalseValue(), ReturnInsts, Values, Visited); + return; + } + + // Look through phi nodes. Recursion is not a problem as we keep the PHI + // node in the container as well (see above). + if (auto *PHI = dyn_cast<PHINode>(V)) { + for (Value *PHIOp : PHI->operands()) + collectValuesRecursively(PHIOp, ReturnInsts, Values, Visited); + return; + } + + // Remember that V can be returned by all return instructions in ReturnInsts. + Values[V].insert(ReturnInsts.begin(), ReturnInsts.end()); +} + ChangeStatus AbstractAttribute::update(Attributor &A) { ChangeStatus Changed = ChangeStatus::UNCHANGED; if (getState().isAtFixpoint()) @@ -120,6 +170,258 @@ return Changed; } +/// --------------------- Function Return Values ------------------------------- + +/// "Attribute" that collects all potential returned values and the return +/// instructions that they arise from. +/// +/// If there is a unique returned value R, the manifest method will: +/// - mark R with the "returned" attribute, if R is an argument. +class AAReturnedValuesImpl final : public AAReturnedValues, AbstractState { + + /// Mapping of values potentially returned by the associated function to the + /// return instructions that might return them. + DenseMap<Value *, SmallPtrSet<ReturnInst *, 2>> ReturnedValues; + + /// Return an assumed unique return value if one is found. If there is none, + /// return a nullptr, if it is not clear yet, return the Optional::NoneType. + Optional<Value *> getAssumedUniqueReturnValue(); + + /// State flags + /// + ///{ + bool IsFixed = false; + bool IsValidState = true; + ///} + +public: + /// See AbstractAttribute::AbstractAttribute(...). + AAReturnedValuesImpl(Function &F) : AAReturnedValues(F) { + // We do not have an associated argument yet. + AssociatedVal = nullptr; + } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + Function &F = cast<Function>(getAnchoredValue()); + + auto &OpcodeInstMap = A.getOpcodeInstMapForFunction(F); + + for (Argument &Arg : F.args()) { + if (Arg.hasReturnedAttr()) { + assert(ReturnedValues.empty()); + + auto &ReturnInstSet = ReturnedValues[&Arg]; + for (Instruction *RI : OpcodeInstMap[Instruction::Ret]) + ReturnInstSet.insert(cast<ReturnInst>(RI)); + + indicateFixpoint(/* Optimistic */ true); + return; + } + } + for (Instruction *RI : OpcodeInstMap[Instruction::Ret]) { + SmallPtrSet<ReturnInst *, 1> RISet({cast<ReturnInst>(RI)}); + SmallPtrSet<Value *, 8> Visited; + collectValuesRecursively(cast<ReturnInst>(RI)->getReturnValue(), RISet, + ReturnedValues, Visited); + } + } + + /// See AbstractAttribute::manifest(...). + virtual ChangeStatus manifest(Attributor &A) override; + + /// See AbstractAttribute::getState(...). + virtual AbstractState &getState() override { return *this; } + + /// See AbstractAttribute::getState(...). + virtual const AbstractState &getState() const override { return *this; } + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_ARGUMENT; + } + + /// See AbstractAttribute::updateImpl(Attributor &A). + virtual ChangeStatus updateImpl(Attributor &A) override; + + /// See AAReturnedValues::getNumReturnValues(). + virtual size_t getNumReturnValues() const override { + return isValidState() ? ReturnedValues.size() : -1; + } + + /// Iterators to walk through all possibly returned values. + ///{ + using iterator = decltype(ReturnedValues)::iterator; + iterator begin() { return ReturnedValues.begin(); } + iterator end() { return ReturnedValues.end(); } + ///} + + /// Pretty print the attribute similar to the IR representation. + virtual const std::string getAsStr() const override; + + /// See AbstractState::isAtFixpoint(). + bool isAtFixpoint() const override { return IsFixed; } + + /// See AbstractState::isValidState(). + bool isValidState() const override { return IsValidState; } + + /// See AbstractState::indicateFixpoint(...). + void indicateFixpoint(bool Optimistic) override { + IsFixed = true; + IsValidState &= Optimistic; + } +}; + +ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) { + ChangeStatus Changed = ChangeStatus::UNCHANGED; + + // Check if we have an assumed unique return value that we could manifest. + Optional<Value *> UniqueRV = getAssumedUniqueReturnValue(); + if (!UniqueRV.hasValue() || !UniqueRV.getValue()) + return Changed; + + // Bookkeeping. + NumFnUniqueReturned++; + + // If the assumed unique return value is an argument, annotate it. + if (auto *UniqueRVArg = dyn_cast<Argument>(UniqueRV.getValue())) { + AssociatedVal = UniqueRVArg; + Changed = AbstractAttribute::manifest(A) | Changed; + } + + return Changed; +} + +const std::string AAReturnedValuesImpl::getAsStr() const { + return (isAtFixpoint() ? "returns(#" : "may-return(#") + + (isValidState() ? std::to_string(getNumReturnValues()) : "?") + ")"; +} + +Optional<Value *> AAReturnedValuesImpl::getAssumedUniqueReturnValue() { + Optional<Value *> UniqueRV; + + // Check all returned values but ignore call sites. If there is a unique one + // left, it is returned and assumed to be the actual return value. If there + // are multiple, a nullptr is returned indicating there is no unique returned + // value. Finally, if there is none, an Optional::NoneType is returned to + // indicate we need to continue resolving calls. + for (auto &It : ReturnedValues) { + Value *RV = It.first; + if (ImmutableCallSite(RV)) + continue; + if (UniqueRV.hasValue()) + return Optional<Value *>(nullptr); + UniqueRV = RV; + } + + return UniqueRV; +} + +ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) { + + // Check if we know of any values returned by the associated function, + // if not, we are done. + if (getNumReturnValues() == 0) { + indicateFixpoint(/* Optimistic */ true); + return ChangeStatus::UNCHANGED; + } + + // Check if any of the returned values is a call site we can refine. + SmallPtrSet<Value *, 16> DelRVs; + decltype(ReturnedValues) AddRVs; + bool HasNonFixedCallSiteRVs = false; + + // Look at all returned call sites. + for (auto &It : ReturnedValues) { + SmallPtrSet<Value *, 8> Visited; + SmallPtrSet<ReturnInst *, 2> &ReturnInsts = It.second; + Value *RV = It.first; + LLVM_DEBUG(dbgs() << "[AAReturnedValues] Potentially returned value " << *RV + << "\n"); + + // Only call sites can change during an update, ignore the rest. + CallSite RetCS(RV); + if (!RetCS) + continue; + + // For now, any call site we see will prevent us from directly fixing the + // state. However, if the information on the callees is fixed, the call + // sites will be removed and we will fix the information for this state. + HasNonFixedCallSiteRVs = true; + + // If we do not have information on the + auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(*this, *RV); + if (!RetCSAA || !RetCSAA->isValidState()) { + LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned call site (" << *RV + << ") with " << (RetCSAA ? "invalid" : "no") + << " associated state\n"); + continue; + } + + // Try to find a unique assumed return value for the called function. + Optional<Value *> AssumedUniqueRV = RetCSAA->getAssumedUniqueReturnValue(); + + // If no unique assumed return value was given we may need to resolve more + // calls or the called function will not return. Either way, we simply stick + // with the call site as return value. + if (!AssumedUniqueRV.hasValue()) + continue; + + // If there cannot be a unique assumed return value for the called function + // we give up. + if (!AssumedUniqueRV.getValue()) { + LLVM_DEBUG(dbgs() << "[AAReturnedValues] Returned call site has multiple " + "potentially returned values\n"); + continue; + } + + LLVM_DEBUG( + dbgs() + << "[AAReturnedValues] Returned call site assumed unique return value: " + << *AssumedUniqueRV.getValue() << "\n"); + + // The unique assumed return value. + Value *AssumedRetVal = AssumedUniqueRV.getValue(); + + // If the unique assumed return value is an argument, lookup the matching + // call site operand and recursively collect new returned values. + // If it is not an argument, it is just put into the set of returned values + // as we would have already looked through casts, phis, and similar values. + if (Argument *AssumedRetArg = dyn_cast<Argument>(AssumedRetVal)) + collectValuesRecursively(RetCS.getArgument(AssumedRetArg->getArgNo()), + ReturnInsts, AddRVs, Visited); + else + AddRVs[AssumedRetVal].insert(ReturnInsts.begin(), ReturnInsts.end()); + + // If the information for the called function is already fix we can drop the + // call from the set of returned values. + if (RetCSAA->isAtFixpoint()) + DelRVs.insert(RV); + } + + ChangeStatus Changed = + DelRVs.empty() ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; + + // Update the return values set after we stopped iterating over it. + for (Value *RV : DelRVs) + ReturnedValues.erase(RV); + + for (auto &It : AddRVs) { + auto &ReturnInsts = ReturnedValues[It.first]; + for (ReturnInst *RI : It.second) + if (ReturnInsts.insert(RI).second) + Changed = ChangeStatus::CHANGED; + } + + // If there is no call site in the returned values we are done. + if (!HasNonFixedCallSiteRVs) { + indicateFixpoint(/* Optimistic */ true); + return ChangeStatus::CHANGED; + } + + return Changed; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -292,6 +594,14 @@ } void Attributor::identifyAbstractAttributes(Function &F) { + // Return attributes are only appropriate if the return type is non void. + Type *ReturnType = F.getReturnType(); + if (!ReturnType->isVoidTy()) { + // Argument attribute "returned" --- Create only one per function even + // though it is an argument attribute. + registerAA(*new AAReturnedValuesImpl(F)); + } + // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be querried by abstract attributes // during their initialziation or update. @@ -301,6 +611,8 @@ switch (I.getOpcode()) { default: break; + case Instruction::Ret: // ReturnInst are interesting for AAReturnedValues. + IsInteresting = true; } if (IsInteresting) InstOpcodeMap[I.getOpcode()].push_back(&I); Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -235,6 +235,21 @@ /// Abstract Attribute Classes /// ---------------------------------------------------------------------------- +/// An abstract attribute for the returned values of a function. +struct AAReturnedValues : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AAReturnedValues(Function &F) : AbstractAttribute(F) {} + + /// Return the number of potential return values, -1 if unknown. + virtual size_t getNumReturnValues() const = 0; + + /// See AbstractAttribute::getAttrKind() + virtual Attribute::AttrKind getAttrKind() const override { return ID; } + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = Attribute::Returned; +}; /// ---------------------------------------------------------------------------- /// Pass (Manager) Boilerplate Index: clang/test/CodeGenOpenCL/as_type.cl =================================================================== --- clang/test/CodeGenOpenCL/as_type.cl +++ clang/test/CodeGenOpenCL/as_type.cl @@ -67,7 +67,7 @@ return __builtin_astype(x, int3); } -//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone %[[x:.*]]) +//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned %[[x:.*]]) //CHECK: %[[cast:.*]] = addrspacecast i32* %[[x]] to i32 addrspace(1)* //CHECK: ret i32 addrspace(1)* %[[cast]] global int* addr_cast(int *x) {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits