1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "write_barrier_elimination.h"
18
19 #include "base/arena_allocator.h"
20 #include "base/scoped_arena_allocator.h"
21 #include "base/scoped_arena_containers.h"
22 #include "optimizing/nodes.h"
23
24 // TODO(b/310755375, solanes): Enable WBE with the fixes.
25 constexpr bool kWBEEnabled = true;
26
27 namespace art HIDDEN {
28
29 class WBEVisitor final : public HGraphVisitor {
30 public:
WBEVisitor(HGraph * graph,OptimizingCompilerStats * stats)31 WBEVisitor(HGraph* graph, OptimizingCompilerStats* stats)
32 : HGraphVisitor(graph),
33 scoped_allocator_(graph->GetArenaStack()),
34 current_write_barriers_(scoped_allocator_.Adapter(kArenaAllocWBE)),
35 stats_(stats) {}
36
VisitBasicBlock(HBasicBlock * block)37 void VisitBasicBlock(HBasicBlock* block) override {
38 // We clear the map to perform this optimization only in the same block. Doing it across blocks
39 // would entail non-trivial merging of states.
40 current_write_barriers_.clear();
41 VisitNonPhiInstructions(block);
42 }
43
VisitInstanceFieldSet(HInstanceFieldSet * instruction)44 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
45 DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
46
47 if (instruction->GetFieldType() != DataType::Type::kReference ||
48 HuntForOriginalReference(instruction->GetValue())->IsNullConstant()) {
49 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
50 return;
51 }
52
53 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
54 HInstruction* obj = HuntForOriginalReference(instruction->InputAt(0));
55 auto it = current_write_barriers_.find(obj);
56 if (it != current_write_barriers_.end()) {
57 DCHECK(it->second->IsInstanceFieldSet());
58 DCHECK(it->second->AsInstanceFieldSet()->GetWriteBarrierKind() !=
59 WriteBarrierKind::kDontEmit);
60 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
61 it->second->AsInstanceFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitBeingReliedOn);
62 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
63 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
64 } else {
65 const bool inserted = current_write_barriers_.insert({obj, instruction}).second;
66 DCHECK(inserted);
67 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
68 }
69 }
70
VisitStaticFieldSet(HStaticFieldSet * instruction)71 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
72 DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
73
74 if (instruction->GetFieldType() != DataType::Type::kReference ||
75 HuntForOriginalReference(instruction->GetValue())->IsNullConstant()) {
76 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
77 return;
78 }
79
80 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
81 HInstruction* cls = HuntForOriginalReference(instruction->InputAt(0));
82 auto it = current_write_barriers_.find(cls);
83 if (it != current_write_barriers_.end()) {
84 DCHECK(it->second->IsStaticFieldSet());
85 DCHECK(it->second->AsStaticFieldSet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
86 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
87 it->second->AsStaticFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitBeingReliedOn);
88 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
89 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
90 } else {
91 const bool inserted = current_write_barriers_.insert({cls, instruction}).second;
92 DCHECK(inserted);
93 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
94 }
95 }
96
VisitArraySet(HArraySet * instruction)97 void VisitArraySet(HArraySet* instruction) override {
98 if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
99 ClearCurrentValues();
100 }
101
102 if (instruction->GetComponentType() != DataType::Type::kReference ||
103 HuntForOriginalReference(instruction->GetValue())->IsNullConstant()) {
104 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
105 return;
106 }
107
108 HInstruction* arr = HuntForOriginalReference(instruction->InputAt(0));
109 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
110 auto it = current_write_barriers_.find(arr);
111 if (it != current_write_barriers_.end()) {
112 DCHECK(it->second->IsArraySet());
113 DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
114 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
115 it->second->AsArraySet()->SetWriteBarrierKind(WriteBarrierKind::kEmitBeingReliedOn);
116 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
117 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
118 } else {
119 const bool inserted = current_write_barriers_.insert({arr, instruction}).second;
120 DCHECK(inserted);
121 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
122 }
123 }
124
VisitInstruction(HInstruction * instruction)125 void VisitInstruction(HInstruction* instruction) override {
126 if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
127 ClearCurrentValues();
128 }
129 }
130
131 private:
ClearCurrentValues()132 void ClearCurrentValues() { current_write_barriers_.clear(); }
133
HuntForOriginalReference(HInstruction * ref) const134 HInstruction* HuntForOriginalReference(HInstruction* ref) const {
135 // An original reference can be transformed by instructions like:
136 // i0 NewArray
137 // i1 HInstruction(i0) <-- NullCheck, BoundType, IntermediateAddress.
138 // i2 ArraySet(i1, index, value)
139 DCHECK(ref != nullptr);
140 while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) {
141 ref = ref->InputAt(0);
142 }
143 return ref;
144 }
145
146 ScopedArenaAllocator scoped_allocator_;
147
148 // Stores a map of <Receiver, InstructionWhereTheWriteBarrierIs>.
149 // `InstructionWhereTheWriteBarrierIs` is used for DCHECKs only.
150 ScopedArenaHashMap<HInstruction*, HInstruction*> current_write_barriers_;
151
152 OptimizingCompilerStats* const stats_;
153
154 DISALLOW_COPY_AND_ASSIGN(WBEVisitor);
155 };
156
Run()157 bool WriteBarrierElimination::Run() {
158 if (kWBEEnabled) {
159 WBEVisitor wbe_visitor(graph_, stats_);
160 wbe_visitor.VisitReversePostOrder();
161 }
162 return true;
163 }
164
165 } // namespace art
166