1 /*
2  * Copyright (c) 2024 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/escape_analysis.h"
17 
18 namespace panda::ecmascript::kungfu {
19 
VirtualObject(size_t numIn, Chunk* chunk)20 VirtualObject::VirtualObject(size_t numIn, Chunk* chunk)
21     : fields_(chunk), users_(chunk)
22 {
23     for (size_t i = 0; i < numIn; i++) {
24         FieldLocation tmp;
25         fields_.emplace_back(tmp);
26     }
27 }
28 
SetEscaped()29 void VirtualObject::SetEscaped()
30 {
31     escaped_ = true;
32 }
33 
IsEscaped() const34 bool VirtualObject::IsEscaped() const
35 {
36     return escaped_;
37 }
38 
GetField(size_t offset)39 FieldLocation VirtualObject::GetField(size_t offset)
40 {
41     constexpr size_t fieldSize = 8;
42     ASSERT(offset % fieldSize == 0);
43     if (offset / fieldSize >= fields_.size()) {
44         return FieldLocation::Invalid();
45     }
46     return fields_.at(offset/fieldSize);
47 }
48 
49 size_t FieldLocation::maxid = 0;
50 
GetUsers()51 ChunkVector<GateRef>& VirtualObject::GetUsers()
52 {
53     return users_;
54 }
55 
ClearUsers()56 void VirtualObject::ClearUsers()
57 {
58     users_.clear();
59 }
60 
AddUser(GateRef gate)61 void VirtualObject::AddUser(GateRef gate)
62 {
63     users_.push_back(gate);
64 }
65 
GateInfo(Circuit* circuit, GateRef curGate, EscapeAnalysis* escapeAnalysis, Chunk* chunk)66 GateInfo::GateInfo(Circuit* circuit, GateRef curGate, EscapeAnalysis* escapeAnalysis, Chunk* chunk)
67     : circuit_(circuit), acc_(circuit), curGate_(curGate), escapeAnalysis_(escapeAnalysis), state_(chunk)
68 {
69     if (acc_.GetOpCode(curGate) == OpCode::DEPEND_SELECTOR) {
70         state_ = MergeState(curGate);
71     } else {
72         ASSERT(acc_.GetDependCount(curGate) <= 1);
73         if (acc_.GetDependCount(curGate) == 1) {
74             state_ = escapeAnalysis_->GetOrCreateState(acc_.GetDep(curGate));
75         }
76     }
77 }
78 
~GateInfo()79 GateInfo::~GateInfo()
80 {
81     State& preState = escapeAnalysis_->GetOrCreateState(curGate_);
82     if (state_.IsMapEqual(preState) ||
83         object_ != escapeAnalysis_->TryGetVirtualObject(curGate_)) {
84         escapeAnalysis_->SetReplaceGate(curGate_);
85     }
86     escapeAnalysis_->SetState(curGate_, state_);
87 
88     escapeAnalysis_->SetVirtualObject(curGate_, object_);
89     escapeAnalysis_->SetReplacement(curGate_, replacement_);
90 }
91 
GetCurrentGate() const92 GateRef GateInfo::GetCurrentGate() const
93 {
94     return curGate_;
95 }
96 
MergeState(GateRef gate)97 State GateInfo::MergeState(GateRef gate)
98 {
99     size_t numIn = acc_.GetDependCount(gate);
100     State& preState = escapeAnalysis_->GetOrCreateState(acc_.GetDep(gate, 0));
101     State result = preState;
102     for (auto fieldValue : preState) {
103         FieldLocation field = fieldValue.first;
104         GateRef value = fieldValue.second;
105         ASSERT(field != FieldLocation::Invalid());
106         if (value != Circuit::NullGate()) {
107             std::vector<GateRef> input;
108             input.push_back(acc_.GetState(gate));
109             input.push_back(value);
110             size_t numAliveState = 1;
111             bool differentFlag = false;
112             for (size_t i = 1; i < numIn; i++) {
113                 State& inputState = escapeAnalysis_->GetOrCreateState(acc_.GetDep(gate, i));
114                 GateRef inputValue = inputState.GetFieldValue(field);
115                 if (inputValue != Circuit::NullGate()) {
116                     numAliveState++;
117                 }
118                 if (inputValue != value) {
119                     differentFlag = true;
120                 }
121                 input.push_back(inputValue);
122             }
123 
124             if (numAliveState == 1 && acc_.GetOpCode(acc_.GetState(gate)) == OpCode::LOOP_BEGIN) {
125                 result.SetFieldValue(field, value);
126             } else if (numAliveState < numIn) {
127                 result.SetFieldValue(field, Circuit::NullGate());
128             } else {
129                 if (!differentFlag) {
130                     result.SetFieldValue(field, value);
131                 } else {
132                     State& oldState = escapeAnalysis_->GetOrCreateState(gate);
133                     GateRef oldValue = oldState.GetFieldValue(field);
134                     if (oldValue != Circuit::NullGate() &&
135                         acc_.GetOpCode(oldValue) == OpCode::DEPEND_SELECTOR &&
136                         acc_.GetState(oldValue) == acc_.GetState(gate)) {
137                         for (size_t i = 0; i < numIn; i++) {
138                             ASSERT(input[i + 1] != Circuit::NullGate());
139                             if (acc_.GetValueIn(oldValue, i) != input[i + 1]) {
140                                 acc_.ReplaceValueIn(oldValue, input[i + 1], i);
141                                 escapeAnalysis_->RevisitGate(oldValue);
142                             }
143                         }
144                         result.SetFieldValue(field, oldValue);
145                     } else {
146                         MachineType machineType = acc_.GetMachineType(value);
147                         auto gateType = acc_.GetGateType(value);
148                         const GateMetaData* meta = circuit_->ValueSelector(numIn);
149                         GateRef valueSelector = circuit_->NewGate(meta, machineType, input.size(),
150                                                                   input.data(), gateType);
151                         result.SetFieldValue(field, valueSelector);
152                         escapeAnalysis_->RevisitGate(valueSelector);
153                     }
154                 }
155             }
156         }
157     }
158     return result;
159 }
160 
GetFieldValue(FieldLocation field) const161 GateRef GateInfo::GetFieldValue(FieldLocation field) const
162 {
163     return state_.GetFieldValue(field);
164 }
165 
SetFieldValue(FieldLocation field, GateRef value)166 void GateInfo::SetFieldValue(FieldLocation field, GateRef value)
167 {
168     state_.SetFieldValue(field, value);
169 }
170 
SetEliminated()171 void GateInfo::SetEliminated()
172 {
173     replacement_ = circuit_->DeadGate();
174     object_ = nullptr;
175 }
176 
SetReplacement(GateRef replacement)177 void GateInfo::SetReplacement(GateRef replacement)
178 {
179     replacement_ = replacement;
180     object_ = escapeAnalysis_->TryGetVirtualObject(replacement);
181 }
182 
SetVirtualObject(VirtualObject* object)183 void GateInfo::SetVirtualObject(VirtualObject* object)
184 {
185     object_ = object;
186 }
187 
SetFieldValue(FieldLocation field, GateRef gate)188 void State::SetFieldValue(FieldLocation field, GateRef gate)
189 {
190     map_[field] = gate;
191 }
192 
GetFieldValue(FieldLocation field) const193 GateRef State::GetFieldValue(FieldLocation field) const
194 {
195     auto result = map_.find(field);
196     if (result == map_.end()) {
197         return Circuit::NullGate();
198     }
199     return result->second;
200 }
201 
IsMapEqual(const State &state) const202 bool State::IsMapEqual(const State &state) const
203 {
204     return map_.size() == state.map_.size() &&
205            std::equal(map_.begin(), map_.end(), state.map_.begin());
206 }
207 
SetReplacement(GateRef gate, GateRef replacement)208 void EscapeAnalysis::SetReplacement(GateRef gate, GateRef replacement)
209 {
210     replacements_[gate] = replacement;
211 }
212 
TryGetReplacement(GateRef gate) const213 GateRef EscapeAnalysis::TryGetReplacement(GateRef gate) const
214 {
215     if (!replacements_.count(gate)) {
216         return Circuit::NullGate();
217     }
218     return replacements_.at(gate);
219 }
220 
GetCurrentGate(GateRef gate) const221 GateRef EscapeAnalysis::GetCurrentGate(GateRef gate) const
222 {
223     GateRef replacement = TryGetReplacement(gate);
224     if (replacement == Circuit::NullGate()) {
225         return gate;
226     }
227     return replacement;
228 }
229 
TryGetVirtualObject(GateRef gate) const230 VirtualObject* EscapeAnalysis::TryGetVirtualObject(GateRef gate) const
231 {
232     if (gateToVirtualObject_.count(gate)) {
233         VirtualObject* vObj = gateToVirtualObject_.at(gate);
234         return vObj;
235     }
236     return nullptr;
237 }
238 
TryGetVirtualObjectAndAddUser(GateRef gate, GateRef currentGate)239 VirtualObject* EscapeAnalysis::TryGetVirtualObjectAndAddUser(GateRef gate, GateRef currentGate)
240 {
241     if (gateToVirtualObject_.count(gate)) {
242         VirtualObject* vObj = gateToVirtualObject_[gate];
243         if (vObj != nullptr) {
244             vObj->AddUser(currentGate);
245         }
246         return vObj;
247     }
248     return nullptr;
249 }
250 
251 
SetVirtualObject(GateRef gate, VirtualObject* object)252 void EscapeAnalysis::SetVirtualObject(GateRef gate, VirtualObject* object)
253 {
254     gateToVirtualObject_[gate] = object;
255 }
256 
RevisitUser(VirtualObject* vObj)257 void EscapeAnalysis::RevisitUser(VirtualObject* vObj)
258 {
259     auto& users = vObj->GetUsers();
260     for (auto user : users) {
261         if (isTraced_) {
262             LOG_COMPILER(INFO) << "[escape analysis] revisit user : " <<acc_.GetId(user);
263         }
264         visitor_->ReVisitGate(user);
265     }
266     vObj->ClearUsers();
267 }
268 
SetEscaped(GateRef gate)269 void EscapeAnalysis::SetEscaped(GateRef gate)
270 {
271     if (isTraced_) {
272         LOG_COMPILER(INFO) << "[escape analysis] set escaped: " << acc_.GetId(gate);
273     }
274     VirtualObject* vObj = TryGetVirtualObject(gate);
275     if (vObj != nullptr && !vObj->IsEscaped()) {
276         vObj->SetEscaped();
277         RevisitUser(vObj);
278     }
279 }
280 
GetOrCreateVirtualObject(size_t numIn, GateInfo* info)281 VirtualObject* EscapeAnalysis::GetOrCreateVirtualObject(size_t numIn, GateInfo* info)
282 {
283     GateRef gate = info->GetCurrentGate();
284     VirtualObject* vobj = TryGetVirtualObject(gate);
285     if (vobj == nullptr) {
286         vobj = chunk_->New<VirtualObject>(numIn, chunk_);
287     }
288     vobj->AddUser(gate);
289     info->SetVirtualObject(vobj);
290     return vobj;
291 }
292 
293 
VisitCreateObjectWithBuffer(GateRef gate, GateInfo* info)294 GateRef EscapeAnalysis::VisitCreateObjectWithBuffer(GateRef gate, GateInfo* info)
295 {
296     constexpr size_t startIn = 4; // 4 : start of props
297     constexpr size_t fieldSize = 8; // 8 : bytes per field
298     constexpr size_t stride = 2; // 2: offset and value
299     auto numIn = acc_.GetNumValueIn(gate);
300     size_t size = acc_.GetConstantValue(acc_.GetValueIn(gate, 0)) / fieldSize;
301     VirtualObject* vObj = GetOrCreateVirtualObject(size, info);
302 
303     for (size_t i = startIn; i < numIn; i += stride) {
304         GateRef value = acc_.GetValueIn(gate, i);
305         GateRef offset = acc_.GetValueIn(gate, i + 1);
306         if (vObj != nullptr && !vObj->IsEscaped() &&
307             vObj->GetField(acc_.GetConstantValue(offset)) != FieldLocation::Invalid()) {
308             info->SetFieldValue(vObj->GetField(acc_.GetConstantValue(offset)), value);
309         } else {
310             SetEscaped(value);
311             SetEscaped(gate);
312         }
313     }
314     info->SetVirtualObject(vObj);
315     return replaceGate_;
316 }
317 
VisitLoadProperty(GateRef gate, GateInfo* info)318 GateRef EscapeAnalysis::VisitLoadProperty(GateRef gate, GateInfo* info)
319 {
320     GateRef object = acc_.GetValueIn(gate, 0);
321     GateRef offset = acc_.GetValueIn(gate, 1);
322     VirtualObject* vObj = TryGetVirtualObjectAndAddUser(object, gate);
323 
324     PropertyLookupResult plr(acc_.GetConstantValue(offset));
325 
326     if (vObj != nullptr && !vObj->IsEscaped() && vObj->GetField(plr.GetOffset()) != FieldLocation::Invalid()) {
327         GateRef value = info->GetFieldValue(vObj->GetField(plr.GetOffset()));
328         if (value != Circuit::NullGate()) {
329             if (isTraced_) {
330                 LOG_COMPILER(INFO) << "[escape analysis] replace" << acc_.GetId(gate) << " with " << acc_.GetId(value);
331             }
332             info->SetReplacement(value);
333         } else {
334             SetEscaped(object);
335         }
336     } else {
337         SetEscaped(object);
338     }
339     return replaceGate_;
340 }
341 
VisitLoadConstOffset(GateRef gate, GateInfo* info)342 GateRef EscapeAnalysis::VisitLoadConstOffset(GateRef gate, GateInfo* info)
343 {
344     GateRef object = acc_.GetValueIn(gate, 0);
345     size_t offset = acc_.GetOffset(gate);
346     VirtualObject* vObj = TryGetVirtualObjectAndAddUser(object, gate);
347 
348     if (vObj != nullptr && !vObj->IsEscaped() && vObj->GetField(offset) != FieldLocation::Invalid()) {
349         GateRef value = info->GetFieldValue(vObj->GetField(offset));
350         if (value != Circuit::NullGate()) {
351             if (isTraced_) {
352                 LOG_COMPILER(INFO) << "[escape analysis] replace " <<
353                                       acc_.GetId(gate) << " with " << acc_.GetId(value);
354             }
355             info->SetReplacement(value);
356         } else {
357             SetEscaped(object);
358         }
359     } else {
360         SetEscaped(object);
361     }
362     return replaceGate_;
363 }
364 
365 
VisitStoreProperty(GateRef gate, GateInfo* info)366 GateRef EscapeAnalysis::VisitStoreProperty(GateRef gate, GateInfo* info)
367 {
368     GateRef object = acc_.GetValueIn(gate, 0);
369     GateRef offset = acc_.GetValueIn(gate, 1);
370     GateRef value = acc_.GetValueIn(gate, 2);
371     VirtualObject* vObj = TryGetVirtualObjectAndAddUser(object, gate);
372     PropertyLookupResult plr(acc_.GetConstantValue(offset));
373 
374     if (vObj != nullptr && !vObj->IsEscaped() && vObj->GetField(plr.GetOffset()) != FieldLocation::Invalid()) {
375         info->SetFieldValue(vObj->GetField(plr.GetOffset()), value);
376         info->SetEliminated();
377     } else {
378         SetEscaped(value);
379         SetEscaped(object);
380     }
381     return replaceGate_;
382 }
383 
VisitObjectTypeCheck(GateRef gate, GateInfo* info)384 GateRef EscapeAnalysis::VisitObjectTypeCheck(GateRef gate, GateInfo* info)
385 {
386     info->SetVirtualObject(TryGetVirtualObject(acc_.GetValueIn(gate)));
387     return Circuit::NullGate();
388 }
389 
GetOrCreateState(GateRef gate)390 State& EscapeAnalysis::GetOrCreateState(GateRef gate)
391 {
392     auto it = gateToState_.find(gate);
393     if (it == gateToState_.end()) {
394         State tmp(chunk_);
395         auto result = gateToState_.insert(std::make_pair(gate, std::move(tmp)));
396         return result.first->second;
397     }
398     return it->second;
399 }
400 
SetState(GateRef gate, State state)401 void EscapeAnalysis::SetState(GateRef gate, State state)
402 {
403     auto it = gateToState_.find(gate);
404     if (it == gateToState_.end()) {
405         gateToState_.insert(std::make_pair(gate, std::move(state)));
406     } else {
407         it->second = state;
408     }
409 }
410 
RevisitGate(GateRef gate)411 void EscapeAnalysis::RevisitGate(GateRef gate)
412 {
413     visitor_->ReVisitGate(gate);
414 }
415 
SetReplaceGate(GateRef gate)416 void EscapeAnalysis::SetReplaceGate(GateRef gate)
417 {
418     replaceGate_ = gate;
419 }
420 
VisitGate(GateRef gate)421 GateRef EscapeAnalysis::VisitGate(GateRef gate)
422 {
423     GateInfo info(circuit_, gate, this, chunk_);
424     auto opcode = acc_.GetOpCode(gate);
425     replaceGate_ = Circuit::NullGate();
426     switch (opcode) {
427         case OpCode::STORE_PROPERTY:
428         case OpCode::STORE_PROPERTY_NO_BARRIER:
429             return VisitStoreProperty(gate, &info);
430         case OpCode::LOAD_PROPERTY:
431             return VisitLoadProperty(gate, &info);
432         case OpCode::LOAD_CONST_OFFSET:
433             return VisitLoadConstOffset(gate, &info);
434         case OpCode::TYPED_CREATE_OBJ_WITH_BUFFER:
435             return VisitCreateObjectWithBuffer(gate, &info);
436         case OpCode::OBJECT_TYPE_CHECK:
437         case OpCode::CHECK_AND_CONVERT:
438             return VisitObjectTypeCheck(gate, &info);
439         case OpCode::FRAME_VALUES:
440         case OpCode::STATE_SPLIT:
441         case OpCode::FRAME_STATE:
442             break;
443         default : {
444             size_t numIns = acc_.GetNumValueIn(gate);
445             for (size_t i = 0; i < numIns; i++) {
446                 GateRef in = GetCurrentGate(acc_.GetValueIn(gate, i));
447                 SetEscaped(in);
448             }
449         }
450     }
451     return Circuit::NullGate();
452 }
453 
454 }