blob: 30c33dd5c55ebdc690a978d8e047322147833c8b [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 "constructor_fence_redundancy_elimination.h"
#include "base/arena_allocator.h"
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
namespace art HIDDEN {
static constexpr bool kCfreLogFenceInputCount = false;
// TODO: refactor this code by reusing escape analysis.
class CFREVisitor final : public HGraphVisitor {
public:
CFREVisitor(HGraph* graph, OptimizingCompilerStats* stats)
: HGraphVisitor(graph),
scoped_allocator_(graph->GetArenaStack()),
candidate_fences_(scoped_allocator_.Adapter(kArenaAllocCFRE)),
candidate_fence_targets_(std::nullopt),
stats_(stats) {}
void VisitBasicBlock(HBasicBlock* block) override {
// Visit all non-Phi instructions in the block.
VisitNonPhiInstructions(block);
// If there were any unmerged fences left, merge them together,
// the objects are considered 'published' at the end of the block.
MergeCandidateFences();
}
void VisitConstructorFence(HConstructorFence* constructor_fence) override {
candidate_fences_.push_back(constructor_fence);
if (!candidate_fence_targets_.has_value()) {
size_t number_of_instructions = GetGraph()->GetCurrentInstructionId();
candidate_fence_targets_.emplace(
&scoped_allocator_, number_of_instructions, /*expandable=*/ false, kArenaAllocCFRE);
candidate_fence_targets_->ClearAllBits();
}
for (HInstruction* input : constructor_fence->GetInputs()) {
candidate_fence_targets_->SetBit(input->GetId());
}
}
void VisitBoundType(HBoundType* bound_type) override {
VisitAlias(bound_type);
}
void VisitNullCheck(HNullCheck* null_check) override {
VisitAlias(null_check);
}
void VisitSelect(HSelect* select) override {
VisitAlias(select);
}
void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
HInstruction* value = instruction->InputAt(1);
VisitSetLocation(instruction, value);
}
void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
HInstruction* value = instruction->InputAt(1);
VisitSetLocation(instruction, value);
}
void VisitArraySet(HArraySet* instruction) override {
HInstruction* value = instruction->InputAt(2);
VisitSetLocation(instruction, value);
}
void VisitDeoptimize([[maybe_unused]] HDeoptimize* instruction) override {
// Pessimize: Merge all fences.
MergeCandidateFences();
}
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override {
HandleInvoke(invoke);
}
void VisitInvokeVirtual(HInvokeVirtual* invoke) override {
HandleInvoke(invoke);
}
void VisitInvokeInterface(HInvokeInterface* invoke) override {
HandleInvoke(invoke);
}
void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
HandleInvoke(invoke);
}
void VisitInvokePolymorphic(HInvokePolymorphic* invoke) override {
HandleInvoke(invoke);
}
void VisitClinitCheck(HClinitCheck* clinit) override {
HandleInvoke(clinit);
}
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
// Conservatively treat it as an invocation.
HandleInvoke(instruction);
}
void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
// Conservatively treat it as an invocation.
HandleInvoke(instruction);
}
void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
// Conservatively treat it as an invocation.
HandleInvoke(instruction);
}
void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
// Conservatively treat it as an invocation.
HandleInvoke(instruction);
}
private:
void HandleInvoke(HInstruction* invoke) {
// An object is considered "published" if it escapes into an invoke as any of the parameters.
if (HasInterestingPublishTargetAsInput(invoke)) {
MergeCandidateFences();
}
}
// Called by any instruction visitor that may create an alias.
// These instructions may create an alias:
// - BoundType
// - NullCheck
// - Select
//
// These also create an alias, but are not handled by this function:
// - Phi: propagates values across blocks, but we always merge at the end of a block.
// - Invoke: this is handled by HandleInvoke.
void VisitAlias(HInstruction* aliasing_inst) {
// An object is considered "published" if it becomes aliased by other instructions.
if (HasInterestingPublishTargetAsInput(aliasing_inst)) {
MergeCandidateFences();
}
}
void VisitSetLocation([[maybe_unused]] HInstruction* inst, HInstruction* store_input) {
if (candidate_fences_.empty()) {
// There is no need to look at inputs if there are no candidate fence targets.
DCHECK_IMPLIES(candidate_fence_targets_.has_value(),
!candidate_fence_targets_->IsAnyBitSet());
return;
}
// An object is considered "published" if it's stored onto the heap.
// Sidenote: A later "LSE" pass can still remove the fence if it proves the
// object doesn't actually escape.
if (IsInterestingPublishTarget(store_input)) {
// Merge all constructor fences that we've seen since
// the last interesting store (or since the beginning).
MergeCandidateFences();
}
}
bool HasInterestingPublishTargetAsInput(HInstruction* inst) {
if (candidate_fences_.empty()) {
// There is no need to look at inputs if there are no candidate fence targets.
DCHECK_IMPLIES(candidate_fence_targets_.has_value(),
!candidate_fence_targets_->IsAnyBitSet());
return false;
}
for (HInstruction* input : inst->GetInputs()) {
if (IsInterestingPublishTarget(input)) {
return true;
}
}
return false;
}
// Merges all the existing fences we've seen so far into the last-most fence.
//
// This resets the list of candidate fences and their targets back to {}.
void MergeCandidateFences() {
if (candidate_fences_.empty()) {
// Nothing to do, need 1+ fences to merge.
return;
}
// The merge target is always the "last" candidate fence.
HConstructorFence* merge_target = candidate_fences_.back();
candidate_fences_.pop_back();
for (HConstructorFence* fence : candidate_fences_) {
DCHECK_NE(merge_target, fence);
merge_target->Merge(fence);
MaybeRecordStat(stats_, MethodCompilationStat::kConstructorFenceRemovedCFRE);
}
if (kCfreLogFenceInputCount) {
LOG(INFO) << "CFRE-MergeCandidateFences: Post-merge fence input count "
<< merge_target->InputCount();
}
// Each merge acts as a cut-off point. The optimization is reset completely.
// In theory, we could push the fence as far as its publish, but in practice
// there is no benefit to this extra complexity unless we also reordered
// the stores to come later.
candidate_fences_.clear();
DCHECK(candidate_fence_targets_.has_value());
candidate_fence_targets_->ClearAllBits();
}
// A publishing 'store' is only interesting if the value being stored
// is one of the fence `targets` in `candidate_fences`.
bool IsInterestingPublishTarget(HInstruction* store_input) const {
DCHECK(candidate_fence_targets_.has_value());
return candidate_fence_targets_->IsBitSet(store_input->GetId());
}
// Phase-local heap memory allocator for CFRE optimizer.
ScopedArenaAllocator scoped_allocator_;
// Set of constructor fences that we've seen in the current block.
// Each constructor fences acts as a guard for one or more `targets`.
// There exist no stores to any `targets` between any of these fences.
//
// Fences are in succession order (e.g. fence[i] succeeds fence[i-1]
// within the same basic block).
ScopedArenaVector<HConstructorFence*> candidate_fences_;
// Stores a set of the fence targets, to allow faster lookup of whether
// a detected publish is a target of one of the candidate fences.
std::optional<ArenaBitVector> candidate_fence_targets_;
// Used to record stats about the optimization.
OptimizingCompilerStats* const stats_;
DISALLOW_COPY_AND_ASSIGN(CFREVisitor);
};
bool ConstructorFenceRedundancyElimination::Run() {
CFREVisitor cfre_visitor(graph_, stats_);
// Arbitrarily visit in reverse-post order.
// The exact block visit order does not matter, as the algorithm
// only operates on a single block at a time.
cfre_visitor.VisitReversePostOrder();
return true;
}
} // namespace art