@@ -2281,42 +2281,181 @@ LowererMD::GenerateFastDivByPow2(IR::Instr *instr)
22812281}
22822282
22832283bool
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}
0 commit comments