Skip to content

[AddressLowering] Allow use-projection into phi. #69314

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

Conversation

nate-chandler
Copy link
Contributor

Resolves numerous regressions when enabling AddressLowering early in the pipeline.

Previously, if a value incoming into a phi had storage which itself was a use-projection out of some other storage, PhiStorageOptimizer bailed out. The result was unnecessary "moves" (i.e. copy_addr [take] [init] instructions).

Here, this bailout is removed. In order to do this, it is necessary to find (1) all values whose storage recursively project out of an incoming value (such a value may have storage which is either a use or a def projection) and (2) the block which dominates the defs of all these values.

Together, these values are used to compute liveness to determine interference. Previously, the live region was that between the uses of an incoming value and its defining block. Now, it is that between the uses of any of the values found in (1) and the dominating block found in (2).

Allow in-IR tests to print to stdout.
Via `.result[idx]`.
@nate-chandler nate-chandler force-pushed the opaque-values/20231020/1/coalesce-use-projections-into-phis branch from e3023f0 to d7fc12e Compare October 21, 2023 02:25
@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler nate-chandler marked this pull request as ready for review October 23, 2023 15:10
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.

Nice!

projectedValues->push_back(value);
auto *defInst = value->getDefiningInstruction();
if (!defInst) {
assert(!PhiValue(value));
Copy link
Contributor

Choose a reason for hiding this comment

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

Mysterious assert alert. It looks like it's saying that incomingVal can never be a phi. Where do we guarantee that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is a result of

  if (PhiValue(incomingVal)) {
    // Do not coalesce a phi with other phis. This would require liveness
    // analysis of the whole phi web before coalescing phi operands.
    return nullptr;
  }

We should be able to remove this bail-out (and add all values incoming to PhiValue(value) to the values ValueWorklist here) in a follow-up.

//
// It is also collecting values whose storage is projected out of the
// phi, however, so the walk must continue.
updateLCA(defInst->getParent());
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you assert that projecteds is non-empty here? If, so, why do you need to update the LCA given that the projecteds must also update the LCA?

Copy link
Contributor Author

@nate-chandler nate-chandler Oct 23, 2023

Choose a reason for hiding this comment

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

Yeah, we can assert that--projecteds is empty if and only findDefProjections returns false. Asserted here and for findUseProjections.

I don't think we can skip updating the LCA here: it needs to dominate defInst->getParent(). The structure of chain projections is such that in order for a def projection to appear, it has to look something like this (projection_chain_structure):

parent:
  %val = ...
  cond_br ..., left, right

left:
  %d = disaggregate %val
  ...

right:
  %a = aggregate %val

...

  br merge( aggregate(...(aggregate(%a)...) )

In this case, so far so good: we'd update the LCA with %d = disaggregate %val which would make parent the LCA. If the branch that's the LCA of disaggregate and aggregate is different from the block containing the def %val, however, the LCA is after the def block:

grandparent:
  %val = ...
  cond_br ..., parent, other

parent:
  cond_br ..., left, right

left:
  %d = disaggregate %val
  ...

right:
  %a = aggregate %val

...

  br merge( aggregate(...(aggregate(%a)...) )

In the worst case, because the LCA is wrong, the walk where liveness is determined continues past the LCA (since we just compare the current block to the LCA before pushing its predecessors) resulting in redundant alloc_stacks. Without including this defInst->getParent() in the LCA computation, PhiStorageOptimizer incorrectly determines that there's interference in entry because the walk back from destroy_value %v continues all the way to entry (because LCA is incorrectly determined to be parent rather than trouble2).

sil [ossa] @g : $@convention(thin) <T> () -> () {
entry:
  %agg4 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
  cond_br undef, okay, trouble

okay:
  br merge(%agg4 : $Box<Box<T>>)


trouble:
  destroy_value %agg4 : $Box<Box<T>>
  br trouble2

trouble2:
  %v = apply undef<Box<T>>() : $@convention(thin) <T> () -> (@out T)
  cond_br undef, grandparent, other

grandparent:
  br parent

parent:
  cond_br undef, left, right

left:
  %t = destructure_struct %v : $Box<T>
  destroy_value %t : $T
  br left2

left2:
  %agg2 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
  br merge(%agg2 : $Box<Box<T>>)

right:
  %agg = struct $Box<Box<T>>(%v : $Box<T>)
  br merge(%agg : $Box<Box<T>>)

other:
  destroy_value %v : $Box<T>
  br other2

other2:
  %agg3 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
  br merge(%agg3 : $Box<Box<T>>)

merge(%p : @owned $Box<Box<T>>):
  destroy_value %p : $Box<Box<T>>
  br exit

exit:
  %retval = tuple ()
  return %retval : $()
}

This is now f190_vexing_incoming_def_projection in address_lowering_phi.sil.

@nate-chandler nate-chandler force-pushed the opaque-values/20231020/1/coalesce-use-projections-into-phis branch 2 times, most recently from 902d305 to d15a78c Compare October 23, 2023 21:55
Resolves numerous regressions when enabling AddressLowering early in the
pipeline.

Previously, if a value incoming into a phi had storage which itself was
a use-projection out of some other storage, PhiStorageOptimizer bailed
out.  The result was unnecessary "moves" (i.e. `copy_addr [take] [init]`
instructions).

Here, this bailout is removed.  In order to do this, it is necessary to
find (1) all values whose storage recursively project out of an incoming
value (such a value may have storage which is either a use _or_ a def
projection) and (2) the block which dominates the defs of all these
values.

Together, these values are used to compute liveness to determine
interference.  Previously, the live region was that between the uses of
an incoming value and its defining block.  Now, it is that between the
uses of any of the values found in (1) and the dominating block found in
(2).
@nate-chandler nate-chandler force-pushed the opaque-values/20231020/1/coalesce-use-projections-into-phis branch from d15a78c to 48f4b5a Compare October 23, 2023 23:19
@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler nate-chandler merged commit 1a6d76e into swiftlang:main Oct 24, 2023
@nate-chandler nate-chandler deleted the opaque-values/20231020/1/coalesce-use-projections-into-phis branch October 24, 2023 14:06
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.

2 participants