Skip to content

Handle global_addr for concrete type propagation #16116

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
merged 1 commit into from
May 2, 2018

Conversation

rajbarik
Copy link
Contributor

This PR extends concrete type propagation in SILCombiner to handle global_addrs.

@rajbarik
Copy link
Contributor Author

@slavapestov Please review this PR for extending concrete type propagation in SILCombiner.

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

Thanks!

// CHECK: init_existential_addr
// CHECK: alloc_ref
// CHECK: store
// CHECK: witness_method
Copy link
Contributor

Choose a reason for hiding this comment

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

Don’t we expect to see the witness_method get devirtualized here?

@rajbarik
Copy link
Contributor Author

rajbarik commented Apr 24, 2018 via email

@gottesmm
Copy link
Contributor

@rajbarik We already have a ton of things going on in that test file that interact. If you are going to add -devirtualize can you create a seperate sil_combine test file where that is an assumption.

@@ -1,4 +1,4 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine -verify-skip-unreachable-must-be-last | %FileCheck %s
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine -verify-skip-unreachable-must-be-last -devirtualizer | %FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

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

You can remove this line now

@slavapestov slavapestov self-assigned this Apr 25, 2018
@slavapestov
Copy link
Contributor

Can you squash the commits together and also set your git email, then do git commit --amend --reset-author so that the change is properly credited to you on github?

@rajbarik
Copy link
Contributor Author

Done!

@slavapestov
Copy link
Contributor

@swift-ci Please smoke test

@slavapestov
Copy link
Contributor

@gottesmm I'm OK with merging this if you are, want to take one final look at it?

@slavapestov
Copy link
Contributor

@swift-ci Please smoke test

@gottesmm
Copy link
Contributor

@slavapestov Let me read it on the bus.

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

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

A few nits and some semantic questions.

static SILValue findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI) {
/// Check for a single InitExistential.
SILInstruction *SingleIE = NULL;
for (auto Use : GAI->getUses()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an Operand *. Can you change to auto * like you did with User. The reason I am asking for this is that it breaks symmetry with the rest of the change.

/// store %5 to %4 : $*SomeC
static SILValue findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI) {
/// Check for a single InitExistential.
SILInstruction *SingleIE = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

Two things:

  1. This should be nullptr. We do not use NULL in the code base.
  2. If you are just looking for InitExistentialAddrInst, then why not just make it this type?

SILInstruction *SingleIE = NULL;
for (auto Use : GAI->getUses()) {
auto *User = Use->getUser();
if(auto *InitExistential = dyn_cast<InitExistentialAddrInst>(User)) {
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 run the PR through git-clang-format? I am pretty sure that it will put a space in between the if and the (.

init()
func foo() -> Int
}
sil hidden [thunk] [always_inline] @foo_ : $@convention(witness_method:SomeProtocol) (@in_guaranteed SomeClass) -> Int {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please provide a failing test case where we have multiple init_existential_addr. I would just do this

BB1 (global) -> (BB2, BB3) where init existential_addr is -> BB4 the rest.

}
}
if (!SingleIE) return SILValue();
return cast<InitExistentialAddrInst>(SingleIE);
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 guarantee that through some sort of obfuscation there aren't any init existential addr inst. For instance, imagine if I do a cast then cast back and then do init existential addr inst. (I just made this up keep in mind).

@rajbarik
Copy link
Contributor Author

Thanks. I added a failing test in the same file sil_combine_global_addr.sil. Please have a look.

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

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

1 nit and 1 unresolved question.

InitExistentialAddrInst *SingleIE = nullptr;
for (auto Use : GAI->getUses()) {
auto *User = Use->getUser();
if (auto *InitExistential = dyn_cast<InitExistentialAddrInst>(User)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This still is pretty optimistic in terms of assuming that init_existential_addr can not be obfuscated in some way. Is this guaranteed in the language? I don't see anything in the verifier. I can definitely write SIL that would cause this to miscompile, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I missed this point earlier. You are right that in some obfuscation the global_addr, that is the source of copy_addr, could be used in an init_existential that occurs after the copy_addr instruction. In that case, we can either make sure both global_addr and init_existential dominate the copy_addr (heavy weight) or restrict all of them to our desired order within a basic block (light-weight). Since this is a peephole, I suggest we use the later approach. Thoughts?

// CHECK: store
// CHECK: function_ref
// CHECK: apply
// CHECK: return
Copy link
Contributor

Choose a reason for hiding this comment

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

Quick suggestion. Rather than checking for return to signal the end of the function, can you check for:

} // end sil function '$INSERT_FUNCTION_NAME_HERE'

This prevents patterns from matching in the wrong function in an absolute way. Can you change the patterns here to use that?

@rajbarik
Copy link
Contributor Author

@gottesmm both addressed! Please have a look.

/// Check for a single InitExistential.
InitExistentialAddrInst *SingleIE = nullptr;
auto *BB = CAI->getParent();
for (auto Use : GAI->getUses()) {
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 make this an auto '*'

/// the same basic block and not more than one InitExistential
/// occurs before CAI.
auto *IEBB = InitExistential->getParent();
if (BB == IEBB &&
Copy link
Contributor

Choose a reason for hiding this comment

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

You are ignoring init_existential_addr that is in a different block. In such a case you should bail, no? So this should be:

if (BB != IEBB) {
  return SILValue();
}

if (std::find_if(...) == BB->end()) {
  return SILValue();
}

if (SingleIE) {
  return SILValue();
}

SingleIE = InitExistentialAddr;

Assuming that I am correct, can you add a test case for this as well?

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 first condition you mentioned for BB check is not correct (premature return)! I need one occurrence of IE in BB -- IEs in other BB's are skipped -- Ideally I just want to iterate on the uses of GAI in BB only -- is there any API for this? Current code is fine, but does little more work -- iterates over all uses doing nothing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think what I wrote is correct depending on what we are trying to implement ; ). That is: to be very conservative, I was trying to make it so that we bailed if we saw /any/ other init_existential_addr in other BBs on the GAI. In that case we should bail/fail early in the way that I mentioned. That being said, I do see your point that such things should not matter if the init_existential_addr and the copy_addr are in the same block and there is a single init_existential_addr in the block before the copy_addr.

In terms of the API, I am not sure if one exists. That being said, I wonder if the better way to do this is to put all of the uses of GAI into a set and then just iterate backwards from the CAI to the beginning of the block looking for the target. Specifically:

llvm::SmallPtrSet<InitExistentialAddrInst *, 8> Initializers;
for (auto *Use : GAI->getUsers()) {
  if (auto *I = dyn_cast<InitExistentialAddrInst>(Use->getUser()) {
    Initializers.insert(I);
  }
}

if (Initializers.empty())
  return SILValue();

SILValue Result;
for (auto II = CAI->getIterator().getReverse(), IE = CAI->getParent()->rend(); II != IE; ++II) {
    if (!Initializers.count(II)) {
      continue;
    }

    if (Result)
      return SILValue();

   Result = *II;
}

}
}
}
if (!SingleIE) return SILValue();
Copy link
Contributor

Choose a reason for hiding this comment

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

SILValue() == SILValue(nullptr);

So you should be able to just return SingleIE.

/// the same basic block and not more than one InitExistential
/// occurs before CAI.
auto *IEBB = InitExistential->getParent();
if (BB == IEBB &&
Copy link
Contributor

Choose a reason for hiding this comment

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

I think what I wrote is correct depending on what we are trying to implement ; ). That is: to be very conservative, I was trying to make it so that we bailed if we saw /any/ other init_existential_addr in other BBs on the GAI. In that case we should bail/fail early in the way that I mentioned. That being said, I do see your point that such things should not matter if the init_existential_addr and the copy_addr are in the same block and there is a single init_existential_addr in the block before the copy_addr.

In terms of the API, I am not sure if one exists. That being said, I wonder if the better way to do this is to put all of the uses of GAI into a set and then just iterate backwards from the CAI to the beginning of the block looking for the target. Specifically:

llvm::SmallPtrSet<InitExistentialAddrInst *, 8> Initializers;
for (auto *Use : GAI->getUsers()) {
  if (auto *I = dyn_cast<InitExistentialAddrInst>(Use->getUser()) {
    Initializers.insert(I);
  }
}

if (Initializers.empty())
  return SILValue();

SILValue Result;
for (auto II = CAI->getIterator().getReverse(), IE = CAI->getParent()->rend(); II != IE; ++II) {
    if (!Initializers.count(II)) {
      continue;
    }

    if (Result)
      return SILValue();

   Result = *II;
}

/// %3 = global_addr @$P : $*SomeP
/// %4 = init_existential_addr %3 : $*SomeP, $SomeC
/// %5 = alloc_ref $SomeC
/// store %5 to %4 : $*SomeC
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 update the comment to use a copy_addr instead?

/// store %5 to %4 : $*SomeC
static SILValue findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI,
CopyAddrInst *CAI) {
/// Check for a single InitExistential.
Copy link
Contributor

Choose a reason for hiding this comment

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

There is an assumption in this code that CAI->getSrc() == GAI that is not apparent from the title. Can you at least add the assertion here... maybe something like:

assert(CAI->getSrc() == SILValue(GAI) && "Broken Assumption! Global Addr is not the source of the passed in copy_addr?!");

@rajbarik
Copy link
Contributor Author

Things done:

  1. Added a failed test case for more than one IE in a basic block.
  2. Restructured code to collect IEs first and then walk backwards from CAI looking for an IE (that is use of GAI).
  3. Added assertion and updated comment.

@gottesmm
Copy link
Contributor

LGTM. I would just suggest running it through git-clang-format. The formatting is inconsistent. git-clang-format will just make it go away.

I can explain to you how to do this if you want.

@rajbarik
Copy link
Contributor Author

Should be good now..

@gottesmm
Copy link
Contributor

gottesmm commented May 1, 2018

LGTM

@slavapestov
Copy link
Contributor

@swift-ci Please smoke test

@slavapestov slavapestov merged commit d5b98a0 into swiftlang:master May 2, 2018
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