diff --git a/ecmascript/compiler/BUILD.gn b/ecmascript/compiler/BUILD.gn index 7b996a74d8e28d06209572f3342397f6a857fb00..628998db13c6b76d967533aab5fe71f54471638c 100644 --- a/ecmascript/compiler/BUILD.gn +++ b/ecmascript/compiler/BUILD.gn @@ -158,6 +158,7 @@ libark_jsoptimizer_sources = [ "profiler_stub_builder.cpp", "range_analysis.cpp", "range_guard.cpp", + "read_barrier_elimination.cpp", "rt_call_signature.cpp", "scheduler.cpp", "share_gate_meta_data.cpp", diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 0afb37c48292c8f1b7b97a3a9b5fb81966287387..9667caa1c023a1c22b27a95aca1bc538f822e58c 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -937,6 +937,11 @@ public: MemoryAttribute mAttr = MemoryAttribute::Default()); GateRef LoadWithoutBarrier(VariableType type, GateRef addr, MemoryAttribute mAttr = MemoryAttribute::Default()); + GateRef LoadBarrierCondition(GateRef glue); + + GateRef LoadWithCondition(VariableType type, GateRef glue, GateRef addr, GateRef condition, + MemoryAttribute mAttr = MemoryAttribute::Default()); + void Store(VariableType type, GateRef glue, GateRef base, GateRef offset, GateRef value, MemoryAttribute mAttr = MemoryAttribute::Default()); void StoreHClass(VariableType type, GateRef glue, GateRef base, GateRef offset, GateRef value, GateRef compValue, diff --git a/ecmascript/compiler/gate_accessor.cpp b/ecmascript/compiler/gate_accessor.cpp index 900c6243065508f720118d13628f6589d2b37781..9494f2609f0c6fc8c6787a37f425dfd165acd583 100644 --- a/ecmascript/compiler/gate_accessor.cpp +++ b/ecmascript/compiler/gate_accessor.cpp @@ -139,6 +139,7 @@ MemoryAttribute GateAccessor::GetMemoryAttribute(GateRef gate) const switch (op) { case OpCode::LOAD_WITHOUT_BARRIER: case OpCode::LOAD: + case OpCode::LOAD_WITH_CONDITION: case OpCode::STORE_WITHOUT_BARRIER: case OpCode::STORE: { auto accessor = LoadStoreAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); diff --git a/ecmascript/compiler/lcr_circuit_builder.cpp b/ecmascript/compiler/lcr_circuit_builder.cpp index 549495fd348cbb53be8579a115609ab62387ecd6..72a290ca8a9b6704d9f9384d980ca8568f371cca 100644 --- a/ecmascript/compiler/lcr_circuit_builder.cpp +++ b/ecmascript/compiler/lcr_circuit_builder.cpp @@ -198,6 +198,33 @@ GateRef CircuitBuilder::LoadWithoutBarrier(VariableType type, GateRef addr, Memo return result; } +GateRef CircuitBuilder::LoadBarrierCondition(GateRef glue) +{ + GateRef bitOffset = circuit_->GetConstantGateWithoutCache( + MachineType::I64, JSThread::GlueData::GetThreadHolderOffset(false), GateType::NJSValue()); + GateRef bitAddr = PtrAdd(glue, bitOffset); + GateRef threadHolder = LoadWithoutBarrier(VariableType::NATIVE_POINTER(), bitAddr); + GateRef mutatorBase = LoadWithoutBarrier(VariableType::NATIVE_POINTER(), + threadHolder); // currently offset is zero + GateRef gcPhase = LoadWithoutBarrier(VariableType::INT8(), mutatorBase); // currently offset is zero + GateRef conditionValue = circuit_->GetConstantGateWithoutCache( + MachineType::I8, GCPhase::GC_PHASE_PRECOPY, GateType::NJSValue()); + GateRef condition = Int8GreaterThanOrEqual(gcPhase, conditionValue); + + return condition; +} + +GateRef CircuitBuilder::LoadWithCondition(VariableType type, GateRef glue, GateRef addr, GateRef condition, MemoryAttribute mAttr) +{ + auto label = GetCurrentLabel(); + auto depend = label->GetDepend(); + auto bits = LoadStoreAccessor::ToValue(mAttr); + GateRef result = GetCircuit()->NewGate(GetCircuit()->LoadWithCondition(bits), type.GetMachineType(), + { depend, glue, addr, condition}, type.GetGateType()); + label->SetDepend(result); + return result; +} + GateRef CircuitBuilder::DoubleTrunc(GateRef gate, GateRef value, const char* comment) { if (GetCompilationConfig()->IsAArch64()) { diff --git a/ecmascript/compiler/lcr_opcodes.h b/ecmascript/compiler/lcr_opcodes.h index fc869fd238eb25e48f758002e31113d2cf0ada59..adf5543b36d0e856f1c645e49110a27e16f876e8 100644 --- a/ecmascript/compiler/lcr_opcodes.h +++ b/ecmascript/compiler/lcr_opcodes.h @@ -76,6 +76,7 @@ namespace panda::ecmascript::kungfu { V(Fcmp, FCMP, GateFlags::NONE_FLAG, 0, 0, 2) \ V(Load, LOAD, GateFlags::NO_WRITE, 0, 1, 2) \ V(LoadWithoutBarrier, LOAD_WITHOUT_BARRIER, GateFlags::NO_WRITE, 0, 1, 1) \ + V(LoadWithCondition, LOAD_WITH_CONDITION, GateFlags::NO_WRITE, 0, 1, 3) \ V(Store, STORE, GateFlags::NONE_FLAG, 0, 1, 5) \ V(StoreWithoutBarrier, STORE_WITHOUT_BARRIER, GateFlags::NONE_FLAG, 0, 1, 2) \ V(Alloca, ALLOCA, GateFlags::NONE_FLAG, 0, 0, 0) diff --git a/ecmascript/compiler/pass.h b/ecmascript/compiler/pass.h index e3f17890a4365f011d4dda026fe7838c317221d7..1ae7918babc314bb1042e309f718096254e75159 100644 --- a/ecmascript/compiler/pass.h +++ b/ecmascript/compiler/pass.h @@ -42,6 +42,7 @@ #include "ecmascript/compiler/number_speculative_runner.h" #include "ecmascript/compiler/post_schedule.h" #include "ecmascript/compiler/precompile_checker.h" +#include "ecmascript/compiler/read_barrier_elimination.h" #include "ecmascript/compiler/scheduler.h" #include "ecmascript/compiler/string_builder_optimizer.h" #include "ecmascript/compiler/slowpath_lowering.h" @@ -202,6 +203,11 @@ public: methodInfo_->SetIsCompiled(false); } + void SetAllocator(NativeAreaAllocator *allocator) + { + allocator_ = allocator; + } + private: BytecodeCircuitBuilder *builder_ {nullptr}; Circuit *circuit_ {nullptr}; @@ -685,6 +691,43 @@ public: } }; +class StubReadBarrierEliminationPass { +public: + bool Run(PassData *data) + { + TimeScope timescope("ReadBarrierEliminationPass", data->GetMethodName(), data->GetMethodOffset(), + data->GetLog()); + Chunk chunk(data->GetNativeAreaAllocator()); + bool enableLog = data->GetLog()->EnableMethodCIRLog(); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + ReadBarrierElimination readBarrierElimination(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&readBarrierElimination); + visitor.VisitGraph(); + visitor.PrintLog("Read Barrier Elimination"); + return true; + } +}; + +class ReadBarrierEliminationPass { +public: + bool Run(PassData *data) + { + JSRuntimeOptions runtimeOption = data->GetPassContext()->GetCompilationEnv()->GetJSOptions(); + if (runtimeOption.IsEnableReadBarrierElimination()) { + TimeScope timescope("ReadBarrierEliminationPass", data->GetMethodName(), data->GetMethodOffset(), + data->GetLog()); + Chunk chunk(data->GetNativeAreaAllocator()); + bool enableLog = data->GetLog()->EnableMethodCIRLog(); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + ReadBarrierElimination readBarrierElimination(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&readBarrierElimination); + visitor.VisitGraph(); + visitor.PrintLog("Read Barrier Elimination"); + } + return true; + } +}; + class UselessGateEliminationPass { public: bool Run(PassData* data) diff --git a/ecmascript/compiler/pass_manager.cpp b/ecmascript/compiler/pass_manager.cpp index 26474d8eb6586682a6b63b2d6eb5f641f6de0fa9..cd3bdd70071c2ddb0d4e88087f3ad7e0eb639e3c 100644 --- a/ecmascript/compiler/pass_manager.cpp +++ b/ecmascript/compiler/pass_manager.cpp @@ -183,6 +183,9 @@ bool JitPassManager::Compile(JSHandle &profileTypeInfo, } pipeline.RunPass(); pipeline.RunPass(); +#ifdef USE_READ_BARRIER + pipeline.RunPass(); +#endif pipeline.RunPass(); if (!compilationEnv_->GetJSOptions().IsEnableJitFastCompile() && compilationEnv_->GetJSOptions().IsEnableJitVerifyPass()) { @@ -348,6 +351,9 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); +#ifdef USE_READ_BARRIER + pipeline.RunPass(); +#endif pipeline.RunPass(); if (passOptions_->EnableVerifierPass()) { pipeline.RunPass(); diff --git a/ecmascript/compiler/post_schedule.cpp b/ecmascript/compiler/post_schedule.cpp index ea05c3cafe22c7f521443fb651a943e37b6137ad..f2d6084434f2532256862e266d4e633733db0372 100644 --- a/ecmascript/compiler/post_schedule.cpp +++ b/ecmascript/compiler/post_schedule.cpp @@ -62,6 +62,10 @@ void PostSchedule::GenerateExtraBB(ControlFlowGraph &cfg) needRetraverse = VisitLoad(current, cfg, bbIdx, instIdx); break; } + case OpCode::LOAD_WITH_CONDITION: { + needRetraverse = VisitLoadWithCondition(current, cfg, bbIdx, instIdx); + break; + } default: { break; } @@ -671,6 +675,28 @@ void PostSchedule::LoweringStoreUnknownBarrierAndPrepareScheduleGate(GateRef gat acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } +bool PostSchedule::VisitLoadWithCondition(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx) +{ + std::vector currentBBGates; + std::vector successBBGates; + std::vector failBBGates; + std::vector endBBGates; +#ifdef USE_CMC_GC + LoweringLoadWithConditionAndPrepareScheduleGate(gate, currentBBGates, successBBGates, + failBBGates, endBBGates); + ReplaceBBState(cfg, bbIdx, currentBBGates, endBBGates); + ScheduleEndBB(endBBGates, cfg, bbIdx, instIdx); + ScheduleNewBB(successBBGates, cfg, bbIdx); + ScheduleNewBB(failBBGates, cfg, bbIdx); + ScheduleCurrentBB(currentBBGates, cfg, bbIdx, instIdx); + return true; +#else + LoweringLoadWithBarrierAndPrepareScheduleGate(gate, currentBBGates); + ReplaceGateDirectly(currentBBGates, cfg, bbIdx, instIdx); + return false; +#endif +} + bool PostSchedule::VisitLoad(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx) { std::vector currentBBGates; @@ -714,6 +740,86 @@ bool PostSchedule::VisitLoad(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, } #ifdef USE_CMC_GC +void PostSchedule::LoweringLoadWithConditionAndPrepareScheduleGate(GateRef gate, + std::vector ¤tBBGates, + std::vector &successBBGates, + std::vector &failBBGates, + std::vector &endBBGates) +{ + Environment env(gate, circuit_, &builder_); + Label exit(&builder_); + GateRef glue = acc_.GetValueIn(gate, 0); + GateRef addr = acc_.GetValueIn(gate, 1); + VariableType type = VariableType(acc_.GetMachineType(gate), acc_.GetGateType(gate)); + + GateRef hole = circuit_->GetConstantGateWithoutCache( + MachineType::I64, JSTaggedValue::VALUE_HOLE, GateType::TaggedValue()); + DEFVALUE(result, (&builder_), type, hole); + + Label callRuntime(&builder_); + Label noBarrier(&builder_); + GateRef condition = acc_.GetValueIn(gate, 2); + Label *currentLabel = env.GetCurrentLabel(); + BRANCH_CIR_UNLIKELY(condition, &callRuntime, &noBarrier); + { + GateRef ifBranch = currentLabel->GetControl(); + PrepareToScheduleNewGate(ifBranch, currentBBGates); + PrepareToScheduleNewGate(condition, currentBBGates); + PrepareToScheduleNewGate(conditionValue, currentBBGates); + PrepareToScheduleNewGate(gcPhase, currentBBGates); + PrepareToScheduleNewGate(mutatorBase, currentBBGates); + PrepareToScheduleNewGate(threadHolder, currentBBGates); + PrepareToScheduleNewGate(bitAddr, currentBBGates); + PrepareToScheduleNewGate(bitOffset, currentBBGates); + PrepareToScheduleNewGate(hole, currentBBGates); + } + builder_.Bind(&callRuntime); + { + GateRef ifTrue = builder_.GetState(); + int index = CommonStubCSigns::GetValueWithBarrier; + const CallSignature *cs = CommonStubCSigns::Get(index); + ASSERT(cs->IsCommonStub()); + GateRef target = circuit_->GetConstantGateWithoutCache(MachineType::ARCH, index, GateType::NJSValue()); + GateRef reservedFrameArgs = circuit_->GetConstantGateWithoutCache(MachineType::I64, 0, GateType::NJSValue()); + GateRef reservedPc = circuit_->GetConstantGateWithoutCache(MachineType::I64, 0, GateType::NJSValue()); + GateRef loadBarrier = builder_.Call(cs, glue, target, builder_.GetDepend(), + { glue, addr, reservedFrameArgs, reservedPc }, + Circuit::NullGate(), "load barrier"); + result = loadBarrier; + builder_.Jump(&exit); + { + GateRef ordinaryBlock = callRuntime.GetControl(); + PrepareToScheduleNewGate(ordinaryBlock, successBBGates); + PrepareToScheduleNewGate(loadBarrier, successBBGates); + PrepareToScheduleNewGate(reservedPc, successBBGates); + PrepareToScheduleNewGate(reservedFrameArgs, successBBGates); + PrepareToScheduleNewGate(target, successBBGates); + PrepareToScheduleNewGate(ifTrue, successBBGates); + } + } + builder_.Bind(&noBarrier); + { + GateRef ifFalse = builder_.GetState(); + GateRef loadWithoutBarrier = builder_.LoadWithoutBarrier(type, addr, acc_.GetMemoryAttribute(gate)); + result = loadWithoutBarrier; + builder_.Jump(&exit); + { + GateRef ordinaryBlock = noBarrier.GetControl(); + PrepareToScheduleNewGate(ordinaryBlock, failBBGates); + PrepareToScheduleNewGate(loadWithoutBarrier, failBBGates); + PrepareToScheduleNewGate(ifFalse, failBBGates); + } + } + builder_.Bind(&exit); + { + GateRef merge = builder_.GetState(); + GateRef phi = *result; + PrepareToScheduleNewGate(merge, endBBGates); + PrepareToScheduleNewGate(phi, endBBGates); + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result); +} + void PostSchedule::LoweringLoadWithBarrierAndPrepareScheduleGate(GateRef gate, std::vector ¤tBBGates, std::vector &successBBGates, diff --git a/ecmascript/compiler/post_schedule.h b/ecmascript/compiler/post_schedule.h index 8a3a1ba90b8489780a89eba3ef863dda46cea238..d28f8684573d2e4d06bc46b7e8a637b1447ae864 100644 --- a/ecmascript/compiler/post_schedule.h +++ b/ecmascript/compiler/post_schedule.h @@ -50,6 +50,7 @@ private: bool VisitHeapAlloc(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); bool VisitStore(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); bool VisitLoad(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); + bool VisitLoadWithCondition(GateRef gate, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); void ReplaceGateDirectly(std::vector &gates, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); void ScheduleEndBB(std::vector &gates, ControlFlowGraph &cfg, size_t bbIdx, size_t instIdx); @@ -82,9 +83,14 @@ private: std::vector &successBBGates, std::vector &failBBGates, std::vector &endBBGates); + void LoweringLoadWithConditionAndPrepareScheduleGate(GateRef gate, std::vector ¤tBBGates, + std::vector &successBBGates, + std::vector &failBBGates, + std::vector &endBBGates); #else void LoweringLoadWithBarrierAndPrepareScheduleGate(GateRef gate, std::vector ¤tBBGates); -#endif + void LoweringLoadWithConditionAndPrepareScheduleGate(GateRef gate, std::vector ¤tBBGates); +#endif void PrepareToScheduleNewGate(GateRef gate, std::vector &gates); MemoryAttribute::Barrier GetBarrierKind(GateRef gate); diff --git a/ecmascript/compiler/read_barrier_elimination.cpp b/ecmascript/compiler/read_barrier_elimination.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8be6fc260ff6875155c26d80b9214f9b2307cbbe --- /dev/null +++ b/ecmascript/compiler/read_barrier_elimination.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2025 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/read_barrier_elimination.h" + +namespace panda::ecmascript::kungfu { + +void ReadBarrierElimination::SetBarrierState(GateRef gate, ReadBarrierElimination::BarrierState state) +{ + barrierStates_[gate] = state; + if (state != ReadBarrierElimination::BarrierState::NOT_NEED_BARRIER) { + SetBarrierGate(gate, Gate::InvalidGateRef); + } +} + +ReadBarrierElimination::BarrierState ReadBarrierElimination::GetBarrierState(GateRef gate) +{ + if (barrierStates_.find(gate) != barrierStates_.end()) { + return barrierStates_[gate]; + } + return ReadBarrierElimination::BarrierState::UNDEFINED; +} + +void ReadBarrierElimination::SetBarrierGate(GateRef gate, GateRef barrierGate) +{ + barrierGates_[gate] = barrierGate; +} + +GateRef ReadBarrierElimination::GetBarrierGate(GateRef gate) +{ + if (barrierGates_.find(gate) != barrierGates_.end()) { + return barrierGates_[gate]; + } + return Gate::InvalidGateRef; +} + +void ReadBarrierElimination::RevisitAllUses(GateRef gate) +{ + auto uses = acc_.Uses(gate); + for (auto use : uses) { + visitor_->ReVisitGate(use); + } +} + +GateRef ReadBarrierElimination::VisitGate(GateRef gate) +{ + OpCode op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::LOAD: + return VisitLoadGate(gate); + case OpCode::CALL: + case OpCode::BUILTINS_CALL: + case OpCode::BUILTINS_CALL_WITH_ARGV: + case OpCode::CALL_OPTIMIZED: + case OpCode::FAST_CALL_OPTIMIZED: + case OpCode::RUNTIME_CALL: + case OpCode::RUNTIME_CALL_WITH_ARGV: + case OpCode::BASELINE_CALL: + return VisitCheckSafePointGate(gate); + case OpCode::NOGC_RUNTIME_CALL: + return VisitNoGCRuntimeCall(gate); + case OpCode::DEPEND_SELECTOR: + return VisitDependSelector(gate); + case OpCode::DEPEND_ENTRY: + return VisitDependEntry(gate); + default: + return VisitGeneralGate(gate); + } +} + +GateRef ReadBarrierElimination::VisitNoGCRuntimeCall(GateRef gate) { + GateRef indexGate = acc_.GetValueIn(gate, 0); + size_t index = acc_.GetConstantValue(indexGate); + if (RuntimeStubCSigns::IsAsmStub(index)) { + return VisitCheckSafePointGate(gate); + } else { + return VisitGeneralGate(gate); + } +} + +GateRef ReadBarrierElimination::VisitDependEntry(GateRef gate) +{ + SetBarrierState(gate, ReadBarrierElimination::BarrierState::NEED_BARRIER); + return Circuit::NullGate(); +} + +GateRef ReadBarrierElimination::VisitLoadGate(GateRef gate) +{ + if (!acc_.IsGCRelated(gate)) { + return VisitGeneralGate(gate); + } + GateRef dep = acc_.GetDep(gate); + auto depBarrierState = GetBarrierState(dep); + if (depBarrierState != ReadBarrierElimination::BarrierState::NOT_NEED_BARRIER) { + if (GetBarrierState(gate) != ReadBarrierElimination::BarrierState::NOT_NEED_BARRIER || + GetBarrierGate(gate) != gate) { + SetBarrierGate(gate, gate); + SetBarrierState(gate, ReadBarrierElimination::BarrierState::NOT_NEED_BARRIER); + RevisitAllUses(gate); + } + } else { + return Propagate(gate, dep); + } + return Circuit::NullGate(); +} + +GateRef ReadBarrierElimination::VisitCheckSafePointGate(GateRef gate) +{ + if (GetBarrierState(gate) != ReadBarrierElimination::BarrierState::NEED_BARRIER) { + SetBarrierState(gate, ReadBarrierElimination::BarrierState::NEED_BARRIER); + RevisitAllUses(gate); + } + return Circuit::NullGate(); +} + +GateRef ReadBarrierElimination::MergeBarrierState(GateRef lGate, GateRef rGate) +{ + if (lGate == Gate::InvalidGateRef || rGate == Gate::InvalidGateRef) { + return Gate::InvalidGateRef; + } + + auto lBarrierState = GetBarrierState(lGate); + auto rBarrierState = GetBarrierState(rGate); + auto lBarrierGate = GetBarrierGate(lGate); + auto rBarrierGate = GetBarrierGate(rGate); + + if (lBarrierState == ReadBarrierElimination::BarrierState::UNDEFINED || + rBarrierState == ReadBarrierElimination::BarrierState::NEED_BARRIER) { + return rGate; + } + if (lBarrierState == ReadBarrierElimination::BarrierState::NEED_BARRIER || + rBarrierState == ReadBarrierElimination::BarrierState::UNDEFINED) { + return lGate; + } + if (lBarrierGate != rBarrierGate) { + return Gate::InvalidGateRef; + } + return lGate; +} + +GateRef ReadBarrierElimination::VisitDependSelector(GateRef gate) +{ + size_t numIn = acc_.GetDependCount(gate); + GateRef mergeTarget = acc_.GetDep(gate, 0); + for (size_t i = 1; i < numIn; i++) { + mergeTarget = MergeBarrierState(mergeTarget, acc_.GetDep(gate, i)); + } + if (mergeTarget == Gate::InvalidGateRef) { + if (GetBarrierState(gate) != ReadBarrierElimination::BarrierState::NEED_BARRIER) { + SetBarrierState(gate, ReadBarrierElimination::BarrierState::NEED_BARRIER); + RevisitAllUses(gate); + } + } else { + if (TryCopyBarrierState(gate, mergeTarget)) { + RevisitAllUses(gate); + } + } + return Circuit::NullGate(); +} + +bool ReadBarrierElimination::TryCopyBarrierState(GateRef gate, GateRef dep) +{ + auto gateBarrierState = GetBarrierState(gate); + auto depBarrierState = GetBarrierState(dep); + auto gateBarrierGate = GetBarrierGate(gate); + auto depBarrierGate = GetBarrierGate(dep); + if (gateBarrierState == depBarrierState && gateBarrierGate == depBarrierGate) { + return false; + } + SetBarrierGate(gate, depBarrierGate); + SetBarrierState(gate, depBarrierState); + return true; +} + +GateRef ReadBarrierElimination::Propagate(GateRef gate, GateRef dep) +{ + if (TryCopyBarrierState(gate, dep)) { + RevisitAllUses(gate); + } + return Circuit::NullGate(); +} + +GateRef ReadBarrierElimination::VisitGeneralGate(GateRef gate) +{ + if (acc_.GetDependCount(gate) == 0) { + return Circuit::NullGate(); + } + ASSERT(acc_.GetDependCount(gate) == 1); + auto dep = acc_.GetDep(gate); + return Propagate(gate, dep); +} + +void ReadBarrierElimination::Finalize() +{ + std::vector allGateList; + circuit_->GetAllGates(allGateList); + for (auto gate : allGateList) { + + if (acc_.GetOpCode(gate) == OpCode::LOAD && acc_.IsGCRelated(gate)) { + ASSERT(GetBarrierState(gate) == ReadBarrierElimination::BarrierState::NOT_NEED_BARRIER); + if (GetBarrierGate(gate) == gate) { + Environment env(gate, circuit_, &builder_); + GateRef glue = acc_.GetValueIn(gate, 0); + GateRef addr = acc_.GetValueIn(gate, 1); + GateRef condition = builder_.LoadBarrierCondition(glue); + conditionGates_[gate] = condition; + LOG_COMPILER(INFO) << acc_.GetId(gate) << " -> " << acc_.GetId(gate); + VariableType type = VariableType(acc_.GetMachineType(gate), acc_.GetGateType(gate)); + GateRef loadWithCondition = builder_.LoadWithCondition(type, glue, addr, condition); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), loadWithCondition); + } + } + } + for (auto gate : allGateList) { + if (acc_.GetOpCode(gate) == OpCode::LOAD && acc_.IsGCRelated(gate)) { + if (GetBarrierGate(gate) != gate) { + Environment env(gate, circuit_, &builder_); + GateRef glue = acc_.GetValueIn(gate, 0); + GateRef addr = acc_.GetValueIn(gate, 1); + GateRef condition = conditionGates_[barrierGates_[gate]]; + LOG_COMPILER(INFO) << acc_.GetId(gate) << " -> " << acc_.GetId(GetBarrierGate(gate)); + VariableType type = VariableType(acc_.GetMachineType(gate), acc_.GetGateType(gate)); + GateRef loadWithCondition = builder_.LoadWithCondition(type, glue, addr, condition); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), loadWithCondition); + } + } + } +} + +} // panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/read_barrier_elimination.h b/ecmascript/compiler/read_barrier_elimination.h new file mode 100644 index 0000000000000000000000000000000000000000..810fe1302e3d0eb00e11106b012be7b0b133d54b --- /dev/null +++ b/ecmascript/compiler/read_barrier_elimination.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 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. + */ + +#ifndef ECMASCRIPT_COMPILER_READ_BARRIER_ELIMINATION_H +#define ECMASCRIPT_COMPILER_READ_BARRIER_ELIMINATION_H + +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/combined_pass_visitor.h" +#include "ecmascript/compiler/gate_accessor.h" + + +namespace panda::ecmascript::kungfu { + +class ReadBarrierElimination : public PassVisitor { +public: + enum class BarrierState { + UNDEFINED, + NEED_BARRIER, // NEED_BARRIER means this gate can't find a barrier gate that already exist + NOT_NEED_BARRIER, // NOT_NEED_BARRIER means an old barrier gate can be used by this gate (maybe generated by itself) + }; + ReadBarrierElimination(Circuit* circuit, RPOVisitor *visitor, Chunk *chunk) + : PassVisitor(circuit, chunk, visitor), builder_(circuit) {} + GateRef VisitGate(GateRef gate) override; + void Finalize() override; +private: + void Init(); + void SetBarrierState(GateRef gate, ReadBarrierElimination::BarrierState state); + ReadBarrierElimination::BarrierState GetBarrierState(GateRef gate); + void SetBarrierGate(GateRef gate, GateRef barrierGate); + GateRef GetBarrierGate(GateRef gate); + GateRef MergeBarrierState(GateRef lGate, GateRef rGate); + bool TryCopyBarrierState(GateRef gate, GateRef dep); + GateRef Propagate(GateRef gate, GateRef dep); + GateRef VisitDependEntry(GateRef gate); + GateRef VisitGeneralGate(GateRef gate); + GateRef VisitDependSelector(GateRef gate); + GateRef VisitCheckSafePointGate(GateRef gate); + GateRef VisitNoGCRuntimeCall(GateRef gate); + GateRef VisitLoadGate(GateRef gate); + void RevisitAllUses(GateRef gate); + + CircuitBuilder builder_; + std::map barrierStates_; + std::map barrierGates_; // if barrier state is NOT_NEED_BARRIER, barrier gate indicates the gate that can be reused. + std::map needBarrierDepNums_; + std::map conditionGates_; + std::queue workList_; +}; +}; // namespace panda::ecmascript::kungfu + +#endif \ No newline at end of file diff --git a/ecmascript/compiler/stub_compiler.cpp b/ecmascript/compiler/stub_compiler.cpp index 5ccf5b1a9c8d378c8286173ca6e1f4919cd7482c..d755daa5d5ecd04e3dd02f5545a533ab119e581f 100644 --- a/ecmascript/compiler/stub_compiler.cpp +++ b/ecmascript/compiler/stub_compiler.cpp @@ -22,11 +22,15 @@ namespace panda::ecmascript::kungfu { class StubPassData : public PassData { public: - StubPassData(Stub *stub, LLVMModule *module, CompilerLog *log) + StubPassData(Stub *stub, LLVMModule *module, CompilerLog *log, NativeAreaAllocator *allocator) : PassData(nullptr, nullptr, nullptr, log, "stubs"), cfg_(module->GetTripleStr()), module_(module), - stub_(stub) {} + stub_(stub) + { + SetAllocator(allocator); + } + ~StubPassData() = default; const CompilationConfig *GetCompilationConfig() const @@ -96,9 +100,12 @@ void StubCompiler::RunPipeline(LLVMModule *module, NativeAreaAllocator *allocato Stub stub(cs, &circuit); log->SetStubLog(stub.GetMethodName(), GetLogList()); - StubPassData data(&stub, module, log); + StubPassData data(&stub, module, log, allocator); PassRunner pipeline(&data); pipeline.RunPass(); +#ifdef USE_READ_BARRIER + pipeline.RunPass(); +#endif pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(i); diff --git a/ecmascript/js_runtime_options.cpp b/ecmascript/js_runtime_options.cpp index 45b58edb3f9313648a7069fcc1d59b6da51cf121..b0a454395ed65e31988ab41c69b3496becfaed64 100644 --- a/ecmascript/js_runtime_options.cpp +++ b/ecmascript/js_runtime_options.cpp @@ -205,6 +205,7 @@ const std::string PUBLIC_API HELP_OPTION_MSG = " Default : 'false'\n" "--compiler-an-file-max-size: Max size of compiler .an file in MB. '0' means Default\n" " Default: No limit for Host, '100' for TargetCompilerMode\n" + "--compiler-enable-read-barrier-elimination: Enable read barrier elimination. Default: 'true'\n" // Please add new options above this line for keep a blank line after help message. "\n"; @@ -356,6 +357,8 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) {"compile-enable-jit-verify-pass", required_argument, nullptr, OPTION_ENABLE_JIT_VERIFY_PASS}, {"compiler-an-file-max-size", required_argument, nullptr, OPTION_COMPILER_AN_FILE_MAX_SIZE}, {"compiler-trace-builtins", required_argument, nullptr, OPTION_COMPILER_TRACE_BUILTINS}, + {"compiler-enable-read-barrier-elimination", required_argument, nullptr, + OPTION_COMPILER_ENABLE_READ_BARRIER_ELIMINATION}, {nullptr, 0, nullptr, 0}, }; @@ -1414,6 +1417,14 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) return false; } break; + case OPTION_COMPILER_ENABLE_READ_BARRIER_ELIMINATION: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableReadBarrierElimination(argBool); + } else { + return false; + } + break; default: LOG_ECMA(ERROR) << "Invalid option\n"; return false; diff --git a/ecmascript/js_runtime_options.h b/ecmascript/js_runtime_options.h index 22f17cad39e46a2eba930621dcee85bd8cfa567c..06328c852b3f0a7e1a0642cf821cde7ca086dc6c 100644 --- a/ecmascript/js_runtime_options.h +++ b/ecmascript/js_runtime_options.h @@ -231,6 +231,7 @@ enum CommandValues { OPTION_ENABLE_HEAP_VERIFY, OPTION_COMPILER_ENABLE_DFX_HISYS_EVENT, OPTION_ENABLE_LOADING_STUBS_LOG, + OPTION_COMPILER_ENABLE_READ_BARRIER_ELIMINATION, }; static_assert(OPTION_INVALID == 63); // Placeholder for invalid options static_assert(OPTION_SPLIT_ONE == 64); // add new option at the bottom, DO NOT modify this value @@ -2115,6 +2116,16 @@ public: return CompilerAnFileMaxByteSize_ == 0; } + void SetEnableReadBarrierElimination(bool value) + { + enableReadBarrierElimination_ = value; + } + + bool IsEnableReadBarrierElimination() const + { + return enableReadBarrierElimination_; + } + public: static constexpr int32_t MAX_APP_COMPILE_METHOD_SIZE = 4_KB; @@ -2419,6 +2430,7 @@ private: #else bool storeBarrierOpt_ {false}; #endif + bool enableReadBarrierElimination_ {true}; uint64_t CompilerAnFileMaxByteSize_ {0_MB}; bool enableJitVerifyPass_ {true}; };