@@ -15123,14 +15123,14 @@ Lowerer::GenerateFastElemICommon(
15123
15123
IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
15124
15124
if (indexOpnd)
15125
15125
{
15126
- if (indexOpnd->GetValueType().IsString ())
15126
+ if (indexOpnd->GetValueType().IsLikelyString ())
15127
15127
{
15128
15128
if (!baseOpnd->GetValueType().IsLikelyOptimizedTypedArray())
15129
15129
{
15130
15130
// 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.
15131
15131
*pIsTypedArrayElement = false;
15132
15132
*pIsStringIndex = true;
15133
- return m_lowererMD. GenerateFastElemIStringIndexCommon(instr, isStore, indirOpnd, labelHelper);
15133
+ return GenerateFastElemIStringIndexCommon(instr, isStore, indirOpnd, labelHelper);
15134
15134
}
15135
15135
else
15136
15136
{
@@ -15157,6 +15157,110 @@ Lowerer::GenerateFastElemICommon(
15157
15157
indirOpndOverflowed);
15158
15158
}
15159
15159
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
+
15160
15264
IR::IndirOpnd *
15161
15265
Lowerer::GenerateFastElemIIntIndexCommon(
15162
15266
IR::Instr * instr,
@@ -17924,63 +18028,73 @@ Lowerer::GenerateFastInlineHasOwnProperty(IR::Instr * instr)
17924
18028
return;
17925
18029
}
17926
18030
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
-
17933
18031
IR::RegOpnd * thisObj = argsOpnd[0]->AsRegOpnd();
17934
18032
IR::RegOpnd * propOpnd = argsOpnd[1]->AsRegOpnd();
17935
18033
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
+
17936
18040
IR::LabelInstr * doneLabel = InsertLabel(false, instr->m_next);
17937
18041
IR::LabelInstr * labelHelper = InsertLabel(true, instr);
17938
18042
17939
- IR::LabelInstr * cacheMissLabel = generateForInFastpath ? InsertLabel(true, labelHelper ) : labelHelper;
18043
+ IR::LabelInstr * cacheMissLabel = generateForInFastpath ? IR::LabelInstr::New(Js::OpCode::Label, m_func, true ) : labelHelper;
17940
18044
17941
- IR::Instr * insertInstr = cacheMissLabel ;
18045
+ IR::Instr * insertInstr = labelHelper ;
17942
18046
17943
- // TEST indexOpnd, AtomTag
18047
+ // GenerateObjectTest(propOpnd, $labelHelper)
17944
18048
// CMP indexOpnd, PropertyString::`vtable'
17945
18049
// JNE $helper
17946
- // MOV propertyCacheOpnd, propOpnd->propCache
17947
- // TEST thisObj, AtomTag
17948
- // JNE $labelHelper
18050
+ // GenerateObjectTest(thisObj, $labelHelper)
18051
+ // MOV inlineCacheOpnd, propOpnd->lsElemInlineCache
17949
18052
// 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
17952
18060
// MOV dst, ValueTrue
17953
18061
// JMP $done
17954
18062
17955
- if (!propOpnd->IsNotTaggedValue())
17956
- {
17957
- m_lowererMD.GenerateObjectTest(propOpnd, insertInstr, labelHelper);
17958
- }
18063
+ m_lowererMD.GenerateObjectTest(propOpnd, insertInstr, labelHelper);
17959
18064
17960
18065
InsertCompareBranch(IR::IndirOpnd::New(propOpnd, 0, TyMachPtr, m_func), LoadVTableValueOpnd(insertInstr, VTableValue::VtablePropertyString), Js::OpCode::BrNeq_A, labelHelper, insertInstr);
17961
18066
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);
17964
18068
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);
17969
18071
17970
18072
IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
17971
18073
InsertMove(objectTypeOpnd, IR::IndirOpnd::New(thisObj, Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, m_func), insertInstr);
17972
18074
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);
17974
18079
17975
18080
InsertMove(instr->GetDst(), LoadLibraryValueOpnd(instr, LibraryValue::ValueTrue), insertInstr);
17976
18081
InsertBranch(Js::OpCode::Br, doneLabel, insertInstr);
17977
18082
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
+
17978
18090
if (!generateForInFastpath)
17979
18091
{
17980
18092
RelocateCallDirectToHelperPath(tmpInstr, labelHelper);
17981
18093
return;
17982
18094
}
17983
18095
18096
+ insertInstr->InsertBefore(cacheMissLabel);
18097
+
17984
18098
// CMP forInEnumeratorOpnd->canUseJitFastPath, 0
17985
18099
// JEQ $labelHelper
17986
18100
// MOV cachedDataTypeOpnd, forInEnumeratorOpnd->enumeratorInitialType
0 commit comments