Better handle clobbered registers in O0 register allocation
https://bugs.webkit.org/show_bug.cgi?id=240205
<rdar://87220688>
Reviewed by Yusuke Suzuki.
This patch makes Air's O0 register allocator better handle clobbered
registers. We now model both early and late clobber directly, and use
this to perform a basic interference analysis when allocating a register
to a Tmp. An early clobber interferes with any Use in an instruction, and
any early Defs. A late clobber interferes with any Defs in an instruction,
and any late Uses. What this enables is an early Use can be allocated
to a register that is only late clobbered. And a result can be allocated
to a register that is only early clobbered.
Prior to this, the algorithm had a bug where a Use may be allocated to
a register that is early clobbered.
* b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp:
(JSC::B3::Air::GenerateAndAllocateRegisters::buildLiveRanges):
(JSC::B3::Air::GenerateAndAllocateRegisters::alloc):
(JSC::B3::Air::GenerateAndAllocateRegisters::freeDeadTmpsIfNeeded):
(JSC::B3::Air::GenerateAndAllocateRegisters::assignTmp):
(JSC::B3::Air::GenerateAndAllocateRegisters::prepareForGeneration):
(JSC::B3::Air::GenerateAndAllocateRegisters::generate):
* b3/air/AirAllocateRegistersAndStackAndGenerateCode.h:
* b3/air/testair.cpp:
* jit/RegisterSet.h:
Canonical link: https://commits.webkit.org/250477@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294087 268f45cc-cd09-0410-ab3c-d52691b4dbfc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (Arg::isAnyUse(role) && m_earlyClobber.get(reg))
returntrue;
if (Arg::isAnyDef(role) && m_lateClobber.get(reg))
returntrue;
if (Arg::activeAt(role, Arg::Phase::Early) && m_earlyClobber.get(reg))
returntrue;
if (Arg::activeAt(role, Arg::Phase::Late) && m_lateClobber.get(reg))
returntrue;
returnfalse;
};
if (Reg reg = m_map[tmp].reg) {
if (!interferesWithClobber(reg)) {
ASSERT(!m_namedDefdRegs.contains(reg));
tmp = Tmp(reg);
markRegisterAsUsed(reg);
ASSERT(!m_availableRegs[bank].get(reg));
returntrue;
}
// This is a rare case when we've already allocated a Tmp in some way, but another
// Role of the Tmp imposes some restriction on the register value. E.g, if
// we have a program like:
// Patch Use:tmp1, LateUse:tmp1, lateClobber:x0
// The first use of tmp1 can be allocated to x0, but the second cannot.
spill(tmp, reg);
if (!m_availableRegs[bank].numberOfSetRegisters())
freeDeadTmpsIfNeeded();
}
if (m_availableRegs[bank].numberOfSetRegisters()) {
// We first take an available register.
for (Reg reg : m_registers[bank]) {
if (m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg))
if (interferesWithClobber(reg) || m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg))
continue;
if (!m_availableRegs[bank].contains(reg))
continue;
m_namedUsedRegs.set(reg); // At this point, it doesn't matter if we add it to the m_namedUsedRegs or m_namedDefdRegs. We just need to mark that we can't use it again.
alloc(tmp, reg, isDef);
markRegisterAsUsed(reg);
alloc(tmp, reg, role);
tmp = Tmp(reg);
returntrue;
}
RELEASE_ASSERT_NOT_REACHED();
}
// Nothing was available, let's make some room.
for (Reg reg : m_registers[bank]) {
if (m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg))
if (interferesWithClobber(reg) || m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg))