Skip to content
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

C++: Field flow through reference-returning functions #11374

Open
wants to merge 1 commit into
base: mathiasvp/replace-ast-with-ir-use-usedataflow
Choose a base branch
from

Conversation

MathiasVP
Copy link
Contributor

@MathiasVP MathiasVP commented Nov 22, 2022

This PR adds use-use flow through examples such as:

int source();
void sink(int);

struct MyInt {
  int i;
  int* get() { return &i; }
};

void test()
{
  MyInt mi;
  *mi.get() = source();
  sink(mi.i);
}

It turns out we almost had this flow on the feature branch already. The shared dataflow library has a concept of flow-through here. This disjunct is responsible for adding a store step from c [post update] to getB() [post update] in the following example:

struct B { int c; };
struct A {
  B b;
  B* getB() { return b; }
};
...
a.getB()->c = v;

the rule used by the shared dataflow library is as follows:

Generate a store step from node1 to node2 (storing f) if:

  1. node1 is a PostUpdateNode with a pre-update node that is an output of a function (that's getB() [post update] in our example above node above with a pre-update node of getB())
  2. node2 is a PostUpdateNode with a pre-update node that is an argument to a function (that's a [post update] in our example above with a pre-update node of a).
  3. There's flow from the argument node, into the function, and to a read step that reads f, which then flows to a return node of the function.

Step 2 and 3 already worked for our example, but there was no PostUpdateNode with a pre-update note that was the output of the function. So this PR simply marks the appropriate OutNode as a PostUpdateNode and assigns the pre-update node to this.

Having the pre-update node equal the post-update node is a consistency violation (as demonstrated by the tests). I'm not aware of anything bad happening due to this, however. I'll run DCA to make sure, though 🤞.

@MathiasVP MathiasVP requested a review from a team as a code owner Nov 22, 2022
@github-actions github-actions bot added the C++ label Nov 22, 2022
@MathiasVP MathiasVP added the no-change-note-required This PR does not need a change note label Nov 22, 2022
@MathiasVP
Copy link
Contributor Author

MathiasVP commented Nov 22, 2022

DCA looks good 🎉. No result changes, and no performance changes.

@rdmarsh2
Copy link
Contributor

rdmarsh2 commented Nov 22, 2022

marks the appropriate OutNode as a PostUpdateNode and assigns the pre-update node to this.

What do other languages do here? Separate post-update and pre-update nodes for each return?

@aschackmull
Copy link
Contributor

aschackmull commented Nov 23, 2022

I'm not aware of anything bad happening due to this, however

The thing that can happen is weird FP flow in certain cases. If the store step that targets the result of the wrapped read isn't written directly, as it is in your examples, but instead occurs as a side-effect of a call then you'll get spurious re-entry flow into that setter. Let me demonstrate with an example:

Foo getFoo() { return this.foo; }
...
void setBar(x) {
  sink(this.Bar);
  this.Bar = x;
}
...
x.getFoo().setBar(source())

If the OutNode associated with the result of getFoo is its own PostUpdateNode then the post-update will also be an argument to setBar and the result of the side-effect from setBar will then be able to re-enter setBar and cause FP flow from source to sink.

@MathiasVP
Copy link
Contributor Author

MathiasVP commented Nov 23, 2022

Thanks for your input, @aschackmull! Luckily, the above example doesn't apply in this case since we're doing the Right Thing℄1�7 for constructs such as:

x.getFoo().setBar(source())

where x.getFoo() will have a post-update node x.getFoo() [post update] since it's the argument of a function call (and its pre-update node will be the expression node for x.getFoo()).

This PR only does the pre-update = post-update on constructs like *mi.get() = source(); (where the destination address is an address returned by a call to a function that returns a reference or a pointer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants