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

NewScObj refactor #6180

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

wyrichte
Copy link
Collaborator

NewScObj refactor - Splits NewScObj into multiple bytecodes with 4 distinct parts: GenCtorObj (makes ctor object), NewScObj (calls ctor with ctor object), determine ret val (done using multiple bytecode instrs), UpdateNewScObjCache.

@wyrichte wyrichte force-pushed the build/wyrichte/NewScObj/BASE_squash_rebase_bcup_refactor_squash branch 2 times, most recently from b3cacab to 9ae76ce Compare June 25, 2019 20:23
@MikeHolman
Copy link
Contributor

Have you done perf testing?

lib/Backend/IR.cpp Outdated Show resolved Hide resolved
// up the ArgOut chain, this instr is accessible via instrDef and thus this instr's
// dst cannot have a symbol that has already been defined.
case Js::OpCode::GenCtorObj:
SetTempUsed(R0, TRUE);
Copy link
Contributor

Choose a reason for hiding this comment

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

any reason why this isn't the same as case for Js::OpCode::UpNewScObjCache?

Copy link
Contributor

@pleath pleath Jun 27, 2019

Choose a reason for hiding this comment

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

What is SetTempUsed accomplishing here? R0 is not a temp and shouldn't be renumbered regardless. (Edit: I see, the passed-in "R0", not the byte code reg "R0".

Copy link
Contributor

Choose a reason for hiding this comment

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

It's odd that we should have to do this. What does the byte code sequence look like? Where is the reg defined?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have any input on this question? Thanks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This hack is necessary for the following scenario:

  • A dest symbol that has a def but no use can be emitted (ex: "a = b + c" where "a" is never used)
  • "a" will be given a symbol (ex: "s5") but the next time a dest symbol is required (ex: a GenCtorObj instr) "s5" will be reused since "a" is never used.
  • now "s5" is the dest of two instrs.
  • "s5" loses its instrDef value because it has more than one instrDef

We need instrDef in order to find the GenCtorObj instr during the ArgOut chain traversal. so to solve this issue we make sure that the symbol of a GenCtorObj instr is unique.

This is unnecessary for UpNewScObjCache because UpNewScObjCache is not involved in any ArgOut chain traversal.

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 give me a script that exhibits the behavior you're talking about? If an unused dst is not consumed at the def point, we'll have more problems than just the one you're describing.

Copy link
Contributor

Choose a reason for hiding this comment

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

@pleath Looks like we ran into this issue for OpCode::StArrInlineItem_CI4 in the past (but handled it differently). Based on comment for that opcode in IRBuilder, I was able to find simple case that doesn't consume the dst: for (let i = 0; i < 0; i + 1) { }

Copy link
Contributor

Choose a reason for hiding this comment

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

Arf. Do you have a little script that behaves this way? We need an automated way of detecting these. Maybe a debug flag that indicates a parse node is participating in a question expression.

Copy link
Contributor

Choose a reason for hiding this comment

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

Now that I think of it, a hint from the parser to identify intermediate results of ?: ops might allow us to simplify all these things and eliminate Unused altogether. That's probably the way to go, not least because there's nothing that requires Unused for correctness, so we'll keep getting these unrenamed lifetimes. I'll look into that.

{
return;
}

bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
const JITTimeConstructorCache * ctorCache = instr->IsProfiledInstr() ?
instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId)) : nullptr;

// TODO: OOP JIT, enable assert
//Assert(ctorCache == nullptr || srcVal->GetValueInfo()->IsVarConstant() && Js::VarIs<Js::JavascriptFunction>(srcVal->GetValueInfo()->AsVarConstant()->VarValue()));
Assert(ctorCache == nullptr || !ctorCache->IsTypeFinal() || ctorCache->CtorHasNoExplicitReturnValue());

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like this is going to produce a guard check where we don't really need one. Is that true?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

GenCtorObj is performing similar functionality to NewScObjectNoCtor; this change was made because the only instr that can reach this point is GenCtorObj thus isCtorInlined || ctorCache->IsTypeFinal() would always return true.

The way to determine if this bailout is necessary is to see why NewScObjectNoCtor instrs were originally emitted and if the emitted GenCtorObj instr has those same properties.

Copy link
Contributor

Choose a reason for hiding this comment

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

The key difference is that GenCtorObj is emitted whether the ctor is inlined or not. That isn't true of NewScObjectNoCtor, which is only emitted as the result of inlining (I think). If there isn't an optimization requiring a guard check (usually inlining, but also, apparently, final type opt on the type assigned to the new object), we shouldn't need a guard check. The final type opt seems to belong to GenCtorObj now, since it's creating the object according to the hints in the ctorCache.

lib/Backend/Inline.cpp Outdated Show resolved Hide resolved
MikeHolman and others added 2 commits July 15, 2019 09:06
…stinct parts: GenCtorObj (makes ctor object), NewScObj (calls ctor with ctor object), determine ret val (done using multiple bytecode instrs), UpdateNewScObjCache.
@wyrichte wyrichte force-pushed the build/wyrichte/NewScObj/BASE_squash_rebase_bcup_refactor_squash branch from 17d30e5 to 3a216cb Compare July 26, 2019 22:44
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.

None yet

3 participants