blob: eb70b670fea4ec613ced67899ee5cf2e4bd98af2 [file] [log] [blame]
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +00001/*
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
24namespace art HIDDEN {
25
Vladimir Marko30759fa2023-04-05 09:43:21 +020026class WBEVisitor final : public HGraphVisitor {
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +000027 public:
28 WBEVisitor(HGraph* graph, OptimizingCompilerStats* stats)
29 : HGraphVisitor(graph),
30 scoped_allocator_(graph->GetArenaStack()),
31 current_write_barriers_(scoped_allocator_.Adapter(kArenaAllocWBE)),
32 stats_(stats) {}
33
34 void VisitBasicBlock(HBasicBlock* block) override {
35 // We clear the map to perform this optimization only in the same block. Doing it across blocks
36 // would entail non-trivial merging of states.
37 current_write_barriers_.clear();
38 HGraphVisitor::VisitBasicBlock(block);
39 }
40
41 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
42 DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
43
44 if (instruction->GetFieldType() != DataType::Type::kReference ||
45 instruction->GetValue()->IsNullConstant()) {
46 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
47 return;
48 }
49
50 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
51 HInstruction* obj = HuntForOriginalReference(instruction->InputAt(0));
52 auto it = current_write_barriers_.find(obj);
53 if (it != current_write_barriers_.end()) {
54 DCHECK(it->second->IsInstanceFieldSet());
Vladimir Markocde64972023-04-25 16:40:06 +000055 DCHECK(it->second->AsInstanceFieldSet()->GetWriteBarrierKind() !=
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +000056 WriteBarrierKind::kDontEmit);
57 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
Vladimir Markocde64972023-04-25 16:40:06 +000058 it->second->AsInstanceFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +000059 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
60 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
61 } else {
62 const bool inserted = current_write_barriers_.insert({obj, instruction}).second;
63 DCHECK(inserted);
64 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
65 }
66 }
67
68 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
69 DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
70
71 if (instruction->GetFieldType() != DataType::Type::kReference ||
72 instruction->GetValue()->IsNullConstant()) {
73 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
74 return;
75 }
76
77 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
78 HInstruction* cls = HuntForOriginalReference(instruction->InputAt(0));
79 auto it = current_write_barriers_.find(cls);
80 if (it != current_write_barriers_.end()) {
81 DCHECK(it->second->IsStaticFieldSet());
Vladimir Markocde64972023-04-25 16:40:06 +000082 DCHECK(it->second->AsStaticFieldSet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +000083 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
Vladimir Markocde64972023-04-25 16:40:06 +000084 it->second->AsStaticFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +000085 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
86 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
87 } else {
88 const bool inserted = current_write_barriers_.insert({cls, instruction}).second;
89 DCHECK(inserted);
90 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
91 }
92 }
93
94 void VisitArraySet(HArraySet* instruction) override {
95 if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
96 ClearCurrentValues();
97 }
98
99 if (instruction->GetComponentType() != DataType::Type::kReference ||
100 instruction->GetValue()->IsNullConstant()) {
101 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
102 return;
103 }
104
105 HInstruction* arr = HuntForOriginalReference(instruction->InputAt(0));
106 MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
107 auto it = current_write_barriers_.find(arr);
108 if (it != current_write_barriers_.end()) {
109 DCHECK(it->second->IsArraySet());
Vladimir Markocde64972023-04-25 16:40:06 +0000110 DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +0000111 DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
112 // We never skip the null check in ArraySets so that value is already set.
Vladimir Markocde64972023-04-25 16:40:06 +0000113 DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() == WriteBarrierKind::kEmitNoNullCheck);
Santiago Aboy Solanesd4229602023-01-03 16:20:50 +0000114 instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
115 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
116 } else {
117 const bool inserted = current_write_barriers_.insert({arr, instruction}).second;
118 DCHECK(inserted);
119 DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
120 }
121 }
122
123 void VisitInstruction(HInstruction* instruction) override {
124 if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
125 ClearCurrentValues();
126 }
127 }
128
129 private:
130 void ClearCurrentValues() { current_write_barriers_.clear(); }
131
132 HInstruction* HuntForOriginalReference(HInstruction* ref) const {
133 // An original reference can be transformed by instructions like:
134 // i0 NewArray
135 // i1 HInstruction(i0) <-- NullCheck, BoundType, IntermediateAddress.
136 // i2 ArraySet(i1, index, value)
137 DCHECK(ref != nullptr);
138 while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) {
139 ref = ref->InputAt(0);
140 }
141 return ref;
142 }
143
144 ScopedArenaAllocator scoped_allocator_;
145
146 // Stores a map of <Receiver, InstructionWhereTheWriteBarrierIs>.
147 // `InstructionWhereTheWriteBarrierIs` is used for DCHECKs only.
148 ScopedArenaHashMap<HInstruction*, HInstruction*> current_write_barriers_;
149
150 OptimizingCompilerStats* const stats_;
151
152 DISALLOW_COPY_AND_ASSIGN(WBEVisitor);
153};
154
155bool WriteBarrierElimination::Run() {
156 WBEVisitor wbe_visitor(graph_, stats_);
157 wbe_visitor.VisitReversePostOrder();
158 return true;
159}
160
161} // namespace art