B3MoveConstants.cpp [plain text]
#include "config.h"
#include "B3MoveConstants.h"
#if ENABLE(B3_JIT)
#include "AirArg.h"
#include "B3BasicBlockInlines.h"
#include "B3Dominators.h"
#include "B3InsertionSetInlines.h"
#include "B3MemoryValueInlines.h"
#include "B3PhaseScope.h"
#include "B3ProcedureInlines.h"
#include "B3ValueInlines.h"
#include "B3ValueKeyInlines.h"
#include <wtf/HashMap.h>
#include <wtf/Vector.h>
namespace JSC { namespace B3 {
namespace {
class MoveConstants {
public:
MoveConstants(Procedure& proc)
: m_proc(proc)
, m_insertionSet(proc)
{
}
void run()
{
hoistConstants(
[&] (const ValueKey& key) -> bool {
return key.opcode() == ConstFloat || key.opcode() == ConstDouble;
});
lowerFPConstants();
hoistConstants(
[&] (const ValueKey& key) -> bool {
return key.opcode() == Const32 || key.opcode() == Const64 || key.opcode() == ArgumentReg;
});
}
private:
template<typename Filter>
void hoistConstants(const Filter& filter)
{
Dominators& dominators = m_proc.dominators();
HashMap<ValueKey, Value*> valueForConstant;
IndexMap<BasicBlock*, Vector<Value*>> materializations(m_proc.size());
for (BasicBlock* block : m_proc) {
for (Value* value : *block) {
for (Value*& child : value->children()) {
ValueKey key = child->key();
if (!filter(key))
continue;
auto result = valueForConstant.add(key, child);
if (result.isNewEntry) {
child->owner = block;
continue;
}
child = result.iterator->value;
while (!dominators.dominates(child->owner, block))
child->owner = dominators.idom(child->owner);
}
}
}
for (auto& entry : valueForConstant) {
Value* value = entry.value;
for (BasicBlock* block = value->owner; block; block = dominators.idom(block)) {
if (block->frequency() < value->owner->frequency())
value->owner = block;
}
materializations[value->owner].append(value);
}
for (BasicBlock* block : m_proc) {
for (Value*& value : *block) {
ValueKey key = value->key();
if (!filter(key))
continue;
if (valueForConstant.get(key) == value)
value = m_proc.add<Value>(Nop, value->origin());
else
value->replaceWithNopIgnoringType();
}
}
for (BasicBlock* block : m_proc) {
for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
Value* value = block->at(valueIndex);
auto findBestConstant = [&] (const auto& predicate) -> Value* {
Value* result = nullptr;
dominators.forAllDominatorsOf(
block,
[&] (BasicBlock* dominator) {
for (Value* value : materializations[dominator]) {
if (predicate(value)) {
result = value;
break;
}
}
});
return result;
};
auto materialize = [&] (Value* child) {
ValueKey key = child->key();
if (!filter(key))
return;
ASSERT(valueForConstant.get(key) == child);
if (child->owner != block) {
return;
}
m_insertionSet.insertValue(valueIndex, child);
child->owner = nullptr;
};
if (MemoryValue* memoryValue = value->as<MemoryValue>()) {
Value* pointer = memoryValue->lastChild();
if (pointer->hasIntPtr() && filter(pointer->key())) {
auto desiredOffset = [&] (Value* otherPointer) -> intptr_t {
uintptr_t c = static_cast<uintptr_t>(static_cast<intptr_t>(memoryValue->offset()));
uintptr_t p = pointer->asIntPtr();
uintptr_t q = otherPointer->asIntPtr();
return c + p - q;
};
Value* bestPointer = findBestConstant(
[&] (Value* candidatePointer) -> bool {
if (!candidatePointer->hasIntPtr())
return false;
int64_t offset = desiredOffset(candidatePointer);
return memoryValue->isLegalOffset(offset);
});
if (bestPointer) {
memoryValue->lastChild() = bestPointer;
memoryValue->setOffset(static_cast<int32_t>(desiredOffset(bestPointer)));
}
}
} else {
switch (value->opcode()) {
case Add:
case Sub: {
Value* addend = value->child(1);
if (!addend->hasInt() || !filter(addend->key()))
break;
int64_t addendConst = addend->asInt();
Value* bestAddend = findBestConstant(
[&] (Value* candidateAddend) -> bool {
if (candidateAddend->type() != addend->type())
return false;
if (!candidateAddend->hasInt())
return false;
return candidateAddend == addend
|| candidateAddend->asInt() == -addendConst;
});
if (!bestAddend || bestAddend == addend)
break;
materialize(value->child(0));
materialize(bestAddend);
value->replaceWithIdentity(
m_insertionSet.insert<Value>(
valueIndex, value->opcode() == Add ? Sub : Add, value->origin(),
value->child(0), bestAddend));
break;
}
default:
break;
}
}
for (Value* child : value->children())
materialize(child);
}
for (Value* value : materializations[block]) {
if (!value->owner) {
continue;
}
m_insertionSet.insertValue(block->size() - 1, value);
}
m_insertionSet.execute(block);
}
}
void lowerFPConstants()
{
for (Value* value : m_proc.values()) {
ValueKey key = value->key();
if (goesInTable(key))
m_constTable.add(key, m_constTable.size());
}
m_dataSection = static_cast<int64_t*>(m_proc.addDataSection(m_constTable.size() * sizeof(int64_t)));
for (auto& entry : m_constTable)
m_dataSection[entry.value] = entry.key.value();
IndexSet<Value*> offLimits;
for (BasicBlock* block : m_proc) {
for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
StackmapValue* value = block->at(valueIndex)->as<StackmapValue>();
if (!value)
continue;
for (unsigned childIndex = 0; childIndex < value->numChildren(); ++childIndex) {
if (!value->constrainedChild(childIndex).rep().isAny())
continue;
Value*& child = value->child(childIndex);
ValueKey key = child->key();
if (!goesInTable(key))
continue;
child = m_insertionSet.insertValue(
valueIndex, key.materialize(m_proc, value->origin()));
offLimits.add(child);
}
}
m_insertionSet.execute(block);
}
for (BasicBlock* block : m_proc) {
for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
Value* value = block->at(valueIndex);
ValueKey key = value->key();
if (!goesInTable(key))
continue;
if (offLimits.contains(value))
continue;
auto offset = sizeof(int64_t) * m_constTable.get(key);
if (!isRepresentableAs<Value::OffsetType>(offset))
continue;
Value* tableBase = m_insertionSet.insertIntConstant(
valueIndex, value->origin(), pointerType(),
bitwise_cast<intptr_t>(m_dataSection));
Value* result = m_insertionSet.insert<MemoryValue>(
valueIndex, Load, value->type(), value->origin(), tableBase,
static_cast<Value::OffsetType>(offset));
value->replaceWithIdentity(result);
}
m_insertionSet.execute(block);
}
}
bool goesInTable(const ValueKey& key)
{
return (key.opcode() == ConstDouble && key != doubleZero())
|| (key.opcode() == ConstFloat && key != floatZero());
}
static ValueKey doubleZero()
{
return ValueKey(ConstDouble, Double, 0.0);
}
static ValueKey floatZero()
{
return ValueKey(ConstFloat, Float, 0.0);
}
Procedure& m_proc;
HashMap<ValueKey, unsigned> m_constTable;
int64_t* m_dataSection;
InsertionSet m_insertionSet;
};
}
void moveConstants(Procedure& proc)
{
PhaseScope phaseScope(proc, "moveConstants");
MoveConstants moveConstants(proc);
moveConstants.run();
}
} }
#endif // ENABLE(B3_JIT)