-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
Conversation
@slavapestov Please review this PR for extending concrete type propagation in SILCombiner. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
test/SILOptimizer/sil_combine.sil
Outdated
// CHECK: init_existential_addr | ||
// CHECK: alloc_ref | ||
// CHECK: store | ||
// CHECK: witness_method |
There was a problem hiding this comment.
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?
Adding -devirtualizer with
// 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
will devirtualize it. I have updated the options.
…On Mon, Apr 23, 2018 at 6:08 PM, Slava Pestov ***@***.***> wrote:
***@***.**** commented on this pull request.
Thanks!
------------------------------
In test/SILOptimizer/sil_combine.sil
<#16116 (comment)>:
> +sil hidden [thunk] [always_inline] @foo : ***@***.***(method) ***@***.*** SomeClass) -> Int {
+bb0(%0: $SomeClass):
+ %1 = integer_literal $Builtin.Int64, 10
+ %2 = struct $Int (%1 : $Builtin.Int64)
+ return %2 : $Int
+}
+sil_global hidden [let] @$global_var : $SomeProtocol
+
+// CHECK-LABEL: sil @witness_global_addr
+// CHECK: bb0
+// CHECK: alloc_global
+// CHECK: global_addr
+// CHECK: init_existential_addr
+// CHECK: alloc_ref
+// CHECK: store
+// CHECK: witness_method
Don’t we expect to see the witness_method get devirtualized here?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#16116 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AFBwabksque4CBf7_04joygf5BGdE_w_ks5trnsPgaJpZM4TgwjN>
.
|
@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. |
test/SILOptimizer/sil_combine.sil
Outdated
@@ -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 |
There was a problem hiding this comment.
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
Can you squash the commits together and also set your git email, then do |
Done! |
@swift-ci Please smoke test |
@gottesmm I'm OK with merging this if you are, want to take one final look at it? |
@swift-ci Please smoke test |
@slavapestov Let me read it on the bus. |
There was a problem hiding this 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()) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two things:
- This should be nullptr. We do not use NULL in the code base.
- 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)) { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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).
Thanks. I added a failing test in the same file sil_combine_global_addr.sil. Please have a look. |
There was a problem hiding this 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)) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
@gottesmm both addressed! Please have a look. |
/// Check for a single InitExistential. | ||
InitExistentialAddrInst *SingleIE = nullptr; | ||
auto *BB = CAI->getParent(); | ||
for (auto Use : GAI->getUses()) { |
There was a problem hiding this comment.
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 && |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 && |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?!");
Things done:
|
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. |
Should be good now.. |
LGTM |
@swift-ci Please smoke test |
This PR extends concrete type propagation in SILCombiner to handle global_addrs.