//------------------------------------------------------------------------------------------------------- // 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 * SimpleLayout::MoveHelperBlock(IR::Instr * lastOpHelperLabel, uint32 lastOpHelperStatementIndex, Func* lastOpHelperFunc, IR::LabelInstr * nextLabel, IR::Instr * instrAfter) { // Add pragma instructions around the moved code to track source mapping Func* pragmatInstrFunc = lastOpHelperFunc ? lastOpHelperFunc : this->func; if (instrAfter->IsPragmaInstr() && instrAfter->m_opcode == Js::OpCode::StatementBoundary) { IR::PragmaInstr* pragmaInstr = instrAfter->AsPragmaInstr(); pragmaInstr->m_statementIndex = lastOpHelperStatementIndex; pragmaInstr->m_func = pragmatInstrFunc; } else { IR::PragmaInstr* pragmaInstr = IR::PragmaInstr::New(Js::OpCode::StatementBoundary, lastOpHelperStatementIndex, pragmatInstrFunc); instrAfter->InsertAfter(pragmaInstr); instrAfter = pragmaInstr; } // Move all the instruction between lastOpHelperLabel to lastOpHelperInstr // to the end of the function IR::Instr * lastOpHelperInstr = nextLabel->GetPrevRealInstrOrLabel(); IR::Instr::MoveRangeAfter(lastOpHelperLabel, lastOpHelperInstr, instrAfter); instrAfter = lastOpHelperInstr; // Add the jmp back if the lastOpHelperInstr has fall through if (instrAfter->HasFallThrough()) { IR::BranchInstr * branchInstr = IR::BranchInstr::New( LowererMD::MDUncondBranchOpcode, nextLabel, this->func); instrAfter->InsertAfter(branchInstr); instrAfter = branchInstr; } // Add pragma terminating this source mapping range IR::PragmaInstr* pragmaInstr = IR::PragmaInstr::New(Js::OpCode::StatementBoundary, Js::Constants::NoStatementIndex, this->func); instrAfter->InsertAfter(pragmaInstr); instrAfter = pragmaInstr; return instrAfter; } void SimpleLayout::Layout() { if (PHASE_OFF(Js::LayoutPhase, this->func) || CONFIG_ISENABLED(Js::DebugFlag)) { return; } // Do simple layout of helper block. Push them to after FunctionExit. IR::Instr * lastInstr = func->m_tailInstr; IR::LabelInstr * lastOpHelperLabel = NULL; uint32 lastOpHelperStatementIndex = Js::Constants::NoStatementIndex; Func* lastOpHelperFunc = nullptr; FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, func->m_headInstr, func->m_tailInstr->m_prev) { if (instr->IsPragmaInstr() && instr->m_opcode == Js::OpCode::StatementBoundary) { currentStatement = instr->AsPragmaInstr(); } else if (instr->IsLabelInstr()) { IR::LabelInstr * labelInstr = instr->AsLabelInstr(); if (labelInstr->isOpHelper) { if (lastOpHelperLabel == NULL) { lastOpHelperLabel = labelInstr; lastOpHelperStatementIndex = currentStatement ? currentStatement->m_statementIndex : Js::Constants::NoStatementIndex; lastOpHelperFunc = currentStatement ? currentStatement->m_func : nullptr; } } else if (lastOpHelperLabel != NULL) { IR::Instr * prevInstr = lastOpHelperLabel->GetPrevRealInstrOrLabel(); if (prevInstr->IsBranchInstr()) { // If the previous instruction is to jump around this helper block // Then we move the helper block to the end of the function to // avoid the jmp in the fast path. // jxx $label <== prevInstr // $helper: <== lastOpHelperLabel // ... // ... <== lastOpHelperInstr // $label: <== labelInstr IR::BranchInstr * prevBranchInstr = prevInstr->AsBranchInstr(); if (prevBranchInstr->GetTarget() == labelInstr) { lastInstr = this->MoveHelperBlock(lastOpHelperLabel, lastOpHelperStatementIndex, lastOpHelperFunc, labelInstr, lastInstr); if (prevBranchInstr->IsUnconditional()) { // Remove the branch to next after the helper block is moved. prevBranchInstr->Remove(); } else { // Reverse the condition LowererMD::InvertBranch(prevBranchInstr); prevBranchInstr->SetTarget(lastOpHelperLabel); } } else if (prevBranchInstr->IsUnconditional()) { IR::Instr * prevPrevInstr = prevInstr->GetPrevRealInstrOrLabel(); if (prevPrevInstr->IsBranchInstr() && prevPrevInstr->AsBranchInstr()->IsConditional() && prevPrevInstr->AsBranchInstr()->GetTarget() == labelInstr) { // jcc $label <== prevPrevInstr // jmp $blah <== prevInstr // $helper: <== lastOpHelperLabel // ... // ... <== lastOpHelperInstr // $label: <== labelInstr // Transform to // jncc $blah <== prevPrevInstr // $label: <== labelInstr // $helper: <== lastOpHelperLabel // ... // ... <== lastOpHelperInstr // jmp $label: <== labelInstr lastInstr = this->MoveHelperBlock(lastOpHelperLabel, lastOpHelperStatementIndex, lastOpHelperFunc, labelInstr, lastInstr); LowererMD::InvertBranch(prevPrevInstr->AsBranchInstr()); prevPrevInstr->AsBranchInstr()->SetTarget(prevBranchInstr->GetTarget()); prevBranchInstr->Remove(); } else { IR::Instr *lastOpHelperInstr = labelInstr->GetPrevRealInstr(); if (lastOpHelperInstr->IsBranchInstr()) { IR::BranchInstr *lastOpHelperBranchInstr = lastOpHelperInstr->AsBranchInstr(); // jmp $target <== prevInstr //this is unconditional jump // $helper: <== lastOpHelperLabel // ... // jmp $labeln <== lastOpHelperInstr //Conditional/Unconditional jump // $label: <== labelInstr lastInstr = this->MoveHelperBlock(lastOpHelperLabel, lastOpHelperStatementIndex, lastOpHelperFunc, labelInstr, lastInstr); //Compensation code if its not unconditional jump if (!lastOpHelperBranchInstr->IsUnconditional()) { IR::BranchInstr *branchInstr = IR::BranchInstr::New(LowererMD::MDUncondBranchOpcode, labelInstr, this->func); lastOpHelperBranchInstr->InsertAfter(branchInstr); } } } } } lastOpHelperLabel = NULL; } } } NEXT_INSTR_EDITING_IN_RANGE; func->m_tailInstr = lastInstr; }