blob: a12d48ff78c8ac98744ea6ada4c22d414b185ffb [file]
//-------------------------------------------------------------------------------------------------------
// 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"
///----------------------------------------------------------------------------
///
/// AgenPeeps::PeepFunc
///
/// Looks for AGEN dependencies between a MOV and another instruction. The heuristic works as follows:
/// - Look for an AGEN dependency. If found, move the first instruction upwards in the BB as far as possible.
/// - For subsequent dependencies (dep. chain), we move the instruction at least 3 slots from the previous instruction.
/// - We assume no dependency between different dep. chains.
/// Example:
/// BB_start
/// ...
/// s2 = MOV [s1 + offset] ; start of dep chain. Move instruction up as far as possible
/// s3 = MOV [s2] ; inside dep chain. Move instruction up at least 3 slots from prev. instruction
/// s4 = MOV [s3 + offset] ; inside dep chain. Move instruction up at least 3 slots from prev. instruction
/// ...
/// s5 = MOV [xxxx] ; new dep chain. Move instruction up as far as possible
/// s6 = MOV [s5 + offset]
///----------------------------------------------------------------------------
void AgenPeeps::PeepFunc()
{
int distance = 0;
IR::Instr *blockStart, *nextRealInstr;
const uint stall_cycles = 3;
if (AutoSystemInfo::Data.IsAtomPlatform())
{
// On Atom, always optimize unless phase is off
if (PHASE_OFF(Js::AtomPhase, func) || PHASE_OFF(Js::AgenPeepsPhase, func))
return;
}
else
{
// On other platforms, don't optimize unless phase is forced
if (!PHASE_FORCE(Js::AtomPhase, func) && !PHASE_FORCE(Js::AgenPeepsPhase, func))
return;
}
blockStart = nullptr;
FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
{
if (!instr->IsRealInstr() && !instr->IsLabelInstr())
continue;
// BB boundary ?
if (instr->EndsBasicBlock() || instr->StartsBasicBlock())
{
distance = 0;
instrNext = blockStart = instr->GetNextRealInstr();
continue;
}
nextRealInstr = instr->GetNextRealInstrOrLabel();
// Check for AGEN dependency with the next instruction in the same BB
if (!nextRealInstr->EndsBasicBlock() && !nextRealInstr->StartsBasicBlock() &&
AgenDependentInstrs(instr, nextRealInstr))
{
Assert(blockStart);
// Move instr up
distance = MoveInstrUp(instr, blockStart, distance) - stall_cycles;
} else
distance = 0;
} NEXT_INSTR_IN_FUNC_EDITING;
}
///----------------------------------------------------------------------------
///
/// AgenPeeps::MoveInstrUp
///
/// Moves an instruction up in the BB up to a bound or until it hits
/// a dependent instruction. If bound <= 0, move as far as possible.
///----------------------------------------------------------------------------
int AgenPeeps::MoveInstrUp(IR::Instr *instrToMove, IR::Instr *blockStart, int bound)
{
AssertMsg(LowererMD::IsAssign(instrToMove), "We only reschedule move instructions for now");
IR::Instr *instr;
int i = 1;
if (instrToMove == blockStart || DependentInstrs(instrToMove, instrToMove->GetPrevRealInstr()))
return 0;
instr = instrToMove->GetPrevRealInstr();
instrToMove->Unlink();
while (!DependentInstrs(instrToMove, instr))
{
if (instr == blockStart || (bound > 0 && i == bound))
{
instr->InsertBefore(instrToMove);
return i;
}
instr = instr->GetPrevRealInstr();
i++;
}
// Insert after dependent instruction
instr->InsertAfter(instrToMove);
return i - 1;
}
///----------------------------------------------------------------------------
///
/// AgenPeeps::AgenDependentInstr
///
/// Determines if two instructions are Agen dependent
///
///----------------------------------------------------------------------------
bool AgenPeeps::AgenDependentInstrs(IR::Instr *instr1, IR::Instr *instr2)
{
// We only deal with assign instructions for now.
if (!LowererMD::IsAssign(instr1) || !DependentInstrs(instr1, instr2))
return false;
if (instr1->GetDst()->IsRegOpnd())
{
IR::IndirOpnd *src1 = (instr2->GetSrc1() && instr2->GetSrc1()->IsIndirOpnd()) ? instr2->GetSrc1()->AsIndirOpnd() : nullptr;
IR::IndirOpnd *src2 = (instr2->GetSrc2() && instr2->GetSrc2()->IsIndirOpnd()) ? instr2->GetSrc2()->AsIndirOpnd() : nullptr;
IR::IndirOpnd *dst = (instr2->GetDst() && instr2->GetDst()->IsIndirOpnd()) ? instr2->GetDst()->AsIndirOpnd() : nullptr;
IR::RegOpnd *regOpnd = instr1->GetDst()->AsRegOpnd();
IR::RegOpnd *base, *index;
if (src1)
{
base = src1->GetBaseOpnd();
index = src1->GetIndexOpnd();
return (base && regOpnd->IsSameRegUntyped(base)) || (index && regOpnd->IsSameRegUntyped(index));
}
if (src2)
{
base = src2->GetBaseOpnd();
index = src2->GetIndexOpnd();
return (base && regOpnd->IsSameRegUntyped(base)) || (index && regOpnd->IsSameRegUntyped(index));
}
if (dst)
{
base = dst->GetBaseOpnd();
index = dst->GetIndexOpnd();
return (base && regOpnd->IsSameRegUntyped(base)) || (index && regOpnd->IsSameRegUntyped(index));
}
}
return false;
}
///----------------------------------------------------------------------------
///
/// AgenPeeps::DependentInstr
///
/// Determines if two instructions are dependent
///
///----------------------------------------------------------------------------
bool AgenPeeps::DependentInstrs(IR::Instr *instr1, IR::Instr *instr2)
{
if (AlwaysDependent(instr1) || AlwaysDependent(instr2))
return true;
// Check for RAW, WAR and WAW dependence
return \
DependentOpnds(instr1->GetSrc1(), instr2->GetDst()) ||
DependentOpnds(instr1->GetSrc2(), instr2->GetDst()) ||
DependentOpnds(instr1->GetDst(), instr2->GetDst()) ||
DependentOpnds(instr2->GetSrc1(), instr1->GetDst()) ||
DependentOpnds(instr2->GetSrc2(), instr1->GetDst()) ||
(instr1->m_opcode == Js::OpCode::XCHG && // XCHG's src2 is also a dst
(DependentOpnds(instr2->GetSrc1(), instr1->GetSrc2()) ||
DependentOpnds(instr2->GetSrc2(), instr1->GetSrc2()))) ||
(instr2->m_opcode == Js::OpCode::XCHG && // XCHG's src2 is also a dst
(DependentOpnds(instr1->GetSrc1(), instr2->GetSrc2()) ||
DependentOpnds(instr1->GetSrc2(), instr2->GetSrc2())));
}
// Being conservative here about instructions that implicitly reads/writes memory/regs without explicit Opnds
bool AgenPeeps::AlwaysDependent(IR::Instr *instr)
{
return LowererMD::IsCall(instr) ||
instr->m_opcode == Js::OpCode::PUSH ||
instr->m_opcode == Js::OpCode::POP ||
instr->m_opcode == Js::OpCode::DIV ||
instr->m_opcode == Js::OpCode::IDIV ||
instr->m_opcode == Js::OpCode::IMUL;
}
///----------------------------------------------------------------------------
///
/// AgenPeeps::DependentOpnd
///
/// Determines if two operands are dependent. This function is commutative.
///
///----------------------------------------------------------------------------
bool AgenPeeps::DependentOpnds(IR::Opnd *opnd1, IR::Opnd *opnd2)
{
#if defined (_M_IX86)
const RegNum baseReg = RegEBP;
#elif defined (_M_X64)
const RegNum baseReg = RegRBP;
#else
AssertMsg(false, "Optimization not supported for ARM");
#endif
if (!opnd1 || !opnd2)
return false;
// Memory dependence
if (IsMemoryOpnd(opnd1) && IsMemoryOpnd(opnd2))
{
IR::SymOpnd *symOpnd1 = opnd1->IsSymOpnd() ? opnd1->AsSymOpnd() : nullptr;
IR::SymOpnd *symOpnd2 = opnd2->IsSymOpnd() ? opnd2->AsSymOpnd() : nullptr;
if (symOpnd1 || symOpnd2)
{
// SymOpnd do not alias with MemRefOpnd/IndirOpnd
if (opnd1->IsMemRefOpnd() || opnd2->IsMemRefOpnd() || opnd1->IsIndirOpnd() || opnd2->IsIndirOpnd())
return false;
// Two symOpnds are dependent if they point to the same stack symbol
if (symOpnd1 && symOpnd2 &&
symOpnd1->m_sym->IsStackSym() && symOpnd2->m_sym->IsStackSym() )
{
return symOpnd1->m_sym->AsStackSym()->m_offset == symOpnd2->m_sym->AsStackSym()->m_offset;
}
}
// all other memory operands are dependent
return true;
}
IR::RegOpnd *regOpnd, *base, *index;
// Register dependences
if (opnd1->IsRegOpnd())
{
regOpnd = opnd1->AsRegOpnd();
// reg-to-reg
if (opnd2->IsRegOpnd() && regOpnd->IsSameRegUntyped(opnd2->AsRegOpnd()))
return true;
// opnd2 = [base + indx + offset] and (opnd1 = base or opnd1 = indx)
if (opnd2->IsIndirOpnd())
{
base = opnd2->AsIndirOpnd()->GetBaseOpnd();
index = opnd2->AsIndirOpnd()->GetIndexOpnd();
if ( (base && regOpnd->IsSameRegUntyped(base)) || (index && regOpnd->IsSameRegUntyped(index)) )
return true;
}
// opnd1 = ebp/rbp and opnd2 = [ebp/rbp + offset]
if (opnd2->IsSymOpnd() && opnd2->AsSymOpnd()->m_sym->IsStackSym() && regOpnd->GetReg() == baseReg)
return true;
}
if (opnd2->IsRegOpnd())
{
regOpnd = opnd2->AsRegOpnd();
// opnd1 = [base + indx + offset] and (opnd2 = base or opnd2 = indx)
if (opnd1->IsIndirOpnd())
{
base = opnd1->AsIndirOpnd()->GetBaseOpnd();
index = opnd1->AsIndirOpnd()->GetIndexOpnd();
if ( (base && regOpnd->IsSameRegUntyped(base)) || (index && regOpnd->IsSameRegUntyped(index)) )
return true;
}
// opnd2 = ebp/rbp and opnd1 = [ebp/rbp + offset]
if (opnd1->IsSymOpnd() && opnd1->AsSymOpnd()->m_sym->IsStackSym() && regOpnd->GetReg() == baseReg)
return true;
}
return false;
}
bool AgenPeeps::IsMemoryOpnd(IR::Opnd *opnd)
{
return opnd->IsSymOpnd() || opnd->IsIndirOpnd() || opnd->IsMemRefOpnd();
}