mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-07 08:03:29 +00:00
5efb392145
Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I7OYPF Signed-off-by: ginxu <xujie101@huawei.com> Change-Id: I307a3119abd64e41447635da9150dbd40b41677f
541 lines
16 KiB
C++
541 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ecmascript/compiler/early_elimination.h"
|
|
|
|
namespace panda::ecmascript::kungfu {
|
|
|
|
void EarlyElimination::Run()
|
|
{
|
|
dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size
|
|
renames_.resize(circuit_->GetMaxGateId() + 1, Circuit::NullGate()); // 1: +1 for size
|
|
GateRef entry = acc_.GetDependRoot();
|
|
VisitDependEntry(entry);
|
|
VisitGraph();
|
|
|
|
if (IsLogEnabled()) {
|
|
LOG_COMPILER(INFO) << "";
|
|
LOG_COMPILER(INFO) << "\033[34m"
|
|
<< "===================="
|
|
<< " After early elimination "
|
|
<< "[" << GetMethodName() << "]"
|
|
<< "===================="
|
|
<< "\033[0m";
|
|
circuit_->PrintAllGatesWithBytecode();
|
|
LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
|
|
}
|
|
}
|
|
|
|
DependInfoNode* EarlyElimination::GetLoopDependInfo(GateRef depend)
|
|
{
|
|
auto depIn = acc_.GetDep(depend);
|
|
auto dependChain = GetDependChain(depIn);
|
|
if (dependChain == nullptr) {
|
|
return nullptr;
|
|
}
|
|
auto newChain = new (chunk_) DependInfoNode(chunk_);
|
|
newChain->CopyFrom(dependChain);
|
|
ChunkSet<GateRef> visited(chunk_);
|
|
ChunkQueue<GateRef> workList(chunk_);
|
|
workList.push(depend);
|
|
visited.insert(acc_.GetDep(depend));
|
|
while (!workList.empty()) {
|
|
auto curDep = workList.front();
|
|
workList.pop();
|
|
if (visited.count(curDep)) {
|
|
continue;
|
|
}
|
|
if (!acc_.IsNotWrite(curDep)) {
|
|
newChain = UpdateWrite(curDep, newChain);
|
|
}
|
|
visited.insert(curDep);
|
|
auto depCount = acc_.GetDependCount(curDep);
|
|
for (size_t i = 0; i < depCount; ++i) {
|
|
workList.push(acc_.GetDep(curDep, i));
|
|
}
|
|
}
|
|
return newChain;
|
|
}
|
|
|
|
GateRef EarlyElimination::VisitDependEntry(GateRef gate)
|
|
{
|
|
auto empty = new (chunk_) DependInfoNode(chunk_);
|
|
return UpdateDependChain(gate, empty);
|
|
}
|
|
|
|
GateRef EarlyElimination::VisitGate(GateRef gate)
|
|
{
|
|
auto opcode = acc_.GetOpCode(gate);
|
|
switch (opcode) {
|
|
case OpCode::LOAD_PROPERTY:
|
|
case OpCode::LOAD_ELEMENT:
|
|
case OpCode::LOAD_ARRAY_LENGTH:
|
|
case OpCode::LOAD_TYPED_ARRAY_LENGTH:
|
|
case OpCode::TYPED_ARRAY_CHECK:
|
|
case OpCode::OBJECT_TYPE_CHECK:
|
|
case OpCode::OBJECT_TYPE_COMPARE:
|
|
case OpCode::STABLE_ARRAY_CHECK:
|
|
case OpCode::INDEX_CHECK:
|
|
case OpCode::TYPED_CALL_CHECK:
|
|
case OpCode::LOAD_CONST_OFFSET:
|
|
case OpCode::TYPED_BINARY_OP:
|
|
case OpCode::TYPED_UNARY_OP:
|
|
case OpCode::JSINLINETARGET_TYPE_CHECK:
|
|
case OpCode::INLINE_ACCESSOR_CHECK:
|
|
return TryEliminateGate(gate);
|
|
case OpCode::STATE_SPLIT:
|
|
return TryEliminateFrameState(gate);
|
|
case OpCode::DEPEND_SELECTOR:
|
|
return TryEliminateDependSelector(gate);
|
|
default:
|
|
if (acc_.GetDependCount(gate) == 1) { // 1: depend in is 1
|
|
return TryEliminateOther(gate);
|
|
}
|
|
}
|
|
return Circuit::NullGate();
|
|
}
|
|
|
|
GateRef EarlyElimination::TryEliminateOther(GateRef gate)
|
|
{
|
|
ASSERT(acc_.GetDependCount(gate) >= 1);
|
|
auto depIn = acc_.GetDep(gate);
|
|
auto dependChain = GetDependChain(depIn);
|
|
if (dependChain == nullptr) {
|
|
return Circuit::NullGate();
|
|
}
|
|
|
|
if (!acc_.IsNotWrite(gate)) {
|
|
dependChain = UpdateWrite(gate, dependChain);
|
|
}
|
|
|
|
return UpdateDependChain(gate, dependChain);
|
|
}
|
|
|
|
GateRef EarlyElimination::TryEliminateGate(GateRef gate)
|
|
{
|
|
ASSERT(acc_.GetDependCount(gate) == 1);
|
|
auto depIn = acc_.GetDep(gate);
|
|
auto dependChain = GetDependChain(depIn);
|
|
// dependChain is null
|
|
if (dependChain == nullptr) {
|
|
return Circuit::NullGate();
|
|
}
|
|
|
|
if (!acc_.IsNotWrite(gate)) {
|
|
dependChain = UpdateWrite(gate, dependChain);
|
|
return UpdateDependChain(gate, dependChain);
|
|
}
|
|
|
|
auto numIns = acc_.GetNumValueIn(gate);
|
|
for (size_t i = 0; i < numIns; ++i) {
|
|
auto origin = acc_.GetValueIn(gate, i);
|
|
auto checkd = dependChain->LookupCheckedNode(this, origin);
|
|
if (origin != checkd) {
|
|
acc_.ReplaceValueIn(gate, checkd, i);
|
|
}
|
|
}
|
|
|
|
// lookup gate, replace
|
|
auto preGate = dependChain->LookupNode(this, gate);
|
|
if (preGate != Circuit::NullGate()) {
|
|
return preGate;
|
|
}
|
|
// update gate, for others elimination
|
|
dependChain = dependChain->UpdateNode(gate);
|
|
return UpdateDependChain(gate, dependChain);
|
|
}
|
|
|
|
GateRef EarlyElimination::TryEliminateFrameState(GateRef gate)
|
|
{
|
|
ASSERT(acc_.GetOpCode(gate) == OpCode::STATE_SPLIT);
|
|
auto depIn = acc_.GetDep(gate);
|
|
auto dependChain = GetDependChain(depIn);
|
|
// dependChain is null
|
|
if (dependChain == nullptr) {
|
|
return Circuit::NullGate();
|
|
}
|
|
// lookup gate, replace
|
|
auto preFrame = dependChain->LookupFrameState();
|
|
auto curFrame = acc_.GetFrameState(gate);
|
|
if ((preFrame != Circuit::NullGate()) && (preFrame != curFrame) &&
|
|
acc_.GetFrameState(preFrame) == acc_.GetFrameState(curFrame)) {
|
|
acc_.UpdateAllUses(curFrame, preFrame);
|
|
auto frameValues = acc_.GetValueIn(curFrame, 1); // 1: frameValues
|
|
acc_.DeleteGate(frameValues);
|
|
acc_.DeleteGate(curFrame);
|
|
return depIn;
|
|
} else {
|
|
dependChain = dependChain->UpdateFrameState(curFrame);
|
|
}
|
|
// update gate, for others elimination
|
|
|
|
return UpdateDependChain(gate, dependChain);
|
|
}
|
|
|
|
GateRef EarlyElimination::TryEliminateDependSelector(GateRef gate)
|
|
{
|
|
auto state = acc_.GetState(gate);
|
|
if (acc_.IsLoopHead(state)) {
|
|
auto dependChain = GetLoopDependInfo(gate);
|
|
if (dependChain == nullptr) {
|
|
return Circuit::NullGate();
|
|
}
|
|
return UpdateDependChain(gate, dependChain);
|
|
}
|
|
|
|
auto dependCount = acc_.GetDependCount(gate);
|
|
for (size_t i = 0; i < dependCount; ++i) {
|
|
auto depend = acc_.GetDep(gate, i);
|
|
auto dependChain = GetDependChain(depend);
|
|
if (dependChain == nullptr) {
|
|
return Circuit::NullGate();
|
|
}
|
|
}
|
|
|
|
// all depend done.
|
|
auto depend = acc_.GetDep(gate);
|
|
auto dependChain = GetDependChain(depend);
|
|
DependInfoNode* copy = new (chunk_) DependInfoNode(chunk_);
|
|
copy->CopyFrom(dependChain);
|
|
for (size_t i = 1; i < dependCount; ++i) { // 1: second in
|
|
auto dependIn = acc_.GetDep(gate, i);
|
|
auto tempChain = GetDependChain(dependIn);
|
|
copy->Merge(this, tempChain);
|
|
}
|
|
return UpdateDependChain(gate, copy);
|
|
}
|
|
|
|
GateRef EarlyElimination::UpdateDependChain(GateRef gate, DependInfoNode* dependChain)
|
|
{
|
|
ASSERT(dependChain != nullptr);
|
|
auto oldDependChain = GetDependChain(gate);
|
|
if (dependChain->Equals(oldDependChain)) {
|
|
return Circuit::NullGate();
|
|
}
|
|
dependChains_[acc_.GetId(gate)] = dependChain;
|
|
return gate;
|
|
}
|
|
|
|
DependInfoNode* EarlyElimination::UpdateWrite(GateRef gate, DependInfoNode* dependInfo)
|
|
{
|
|
auto op = acc_.GetOpCode(gate);
|
|
switch (op) {
|
|
case OpCode::STORE_PROPERTY:
|
|
case OpCode::STORE_PROPERTY_NO_BARRIER:
|
|
case OpCode::STORE_CONST_OFFSET:
|
|
case OpCode::STORE_ELEMENT:
|
|
case OpCode::STORE_MEMORY:
|
|
return dependInfo->UpdateStoreProperty(this, gate);
|
|
default:
|
|
return new (chunk_) DependInfoNode(chunk_);
|
|
}
|
|
}
|
|
|
|
bool EarlyElimination::MayAccessOneMemory(GateRef lhs, GateRef rhs)
|
|
{
|
|
auto rop = acc_.GetOpCode(rhs);
|
|
auto lop = acc_.GetOpCode(lhs);
|
|
switch (rop) {
|
|
case OpCode::STORE_MEMORY:
|
|
ASSERT(acc_.GetMemoryType(rhs) == MemoryType::ELEMENT_TYPE);
|
|
return acc_.GetOpCode(lhs) == OpCode::LOAD_ELEMENT;
|
|
case OpCode::STORE_ELEMENT: {
|
|
if(lop == OpCode::LOAD_ELEMENT) {
|
|
auto lopIsTypedArray = static_cast<uint8_t>(acc_.GetTypedLoadOp(lhs)) > 0;
|
|
auto ropIsTypedArray = static_cast<uint8_t>(acc_.GetTypedStoreOp(rhs)) > 0;
|
|
return lopIsTypedArray == ropIsTypedArray;
|
|
}
|
|
return false;
|
|
}
|
|
case OpCode::STORE_PROPERTY:
|
|
case OpCode::STORE_PROPERTY_NO_BARRIER: {
|
|
if (lop == OpCode::LOAD_PROPERTY) {
|
|
auto loff = acc_.GetValueIn(lhs, 1);
|
|
auto roff = acc_.GetValueIn(rhs, 1);
|
|
ASSERT(acc_.GetOpCode(loff) == OpCode::CONSTANT);
|
|
ASSERT(acc_.GetOpCode(roff) == OpCode::CONSTANT);
|
|
return loff == roff;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::STORE_CONST_OFFSET: {
|
|
if (lop == OpCode::LOAD_CONST_OFFSET) {
|
|
auto loff = acc_.GetOffset(lhs);
|
|
auto roff = acc_.GetOffset(rhs);
|
|
return loff == roff;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool EarlyElimination::CompareOrder(GateRef lhs, GateRef rhs)
|
|
{
|
|
return GetGateOrder(lhs) < GetGateOrder(rhs);
|
|
}
|
|
|
|
bool EarlyElimination::CheckReplacement(GateRef lhs, GateRef rhs)
|
|
{
|
|
if (!acc_.MetaDataEqu(lhs, rhs)) {
|
|
if (acc_.GetOpCode(lhs) != acc_.GetOpCode(rhs)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
size_t valueCount = acc_.GetNumValueIn(lhs);
|
|
for (size_t i = 0; i < valueCount; i++) {
|
|
if (Rename(acc_.GetValueIn(lhs, i)) != Rename(acc_.GetValueIn(rhs, i))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto opcode = acc_.GetOpCode(lhs);
|
|
switch (opcode) {
|
|
case OpCode::LOAD_ELEMENT: {
|
|
if (acc_.GetTypedLoadOp(lhs) != acc_.GetTypedLoadOp(rhs)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::TYPED_BINARY_OP: {
|
|
auto lhsOp = acc_.GetTypedBinaryOp(lhs);
|
|
auto rhsOp = acc_.GetTypedBinaryOp(rhs);
|
|
if (lhsOp != rhsOp) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::TYPED_UNARY_OP: {
|
|
auto lhsOp = acc_.GetTypedUnAccessor(lhs).GetTypedUnOp();
|
|
auto rhsOp = acc_.GetTypedUnAccessor(rhs).GetTypedUnOp();
|
|
if (lhsOp != rhsOp) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::TYPED_ARRAY_CHECK:
|
|
case OpCode::OBJECT_TYPE_CHECK:
|
|
case OpCode::OBJECT_TYPE_COMPARE:
|
|
case OpCode::INDEX_CHECK: {
|
|
if (acc_.GetParamGateType(lhs) != acc_.GetParamGateType(rhs)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::LOAD_CONST_OFFSET: {
|
|
if (acc_.GetOffset(lhs) != acc_.GetOffset(rhs)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case OpCode::JSINLINETARGET_TYPE_CHECK: {
|
|
if (acc_.GetFuncGT(lhs) != acc_.GetFuncGT(rhs)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EarlyElimination::CheckRenameReplacement(GateRef lhs, GateRef rhs)
|
|
{
|
|
auto opcode = acc_.GetOpCode(lhs);
|
|
switch (opcode) {
|
|
case OpCode::INDEX_CHECK: {
|
|
auto index = acc_.GetValueIn(lhs, 1);
|
|
if (Rename(index) == Rename(rhs)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GateRef EarlyElimination::Rename(GateRef gate)
|
|
{
|
|
ChunkStack<GateRef> gateStack(chunk_);
|
|
while (true) {
|
|
auto op = acc_.GetOpCode(gate);
|
|
bool renamed = false;
|
|
switch (op) {
|
|
case OpCode::INDEX_CHECK: {
|
|
GateRef ans = renames_[acc_.GetId(gate)];
|
|
if (ans == Circuit::NullGate()) {
|
|
renamed = true;
|
|
gateStack.push(gate);
|
|
gate = acc_.GetValueIn(gate, 1);
|
|
} else {
|
|
gate = ans;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (!renamed) {
|
|
break;
|
|
}
|
|
}
|
|
while (!gateStack.empty()) {
|
|
auto topGate = gateStack.top();
|
|
gateStack.pop();
|
|
renames_[acc_.GetId(topGate)] = gate;
|
|
}
|
|
return gate;
|
|
}
|
|
|
|
void DependInfoNode::Merge(EarlyElimination* elimination, DependInfoNode* that)
|
|
{
|
|
auto siz = this->size_; // size of lhs-chain
|
|
auto lhs = this->head_;
|
|
auto rhs = that->head_;
|
|
ChunkStack<GateRef> gateStack(chunk_);
|
|
while (lhs != rhs) {
|
|
if (lhs == nullptr || rhs == nullptr) {
|
|
siz = 0;
|
|
lhs = nullptr;
|
|
break;
|
|
} else if (lhs->gate == rhs->gate) {
|
|
gateStack.push(lhs->gate);
|
|
siz--;
|
|
lhs = lhs->next;
|
|
rhs = rhs->next;
|
|
} else if (elimination->CompareOrder(lhs->gate, rhs->gate)) {
|
|
rhs = rhs->next;
|
|
} else {
|
|
siz--;
|
|
lhs = lhs->next;
|
|
}
|
|
}
|
|
// lhs : common suffix of lhs-chain and rhs-chain
|
|
this->head_ = lhs;
|
|
this->size_ = siz;
|
|
while (!gateStack.empty()) {
|
|
Node* node = chunk_->New<Node>(gateStack.top(), head_);
|
|
gateStack.pop();
|
|
this->size_++;
|
|
this->head_ = node;
|
|
}
|
|
if (this->frameState_ != that->frameState_) {
|
|
this->frameState_ = Circuit::NullGate();
|
|
}
|
|
}
|
|
|
|
bool DependInfoNode::Equals(DependInfoNode* that)
|
|
{
|
|
if (that == nullptr) {
|
|
return false;
|
|
}
|
|
if (size_ != that->size_ || frameState_ != that->frameState_) {
|
|
return false;
|
|
}
|
|
auto lhs = this->head_;
|
|
auto rhs = that->head_;
|
|
while (lhs != rhs) {
|
|
if (lhs->gate != rhs->gate) {
|
|
return false;
|
|
}
|
|
lhs = lhs->next;
|
|
rhs = rhs->next;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GateRef DependInfoNode::LookupFrameState() const
|
|
{
|
|
return frameState_;
|
|
}
|
|
|
|
GateRef DependInfoNode::LookupCheckedNode(EarlyElimination* elimination, GateRef gate)
|
|
{
|
|
for (Node* node = head_; node != nullptr; node = node->next) {
|
|
if (elimination->CheckRenameReplacement(node->gate, gate)) {
|
|
return node->gate;
|
|
}
|
|
}
|
|
return gate;
|
|
}
|
|
|
|
void DependInfoNode::GetGates(std::vector<GateRef>& gates) const
|
|
{
|
|
ChunkStack<GateRef> st(chunk_);
|
|
for (Node* node = head_; node != nullptr; node = node->next) {
|
|
st.push(node->gate);
|
|
}
|
|
while (!st.empty()) {
|
|
gates.emplace_back(st.top());
|
|
st.pop();
|
|
}
|
|
}
|
|
|
|
GateRef DependInfoNode::LookupNode(EarlyElimination* elimination, GateRef gate)
|
|
{
|
|
for (Node* node = head_; node != nullptr; node = node->next) {
|
|
if (elimination->CheckReplacement(node->gate, gate)) {
|
|
return node->gate;
|
|
}
|
|
}
|
|
return Circuit::NullGate();
|
|
}
|
|
|
|
DependInfoNode* DependInfoNode::UpdateNode(GateRef gate)
|
|
{
|
|
// assign node->next to head
|
|
Node* node = chunk_->New<Node>(gate, head_);
|
|
DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
|
|
// assign head to node
|
|
that->head_ = node;
|
|
that->size_ = size_ + 1;
|
|
that->frameState_ = frameState_;
|
|
return that;
|
|
}
|
|
|
|
DependInfoNode* DependInfoNode::UpdateFrameState(GateRef framestate)
|
|
{
|
|
// assign node->next to head
|
|
DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
|
|
// assign head to node
|
|
that->head_ = head_;
|
|
that->size_ = size_;
|
|
that->frameState_ = framestate;
|
|
return that;
|
|
}
|
|
|
|
DependInfoNode* DependInfoNode::UpdateStoreProperty(EarlyElimination* elimination, GateRef gate)
|
|
{
|
|
DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
|
|
ChunkStack<GateRef> gateStack(chunk_);
|
|
for (Node* node = head_; node != nullptr; node = node->next) {
|
|
if (!elimination->MayAccessOneMemory(node->gate, gate)) {
|
|
gateStack.push(node->gate);
|
|
}
|
|
}
|
|
while (!gateStack.empty()) {
|
|
that = that->UpdateNode(gateStack.top());
|
|
gateStack.pop();
|
|
}
|
|
return that;
|
|
}
|
|
} // namespace panda::ecmascript::kungfu
|