#include "config.h"
#include "AirGenerate.h"
#if ENABLE(B3_JIT)
#include "AirAllocateRegistersAndStackByLinearScan.h"
#include "AirAllocateRegistersByGraphColoring.h"
#include "AirAllocateStackByGraphColoring.h"
#include "AirCode.h"
#include "AirEliminateDeadCode.h"
#include "AirFixObviousSpills.h"
#include "AirFixPartialRegisterStalls.h"
#include "AirGenerationContext.h"
#include "AirLogRegisterPressure.h"
#include "AirLowerAfterRegAlloc.h"
#include "AirLowerEntrySwitch.h"
#include "AirLowerMacros.h"
#include "AirLowerStackArgs.h"
#include "AirOpcodeUtils.h"
#include "AirOptimizeBlockOrder.h"
#include "AirReportUsedRegisters.h"
#include "AirSimplifyCFG.h"
#include "AirValidate.h"
#include "B3Common.h"
#include "B3Procedure.h"
#include "B3TimingScope.h"
#include "B3ValueInlines.h"
#include "CCallHelpers.h"
#include "DisallowMacroScratchRegisterUsage.h"
#include "LinkBuffer.h"
#include <wtf/IndexMap.h>
namespace JSC { namespace B3 { namespace Air {
void prepareForGeneration(Code& code)
{
TimingScope timingScope("Air::prepareForGeneration");
if (shouldDumpIR(AirMode) && !shouldDumpIRAtEachPhase(AirMode)) {
dataLog("Initial air:\n");
dataLog(code);
}
code.resetReachability();
if (shouldValidateIR())
validate(code);
simplifyCFG(code);
lowerMacros(code);
eliminateDeadCode(code);
if (code.optLevel() <= 1) {
allocateRegistersAndStackByLinearScan(code);
if (Options::logAirRegisterPressure()) {
dataLog("Register pressure after register allocation:\n");
logRegisterPressure(code);
}
lowerAfterRegAlloc(code);
} else {
allocateRegistersByGraphColoring(code);
if (Options::logAirRegisterPressure()) {
dataLog("Register pressure after register allocation:\n");
logRegisterPressure(code);
}
fixObviousSpills(code);
lowerAfterRegAlloc(code);
allocateStackByGraphColoring(code);
}
lowerStackArgs(code);
simplifyCFG(code);
if (code.optLevel() >= 2 || code.needsUsedRegisters())
reportUsedRegisters(code);
fixPartialRegisterStalls(code);
lowerEntrySwitch(code);
simplifyCFG(code);
optimizeBlockOrder(code);
if (shouldValidateIR())
validate(code);
if (shouldDumpIR(AirMode)) {
dataLog("Air after ", code.lastPhaseName(), ", before generation:\n");
dataLog(code);
}
}
void generate(Code& code, CCallHelpers& jit)
{
TimingScope timingScope("Air::generate");
DisallowMacroScratchRegisterUsage disallowScratch(jit);
GenerationContext context;
context.code = &code;
context.blockLabels.resize(code.size());
for (BasicBlock* block : code) {
if (block)
context.blockLabels[block] = Box<CCallHelpers::Label>::create();
}
IndexMap<BasicBlock*, CCallHelpers::JumpList> blockJumps(code.size());
auto link = [&] (CCallHelpers::Jump jump, BasicBlock* target) {
if (context.blockLabels[target]->isSet()) {
jump.linkTo(*context.blockLabels[target], &jit);
return;
}
blockJumps[target].append(jump);
};
PCToOriginMap& pcToOriginMap = code.proc().pcToOriginMap();
auto addItem = [&] (Inst& inst) {
if (!inst.origin) {
pcToOriginMap.appendItem(jit.labelIgnoringWatchpoints(), Origin());
return;
}
pcToOriginMap.appendItem(jit.labelIgnoringWatchpoints(), inst.origin->origin());
};
Disassembler* disassembler = code.disassembler();
for (BasicBlock* block : code) {
context.currentBlock = block;
context.indexInBlock = UINT_MAX;
blockJumps[block].link(&jit);
CCallHelpers::Label label = jit.label();
*context.blockLabels[block] = label;
if (disassembler)
disassembler->startBlock(block, jit);
if (std::optional<unsigned> entrypointIndex = code.entrypointIndex(block)) {
ASSERT(code.isEntrypoint(block));
if (disassembler)
disassembler->startEntrypoint(jit);
code.prologueGeneratorForEntrypoint(*entrypointIndex)->run(jit, code);
if (disassembler)
disassembler->endEntrypoint(jit);
} else
ASSERT(!code.isEntrypoint(block));
ASSERT(block->size() >= 1);
for (unsigned i = 0; i < block->size() - 1; ++i) {
context.indexInBlock = i;
Inst& inst = block->at(i);
addItem(inst);
auto start = jit.labelIgnoringWatchpoints();
CCallHelpers::Jump jump = inst.generate(jit, context);
ASSERT_UNUSED(jump, !jump.isSet());
auto end = jit.labelIgnoringWatchpoints();
if (disassembler)
disassembler->addInst(&inst, start, end);
}
context.indexInBlock = block->size() - 1;
if (block->last().kind.opcode == Jump
&& block->successorBlock(0) == code.findNextBlock(block))
continue;
addItem(block->last());
if (isReturn(block->last().kind.opcode)) {
auto start = jit.labelIgnoringWatchpoints();
if (code.frameSize()) {
jit.emitRestore(code.calleeSaveRegisterAtOffsetList());
jit.emitFunctionEpilogue();
} else
jit.emitFunctionEpilogueWithEmptyFrame();
jit.ret();
addItem(block->last());
auto end = jit.labelIgnoringWatchpoints();
if (disassembler)
disassembler->addInst(&block->last(), start, end);
continue;
}
auto start = jit.labelIgnoringWatchpoints();
CCallHelpers::Jump jump = block->last().generate(jit, context);
auto end = jit.labelIgnoringWatchpoints();
if (disassembler)
disassembler->addInst(&block->last(), start, end);
if (jump.isSet()) {
switch (block->numSuccessors()) {
case 1:
link(jump, block->successorBlock(0));
break;
case 2:
link(jump, block->successorBlock(0));
if (block->successorBlock(1) != code.findNextBlock(block))
link(jit.jump(), block->successorBlock(1));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
}
addItem(block->last());
}
context.currentBlock = nullptr;
context.indexInBlock = UINT_MAX;
Vector<CCallHelpers::Label> entrypointLabels(code.numEntrypoints());
for (unsigned i = code.numEntrypoints(); i--;)
entrypointLabels[i] = *context.blockLabels[code.entrypoint(i).block()];
code.setEntrypointLabels(WTFMove(entrypointLabels));
pcToOriginMap.appendItem(jit.label(), Origin());
if (disassembler)
disassembler->startLatePath(jit);
for (auto& latePath : context.latePaths)
latePath->run(jit, context);
if (disassembler)
disassembler->endLatePath(jit);
pcToOriginMap.appendItem(jit.labelIgnoringWatchpoints(), Origin());
}
} } }
#endif // ENABLE(B3_JIT)