Skip to content

[caller-analysis] Implement findLocalApplySites and reimplement caller analysis on top of it #17971

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

Merged

Conversation

gottesmm
Copy link
Contributor

This is a reimplementation of caller analysis on top of a new utility called findLocalApplySites. This utility works in spirit as an inverse operation to ApplySite::getCalleeOrigin(). At a high level starting at a specific function_ref, the utility traverses through the transitive use edges from the function_ref, looking through specific conversion insts and uses that it understands. All of the apply sites found this way are returned to the user and more importantly if there is an unknown use, a bool is set. As a result, the caller analysis (although not used anywhere beyond tests in this commit), can not be used to determine if the analysis can guarantee that all callers for a specific callee have been found.

rdar://41146023

@gottesmm gottesmm requested a review from atrick July 15, 2018 23:19
@gottesmm
Copy link
Contributor Author

@swift-ci smoke test

@gottesmm
Copy link
Contributor Author

Made a mistake while splicing things... hmmm...

@gottesmm
Copy link
Contributor Author

Yea... its some sort of memory error... Hmm... Compiling locally with asan...

@gottesmm
Copy link
Contributor Author

Just discovered that we are not being complete with our notifyAdd/Delete notifications as I was tracking down the memory issue.

@gottesmm
Copy link
Contributor Author

I am going to fix that in a different PR though.

Copy link
Contributor

@atrick atrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. Most of my comments are about clearly communicating the intent and semantics and sticking with certain conventions so we can easily refer to the API in the future without deriving its meaning from the code.

/// NOTE: We want to return true if escapes has any value. We want to return
/// true if we have any non-conservative information that the user should
/// process.
operator bool() const {
Copy link
Contributor

@atrick atrick Jul 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An operator bool overload should only be used for a result that has a completely obvious "has a valid value" or "has no value". A false result should mean that the analysis failed, so no information is available. i.e. it found an unrecognized SIL pattern. That seems to be the intention based on the comments, although that could be stated directly as: "Returns false for an invalid result. No assumptions can be made based an an invalid result".

However, the implementation seems to do something different. It apparently returns 'true' if an unknown use was found and 'false' if no uses were found. If that's really the desired semantics, then the comment needs to be rewritten and the name should be explicit, like foundUse or hasUseWithSideEffects (I started to say "hasNonTrivialUse", then realized we restrict the term "trivial" to ownership).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason that I put this is in was so that I could use an if scope, e.x.:

        if (auto result = findLocalApplySites(fri)) {

Looks nice, right? But I get your point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get your point. I'm saying you should be able to do this:

if (auto result = findLocalApplySites(fri)) {

As long as your intention is that the if statement executes whenever the result is valid.

///
/// The none case is so that we can distinguish in between saying that a value
/// did escape and saying that we did not find any conservative information.
Optional<bool> escapes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are no (nontrivial) uses of the function_ref, then I would expect this to contain a definite negative result (false). A missing result should indicate that the analysis cannot determine the result.

llvm::DenseMap<SILFunction *, FunctionInfo> FuncInfos;
///
/// We use a map vector to ensure that when we dump the state of the caller
/// analysis,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment trails off.

/// indirectly. That is a separate query that is type system specific.
bool isDirectCallerSetComplete : 1;

CallerInfo()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A default ctor isn't necessary.


/// The number of partial applied arguments of this function.
/// This is a special set vector that is an abuse as a performance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment appears to be a commentary on some other code. Is there some abuse happening? Do you mean optimization passes are using/abusing this information? Or does this just make the CallerAnalysis invalidation more efficient?

The comment should first state what calleeStates actually represents. I think this is simply the set of callees computed during analysis. If so, we should just say that. Maybe you're saying that this set should not be used for any purpose other than recomputing CallerInfo because it may be incomplete or out of date? If so, we should say that.


/// Internal only way for getting a caller info. Will insert f if needed and
/// _WILL NOT_ preform any recomputation of the callgraph.
FunctionInfo &getOrInsertCallerInfo(SILFunction *f);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I keep getting confused by the name getOrInsertCallerInfo. I'm naturally expecting it to return a CallerInfo. I guess this should actually be getOrInsertFunctioInfo.

// TODO: Make this more aggressive by considering
// final/visibility/etc.
calleeInfo.mayHaveIndirectCallers =
canBeCalledIndirectly(calleeFn->getRepresentation());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing that this is handled while analyzing the call site. Shouldn't calleeInfo.mayHaveIndirectCallers be part of FunctionInfo initialization?

/// Treat this function ref as escaping only if we found an actual user we
/// didn't understand. Do not treat it as escaping if we did not find any
/// users at all.
bool isEscaping() const { return escapes.getValueOr(false); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name mayEscape would match the semantics you have: null or false.

/// Returns true if this function has at least one caller.
bool hasCaller() const { return !Callers.empty(); }
bool hasCaller() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called hasDirectCaller?

FunctionInfo &CalleeInfo = FuncInfos[Callee];
CalleeInfo.Callers.erase(F);
CalleeInfo.PartialAppliers.erase(F);
void CallerAnalysis::invalidateExistingCalleeRelation(SILFunction *caller) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what Existing implies here. If you don't want to refer to the computed callees simply as callees, then I would use the term "known callees" consistently:
calleeStates -> knownCallees
invalidateExistingCalleeRelation -> invalidateKnownCallees

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

Got in the functionality I needed to make sure that we get all callee relations in the short term (need to do a bit more work, but I can at least get htis in and get it out of the way).

@gottesmm gottesmm force-pushed the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch from 744fa4f to 6ec9dad Compare August 7, 2018 19:32
@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test macOS platform

2 similar comments
@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test macOS platform

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test macOS platform

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test osx platform

1 similar comment
@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test osx platform

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci smoke test OS X platform

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

@swift-ci asan test

@gottesmm
Copy link
Contributor Author

gottesmm commented Aug 7, 2018

Reproduced the crasher with ASAN.

@gottesmm gottesmm closed this Aug 8, 2018
@gottesmm gottesmm deleted the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch August 8, 2018 18:11
@gottesmm gottesmm restored the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch August 8, 2018 18:15
@gottesmm gottesmm reopened this Aug 8, 2018
@gottesmm gottesmm force-pushed the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch from 6ec9dad to 285e301 Compare August 18, 2018 20:00
@gottesmm
Copy link
Contributor Author

@swift-ci test

3 similar comments
@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci test asan

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 744fa4faac2891eaa43999b1ea3ce9943d3b6847

@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci test asan

@gottesmm
Copy link
Contributor Author

@swift-ci smoke benchmark

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 285e301bd3b50172b4c701105c162b596165c93a

@swift-ci
Copy link
Contributor

Build comment file:

Compilation-performance test failed

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 285e301bd3b50172b4c701105c162b596165c93a

@swift-ci
Copy link
Contributor

Build comment file:

Build failed before running benchmark.


Copy link
Contributor

@atrick atrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks great. Please have a look at the comments below before merging.

/// an ask for information.
///
/// This laziness is implemented by the pass invalidating its internal state for
/// a function f when receiving an invalidation message for f and then adding f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a function f when

I thought this was a typo. Use F like you do in other comments.

///
/// By storing the minimum number of partial applied arguments, we are able
/// to decide quickly if we are able to eliminate dead captured arguments.
Optional<unsigned> numPartialAppliedArguments;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to micro-optimize, but this is quadratic in the number of functions (x2 for MapVectors), so...

This creates a lot of fragmentation within the callerStates map, and the "Optional" doesn't help with clarity. I would use a bitfield for numPartialAppliedArguments initialized to zero, a separate bit for "seen partial apply", and assert that it doesn't overflow, which is good to check anyway.

if (auto *fri = dyn_cast<FunctionRefInst>(&i)) {
// If we found any interesting information, update our internal state.
if (auto result = findLocalApplySites(fri)) {
findApplySites(callSitesThatMustBeVisited, callerFn, callerInfo, fri,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this code will build in Release mode. It looks like you might want to define a class/struct to hold the state required to process a caller function. That would solve the NDEBUG problem and address the findApplySite code smell where you're passing in a bunch of context that isn't specific to this invocation.

This utility works by taking in a function_ref and then traverses the transitive
uses of the function_ref until it finds either a use it does not understand
"escape" or an "apply" instruction. It returns a result structure that contains
the final found applications and more importantly a bool telling the caller if
we found any "escaping" uses.

This is intended to be an inverse operation to ApplySite::getCalleeOrigin(). As
such it has a bunch of assertions in it that check that the two stay in sync.

rdar://41146023
@gottesmm gottesmm force-pushed the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch 3 times, most recently from 1f7d373 to 858fa7c Compare August 22, 2018 00:42
@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci asan test

@gottesmm
Copy link
Contributor Author

@swift-ci test

1 similar comment
@gottesmm
Copy link
Contributor Author

@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 858fa7c86bffc72231bad21d1a1b6c9e1aa78652

@gottesmm gottesmm force-pushed the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch from 858fa7c to 71f6d68 Compare August 22, 2018 01:17
@gottesmm
Copy link
Contributor Author

@swift-ci test linux platform

3 similar comments
@gottesmm
Copy link
Contributor Author

@swift-ci test linux platform

@gottesmm
Copy link
Contributor Author

@swift-ci test linux platform

@gottesmm
Copy link
Contributor Author

@swift-ci test linux platform

@gottesmm
Copy link
Contributor Author

@swift-ci test Linux platform

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 71f6d68e44d9ef2f734b07eea0770b4229b72f11

…ites.

Now the caller analysis can tell callers if it was able to find /all/ callers of
a callee.

NOTE: This does not change FSO itself yet.

rdar://41146023
@gottesmm gottesmm force-pushed the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch from 71f6d68 to 03afdc5 Compare August 22, 2018 02:35
@gottesmm
Copy link
Contributor Author

@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 858fa7c86bffc72231bad21d1a1b6c9e1aa78652

@gottesmm
Copy link
Contributor Author

The original linux failure was due to an issue with my result type being a move only type and not getting the NRVO behavior I wanted on Linux due to a conversion.

I fixed it by making the code "more NRVO"-able by eliminating the conversion and just working with an optional.

The second failure was b/c I forgot to do an emplace with that optional. = (. But at least on Linux it compiled!

I think this run will work.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 71f6d68e44d9ef2f734b07eea0770b4229b72f11

@gottesmm gottesmm merged commit f2a2376 into swiftlang:master Aug 22, 2018
@gottesmm gottesmm deleted the pr-3ce5268ef004895c1821c778f3e2af3e77a28509 branch August 22, 2018 06:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants