@@ -15123,14 +15123,14 @@ Lowerer::GenerateFastElemICommon(
1512315123 IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
1512415124 if (indexOpnd)
1512515125 {
15126- if (indexOpnd->GetValueType().IsString ())
15126+ if (indexOpnd->GetValueType().IsLikelyString ())
1512715127 {
1512815128 if (!baseOpnd->GetValueType().IsLikelyOptimizedTypedArray())
1512915129 {
1513015130 // If profile data says that it's a typed array - do not generate the property string fast path as the src. could be a temp and that would cause a bug.
1513115131 *pIsTypedArrayElement = false;
1513215132 *pIsStringIndex = true;
15133- return m_lowererMD. GenerateFastElemIStringIndexCommon(instr, isStore, indirOpnd, labelHelper);
15133+ return GenerateFastElemIStringIndexCommon(instr, isStore, indirOpnd, labelHelper);
1513415134 }
1513515135 else
1513615136 {
@@ -15157,6 +15157,110 @@ Lowerer::GenerateFastElemICommon(
1515715157 indirOpndOverflowed);
1515815158}
1515915159
15160+ void
15161+ Lowerer::GenerateDynamicLoadPolymorphicInlineCacheSlot(IR::Instr * instrInsert, IR::RegOpnd * inlineCacheOpnd, IR::Opnd * objectTypeOpnd)
15162+ {
15163+ // Generates:
15164+ // MOV opndOffset, objectTypeOpnd
15165+ // SHR opndOffset, PolymorphicInlineCacheShift
15166+ // MOVZX cacheIndexOpnd, inlineCacheOpnd->size
15167+ // DEC cacheIndexOpnd
15168+ // AND opndOffset, cacheIndexOpnd
15169+ // SHL opndOffset, Math::Log2(sizeof(Js::InlineCache))
15170+ // MOV inlineCacheOpnd, inlineCacheOpnd->inlineCaches
15171+ // LEA inlineCacheOpnd, [inlineCacheOpnd + opndOffset]
15172+
15173+ IntConstType rightShiftAmount = PolymorphicInlineCacheShift;
15174+ IntConstType leftShiftAmount = Math::Log2(sizeof(Js::InlineCache));
15175+ Assert(rightShiftAmount > leftShiftAmount);
15176+ IR::RegOpnd * opndOffset = IR::RegOpnd::New(TyMachPtr, m_func);
15177+ InsertShift(Js::OpCode::ShrU_A, false, opndOffset, objectTypeOpnd, IR::IntConstOpnd::New(rightShiftAmount, TyUint8, m_func, true), instrInsert);
15178+
15179+ IR::RegOpnd * cacheIndexOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
15180+ InsertMove(cacheIndexOpnd, IR::IndirOpnd::New(inlineCacheOpnd, Js::PolymorphicInlineCache::GetOffsetOfSize(), TyUint16, m_func), instrInsert);
15181+ InsertSub(false, cacheIndexOpnd, cacheIndexOpnd, IR::IntConstOpnd::New(1, TyMachPtr, m_func), instrInsert);
15182+ InsertAnd(opndOffset, opndOffset, cacheIndexOpnd, instrInsert);
15183+ InsertShift(Js::OpCode::Shl_A, false, opndOffset, opndOffset, IR::IntConstOpnd::New(leftShiftAmount, TyUint8, m_func), instrInsert);
15184+ InsertMove(inlineCacheOpnd, IR::IndirOpnd::New(inlineCacheOpnd, Js::PolymorphicInlineCache::GetOffsetOfInlineCaches(), TyMachPtr, m_func), instrInsert);
15185+ InsertLea(inlineCacheOpnd, IR::IndirOpnd::New(inlineCacheOpnd, opndOffset, TyMachPtr, m_func), instrInsert);
15186+ }
15187+
15188+ IR::IndirOpnd *
15189+ Lowerer::GenerateFastElemIStringIndexCommon(IR::Instr * instrInsert, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper)
15190+ {
15191+ IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
15192+ IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
15193+ Assert(baseOpnd != nullptr);
15194+ Assert(indexOpnd->GetValueType().IsLikelyString());
15195+
15196+ // Generates:
15197+ // StringTest(indexOpnd, $helper) ; verify index is string type
15198+ // CMP indexOpnd, PropertyString::`vtable' ; verify index is property string
15199+ // JNE $helper
15200+ // MOV inlineCacheOpnd, index->inlineCache
15201+ // GenerateObjectTest(baseOpnd, $helper) ; verify base is an object
15202+ // MOV objectTypeOpnd, baseOpnd->type
15203+ // GenerateDynamicLoadPolymorphicInlineCacheSlot(inlineCacheOpnd, objectTypeOpnd) ; loads inline cache for given type
15204+ // LocalInlineCacheCheck(objectTypeOpnd, inlineCacheOpnd, $notInlineSlots) ; check for type in local inline slots, jump to $notInlineSlotsLabel on failure
15205+ // MOV opndSlotArray, baseOpnd
15206+ // JMP slotArrayLoadedLabel
15207+ // $notInlineSlotsLabel
15208+ // opndTaggedType = GenerateLoadTaggedType(objectTypeOpnd) ; load objectTypeOpnd with InlineCacheAuxSlotTypeTag into opndTaggedType
15209+ // LocalInlineCacheCheck(opndTaggedType, inlineCacheOpnd, $helper) ; check for type in local aux slots, jump to $helper on failure
15210+ // MOV opndSlotArray, baseOpnd->auxSlots ; load the aux slot array
15211+ // $slotArrayLoadedLabel
15212+ // MOV opndSlotIndex, inlineCacheOpnd->u.local.slotIndex ; load the cached slot offset or index
15213+ // INC indexOpnd->hitRate
15214+
15215+ GenerateStringTest(indexOpnd, instrInsert, labelHelper);
15216+
15217+ InsertCompareBranch(
15218+ IR::IndirOpnd::New(indexOpnd, 0, TyMachPtr, m_func),
15219+ LoadVTableValueOpnd(instrInsert, VTableValue::VtablePropertyString),
15220+ Js::OpCode::BrNeq_A, labelHelper, instrInsert);
15221+
15222+ m_lowererMD.GenerateObjectTest(baseOpnd, instrInsert, labelHelper);
15223+
15224+ IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
15225+ InsertMove(objectTypeOpnd, IR::IndirOpnd::New(baseOpnd, Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, m_func), instrInsert);
15226+
15227+ const uint32 inlineCacheOffset = isStore ? Js::PropertyString::GetOffsetOfStElemInlineCache() : Js::PropertyString::GetOffsetOfLdElemInlineCache();
15228+
15229+ IR::RegOpnd * inlineCacheOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
15230+ InsertMove(inlineCacheOpnd, IR::IndirOpnd::New(indexOpnd, inlineCacheOffset, TyMachPtr, m_func), instrInsert);
15231+
15232+ GenerateDynamicLoadPolymorphicInlineCacheSlot(instrInsert, inlineCacheOpnd, objectTypeOpnd);
15233+
15234+ IR::LabelInstr * notInlineSlotsLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
15235+ IR::LabelInstr * slotArrayLoadedLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
15236+
15237+ m_lowererMD.GenerateLocalInlineCacheCheck(instrInsert, objectTypeOpnd, inlineCacheOpnd, notInlineSlotsLabel);
15238+
15239+ IR::RegOpnd * opndSlotArray = IR::RegOpnd::New(TyMachReg, instrInsert->m_func);
15240+ InsertMove(opndSlotArray, baseOpnd, instrInsert);
15241+ InsertBranch(Js::OpCode::Br, slotArrayLoadedLabel, instrInsert);
15242+
15243+ instrInsert->InsertBefore(notInlineSlotsLabel);
15244+ IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, this->m_func);
15245+ m_lowererMD.GenerateLoadTaggedType(instrInsert, objectTypeOpnd, opndTaggedType);
15246+ m_lowererMD.GenerateLocalInlineCacheCheck(instrInsert, opndTaggedType, inlineCacheOpnd, labelHelper);
15247+
15248+ IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(baseOpnd, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrInsert->m_func);
15249+ InsertMove(opndSlotArray, opndIndir, instrInsert);
15250+
15251+ instrInsert->InsertBefore(slotArrayLoadedLabel);
15252+
15253+ IR::RegOpnd * opndSlotIndex = IR::RegOpnd::New(TyMachReg, instrInsert->m_func);
15254+ InsertMove(opndSlotIndex, IR::IndirOpnd::New(inlineCacheOpnd, (int32)offsetof(Js::InlineCache, u.local.slotIndex), TyUint16, instrInsert->m_func), instrInsert);
15255+
15256+ IR::IndirOpnd * hitRateOpnd = IR::IndirOpnd::New(indexOpnd, Js::PropertyString::GetOffsetOfHitRate(), TyInt32, m_func);
15257+ IR::IntConstOpnd * incOpnd = IR::IntConstOpnd::New(1, TyInt32, instrInsert->m_func);
15258+ InsertAdd(false, hitRateOpnd, hitRateOpnd, incOpnd, instrInsert);
15259+
15260+ // return [opndSlotArray + opndSlotIndex * PtrSize]
15261+ return IR::IndirOpnd::New(opndSlotArray, opndSlotIndex, m_lowererMD.GetDefaultIndirScale(), TyMachReg, instrInsert->m_func);
15262+ }
15263+
1516015264IR::IndirOpnd *
1516115265Lowerer::GenerateFastElemIIntIndexCommon(
1516215266 IR::Instr * instr,
@@ -17924,63 +18028,73 @@ Lowerer::GenerateFastInlineHasOwnProperty(IR::Instr * instr)
1792418028 return;
1792518029 }
1792618030
17927- // fast path case where hasOwnProperty is being called using a property name loaded via a for-in loop
17928- bool generateForInFastpath = argsOpnd[1]->GetValueType().IsString()
17929- && argsOpnd[1]->AsRegOpnd()->m_sym->m_isSingleDef
17930- && (argsOpnd[1]->AsRegOpnd()->m_sym->m_instrDef->m_opcode == Js::OpCode::BrOnEmpty
17931- || argsOpnd[1]->AsRegOpnd()->m_sym->m_instrDef->m_opcode == Js::OpCode::BrOnNotEmpty);
17932-
1793318031 IR::RegOpnd * thisObj = argsOpnd[0]->AsRegOpnd();
1793418032 IR::RegOpnd * propOpnd = argsOpnd[1]->AsRegOpnd();
1793518033
18034+ // fast path case where hasOwnProperty is being called using a property name loaded via a for-in loop
18035+ bool generateForInFastpath = propOpnd->GetValueType().IsString()
18036+ && propOpnd->m_sym->m_isSingleDef
18037+ && (propOpnd->m_sym->m_instrDef->m_opcode == Js::OpCode::BrOnEmpty
18038+ || propOpnd->m_sym->m_instrDef->m_opcode == Js::OpCode::BrOnNotEmpty);
18039+
1793618040 IR::LabelInstr * doneLabel = InsertLabel(false, instr->m_next);
1793718041 IR::LabelInstr * labelHelper = InsertLabel(true, instr);
1793818042
17939- IR::LabelInstr * cacheMissLabel = generateForInFastpath ? InsertLabel(true, labelHelper ) : labelHelper;
18043+ IR::LabelInstr * cacheMissLabel = generateForInFastpath ? IR::LabelInstr::New(Js::OpCode::Label, m_func, true ) : labelHelper;
1794018044
17941- IR::Instr * insertInstr = cacheMissLabel ;
18045+ IR::Instr * insertInstr = labelHelper ;
1794218046
17943- // TEST indexOpnd, AtomTag
18047+ // GenerateObjectTest(propOpnd, $labelHelper)
1794418048 // CMP indexOpnd, PropertyString::`vtable'
1794518049 // JNE $helper
17946- // MOV propertyCacheOpnd, propOpnd->propCache
17947- // TEST thisObj, AtomTag
17948- // JNE $labelHelper
18050+ // GenerateObjectTest(thisObj, $labelHelper)
18051+ // MOV inlineCacheOpnd, propOpnd->lsElemInlineCache
1794918052 // MOV objectTypeOpnd, thisObj->type
17950- // CMP propertyCacheOpnd->type, objectTypeOpnd
17951- // JNE $cacheMissLabel
18053+ // GenerateDynamicLoadPolymorphicInlineCacheSlot(inlineCacheOpnd, objectTypeOpnd) ; loads inline cache for given type
18054+ // GenerateLocalInlineCacheCheck(objectTypeOpnd, inlineCacheOpnd, $notInlineSlotsLabel) ; check for type in inline slots, jump to $notInlineSlotsLabel on failure
18055+ // MOV dst, ValueTrue
18056+ // JMP $done
18057+ // $notInlineSlotsLabel:
18058+ // GenerateLoadTaggedType(objectTypeOpnd, opndTaggedType)
18059+ // GenerateLocalInlineCacheCheck(opndTaggedType, inlineCacheOpnd, $cacheMissLabel) ; check for type in aux slot, jump to $cacheMissLabel on failure
1795218060 // MOV dst, ValueTrue
1795318061 // JMP $done
1795418062
17955- if (!propOpnd->IsNotTaggedValue())
17956- {
17957- m_lowererMD.GenerateObjectTest(propOpnd, insertInstr, labelHelper);
17958- }
18063+ m_lowererMD.GenerateObjectTest(propOpnd, insertInstr, labelHelper);
1795918064
1796018065 InsertCompareBranch(IR::IndirOpnd::New(propOpnd, 0, TyMachPtr, m_func), LoadVTableValueOpnd(insertInstr, VTableValue::VtablePropertyString), Js::OpCode::BrNeq_A, labelHelper, insertInstr);
1796118066
17962- IR::RegOpnd * propertyCacheOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
17963- InsertMove(propertyCacheOpnd, IR::IndirOpnd::New(propOpnd, Js::PropertyString::GetOffsetOfPropertyCache(), TyMachPtr, m_func), insertInstr);
18067+ m_lowererMD.GenerateObjectTest(thisObj, insertInstr, labelHelper);
1796418068
17965- if (!thisObj->IsNotTaggedValue())
17966- {
17967- m_lowererMD.GenerateObjectTest(thisObj, insertInstr, labelHelper);
17968- }
18069+ IR::RegOpnd * inlineCacheOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
18070+ InsertMove(inlineCacheOpnd, IR::IndirOpnd::New(propOpnd, Js::PropertyString::GetOffsetOfLdElemInlineCache(), TyMachPtr, m_func), insertInstr);
1796918071
1797018072 IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
1797118073 InsertMove(objectTypeOpnd, IR::IndirOpnd::New(thisObj, Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, m_func), insertInstr);
1797218074
17973- InsertCompareBranch(IR::IndirOpnd::New(propertyCacheOpnd, (int32)offsetof(Js::PropertyCache, type), TyMachPtr, m_func), objectTypeOpnd, Js::OpCode::BrNeq_A, cacheMissLabel, insertInstr);
18075+ GenerateDynamicLoadPolymorphicInlineCacheSlot(insertInstr, inlineCacheOpnd, objectTypeOpnd);
18076+
18077+ IR::LabelInstr * notInlineSlotsLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
18078+ m_lowererMD.GenerateLocalInlineCacheCheck(insertInstr, objectTypeOpnd, inlineCacheOpnd, notInlineSlotsLabel);
1797418079
1797518080 InsertMove(instr->GetDst(), LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue), insertInstr);
1797618081 InsertBranch(Js::OpCode::Br, doneLabel, insertInstr);
1797718082
18083+ insertInstr->InsertBefore(notInlineSlotsLabel);
18084+ IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, m_func);
18085+ m_lowererMD.GenerateLoadTaggedType(insertInstr, objectTypeOpnd, opndTaggedType);
18086+ m_lowererMD.GenerateLocalInlineCacheCheck(insertInstr, opndTaggedType, inlineCacheOpnd, cacheMissLabel);
18087+ InsertMove(instr->GetDst(), LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue), insertInstr);
18088+ InsertBranch(Js::OpCode::Br, doneLabel, insertInstr);
18089+
1797818090 if (!generateForInFastpath)
1797918091 {
1798018092 RelocateCallDirectToHelperPath(tmpInstr, labelHelper);
1798118093 return;
1798218094 }
1798318095
18096+ insertInstr->InsertBefore(cacheMissLabel);
18097+
1798418098 // CMP forInEnumeratorOpnd->canUseJitFastPath, 0
1798518099 // JEQ $labelHelper
1798618100 // MOV cachedDataTypeOpnd, forInEnumeratorOpnd->enumeratorInitialType
0 commit comments