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