1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "adjust_arefs.h"
17 #include "optimizer/ir/basicblock.h"
18 #include "optimizer/ir/graph.h"
19 #include "optimizer/analysis/loop_analyzer.h"
20
21 namespace panda::compiler {
AdjustRefs(Graph * graph)22 AdjustRefs::AdjustRefs(Graph *graph)
23 : Optimization {graph},
24 defs_ {graph->GetLocalAllocator()->Adapter()},
25 workset_ {graph->GetLocalAllocator()->Adapter()},
26 heads_ {graph->GetLocalAllocator()->Adapter()},
27 instsToReplace_ {graph->GetLocalAllocator()->Adapter()}
28 {
29 }
30
IsRefAdjustable(const Inst * inst)31 static bool IsRefAdjustable(const Inst *inst)
32 {
33 switch (inst->GetOpcode()) {
34 case Opcode::StoreArray:
35 return !inst->CastToStoreArray()->GetNeedBarrier();
36 case Opcode::LoadArray:
37 return !inst->CastToLoadArray()->GetNeedBarrier() && !inst->CastToLoadArray()->IsString();
38 default:
39 break;
40 }
41
42 return false;
43 }
44
RunImpl()45 bool AdjustRefs::RunImpl()
46 {
47 auto defMarker = GetGraph()->NewMarker();
48 for (const auto &bb : GetGraph()->GetBlocksRPO()) {
49 if (bb->GetLoop()->IsRoot()) {
50 continue;
51 }
52
53 for (auto inst : bb->Insts()) {
54 if (!IsRefAdjustable(inst)) {
55 continue;
56 }
57 auto def = inst->GetInput(0).GetInst();
58 if (!def->SetMarker(defMarker)) {
59 defs_.push_back(def);
60 }
61 }
62 }
63 GetGraph()->EraseMarker(defMarker);
64 for (auto def : defs_) {
65 workset_.clear();
66 auto markerHolder = MarkerHolder(GetGraph());
67 worksetMarker_ = markerHolder.GetMarker();
68 for (auto &user : def->GetUsers()) {
69 auto i = user.GetInst();
70 if (!IsRefAdjustable(i) || i->GetBasicBlock()->GetLoop()->IsRoot()) {
71 continue;
72 }
73 workset_.push_back(i);
74 i->SetMarker(worksetMarker_);
75 }
76 ProcessArrayUses();
77 }
78 // We make second pass, because some LoadArrays and StoreArrays can be removed
79 for (const auto &bb : GetGraph()->GetBlocksRPO()) {
80 for (auto inst : bb->Insts()) {
81 if (IsRefAdjustable(inst)) {
82 ProcessIndex(inst);
83 }
84 }
85 }
86
87 return added_;
88 }
89
ProcessArrayUses()90 void AdjustRefs::ProcessArrayUses()
91 {
92 ASSERT(heads_.empty());
93 auto enteredHolder = MarkerHolder(GetGraph());
94 blockEntered_ = enteredHolder.GetMarker();
95 GetHeads();
96 while (!heads_.empty()) {
97 auto head = heads_.back();
98 heads_.pop_back();
99 ASSERT(IsRefAdjustable(head));
100 ASSERT(head->IsMarked(worksetMarker_));
101 ASSERT(head->GetBasicBlock() != nullptr);
102 loop_ = head->GetBasicBlock()->GetLoop();
103 auto processedHolder = MarkerHolder(GetGraph());
104 blockProcessed_ = processedHolder.GetMarker();
105 instsToReplace_.clear();
106 ASSERT(!head->GetBasicBlock()->IsMarked(blockProcessed_));
107 WalkChainDown(head->GetBasicBlock(), head, head);
108 if (instsToReplace_.size() > 1) {
109 ProcessChain(head);
110 }
111 }
112 }
113
114 /* Create the list of "heads" - the instructions that are not dominated by any other
115 * instruction in the workset, or have a potential GC trigger after the dominating
116 * instruction and thus cannot be merged with it. In both cases "head" can potentially be
117 * the first instruction in a chain. */
GetHeads()118 void AdjustRefs::GetHeads()
119 {
120 for (const auto i : workset_) {
121 auto comp = [i](const Inst *i1) { return i1->IsDominate(i) && i != i1; };
122 if (workset_.end() == std::find_if(workset_.begin(), workset_.end(), comp)) {
123 heads_.push_back(i);
124 i->GetBasicBlock()->SetMarker(blockEntered_);
125 }
126 }
127 }
128
129 /* Add instructions which can be merged with head to insts_to_replace_
130 * Instructions which are visited but cannot be merged are added to heads_ */
WalkChainDown(BasicBlock * bb,Inst * startFrom,Inst * head)131 void AdjustRefs::WalkChainDown(BasicBlock *bb, Inst *startFrom, Inst *head)
132 {
133 bb->SetMarker(blockEntered_);
134 for (auto cur = startFrom; cur != nullptr; cur = cur->GetNext()) {
135 /* potential switch to VM, the chain breaks here */
136 if (cur->IsRuntimeCall() || cur->GetOpcode() == Opcode::SafePoint) {
137 head = nullptr;
138 } else if (cur->IsMarked(worksetMarker_)) {
139 if (head == nullptr) {
140 heads_.push_back(cur);
141 return;
142 }
143 ASSERT(head->IsDominate(cur));
144 instsToReplace_.push_back(cur);
145 }
146 }
147 if (head != nullptr) {
148 bb->SetMarker(blockProcessed_);
149 }
150 for (auto succ : bb->GetSuccsBlocks()) {
151 if (succ->GetLoop() != loop_ || succ->IsMarked(blockEntered_)) {
152 continue;
153 }
154 auto allPredsVisited = true;
155 if (head != nullptr) {
156 for (auto pred : succ->GetPredsBlocks()) {
157 if (!pred->IsMarked(blockProcessed_)) {
158 allPredsVisited = false;
159 break;
160 }
161 }
162 }
163 // If all predecessors of succ were walked with the current value of head,
164 // we can be sure that there are no SafePoints or runtime calls
165 // on any path from block with head to succ
166 if (allPredsVisited) {
167 WalkChainDown(succ, succ->GetFirstInst(), head);
168 }
169 }
170 }
171
ProcessChain(Inst * head)172 void AdjustRefs::ProcessChain(Inst *head)
173 {
174 Inst *def = head->GetInput(0).GetInst();
175 auto off = GetGraph()->GetRuntime()->GetArrayDataOffset(GetGraph()->GetArch());
176 auto arrData = InsertPointerArithmetic(def, off, head, def->GetPc(), true);
177 ASSERT(arrData != nullptr);
178
179 for (auto inst : instsToReplace_) {
180 auto scale = DataType::ShiftByType(inst->GetType(), GetGraph()->GetArch());
181 InsertMem(inst, arrData, inst->GetInput(1).GetInst(), scale);
182 }
183
184 added_ = true;
185 }
186
InsertPointerArithmetic(Inst * input,uint64_t imm,Inst * insertBefore,uint32_t pc,bool isAdd)187 Inst *AdjustRefs::InsertPointerArithmetic(Inst *input, uint64_t imm, Inst *insertBefore, uint32_t pc, bool isAdd)
188 {
189 uint32_t size = DataType::GetTypeSize(DataType::POINTER, GetGraph()->GetArch());
190 if (!GetGraph()->GetEncoder()->CanEncodeImmAddSubCmp(imm, size, false)) {
191 return nullptr;
192 }
193 Inst *newInst;
194 if (isAdd) {
195 newInst = GetGraph()->CreateInstAddI(DataType::POINTER, pc, input, imm);
196 } else {
197 newInst = GetGraph()->CreateInstSubI(DataType::POINTER, pc, input, imm);
198 }
199 insertBefore->InsertBefore(newInst);
200 return newInst;
201 }
202
InsertMem(Inst * org,Inst * base,Inst * index,uint8_t scale)203 void AdjustRefs::InsertMem(Inst *org, Inst *base, Inst *index, uint8_t scale)
204 {
205 Inst *ldst = nullptr;
206
207 ASSERT(base->IsDominate(org));
208
209 if (org->IsStore()) {
210 constexpr auto VALUE_IDX = 2;
211 ldst = GetGraph()->CreateInst(Opcode::Store);
212 ldst->SetInput(VALUE_IDX, org->GetInput(VALUE_IDX).GetInst());
213 ldst->CastToStore()->SetScale(scale);
214 } else if (org->IsLoad()) {
215 ldst = GetGraph()->CreateInst(Opcode::Load);
216 ldst->CastToLoad()->SetScale(scale);
217 } else {
218 UNREACHABLE();
219 }
220 ldst->SetInput(0, base);
221 ldst->SetInput(1, index);
222 ldst->SetType(org->GetType());
223 org->ReplaceUsers(ldst);
224 org->RemoveInputs();
225 org->GetBasicBlock()->ReplaceInst(org, ldst);
226 }
227
228 // from
229 // 3.i32 AddI v2, 0xN -> v4
230 // 4.i64 LoadArray v1, v3 -> ....
231 // to
232 // 5.ptr AddI v1, 0x10 + (0xN << 3) -> v6
233 // 6.i64 Load v5, v2 -> ....
ProcessIndex(Inst * mem)234 void AdjustRefs::ProcessIndex(Inst *mem)
235 {
236 Inst *index = mem->GetInput(1).GetInst();
237 bool isAdd;
238 uint64_t imm;
239 if (index->GetOpcode() == Opcode::AddI) {
240 isAdd = true;
241 imm = index->CastToAddI()->GetImm();
242 } else if (index->GetOpcode() == Opcode::SubI) {
243 isAdd = false;
244 imm = index->CastToSubI()->GetImm();
245 } else {
246 return;
247 }
248 auto scale = DataType::ShiftByType(mem->GetType(), GetGraph()->GetArch());
249 uint64_t off = GetGraph()->GetRuntime()->GetArrayDataOffset(GetGraph()->GetArch());
250 Inst *base = mem->GetInput(0).GetInst();
251
252 Inst *newBase;
253 if (!isAdd) {
254 if (off > (imm << scale)) {
255 uint64_t newOff = off - (imm << scale);
256 newBase = InsertPointerArithmetic(base, newOff, mem, mem->GetPc(), true);
257 } else if (off < (imm << scale)) {
258 uint64_t newOff = (imm << scale) - off;
259 newBase = InsertPointerArithmetic(base, newOff, mem, mem->GetPc(), false);
260 } else {
261 ASSERT(off == (imm << scale));
262 newBase = base;
263 }
264 } else {
265 uint64_t newOff = off + (imm << scale);
266 newBase = InsertPointerArithmetic(base, newOff, mem, mem->GetPc(), true);
267 }
268 if (newBase == nullptr) {
269 return;
270 }
271 InsertMem(mem, newBase, index->GetInput(0).GetInst(), scale);
272
273 added_ = true;
274 }
275
276 } // namespace panda::compiler
277