1 /*
2 * Copyright (c) 2021 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/scheduler.h"
17 #include <cmath>
18 #include <stack>
19 #include "ecmascript/compiler/gate_accessor.h"
20 #include "ecmascript/compiler/verifier.h"
21
22 namespace panda::ecmascript::kungfu {
UnionFind(std::vector<size_t> & semiDom,std::vector<size_t> & parent,std::vector<size_t> & minIdx,size_t idx)23 size_t UnionFind(std::vector<size_t> &semiDom, std::vector<size_t> &parent, std::vector<size_t> &minIdx, size_t idx)
24 {
25 std::stack<size_t> allIdxs;
26 allIdxs.emplace(idx);
27 size_t pIdx = parent[idx];
28 while (pIdx != allIdxs.top()) {
29 allIdxs.emplace(pIdx);
30 pIdx = parent[pIdx];
31 }
32 size_t unionFindSetRoot = pIdx;
33 while (!allIdxs.empty()) {
34 if (semiDom[minIdx[allIdxs.top()]] > semiDom[minIdx[pIdx]]) {
35 minIdx[allIdxs.top()] = minIdx[pIdx];
36 }
37 parent[allIdxs.top()] = unionFindSetRoot;
38 pIdx = allIdxs.top();
39 allIdxs.pop();
40 }
41 return unionFindSetRoot;
42 }
43
CalculateDominatorTree(const Circuit * circuit,std::vector<GateRef> & bbGatesList,std::unordered_map<GateRef,size_t> & bbGatesAddrToIdx,std::vector<size_t> & immDom)44 void Scheduler::CalculateDominatorTree(const Circuit *circuit,
45 std::vector<GateRef>& bbGatesList,
46 std::unordered_map<GateRef, size_t> &bbGatesAddrToIdx,
47 std::vector<size_t> &immDom)
48 {
49 GateAccessor acc(const_cast<Circuit*>(circuit));
50 std::unordered_map<GateRef, size_t> dfsTimestamp;
51 std::unordered_map<GateRef, size_t> dfsFatherIdx;
52 circuit->AdvanceTime();
53 {
54 size_t timestamp = 0;
55 std::deque<GateRef> pendingList;
56 auto startGate = acc.GetStateRoot();
57 acc.SetMark(startGate, MarkCode::VISITED);
58 pendingList.push_back(startGate);
59 while (!pendingList.empty()) {
60 auto curGate = pendingList.back();
61 dfsTimestamp[curGate] = timestamp++;
62 pendingList.pop_back();
63 bbGatesList.push_back(curGate);
64 if (acc.GetOpCode(curGate) != OpCode::LOOP_BACK) {
65 auto uses = acc.Uses(curGate);
66 for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) {
67 if (useIt.GetIndex() < acc.GetStateCount(*useIt) &&
68 acc.IsState(*useIt) && acc.GetMark(*useIt) == MarkCode::NO_MARK) {
69 acc.SetMark(*useIt, MarkCode::VISITED);
70 pendingList.push_back(*useIt);
71 dfsFatherIdx[*useIt] = dfsTimestamp[curGate];
72 }
73 }
74 }
75 }
76 for (size_t idx = 0; idx < bbGatesList.size(); idx++) {
77 bbGatesAddrToIdx[bbGatesList[idx]] = idx;
78 }
79 }
80 immDom.resize(bbGatesList.size());
81 std::vector<size_t> semiDom(bbGatesList.size());
82 std::vector<std::vector<size_t> > semiDomTree(bbGatesList.size());
83 {
84 std::vector<size_t> parent(bbGatesList.size());
85 std::iota(parent.begin(), parent.end(), 0);
86 std::vector<size_t> minIdx(bbGatesList.size());
87 auto merge = [&] (size_t fatherIdx, size_t sonIdx) -> void {
88 size_t parentFatherIdx = UnionFind(semiDom, parent, minIdx, fatherIdx);
89 size_t parentSonIdx = UnionFind(semiDom, parent, minIdx, sonIdx);
90 parent[parentSonIdx] = parentFatherIdx;
91 };
92 std::iota(semiDom.begin(), semiDom.end(), 0);
93 semiDom[0] = semiDom.size();
94 ASSERT(bbGatesList.size() > 0);
95 for (size_t idx = bbGatesList.size() - 1; idx >= 1; idx--) {
96 std::vector<GateRef> preGates;
97 acc.GetInStates(bbGatesList[idx], preGates);
98 for (const auto &predGate : preGates) {
99 if (bbGatesAddrToIdx.count(predGate) > 0) {
100 size_t preGateIdx = bbGatesAddrToIdx[predGate];
101 if (preGateIdx < idx) {
102 semiDom[idx] = std::min(semiDom[idx], preGateIdx);
103 } else {
104 UnionFind(semiDom, parent, minIdx, preGateIdx);
105 semiDom[idx] = std::min(semiDom[idx], semiDom[minIdx[preGateIdx]]);
106 }
107 }
108 }
109 for (const auto &succDomIdx : semiDomTree[idx]) {
110 UnionFind(semiDom, parent, minIdx, succDomIdx);
111 if (idx == semiDom[minIdx[succDomIdx]]) {
112 immDom[succDomIdx] = idx;
113 } else {
114 immDom[succDomIdx] = minIdx[succDomIdx];
115 }
116 }
117 minIdx[idx] = idx;
118 merge(dfsFatherIdx[bbGatesList[idx]], idx);
119 semiDomTree[semiDom[idx]].push_back(idx);
120 }
121 for (size_t idx = 1; idx < bbGatesList.size(); idx++) {
122 if (immDom[idx] != semiDom[idx]) {
123 immDom[idx] = immDom[immDom[idx]];
124 }
125 }
126 semiDom[0] = 0;
127 }
128 }
129
Run(const Circuit * circuit,ControlFlowGraph & result,const std::string & methodName,bool enableLog)130 void Scheduler::Run(const Circuit *circuit, ControlFlowGraph &result,
131 [[maybe_unused]] const std::string& methodName, [[maybe_unused]] bool enableLog)
132 {
133 GateAccessor acc(const_cast<Circuit*>(circuit));
134 std::vector<GateRef> bbGatesList;
135 std::unordered_map<GateRef, size_t> bbGatesAddrToIdx;
136 std::vector<size_t> immDom;
137 Scheduler::CalculateDominatorTree(circuit, bbGatesList, bbGatesAddrToIdx, immDom);
138 ASSERT(result.size() == 0);
139 result.resize(bbGatesList.size());
140 for (size_t idx = 0; idx < bbGatesList.size(); idx++) {
141 result[idx].push_back(bbGatesList[idx]);
142 }
143 // assuming CFG is always reducible
144 std::vector<std::vector<size_t>> sonList(result.size());
145 for (size_t idx = 1; idx < immDom.size(); idx++) {
146 sonList[immDom[idx]].push_back(idx);
147 }
148 const size_t sizeLog = std::ceil(std::log2(static_cast<double>(result.size())) + 1);
149 std::vector<size_t> timeIn(result.size());
150 std::vector<size_t> timeOut(result.size());
151 std::vector<std::vector<size_t>> jumpUp;
152 jumpUp.assign(result.size(), std::vector<size_t>(sizeLog + 1));
153 {
154 size_t timestamp = 0;
155 struct DFSState {
156 size_t cur;
157 std::vector<size_t> &succList;
158 size_t idx;
159 };
160 std::stack<DFSState> dfsStack;
161 size_t root = 0;
162 dfsStack.push({root, sonList[root], 0});
163 timeIn[root] = timestamp++;
164 jumpUp[root][0] = root;
165 for (size_t stepSize = 1; stepSize <= sizeLog; stepSize++) {
166 auto jumpUpHalf = jumpUp[root][stepSize - 1];
167 jumpUp[root][stepSize] = jumpUp[jumpUpHalf][stepSize - 1];
168 }
169 while (!dfsStack.empty()) {
170 auto &curState = dfsStack.top();
171 auto &cur = curState.cur;
172 auto &succList = curState.succList;
173 auto &idx = curState.idx;
174 if (idx == succList.size()) {
175 timeOut[cur] = timestamp++;
176 dfsStack.pop();
177 continue;
178 }
179 const auto &succ = succList[idx];
180 dfsStack.push({succ, sonList[succ], 0});
181 timeIn[succ] = timestamp++;
182 jumpUp[succ][0] = cur;
183 for (size_t stepSize = 1; stepSize <= sizeLog; stepSize++) {
184 auto jumpUpHalf = jumpUp[succ][stepSize - 1];
185 jumpUp[succ][stepSize] = jumpUp[jumpUpHalf][stepSize - 1];
186 }
187 idx++;
188 }
189 }
190 auto isAncestor = [&](size_t nodeA, size_t nodeB) -> bool {
191 return (timeIn[nodeA] <= timeIn[nodeB]) && (timeOut[nodeA] >= timeOut[nodeB]);
192 };
193 auto lowestCommonAncestor = [&](size_t nodeA, size_t nodeB) -> size_t {
194 if (isAncestor(nodeA, nodeB)) {
195 return nodeA;
196 }
197 if (isAncestor(nodeB, nodeA)) {
198 return nodeB;
199 }
200 for (size_t stepSize = sizeLog + 1; stepSize > 0; stepSize--) {
201 if (!isAncestor(jumpUp[nodeA][stepSize - 1], nodeB)) {
202 nodeA = jumpUp[nodeA][stepSize - 1];
203 }
204 }
205 return jumpUp[nodeA][0];
206 };
207 {
208 std::vector<GateRef> order;
209 std::unordered_map<GateRef, size_t> lowerBound;
210 Scheduler::CalculateSchedulingLowerBound(circuit, bbGatesAddrToIdx, lowestCommonAncestor, lowerBound, &order);
211 for (const auto &schedulableGate : order) {
212 result[lowerBound.at(schedulableGate)].push_back(schedulableGate);
213 }
214 std::vector<GateRef> argList;
215 acc.GetOuts(acc.GetArgRoot(), argList);
216 std::sort(argList.begin(), argList.end(), [&](const GateRef &lhs, const GateRef &rhs) -> bool {
217 return acc.TryGetValue(lhs) > acc.TryGetValue(rhs);
218 });
219 for (const auto &arg : argList) {
220 result.front().push_back(arg);
221 }
222 for (const auto &bbGate : bbGatesList) {
223 auto uses = acc.Uses(bbGate);
224 for (auto i = uses.begin(); i != uses.end(); i++) {
225 GateRef succGate = *i;
226 if (acc.IsFixed(succGate)) {
227 result[bbGatesAddrToIdx.at(acc.GetIn(succGate, 0))].push_back(succGate);
228 }
229 }
230 }
231 }
232 if (enableLog) {
233 Print(&result, circuit);
234 }
235 }
236
CalculateSchedulingUpperBound(const Circuit * circuit,const std::unordered_map<GateRef,size_t> & bbGatesAddrToIdx,const std::function<bool (size_t,size_t)> & isAncestor,const std::vector<GateRef> & schedulableGatesList,std::unordered_map<GateRef,size_t> & upperBound)237 bool Scheduler::CalculateSchedulingUpperBound(const Circuit *circuit,
238 const std::unordered_map<GateRef, size_t> &bbGatesAddrToIdx,
239 const std::function<bool(size_t, size_t)> &isAncestor,
240 const std::vector<GateRef> &schedulableGatesList,
241 std::unordered_map<GateRef, size_t> &upperBound)
242 {
243 GateAccessor acc(const_cast<Circuit*>(circuit));
244 struct DFSState {
245 GateRef curGate = Circuit::NullGate();
246 std::vector<GateRef> predGates;
247 size_t idx = 0;
248 size_t curUpperBound = 0;
249 };
250 DFSState emptyState = {Circuit::NullGate(), std::vector<GateRef>(0), 0, 0};
251 bool getReturn = false;
252 std::optional<size_t> returnValue = 0;
253 std::stack<DFSState> dfsStack;
254 auto CheckUnschedulable = [&](GateRef gate) -> void {
255 if (upperBound.count(gate) > 0) {
256 returnValue = upperBound[gate];
257 getReturn = true;
258 } else if (acc.IsProlog(gate) || acc.IsRoot(gate) || acc.IsVirtualState(gate)) {
259 returnValue = 0;
260 getReturn = true;
261 } else if (acc.IsFixed(gate)) {
262 returnValue = bbGatesAddrToIdx.at(acc.GetIn(gate, 0));
263 getReturn = true;
264 } else if (acc.IsState(gate)) {
265 returnValue = bbGatesAddrToIdx.at(gate);
266 getReturn = true;
267 }
268 // then gate is schedulable
269 };
270 for (const auto &schedulableGate : schedulableGatesList) {
271 if (upperBound.count(schedulableGate) != 0) {
272 continue;
273 }
274 getReturn = false;
275 CheckUnschedulable(schedulableGate);
276 if (getReturn) {
277 continue;
278 }
279 dfsStack.push(emptyState);
280 auto &rootState = dfsStack.top();
281 auto &rootPredGates = rootState.predGates;
282 rootState.curGate = schedulableGate;
283 acc.GetIns(schedulableGate, rootPredGates);
284 while (!dfsStack.empty()) {
285 auto &curState = dfsStack.top();
286 auto &curGate = curState.curGate;
287 const auto &predGates = curState.predGates;
288 auto &idx = curState.idx;
289 auto &curUpperBound = curState.curUpperBound;
290 if (idx == predGates.size()) {
291 upperBound[curGate] = curUpperBound;
292 returnValue = curUpperBound;
293 dfsStack.pop();
294 getReturn = true;
295 continue;
296 }
297 if (getReturn) {
298 if (!returnValue.has_value()) {
299 break;
300 }
301 auto predUpperBound = returnValue.value();
302 if (!isAncestor(curUpperBound, predUpperBound) && !isAncestor(predUpperBound, curUpperBound)) {
303 PrintUpperBoundError(circuit, curGate, predUpperBound, curUpperBound);
304 returnValue = std::nullopt;
305 break;
306 }
307 if (isAncestor(curUpperBound, predUpperBound)) {
308 curUpperBound = predUpperBound;
309 }
310 getReturn = false;
311 idx++;
312 } else {
313 const auto &predGate = predGates[idx];
314 CheckUnschedulable(predGate);
315 if (getReturn) {
316 continue;
317 }
318 dfsStack.push(emptyState);
319 auto &newState = dfsStack.top();
320 auto &newPredGates = newState.predGates;
321 newState.curGate = predGate;
322 acc.GetIns(predGate, newPredGates);
323 }
324 }
325 if (!returnValue.has_value()) {
326 return false;
327 }
328 }
329 return true;
330 }
331
PrintUpperBoundError(const Circuit * circuit,GateRef curGate,GateRef predUpperBound,GateRef curUpperBound)332 void Scheduler::PrintUpperBoundError(const Circuit *circuit, GateRef curGate,
333 GateRef predUpperBound, GateRef curUpperBound)
334 {
335 GateAccessor ac(const_cast<Circuit*>(circuit));
336 LOG_COMPILER(ERROR) << "[Verifier][Error] Scheduling upper bound of gate (id="
337 << ac.GetId(curGate)
338 << ") does not exist, current-upper-bound = "
339 << curUpperBound << ", pred-upper-bound = "
340 << predUpperBound << ", there is no dominator relationship between them.";
341 }
342
CalculateFixedGatesList(const Circuit * circuit,const std::unordered_map<GateRef,size_t> & bbGatesAddrToIdx,std::vector<GateRef> & bbAndFixedGatesList)343 void Scheduler::CalculateFixedGatesList(const Circuit *circuit,
344 const std::unordered_map<GateRef, size_t> &bbGatesAddrToIdx,
345 std::vector<GateRef> &bbAndFixedGatesList)
346 {
347 GateAccessor acc(const_cast<Circuit*>(circuit));
348 for (const auto &item : bbGatesAddrToIdx) {
349 bbAndFixedGatesList.push_back(item.first);
350 auto uses = acc.Uses(item.first);
351 for (auto i = uses.begin(); i != uses.end(); i++) {
352 GateRef succGate = *i;
353 if (acc.IsFixed(succGate)) {
354 bbAndFixedGatesList.push_back(succGate);
355 }
356 }
357 }
358 }
359
CalculateSchedulingLowerBound(const Circuit * circuit,const std::unordered_map<GateRef,size_t> & bbGatesAddrToIdx,const std::function<size_t (size_t,size_t)> & lowestCommonAncestor,std::unordered_map<GateRef,size_t> & lowerBound,std::vector<GateRef> * order)360 void Scheduler::CalculateSchedulingLowerBound(const Circuit *circuit,
361 const std::unordered_map<GateRef, size_t> &bbGatesAddrToIdx,
362 const std::function<size_t(size_t, size_t)> &lowestCommonAncestor,
363 std::unordered_map<GateRef, size_t> &lowerBound,
364 std::vector<GateRef> *order)
365 {
366 GateAccessor acc(const_cast<Circuit*>(circuit));
367 std::vector<GateRef> bbAndFixedGatesList;
368 CalculateFixedGatesList(circuit, bbGatesAddrToIdx, bbAndFixedGatesList);
369 std::unordered_map<GateRef, size_t> useCount;
370 struct DFSVisitState {
371 std::vector<GateRef> prevGates;
372 size_t idx = 0;
373 };
374 DFSVisitState emptyVisitState = {std::vector<GateRef>(0), 0};
375 std::stack<DFSVisitState> dfsVisitStack;
376 for (const auto &gate : bbAndFixedGatesList) {
377 dfsVisitStack.push(emptyVisitState);
378 auto &rootState = dfsVisitStack.top();
379 auto &rootPrevGates = rootState.prevGates;
380 acc.GetIns(gate, rootPrevGates);
381 while (!dfsVisitStack.empty()) {
382 auto &curState = dfsVisitStack.top();
383 auto &prevGates = curState.prevGates;
384 auto &idx = curState.idx;
385 if (idx == prevGates.size()) {
386 dfsVisitStack.pop();
387 continue;
388 }
389 const auto &prevGate = prevGates[idx];
390 if (!acc.IsSchedulable(prevGate)) {
391 ++idx;
392 continue;
393 }
394 useCount[prevGate]++;
395 if (useCount[prevGate] == 1) {
396 dfsVisitStack.push(emptyVisitState);
397 auto &newState = dfsVisitStack.top();
398 auto &newPrevGates = newState.prevGates;
399 acc.GetIns(prevGate, newPrevGates);
400 }
401 ++idx;
402 }
403 }
404 struct DFSFinishState {
405 GateRef curGate = Circuit::NullGate();
406 std::vector<GateRef> prevGates;
407 size_t idx = 0;
408 };
409 DFSFinishState emptyFinishState = {Circuit::NullGate(), std::vector<GateRef>(0), 0};
410 std::stack<DFSFinishState> dfsFinishStack;
411 for (const auto &gate : bbAndFixedGatesList) {
412 dfsFinishStack.push(emptyFinishState);
413 auto &rootState = dfsFinishStack.top();
414 auto &rootPrevGates = rootState.prevGates;
415 rootState.curGate = gate;
416 acc.GetIns(gate, rootPrevGates);
417 while (!dfsFinishStack.empty()) {
418 auto &curState = dfsFinishStack.top();
419 auto &curGate = curState.curGate;
420 auto &prevGates = curState.prevGates;
421 auto &idx = curState.idx;
422 if (idx == prevGates.size()) {
423 dfsFinishStack.pop();
424 continue;
425 }
426 const auto &prevGate = prevGates[idx];
427 if (!acc.IsSchedulable(prevGate)) {
428 ++idx;
429 continue;
430 }
431 useCount[prevGate]--;
432 size_t curLowerBound;
433 if (acc.IsState(curGate)) { // cur_opcode would not be STATE_ENTRY
434 curLowerBound = bbGatesAddrToIdx.at(curGate);
435 } else if (acc.IsSelector(curGate)) {
436 ASSERT(idx > 0);
437 curLowerBound = bbGatesAddrToIdx.at(acc.GetIn(acc.GetIn(curGate, 0), idx - 1));
438 } else if (acc.IsFixed(curGate)) {
439 ASSERT(idx > 0);
440 curLowerBound = bbGatesAddrToIdx.at(acc.GetIn(curGate, 0));
441 } else {
442 curLowerBound = lowerBound.at(curGate);
443 }
444 if (lowerBound.count(prevGate) == 0) {
445 lowerBound[prevGate] = curLowerBound;
446 } else {
447 lowerBound[prevGate] = lowestCommonAncestor(lowerBound[prevGate], curLowerBound);
448 }
449 if (useCount[prevGate] == 0) {
450 if (order != nullptr) {
451 order->push_back(prevGate);
452 }
453 dfsFinishStack.push(emptyFinishState);
454 auto &newState = dfsFinishStack.top();
455 auto &newPrevGates = newState.prevGates;
456 newState.curGate = prevGate;
457 acc.GetIns(prevGate, newPrevGates);
458 }
459 ++idx;
460 }
461 }
462 }
463
Print(const std::vector<std::vector<GateRef>> * cfg,const Circuit * circuit)464 void Scheduler::Print(const std::vector<std::vector<GateRef>> *cfg, const Circuit *circuit)
465 {
466 GateAccessor acc(const_cast<Circuit*>(circuit));
467 std::vector<GateRef> bbGatesList;
468 std::unordered_map<GateRef, size_t> bbGatesAddrToIdx;
469 std::vector<size_t> immDom;
470 Scheduler::CalculateDominatorTree(circuit, bbGatesList, bbGatesAddrToIdx, immDom);
471 LOG_COMPILER(INFO) << "==================================== Scheduling ==================================";
472 for (size_t bbIdx = 0; bbIdx < cfg->size(); bbIdx++) {
473 auto opcode = acc.GetOpCode((*cfg)[bbIdx].front());
474 LOG_COMPILER(INFO) << "B" << bbIdx << "_" << opcode << ":"
475 << " immDom=" << immDom[bbIdx];
476 LOG_COMPILER(INFO) << " pred=[";
477 bool isFirst = true;
478 GateRef head = cfg->at(bbIdx).front();
479 auto ins = acc.Ins(head);
480 for (auto i = ins.begin(); i != ins.end(); i++) {
481 GateRef predState = *i;
482 if (acc.IsState(predState) ||
483 acc.GetOpCode(predState) == OpCode::STATE_ENTRY) {
484 LOG_COMPILER(INFO) << (isFirst ? "" : " ") << bbGatesAddrToIdx.at(predState);
485 isFirst = false;
486 }
487 }
488 LOG_COMPILER(INFO) << "] succ=[";
489 isFirst = true;
490 GateRef h = cfg->at(bbIdx).front();
491 auto uses = acc.Uses(h);
492 for (auto i = uses.begin(); i != uses.end(); i++) {
493 GateRef succState = *i;
494 if (acc.IsState(succState) ||
495 acc.GetOpCode(succState) == OpCode::STATE_ENTRY) {
496 LOG_COMPILER(INFO) << (isFirst ? "" : " ") << bbGatesAddrToIdx.at(succState);
497 isFirst = false;
498 }
499 }
500 LOG_COMPILER(INFO) << "]";
501 for (size_t instIdx = (*cfg)[bbIdx].size(); instIdx > 0; instIdx--) {
502 acc.Print((*cfg)[bbIdx][instIdx - 1]);
503 }
504 }
505 LOG_COMPILER(INFO) << "==================================== Scheduling ==================================";
506 }
507 } // namespace panda::ecmascript::kungfu
508