mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-26 21:50:53 +00:00
445 lines
11 KiB
C++
445 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2015-2020 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "B3Procedure.h"
|
|
|
|
#if ENABLE(B3_JIT)
|
|
|
|
#include "AirCode.h"
|
|
#include "B3BackwardsCFG.h"
|
|
#include "B3BackwardsDominators.h"
|
|
#include "B3BasicBlockUtils.h"
|
|
#include "B3CFG.h"
|
|
#include "B3DataSection.h"
|
|
#include "B3Dominators.h"
|
|
#include "B3NaturalLoops.h"
|
|
#include "B3OpaqueByproducts.h"
|
|
#include "B3ProcedureInlines.h"
|
|
#include "B3StackSlot.h"
|
|
#include "B3ValueInlines.h"
|
|
#include "B3Variable.h"
|
|
|
|
namespace JSC { namespace B3 {
|
|
|
|
Procedure::Procedure()
|
|
: m_cfg(new CFG(*this))
|
|
, m_lastPhaseName("initial")
|
|
, m_byproducts(makeUnique<OpaqueByproducts>())
|
|
, m_code(new Air::Code(*this))
|
|
{
|
|
m_code->setNumEntrypoints(m_numEntrypoints);
|
|
}
|
|
|
|
Procedure::~Procedure()
|
|
{
|
|
}
|
|
|
|
void Procedure::printOrigin(PrintStream& out, Origin origin) const
|
|
{
|
|
if (m_originPrinter)
|
|
m_originPrinter->run(out, origin);
|
|
else
|
|
out.print(origin);
|
|
}
|
|
|
|
BasicBlock* Procedure::addBlock(double frequency)
|
|
{
|
|
std::unique_ptr<BasicBlock> block(new BasicBlock(m_blocks.size(), frequency));
|
|
BasicBlock* result = block.get();
|
|
m_blocks.append(WTFMove(block));
|
|
return result;
|
|
}
|
|
|
|
StackSlot* Procedure::addStackSlot(unsigned byteSize)
|
|
{
|
|
return m_stackSlots.addNew(byteSize);
|
|
}
|
|
|
|
Variable* Procedure::addVariable(Type type)
|
|
{
|
|
return m_variables.addNew(type);
|
|
}
|
|
|
|
Type Procedure::addTuple(Vector<Type>&& types)
|
|
{
|
|
Type result = Type::tupleFromIndex(m_tuples.size());
|
|
m_tuples.append(WTFMove(types));
|
|
ASSERT(result.isTuple());
|
|
return result;
|
|
}
|
|
|
|
bool Procedure::isValidTuple(Type tuple) const
|
|
{
|
|
return tuple.tupleIndex() < m_tuples.size();
|
|
}
|
|
|
|
const Vector<Type>& Procedure::tupleForType(Type tuple) const
|
|
{
|
|
return m_tuples[tuple.tupleIndex()];
|
|
}
|
|
|
|
Value* Procedure::clone(Value* value)
|
|
{
|
|
std::unique_ptr<Value> clone(value->cloneImpl());
|
|
clone->m_index = UINT_MAX;
|
|
clone->owner = nullptr;
|
|
return m_values.add(WTFMove(clone));
|
|
}
|
|
|
|
Value* Procedure::addIntConstant(Origin origin, Type type, int64_t value)
|
|
{
|
|
switch (type.kind()) {
|
|
case Int32:
|
|
return add<Const32Value>(origin, static_cast<int32_t>(value));
|
|
case Int64:
|
|
return add<Const64Value>(origin, value);
|
|
case Double:
|
|
return add<ConstDoubleValue>(origin, static_cast<double>(value));
|
|
case Float:
|
|
return add<ConstFloatValue>(origin, static_cast<float>(value));
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Value* Procedure::addIntConstant(Value* likeValue, int64_t value)
|
|
{
|
|
return addIntConstant(likeValue->origin(), likeValue->type(), value);
|
|
}
|
|
|
|
Value* Procedure::addConstant(Origin origin, Type type, uint64_t bits)
|
|
{
|
|
switch (type.kind()) {
|
|
case Int32:
|
|
return add<Const32Value>(origin, static_cast<int32_t>(bits));
|
|
case Int64:
|
|
return add<Const64Value>(origin, bits);
|
|
case Float:
|
|
return add<ConstFloatValue>(origin, bitwise_cast<float>(static_cast<int32_t>(bits)));
|
|
case Double:
|
|
return add<ConstDoubleValue>(origin, bitwise_cast<double>(bits));
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Value* Procedure::addBottom(Origin origin, Type type)
|
|
{
|
|
if (type.isTuple())
|
|
return add<BottomTupleValue>(origin, type);
|
|
return addIntConstant(origin, type, 0);
|
|
}
|
|
|
|
Value* Procedure::addBottom(Value* value)
|
|
{
|
|
return addBottom(value->origin(), value->type());
|
|
}
|
|
|
|
Value* Procedure::addBoolConstant(Origin origin, TriState triState)
|
|
{
|
|
int32_t value = 0;
|
|
switch (triState) {
|
|
case TriState::False:
|
|
value = 0;
|
|
break;
|
|
case TriState::True:
|
|
value = 1;
|
|
break;
|
|
case TriState::Indeterminate:
|
|
return nullptr;
|
|
}
|
|
|
|
return addIntConstant(origin, Int32, value);
|
|
}
|
|
|
|
void Procedure::resetValueOwners()
|
|
{
|
|
for (BasicBlock* block : *this) {
|
|
for (Value* value : *block)
|
|
value->owner = block;
|
|
}
|
|
}
|
|
|
|
void Procedure::resetReachability()
|
|
{
|
|
recomputePredecessors(m_blocks);
|
|
|
|
// The common case is that this does not find any dead blocks.
|
|
bool foundDead = false;
|
|
for (auto& block : m_blocks) {
|
|
if (isBlockDead(block.get())) {
|
|
foundDead = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!foundDead)
|
|
return;
|
|
|
|
resetValueOwners();
|
|
|
|
for (Value* value : values()) {
|
|
if (UpsilonValue* upsilon = value->as<UpsilonValue>()) {
|
|
if (isBlockDead(upsilon->phi()->owner))
|
|
upsilon->replaceWithNop();
|
|
}
|
|
}
|
|
|
|
for (auto& block : m_blocks) {
|
|
if (isBlockDead(block.get())) {
|
|
for (Value* value : *block)
|
|
deleteValue(value);
|
|
block = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Procedure::invalidateCFG()
|
|
{
|
|
m_dominators = nullptr;
|
|
m_naturalLoops = nullptr;
|
|
m_backwardsCFG = nullptr;
|
|
m_backwardsDominators = nullptr;
|
|
}
|
|
|
|
void Procedure::dump(PrintStream& out) const
|
|
{
|
|
IndexSet<Value*> valuesInBlocks;
|
|
for (BasicBlock* block : *this) {
|
|
out.print(deepDump(*this, block));
|
|
valuesInBlocks.addAll(*block);
|
|
}
|
|
bool didPrint = false;
|
|
for (Value* value : values()) {
|
|
if (valuesInBlocks.contains(value))
|
|
continue;
|
|
|
|
if (!didPrint) {
|
|
dataLog(tierName, "Orphaned values:\n");
|
|
didPrint = true;
|
|
}
|
|
dataLog(tierName, " ", deepDump(*this, value), "\n");
|
|
}
|
|
if (hasQuirks())
|
|
out.print(tierName, "Has Quirks: True\n");
|
|
if (variables().size()) {
|
|
out.print(tierName, "Variables:\n");
|
|
for (Variable* variable : variables())
|
|
out.print(tierName, " ", deepDump(variable), "\n");
|
|
}
|
|
if (stackSlots().size()) {
|
|
out.print(tierName, "Stack slots:\n");
|
|
for (StackSlot* slot : stackSlots())
|
|
out.print(tierName, " ", pointerDump(slot), ": ", deepDump(slot), "\n");
|
|
}
|
|
if (m_byproducts->count())
|
|
out.print(*m_byproducts);
|
|
}
|
|
|
|
Vector<BasicBlock*> Procedure::blocksInPreOrder()
|
|
{
|
|
return B3::blocksInPreOrder(at(0));
|
|
}
|
|
|
|
Vector<BasicBlock*> Procedure::blocksInPostOrder()
|
|
{
|
|
return B3::blocksInPostOrder(at(0));
|
|
}
|
|
|
|
void Procedure::deleteStackSlot(StackSlot* stackSlot)
|
|
{
|
|
m_stackSlots.remove(stackSlot);
|
|
}
|
|
|
|
void Procedure::deleteVariable(Variable* variable)
|
|
{
|
|
m_variables.remove(variable);
|
|
}
|
|
|
|
void Procedure::deleteValue(Value* value)
|
|
{
|
|
m_values.remove(value);
|
|
}
|
|
|
|
void Procedure::deleteOrphans()
|
|
{
|
|
IndexSet<Value*> valuesInBlocks;
|
|
for (BasicBlock* block : *this)
|
|
valuesInBlocks.addAll(*block);
|
|
|
|
// Since this method is not on any hot path, we do it conservatively: first a pass to
|
|
// identify the values to be removed, and then a second pass to remove them. This avoids any
|
|
// risk of the value iteration being broken by removals.
|
|
Vector<Value*, 16> toRemove;
|
|
for (Value* value : values()) {
|
|
if (!valuesInBlocks.contains(value))
|
|
toRemove.append(value);
|
|
else if (UpsilonValue* upsilon = value->as<UpsilonValue>()) {
|
|
if (!valuesInBlocks.contains(upsilon->phi()))
|
|
upsilon->replaceWithNop();
|
|
}
|
|
}
|
|
|
|
for (Value* value : toRemove)
|
|
deleteValue(value);
|
|
}
|
|
|
|
Dominators& Procedure::dominators()
|
|
{
|
|
if (!m_dominators)
|
|
m_dominators = makeUnique<Dominators>(*this);
|
|
return *m_dominators;
|
|
}
|
|
|
|
NaturalLoops& Procedure::naturalLoops()
|
|
{
|
|
if (!m_naturalLoops)
|
|
m_naturalLoops = makeUnique<NaturalLoops>(*this);
|
|
return *m_naturalLoops;
|
|
}
|
|
|
|
BackwardsCFG& Procedure::backwardsCFG()
|
|
{
|
|
if (!m_backwardsCFG)
|
|
m_backwardsCFG = makeUnique<BackwardsCFG>(*this);
|
|
return *m_backwardsCFG;
|
|
}
|
|
|
|
BackwardsDominators& Procedure::backwardsDominators()
|
|
{
|
|
if (!m_backwardsDominators)
|
|
m_backwardsDominators = makeUnique<BackwardsDominators>(*this);
|
|
return *m_backwardsDominators;
|
|
}
|
|
|
|
void Procedure::addFastConstant(const ValueKey& constant)
|
|
{
|
|
RELEASE_ASSERT(constant.isConstant());
|
|
m_fastConstants.add(constant);
|
|
}
|
|
|
|
bool Procedure::isFastConstant(const ValueKey& constant)
|
|
{
|
|
if (!constant)
|
|
return false;
|
|
return m_fastConstants.contains(constant);
|
|
}
|
|
|
|
void* Procedure::addDataSection(size_t size)
|
|
{
|
|
if (!size)
|
|
return nullptr;
|
|
std::unique_ptr<DataSection> dataSection = makeUnique<DataSection>(size);
|
|
void* result = dataSection->data();
|
|
m_byproducts->add(WTFMove(dataSection));
|
|
return result;
|
|
}
|
|
|
|
unsigned Procedure::callArgAreaSizeInBytes() const
|
|
{
|
|
return code().callArgAreaSizeInBytes();
|
|
}
|
|
|
|
void Procedure::requestCallArgAreaSizeInBytes(unsigned size)
|
|
{
|
|
code().requestCallArgAreaSizeInBytes(size);
|
|
}
|
|
|
|
void Procedure::pinRegister(Reg reg)
|
|
{
|
|
code().pinRegister(reg);
|
|
}
|
|
|
|
void Procedure::setOptLevel(unsigned optLevel)
|
|
{
|
|
m_optLevel = optLevel;
|
|
code().setOptLevel(optLevel);
|
|
}
|
|
|
|
unsigned Procedure::frameSize() const
|
|
{
|
|
return code().frameSize();
|
|
}
|
|
|
|
RegisterAtOffsetList Procedure::calleeSaveRegisterAtOffsetList() const
|
|
{
|
|
return code().calleeSaveRegisterAtOffsetList();
|
|
}
|
|
|
|
Value* Procedure::addValueImpl(Value* value)
|
|
{
|
|
return m_values.add(std::unique_ptr<Value>(value));
|
|
}
|
|
|
|
void Procedure::setBlockOrderImpl(Vector<BasicBlock*>& blocks)
|
|
{
|
|
IndexSet<BasicBlock*> blocksSet;
|
|
blocksSet.addAll(blocks);
|
|
|
|
for (BasicBlock* block : *this) {
|
|
if (!blocksSet.contains(block))
|
|
blocks.append(block);
|
|
}
|
|
|
|
// Place blocks into this's block list by first leaking all of the blocks and then readopting
|
|
// them.
|
|
for (auto& entry : m_blocks)
|
|
entry.release();
|
|
|
|
m_blocks.resize(blocks.size());
|
|
for (unsigned i = 0; i < blocks.size(); ++i) {
|
|
BasicBlock* block = blocks[i];
|
|
block->m_index = i;
|
|
m_blocks[i] = std::unique_ptr<BasicBlock>(block);
|
|
}
|
|
}
|
|
|
|
void Procedure::setWasmBoundsCheckGenerator(RefPtr<WasmBoundsCheckGenerator> generator)
|
|
{
|
|
code().setWasmBoundsCheckGenerator(generator);
|
|
}
|
|
|
|
RegisterSet Procedure::mutableGPRs()
|
|
{
|
|
return code().mutableGPRs();
|
|
}
|
|
|
|
RegisterSet Procedure::mutableFPRs()
|
|
{
|
|
return code().mutableFPRs();
|
|
}
|
|
|
|
void Procedure::setNumEntrypoints(unsigned numEntrypoints)
|
|
{
|
|
m_numEntrypoints = numEntrypoints;
|
|
m_code->setNumEntrypoints(numEntrypoints);
|
|
}
|
|
|
|
} } // namespace JSC::B3
|
|
|
|
#endif // ENABLE(B3_JIT)
|