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::STABLE_ARRAY_CHECK:
76 case OpCode::INDEX_CHECK:
77 case OpCode::ELEMENTSKIND_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::INTERN_STRING_CHECK:
89 case OpCode::BUILTIN_PROTOTYPE_HCLASS_CHECK:
90 case OpCode::TYPE_OF_CHECK:
91 case OpCode::ARRAY_CONSTRUCTOR_CHECK:
92 case OpCode::FLOAT32_ARRAY_CONSTRUCTOR_CHECK:
93 case OpCode::OBJECT_CONSTRUCTOR_CHECK:
94 case OpCode::BOOLEAN_CONSTRUCTOR_CHECK:
95 case OpCode::PROTO_CHANGE_MARKER_CHECK:
96 case OpCode::MONO_LOAD_PROPERTY_ON_PROTO:
97 case OpCode::LOAD_BUILTIN_OBJECT:
98 case OpCode::LOOK_UP_HOLDER:
99 case OpCode::IS_CALLABLE_CHECK:
100 case OpCode::MATH_HCLASS_CONSISTENCY_CHECK:
101 return TryEliminateGate(gate);
102 case OpCode::STATE_SPLIT:
103 if (enableFrameStateElimination_) {
104 return TryEliminateFrameState(gate);
105 }
106 break;
107 case OpCode::DEPEND_SELECTOR:
108 return TryEliminateDependSelector(gate);
109 default:
110 if (acc_.GetDependCount(gate) == 1) { // 1: depend in is 1
111 return TryEliminateOther(gate);
112 }
113 break;
114 }
115 return Circuit::NullGate();
116 }
117
TryEliminateOther(GateRef gate)118 GateRef EarlyElimination::TryEliminateOther(GateRef gate)
119 {
120 ASSERT(acc_.GetDependCount(gate) >= 1);
121 auto depIn = acc_.GetDep(gate);
122 auto dependChain = GetDependChain(depIn);
123 if (dependChain == nullptr) {
124 return Circuit::NullGate();
125 }
126
127 if (!acc_.IsNotWrite(gate)) {
128 dependChain = UpdateWrite(gate, dependChain);
129 }
130
131 return UpdateDependChain(gate, dependChain);
132 }
133
TryEliminateGate(GateRef gate)134 GateRef EarlyElimination::TryEliminateGate(GateRef gate)
135 {
136 ASSERT(acc_.GetDependCount(gate) == 1);
137 auto depIn = acc_.GetDep(gate);
138 auto dependChain = GetDependChain(depIn);
139 // dependChain is null
140 if (dependChain == nullptr) {
141 return Circuit::NullGate();
142 }
143
144 if (!acc_.IsNotWrite(gate)) {
145 dependChain = UpdateWrite(gate, dependChain);
146 return UpdateDependChain(gate, dependChain);
147 }
148
149 auto numIns = acc_.GetNumValueIn(gate);
150 for (size_t i = 0; i < numIns; ++i) {
151 auto origin = acc_.GetValueIn(gate, i);
152 auto checkd = dependChain->LookupCheckedNode(this, origin);
153 if (origin != checkd) {
154 acc_.ReplaceValueIn(gate, checkd, i);
155 }
156 }
157
158 // lookup gate, replace
159 auto preGate = dependChain->LookupNode(this, gate);
160 if (preGate != Circuit::NullGate()) {
161 return preGate;
162 }
163 // update gate, for others elimination
164 dependChain = dependChain->UpdateNode(gate);
165 return UpdateDependChain(gate, dependChain);
166 }
167
TryEliminateFrameState(GateRef gate)168 GateRef EarlyElimination::TryEliminateFrameState(GateRef gate)
169 {
170 ASSERT(acc_.GetOpCode(gate) == OpCode::STATE_SPLIT);
171 auto depIn = acc_.GetDep(gate);
172 auto dependChain = GetDependChain(depIn);
173 // dependChain is null
174 if (dependChain == nullptr) {
175 return Circuit::NullGate();
176 }
177 // lookup gate, replace
178 auto preFrame = dependChain->LookupFrameState();
179 auto curFrame = acc_.GetFrameState(gate);
180 if ((preFrame != Circuit::NullGate()) && (preFrame != curFrame) &&
181 acc_.GetFrameState(preFrame) == acc_.GetFrameState(curFrame)) {
182 acc_.UpdateAllUses(curFrame, preFrame);
183 auto frameValues = acc_.GetValueIn(curFrame, 1); // 1: frameValues
184 acc_.DeleteGate(frameValues);
185 acc_.DeleteGate(curFrame);
186 return depIn;
187 } else {
188 dependChain = dependChain->UpdateFrameState(curFrame);
189 }
190 // update gate, for others elimination
191
192 return UpdateDependChain(gate, dependChain);
193 }
194
TryEliminateDependSelector(GateRef gate)195 GateRef EarlyElimination::TryEliminateDependSelector(GateRef gate)
196 {
197 auto state = acc_.GetState(gate);
198 if (acc_.IsLoopHead(state)) {
199 auto dependChain = GetLoopDependInfo(gate);
200 if (dependChain == nullptr) {
201 return Circuit::NullGate();
202 }
203 return UpdateDependChain(gate, dependChain);
204 }
205
206 auto dependCount = acc_.GetDependCount(gate);
207 for (size_t i = 0; i < dependCount; ++i) {
208 auto depend = acc_.GetDep(gate, i);
209 auto dependChain = GetDependChain(depend);
210 if (dependChain == nullptr) {
211 return Circuit::NullGate();
212 }
213 }
214
215 // all depend done.
216 auto depend = acc_.GetDep(gate);
217 auto dependChain = GetDependChain(depend);
218 DependInfoNode* copy = new (chunk_) DependInfoNode(chunk_);
219 copy->CopyFrom(dependChain);
220 for (size_t i = 1; i < dependCount; ++i) { // 1: second in
221 auto dependIn = acc_.GetDep(gate, i);
222 auto tempChain = GetDependChain(dependIn);
223 copy->Merge(this, tempChain);
224 }
225 return UpdateDependChain(gate, copy);
226 }
227
UpdateDependChain(GateRef gate,DependInfoNode * dependChain)228 GateRef EarlyElimination::UpdateDependChain(GateRef gate, DependInfoNode* dependChain)
229 {
230 ASSERT(dependChain != nullptr);
231 auto oldDependChain = GetDependChain(gate);
232 if (dependChain->Equals(oldDependChain)) {
233 return Circuit::NullGate();
234 }
235 dependChains_[acc_.GetId(gate)] = dependChain;
236 return gate;
237 }
238
UpdateWrite(GateRef gate,DependInfoNode * dependInfo)239 DependInfoNode* EarlyElimination::UpdateWrite(GateRef gate, DependInfoNode* dependInfo)
240 {
241 if (!enableMemoryAnalysis_) {
242 return new (chunk_) DependInfoNode(chunk_);
243 }
244 auto op = acc_.GetOpCode(gate);
245 switch (op) {
246 case OpCode::STORE_PROPERTY:
247 case OpCode::STORE_PROPERTY_NO_BARRIER:
248 case OpCode::STORE_CONST_OFFSET:
249 case OpCode::STORE_ELEMENT:
250 case OpCode::STORE_MEMORY:
251 case OpCode::MIGRATE_ARRAY_WITH_KIND:
252 case OpCode::MONO_STORE_PROPERTY_LOOK_UP_PROTO:
253 case OpCode::MONO_STORE_PROPERTY:
254 return dependInfo->UpdateStoreProperty(this, gate);
255 default:
256 return new (chunk_) DependInfoNode(chunk_);
257 }
258 }
259
MayAccessOneMemory(GateRef lhs,GateRef rhs)260 bool EarlyElimination::MayAccessOneMemory(GateRef lhs, GateRef rhs)
261 {
262 auto rop = acc_.GetOpCode(rhs);
263 auto lop = acc_.GetOpCode(lhs);
264 switch (rop) {
265 case OpCode::STORE_MEMORY:
266 ASSERT(acc_.GetMemoryType(rhs) == MemoryType::ELEMENT_TYPE);
267 return acc_.GetOpCode(lhs) == OpCode::LOAD_ELEMENT;
268 case OpCode::MIGRATE_ARRAY_WITH_KIND: {
269 if (lop == OpCode::LOAD_ELEMENT) {
270 GateRef lopValueIn = acc_.GetValueIn(lhs, 0); // loadelement receiver
271 GateRef ropValueIn = acc_.GetValueIn(rhs, 0); // migrate receiver
272 return lopValueIn == ropValueIn;
273 }
274 return false;
275 }
276 case OpCode::STORE_ELEMENT: {
277 if (lop == OpCode::LOAD_ELEMENT) {
278 bool lopIsTypedArray = acc_.TypedOpIsTypedArray(lhs, TypedOpKind::TYPED_LOAD_OP);
279 bool ropIsTypedArray = acc_.TypedOpIsTypedArray(rhs, TypedOpKind::TYPED_STORE_OP);
280 return lopIsTypedArray == ropIsTypedArray;
281 }
282 return false;
283 }
284 case OpCode::STORE_PROPERTY:
285 case OpCode::STORE_PROPERTY_NO_BARRIER: {
286 if (lop == OpCode::LOAD_PROPERTY) {
287 auto loff = acc_.GetValueIn(lhs, 1);
288 auto roff = acc_.GetValueIn(rhs, 1);
289 ASSERT(acc_.GetOpCode(loff) == OpCode::CONSTANT);
290 ASSERT(acc_.GetOpCode(roff) == OpCode::CONSTANT);
291 return loff == roff;
292 } else if (lop == OpCode::PROTOTYPE_CHECK) {
293 auto lindex = acc_.GetHClassIndex(lhs);
294 auto rindex = acc_.GetHClassIndex(rhs);
295 return (lindex == 0) || (rindex == 0) || (lindex != rindex);
296 }
297 break;
298 }
299 case OpCode::STORE_CONST_OFFSET: {
300 if (lop == OpCode::LOAD_CONST_OFFSET) {
301 auto loff = acc_.GetOffset(lhs);
302 auto roff = acc_.GetOffset(rhs);
303 return loff == roff;
304 }
305 break;
306 }
307 case OpCode::LOAD_PROPERTY:
308 case OpCode::MONO_LOAD_PROPERTY_ON_PROTO:
309 if (acc_.GetGateType(lhs).Value() != acc_.GetGateType(rhs).Value()) {
310 return false;
311 }
312 break;
313 default:
314 break;
315 }
316 return false;
317 }
318
CompareOrder(GateRef lhs,GateRef rhs)319 bool EarlyElimination::CompareOrder(GateRef lhs, GateRef rhs)
320 {
321 return visitor_->GetGateOrder(lhs) < visitor_->GetGateOrder(rhs);
322 }
323
CheckReplacement(GateRef lhs,GateRef rhs)324 bool EarlyElimination::CheckReplacement(GateRef lhs, GateRef rhs)
325 {
326 if (!acc_.MetaDataEqu(lhs, rhs)) {
327 if (acc_.GetOpCode(lhs) != acc_.GetOpCode(rhs)) {
328 return false;
329 }
330 }
331
332 size_t valueCount = acc_.GetNumValueIn(lhs);
333 for (size_t i = 0; i < valueCount; i++) {
334 if (Rename(acc_.GetValueIn(lhs, i)) != Rename(acc_.GetValueIn(rhs, i))) {
335 return false;
336 }
337 }
338
339 auto opcode = acc_.GetOpCode(lhs);
340 switch (opcode) {
341 case OpCode::LOAD_ELEMENT: {
342 if (acc_.GetTypedLoadOp(lhs) != acc_.GetTypedLoadOp(rhs)) {
343 return false;
344 }
345 break;
346 }
347 case OpCode::TYPED_BINARY_OP: {
348 auto lhsOp = acc_.GetTypedBinaryOp(lhs);
349 auto rhsOp = acc_.GetTypedBinaryOp(rhs);
350 if (lhsOp != rhsOp) {
351 return false;
352 }
353 break;
354 }
355 case OpCode::TYPED_UNARY_OP: {
356 auto lhsOp = acc_.GetTypedUnAccessor(lhs).GetTypedUnOp();
357 auto rhsOp = acc_.GetTypedUnAccessor(rhs).GetTypedUnOp();
358 if (lhsOp != rhsOp) {
359 return false;
360 }
361 break;
362 }
363 case OpCode::TYPED_ARRAY_CHECK: {
364 TypedArrayMetaDataAccessor lhsAccessor = acc_.GetTypedArrayMetaDataAccessor(lhs);
365 TypedArrayMetaDataAccessor rhsAccessor = acc_.GetTypedArrayMetaDataAccessor(rhs);
366 if ((lhsAccessor.GetParamType() != rhsAccessor.GetParamType())) {
367 return false;
368 }
369
370 OnHeapMode lMode = lhsAccessor.GetOnHeapMode();
371 OnHeapMode rMode = rhsAccessor.GetOnHeapMode();
372 // When the onheapmode types of the two checks are inconsistent and one type is none, the none type will be
373 // updated to another type. Because there is no side effect between the two checks, the onheapmode types are
374 // also consistent.
375 if (lMode != rMode) {
376 if (OnHeap::IsNone(lMode)) {
377 acc_.UpdateOnHeapMode(lhs, rMode);
378 return true;
379 } else if (OnHeap::IsNone(rMode)) {
380 acc_.UpdateOnHeapMode(rhs, lMode);
381 return true;
382 }
383 return false;
384 }
385 break;
386 }
387 case OpCode::TYPE_OF_CHECK: {
388 if (acc_.GetParamType(lhs) != acc_.GetParamType(rhs)) {
389 return false;
390 }
391 break;
392 }
393 case OpCode::PROTOTYPE_CHECK: {
394 if (acc_.GetHClassIndex(lhs) != acc_.GetHClassIndex(rhs)) {
395 return false;
396 }
397 break;
398 }
399 case OpCode::LOAD_CONST_OFFSET: {
400 if (acc_.GetOffset(lhs) != acc_.GetOffset(rhs)) {
401 return false;
402 }
403 if (acc_.GetMachineType(lhs) != acc_.GetMachineType(rhs)) {
404 return false;
405 }
406 if (acc_.GetMemoryAttribute(lhs).Value() != acc_.GetMemoryAttribute(rhs).Value()) {
407 return false;
408 }
409 break;
410 }
411 case OpCode::LOAD_HCLASS_FROM_CONSTPOOL: {
412 if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) {
413 return false;
414 }
415 break;
416 }
417 case OpCode::JSINLINETARGET_TYPE_CHECK: {
418 if (acc_.GetFuncGT(lhs) != acc_.GetFuncGT(rhs)) {
419 return false;
420 }
421 break;
422 }
423 case OpCode::ARRAY_CONSTRUCTOR_CHECK:
424 case OpCode::FLOAT32_ARRAY_CONSTRUCTOR_CHECK:
425 case OpCode::OBJECT_CONSTRUCTOR_CHECK:
426 case OpCode::BOOLEAN_CONSTRUCTOR_CHECK: {
427 if (acc_.GetValueIn(lhs) != acc_.GetValueIn(rhs)) {
428 return false;
429 }
430 break;
431 }
432 case OpCode::LOAD_BUILTIN_OBJECT: {
433 if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) {
434 return false;
435 }
436 break;
437 }
438 default:
439 break;
440 }
441 return true;
442 }
443
CheckRenameReplacement(GateRef lhs,GateRef rhs)444 bool EarlyElimination::CheckRenameReplacement(GateRef lhs, GateRef rhs)
445 {
446 auto opcode = acc_.GetOpCode(lhs);
447 switch (opcode) {
448 case OpCode::INDEX_CHECK: {
449 auto index = acc_.GetValueIn(lhs, 1);
450 if (Rename(index) == Rename(rhs)) {
451 return true;
452 }
453 break;
454 }
455 default:
456 break;
457 }
458 return false;
459 }
460
Rename(GateRef gate)461 GateRef EarlyElimination::Rename(GateRef gate)
462 {
463 ChunkStack<GateRef> gateStack(chunk_);
464 while (true) {
465 auto op = acc_.GetOpCode(gate);
466 bool renamed = false;
467 switch (op) {
468 case OpCode::INDEX_CHECK: {
469 GateRef ans = renames_[acc_.GetId(gate)];
470 if (ans == Circuit::NullGate()) {
471 renamed = true;
472 gateStack.push(gate);
473 gate = acc_.GetValueIn(gate, 1);
474 } else {
475 gate = ans;
476 }
477 break;
478 }
479 default:
480 break;
481 }
482 if (!renamed) {
483 break;
484 }
485 }
486 while (!gateStack.empty()) {
487 auto topGate = gateStack.top();
488 gateStack.pop();
489 renames_[acc_.GetId(topGate)] = gate;
490 }
491 return gate;
492 }
493
Merge(EarlyElimination * elimination,DependInfoNode * that)494 void DependInfoNode::Merge(EarlyElimination* elimination, DependInfoNode* that)
495 {
496 auto siz = this->size_; // size of lhs-chain
497 auto lhs = this->head_;
498 auto rhs = that->head_;
499 ChunkStack<GateRef> gateStack(chunk_);
500 while (lhs != rhs) {
501 if (lhs == nullptr || rhs == nullptr) {
502 siz = 0;
503 lhs = nullptr;
504 break;
505 } else if (lhs->gate == rhs->gate) {
506 gateStack.push(lhs->gate);
507 siz--;
508 lhs = lhs->next;
509 rhs = rhs->next;
510 } else if (elimination->CompareOrder(lhs->gate, rhs->gate)) {
511 rhs = rhs->next;
512 } else {
513 siz--;
514 lhs = lhs->next;
515 }
516 }
517 // lhs : common suffix of lhs-chain and rhs-chain
518 this->head_ = lhs;
519 this->size_ = siz;
520 while (!gateStack.empty()) {
521 Node* node = chunk_->New<Node>(gateStack.top(), head_);
522 gateStack.pop();
523 this->size_++;
524 this->head_ = node;
525 }
526 if (this->frameState_ != that->frameState_) {
527 this->frameState_ = Circuit::NullGate();
528 }
529 }
530
Equals(DependInfoNode * that)531 bool DependInfoNode::Equals(DependInfoNode* that)
532 {
533 if (that == nullptr) {
534 return false;
535 }
536 if (size_ != that->size_ || frameState_ != that->frameState_) {
537 return false;
538 }
539 auto lhs = this->head_;
540 auto rhs = that->head_;
541 while (lhs != rhs) {
542 if (lhs->gate != rhs->gate) {
543 return false;
544 }
545 lhs = lhs->next;
546 rhs = rhs->next;
547 }
548 return true;
549 }
550
LookupFrameState() const551 GateRef DependInfoNode::LookupFrameState() const
552 {
553 return frameState_;
554 }
555
LookupCheckedNode(EarlyElimination * elimination,GateRef gate)556 GateRef DependInfoNode::LookupCheckedNode(EarlyElimination* elimination, GateRef gate)
557 {
558 for (Node* node = head_; node != nullptr; node = node->next) {
559 if (elimination->CheckRenameReplacement(node->gate, gate)) {
560 return node->gate;
561 }
562 }
563 return gate;
564 }
565
GetGates(std::vector<GateRef> & gates) const566 void DependInfoNode::GetGates(std::vector<GateRef>& gates) const
567 {
568 ChunkStack<GateRef> st(chunk_);
569 for (Node* node = head_; node != nullptr; node = node->next) {
570 st.push(node->gate);
571 }
572 while (!st.empty()) {
573 gates.emplace_back(st.top());
574 st.pop();
575 }
576 }
577
LookupNode(EarlyElimination * elimination,GateRef gate)578 GateRef DependInfoNode::LookupNode(EarlyElimination* elimination, GateRef gate)
579 {
580 for (Node* node = head_; node != nullptr; node = node->next) {
581 if (elimination->CheckReplacement(node->gate, gate)) {
582 return node->gate;
583 }
584 }
585 return Circuit::NullGate();
586 }
587
UpdateNode(GateRef gate)588 DependInfoNode* DependInfoNode::UpdateNode(GateRef gate)
589 {
590 // assign node->next to head
591 Node* node = chunk_->New<Node>(gate, head_);
592 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
593 // assign head to node
594 that->head_ = node;
595 that->size_ = size_ + 1;
596 that->frameState_ = frameState_;
597 return that;
598 }
599
UpdateFrameState(GateRef framestate)600 DependInfoNode* DependInfoNode::UpdateFrameState(GateRef framestate)
601 {
602 // assign node->next to head
603 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
604 // assign head to node
605 that->head_ = head_;
606 that->size_ = size_;
607 that->frameState_ = framestate;
608 return that;
609 }
610
UpdateStoreProperty(EarlyElimination * elimination,GateRef gate)611 DependInfoNode* DependInfoNode::UpdateStoreProperty(EarlyElimination* elimination, GateRef gate)
612 {
613 DependInfoNode* that = new (chunk_) DependInfoNode(chunk_);
614 ChunkStack<GateRef> gateStack(chunk_);
615 for (Node* node = head_; node != nullptr; node = node->next) {
616 if (!elimination->MayAccessOneMemory(node->gate, gate)) {
617 gateStack.push(node->gate);
618 }
619 }
620 while (!gateStack.empty()) {
621 that = that->UpdateNode(gateStack.top());
622 gateStack.pop();
623 }
624 return that;
625 }
626 } // namespace panda::ecmascript::kungfu
627