Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright 2012 the V8 project authors. All rights reserved. | |
| 4 # Redistribution and use in source and binary forms, with or without | |
| 5 # modification, are permitted provided that the following conditions are | |
| 6 # met: | |
| 7 # | |
| 8 # * Redistributions of source code must retain the above copyright | |
| 9 # notice, this list of conditions and the following disclaimer. | |
| 10 # * Redistributions in binary form must reproduce the above | |
| 11 # copyright notice, this list of conditions and the following | |
| 12 # disclaimer in the documentation and/or other materials provided | |
| 13 # with the distribution. | |
| 14 # * Neither the name of Google Inc. nor the names of its | |
| 15 # contributors may be used to endorse or promote products derived | |
| 16 # from this software without specific prior written permission. | |
| 17 # | |
| 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 | |
| 30 | |
| 31 import optparse | |
| 32 import os | |
| 33 from os.path import join | |
| 34 import sys | |
| 35 import shlex | |
|
Jakob Kummerow
2012/11/16 17:31:57
nit: sort imports alphabetically please
mvstanton
2013/01/06 11:26:36
Done.
| |
| 36 import collections | |
| 37 import tempfile | |
| 38 import subprocess | |
| 39 import glob | |
| 40 | |
| 41 | |
| 42 # Parse() returns: | |
| 43 # a map of compilations (functions) | |
| 44 # each compilation has a map of phases, and a name | |
| 45 # each phase has a map of blocks, and a name | |
| 46 # each block has a map of locals, and a map of statements | |
| 47 # functions, phases, blocks, statements | |
| 48 # | |
| 49 # The first statement of foo, in final phase | |
| 50 # data["foo"]["Z_Code generation"]["B0"][0] | |
| 51 # | |
| 52 # a phase is the typical area of work. Offers | |
| 53 # phase.visit_statements(visitor) - visit every statement in the phase | |
| 54 # phase.visit_blocks(visitor) | |
| 55 # | |
| 56 # of course, | |
| 57 # blocks.visit_statements(visitor) | |
| 58 # blocks.visit_locals(visitor) | |
| 59 # | |
| 60 | |
| 61 | |
| 62 USAGE="""usage: %prog [OPTION]... | |
| 63 | |
| 64 Looks at a hydrogen cfg file and produces graphviz (ie, dot) output. | |
| 65 Output is directed to stdout for further customization before creating | |
| 66 a graph. | |
| 67 | |
| 68 Examples: | |
| 69 # Run on the last function defined in the file, looking at the final | |
| 70 # HIR | |
| 71 $ %prog > output.dot | |
| 72 | |
| 73 # Create the graphic too, this time with HIR, all on one line | |
| 74 $ %prog --displayHIR | dot -Tpng > out.png | |
| 75 | |
| 76 # Examine a different phase of compilation for the last function | |
| 77 # Output to the screen | |
| 78 $ %prog --phase="H_Redundant phi elimination" --displayHIR | |
| 79 | |
| 80 # Create an html report of the last function, with all HIR | |
| 81 $ %prog --command="report" | |
| 82 """ | |
| 83 | |
| 84 | |
| 85 def OrderedDictionary(): | |
| 86 return collections.OrderedDict() | |
| 87 | |
| 88 | |
| 89 def die(inputobj,expected): | |
|
Jakob Kummerow
2012/11/16 17:31:57
Looking at what this method does, shouldn't it be
mvstanton
2013/01/06 11:26:36
thx. I changed to use assert. I kept diemsg() as a
| |
| 90 if inputobj != expected: | |
| 91 print "Expected: %s Got: %s" % (inputobj, expected) | |
| 92 sys.exit(1) | |
| 93 | |
| 94 | |
| 95 def diemsg(msg): | |
| 96 print "Unexpected error: %s" % msg | |
| 97 sys.exit(1) | |
| 98 | |
| 99 | |
| 100 class Printer(object): | |
| 101 def __init__(self): | |
| 102 self.stream = sys.stdout | |
| 103 | |
| 104 def CreateFile(self,filename): | |
|
Jakob Kummerow
2012/11/16 17:31:57
Create/close pairs are brittle. The most robust wa
mvstanton
2013/01/06 11:26:36
Done.
| |
| 105 self.stream = open(filename,"w+t") | |
| 106 | |
| 107 def CloseFile(self): | |
| 108 self.stream.close() | |
| 109 | |
| 110 def SetStream(self,newStream): | |
| 111 self.stream = newStream | |
| 112 | |
| 113 def Print(self,string): | |
| 114 self.stream.write(string) | |
| 115 | |
| 116 def PrintLine(self,string): | |
| 117 self.Print(string + "\n") | |
| 118 | |
| 119 | |
| 120 class Block(object): | |
| 121 """A compilation block.""" | |
| 122 | |
| 123 def __init__(self, name, predecessors, successors): | |
| 124 self.name = name | |
| 125 self.predecessors = predecessors | |
| 126 self.successors = successors | |
| 127 self.hir = OrderedDictionary() | |
| 128 self.lir = OrderedDictionary() | |
| 129 self.locals = OrderedDictionary() | |
| 130 | |
| 131 def GetName(self): | |
| 132 return self.name | |
| 133 | |
| 134 def GetPredecessors(self): | |
| 135 return self.predecessors | |
| 136 | |
| 137 def GetSuccessors(self): | |
| 138 return self.successors | |
| 139 | |
| 140 def AddHIR(self,statement): | |
| 141 self.hir[len(self.hir)] = statement | |
|
Jakob Kummerow
2012/11/16 17:31:57
self.hir.append(statement)
| |
| 142 | |
| 143 def GetHIRs(self): | |
| 144 return self.hir.values() | |
| 145 | |
| 146 def AddLIR(self,statement): | |
| 147 self.lir[len(self.lir)] = statement | |
| 148 | |
| 149 def GetLIRs(self): | |
| 150 return self.lir.values() | |
| 151 | |
| 152 def visit(self,visitor): | |
| 153 """Visit every HIR statement in the block.""" | |
| 154 for h in hir.values(): | |
| 155 visitor(h) | |
| 156 | |
| 157 def AddLocal(self,name,local): | |
| 158 self.locals[name] = local | |
| 159 | |
| 160 def visit_locals(self,visitor): | |
| 161 for l in self.locals.values(): | |
| 162 visitor(l) | |
| 163 | |
| 164 def GetLocals(self): | |
| 165 return self.locals.values() | |
| 166 | |
| 167 def __str__(self): | |
| 168 return "%s (pred: %d) (succ: %d)" % ( | |
| 169 self.name, | |
|
Jakob Kummerow
2012/11/16 17:31:57
4-space indentation when you break inside ()
mvstanton
2013/01/06 11:26:36
Done.
| |
| 170 len(self.predecessors), | |
| 171 len(self.successors)) | |
| 172 | |
| 173 | |
| 174 class Phase(object): | |
| 175 """A compilation phase.""" | |
| 176 | |
| 177 def __init__(self, name): | |
| 178 self.name = name | |
| 179 self.blocks = OrderedDictionary() | |
| 180 | |
| 181 def GetName(self): | |
| 182 return self.name | |
| 183 | |
| 184 def AddBlock(self,name,block): | |
| 185 self.blocks[name] = block | |
| 186 | |
| 187 def GetBlocks(self): | |
| 188 return self.blocks.values() | |
| 189 | |
| 190 def GetBlock(self,block): | |
| 191 return self.blocks[block] | |
| 192 | |
| 193 def visit_statements(self,visitor): | |
| 194 for block in self.blocks: | |
| 195 block.visit(visitor) | |
| 196 | |
| 197 def visit_blocks(self,visitor): | |
| 198 for block in self.blocks.values(): | |
| 199 visitor(block) | |
| 200 | |
| 201 def Print(self,printer): | |
| 202 printer.Print(self) | |
| 203 for block in self.blocks.values(): | |
| 204 printer.Print(block) | |
| 205 | |
| 206 def __str__(self): | |
| 207 return "%s (%d blocks)" % ( | |
| 208 self.name, | |
| 209 len(self.blocks)) | |
| 210 | |
| 211 | |
| 212 class Compilation(object): | |
| 213 """A compilation unit.""" | |
| 214 | |
| 215 def __init__(self, name, method): | |
| 216 self.phases = OrderedDictionary() | |
| 217 self.name = name | |
| 218 self.method = method | |
| 219 | |
| 220 def AddPhase(self, name, phase): | |
| 221 self.phases[name] = phase | |
| 222 | |
| 223 def AllPhases(self): | |
| 224 for phase in self.phases.itervalues(): | |
| 225 yield phase | |
| 226 | |
| 227 def AllPhaseNames(self): | |
| 228 return self.phases.keys() | |
| 229 | |
| 230 def GetName(self): | |
| 231 return self.name | |
| 232 | |
| 233 def GetPhase(self,phase): | |
| 234 return self.phases[phase] | |
| 235 | |
| 236 def GetPreviousPhase(self,phase): | |
| 237 # useful for diffs | |
| 238 index = self.phases.keys().index(phase) | |
| 239 if index == 0: | |
| 240 diemsg("No previous phase of " + phase) | |
| 241 return self.phases[self.phases.keys()[index - 1]] | |
| 242 | |
| 243 def Print(self,printer): | |
| 244 printer.PrintLine('Compilation ' + self.name) | |
| 245 for phase in self.AllPhases(): | |
| 246 phase.Print() | |
| 247 | |
| 248 def __str__(self): | |
| 249 return "%s (%d phases)" % ( | |
| 250 self.name, | |
| 251 len(self.phases)) | |
| 252 | |
| 253 | |
| 254 def BuildOptions(): | |
| 255 result = optparse.OptionParser(USAGE) | |
| 256 result.add_option("--input", | |
| 257 help="Input file (default hydrogen.cfg)", | |
|
Jakob Kummerow
2012/11/16 17:31:57
No need to type the default value twice:
help="In
mvstanton
2013/01/06 11:26:36
Done.
| |
| 258 default="hydrogen.cfg") | |
| 259 result.add_option("--command", | |
| 260 help="The command to run: 'diff','report','blocks' (default) ", | |
|
Jakob Kummerow
2012/11/16 17:31:57
nit: long line
mvstanton
2013/01/06 11:26:36
Done.
| |
| 261 default="blocks") | |
| 262 result.add_option("--function", | |
| 263 help="The function to analyze (default @last)", | |
| 264 default="@last") | |
| 265 result.add_option("--phase", | |
| 266 help="The compilation phase to examine (default Z_Code generation)", | |
| 267 default="Z_Code generation") | |
| 268 result.add_option("--displayHIR", | |
| 269 help="Display HIR in blocks? (default false)", | |
| 270 default=False, action="store_true") | |
| 271 result.add_option("--displayLIR", | |
| 272 help="Display LIR in blocks? (default false)", | |
| 273 default=False, action="store_true") | |
| 274 result.add_option("-v", "--verbose", help="Verbose output", | |
| 275 default=False, action="store_true") | |
| 276 result.add_option("-d", "--debug", help="Debug output", | |
| 277 default=False, action="store_true") | |
| 278 return result | |
| 279 | |
| 280 | |
| 281 def ProcessOptions(options): | |
| 282 if not options.command in ["blocks","diff","report"]: | |
| 283 print "Unknown command %s" % options.command | |
| 284 return False | |
| 285 return True | |
| 286 | |
| 287 | |
| 288 def parseKeyString(line): | |
| 289 tokens = shlex.split(line.strip()) | |
| 290 return tokens | |
| 291 | |
| 292 | |
| 293 class CFGReader(object): | |
| 294 """CFG file reader.""" | |
| 295 | |
| 296 def __init__(self, filename): | |
| 297 self.file = open(filename, "r") | |
| 298 self.savedline = None | |
| 299 | |
| 300 def Dispose(self): | |
| 301 self.log.close() | |
| 302 self.log_file.close() | |
| 303 | |
| 304 def pushline(self,line): | |
| 305 self.savedline = line | |
| 306 | |
| 307 def nextline(self): | |
| 308 # we can push one line back | |
| 309 if self.savedline: | |
| 310 s = self.savedline | |
| 311 self.savedline = None | |
| 312 return s | |
| 313 | |
| 314 return self.file.readline() | |
| 315 | |
| 316 def Parse(self): | |
| 317 """Return a map of compilation units.""" | |
| 318 compilations = OrderedDictionary() | |
| 319 while True: | |
| 320 compilation = self.ParseCompilation() | |
| 321 if not compilation: | |
| 322 break | |
| 323 compilations[compilation.name] = compilation | |
| 324 return compilations | |
| 325 | |
| 326 def ParseCompilation(self): | |
| 327 line = self.nextline() | |
| 328 if not line: | |
| 329 return None | |
| 330 die("begin_compilation",line.strip()) | |
| 331 pair = parseKeyString(self.nextline()) | |
| 332 die("name",pair[0]) | |
| 333 name = pair[1] | |
| 334 compilation = Compilation(name,name) | |
| 335 # now read into configs | |
| 336 while line.strip() != "end_compilation": | |
| 337 line = self.nextline() | |
| 338 if not line: | |
| 339 diemsg("Unexpected end of file") | |
| 340 | |
| 341 while True: | |
| 342 phase = self.ParsePhase() | |
| 343 if not phase: | |
| 344 break | |
| 345 compilation.AddPhase(phase.name,phase) | |
| 346 return compilation | |
| 347 | |
| 348 def ParseHIRs(self,block): | |
| 349 while True: | |
| 350 line = self.nextline().strip() | |
| 351 if line == "end_HIR": | |
| 352 break | |
| 353 # we don't need the first number ("0 ") and some symbols at the end | |
| 354 line = line[2:] | |
| 355 line = line.replace("<|@","") | |
| 356 block.AddHIR(line) | |
| 357 | |
| 358 def ParseLIRs(self,block): | |
| 359 while True: | |
| 360 line = self.nextline().strip() | |
| 361 if line == "end_LIR": | |
| 362 break | |
| 363 # we don't need some symbols at the end | |
| 364 line = line.replace("<|@","") | |
| 365 block.AddLIR(line) | |
| 366 | |
| 367 def ParseLocals(self,block): | |
| 368 while True: | |
| 369 line = self.nextline() | |
| 370 line = line.strip() | |
| 371 if line == "end_locals": | |
| 372 break | |
| 373 if len(line) > 0 and (line[0]>='0' and line[0]<='9'): | |
| 374 keys = parseKeyString(line) | |
| 375 block.AddLocal(keys[1],line) | |
| 376 | |
| 377 def ParseBlock(self): | |
| 378 line = self.nextline() | |
| 379 if not line: | |
| 380 return None | |
| 381 | |
| 382 if line.strip() != "begin_block": | |
| 383 self.pushline(line) | |
| 384 return None | |
| 385 | |
| 386 die("begin_block",line.strip()) | |
| 387 pair = parseKeyString(self.nextline()) | |
| 388 die("name",pair[0]) | |
| 389 name = pair[1] | |
| 390 # read pred and succ | |
| 391 while True: | |
| 392 line = self.nextline() | |
| 393 if line.strip() == "begin_states": | |
| 394 break; | |
| 395 pair = parseKeyString(line) | |
| 396 if pair[0] == "predecessors": | |
| 397 pred = pair[1:] | |
| 398 elif pair[0] == "successors": | |
| 399 succ = pair[1:] | |
| 400 | |
| 401 block = Block(name,pred,succ) | |
| 402 # add hrs if found | |
| 403 while True: | |
| 404 line = self.nextline().strip() | |
| 405 if line == "end_block": | |
| 406 break; | |
| 407 elif line == "begin_locals": | |
| 408 self.ParseLocals(block) | |
| 409 elif line == "begin_LIR": | |
| 410 self.ParseLIRs(block) | |
| 411 elif line == "begin_HIR": | |
| 412 self.ParseHIRs(block) | |
| 413 | |
| 414 return block | |
| 415 | |
| 416 def ParsePhase(self): | |
| 417 line = self.nextline() | |
| 418 if not line: | |
| 419 return None | |
| 420 | |
| 421 if line.strip() != "begin_cfg": | |
| 422 self.pushline(line) | |
| 423 return None | |
| 424 | |
| 425 die("begin_cfg",line.strip()) | |
| 426 pair = parseKeyString(self.nextline()) | |
| 427 die("name",pair[0]) | |
| 428 name = pair[1] | |
| 429 phase = Phase(name) | |
| 430 | |
| 431 while True: | |
| 432 block = self.ParseBlock() | |
| 433 if not block: | |
| 434 break | |
| 435 phase.AddBlock(block.name,block) | |
| 436 | |
| 437 # just consume blocks | |
| 438 # consume the "end_cfg" message | |
| 439 die("end_cfg", self.nextline().strip()) | |
| 440 return phase | |
| 441 | |
| 442 | |
| 443 def DumpCompilations(compilations,printer): | |
| 444 printer.Print("len = %d" % len(compilations)) | |
| 445 for comp in compilations.itervalues(): | |
| 446 comp.Print(printer) | |
| 447 | |
| 448 | |
| 449 def WriteTempFile(lines): | |
| 450 temp = tempfile.NamedTemporaryFile(mode="w+t") | |
| 451 for line in lines: | |
| 452 temp.write(line) | |
| 453 temp.write("\n") | |
| 454 temp.seek(0) | |
| 455 return temp | |
| 456 | |
| 457 | |
| 458 def diffText(lines1,lines2): | |
| 459 """Takes two lists of text lines, creates a diff output of them as an array""" | |
| 460 | |
| 461 try: | |
| 462 temp1 = WriteTempFile(lines1) | |
| 463 temp2 = WriteTempFile(lines2) | |
| 464 outputlines = [] | |
| 465 cmdline = "diff -u %s %s" % (temp1.name, temp2.name) | |
| 466 process = subprocess.Popen(cmdline, | |
| 467 shell=True, | |
| 468 stdout=subprocess.PIPE, | |
| 469 stderr=subprocess.STDOUT) | |
| 470 pipe = process.stdout | |
| 471 try: | |
| 472 headerLines = 2 | |
| 473 lineCount = 0 | |
| 474 for line in pipe: | |
| 475 if lineCount > headerLines: | |
| 476 # strip newline | |
| 477 outputlines.append(line.strip()) | |
| 478 lineCount = lineCount + 1 | |
| 479 except Exception as e: | |
| 480 diemsg("error in diff = " + str(e)) | |
| 481 finally: | |
| 482 pipe.close() | |
| 483 | |
| 484 finally: | |
| 485 temp1.close() | |
| 486 temp2.close() | |
| 487 | |
| 488 return outputlines | |
| 489 | |
| 490 | |
| 491 def Bold(line): | |
| 492 return "<b>%s</b>" % line | |
| 493 | |
| 494 | |
| 495 def Color(color,line): | |
| 496 return "<font color=\"%s\">%s</font>" % (color,line) | |
| 497 | |
| 498 | |
| 499 def LeftLineBreak(): | |
| 500 return "<br align=\"left\"/>" | |
| 501 | |
| 502 | |
| 503 def HtmlLikeEscape(line): | |
| 504 return line.replace("<","<").replace(">",">") | |
| 505 | |
| 506 | |
| 507 def TableBegin(): | |
| 508 return "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">\n" | |
| 509 | |
| 510 | |
| 511 def TableEnd(): | |
| 512 return "</table>\n" | |
| 513 | |
| 514 | |
| 515 def NameRow(blockName): | |
| 516 return "<tr><td>%s</td></tr>\n" % Bold(blockName) | |
| 517 | |
| 518 | |
| 519 def FormatLine(line): | |
| 520 tesc = HtmlLikeEscape(line) | |
| 521 # Deal with diff output | |
| 522 if tesc.startswith("-"): | |
| 523 tesc = Color("red",tesc) | |
| 524 elif tesc.startswith("+"): | |
| 525 tesc = Color("blue",tesc) | |
| 526 return " %s%s\n" % (tesc, LeftLineBreak()) | |
| 527 | |
| 528 | |
| 529 def FormatLines(lineList): | |
| 530 result = "" | |
| 531 if len(lineList) > 0: | |
| 532 result += "<tr><td>\n" | |
| 533 for l in lineList: | |
| 534 result = result + FormatLine(l) | |
| 535 result = result + "</td></tr>\n" | |
| 536 return result | |
| 537 | |
| 538 | |
| 539 def FormatLabel(blockName,localsList,hirList,lirList): | |
| 540 label = TableBegin() | |
| 541 label += NameRow(blockName) | |
| 542 label += FormatLines(localsList) | |
| 543 label += FormatLines(hirList) | |
| 544 label += FormatLines(lirList) | |
| 545 label += TableEnd() | |
| 546 return label | |
| 547 | |
| 548 | |
| 549 def DotBlockVisit(displayHIR,displayLIR,p,block): | |
| 550 localsList = [] | |
| 551 hirList = [] | |
| 552 lirList = [] | |
| 553 | |
|
Jakob Kummerow
2012/11/16 17:31:57
nit: one empty line is enough
mvstanton
2013/01/06 11:26:36
Done.
| |
| 554 | |
| 555 if displayHIR or displayLIR: | |
| 556 localsList = block.GetLocals() | |
| 557 if displayHIR: | |
| 558 hirList = block.GetHIRs() | |
| 559 if displayLIR: | |
| 560 lirList = block.GetLIRs() | |
| 561 | |
| 562 label = FormatLabel(block.GetName(),localsList,hirList,lirList) | |
| 563 # extra newline for readability | |
| 564 label = "\n" + label | |
| 565 p.PrintLine(" block%s [shape=plaintext,label=<%s>];" | |
| 566 % (block.GetName(), label)) | |
| 567 for succ in block.GetSuccessors(): | |
| 568 p.PrintLine(" block%s -> block%s;" % (block.GetName(), succ)) | |
| 569 | |
| 570 | |
| 571 def DotBlocks(functionName,phase,displayHIR,displayLIR,p): | |
| 572 """Diagram the blocks in the given function.""" | |
| 573 p.PrintLine("digraph Blocks_%s {" % functionName) | |
| 574 p.PrintLine(" ratio = 1.0;") | |
| 575 phase.visit_blocks(lambda b: DotBlockVisit(displayHIR,displayLIR,p,b)) | |
| 576 p.PrintLine("}") | |
| 577 | |
| 578 | |
| 579 def DotDiffVisit(diffs,p,block): | |
| 580 label = FormatLabel(block.GetName(),[],diffs[block.GetName()],[]) | |
| 581 p.PrintLine(" block%s [shape=plaintext,label=<%s>];" | |
| 582 % (block.GetName(), label)) | |
| 583 for succ in block.GetSuccessors(): | |
| 584 p.PrintLine(" block%s -> block%s;" % (block.GetName(), succ)) | |
| 585 | |
| 586 | |
| 587 def DotDiff(functionName, phasePrev, phase, changedBlocksOnly, p): | |
| 588 """Run a diff block by block between two phases in the given function.""" | |
| 589 p.PrintLine("// comparing phase %s with next phase %s" % (phasePrev.GetName(), | |
|
Jakob Kummerow
2012/11/16 17:31:57
nit: trailing whitespace
| |
| 590 phase.GetName())) | |
| 591 p.PrintLine("digraph Diff_%s {" % functionName) | |
| 592 p.PrintLine(" ratio = 1.0;") | |
| 593 # need to run diff -u phasePrev phase | |
| 594 # on the HIR output | |
| 595 # I think the # of blocks should be the same | |
| 596 diffResults = {} | |
| 597 blocks = phasePrev.GetBlocks() | |
| 598 for blockPrev in blocks: | |
| 599 block = phase.GetBlock(blockPrev.GetName()) | |
| 600 diffResults[blockPrev.GetName()] = diffText(blockPrev.GetHIRs(), | |
| 601 block.GetHIRs()) | |
| 602 | |
| 603 phasePrev.visit_blocks(lambda b: DotDiffVisit(diffResults,p,b)) | |
| 604 p.PrintLine("}") | |
| 605 | |
| 606 | |
| 607 reportText = """<html> | |
|
Jakob Kummerow
2012/11/16 17:31:57
Why are these (constant) variables when Bold() etc
mvstanton
2013/01/06 11:26:36
Good point. I got rid of the Bold() function, but
| |
| 608 <title>Report on %s</title> | |
| 609 <body> | |
| 610 <h2>Block structure</h2> | |
| 611 <a href=blocks.svg><img width=800 src=blocks.svg></a> | |
| 612 <h2>All HIR</h2> | |
| 613 <a href=hirblocks.svg><img width=800 src=hirblocks.svg></a> | |
| 614 <h2>All LIR</h2> | |
| 615 <a href=lirblocks.svg><img width=800 src=lirblocks.svg></a> | |
| 616 <h2>Diffs</h2> | |
| 617 %s | |
| 618 </body> | |
| 619 </html>""" | |
| 620 | |
| 621 | |
| 622 diffsTemplate = """<h2>%s</h2> | |
| 623 <a href=%s><img width=800 src=%s></a>""" | |
| 624 | |
| 625 | |
| 626 def AsFilename(phaseName,ext): | |
| 627 phaseName = phaseName.replace(" ","_") | |
| 628 phaseName += ext | |
| 629 return phaseName | |
| 630 | |
| 631 | |
| 632 def DotReport(function): | |
| 633 """Make a report on the function.""" | |
| 634 escapedFunctionName = function.GetName().replace(".","_") | |
| 635 dirname = "report_%s" % escapedFunctionName | |
| 636 if not os.path.exists(dirname): | |
| 637 os.mkdir(dirname) | |
|
Jakob Kummerow
2012/11/16 17:31:57
You probably want os.makedirs() here, which can al
mvstanton
2013/01/06 11:26:36
Done.
| |
| 638 | |
| 639 p = Printer() | |
| 640 htmlfilename = dirname + "/index.html" | |
| 641 print "Writing %s..." % htmlfilename | |
| 642 p.CreateFile(htmlfilename) | |
| 643 diffs = "" | |
| 644 phases = function.AllPhaseNames() | |
| 645 for ph in phases[1:]: | |
| 646 phfilename = AsFilename(ph,".svg") | |
| 647 diffs += diffsTemplate % (ph,phfilename,phfilename) | |
| 648 htmlText = reportText % (escapedFunctionName,diffs) | |
| 649 p.PrintLine(htmlText) | |
| 650 p.CloseFile() | |
| 651 | |
| 652 # Write all dot files | |
| 653 # get last phase | |
| 654 phase = function.GetPhase(phases[-1]) | |
| 655 p.CreateFile(dirname + "/blocks.dot") | |
| 656 DotBlocks(escapedFunctionName,phase,False,False,p) | |
| 657 p.CloseFile() | |
| 658 | |
| 659 p.CreateFile(dirname + "/hirblocks.dot") | |
| 660 DotBlocks(escapedFunctionName,phase,True,False,p) | |
| 661 p.CloseFile() | |
| 662 | |
| 663 p.CreateFile(dirname + "/lirblocks.dot") | |
| 664 DotBlocks(escapedFunctionName,phase,False,True,p) | |
| 665 p.CloseFile() | |
| 666 | |
| 667 print "Computing diffs..." | |
| 668 | |
| 669 for ph in phases[1:]: | |
| 670 phase = function.GetPhase(ph) | |
| 671 phasePrev = function.GetPreviousPhase(ph) | |
| 672 p.CreateFile(dirname + "/" + AsFilename(ph,".dot")) | |
| 673 DotDiff(escapedFunctionName, phasePrev, phase, True, p) | |
| 674 p.CloseFile() | |
| 675 | |
| 676 # Compile the dot files | |
| 677 cmdline = "dot -Tsvg %s > %s" | |
| 678 # diff -u %s %s" % (temp1.name, temp2.name) | |
| 679 files = glob.glob(dirname + "/*.dot") | |
| 680 for f in files: | |
| 681 print "compiling " + f | |
| 682 process = subprocess.Popen(cmdline % (f,f.replace(".dot",".svg")), | |
| 683 shell=True) | |
| 684 process.wait() | |
| 685 print "done" | |
| 686 | |
| 687 | |
| 688 def Main(): | |
| 689 p = Printer() | |
| 690 | |
| 691 parser = BuildOptions() | |
| 692 (options, args) = parser.parse_args() | |
| 693 if not ProcessOptions(options): | |
| 694 parser.print_help() | |
| 695 return 1 | |
| 696 | |
| 697 exit_code = 0 | |
|
Jakob Kummerow
2012/11/16 17:31:57
Did you mean to overwrite this when some error occ
mvstanton
2013/01/06 11:26:36
Done.
| |
| 698 | |
| 699 # Open input file and parse compilations, phases, blocks | |
| 700 reader = CFGReader(options.input) | |
| 701 compilations = reader.Parse() | |
| 702 | |
| 703 if options.debug: | |
| 704 DumpCompilations(compilations, p) | |
| 705 | |
| 706 function = options.function | |
| 707 if function == "@last": | |
| 708 function = compilations.keys()[-1] | |
| 709 elif function == "@first": | |
| 710 function = compilations.keys()[0] | |
| 711 if options.verbose: | |
| 712 print "// dump function %s" % function | |
| 713 | |
| 714 phase = options.phase | |
| 715 | |
| 716 # find the method | |
| 717 if not function in compilations: | |
| 718 diemsg("function " + function + " not found in input file") | |
| 719 | |
| 720 function = compilations[function] | |
| 721 # find the phase | |
| 722 phase = function.GetPhase(phase) | |
| 723 | |
| 724 # now what we do depends on the command | |
| 725 | |
| 726 escapedFunctionName = function.GetName().replace(".","_") | |
| 727 if options.command == "blocks": | |
| 728 DotBlocks(escapedFunctionName, phase, options.displayHIR, | |
| 729 options.displayLIR, p) | |
| 730 elif options.command == "diff": | |
| 731 # take the phase and the preceding phase. | |
| 732 phasePrev = function.GetPreviousPhase(phase.GetName()) | |
| 733 DotDiff(escapedFunctionName, phasePrev, phase, True, p) | |
| 734 elif options.command == "report": | |
| 735 DotReport(function) | |
| 736 return exit_code | |
| 737 | |
| 738 | |
| 739 if __name__ == "__main__": | |
| 740 sys.exit(Main()) | |
| OLD | NEW |