| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "Backend.h"
- IR::Instr *Lowerer::PreLowerPeepInstr(IR::Instr *instr, IR::Instr **pInstrPrev)
- {
- if (PHASE_OFF(Js::PreLowererPeepsPhase, this->m_func))
- {
- return instr;
- }
- switch (instr->m_opcode)
- {
- #if defined(_M_IX86) || defined(_M_X64)
- // this sort of addressing mode magic only applies to x86 platforms
- case Js::OpCode::Add_I4:
- instr = this->PeepShiftAdd(instr);
- *pInstrPrev = instr->m_prev;
- break;
- #endif
- case Js::OpCode::Shl_I4:
- instr = this->PeepShl(instr);
- *pInstrPrev = instr->m_prev;
- break;
- case Js::OpCode::BrTrue_I4:
- case Js::OpCode::BrFalse_I4:
- instr = this->PeepBrBool(instr);
- *pInstrPrev = instr->m_prev;
- break;
- }
- return instr;
- }
- IR::Instr *
- Lowerer::TryShiftAdd(IR::Instr *instrAdd, IR::Opnd * opndFold, IR::Opnd * opndAdd)
- {
- Assert(instrAdd->m_opcode == Js::OpCode::Add_I4);
- if (!opndFold->GetIsDead())
- {
- return instrAdd;
- }
- if (!(opndAdd->IsRegOpnd() || (opndAdd->IsInt32() && opndAdd->IsIntConstOpnd())))
- {
- return instrAdd;
- }
- if (!opndFold->IsRegOpnd() || !opndFold->AsRegOpnd()->m_sym->IsSingleDef())
- {
- return instrAdd;
- }
- IR::Instr * instrDef = opndFold->AsRegOpnd()->m_sym->GetInstrDef();
- if ((instrDef->m_opcode != Js::OpCode::Shl_I4 && instrDef->m_opcode != Js::OpCode::Mul_I4) || !instrDef->GetSrc1()->IsRegOpnd() || !instrDef->GetSrc2()->IsIntConstOpnd())
- {
- return instrAdd;
- }
- if (instrDef->HasBailOutInfo())
- {
- return instrAdd;
- }
- byte scale = 0;
- IntConstType constVal = instrDef->GetSrc2()->AsIntConstOpnd()->GetValue();
- if (instrDef->m_opcode == Js::OpCode::Shl_I4)
- {
- if (constVal < 0 || constVal > 3)
- {
- return instrAdd;
- }
- scale = (byte)constVal;
- }
- else
- {
- Assert(instrDef->m_opcode == Js::OpCode::Mul_I4);
- switch (constVal)
- {
- case 1:
- scale = 0;
- break;
- case 2:
- scale = 1;
- break;
- case 4:
- scale = 2;
- break;
- case 8:
- scale = 3;
- break;
- default:
- return instrAdd;
- }
- }
- StackSym * defSrc1Sym = instrDef->GetSrc1()->AsRegOpnd()->m_sym;
- StackSym * foldSym = opndFold->AsRegOpnd()->m_sym;
- FOREACH_INSTR_IN_RANGE(instrIter, instrDef->m_next, instrAdd->m_prev)
- {
- // if any branch between def-use, don't do peeps on it because branch target might depend on the def
- if (instrIter->IsBranchInstr())
- {
- return instrAdd;
- }
- if (instrIter->HasBailOutInfo())
- {
- return instrAdd;
- }
- if (instrIter->FindRegDef(defSrc1Sym))
- {
- return instrAdd;
- }
- if (instrIter->HasSymUse(foldSym))
- {
- return instrAdd;
- }
- } NEXT_INSTR_IN_RANGE;
- #if DBG_DUMP
- if (PHASE_TRACE(Js::PreLowererPeepsPhase, instrAdd->m_func))
- {
- Output::Print(_u("PeepShiftAdd : %s (%d) : folding:\n"), instrAdd->m_func->GetJITFunctionBody()->GetDisplayName(), instrAdd->m_func->GetFunctionNumber());
- instrDef->Dump();
- instrAdd->Dump();
- }
- #endif
- IR::IndirOpnd * leaOpnd = nullptr;
- if (opndAdd->IsRegOpnd())
- {
- leaOpnd = IR::IndirOpnd::New(opndAdd->AsRegOpnd(), instrDef->UnlinkSrc1()->AsRegOpnd(), scale, opndFold->GetType(), instrAdd->m_func);
- }
- else
- {
- Assert(opndAdd->IsInt32() && opndAdd->IsIntConstOpnd());
- leaOpnd = IR::IndirOpnd::New(instrDef->UnlinkSrc1()->AsRegOpnd(), opndAdd->AsIntConstOpnd()->AsInt32(), scale, opndFold->GetType(), instrAdd->m_func);
- }
- IR::Instr * leaInstr = InsertLea(instrAdd->UnlinkDst()->AsRegOpnd(), leaOpnd, instrAdd);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::PreLowererPeepsPhase, instrAdd->m_func))
- {
- Output::Print(_u("into:\n"), instrAdd->m_func->GetJITFunctionBody()->GetDisplayName(), instrAdd->m_func->GetFunctionNumber());
- leaInstr->Dump();
- }
- #endif
- instrAdd->Remove();
- instrDef->Remove();
- return leaInstr;
- }
- IR::Instr *
- Lowerer::PeepShiftAdd(IR::Instr *instrAdd)
- {
- Assert(instrAdd->m_opcode == Js::OpCode::Add_I4);
- // Peep:
- // t1 = SHL X, 0|1|2|3 / t1 = MUL X, 1|2|4|8
- // t2 = ADD t1, Y
- //
- // Into:
- // t2 = LEA [X * scale + Y]
- if (instrAdd->HasBailOutInfo())
- {
- return instrAdd;
- }
- if (!instrAdd->GetDst()->IsRegOpnd())
- {
- return instrAdd;
- }
- IR::Opnd * src2 = instrAdd->GetSrc2();
- IR::Opnd * src1 = instrAdd->GetSrc1();
- // we can't remove t1 in case both srcs are uses of t1
- if (src1->IsEqual(src2))
- {
- return instrAdd;
- }
- IR::Instr * resultInstr = TryShiftAdd(instrAdd, src1, src2);
- if (resultInstr->m_opcode == Js::OpCode::Add_I4)
- {
- resultInstr = TryShiftAdd(instrAdd, src2, src1);
- }
- return resultInstr;
- }
- IR::Instr *Lowerer::PeepShl(IR::Instr *instrShl)
- {
- IR::Opnd *src1;
- IR::Opnd *src2;
- IR::Instr *instrDef;
- src1 = instrShl->GetSrc1();
- src2 = instrShl->GetSrc2();
- // Peep:
- // t1 = SHR X, cst
- // t2 = SHL t1, cst
- //
- // Into:
- // t2 = AND X, mask
- if (!src1->IsRegOpnd() || !src2->IsIntConstOpnd())
- {
- return instrShl;
- }
- if (!src1->AsRegOpnd()->m_sym->IsSingleDef())
- {
- return instrShl;
- }
- if (instrShl->HasBailOutInfo())
- {
- return instrShl;
- }
- instrDef = src1->AsRegOpnd()->m_sym->GetInstrDef();
- if (instrDef->m_opcode != Js::OpCode::Shr_I4 || !instrDef->GetSrc2()->IsIntConstOpnd()
- || instrDef->GetSrc2()->AsIntConstOpnd()->GetValue() != src2->AsIntConstOpnd()->GetValue()
- || !instrDef->GetSrc1()->IsRegOpnd())
- {
- return instrShl;
- }
- if (!src1->GetIsDead())
- {
- return instrShl;
- }
- if (instrDef->HasBailOutInfo())
- {
- return instrShl;
- }
- FOREACH_INSTR_IN_RANGE(instrIter, instrDef->m_next, instrShl->m_prev)
- {
- if (instrIter->HasBailOutInfo())
- {
- return instrShl;
- }
- if (instrIter->FindRegDef(instrDef->GetSrc1()->AsRegOpnd()->m_sym))
- {
- return instrShl;
- }
- if (instrIter->HasSymUse(src1->AsRegOpnd()->m_sym))
- {
- return instrShl;
- }
- // if any branch between def-use, don't do peeps on it because branch target might depend on the def
- if (instrIter->IsBranchInstr())
- {
- return instrShl;
- }
- } NEXT_INSTR_IN_RANGE;
- instrShl->FreeSrc1();
- instrShl->SetSrc1(instrDef->UnlinkSrc1());
- instrDef->Remove();
- IntConstType oldValue = src2->AsIntConstOpnd()->GetValue();
- // Left shift operator (<<) on arm32 is implemented by LSL which doesn't discard bits beyond lowerest 5-bit.
- // Need to discard such bits to conform to << in JavaScript. This is not a problem for x86 and x64 because
- // behavior of SHL is consistent with JavaScript but keep the below line for clarity.
- oldValue %= sizeof(int32) * 8;
- oldValue = ~((1 << oldValue) - 1);
- src2->AsIntConstOpnd()->SetValue(oldValue);
- instrShl->m_opcode = Js::OpCode::And_I4;
- return instrShl;
- }
- IR::Instr *Lowerer::PeepBrBool(IR::Instr *instrBr)
- {
- IR::Opnd *src1;
- IR::Instr *instrBinOp, *instrCm1, *instrCm2;
- // Peep:
- // t1 = CmCC_I4 a, b
- // t2 = CmCC_i4 c, d
- // t3 = AND/OR t1, t2
- // BrTrue/False t3, $L_true
- //
- // Into:
- // BrCC a, b, $L_true/false
- // BrCC c, d, $L_true
- //$L_false:
- src1 = instrBr->GetSrc1();
- if (!src1->IsRegOpnd())
- {
- return instrBr;
- }
- Assert(!instrBr->HasBailOutInfo());
- instrBinOp = instrBr->GetPrevRealInstrOrLabel();
- if (instrBinOp->m_opcode != Js::OpCode::And_I4 && instrBinOp->m_opcode != Js::OpCode::Or_I4)
- {
- return instrBr;
- }
- if (!instrBinOp->GetDst()->IsEqual(src1))
- {
- return instrBr;
- }
- IR::RegOpnd *src1Reg = src1->AsRegOpnd();
- if (!src1Reg->m_sym->IsSingleDef() || !src1Reg->GetIsDead())
- {
- return instrBr;
- }
- Assert(!instrBinOp->HasBailOutInfo());
- instrCm2 = instrBinOp->GetPrevRealInstrOrLabel();
- if (!instrCm2->IsCmCC_I4())
- {
- return instrBr;
- }
- IR::RegOpnd *cm2DstReg = instrCm2->GetDst()->AsRegOpnd();
- if (!cm2DstReg->m_sym->IsSingleDef())
- {
- return instrBr;
- }
- if (cm2DstReg->IsEqual(instrBinOp->GetSrc1()))
- {
- if (!instrBinOp->GetSrc1()->AsRegOpnd()->GetIsDead())
- {
- return instrBr;
- }
- }
- else if (cm2DstReg->IsEqual(instrBinOp->GetSrc2()))
- {
- if (!instrBinOp->GetSrc2()->AsRegOpnd()->GetIsDead())
- {
- return instrBr;
- }
- }
- else
- {
- return instrBr;
- }
- Assert(!instrCm2->HasBailOutInfo());
- instrCm1 = instrCm2->GetPrevRealInstrOrLabel();
- if (!instrCm1->IsCmCC_I4())
- {
- return instrBr;
- }
- Assert(!instrCm1->GetDst()->IsEqual(instrCm2->GetDst()));
- IR::RegOpnd *cm1DstReg = instrCm1->GetDst()->AsRegOpnd();
- if (!cm1DstReg->m_sym->IsSingleDef())
- {
- return instrBr;
- }
- if (cm1DstReg->IsEqual(instrCm2->GetSrc1()) || cm1DstReg->IsEqual(instrCm2->GetSrc2()))
- {
- return instrBr;
- }
- if (cm1DstReg->IsEqual(instrBinOp->GetSrc1()))
- {
- if (!instrBinOp->GetSrc1()->AsRegOpnd()->GetIsDead())
- {
- return instrBr;
- }
- }
- else if (cm1DstReg->IsEqual(instrBinOp->GetSrc2()))
- {
- if (!instrBinOp->GetSrc2()->AsRegOpnd()->GetIsDead())
- {
- return instrBr;
- }
- }
- else
- {
- return instrBr;
- }
- Assert(!instrCm1->HasBailOutInfo());
- IR::LabelInstr *falseLabel = instrBr->AsBranchInstr()->GetTarget();
- IR::LabelInstr *trueLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
- instrBr->InsertAfter(trueLabel);
- IR::BranchInstr *instrBr1;
- IR::BranchInstr *instrBr2;
- if (instrBinOp->m_opcode == Js::OpCode::And_I4)
- {
- instrBr1 = instrCm1->ChangeCmCCToBranchInstr(instrBr->m_opcode == Js::OpCode::BrFalse_I4 ? falseLabel : trueLabel);
- instrBr1->Invert();
- instrBr2 = instrCm2->ChangeCmCCToBranchInstr(falseLabel);
- if (instrBr->m_opcode == Js::OpCode::BrFalse_I4)
- {
- instrBr2->Invert();
- }
- }
- else
- {
- Assert(instrBinOp->m_opcode == Js::OpCode::Or_I4);
- instrBr1 = instrCm1->ChangeCmCCToBranchInstr(instrBr->m_opcode == Js::OpCode::BrTrue_I4 ? falseLabel : trueLabel);
- instrBr2 = instrCm2->ChangeCmCCToBranchInstr(falseLabel);
- if (instrBr->m_opcode == Js::OpCode::BrFalse_I4)
- {
- instrBr2->Invert();
- }
- }
- instrBinOp->Remove();
- instrBr->Remove();
- return instrBr2;
- }
|