Skip to content

Commit d3ae360

Browse files
Fast paths for strings, likely boolean and likely objects for opcodes
CmEq_A, CmNeq_A, CmSrEq_A and CmSrNeq_A We have fast paths for equivalent Br* opcodes so I refactored the code and reused it for Cm* opcodes. Basically Cm* opcodes behave as branch where we store success if equal otherwise false in destination.
1 parent 2db2231 commit d3ae360

File tree

9 files changed

+470
-175
lines changed

9 files changed

+470
-175
lines changed

lib/Backend/Chakra.Backend.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@
340340
<ClInclude Include="DbCheckPostLower.h" />
341341
<ClInclude Include="EmitBuffer.h" />
342342
<ClInclude Include="Encoder.h" />
343+
<ClInclude Include="ExternalLowerer.h" />
343344
<ClInclude Include="FlowGraph.h" />
344345
<ClInclude Include="Func.h" />
345346
<ClInclude Include="GlobHashTable.h" />

lib/Backend/ExternalLowerer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
class ExternalLowerer
99
{
1010
public:
11-
static bool TryGenerateFastExternalEqTest(IR::Opnd * src1, IR::Opnd * src2, IR::BranchInstr * instrBranch,
11+
static bool TryGenerateFastExternalEqTest(IR::Opnd * src1, IR::Opnd * src2, IR::Instr * instrBranch,
1212
IR::LabelInstr * labelHelper, IR::LabelInstr * labelBooleanCmp, Lowerer * lowerer, bool isStrictBr);
1313
};
1414

1515
#if !NTBUILD
1616
// ChakraCore default implementation doesn't have anything external type to check
1717
inline bool
18-
ExternalLowerer::TryGenerateFastExternalEqTest(IR::Opnd * src1, IR::Opnd * src2, IR::BranchInstr * instrBranch,
18+
ExternalLowerer::TryGenerateFastExternalEqTest(IR::Opnd * src1, IR::Opnd * src2, IR::Instr * instrBranch,
1919
IR::LabelInstr * labelHelper, IR::LabelInstr * labelBooleanCmp, Lowerer * lowerer, bool isStrictBr)
2020
{
2121
return false;

lib/Backend/Lower.cpp

Lines changed: 189 additions & 41 deletions
Large diffs are not rendered by default.

lib/Backend/Lower.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class Lowerer
247247
IR::BranchInstr* GenerateFastBrConst(IR::BranchInstr *branchInstr, IR::Opnd * constOpnd, bool isEqual);
248248
bool GenerateFastCondBranch(IR::BranchInstr * instrBranch, bool *pIsHelper);
249249
bool GenerateFastBrEqLikely(IR::BranchInstr * instrBranch, bool *pNeedHelper);
250+
bool GenerateFastBooleanAndObjectEqLikely(IR::Instr * instr, IR::Opnd *src1, IR::Opnd *src2, IR::LabelInstr * labelHelper, IR::LabelInstr * labelEqualLikely, bool *pNeedHelper);
251+
bool GenerateFastCmEqLikely(IR::Instr * instr, bool *pNeedHelper);
250252
bool GenerateFastBrBool(IR::BranchInstr *const instr);
251253
static IR::Instr *LoadFloatFromNonReg(IR::Opnd * opndOrig, IR::Opnd * regOpnd, IR::Instr * instrInsert);
252254
void LoadInt32FromUntaggedVar(IR::Instr *const instrLoad);

lib/Backend/LowerMDShared.cpp

Lines changed: 173 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,42 +2281,181 @@ LowererMD::GenerateFastDivByPow2(IR::Instr *instr)
22812281
}
22822282

22832283
bool
2284-
LowererMD::GenerateFastBrString(IR::BranchInstr *branchInstr)
2284+
LowererMD::GenerateFastBrOrCmString(IR::Instr* instr)
22852285
{
2286-
Assert(branchInstr->m_opcode == Js::OpCode::BrSrEq_A ||
2287-
branchInstr->m_opcode == Js::OpCode::BrSrNeq_A ||
2288-
branchInstr->m_opcode == Js::OpCode::BrEq_A ||
2289-
branchInstr->m_opcode == Js::OpCode::BrNeq_A ||
2290-
branchInstr->m_opcode == Js::OpCode::BrSrNotEq_A ||
2291-
branchInstr->m_opcode == Js::OpCode::BrSrNotNeq_A ||
2292-
branchInstr->m_opcode == Js::OpCode::BrNotEq_A ||
2293-
branchInstr->m_opcode == Js::OpCode::BrNotNeq_A
2294-
);
2286+
IR::RegOpnd *srcReg1 = instr->GetSrc1()->IsRegOpnd() ? instr->GetSrc1()->AsRegOpnd() : nullptr;
2287+
IR::RegOpnd *srcReg2 = instr->GetSrc2()->IsRegOpnd() ? instr->GetSrc2()->AsRegOpnd() : nullptr;
22952288

2296-
IR::Instr* instrInsert = branchInstr;
2297-
IR::RegOpnd *srcReg1 = branchInstr->GetSrc1()->IsRegOpnd() ? branchInstr->GetSrc1()->AsRegOpnd() : nullptr;
2298-
IR::RegOpnd *srcReg2 = branchInstr->GetSrc2()->IsRegOpnd() ? branchInstr->GetSrc2()->AsRegOpnd() : nullptr;
2289+
if (!srcReg1 ||
2290+
!srcReg2 ||
2291+
srcReg1->IsTaggedInt() ||
2292+
srcReg2->IsTaggedInt() ||
2293+
!srcReg1->GetValueType().IsLikelyString() ||
2294+
!srcReg2->GetValueType().IsLikelyString())
2295+
{
2296+
return false;
2297+
}
2298+
2299+
IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
2300+
IR::LabelInstr *labelBranchFail = nullptr;
2301+
IR::LabelInstr *labelBranchSuccess = nullptr;
2302+
IR::Instr *instrMovSuccess = nullptr;
2303+
IR::Instr *instrMovFailure = nullptr;
2304+
2305+
bool isEqual = false;
2306+
bool isStrict = false;
2307+
bool isBranch = true;
2308+
bool isCmNegOp = false;
22992309

2300-
if (srcReg1 && srcReg2)
2310+
switch (instr->m_opcode)
23012311
{
2302-
if (srcReg1->IsTaggedInt() || srcReg2->IsTaggedInt())
2303-
{
2304-
return false;
2305-
}
2312+
case Js::OpCode::BrSrEq_A:
2313+
case Js::OpCode::BrSrNotNeq_A:
2314+
isStrict = true;
2315+
case Js::OpCode::BrEq_A:
2316+
case Js::OpCode::BrNotNeq_A:
2317+
labelBranchFail = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2318+
labelBranchSuccess = instr->AsBranchInstr()->GetTarget();
2319+
instr->InsertAfter(labelBranchFail);
2320+
isEqual = true;
2321+
break;
23062322

2307-
bool isSrc1String = srcReg1->GetValueType().IsLikelyString();
2308-
bool isSrc2String = srcReg2->GetValueType().IsLikelyString();
2309-
//Left and right hand are both LikelyString
2310-
if (!isSrc1String || !isSrc2String)
2311-
{
2312-
return false;
2313-
}
2323+
case Js::OpCode::BrSrNeq_A:
2324+
case Js::OpCode::BrSrNotEq_A:
2325+
isStrict = true;
2326+
case Js::OpCode::BrNeq_A:
2327+
case Js::OpCode::BrNotEq_A:
2328+
labelBranchSuccess = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2329+
labelBranchFail = instr->AsBranchInstr()->GetTarget();
2330+
instr->InsertAfter(labelBranchSuccess);
2331+
isEqual = false;
2332+
break;
2333+
2334+
case Js::OpCode::CmSrEq_A:
2335+
isStrict = true;
2336+
case Js::OpCode::CmEq_A:
2337+
isEqual = true;
2338+
isBranch = false;
2339+
break;
2340+
2341+
case Js::OpCode::CmSrNeq_A:
2342+
isStrict = true;
2343+
case Js::OpCode::CmNeq_A:
2344+
isEqual = false;
2345+
isBranch = false;
2346+
isCmNegOp = true;
2347+
break;
2348+
2349+
default:
2350+
Assert(UNREACHED);
2351+
__assume(0);
2352+
}
2353+
2354+
if (!isBranch)
2355+
{
2356+
labelBranchSuccess = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2357+
labelBranchFail = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2358+
2359+
LibraryValue successValueType = !isCmNegOp ? LibraryValue::ValueTrue : LibraryValue::ValueFalse;
2360+
LibraryValue failureValueType = !isCmNegOp ? LibraryValue::ValueFalse : LibraryValue::ValueTrue;
2361+
2362+
instrMovFailure = IR::Instr::New(Js::OpCode::MOV, instr->GetDst(), this->m_lowerer->LoadLibraryValueOpnd(instr, failureValueType), m_func);
2363+
instrMovSuccess = IR::Instr::New(Js::OpCode::MOV, instr->GetDst(), this->m_lowerer->LoadLibraryValueOpnd(instr, successValueType), m_func);
2364+
}
2365+
2366+
this->GenerateFastStringCheck(instr, srcReg1, srcReg2, isEqual, isStrict, labelHelper, labelBranchSuccess, labelBranchFail);
2367+
2368+
IR::LabelInstr *labelFallthrough = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2369+
2370+
if (!isBranch)
2371+
{
2372+
instr->InsertBefore(labelBranchSuccess);
2373+
instr->InsertBefore(instrMovSuccess);
2374+
instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JMP, labelFallthrough, this->m_func));
2375+
2376+
instr->InsertBefore(labelBranchFail);
2377+
instr->InsertBefore(instrMovFailure);
2378+
instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JMP, labelFallthrough, this->m_func));
2379+
}
2380+
2381+
instr->InsertBefore(labelHelper);
2382+
2383+
instr->InsertAfter(labelFallthrough);
2384+
2385+
#if DBG
2386+
// The fast-path for strings assumes the case where 2 strings are equal is rare, and marks that path as 'helper'.
2387+
// This breaks the helper label dbchecks as it can result in non-helper blocks be reachable only from helper blocks.
2388+
// Use m_isHelperToNonHelperBranch and m_noHelperAssert to fix this.
2389+
IR::Instr *blockEndInstr;
2390+
2391+
if (isEqual)
2392+
{
2393+
blockEndInstr = labelHelper->GetNextBranchOrLabel();
23142394
}
23152395
else
23162396
{
2317-
return false;
2397+
blockEndInstr = instr->GetNextBranchOrLabel();
23182398
}
23192399

2400+
if (blockEndInstr->IsBranchInstr())
2401+
{
2402+
blockEndInstr->AsBranchInstr()->m_isHelperToNonHelperBranch = true;
2403+
}
2404+
2405+
labelFallthrough->m_noHelperAssert = true;
2406+
#endif
2407+
2408+
return true;
2409+
}
2410+
2411+
bool
2412+
LowererMD::GenerateFastStringCheck(IR::Instr *instr, IR::RegOpnd *srcReg1, IR::RegOpnd *srcReg2, bool isEqual, bool isStrict, IR::LabelInstr *labelHelper, IR::LabelInstr *labelBranchSuccess, IR::LabelInstr *labelBranchFail)
2413+
{
2414+
Assert(instr->m_opcode == Js::OpCode::BrSrEq_A ||
2415+
instr->m_opcode == Js::OpCode::BrSrNeq_A ||
2416+
instr->m_opcode == Js::OpCode::BrEq_A ||
2417+
instr->m_opcode == Js::OpCode::BrNeq_A ||
2418+
instr->m_opcode == Js::OpCode::BrSrNotEq_A ||
2419+
instr->m_opcode == Js::OpCode::BrSrNotNeq_A ||
2420+
instr->m_opcode == Js::OpCode::BrNotEq_A ||
2421+
instr->m_opcode == Js::OpCode::BrNotNeq_A ||
2422+
instr->m_opcode == Js::OpCode::CmEq_A ||
2423+
instr->m_opcode == Js::OpCode::CmNeq_A ||
2424+
instr->m_opcode == Js::OpCode::CmSrEq_A ||
2425+
instr->m_opcode == Js::OpCode::CmSrNeq_A );
2426+
2427+
// if src1 is not string
2428+
// generate object test, if not equal jump to $helper
2429+
// compare type check to string, if not jump to $helper
2430+
//
2431+
// if strict mode generate string test as above for src1 and jump to $failure if failed any time
2432+
// else if not strict generate string test as above for src1 and jump to $helper if failed any time
2433+
//
2434+
// Compare length of src1 and src2 if not equal goto $failure
2435+
//
2436+
// if src1 is not flat string jump to $helper
2437+
//
2438+
// if src1 and src2 m_pszValue pointer match goto $success
2439+
//
2440+
// if src2 is not flat string jump to $helper
2441+
//
2442+
// if first character of src1 and src2 doesn't match goto $failure
2443+
//
2444+
// shift left by 1 length of src1 (length*2)
2445+
//
2446+
// memcmp src1 and src2 flat strings till length * 2
2447+
//
2448+
// test eax (result of memcmp)
2449+
// if equal jump to $success else to $failure
2450+
//
2451+
// $success
2452+
// jmp to $fallthrough
2453+
// $failure
2454+
// jmp to $fallthrough
2455+
// $helper
2456+
//
2457+
// $fallthrough
2458+
23202459
// Generates:
23212460
// GenerateObjectTest(src1);
23222461
// MOV s1, [srcReg1 + offset(Type)]
@@ -2346,42 +2485,7 @@ LowererMD::GenerateFastBrString(IR::BranchInstr *branchInstr)
23462485
// JEQ $success
23472486
// JMP $fail
23482487

2349-
2350-
IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
2351-
IR::LabelInstr *labelTarget = branchInstr->GetTarget();
2352-
IR::LabelInstr *labelBranchFail = nullptr;
2353-
IR::LabelInstr *labelBranchSuccess = nullptr;
2354-
bool isEqual = false;
2355-
bool isStrict = false;
2356-
2357-
switch (branchInstr->m_opcode)
2358-
{
2359-
case Js::OpCode::BrSrEq_A:
2360-
case Js::OpCode::BrSrNotNeq_A:
2361-
isStrict = true;
2362-
case Js::OpCode::BrEq_A:
2363-
case Js::OpCode::BrNotNeq_A:
2364-
labelBranchFail = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2365-
labelBranchSuccess = labelTarget;
2366-
branchInstr->InsertAfter(labelBranchFail);
2367-
isEqual = true;
2368-
break;
2369-
2370-
case Js::OpCode::BrSrNeq_A:
2371-
case Js::OpCode::BrSrNotEq_A:
2372-
isStrict = true;
2373-
case Js::OpCode::BrNeq_A:
2374-
case Js::OpCode::BrNotEq_A:
2375-
labelBranchSuccess = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2376-
labelBranchFail = labelTarget;
2377-
branchInstr->InsertAfter(labelBranchSuccess);
2378-
isEqual = false;
2379-
break;
2380-
2381-
default:
2382-
Assert(UNREACHED);
2383-
__assume(0);
2384-
}
2488+
IR::Instr* instrInsert = instr;
23852489

23862490
this->m_lowerer->GenerateStringTest(srcReg1, instrInsert, labelHelper);
23872491

@@ -2471,50 +2575,24 @@ LowererMD::GenerateFastBrString(IR::BranchInstr *branchInstr)
24712575

24722576
// eax = memcmp(src1String, src2String, length*2)
24732577

2474-
this->LoadHelperArgument(branchInstr, src1LengthOpnd);
2475-
this->LoadHelperArgument(branchInstr, src1FlatString);
2476-
this->LoadHelperArgument(branchInstr, src2FlatString);
2578+
this->LoadHelperArgument(instr, src1LengthOpnd);
2579+
this->LoadHelperArgument(instr, src1FlatString);
2580+
this->LoadHelperArgument(instr, src2FlatString);
24772581
IR::RegOpnd *dstOpnd = IR::RegOpnd::New(TyInt32, this->m_func);
24782582
IR::Instr *instrCall = IR::Instr::New(Js::OpCode::CALL, dstOpnd, IR::HelperCallOpnd::New(IR::HelperMemCmp, this->m_func), this->m_func);
2479-
branchInstr->InsertBefore(instrCall);
2583+
instr->InsertBefore(instrCall);
24802584
this->LowerCall(instrCall, 3);
24812585

24822586
// TEST eax, eax
24832587
IR::Instr *instrTest = IR::Instr::New(Js::OpCode::TEST, this->m_func);
24842588
instrTest->SetSrc1(instrCall->GetDst());
24852589
instrTest->SetSrc2(instrCall->GetDst());
24862590
instrInsert->InsertBefore(instrTest);
2591+
24872592
// JEQ success
2488-
instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::JEQ, labelBranchSuccess, this->m_func));
2593+
instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JEQ, labelBranchSuccess, this->m_func));
24892594
// JMP fail
2490-
instrInsert->InsertBefore(IR::BranchInstr::New(Js::OpCode::JMP, labelBranchFail, this->m_func));
2491-
2492-
branchInstr->InsertBefore(labelHelper);
2493-
IR::LabelInstr *labelFallthrough = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
2494-
branchInstr->InsertAfter(labelFallthrough);
2495-
2496-
#if DBG
2497-
// The fast-path for strings assumes the case where 2 strings are equal is rare, and marks that path as 'helper'.
2498-
// This breaks the helper label dbchecks as it can result in non-helper blocks be reachable only from helper blocks.
2499-
// Use m_isHelperToNonHelperBranch and m_noHelperAssert to fix this.
2500-
IR::Instr *blockEndInstr;
2501-
2502-
if (isEqual)
2503-
{
2504-
blockEndInstr = labelHelper->GetNextBranchOrLabel();
2505-
}
2506-
else
2507-
{
2508-
blockEndInstr = branchInstr->GetNextBranchOrLabel();
2509-
}
2510-
2511-
if (blockEndInstr->IsBranchInstr())
2512-
{
2513-
blockEndInstr->AsBranchInstr()->m_isHelperToNonHelperBranch = true;
2514-
}
2515-
2516-
labelFallthrough->m_noHelperAssert = true;
2517-
#endif
2595+
instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JMP, labelBranchFail, this->m_func));
25182596

25192597
return true;
25202598
}

lib/Backend/LowerMDShared.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ class LowererMD
132132
#if FLOATVAR
133133
IR::RegOpnd* CheckFloatAndUntag(IR::RegOpnd * opndSrc, IR::Instr * insertInstr, IR::LabelInstr* labelHelper);
134134
#endif
135-
bool GenerateFastBrString(IR::BranchInstr* instr);
135+
bool GenerateFastBrOrCmString(IR::Instr* instr);
136+
bool GenerateFastStringCheck(IR::Instr* instr, IR::RegOpnd *srcReg1, IR::RegOpnd *srcReg2, bool isEqual, bool isStrict, IR::LabelInstr *labelHelper, IR::LabelInstr *labelBranchSuccess, IR::LabelInstr *labelBranchFail);
136137
bool GenerateFastCmSrEqConst(IR::Instr *instr);
137138
bool GenerateFastCmXxTaggedInt(IR::Instr *instr);
138139
void GenerateFastCmXxI4(IR::Instr *instr);

0 commit comments

Comments
 (0)