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 "ecmascript/compiler/early_elimination.h"
17
18 namespace panda::ecmascript::kungfu {
19
Initialize()20 void EarlyElimination::Initialize()
21 {
22 dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size
23 renames_.resize(circuit_->GetMaxGateId() + 1, Circuit::NullGate()); // 1: +1 for size
24 GateRef entry = acc_.GetDependRoot();
25 VisitDependEntry(entry);
26 }
27
GetLoopDependInfo(GateRef depend)28 DependInfoNode* EarlyElimination::GetLoopDependInfo(GateRef depend)
29 {
30 auto depIn = acc_.GetDep(depend);
31 auto dependChain = GetDependChain(depIn);
32 if (dependChain == nullptr) {
33 return nullptr;
34 }
35 auto newChain = new (chunk_) DependInfoNode(chunk_);
36 newChain->CopyFrom(dependChain);
37 ChunkSet<GateRef> visited(chunk_);
38 ChunkQueue<GateRef> workList(chunk_);
39 workList.push(depend);
40 visited.insert(acc_.GetDep(depend));
41 while (!workList.empty()) {
42 auto curDep = workList.front();
43 workList.pop();
44 if (visited.count(curDep)) {
45 continue;
46 }
47 if (!acc_.IsNotWrite(curDep)) {
48 newChain = UpdateWrite(curDep, newChain);
49 }
50 visited.insert(curDep);
51 auto depCount = acc_.GetDependCount(curDep);
52 for (size_t i = 0; i < depCount; ++i) {
53 workList.push(acc_.GetDep(curDep, i));
54 }
55 }
56 return newChain;
57 }
58
VisitDependEntry(GateRef gate)59 GateRef EarlyElimination::VisitDependEntry(GateRef gate)
60 {
61 auto empty = new (chunk_) DependInfoNode(chunk_);
62 return UpdateDependChain(gate, empty);
63 }
64
VisitGate(GateRef gate)65 GateRef EarlyElimination::VisitGate(GateRef gate)
66 {
67 auto opcode = acc_.GetOpCode(gate);
68 switch (opcode) {
69 case OpCode::LOAD_PROPERTY:
70 case OpCode::LOAD_ELEMENT:
71 case OpCode::LOAD_ARRAY_LENGTH:
72 case OpCode::LOAD_TYPED_ARRAY_LENGTH:
73 case OpCode::TYPED_ARRAY_CHECK:
74 case OpCode::OBJECT_TYPE_CHECK:
75 case OpCode::OBJECT_TYPE_COMPARE:
76 case OpCode::STABLE_ARRAY_CHECK:
77 case OpCode::INDEX_CHECK:
78 case OpCode::TYPED_CALL_CHECK:
79 case OpCode::LOAD_CONST_OFFSET:
80 case OpCode::LOAD_HCLASS_FROM_CONSTPOOL:
81 case OpCode::TYPED_BINARY_OP:
82 case OpCode::TYPED_UNARY_OP:
83 case OpCode::JSINLINETARGET_TYPE_CHECK:
84 case OpCode::PROTOTYPE_CHECK:
85 case OpCode::LOAD_GETTER:
86 case OpCode::LOAD_SETTER:
87 case OpCode::ECMA_STRING_CHECK:
88 case OpCode::BUILTIN_PROTOTYPE_HCLASS_CHECK:
89 case OpCode::TYPE_OF_CHECK:
90 case OpCode::ARRAY_CONSTRUCTOR_CHECK:
91 case OpCode::OBJECT_CONSTRUCTOR_CHECK:
92 case OpCode::PROTO_CHANGE_MARKER_CHECK:
93 case OpCode::MONO_LOAD_PROPERTY_ON_PROTO:
94 case OpCode::LOAD_BUILTIN_OBJECT:
95 case OpCode::LOOK_UP_HOLDER:
96 return TryEliminateGate(gate);
97 case OpCode::STATE_SPLIT:
98 return TryEliminateFrameState(gate);
99 case OpCode::DEPEND_SELECTOR:
100 return TryEliminateDependSelector(gate);
101 default:
102 if (acc_.GetDependCount(gate) == 1) { // 1: depend in is 1
103 return TryEliminateOther(gate);
104 }
105 }
106 return Circuit::NullGate();
107 }
108
TryEliminateOther(GateRef gate)109 GateRef EarlyElimination::TryEliminateOther(GateRef gate)
110 {
111 ASSERT(acc_.GetDependCount(gate) >= 1);
112 auto depIn = acc_.GetDep(gate);
113 auto dependChain = GetDependChain(depIn);
114 if (dependChain == nullptr) {
115 return Circuit::NullGate();
116 }
117
118 if (!acc_.IsNotWrite(gate)) {
119 dependChain = UpdateWrite(gate, dependChain);
120 }
121
122 return UpdateDependChain(gate, dependChain);
123 }
124
TryEliminateGate(GateRef gate)125 GateRef EarlyElimination::TryEliminateGate(GateRef gate)
126 {
127 ASSERT(acc_.GetDependCount(gate) == 1);
128 auto depIn = acc_.GetDep(gate);
129 auto dependChain = GetDependChain(depIn);
130 // dependChain is null
131 if (dependChain == nullptr) {
132 return Circuit::NullGate();
133 }
134
135 if (!acc_.IsNotWrite(gate)) {
136 dependChain = UpdateWrite(gate, dependChain);
137 return UpdateDependChain(gate, dependChain);
138 }
139
140 auto numIns = acc_.GetNumValueIn(gate);
141 for (size_t i = 0; i < numIns; ++i) {
142 auto origin = acc_.GetValueIn(gate, i);
143 auto checkd = dependChain->LookupCheckedNode(this, origin);
144 if (origin != checkd) {
145 acc_.ReplaceValueIn(gate, checkd, i);
146 }
147 }
148
149 // lookup gate, replace
150 auto preGate = dependChain->LookupNode(this, gate);
151 if (preGate != Circuit::NullGate()) {
152 return preGate;
153 }
154 // update gate, for others elimination
155 dependChain = dependChain->UpdateNode(gate);
156 return UpdateDependChain(gate, dependChain);
157 }
158
TryEliminateFrameState(GateRef gate)159 GateRef EarlyElimination::TryEliminateFrameState(GateRef gate)
160 {
161 ASSERT(acc_.GetOpCode(gate) == OpCode::STATE_SPLIT);
162 auto depIn = acc_.GetDep(gate);
163 auto dependChain = GetDependChain(depIn);
164 // dependChain is null
165 if (dependChain == nullptr) {
166 return Circuit::NullGate();
167 }
168 // lookup gate, replace
169 auto preFrame = dependChain->LookupFrameState();
170 auto curFrame = acc_.GetFrameState(gate);
171 if ((preFrame != Circuit::NullGate()) && (preFrame != curFrame) &&
172 acc_.GetFrameState(preFrame) == acc_.GetFrameState(curFrame)) {
173 acc_.UpdateAllUses(curFrame, preFrame);
174 auto frameValues = acc_.GetValueIn(curFrame, 1); // 1: frameValues
175 acc_.DeleteGate(frameValues);
176 acc_.DeleteGate(curFrame);
177 return depIn;
178 } else {
179 dependChain = dependChain->UpdateFrameState(curFrame);
180 }
181 // update gate, for others elimination
182
183 return UpdateDependChain(gate, dependChain);
184 }
185
TryEliminateDependSelector(GateRef gate)186 GateRef EarlyElimination::TryEliminateDependSelector(GateRef gate)
187 {
188 auto state = acc_.GetState(gate);
189 if (acc_.IsLoopHead(state)) {
190 auto dependChain = GetLoopDependInfo(gate);
191 if (dependChain == nullptr) {
192 return Circuit::NullGate();
193 }
194 return UpdateDependChain(gate, dependChain);
195 }
196
197 auto dependCount = acc_.GetDependCount(gate);
198 for (size_t i = 0; i < dependCount; ++i) {
199 auto depend = acc_.GetDep(gate, i);
200 auto dependChain = GetDependChain(depend);
201 if (dependChain == nullptr) {
202 return Circuit::NullGate();
203 }
204 }
205
206 // all depend done.
207 auto depend = acc_.GetDep(gate);
208 auto dependChain = GetDependChain(depend);
209 DependInfoNode* copy = new (chunk_) DependInfoNode(chunk_);
210 copy->CopyFrom(dependChain);
211 for (size_t i = 1; i < dependCount; ++i) { // 1: second in
212 auto dependIn = acc_.GetDep(gate, i);
213 auto tempChain = GetDependChain(dependIn);
214 copy->Merge(this, tempChain);
215 }
216 return UpdateDependChain(gate, copy);
217 }
218
UpdateDependChain(GateRef gate,DependInfoNode * dependChain)219 GateRef EarlyElimination::UpdateDependChain(GateRef gate, DependInfoNode* dependChain)
220 {
221 ASSERT(dependChain != nullptr);
222 auto oldDependChain = GetDependChain(gate);
223 if (dependChain->Equals(oldDependChain)) {
224 return Circuit::NullGate();
225 }
226 dependChains_[acc_.GetId(gate)] = dependChain;
227 return gate;
228 }
229
UpdateWrite(GateRef gate,DependInfoNode * dependInfo)230 DependInfoNode* EarlyElimination::UpdateWrite(GateRef gate, DependInfoNode* dependInfo)
231 {
232 auto op = acc_.GetOpCode(gate);
233 switch (op) {
234 case OpCode::STORE_PROPERTY:
235 case OpCode::STORE_PROPERTY_NO_BARRIER:
236 case OpCode::STORE_CONST_OFFSET:
237 case OpCode::STORE_ELEMENT:
238 case OpCode::STORE_MEMORY:
239 case OpCode::MONO_STORE_PROPERTY_LOOK_UP_PROTO:
240 case OpCode::MONO_STORE_PROPERTY:
241 return dependInfo->UpdateStoreProperty(this, gate);
242 default:
243 return new (chunk_) DependInfoNode(chunk_);
244 }
245 }
246
MayAccessOneMemory(GateRef lhs,GateRef rhs)247 bool EarlyElimination::MayAccessOneMemory(GateRef lhs, GateRef rhs)
248 {
249 auto rop = acc_.GetOpCode(rhs);
250 auto lop = acc_.GetOpCode(lhs);
251 switch (rop) {
252 case OpCode::STORE_MEMORY:
253 ASSERT(acc_.GetMemoryType(rhs) == MemoryType::ELEMENT_TYPE);
254 return acc_.GetOpCode(lhs) == OpCode::LOAD_ELEMENT;
255 case OpCode::STORE_ELEMENT: {
256 if (lop == OpCode::LOAD_ELEMENT) {
257 bool lopIsTypedArray = acc_.TypedOpIsTypedArray(lhs, TypedOpKind::TYPED_LOAD_OP);
258 bool ropIsTypedArray = acc_.TypedOpIsTypedArray(rhs, TypedOpKind::TYPED_STORE_OP);
259 return lopIsTypedArray == ropIsTypedArray;
260 }
261 return false;
262 }
263 case OpCode::STORE_PROPERTY:
264 case OpCode::STORE_PROPERTY_NO_BARRIER: {
265 if (lop == OpCode::LOAD_PROPERTY) {
266 auto loff = acc_.GetValueIn(lhs, 1);
267 auto roff = acc_.GetValueIn(rhs, 1);
268 ASSERT(acc_.GetOpCode(loff) == OpCode::CONSTANT);
269 ASSERT(acc_.GetOpCode(roff) == OpCode::CONSTANT);
270 return loff == roff;
271 } else if (lop == OpCode::PROTOTYPE_CHECK) {
272 auto lindex = acc_.GetHClassIndex(lhs);
273 auto rindex = acc_.GetHClassIndex(rhs);
274 return (lindex == 0) || (rindex == 0) || (lindex != rindex);
275 }
276 break;
277 }
278 case OpCode::STORE_CONST_OFFSET: {
279 if (lop == OpCode::LOAD_CONST_OFFSET) {
280 auto loff = acc_.GetOffset(lhs);
281 auto roff = acc_.GetOffset(rhs);
282 return loff == roff;
283 }
284 break;
285 }
286 case OpCode::LOAD_PROPERTY:
287 case OpCode::MONO_LOAD_PROPERTY_ON_PROTO:
288 if (acc_.GetGateType(lhs).Value() != acc_.GetGateType(rhs).Value()) {
289 return false;
290 }
291 break;
292 default:
293 break;
294 }
295 return false;
296 }
297
CompareOrder(GateRef lhs,GateRef rhs)298 bool EarlyElimination::CompareOrder(GateRef lhs, GateRef rhs)
299 {
300 return visitor_->GetGateOrder(lhs) < visitor_->GetGateOrder(rhs);
301 }
302
CheckReplacement(GateRef lhs,GateRef rhs)303 bool EarlyElimination::CheckReplacement(GateRef lhs, GateRef rhs)
304 {
305 if (!acc_.MetaDataEqu(lhs, rhs)) {
306 if (acc_.GetOpCode(lhs) != acc_.GetOpCode(rhs)) {
307 return false;
308 }
309 }
310
311 size_t valueCount = acc_.GetNumValueIn(lhs);
312 for (size_t i = 0; i < valueCount; i++) {
313 if (Rename(acc_.GetValueIn(lhs, i)) != Rename(acc_.GetValueIn(rhs, i))) {
314 return false;
315 }
316 }
317
318 auto opcode = acc_.GetOpCode(lhs);
319 switch (opcode) {
320 case OpCode::LOAD_ELEMENT: {
321 if (acc_.GetTypedLoadOp(lhs) != acc_.GetTypedLoadOp(rhs)) {
322 return false;
323 }
324 break;
325 }
326 case OpCode::TYPED_BINARY_OP: {
327 auto lhsOp = acc_.GetTypedBinaryOp(lhs);
328 auto rhsOp = acc_.GetTypedBinaryOp(rhs);
329 if (lhsOp != rhsOp) {
330 return false;
331 }
332 break;
333 }
334 case OpCode::TYPED_UNARY_OP: {
335 auto lhsOp = acc_.GetTypedUnAccessor(lhs).GetTypedUnOp();
336 auto rhsOp = acc_.GetTypedUnAccessor(rhs).GetTypedUnOp();
337 if (lhsOp != rhsOp) {
338 return false;
339 }
340 break;
341 }
342 case OpCode::TYPED_ARRAY_CHECK: {
343 if (acc_.GetTypedArrayMetaDateAccessor(lhs).GetType() !=
344 acc_.GetTypedArrayMetaDateAccessor(rhs).GetType()) {
345 return false;
346 }
347 break;
348 }
349 case OpCode::TYPE_OF_CHECK: {
350 if (acc_.GetParamGateType(lhs) != acc_.GetParamGateType(rhs)) {
351 return false;
352 }
353 break;
354 }
355 case OpCode::OBJECT_TYPE_CHECK:
356 case OpCode::OBJECT_TYPE_COMPARE: {
357 if (acc_.GetObjectTypeAccessor(lhs).GetType() != acc_.GetObjectTypeAccessor(rhs).GetType()) {
358 return false;
359 }
360 break;
361 }
362 case OpCode::PROTOTYPE_CHECK: {
363 if (acc_.GetHClassIndex(lhs) != acc_.GetHClassIndex(rhs)) {
364 return false;
365 }
366 break;
367 }
368 case OpCode::LOAD_CONST_OFFSET: {
369 if (acc_.GetOffset(lhs) != acc_.GetOffset(rhs)) {
370 return false;
371 }
372 if (acc_.GetMemoryOrder(lhs).Value() != acc_.GetMemoryOrder(rhs).Value()) {
373 return false;
374 }
375 break;
376 }
377 case OpCode::LOAD_HCLASS_FROM_CONSTPOOL: {
378 if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) {
379 return false;
380 }
381 break;
382 }
383 case OpCode::JSINLINETARGET_TYPE_CHECK: {
384 if (acc_.GetFuncGT(lhs) != acc_.GetFuncGT(rhs)) {
385 return false;
386 }
387 break;
388 }
389 case OpCode::ARRAY_CONSTRUCTOR_CHECK:
390 case OpCode::OBJECT_CONSTRUCTOR_CHECK: {
391 if (acc_.GetValueIn(lhs) != acc_.GetValueIn(rhs)) {
392 return false;
393 }
394 break;
395 }
396 case OpCode::LOAD_BUILTIN_OBJECT: {
397 if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) {
398 return false;
399 }
400 break;
401 }
402 default:
403 break;
404 }
405 return true;
406 }
407
CheckRenameReplacement(GateRef lhs,GateRef rhs)408 bool EarlyElimination::CheckRenameReplacement(GateRef lhs, GateRef rhs)
409 {
410 auto opcode = acc_.GetOpCode(lhs);
411 switch (opcode) {
412 case OpCode::INDEX_CHECK: {
413 auto index = acc_.GetValueIn(lhs, 1);
414 if (Rename(index) == Rename(rhs)) {
415 return true;
416 }
417 break;
418 }
419 default:
420 break;
421 }
422 return false;
423 }
424
Rename(GateRef gate)425 GateRef EarlyElimination::Rename(GateRef gate)
426 {
427 ChunkStack<GateRef> gateStack(chunk_);
428 while (true) {
429 auto op = acc_.GetOpCode(gate);
430 bool renamed = false;
431 switch (op) {
432 case OpCode::INDEX_CHECK: {
433 GateRef ans = renames_[acc_.GetId(gate)];
434 if (ans == Circuit::NullGate()) {
435 renamed = true;
436 gateStack.push(gate);
437 gate = acc_.GetValueIn(gate, 1);
438 } else {
439 gate = ans;
440 }
441 break;
442 }
443 default:
444 break;
445 }
446 if (!renamed) {
447 break;
448 }
449 }
450 while (!gateStack.empty()) {
451 auto topGate = gateStack.top();
452 gateStack.pop();
453 renames_[acc_.GetId(topGate)] = gate;
454 }
455 return gate;
456 }
457
Merge(EarlyElimination * elimination,DependInfoNode * that)458 void DependInfoNode::Merge(EarlyElimination* elimination, DependInfoNode* that)
459 {
460 auto siz = this->size_; // size of lhs-chain
461 auto lhs = this->head_;
462 auto rhs = that->head_;
463 ChunkStack<GateRef> gateStack(chunk_);
464 while (lhs != rhs) {
465 if (lhs == nullptr || rhs == nullptr) {
466 siz = 0;
467 lhs = nullptr;
468 break;
469 } else if (lhs->gate == rhs->gate) {
470 gateStack.push(lhs->gate);
471 siz--;
472 lhs = lhs->next;
473 rhs = rhs->next;
474 } else if (elimination->CompareOrder(lhs->gate, rhs->gate)) {
475 rhs = rhs->next;
476 } else {
477 siz--;
478 lhs = lhs->next;
479 }
480 }
481 // lhs : common suffix of lhs-chain and rhs-chain
482 this->head_ = lhs;
483 this->size_ = siz;
484 while (!gateStack.empty()) {
485 Node* node = chunk_->New<Node>(gateStack.top(), head_);
486 gateStack.pop();
487 this->size_++;
488 this->head_ = node;
489 }
490 if (this->frameState_ != that->frameState_) {
491 this->frameState_ = Circuit::NullGate();
492 }
493 }
494
Equals(DependInfoNode * that)495 bool DependInfoNode::Equals(DependInfoNode* that)
496 {
497 if (that == nullptr) {
498 return false;
499 }
500 if (size_ != that->size_ || frameState_ != that->frameState_) {
501 return false;
502 }
503 auto lhs = this->head_;
504 auto rhs = that->head_;
505 while (lhs != rhs) {
506 if (lhs->gate != rhs->gate) {
507 return false;
508 }
509 lhs = lhs->next;
510 rhs = rhs->next;
511 }
512 return true;
513 }
514
LookupFrameState() const515 GateRef DependInfoNode::LookupFrameState() const
516 {
517 return frameState_;
518 }
519
LookupCheckedNode(EarlyElimination * elimination,GateRef gate)520 GateRef DependInfoNode::LookupCheckedNode(EarlyElimination* elimination, GateRef gate)
521 {
522 for (Node* node = head_; node != nullptr; node = node->next) {
523 if (elimination->CheckRenameReplacement(node->gate, gate)) {
524 return node->gate;
525 }
526 }
527 return gate;
528 }
529
GetGates(std::vector<GateRef> & gates) const530 void DependInfoNode::GetGates(std::vector<GateRef>& gates) const
531 {
532 ChunkStack<GateRef> st(chunk_);
533 for (Node* node = head_; node != nullptr; node = node->next) {
534 st.push(node->gate);
535 }
536 while (!st.empty()) {
537 gates.emplace_back(st.top());
538 st.pop();
539 }
540 }
541
LookupNode(EarlyElimination * elimination,GateRef gate)542 GateRef DependInfoNode::LookupNode(EarlyElimination* elimination, GateRef gate)
543 {
544 for (Node* node = head_; node != nullptr; node = node->next) {
545 if (elimination->CheckReplacement(node->gate, gate)) {
546 return node->gate;
547 }
548 }
549 return Circuit::NullGate();
550 }
551
UpdateNode(GateRef gate)552 DependInfoNode* DependInfoNode::UpdateNode(GateRef gate)
553 {
554 // assign node->next to head
555 Node* node = chunk_->New<Node>(gate, head_);
556 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
557 // assign head to node
558 that->head_ = node;
559 that->size_ = size_ + 1;
560 that->frameState_ = frameState_;
561 return that;
562 }
563
UpdateFrameState(GateRef framestate)564 DependInfoNode* DependInfoNode::UpdateFrameState(GateRef framestate)
565 {
566 // assign node->next to head
567 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
568 // assign head to node
569 that->head_ = head_;
570 that->size_ = size_;
571 that->frameState_ = framestate;
572 return that;
573 }
574
UpdateStoreProperty(EarlyElimination * elimination,GateRef gate)575 DependInfoNode* DependInfoNode::UpdateStoreProperty(EarlyElimination* elimination, GateRef gate)
576 {
577 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
578 ChunkStack<GateRef> gateStack(chunk_);
579 for (Node* node = head_; node != nullptr; node = node->next) {
580 if (!elimination->MayAccessOneMemory(node->gate, gate)) {
581 gateStack.push(node->gate);
582 }
583 }
584 while (!gateStack.empty()) {
585 that = that->UpdateNode(gateStack.top());
586 gateStack.pop();
587 }
588 return that;
589 }
590 } // namespace panda::ecmascript::kungfu
591