OLD | NEW |
| (Empty) |
1 // We are implementing the Relooper C API, so always export from this file. | |
2 #ifndef RELOOPERDLL_EXPORTS | |
3 #define RELOOPERDLL_EXPORTS | |
4 #endif | |
5 | |
6 #include "Relooper.h" | |
7 | |
8 #include <string.h> | |
9 #include <stdlib.h> | |
10 #include <list> | |
11 #include <stack> | |
12 | |
13 #if EMSCRIPTEN | |
14 #include "ministring.h" | |
15 #else | |
16 #include <string> | |
17 typedef std::string ministring; | |
18 #endif | |
19 | |
20 // uncomment these out to get LLVM errs() debugging support | |
21 //#include <llvm/Support/raw_ostream.h> | |
22 //using namespace llvm; | |
23 | |
24 template <class T, class U> static bool contains(const T& container, const U& co
ntained) { | |
25 return container.count(contained); | |
26 } | |
27 | |
28 #if DEBUG | |
29 static void PrintDebug(const char *Format, ...); | |
30 #define DebugDump(x, ...) Debugging::Dump(x, __VA_ARGS__) | |
31 #else | |
32 #define PrintDebug(x, ...) | |
33 #define DebugDump(x, ...) | |
34 #endif | |
35 | |
36 #define INDENTATION 1 | |
37 | |
38 struct Indenter { | |
39 static int CurrIndent; | |
40 | |
41 static void Indent() { CurrIndent++; } | |
42 static void Unindent() { CurrIndent--; } | |
43 }; | |
44 | |
45 static void PrintIndented(const char *Format, ...); | |
46 static void PutIndented(const char *String); | |
47 | |
48 static char *OutputBufferRoot = NULL; | |
49 static char *OutputBuffer = NULL; | |
50 static int OutputBufferSize = 0; | |
51 static int OutputBufferOwned = false; | |
52 | |
53 static int LeftInOutputBuffer() { | |
54 return OutputBufferSize - (OutputBuffer - OutputBufferRoot); | |
55 } | |
56 | |
57 static bool EnsureOutputBuffer(int Needed) { // ensures the output buffer is suf
ficient. returns true is no problem happened | |
58 Needed++; // ensure the trailing \0 is not forgotten | |
59 int Left = LeftInOutputBuffer(); | |
60 if (!OutputBufferOwned) { | |
61 assert(Needed < Left); | |
62 } else { | |
63 // we own the buffer, and can resize if necessary | |
64 if (Needed >= Left) { | |
65 int Offset = OutputBuffer - OutputBufferRoot; | |
66 int TotalNeeded = OutputBufferSize + Needed - Left + 10240; | |
67 int NewSize = OutputBufferSize; | |
68 while (NewSize < TotalNeeded) NewSize = NewSize + (NewSize/2); | |
69 //printf("resize %d => %d\n", OutputBufferSize, NewSize); | |
70 OutputBufferRoot = (char*)realloc(OutputBufferRoot, NewSize); | |
71 assert(OutputBufferRoot); | |
72 OutputBuffer = OutputBufferRoot + Offset; | |
73 OutputBufferSize = NewSize; | |
74 return false; | |
75 } | |
76 } | |
77 return true; | |
78 } | |
79 | |
80 void PrintIndented(const char *Format, ...) { | |
81 assert(OutputBuffer); | |
82 EnsureOutputBuffer(Indenter::CurrIndent*INDENTATION); | |
83 for (int i = 0; i < Indenter::CurrIndent*INDENTATION; i++, OutputBuffer++) *Ou
tputBuffer = ' '; | |
84 int Written; | |
85 while (1) { // write and potentially resize buffer until we have enough room | |
86 int Left = LeftInOutputBuffer(); | |
87 va_list Args; | |
88 va_start(Args, Format); | |
89 Written = vsnprintf(OutputBuffer, Left, Format, Args); | |
90 va_end(Args); | |
91 #ifdef _MSC_VER | |
92 // VC CRT specific: vsnprintf returns -1 on failure, other runtimes return t
he number of characters that would have been | |
93 // written. On VC, if we get -1, count the number of characters manually. | |
94 if (Written < 0) { | |
95 va_start(Args, Format); | |
96 Written = _vscprintf(Format, Args); | |
97 va_end(Args); | |
98 } | |
99 #endif | |
100 | |
101 if (EnsureOutputBuffer(Written)) break; | |
102 } | |
103 OutputBuffer += Written; | |
104 } | |
105 | |
106 void PutIndented(const char *String) { | |
107 assert(OutputBuffer); | |
108 EnsureOutputBuffer(Indenter::CurrIndent*INDENTATION); | |
109 for (int i = 0; i < Indenter::CurrIndent*INDENTATION; i++, OutputBuffer++) *Ou
tputBuffer = ' '; | |
110 int Needed = strlen(String)+1; | |
111 EnsureOutputBuffer(Needed); | |
112 strcpy(OutputBuffer, String); | |
113 OutputBuffer += strlen(String); | |
114 *OutputBuffer++ = '\n'; | |
115 *OutputBuffer = 0; | |
116 } | |
117 | |
118 static int AsmJS = 0; | |
119 | |
120 // Indenter | |
121 | |
122 int Indenter::CurrIndent = 1; | |
123 | |
124 // Branch | |
125 | |
126 Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL)
, Labeled(true) { | |
127 Condition = ConditionInit ? strdup(ConditionInit) : NULL; | |
128 Code = CodeInit ? strdup(CodeInit) : NULL; | |
129 } | |
130 | |
131 Branch::~Branch() { | |
132 if (Condition) free((void*)Condition); | |
133 if (Code) free((void*)Code); | |
134 } | |
135 | |
136 void Branch::Render(Block *Target, bool SetLabel) { | |
137 if (Code) PrintIndented("%s\n", Code); | |
138 if (SetLabel) PrintIndented("label = %d;\n", Target->Id); | |
139 if (Ancestor) { | |
140 if (Type == Break || Type == Continue) { | |
141 if (Labeled) { | |
142 PrintIndented("%s L%d;\n", Type == Break ? "break" : "continue", Ancesto
r->Id); | |
143 } else { | |
144 PrintIndented("%s;\n", Type == Break ? "break" : "continue"); | |
145 } | |
146 } | |
147 } | |
148 } | |
149 | |
150 // Block | |
151 | |
152 Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id
(-1), IsCheckedMultipleEntry(false) { | |
153 Code = strdup(CodeInit); | |
154 BranchVar = BranchVarInit ? strdup(BranchVarInit) : NULL; | |
155 } | |
156 | |
157 Block::~Block() { | |
158 if (Code) free((void*)Code); | |
159 if (BranchVar) free((void*)BranchVar); | |
160 for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != Pro
cessedBranchesOut.end(); iter++) { | |
161 delete iter->second; | |
162 } | |
163 // XXX If not reachable, expected to have branches here. But need to clean the
m up to prevent leaks! | |
164 } | |
165 | |
166 void Block::AddBranchTo(Block *Target, const char *Condition, const char *Code)
{ | |
167 assert(!contains(BranchesOut, Target)); // cannot add more than one branch to
the same target | |
168 BranchesOut[Target] = new Branch(Condition, Code); | |
169 } | |
170 | |
171 void Block::Render(bool InLoop) { | |
172 if (IsCheckedMultipleEntry && InLoop) { | |
173 PrintIndented("label = 0;\n"); | |
174 } | |
175 | |
176 if (Code) { | |
177 // Print code in an indented manner, even over multiple lines | |
178 char *Start = const_cast<char*>(Code); | |
179 while (*Start) { | |
180 char *End = strchr(Start, '\n'); | |
181 if (End) *End = 0; | |
182 PutIndented(Start); | |
183 if (End) *End = '\n'; else break; | |
184 Start = End+1; | |
185 } | |
186 } | |
187 | |
188 if (!ProcessedBranchesOut.size()) return; | |
189 | |
190 bool SetLabel = true; // in some cases it is clear we can avoid setting label,
see later | |
191 bool ForceSetLabel = Shape::IsEmulated(Parent); | |
192 | |
193 // A setting of the label variable (label = x) is necessary if it can | |
194 // cause an impact. The main case is where we set label to x, then elsewhere | |
195 // we check if label is equal to that value, i.e., that label is an entry | |
196 // in a multiple block. We also need to reset the label when we enter | |
197 // that block, so that each setting is a one-time action: consider | |
198 // | |
199 // while (1) { | |
200 // if (check) label = 1; | |
201 // if (label == 1) { label = 0 } | |
202 // } | |
203 // | |
204 // (Note that this case is impossible due to fusing, but that is not | |
205 // material here.) So setting to 0 is important just to clear the 1 for | |
206 // future iterations. | |
207 // TODO: When inside a loop, if necessary clear the label variable | |
208 // once on the top, and never do settings that are in effect clears | |
209 | |
210 // Fusing: If the next is a Multiple, we can fuse it with this block. Note | |
211 // that we must be the Inner of a Simple, so fusing means joining a Simple | |
212 // to a Multiple. What happens there is that all options in the Multiple | |
213 // *must* appear in the Simple (the Simple is the only one reaching the | |
214 // Multiple), so we can remove the Multiple and add its independent groups | |
215 // into the Simple's branches. | |
216 MultipleShape *Fused = Shape::IsMultiple(Parent->Next); | |
217 if (Fused) { | |
218 PrintDebug("Fusing Multiple to Simple\n", 0); | |
219 Parent->Next = Parent->Next->Next; | |
220 Fused->UseSwitch = false; // TODO: emit switches here | |
221 Fused->RenderLoopPrefix(); | |
222 | |
223 // When the Multiple has the same number of groups as we have branches, | |
224 // they will all be fused, so it is safe to not set the label at all | |
225 if (SetLabel && Fused->InnerMap.size() == ProcessedBranchesOut.size()) { | |
226 SetLabel = false; | |
227 } | |
228 } | |
229 | |
230 Block *DefaultTarget(NULL); // The block we branch to without checking the con
dition, if none of the other conditions held. | |
231 | |
232 // Find the default target, the one without a condition | |
233 for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != Pro
cessedBranchesOut.end(); iter++) { | |
234 if (!iter->second->Condition) { | |
235 assert(!DefaultTarget); // Must be exactly one default | |
236 DefaultTarget = iter->first; | |
237 } | |
238 } | |
239 assert(DefaultTarget); // Since each block *must* branch somewhere, this must
be set | |
240 | |
241 bool useSwitch = BranchVar != NULL; | |
242 | |
243 if (useSwitch) { | |
244 PrintIndented("switch (%s) {\n", BranchVar); | |
245 } | |
246 | |
247 ministring RemainingConditions; | |
248 bool First = !useSwitch; // when using a switch, there is no special first | |
249 for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin();; iter++) { | |
250 Block *Target; | |
251 Branch *Details; | |
252 if (iter != ProcessedBranchesOut.end()) { | |
253 Target = iter->first; | |
254 if (Target == DefaultTarget) continue; // done at the end | |
255 Details = iter->second; | |
256 assert(Details->Condition); // must have a condition if this is not the de
fault target | |
257 } else { | |
258 Target = DefaultTarget; | |
259 Details = ProcessedBranchesOut[DefaultTarget]; | |
260 } | |
261 bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSet
Label; | |
262 bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id); | |
263 bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFuse
dContent || Details->Code; | |
264 if (iter != ProcessedBranchesOut.end()) { | |
265 // If there is nothing to show in this branch, omit the condition | |
266 if (useSwitch) { | |
267 PrintIndented("%s {\n", Details->Condition); | |
268 } else { | |
269 if (HasContent) { | |
270 PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condit
ion); | |
271 First = false; | |
272 } else { | |
273 if (RemainingConditions.size() > 0) RemainingConditions += " && "; | |
274 RemainingConditions += "!("; | |
275 if (BranchVar) { | |
276 RemainingConditions += BranchVar; | |
277 RemainingConditions += " == "; | |
278 } | |
279 RemainingConditions += Details->Condition; | |
280 RemainingConditions += ")"; | |
281 } | |
282 } | |
283 } else { | |
284 // this is the default | |
285 if (useSwitch) { | |
286 PrintIndented("default: {\n"); | |
287 } else { | |
288 if (HasContent) { | |
289 if (RemainingConditions.size() > 0) { | |
290 if (First) { | |
291 PrintIndented("if (%s) {\n", RemainingConditions.c_str()); | |
292 First = false; | |
293 } else { | |
294 PrintIndented("} else if (%s) {\n", RemainingConditions.c_str()); | |
295 } | |
296 } else if (!First) { | |
297 PrintIndented("} else {\n"); | |
298 } | |
299 } | |
300 } | |
301 } | |
302 if (!First) Indenter::Indent(); | |
303 Details->Render(Target, SetCurrLabel); | |
304 if (HasFusedContent) { | |
305 Fused->InnerMap.find(Target->Id)->second->Render(InLoop); | |
306 } else if (Details->Type == Branch::Nested) { | |
307 // Nest the parent content here, and remove it from showing up afterwards
as Next | |
308 assert(Parent->Next); | |
309 Parent->Next->Render(InLoop); | |
310 Parent->Next = NULL; | |
311 } | |
312 if (useSwitch && iter != ProcessedBranchesOut.end()) { | |
313 PrintIndented("break;\n"); | |
314 } | |
315 if (!First) Indenter::Unindent(); | |
316 if (useSwitch) { | |
317 PrintIndented("}\n"); | |
318 } | |
319 if (iter == ProcessedBranchesOut.end()) break; | |
320 } | |
321 if (!First) PrintIndented("}\n"); | |
322 | |
323 if (Fused) { | |
324 Fused->RenderLoopPostfix(); | |
325 } | |
326 } | |
327 | |
328 // MultipleShape | |
329 | |
330 void MultipleShape::RenderLoopPrefix() { | |
331 if (Breaks) { | |
332 if (UseSwitch) { | |
333 if (Labeled) { | |
334 PrintIndented("L%d: ", Id); | |
335 } | |
336 } else { | |
337 if (Labeled) { | |
338 PrintIndented("L%d: do {\n", Id); | |
339 } else { | |
340 PrintIndented("do {\n"); | |
341 } | |
342 Indenter::Indent(); | |
343 } | |
344 } | |
345 } | |
346 | |
347 void MultipleShape::RenderLoopPostfix() { | |
348 if (Breaks && !UseSwitch) { | |
349 Indenter::Unindent(); | |
350 PrintIndented("} while(0);\n"); | |
351 } | |
352 } | |
353 | |
354 void MultipleShape::Render(bool InLoop) { | |
355 RenderLoopPrefix(); | |
356 | |
357 if (!UseSwitch) { | |
358 // emit an if-else chain | |
359 bool First = true; | |
360 for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); i
ter++) { | |
361 if (AsmJS) { | |
362 PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->
first); | |
363 } else { | |
364 PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->firs
t); | |
365 } | |
366 First = false; | |
367 Indenter::Indent(); | |
368 iter->second->Render(InLoop); | |
369 Indenter::Unindent(); | |
370 PrintIndented("}\n"); | |
371 } | |
372 } else { | |
373 // emit a switch | |
374 if (AsmJS) { | |
375 PrintIndented("switch (label|0) {\n"); | |
376 } else { | |
377 PrintIndented("switch (label) {\n"); | |
378 } | |
379 Indenter::Indent(); | |
380 for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); i
ter++) { | |
381 PrintIndented("case %d: {\n", iter->first); | |
382 Indenter::Indent(); | |
383 iter->second->Render(InLoop); | |
384 PrintIndented("break;\n"); | |
385 Indenter::Unindent(); | |
386 PrintIndented("}\n"); | |
387 } | |
388 Indenter::Unindent(); | |
389 PrintIndented("}\n"); | |
390 } | |
391 | |
392 RenderLoopPostfix(); | |
393 if (Next) Next->Render(InLoop); | |
394 } | |
395 | |
396 // LoopShape | |
397 | |
398 void LoopShape::Render(bool InLoop) { | |
399 if (Labeled) { | |
400 PrintIndented("L%d: while(1) {\n", Id); | |
401 } else { | |
402 PrintIndented("while(1) {\n"); | |
403 } | |
404 Indenter::Indent(); | |
405 Inner->Render(true); | |
406 Indenter::Unindent(); | |
407 PrintIndented("}\n"); | |
408 if (Next) Next->Render(InLoop); | |
409 } | |
410 | |
411 // EmulatedShape | |
412 | |
413 void EmulatedShape::Render(bool InLoop) { | |
414 PrintIndented("label = %d;\n", Entry->Id); | |
415 if (Labeled) { | |
416 PrintIndented("L%d: ", Id); | |
417 } | |
418 PrintIndented("while(1) {\n"); | |
419 Indenter::Indent(); | |
420 PrintIndented("switch(label|0) {\n"); | |
421 Indenter::Indent(); | |
422 for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) { | |
423 Block *Curr = *iter; | |
424 PrintIndented("case %d: {\n", Curr->Id); | |
425 Indenter::Indent(); | |
426 Curr->Render(InLoop); | |
427 PrintIndented("break;\n"); | |
428 Indenter::Unindent(); | |
429 PrintIndented("}\n"); | |
430 } | |
431 Indenter::Unindent(); | |
432 PrintIndented("}\n"); | |
433 Indenter::Unindent(); | |
434 PrintIndented("}\n"); | |
435 if (Next) Next->Render(InLoop); | |
436 } | |
437 | |
438 // Relooper | |
439 | |
440 Relooper::Relooper() : Root(NULL), Emulate(false), MinSize(false), BlockIdCounte
r(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings | |
441 } | |
442 | |
443 Relooper::~Relooper() { | |
444 for (unsigned i = 0; i < Blocks.size(); i++) delete Blocks[i]; | |
445 for (unsigned i = 0; i < Shapes.size(); i++) delete Shapes[i]; | |
446 } | |
447 | |
448 void Relooper::AddBlock(Block *New, int Id) { | |
449 New->Id = Id == -1 ? BlockIdCounter++ : Id; | |
450 Blocks.push_back(New); | |
451 } | |
452 | |
453 struct RelooperRecursor { | |
454 Relooper *Parent; | |
455 RelooperRecursor(Relooper *ParentInit) : Parent(ParentInit) {} | |
456 }; | |
457 | |
458 typedef std::list<Block*> BlockList; | |
459 | |
460 void Relooper::Calculate(Block *Entry) { | |
461 // Scan and optimize the input | |
462 struct PreOptimizer : public RelooperRecursor { | |
463 PreOptimizer(Relooper *Parent) : RelooperRecursor(Parent) {} | |
464 BlockSet Live; | |
465 | |
466 void FindLive(Block *Root) { | |
467 BlockList ToInvestigate; | |
468 ToInvestigate.push_back(Root); | |
469 while (ToInvestigate.size() > 0) { | |
470 Block *Curr = ToInvestigate.front(); | |
471 ToInvestigate.pop_front(); | |
472 if (contains(Live, Curr)) continue; | |
473 Live.insert(Curr); | |
474 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter !=
Curr->BranchesOut.end(); iter++) { | |
475 ToInvestigate.push_back(iter->first); | |
476 } | |
477 } | |
478 } | |
479 | |
480 // If a block has multiple entries but no exits, and it is small enough, it
is useful to split it. | |
481 // A common example is a C++ function where everything ends up at a final ex
it block and does some | |
482 // RAII cleanup. Without splitting, we will be forced to introduce labelled
loops to allow | |
483 // reaching the final block | |
484 void SplitDeadEnds() { | |
485 unsigned TotalCodeSize = 0; | |
486 for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) { | |
487 Block *Curr = *iter; | |
488 TotalCodeSize += strlen(Curr->Code); | |
489 } | |
490 BlockSet Splits; | |
491 BlockSet Removed; | |
492 //DebugDump(Live, "before"); | |
493 for (BlockSet::iterator iter = Live.begin(); iter != Live.end(); iter++) { | |
494 Block *Original = *iter; | |
495 if (Original->BranchesIn.size() <= 1 || Original->BranchesOut.size() > 0
) continue; // only dead ends, for now | |
496 if (contains(Original->BranchesOut, Original)) continue; // cannot split
a looping node | |
497 if (strlen(Original->Code)*(Original->BranchesIn.size()-1) > TotalCodeSi
ze/5) continue; // if splitting increases raw code size by a significant amount,
abort | |
498 // Split the node (for simplicity, we replace all the blocks, even thoug
h we could have reused the original) | |
499 PrintDebug("Splitting block %d\n", Original->Id); | |
500 for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Ori
ginal->BranchesIn.end(); iter++) { | |
501 Block *Prior = *iter; | |
502 Block *Split = new Block(Original->Code, Original->BranchVar); | |
503 Parent->AddBlock(Split, Original->Id); | |
504 Split->BranchesIn.insert(Prior); | |
505 Branch *Details = Prior->BranchesOut[Original]; | |
506 Prior->BranchesOut[Split] = new Branch(Details->Condition, Details->Co
de); | |
507 Prior->BranchesOut.erase(Original); | |
508 for (BlockBranchMap::iterator iter = Original->BranchesOut.begin(); it
er != Original->BranchesOut.end(); iter++) { | |
509 Block *Post = iter->first; | |
510 Branch *Details = iter->second; | |
511 Split->BranchesOut[Post] = new Branch(Details->Condition, Details->C
ode); | |
512 Post->BranchesIn.insert(Split); | |
513 } | |
514 Splits.insert(Split); | |
515 Removed.insert(Original); | |
516 } | |
517 for (BlockBranchMap::iterator iter = Original->BranchesOut.begin(); iter
!= Original->BranchesOut.end(); iter++) { | |
518 Block *Post = iter->first; | |
519 Post->BranchesIn.erase(Original); | |
520 } | |
521 //DebugDump(Live, "mid"); | |
522 } | |
523 for (BlockSet::iterator iter = Splits.begin(); iter != Splits.end(); iter+
+) { | |
524 Live.insert(*iter); | |
525 } | |
526 for (BlockSet::iterator iter = Removed.begin(); iter != Removed.end(); ite
r++) { | |
527 Live.erase(*iter); | |
528 } | |
529 //DebugDump(Live, "after"); | |
530 } | |
531 }; | |
532 PreOptimizer Pre(this); | |
533 Pre.FindLive(Entry); | |
534 | |
535 // Add incoming branches from live blocks, ignoring dead code | |
536 for (unsigned i = 0; i < Blocks.size(); i++) { | |
537 Block *Curr = Blocks[i]; | |
538 if (!contains(Pre.Live, Curr)) continue; | |
539 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr
->BranchesOut.end(); iter++) { | |
540 iter->first->BranchesIn.insert(Curr); | |
541 } | |
542 } | |
543 | |
544 if (!Emulate && !MinSize) Pre.SplitDeadEnds(); | |
545 | |
546 // Recursively process the graph | |
547 | |
548 struct Analyzer : public RelooperRecursor { | |
549 Analyzer(Relooper *Parent) : RelooperRecursor(Parent) {} | |
550 | |
551 // Add a shape to the list of shapes in this Relooper calculation | |
552 void Notice(Shape *New) { | |
553 New->Id = Parent->ShapeIdCounter++; | |
554 Parent->Shapes.push_back(New); | |
555 } | |
556 | |
557 // Create a list of entries from a block. If LimitTo is provided, only resul
ts in that set | |
558 // will appear | |
559 void GetBlocksOut(Block *Source, BlockSet& Entries, BlockSet *LimitTo=NULL)
{ | |
560 for (BlockBranchMap::iterator iter = Source->BranchesOut.begin(); iter !=
Source->BranchesOut.end(); iter++) { | |
561 if (!LimitTo || contains(*LimitTo, iter->first)) { | |
562 Entries.insert(iter->first); | |
563 } | |
564 } | |
565 } | |
566 | |
567 // Converts/processes all branchings to a specific target | |
568 void Solipsize(Block *Target, Branch::FlowType Type, Shape *Ancestor, BlockS
et &From) { | |
569 PrintDebug("Solipsizing branches into %d\n", Target->Id); | |
570 DebugDump(From, " relevant to solipsize: "); | |
571 for (BlockSet::iterator iter = Target->BranchesIn.begin(); iter != Target-
>BranchesIn.end();) { | |
572 Block *Prior = *iter; | |
573 if (!contains(From, Prior)) { | |
574 iter++; | |
575 continue; | |
576 } | |
577 Branch *PriorOut = Prior->BranchesOut[Target]; | |
578 PriorOut->Ancestor = Ancestor; | |
579 PriorOut->Type = Type; | |
580 if (MultipleShape *Multiple = Shape::IsMultiple(Ancestor)) { | |
581 Multiple->Breaks++; // We are breaking out of this Multiple, so need a
loop | |
582 } | |
583 iter++; // carefully increment iter before erasing | |
584 Target->BranchesIn.erase(Prior); | |
585 Target->ProcessedBranchesIn.insert(Prior); | |
586 Prior->BranchesOut.erase(Target); | |
587 Prior->ProcessedBranchesOut[Target] = PriorOut; | |
588 PrintDebug(" eliminated branch from %d\n", Prior->Id); | |
589 } | |
590 } | |
591 | |
592 Shape *MakeSimple(BlockSet &Blocks, Block *Inner, BlockSet &NextEntries) { | |
593 PrintDebug("creating simple block with block #%d\n", Inner->Id); | |
594 SimpleShape *Simple = new SimpleShape; | |
595 Notice(Simple); | |
596 Simple->Inner = Inner; | |
597 Inner->Parent = Simple; | |
598 if (Blocks.size() > 1) { | |
599 Blocks.erase(Inner); | |
600 GetBlocksOut(Inner, NextEntries, &Blocks); | |
601 BlockSet JustInner; | |
602 JustInner.insert(Inner); | |
603 for (BlockSet::iterator iter = NextEntries.begin(); iter != NextEntries.
end(); iter++) { | |
604 Solipsize(*iter, Branch::Direct, Simple, JustInner); | |
605 } | |
606 } | |
607 return Simple; | |
608 } | |
609 | |
610 Shape *MakeEmulated(BlockSet &Blocks, Block *Entry, BlockSet &NextEntries) { | |
611 PrintDebug("creating emulated block with entry #%d and everything it can r
each, %d blocks\n", Entry->Id, Blocks.size()); | |
612 EmulatedShape *Emulated = new EmulatedShape; | |
613 Notice(Emulated); | |
614 Emulated->Entry = Entry; | |
615 for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter+
+) { | |
616 Block *Curr = *iter; | |
617 Emulated->Blocks.insert(Curr); | |
618 Curr->Parent = Emulated; | |
619 Solipsize(Curr, Branch::Continue, Emulated, Blocks); | |
620 } | |
621 Blocks.clear(); | |
622 return Emulated; | |
623 } | |
624 | |
625 Shape *MakeLoop(BlockSet &Blocks, BlockSet& Entries, BlockSet &NextEntries)
{ | |
626 // Find the inner blocks in this loop. Proceed backwards from the entries
until | |
627 // you reach a seen block, collecting as you go. | |
628 BlockSet InnerBlocks; | |
629 BlockSet Queue = Entries; | |
630 while (Queue.size() > 0) { | |
631 Block *Curr = *(Queue.begin()); | |
632 Queue.erase(Queue.begin()); | |
633 if (!contains(InnerBlocks, Curr)) { | |
634 // This element is new, mark it as inner and remove from outer | |
635 InnerBlocks.insert(Curr); | |
636 Blocks.erase(Curr); | |
637 // Add the elements prior to it | |
638 for (BlockSet::iterator iter = Curr->BranchesIn.begin(); iter != Curr-
>BranchesIn.end(); iter++) { | |
639 Queue.insert(*iter); | |
640 } | |
641 #if 0 | |
642 // Add elements it leads to, if they are dead ends. There is no reason
not to hoist dead ends | |
643 // into loops, as it can avoid multiple entries after the loop | |
644 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter !
= Curr->BranchesOut.end(); iter++) { | |
645 Block *Target = iter->first; | |
646 if (Target->BranchesIn.size() <= 1 && Target->BranchesOut.size() ==
0) { | |
647 Queue.insert(Target); | |
648 } | |
649 } | |
650 #endif | |
651 } | |
652 } | |
653 assert(InnerBlocks.size() > 0); | |
654 | |
655 for (BlockSet::iterator iter = InnerBlocks.begin(); iter != InnerBlocks.en
d(); iter++) { | |
656 Block *Curr = *iter; | |
657 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter !=
Curr->BranchesOut.end(); iter++) { | |
658 Block *Possible = iter->first; | |
659 if (!contains(InnerBlocks, Possible)) { | |
660 NextEntries.insert(Possible); | |
661 } | |
662 } | |
663 } | |
664 | |
665 #if 0 | |
666 // We can avoid multiple next entries by hoisting them into the loop. | |
667 if (NextEntries.size() > 1) { | |
668 BlockBlockSetMap IndependentGroups; | |
669 FindIndependentGroups(NextEntries, IndependentGroups, &InnerBlocks); | |
670 | |
671 while (IndependentGroups.size() > 0 && NextEntries.size() > 1) { | |
672 Block *Min = NULL; | |
673 int MinSize = 0; | |
674 for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter
!= IndependentGroups.end(); iter++) { | |
675 Block *Entry = iter->first; | |
676 BlockSet &Blocks = iter->second; | |
677 if (!Min || Blocks.size() < MinSize) { // TODO: code size, not # of
blocks | |
678 Min = Entry; | |
679 MinSize = Blocks.size(); | |
680 } | |
681 } | |
682 // check how many new entries this would cause | |
683 BlockSet &Hoisted = IndependentGroups[Min]; | |
684 bool abort = false; | |
685 for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end()
&& !abort; iter++) { | |
686 Block *Curr = *iter; | |
687 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter
!= Curr->BranchesOut.end(); iter++) { | |
688 Block *Target = iter->first; | |
689 if (!contains(Hoisted, Target) && !contains(NextEntries, Target))
{ | |
690 // abort this hoisting | |
691 abort = true; | |
692 break; | |
693 } | |
694 } | |
695 } | |
696 if (abort) { | |
697 IndependentGroups.erase(Min); | |
698 continue; | |
699 } | |
700 // hoist this entry | |
701 PrintDebug("hoisting %d into loop\n", Min->Id); | |
702 NextEntries.erase(Min); | |
703 for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end();
iter++) { | |
704 Block *Curr = *iter; | |
705 InnerBlocks.insert(Curr); | |
706 Blocks.erase(Curr); | |
707 } | |
708 IndependentGroups.erase(Min); | |
709 } | |
710 } | |
711 #endif | |
712 | |
713 PrintDebug("creating loop block:\n", 0); | |
714 DebugDump(InnerBlocks, " inner blocks:"); | |
715 DebugDump(Entries, " inner entries:"); | |
716 DebugDump(Blocks, " outer blocks:"); | |
717 DebugDump(NextEntries, " outer entries:"); | |
718 | |
719 LoopShape *Loop = new LoopShape(); | |
720 Notice(Loop); | |
721 | |
722 // Solipsize the loop, replacing with break/continue and marking branches
as Processed (will not affect later calculations) | |
723 // A. Branches to the loop entries become a continue to this shape | |
724 for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); ite
r++) { | |
725 Solipsize(*iter, Branch::Continue, Loop, InnerBlocks); | |
726 } | |
727 // B. Branches to outside the loop (a next entry) become breaks on this sh
ape | |
728 for (BlockSet::iterator iter = NextEntries.begin(); iter != NextEntries.en
d(); iter++) { | |
729 Solipsize(*iter, Branch::Break, Loop, InnerBlocks); | |
730 } | |
731 // Finish up | |
732 Shape *Inner = Process(InnerBlocks, Entries, NULL); | |
733 Loop->Inner = Inner; | |
734 return Loop; | |
735 } | |
736 | |
737 // For each entry, find the independent group reachable by it. The independe
nt group is | |
738 // the entry itself, plus all the blocks it can reach that cannot be directl
y reached by another entry. Note that we | |
739 // ignore directly reaching the entry itself by another entry. | |
740 // @param Ignore - previous blocks that are irrelevant | |
741 void FindIndependentGroups(BlockSet &Entries, BlockBlockSetMap& IndependentG
roups, BlockSet *Ignore=NULL) { | |
742 typedef std::map<Block*, Block*> BlockBlockMap; | |
743 | |
744 struct HelperClass { | |
745 BlockBlockSetMap& IndependentGroups; | |
746 BlockBlockMap Ownership; // For each block, which entry it belongs to. W
e have reached it from there. | |
747 | |
748 HelperClass(BlockBlockSetMap& IndependentGroupsInit) : IndependentGroups
(IndependentGroupsInit) {} | |
749 void InvalidateWithChildren(Block *New) { // TODO: rename New | |
750 BlockList ToInvalidate; // Being in the list means you need to be inva
lidated | |
751 ToInvalidate.push_back(New); | |
752 while (ToInvalidate.size() > 0) { | |
753 Block *Invalidatee = ToInvalidate.front(); | |
754 ToInvalidate.pop_front(); | |
755 Block *Owner = Ownership[Invalidatee]; | |
756 if (contains(IndependentGroups, Owner)) { // Owner may have been inv
alidated, do not add to IndependentGroups! | |
757 IndependentGroups[Owner].erase(Invalidatee); | |
758 } | |
759 if (Ownership[Invalidatee]) { // may have been seen before and inval
idated already | |
760 Ownership[Invalidatee] = NULL; | |
761 for (BlockBranchMap::iterator iter = Invalidatee->BranchesOut.begi
n(); iter != Invalidatee->BranchesOut.end(); iter++) { | |
762 Block *Target = iter->first; | |
763 BlockBlockMap::iterator Known = Ownership.find(Target); | |
764 if (Known != Ownership.end()) { | |
765 Block *TargetOwner = Known->second; | |
766 if (TargetOwner) { | |
767 ToInvalidate.push_back(Target); | |
768 } | |
769 } | |
770 } | |
771 } | |
772 } | |
773 } | |
774 }; | |
775 HelperClass Helper(IndependentGroups); | |
776 | |
777 // We flow out from each of the entries, simultaneously. | |
778 // When we reach a new block, we add it as belonging to the one we got to
it from. | |
779 // If we reach a new block that is already marked as belonging to someone,
it is reachable by | |
780 // two entries and is not valid for any of them. Remove it and all it can
reach that have been | |
781 // visited. | |
782 | |
783 BlockList Queue; // Being in the queue means we just added this item, and
we need to add its children | |
784 for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); ite
r++) { | |
785 Block *Entry = *iter; | |
786 Helper.Ownership[Entry] = Entry; | |
787 IndependentGroups[Entry].insert(Entry); | |
788 Queue.push_back(Entry); | |
789 } | |
790 while (Queue.size() > 0) { | |
791 Block *Curr = Queue.front(); | |
792 Queue.pop_front(); | |
793 Block *Owner = Helper.Ownership[Curr]; // Curr must be in the ownership
map if we are in the queue | |
794 if (!Owner) continue; // we have been invalidated meanwhile after being
reached from two entries | |
795 // Add all children | |
796 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter !=
Curr->BranchesOut.end(); iter++) { | |
797 Block *New = iter->first; | |
798 BlockBlockMap::iterator Known = Helper.Ownership.find(New); | |
799 if (Known == Helper.Ownership.end()) { | |
800 // New node. Add it, and put it in the queue | |
801 Helper.Ownership[New] = Owner; | |
802 IndependentGroups[Owner].insert(New); | |
803 Queue.push_back(New); | |
804 continue; | |
805 } | |
806 Block *NewOwner = Known->second; | |
807 if (!NewOwner) continue; // We reached an invalidated node | |
808 if (NewOwner != Owner) { | |
809 // Invalidate this and all reachable that we have seen - we reached
this from two locations | |
810 Helper.InvalidateWithChildren(New); | |
811 } | |
812 // otherwise, we have the same owner, so do nothing | |
813 } | |
814 } | |
815 | |
816 // Having processed all the interesting blocks, we remain with just one po
tential issue: | |
817 // If a->b, and a was invalidated, but then b was later reached by someone
else, we must | |
818 // invalidate b. To check for this, we go over all elements in the indepen
dent groups, | |
819 // if an element has a parent which does *not* have the same owner, we mus
t remove it | |
820 // and all its children. | |
821 | |
822 for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); ite
r++) { | |
823 BlockSet &CurrGroup = IndependentGroups[*iter]; | |
824 BlockList ToInvalidate; | |
825 for (BlockSet::iterator iter = CurrGroup.begin(); iter != CurrGroup.end(
); iter++) { | |
826 Block *Child = *iter; | |
827 for (BlockSet::iterator iter = Child->BranchesIn.begin(); iter != Chil
d->BranchesIn.end(); iter++) { | |
828 Block *Parent = *iter; | |
829 if (Ignore && contains(*Ignore, Parent)) continue; | |
830 if (Helper.Ownership[Parent] != Helper.Ownership[Child]) { | |
831 ToInvalidate.push_back(Child); | |
832 } | |
833 } | |
834 } | |
835 while (ToInvalidate.size() > 0) { | |
836 Block *Invalidatee = ToInvalidate.front(); | |
837 ToInvalidate.pop_front(); | |
838 Helper.InvalidateWithChildren(Invalidatee); | |
839 } | |
840 } | |
841 | |
842 // Remove empty groups | |
843 for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); ite
r++) { | |
844 if (IndependentGroups[*iter].size() == 0) { | |
845 IndependentGroups.erase(*iter); | |
846 } | |
847 } | |
848 | |
849 #if DEBUG | |
850 PrintDebug("Investigated independent groups:\n"); | |
851 for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter !=
IndependentGroups.end(); iter++) { | |
852 DebugDump(iter->second, " group: "); | |
853 } | |
854 #endif | |
855 } | |
856 | |
857 Shape *MakeMultiple(BlockSet &Blocks, BlockSet& Entries, BlockBlockSetMap& I
ndependentGroups, Shape *Prev, BlockSet &NextEntries) { | |
858 PrintDebug("creating multiple block with %d inner groups\n", IndependentGr
oups.size()); | |
859 bool Fused = !!(Shape::IsSimple(Prev)); | |
860 MultipleShape *Multiple = new MultipleShape(); | |
861 Notice(Multiple); | |
862 BlockSet CurrEntries; | |
863 for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter !=
IndependentGroups.end(); iter++) { | |
864 Block *CurrEntry = iter->first; | |
865 BlockSet &CurrBlocks = iter->second; | |
866 PrintDebug(" multiple group with entry %d:\n", CurrEntry->Id); | |
867 DebugDump(CurrBlocks, " "); | |
868 // Create inner block | |
869 CurrEntries.clear(); | |
870 CurrEntries.insert(CurrEntry); | |
871 for (BlockSet::iterator iter = CurrBlocks.begin(); iter != CurrBlocks.en
d(); iter++) { | |
872 Block *CurrInner = *iter; | |
873 // Remove the block from the remaining blocks | |
874 Blocks.erase(CurrInner); | |
875 // Find new next entries and fix branches to them | |
876 for (BlockBranchMap::iterator iter = CurrInner->BranchesOut.begin(); i
ter != CurrInner->BranchesOut.end();) { | |
877 Block *CurrTarget = iter->first; | |
878 BlockBranchMap::iterator Next = iter; | |
879 Next++; | |
880 if (!contains(CurrBlocks, CurrTarget)) { | |
881 NextEntries.insert(CurrTarget); | |
882 Solipsize(CurrTarget, Branch::Break, Multiple, CurrBlocks); | |
883 } | |
884 iter = Next; // increment carefully because Solipsize can remove us | |
885 } | |
886 } | |
887 Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries, NUL
L); | |
888 // If we are not fused, then our entries will actually be checked | |
889 if (!Fused) { | |
890 CurrEntry->IsCheckedMultipleEntry = true; | |
891 } | |
892 } | |
893 DebugDump(Blocks, " remaining blocks after multiple:"); | |
894 // Add entries not handled as next entries, they are deferred | |
895 for (BlockSet::iterator iter = Entries.begin(); iter != Entries.end(); ite
r++) { | |
896 Block *Entry = *iter; | |
897 if (!contains(IndependentGroups, Entry)) { | |
898 NextEntries.insert(Entry); | |
899 } | |
900 } | |
901 // The multiple has been created, we can decide how to implement it | |
902 if (Multiple->InnerMap.size() >= 10) { | |
903 Multiple->UseSwitch = true; | |
904 Multiple->Breaks++; // switch captures breaks | |
905 } | |
906 return Multiple; | |
907 } | |
908 | |
909 // Main function. | |
910 // Process a set of blocks with specified entries, returns a shape | |
911 // The Make* functions receive a NextEntries. If they fill it with data, tho
se are the entries for the | |
912 // ->Next block on them, and the blocks are what remains in Blocks (which
Make* modify). In this way | |
913 // we avoid recursing on Next (imagine a long chain of Simples, if we recu
rsed we could blow the stack). | |
914 Shape *Process(BlockSet &Blocks, BlockSet& InitialEntries, Shape *Prev) { | |
915 PrintDebug("Process() called\n", 0); | |
916 BlockSet *Entries = &InitialEntries; | |
917 BlockSet TempEntries[2]; | |
918 int CurrTempIndex = 0; | |
919 BlockSet *NextEntries; | |
920 Shape *Ret = NULL; | |
921 #define Make(call) \ | |
922 Shape *Temp = call; \ | |
923 if (Prev) Prev->Next = Temp; \ | |
924 if (!Ret) Ret = Temp; \ | |
925 if (!NextEntries->size()) { PrintDebug("Process() returning\n", 0); retu
rn Ret; } \ | |
926 Prev = Temp; \ | |
927 Entries = NextEntries; \ | |
928 continue; | |
929 while (1) { | |
930 PrintDebug("Process() running\n", 0); | |
931 DebugDump(Blocks, " blocks : "); | |
932 DebugDump(*Entries, " entries: "); | |
933 | |
934 CurrTempIndex = 1-CurrTempIndex; | |
935 NextEntries = &TempEntries[CurrTempIndex]; | |
936 NextEntries->clear(); | |
937 | |
938 if (Entries->size() == 0) return Ret; | |
939 if (Entries->size() == 1) { | |
940 Block *Curr = *(Entries->begin()); | |
941 if (Parent->Emulate) { | |
942 Make(MakeEmulated(Blocks, Curr, *NextEntries)); | |
943 } | |
944 if (Curr->BranchesIn.size() == 0) { | |
945 // One entry, no looping ==> Simple | |
946 Make(MakeSimple(Blocks, Curr, *NextEntries)); | |
947 } | |
948 // One entry, looping ==> Loop | |
949 Make(MakeLoop(Blocks, *Entries, *NextEntries)); | |
950 } | |
951 | |
952 // More than one entry, try to eliminate through a Multiple groups of | |
953 // independent blocks from an entry/ies. It is important to remove throu
gh | |
954 // multiples as opposed to looping since the former is more performant. | |
955 BlockBlockSetMap IndependentGroups; | |
956 FindIndependentGroups(*Entries, IndependentGroups); | |
957 | |
958 PrintDebug("Independent groups: %d\n", IndependentGroups.size()); | |
959 | |
960 if (IndependentGroups.size() > 0) { | |
961 // We can handle a group in a multiple if its entry cannot be reached
by another group. | |
962 // Note that it might be reachable by itself - a loop. But that is fin
e, we will create | |
963 // a loop inside the multiple block (which is the performant order to
do it). | |
964 for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter
!= IndependentGroups.end();) { | |
965 Block *Entry = iter->first; | |
966 BlockSet &Group = iter->second; | |
967 BlockBlockSetMap::iterator curr = iter++; // iterate carefully, we m
ay delete | |
968 for (BlockSet::iterator iterBranch = Entry->BranchesIn.begin(); iter
Branch != Entry->BranchesIn.end(); iterBranch++) { | |
969 Block *Origin = *iterBranch; | |
970 if (!contains(Group, Origin)) { | |
971 // Reached from outside the group, so we cannot handle this | |
972 PrintDebug("Cannot handle group with entry %d because of incomin
g branch from %d\n", Entry->Id, Origin->Id); | |
973 IndependentGroups.erase(curr); | |
974 break; | |
975 } | |
976 } | |
977 } | |
978 | |
979 // As an optimization, if we have 2 independent groups, and one is a s
mall dead end, we can handle only that dead end. | |
980 // The other then becomes a Next - without nesting in the code and rec
ursion in the analysis. | |
981 // TODO: if the larger is the only dead end, handle that too | |
982 // TODO: handle >2 groups | |
983 // TODO: handle not just dead ends, but also that do not branch to the
NextEntries. However, must be careful | |
984 // there since we create a Next, and that Next can prevent elimi
nating a break (since we no longer | |
985 // naturally reach the same place), which may necessitate a one-
time loop, which makes the unnesting | |
986 // pointless. | |
987 if (IndependentGroups.size() == 2) { | |
988 // Find the smaller one | |
989 BlockBlockSetMap::iterator iter = IndependentGroups.begin(); | |
990 Block *SmallEntry = iter->first; | |
991 int SmallSize = iter->second.size(); | |
992 iter++; | |
993 Block *LargeEntry = iter->first; | |
994 int LargeSize = iter->second.size(); | |
995 if (SmallSize != LargeSize) { // ignore the case where they are iden
tical - keep things symmetrical there | |
996 if (SmallSize > LargeSize) { | |
997 Block *Temp = SmallEntry; | |
998 SmallEntry = LargeEntry; | |
999 LargeEntry = Temp; // Note: we did not flip the Sizes too, they
are now invalid. TODO: use the smaller size as a limit? | |
1000 } | |
1001 // Check if dead end | |
1002 bool DeadEnd = true; | |
1003 BlockSet &SmallGroup = IndependentGroups[SmallEntry]; | |
1004 for (BlockSet::iterator iter = SmallGroup.begin(); iter != SmallGr
oup.end(); iter++) { | |
1005 Block *Curr = *iter; | |
1006 for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin();
iter != Curr->BranchesOut.end(); iter++) { | |
1007 Block *Target = iter->first; | |
1008 if (!contains(SmallGroup, Target)) { | |
1009 DeadEnd = false; | |
1010 break; | |
1011 } | |
1012 } | |
1013 if (!DeadEnd) break; | |
1014 } | |
1015 if (DeadEnd) { | |
1016 PrintDebug("Removing nesting by not handling large group because
small group is dead end\n", 0); | |
1017 IndependentGroups.erase(LargeEntry); | |
1018 } | |
1019 } | |
1020 } | |
1021 | |
1022 PrintDebug("Handleable independent groups: %d\n", IndependentGroups.si
ze()); | |
1023 | |
1024 if (IndependentGroups.size() > 0) { | |
1025 // Some groups removable ==> Multiple | |
1026 Make(MakeMultiple(Blocks, *Entries, IndependentGroups, Prev, *NextEn
tries)); | |
1027 } | |
1028 } | |
1029 // No independent groups, must be loopable ==> Loop | |
1030 Make(MakeLoop(Blocks, *Entries, *NextEntries)); | |
1031 } | |
1032 } | |
1033 }; | |
1034 | |
1035 // Main | |
1036 | |
1037 BlockSet AllBlocks; | |
1038 for (BlockSet::iterator iter = Pre.Live.begin(); iter != Pre.Live.end(); iter+
+) { | |
1039 Block *Curr = *iter; | |
1040 AllBlocks.insert(Curr); | |
1041 #if DEBUG | |
1042 PrintDebug("Adding block %d (%s)\n", Curr->Id, Curr->Code); | |
1043 #endif | |
1044 } | |
1045 | |
1046 BlockSet Entries; | |
1047 Entries.insert(Entry); | |
1048 Root = Analyzer(this).Process(AllBlocks, Entries, NULL); | |
1049 assert(Root); | |
1050 | |
1051 // Post optimizations | |
1052 | |
1053 struct PostOptimizer { | |
1054 Relooper *Parent; | |
1055 void *Closure; | |
1056 | |
1057 PostOptimizer(Relooper *ParentInit) : Parent(ParentInit), Closure(NULL) {} | |
1058 | |
1059 #define RECURSE_Multiple(shape, func) \ | |
1060 for (IdShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->I
nnerMap.end(); iter++) { \ | |
1061 func(iter->second); \ | |
1062 } | |
1063 #define RECURSE_Loop(shape, func) \ | |
1064 func(shape->Inner); | |
1065 #define RECURSE(shape, func) RECURSE_##shape(shape, func); | |
1066 | |
1067 #define SHAPE_SWITCH(var, simple, multiple, loop) \ | |
1068 if (SimpleShape *Simple = Shape::IsSimple(var)) { \ | |
1069 (void)Simple; \ | |
1070 simple; \ | |
1071 } else if (MultipleShape *Multiple = Shape::IsMultiple(var)) { \ | |
1072 (void)Multiple; \ | |
1073 multiple; \ | |
1074 } else if (LoopShape *Loop = Shape::IsLoop(var)) { \ | |
1075 (void)Loop; \ | |
1076 loop; \ | |
1077 } | |
1078 | |
1079 // Find the blocks that natural control flow can get us directly to, or thro
ugh a multiple that we ignore | |
1080 void FollowNaturalFlow(Shape *S, BlockSet &Out) { | |
1081 SHAPE_SWITCH(S, { | |
1082 Out.insert(Simple->Inner); | |
1083 }, { | |
1084 for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Mul
tiple->InnerMap.end(); iter++) { | |
1085 FollowNaturalFlow(iter->second, Out); | |
1086 } | |
1087 FollowNaturalFlow(Multiple->Next, Out); | |
1088 }, { | |
1089 FollowNaturalFlow(Loop->Inner, Out); | |
1090 }); | |
1091 } | |
1092 | |
1093 void FindNaturals(Shape *Root, Shape *Otherwise=NULL) { | |
1094 if (Root->Next) { | |
1095 Root->Natural = Root->Next; | |
1096 FindNaturals(Root->Next, Otherwise); | |
1097 } else { | |
1098 Root->Natural = Otherwise; | |
1099 } | |
1100 | |
1101 SHAPE_SWITCH(Root, { | |
1102 }, { | |
1103 for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Mul
tiple->InnerMap.end(); iter++) { | |
1104 FindNaturals(iter->second, Root->Natural); | |
1105 } | |
1106 }, { | |
1107 FindNaturals(Loop->Inner, Loop->Inner); | |
1108 }); | |
1109 } | |
1110 | |
1111 // Remove unneeded breaks and continues. | |
1112 // A flow operation is trivially unneeded if the shape we naturally get to b
y normal code | |
1113 // execution is the same as the flow forces us to. | |
1114 void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLo
op=NULL, unsigned Depth=0) { | |
1115 BlockSet NaturalBlocks; | |
1116 FollowNaturalFlow(Natural, NaturalBlocks); | |
1117 Shape *Next = Root; | |
1118 while (Next) { | |
1119 Root = Next; | |
1120 Next = NULL; | |
1121 SHAPE_SWITCH(Root, { | |
1122 if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out
the loop (TODO: only for breaks, not continue) | |
1123 | |
1124 if (Simple->Next) { | |
1125 if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut
.size() == 2 && Depth < 20) { | |
1126 // If there is a next block, we already know at Simple creation ti
me to make direct branches, | |
1127 // and we can do nothing more in general. But, we try to optimize
the case of a break and | |
1128 // a direct: This would normally be if (break?) { break; } .. bu
t if we | |
1129 // make sure to nest the else, we can save the break, if (!break?
) { .. } . This is also | |
1130 // better because the more canonical nested form is easier to furt
her optimize later. The | |
1131 // downside is more nesting, which adds to size in builds with whi
tespace. | |
1132 // Note that we avoid switches, as it complicates control flow and
is not relevant | |
1133 // for the common case we optimize here. | |
1134 bool Found = false; | |
1135 bool Abort = false; | |
1136 for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranc
hesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { | |
1137 Block *Target = iter->first; | |
1138 Branch *Details = iter->second; | |
1139 if (Details->Type == Branch::Break) { | |
1140 Found = true; | |
1141 if (!contains(NaturalBlocks, Target)) Abort = true; | |
1142 } else if (Details->Type != Branch::Direct) { | |
1143 Abort = true; | |
1144 } | |
1145 } | |
1146 if (Found && !Abort) { | |
1147 for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBra
nchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { | |
1148 Branch *Details = iter->second; | |
1149 if (Details->Type == Branch::Break) { | |
1150 Details->Type = Branch::Direct; | |
1151 if (MultipleShape *Multiple = Shape::IsMultiple(Details->Anc
estor)) { | |
1152 Multiple->Breaks--; | |
1153 } | |
1154 } else { | |
1155 assert(Details->Type == Branch::Direct); | |
1156 Details->Type = Branch::Nested; | |
1157 } | |
1158 } | |
1159 } | |
1160 Depth++; // this optimization increases depth, for us and all our
next chain (i.e., until this call returns) | |
1161 } | |
1162 Next = Simple->Next; | |
1163 } else { | |
1164 // If there is no next then Natural is where we will | |
1165 // go to by doing nothing, so we can potentially optimize some branc
hes to direct. | |
1166 for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranche
sOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { | |
1167 Block *Target = iter->first; | |
1168 Branch *Details = iter->second; | |
1169 if (Details->Type != Branch::Direct && contains(NaturalBlocks, Tar
get)) { // note: cannot handle split blocks | |
1170 Details->Type = Branch::Direct; | |
1171 if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancesto
r)) { | |
1172 Multiple->Breaks--; | |
1173 } | |
1174 } else if (Details->Type == Branch::Break && LastLoop && LastLoop-
>Natural == Details->Ancestor->Natural) { | |
1175 // it is important to simplify breaks, as simpler breaks enable
other optimizations | |
1176 Details->Labeled = false; | |
1177 if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancesto
r)) { | |
1178 Multiple->Breaks--; | |
1179 } | |
1180 } | |
1181 } | |
1182 } | |
1183 }, { | |
1184 for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != M
ultiple->InnerMap.end(); iter++) { | |
1185 RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->Breaks ?
NULL : LastLoop, Depth+1); | |
1186 } | |
1187 Next = Multiple->Next; | |
1188 }, { | |
1189 RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop, Depth+1); | |
1190 Next = Loop->Next; | |
1191 }); | |
1192 } | |
1193 } | |
1194 | |
1195 // After we know which loops exist, we can calculate which need to be labele
d | |
1196 void FindLabeledLoops(Shape *Root) { | |
1197 bool First = Closure == NULL; | |
1198 if (First) { | |
1199 Closure = (void*)(new std::stack<Shape*>); | |
1200 } | |
1201 std::stack<Shape*> &LoopStack = *((std::stack<Shape*>*)Closure); | |
1202 | |
1203 Shape *Next = Root; | |
1204 while (Next) { | |
1205 Root = Next; | |
1206 Next = NULL; | |
1207 | |
1208 SHAPE_SWITCH(Root, { | |
1209 MultipleShape *Fused = Shape::IsMultiple(Root->Next); | |
1210 // If we are fusing a Multiple with a loop into this Simple, then visi
t it now | |
1211 if (Fused && Fused->Breaks) { | |
1212 LoopStack.push(Fused); | |
1213 } | |
1214 if (Simple->Inner->BranchVar) { | |
1215 LoopStack.push(NULL); // a switch means breaks are now useless, push
a dummy | |
1216 } | |
1217 if (Fused) { | |
1218 if (Fused->UseSwitch) { | |
1219 LoopStack.push(NULL); // a switch means breaks are now useless, pu
sh a dummy | |
1220 } | |
1221 RECURSE_Multiple(Fused, FindLabeledLoops); | |
1222 } | |
1223 for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesO
ut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { | |
1224 Branch *Details = iter->second; | |
1225 if (Details->Type == Branch::Break || Details->Type == Branch::Conti
nue) { | |
1226 assert(LoopStack.size() > 0); | |
1227 if (Details->Ancestor != LoopStack.top() && Details->Labeled) { | |
1228 LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); | |
1229 Labeled->Labeled = true; | |
1230 } else { | |
1231 Details->Labeled = false; | |
1232 } | |
1233 } | |
1234 } | |
1235 if (Fused && Fused->UseSwitch) { | |
1236 LoopStack.pop(); | |
1237 } | |
1238 if (Simple->Inner->BranchVar) { | |
1239 LoopStack.pop(); | |
1240 } | |
1241 if (Fused && Fused->Breaks) { | |
1242 LoopStack.pop(); | |
1243 } | |
1244 if (Fused) { | |
1245 Next = Fused->Next; | |
1246 } else { | |
1247 Next = Root->Next; | |
1248 } | |
1249 }, { | |
1250 if (Multiple->Breaks) { | |
1251 LoopStack.push(Multiple); | |
1252 } | |
1253 RECURSE(Multiple, FindLabeledLoops); | |
1254 if (Multiple->Breaks) { | |
1255 LoopStack.pop(); | |
1256 } | |
1257 Next = Root->Next; | |
1258 }, { | |
1259 LoopStack.push(Loop); | |
1260 RECURSE(Loop, FindLabeledLoops); | |
1261 LoopStack.pop(); | |
1262 Next = Root->Next; | |
1263 }); | |
1264 } | |
1265 | |
1266 if (First) { | |
1267 delete (std::stack<Shape*>*)Closure; | |
1268 } | |
1269 } | |
1270 | |
1271 void Process(Shape *Root) { | |
1272 FindNaturals(Root); | |
1273 RemoveUnneededFlows(Root); | |
1274 FindLabeledLoops(Root); | |
1275 } | |
1276 }; | |
1277 | |
1278 PrintDebug("=== Optimizing shapes ===\n", 0); | |
1279 | |
1280 PostOptimizer(this).Process(Root); | |
1281 } | |
1282 | |
1283 void Relooper::Render() { | |
1284 OutputBuffer = OutputBufferRoot; | |
1285 assert(Root); | |
1286 Root->Render(false); | |
1287 } | |
1288 | |
1289 void Relooper::SetOutputBuffer(char *Buffer, int Size) { | |
1290 OutputBufferRoot = OutputBuffer = Buffer; | |
1291 OutputBufferSize = Size; | |
1292 OutputBufferOwned = false; | |
1293 } | |
1294 | |
1295 void Relooper::MakeOutputBuffer(int Size) { | |
1296 if (OutputBufferRoot && OutputBufferSize >= Size && OutputBufferOwned) return; | |
1297 OutputBufferRoot = OutputBuffer = (char*)malloc(Size); | |
1298 OutputBufferSize = Size; | |
1299 OutputBufferOwned = true; | |
1300 } | |
1301 | |
1302 char *Relooper::GetOutputBuffer() { | |
1303 return OutputBufferRoot; | |
1304 } | |
1305 | |
1306 void Relooper::SetAsmJSMode(int On) { | |
1307 AsmJS = On; | |
1308 } | |
1309 | |
1310 #if DEBUG | |
1311 // Debugging | |
1312 | |
1313 void Debugging::Dump(BlockSet &Blocks, const char *prefix) { | |
1314 if (prefix) printf("%s ", prefix); | |
1315 for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) { | |
1316 Block *Curr = *iter; | |
1317 printf("%d:\n", Curr->Id); | |
1318 for (BlockBranchMap::iterator iter2 = Curr->BranchesOut.begin(); iter2 != Cu
rr->BranchesOut.end(); iter2++) { | |
1319 Block *Other = iter2->first; | |
1320 printf(" -> %d\n", Other->Id); | |
1321 assert(contains(Other->BranchesIn, Curr)); | |
1322 } | |
1323 } | |
1324 } | |
1325 | |
1326 void Debugging::Dump(Shape *S, const char *prefix) { | |
1327 if (prefix) printf("%s ", prefix); | |
1328 if (!S) { | |
1329 printf(" (null)\n"); | |
1330 return; | |
1331 } | |
1332 printf(" %d ", S->Id); | |
1333 SHAPE_SWITCH(S, { | |
1334 printf("<< Simple with block %d\n", Simple->Inner->Id); | |
1335 }, { | |
1336 printf("<< Multiple\n"); | |
1337 for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multipl
e->InnerMap.end(); iter++) { | |
1338 printf(" with entry %d\n", iter->first); | |
1339 } | |
1340 }, { | |
1341 printf("<< Loop\n"); | |
1342 }); | |
1343 } | |
1344 | |
1345 static void PrintDebug(const char *Format, ...) { | |
1346 printf("// "); | |
1347 va_list Args; | |
1348 va_start(Args, Format); | |
1349 vprintf(Format, Args); | |
1350 va_end(Args); | |
1351 } | |
1352 #endif | |
1353 | |
1354 // C API - useful for binding to other languages | |
1355 | |
1356 typedef std::map<void*, int> VoidIntMap; | |
1357 VoidIntMap __blockDebugMap__; // maps block pointers in currently running code t
o block ids, for generated debug output | |
1358 | |
1359 extern "C" { | |
1360 | |
1361 RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size) { | |
1362 #if DEBUG | |
1363 printf("#include \"Relooper.h\"\n"); | |
1364 printf("int main() {\n"); | |
1365 printf(" char buffer[100000];\n"); | |
1366 printf(" rl_set_output_buffer(buffer);\n"); | |
1367 #endif | |
1368 Relooper::SetOutputBuffer(buffer, size); | |
1369 } | |
1370 | |
1371 RELOOPERDLL_API void rl_make_output_buffer(int size) { | |
1372 Relooper::SetOutputBuffer((char*)malloc(size), size); | |
1373 } | |
1374 | |
1375 RELOOPERDLL_API void rl_set_asm_js_mode(int on) { | |
1376 Relooper::SetAsmJSMode(on); | |
1377 } | |
1378 | |
1379 RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var) { | |
1380 Block *ret = new Block(text, branch_var); | |
1381 #if DEBUG | |
1382 printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id); | |
1383 __blockDebugMap__[ret] = ret->Id; | |
1384 printf(" block_map[%d] = b%d;\n", ret->Id, ret->Id); | |
1385 #endif | |
1386 return ret; | |
1387 } | |
1388 | |
1389 RELOOPERDLL_API void rl_delete_block(void *block) { | |
1390 #if DEBUG | |
1391 printf(" rl_delete_block(block_map[%d]);\n", ((Block*)block)->Id); | |
1392 #endif | |
1393 delete (Block*)block; | |
1394 } | |
1395 | |
1396 RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *co
ndition, const char *code) { | |
1397 #if DEBUG | |
1398 printf(" rl_block_add_branch_to(block_map[%d], block_map[%d], %s%s%s, %s%s%s)
;\n", ((Block*)from)->Id, ((Block*)to)->Id, condition ? "\"" : "", condition ? c
ondition : "NULL", condition ? "\"" : "", code ? "\"" : "", code ? code : "NULL"
, code ? "\"" : ""); | |
1399 #endif | |
1400 ((Block*)from)->AddBranchTo((Block*)to, condition, code); | |
1401 } | |
1402 | |
1403 RELOOPERDLL_API void *rl_new_relooper() { | |
1404 #if DEBUG | |
1405 printf(" void *block_map[10000];\n"); | |
1406 printf(" void *rl = rl_new_relooper();\n"); | |
1407 #endif | |
1408 return new Relooper; | |
1409 } | |
1410 | |
1411 RELOOPERDLL_API void rl_delete_relooper(void *relooper) { | |
1412 delete (Relooper*)relooper; | |
1413 } | |
1414 | |
1415 RELOOPERDLL_API void rl_relooper_add_block(void *relooper, void *block) { | |
1416 #if DEBUG | |
1417 printf(" rl_relooper_add_block(rl, block_map[%d]);\n", ((Block*)block)->Id); | |
1418 #endif | |
1419 ((Relooper*)relooper)->AddBlock((Block*)block); | |
1420 } | |
1421 | |
1422 RELOOPERDLL_API void rl_relooper_calculate(void *relooper, void *entry) { | |
1423 #if DEBUG | |
1424 printf(" rl_relooper_calculate(rl, block_map[%d]);\n", ((Block*)entry)->Id); | |
1425 printf(" rl_relooper_render(rl);\n"); | |
1426 printf(" rl_delete_relooper(rl);\n"); | |
1427 printf(" puts(buffer);\n"); | |
1428 printf(" return 0;\n"); | |
1429 printf("}\n"); | |
1430 #endif | |
1431 ((Relooper*)relooper)->Calculate((Block*)entry); | |
1432 } | |
1433 | |
1434 RELOOPERDLL_API void rl_relooper_render(void *relooper) { | |
1435 ((Relooper*)relooper)->Render(); | |
1436 } | |
1437 | |
1438 } | |
OLD | NEW |