Support loop invariant hoist for mir

Issues: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I7F1N4

Signed-off-by: sunzhe23 <sunzhe23@huawei.com>
This commit is contained in:
sunzhe23 2023-06-14 09:09:17 +08:00
parent 0db98de96a
commit 333bdd63ba
8 changed files with 358 additions and 43 deletions

View File

@ -18,6 +18,7 @@
#include <cstddef>
#include <cstdint>
#include "ecmascript/mem/chunk.h"
namespace panda::ecmascript::kungfu {
class BitSet {
@ -29,22 +30,34 @@ public:
static constexpr uint32_t BIT_PER_WORD_LOG2 = 6;
static constexpr uint32_t BIT_PER_WORD_MASK = BIT_PER_WORD - 1;
explicit BitSet(size_t bitSize)
explicit BitSet(Chunk* chunk, size_t bitSize)
{
wordCount_ = SizeOf(bitSize);
if (UseWords()) {
data_.words_ = new uint64_t[wordCount_];
data_.words_ = chunk->NewArray<uint64_t>(wordCount_);
Reset();
}
}
~BitSet()
{
if (UseWords()) {
delete[] data_.words_;
// no need delete chunk memory
data_.words_ = nullptr;
}
}
void Reset()
{
if (!UseWords()) {
data_.inlineWord_ = 0;
} else {
for (size_t i = 0; i < wordCount_; i++) {
data_.words_[i] = 0;
}
}
}
bool TestBit(size_t offset) const
{
if (!UseWords()) {
@ -127,7 +140,7 @@ private:
uint64_t Mask(size_t index) const
{
return 1 << index;
return uint64_t{1} << index;
}
size_t IndexInWord(size_t offset) const

View File

@ -41,6 +41,7 @@ public:
{
head_ = other->head_;
size_ = other->size_;
frameState_ = other->frameState_;
}
private:
struct Node {

View File

@ -32,19 +32,6 @@ FrameStateBuilder::FrameStateBuilder(BytecodeCircuitBuilder *builder,
FrameStateBuilder::~FrameStateBuilder()
{
for (auto state : bcEndStateInfos_) {
if (state != nullptr) {
delete state;
}
}
for (auto state : bbBeginStateInfos_) {
if (state != nullptr) {
delete state;
}
}
if (liveOutResult_ != nullptr) {
delete liveOutResult_;
}
liveOutResult_ = nullptr;
bcEndStateInfos_.clear();
bbBeginStateInfos_.clear();
@ -109,7 +96,8 @@ void FrameStateBuilder::BindStateSplit(GateRef gate, GateRef frameState)
FrameStateInfo *FrameStateBuilder::CreateEmptyStateInfo()
{
auto frameInfo = new FrameStateInfo(numVregs_);
auto chunk = circuit_->chunk();
auto frameInfo = chunk->New<FrameStateInfo>(chunk, numVregs_);
for (size_t i = 0; i < numVregs_; i++) {
frameInfo->SetValuesAt(i, Circuit::NullGate());
}
@ -460,9 +448,6 @@ bool FrameStateBuilder::ShouldInsertFrameStateBefore(BytecodeRegion& bb,
if (gateAcc_.GetOpCode(bb.dependCurrent) == OpCode::GET_EXCEPTION) {
return true;
}
if (gateAcc_.GetOpCode(bb.stateCurrent) == OpCode::IF_SUCCESS) {
return true;
}
} else {
if (gate == Circuit::NullGate() || gateAcc_.GetStateCount(gate) != 1) {
return false;

View File

@ -29,7 +29,8 @@ struct BytecodeRegion;
class FrameStateInfo {
public:
explicit FrameStateInfo(size_t numVregs) : values_(numVregs), liveout_(numVregs) {}
explicit FrameStateInfo(Chunk* chunk, size_t numVregs)
: values_(numVregs, chunk), liveout_(chunk, numVregs) {}
void SetValuesAt(size_t index, GateRef gate)
{
@ -65,7 +66,7 @@ public:
}
private:
// [numVRegs_] [extra args] [numArgs_] [accumulator]
std::vector<GateRef> values_ {};
ChunkVector<GateRef> values_;
BitSet liveout_;
};

View File

@ -14,6 +14,7 @@
*/
#include "ecmascript/compiler/graph_linearizer.h"
#include "ecmascript/compiler/base/bit_set.h"
#include "ecmascript/compiler/scheduler.h"
namespace panda::ecmascript::kungfu {
@ -306,6 +307,249 @@ private:
ChunkVector<ChunkDeque<size_t>> semiDomTree_;
};
struct LoopInfo {
GateRegion* loopHead {nullptr};
BitSet* loopBodys {nullptr};
ChunkVector<GateRegion*>* loopExits {nullptr};
LoopInfo* outer_ {nullptr};
};
class LoopInfoBuilder {
public:
explicit LoopInfoBuilder(GraphLinearizer *linearizer, Chunk* chunk)
: linearizer_(linearizer), pendingList_(chunk),
loops_(chunk), loopbacks_(chunk), chunk_(chunk),
dfsStack_(chunk), acc_(linearizer->circuit_) {}
void Run()
{
ComputeLoopNumber();
ComputeLoopInfo();
ComputeLoopExit();
ComputeLoopHeader();
if (linearizer_->IsLogEnabled()) {
for (size_t i = 0; i < numLoops_; i++) {
auto& loopInfo = loops_[i];
PrintLoop(loopInfo);
}
}
}
void PrintLoop(LoopInfo& loopInfo)
{
auto size = linearizer_->regionList_.size();
LOG_COMPILER(INFO) << "--------------------------------- LoopInfo Start ---------------------------------";
LOG_COMPILER(INFO) << "Head: " << acc_.GetId(loopInfo.loopHead->state_);
LOG_COMPILER(INFO) << "loopNumber: " << loopInfo.loopHead->loopNumber_;
LOG_COMPILER(INFO) << "Body: [";
for (size_t i = 0; i < size; i++) {
if (loopInfo.loopBodys->TestBit(i)) {
auto current = linearizer_->regionList_.at(i)->state_;
LOG_COMPILER(INFO) << acc_.GetId(current) << ", ";
}
}
LOG_COMPILER(INFO) << "]";
LOG_COMPILER(INFO) << "Exit: [";
if (loopInfo.loopExits != nullptr) {
for (auto region : *loopInfo.loopExits) {
auto current = region->state_;
LOG_COMPILER(INFO) << acc_.GetId(current) << ", ";
}
}
LOG_COMPILER(INFO) << "]";
LOG_COMPILER(INFO) << "--------------------------------- LoopInfo End ---------------------------------";
}
void ComputeLoopInfo()
{
auto size = linearizer_->regionList_.size();
loops_.resize(numLoops_, LoopInfo());
for (auto curState : loopbacks_) {
GateRegion* curRegion = curState.region;
GateRegion* loopHead = curRegion->succs_[curState.index];
auto loopNumber = loopHead->GetLoopNumber();
auto& loopInfo = loops_[loopNumber];
if (loopInfo.loopHead == nullptr) {
loopInfo.loopHead = loopHead;
loopInfo.loopBodys = chunk_->New<BitSet>(chunk_, size);
}
if (curRegion != loopHead) {
loopInfo.loopBodys->SetBit(curRegion->GetId());
pendingList_.emplace_back(curRegion);
}
PropagateLoopBody(loopInfo);
}
}
void PropagateLoopBody(LoopInfo& loopInfo)
{
while (!pendingList_.empty()) {
auto curRegion = pendingList_.back();
pendingList_.pop_back();
for (auto pred : curRegion->preds_) {
if (pred != loopInfo.loopHead) {
if (!loopInfo.loopBodys->TestBit(pred->GetId())) {
loopInfo.loopBodys->SetBit(pred->GetId());
pendingList_.emplace_back(pred);
}
}
}
}
}
void ComputeLoopNumber()
{
auto size = linearizer_->regionList_.size();
dfsStack_.resize(size, DFSState(nullptr, 0));
linearizer_->circuit_->AdvanceTime();
auto entry = linearizer_->regionList_.front();
auto currentDepth = Push(entry, 0);
while (currentDepth > 0) {
auto& curState = dfsStack_[currentDepth - 1]; // -1: for current
auto curRegion = curState.region;
auto index = curState.index;
if (index == curRegion->succs_.size()) {
currentDepth--;
curRegion->SetFinished(acc_);
} else {
GateRegion* succ = curRegion->succs_[index];
curState.index = ++index;
if (succ->IsFinished(acc_)) {
continue;
}
if (succ->IsUnvisited(acc_)) {
currentDepth = Push(succ, currentDepth);
} else {
ASSERT(succ->IsVisited(acc_));
loopbacks_.emplace_back(DFSState(curRegion, index - 1)); // -1: for prev
if (!succ->HasLoopNumber()) {
succ->SetLoopNumber(numLoops_++);
}
}
}
}
}
void ComputeLoopExit()
{
linearizer_->circuit_->AdvanceTime();
auto entry = linearizer_->regionList_.front();
LoopInfo *loopInfo = nullptr;
auto currentDepth = Push(entry, 0);
while (currentDepth > 0) {
auto &curState = dfsStack_[currentDepth - 1]; // -1: for current
auto curRegion = curState.region;
auto index = curState.index;
GateRegion* succ = nullptr;
if (index >= curRegion->succs_.size()) {
if (curRegion->HasLoopNumber()) {
if (curRegion->IsVisited(acc_)) {
ASSERT(loopInfo != nullptr && loopInfo->loopHead == curRegion);
loopInfo = loopInfo->outer_;
}
}
curRegion->SetFinished(acc_);
currentDepth--;
} else {
succ = curRegion->succs_[index];
curState.index = ++index;
if (!succ->IsUnvisited(acc_)) {
continue;
}
if (loopInfo != nullptr) {
if (!loopInfo->loopBodys->TestBit(succ->GetId())) {
AddLoopExit(succ, loopInfo);
} else {
succ->loopHead_ = loopInfo->loopHead;
}
}
currentDepth = Push(succ, currentDepth);
loopInfo = EnterInnerLoop(succ, loopInfo);
}
}
}
void AddLoopExit(GateRegion* succ, LoopInfo *loopInfo)
{
if (loopInfo->loopExits == nullptr) {
loopInfo->loopExits = chunk_->New<ChunkVector<GateRegion*>>(chunk_);
}
loopInfo->loopExits->emplace_back(succ);
}
LoopInfo *EnterInnerLoop(GateRegion* succ, LoopInfo *loopInfo)
{
// enter inner loop
if (succ->HasLoopNumber()) {
auto& innerLoop = loops_[succ->GetLoopNumber()];
innerLoop.outer_ = loopInfo;
loopInfo = &innerLoop;
}
return loopInfo;
}
void ComputeLoopHeader()
{
auto size = linearizer_->regionList_.size();
for (size_t i = 0; i < numLoops_; i++) {
auto& loopInfo = loops_[i];
for (size_t j = 0; j < size; j++) {
if (loopInfo.loopBodys->TestBit(j)) {
auto current = linearizer_->regionList_.at(j);
if (!CheckRegionDomLoopExist(current, loopInfo)) {
current->loopHead_ = nullptr;
}
}
}
}
}
bool CheckRegionDomLoopExist(GateRegion* region, LoopInfo& loopInfo)
{
if (loopInfo.loopExits == nullptr) {
return true;
}
for (auto exitRegion : *loopInfo.loopExits) {
if (linearizer_->GetCommonDominator(region, exitRegion) != region) {
return false;
}
}
return true;
}
size_t Push(GateRegion *region, size_t depth)
{
if (region->IsUnvisited(acc_)) {
dfsStack_[depth].region = region;
dfsStack_[depth].index = 0;
region->SetVisited(acc_);
return depth + 1;
}
return depth;
}
private:
struct DFSState {
DFSState(GateRegion *region, size_t index)
: region(region), index(index) {}
GateRegion *region;
size_t index;
};
GraphLinearizer* linearizer_ {nullptr};
ChunkDeque<GateRegion*> pendingList_;
ChunkVector<LoopInfo> loops_;
ChunkVector<DFSState> loopbacks_;
Chunk* chunk_ {nullptr};
ChunkVector<DFSState> dfsStack_;
GateAccessor acc_;
size_t numLoops_{0};
};
class GateScheduler {
public:
explicit GateScheduler(GraphLinearizer *linearizer)
@ -435,12 +679,28 @@ public:
auto region = GetCommonDominatorOfAllUses(curGate);
if (scheduleUpperBound_) {
ASSERT(curInfo.upperBound != nullptr);
ASSERT(GetCommonDominator(region, curInfo.upperBound) == curInfo.upperBound);
region = curInfo.upperBound;
ASSERT(linearizer_->GetCommonDominator(region, curInfo.upperBound) == curInfo.upperBound);
auto uppermost = curInfo.upperBound->depth_;
auto upperRegion = GetUpperBoundRegion(region);
while (upperRegion != nullptr && upperRegion->depth_ >= uppermost) {
region = upperRegion;
upperRegion = GetUpperBoundRegion(region);
}
}
ScheduleGate(curGate, region);
}
GateRegion* GetUpperBoundRegion(GateRegion* region)
{
if (region->IsLoopHead()) {
return region->iDominator_;
}
if (region->loopHead_ != nullptr) {
return region->loopHead_->iDominator_;
}
return nullptr;
}
void ScheduleGate(GateRef gate, GateRegion* region)
{
auto ins = acc_.Ins(gate);
@ -484,27 +744,15 @@ public:
region = useRegion;
} else {
ASSERT(useRegion != nullptr);
region = GetCommonDominator(region, useRegion);
region = linearizer_->GetCommonDominator(region, useRegion);
}
}
return region;
}
GateRegion* GetCommonDominator(GateRegion* left, GateRegion* right) const
{
while (left != right) {
if (left->depth_ < right->depth_) {
right = right->iDominator_;
} else {
left = left->iDominator_;
}
}
return left;
}
bool IsInSameDominatorChain(GateRegion* left, GateRegion* right) const
{
auto dom = GetCommonDominator(left, right);
auto dom = linearizer_->GetCommonDominator(left, right);
return left == dom || right == dom;
}
@ -547,6 +795,11 @@ void GraphLinearizer::LinearizeGraph()
builder.Run();
ImmediateDominatorsGenerator generator(this, chunk_, regionList_.size());
generator.Run();
if (!IsSchedueLIR() && loopNumber_ > 0) {
scheduleUpperBound_ = true;
LoopInfoBuilder loopInfoBuilder(this, chunk_);
loopInfoBuilder.Run();
}
GateScheduler scheduler(this);
scheduler.Prepare();
scheduler.ScheduleUpperBound();
@ -595,6 +848,18 @@ GateRegion* GraphLinearizer::FindPredRegion(GateRef input)
return predRegion;
}
GateRegion* GraphLinearizer::GetCommonDominator(GateRegion* left, GateRegion* right) const
{
while (left != right) {
if (left->depth_ < right->depth_) {
right = right->iDominator_;
} else {
left = left->iDominator_;
}
}
return left;
}
void GraphLinearizer::PrintGraph(const char* title)
{
LOG_COMPILER(INFO) << "======================== " << title << " ========================";
@ -602,9 +867,10 @@ void GraphLinearizer::PrintGraph(const char* title)
auto bb = regionList_[i];
auto front = bb->gateList_.front();
auto opcode = acc_.GetOpCode(front);
auto loopHeadId = bb->loopHead_ != nullptr ? bb->loopHead_->id_ : 0;
LOG_COMPILER(INFO) << "B" << bb->id_ << ": " << "depth: [" << bb->depth_ << "] "
<< opcode << "(" << acc_.GetId(front) << ") "
<< "IDom B" << bb->iDominator_->id_;
<< "IDom B" << bb->iDominator_->id_ << " loop Header: " << loopHeadId;
std::string log("\tPreds: ");
for (size_t k = 0; k < bb->preds_.size(); ++k) {
log += std::to_string(bb->preds_[k]->id_) + ", ";

View File

@ -45,11 +45,26 @@ public:
acc.SetMark(state_, MarkCode::VISITED);
}
void SetFinished(GateAccessor& acc)
{
acc.SetMark(state_, MarkCode::FINISHED);
}
bool IsUnvisited(GateAccessor& acc) const
{
return acc.GetMark(state_) == MarkCode::NO_MARK;
}
bool IsVisited(GateAccessor& acc) const
{
return acc.GetMark(state_) == MarkCode::VISITED;
}
bool IsFinished(GateAccessor& acc) const
{
return acc.GetMark(state_) == MarkCode::FINISHED;
}
bool IsLoopHead() const
{
return stateKind_ == StateKind::LOOP_HEAD;
@ -65,6 +80,21 @@ public:
return id_;
}
void SetLoopNumber(size_t loopNumber)
{
loopNumber_ = static_cast<int32_t>(loopNumber);
}
size_t GetLoopNumber() const
{
return static_cast<size_t>(loopNumber_);
}
bool HasLoopNumber() const
{
return loopNumber_ >= 0;
}
private:
enum StateKind {
BRANCH,
@ -76,15 +106,18 @@ private:
size_t id_ {0};
int32_t depth_ {INVALID_DEPTH};
GateRegion* iDominator_ {nullptr};
GateRegion* loopHead_ {nullptr};
ChunkVector<GateRef> gateList_;
ChunkVector<GateRegion*> preds_;
ChunkVector<GateRegion*> succs_;
ChunkVector<GateRegion*> dominatedRegions_;
GateRef state_ {Circuit::NullGate()};
StateKind stateKind_ {StateKind::OTHER};
int32_t loopNumber_ {INVALID_DEPTH};
friend class CFGBuilder;
friend class GateScheduler;
friend class ImmediateDominatorsGenerator;
friend class LoopInfoBuilder;
friend class GraphLinearizer;
friend class StateDependBuilder;
};
@ -144,6 +177,7 @@ private:
void LinearizeRegions(ControlFlowGraph &result);
void CreateGateRegion(GateRef gate);
GateRegion* FindPredRegion(GateRef input);
GateRegion* GetCommonDominator(GateRegion* left, GateRegion* right) const;
GateInfo& GetGateInfo(GateRef gate)
{
@ -231,6 +265,7 @@ private:
regionRootList_.clear();
scheduleUpperBound_ = false;
model_ = ScheduleModel::LIR;
loopNumber_ = 0;
}
void EnableScheduleUpperBound()
@ -251,7 +286,7 @@ private:
void PrintGraph(const char* title);
bool enableLog_ {false};
bool scheduleUpperBound_ { false};
bool scheduleUpperBound_ {false};
ScheduleModel model_ {ScheduleModel::LIR};
std::string methodName_;
Chunk* chunk_ {nullptr};
@ -266,6 +301,7 @@ private:
friend class CFGBuilder;
friend class GateScheduler;
friend class ImmediateDominatorsGenerator;
friend class LoopInfoBuilder;
friend class StateSplitLinearizer;
};
}; // namespace panda::ecmascript::kungfu

View File

@ -453,7 +453,7 @@ void LCRLowering::LowerGetGlobalEnv(GateRef gate)
{
Environment env(gate, circuit_, &builder_);
GateRef glueGlobalEnvOffset = builder_.IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(false));
GateRef glueGlobalEnv = builder_.Load(VariableType::NATIVE_POINTER(), glue_, glueGlobalEnvOffset);
GateRef glueGlobalEnv = builder_.Load(VariableType::JS_POINTER(), glue_, glueGlobalEnvOffset);
acc_.ReplaceGate(gate, Circuit::NullGate(), builder_.GetDepend(), glueGlobalEnv);
}

View File

@ -18,4 +18,17 @@ var someArray = [1, 5, 7];
for (var tmp of someArray) {
var tmpNumber = tmp;
print(tmpNumber);
}
}
class A {
x: number;
}
function foo(arr: A[]) {
for (var tmp of arr) {
tmp.x = 1;
}
}
let arr = [new A()]
foo(arr);