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