Skip to content

Propagate concrete types of arguments to apply #17370

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
Sep 10, 2018

Conversation

rajbarik
Copy link
Contributor

Final Part of #13991 that propagates concrete types of arguments to apply. This handles non-self arguments as well.

@rajbarik
Copy link
Contributor Author

@slavapestov @atrick This PR is part of #13991 and just focuses on concrete type propagation for all arguments of an Apply.

atrick
atrick previously requested changes Jun 20, 2018
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.

New SILCombine-like optimizations should have SIL level unit tests. Those unit tests should cover any special cases that pass the SIL verifier but aren't currently generated with source-level tests.

Much of the code that you're adding is handled by ConcreteExistentialInfo from
#17315. I'd prefer these correctness fixes be merged first, with additional optimization expressed in terms of what I think is now a correct API.

return ConcreteType;
return type;
},
LookUpConformanceInSignature(*(FnTy->getGenericSignature())));
Copy link
Contributor

Choose a reason for hiding this comment

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

We're not supposed to call this in the optimizer because all conformances should already be present, type checked, and available from SIL instructions. (This is one area where the interface between the type system an compiler is seriously broken). You should use ConcreteExistentialInfo.lookupConformance() from PR17315 instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, you need one ConcreteExistentialInfo for each argument being replaced

SILCombiner::propagateConcreteTypeOfInitExistentialsToAllApplyArgs(
FullApplySite AI) {
/// Check if legal to perform propagation.
if (!AI.hasSubstitutions())
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this because you check for a polymorphic callee below already

Type ContextTy;
bool isCopied = false;

InitExistential = findInitExistential(AI, OrigApplyArg, OpenedArchetype,
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor this as discussed

if (!FnTy->isPolymorphic())
return nullptr;

auto Args = Callee->begin()->getFunctionArguments();
Copy link
Contributor

Choose a reason for hiding this comment

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

What if the callee does not have a body?

if (!AI.hasSubstitutions())
return nullptr;
auto *Callee = AI.getReferencedFunction();
if (!Callee || !Callee->shouldOptimize() || Callee->empty())
Copy link
Contributor

Choose a reason for hiding this comment

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

This restricts the optimization unnecessarily. It should work even if the callee of the apply is not a static function_ref. You should be looking at the operand of the apply's SILFunctionType, and walk the parameters of the function type. The arguments of the callee should not be needed.

@@ -1027,6 +1053,124 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI) {
return propagateConcreteTypeOfInitExistential(AI, PD, PropagateIntoOperand);
}

/// A peephole optimizer that propagates concrete types of all arguments to
/// Apply instruction. It handles all the arguments of an apply including self.
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment describing the transformation

@slavapestov slavapestov self-assigned this Jun 27, 2018
@slavapestov
Copy link
Contributor

Reminder: check for inout

@rajbarik rajbarik force-pushed the raj-cp-allargs branch 3 times, most recently from 9853879 to 9452e0f Compare August 30, 2018 00:22
@rajbarik
Copy link
Contributor Author

@atrick can you review this PR? This PR propagates concrete types to all archetype args, not just self so that generic specializer can create a specialized version. @slavapestov fyi.

@rajbarik rajbarik force-pushed the raj-cp-allargs branch 2 times, most recently from 7ec3892 to 728297c Compare August 30, 2018 18:58
unsigned ArgIdx);
SILInstruction *createApplyWithConcreteType(
FullApplySite Apply,
llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *, 8> &CEIs,
Copy link
Contributor

Choose a reason for hiding this comment

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

Passing SmallDenseMapImpl instead would allow you to avoid hard-coding the size here. Also you should make it 'const'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which header file has this SmallDenseMapImpl?

for (unsigned ArgIdx = 0; ArgIdx < NumApplyArgs; ArgIdx++) {
llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *, 8>::iterator
ArgIt = CEIs.find(ArgIdx);
if (ArgIt != CEIs.end()) {
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 the == case should come first, with a 'continue' at the end. Then the rest does not need a whole level of indent.

// Do a conformance lookup on this witness requirement using the
// existential's conformances. The witness requirement may be a
// base type of the existential's requirements.
return CEI->lookupExistentialConformance(proto).getValue();
Copy link
Contributor

Choose a reason for hiding this comment

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

This callback returns an Optional already, I don't think you need to do getValue() here.

@rajbarik rajbarik force-pushed the raj-cp-allargs branch 2 times, most recently from 32baa7a to d7e59db Compare September 4, 2018 19:41
@rajbarik
Copy link
Contributor Author

rajbarik commented Sep 6, 2018

@atrick find sometime to review this?

@rajbarik rajbarik dismissed atrick’s stale review September 6, 2018 20:54

Already handled.

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.

LGMT other than a couple comments below.

// Construct the map for Self to be used for createApplyWithConcreteType.
llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *> CEIs;
CEIs.insert(std::pair<unsigned, const ConcreteExistentialInfo *>(
Apply.getNumArguments() - 1, &CEI));
Copy link
Contributor

Choose a reason for hiding this comment

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

We do this operand index hard-coding all over, but I would prefer Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()).

// of OpenedArchetype in the function signature, but will only rewrite the
// Arg operand.
//
// Note that the language does not allow Arg to occur in contravariant
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 only applies to Self not Arg. I think we should now say that this bailout check is needed for non-Self arguments, and also needed for Self because even though the language does not allow Self to occur in contravariant position ...

So, I think this bailout is still sufficient. Essentially, for now we only allow specialization on a single generic argument. @slavapestov does it make sense to you?

@rajbarik
Copy link
Contributor Author

rajbarik commented Sep 7, 2018

Thanks @atrick . I pushed a new commit.

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.

LGTM

// references to OpenedArchetype will be substituted. So walk to type to
// find all possible references, such as returning Optional<Arg>.
if (Apply.getType().getASTType().findIf(
[&CEI](Type t) -> bool { return t->isEqual(CEI.OpenedArchetype); })) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This snippet appears in a few places -- extract a new static function?

@slavapestov
Copy link
Contributor

@swift-ci Please test

@slavapestov
Copy link
Contributor

@swift-ci Please test source compatibility

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 9452e0f271ec44dfed0f0b31e6cd81b1ae742d1c

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 9452e0f271ec44dfed0f0b31e6cd81b1ae742d1c

@eeckstein
Copy link
Contributor

@rajbarik Unfortunately this broke the asan bot. I reverted it.

@rajbarik
Copy link
Contributor Author

@eeckstein Anything I can do to help?

@eeckstein
Copy link
Contributor

That's (most likely) caused by a bug in your code. I recommend to reproduce the issue locally with that asan configuration.

@atrick
Copy link
Contributor

atrick commented Sep 12, 2018

@eeckstein @rajbarik normally the log file from the ASAN bot has enough information to give you a clue where to look in the code.

@rajbarik
Copy link
Contributor Author

rajbarik commented Sep 12, 2018 via email

@rajbarik
Copy link
Contributor Author

rajbarik commented Sep 12, 2018 via email

@rajbarik
Copy link
Contributor Author

@atrick I think I narrowed down the problem. I was storing stack allocated CEI structs in a loop using a map and then was using the map outside the loop later:

for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
 991     auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
 992     if (!ArgASTType->hasArchetype())
 993       continue;
 994     const ConcreteExistentialInfo CEI(Apply.getArgumentOperands()[ArgIdx]);
 995     if (!CEI.isValid())
 996       continue;
 997
 998     CEIs.insert(
 999         std::pair<unsigned, const ConcreteExistentialInfo *>(ArgIdx, &CEI));
1000
1001     if (CEI.ConcreteType->isOpenedExistential()) {
1002       // Temporarily record this opened existential def in this local
1003       // BuilderContext before rewriting the witness method.
1004       OpenedArchetypesTracker.addOpenedArchetypeDef(
1005           cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
1006     }

I can think of two ways of fixing this:

  1. make the type of map to be of <unsigned, const ConcreteExistentialInfo> in which case when we store, we copy [this requires that your code
ConcreteExistentialInfo(ConcreteExistentialInfo &) = delete;

be removed and CEIs are copied around. Is there any particular reason you disabled the copy constructor?
2) A ConcreteExistentialInfo be allocated/deallocated on the heap. Lots of boiler plate code be embedded for new/delete. I would prefer the first one though.

Any recommendations?

@atrick
Copy link
Contributor

atrick commented Sep 12, 2018

Deleting the copy constructor just signals the original intention that's it's a grouping of local variables that we don't want to accidentally pass-by-copy or store beyond its caller's scope. In general, you can migrate something like this to a collection of multiple elements using unique_ptr to each "Info" object. However, in this case there's nothing inside ConcreteExistentialInfo except pointer-like things, so the default copy constructor is cheap and does the right thing (you can just remove the deleted copy ctor). Copying them into a hash map of dynamically allocated storage one by one is fine.

@rajbarik
Copy link
Contributor Author

@atrick @slavapestov could you review #19287 now?

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.

5 participants