|  | #!/usr/bin/env python | 
|  |  | 
|  | from __future__ import print_function | 
|  |  | 
|  | import sys | 
|  | import random | 
|  |  | 
|  |  | 
|  | class TimingScriptGenerator: | 
|  | """Used to generate a bash script which will invoke the toy and time it""" | 
|  |  | 
|  | def __init__(self, scriptname, outputname): | 
|  | self.timeFile = outputname | 
|  | self.shfile = open(scriptname, "w") | 
|  | self.shfile.write('echo "" > %s\n' % self.timeFile) | 
|  |  | 
|  | def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls): | 
|  | """Echo some comments and invoke both versions of toy""" | 
|  | rootname = filename | 
|  | if "." in filename: | 
|  | rootname = filename[: filename.rfind(".")] | 
|  | self.shfile.write( | 
|  | 'echo "%s: Calls %d of %d functions, %d total" >> %s\n' | 
|  | % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile) | 
|  | ) | 
|  | self.shfile.write('echo "" >> %s\n' % self.timeFile) | 
|  | self.shfile.write('echo "With MCJIT" >> %s\n' % self.timeFile) | 
|  | self.shfile.write( | 
|  | '/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"' | 
|  | ) | 
|  | self.shfile.write(" -o %s -a " % self.timeFile) | 
|  | self.shfile.write( | 
|  | "./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n" | 
|  | % (filename, rootname, rootname) | 
|  | ) | 
|  | self.shfile.write('echo "" >> %s\n' % self.timeFile) | 
|  | self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile) | 
|  | self.shfile.write( | 
|  | '/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"' | 
|  | ) | 
|  | self.shfile.write(" -o %s -a " % self.timeFile) | 
|  | self.shfile.write( | 
|  | "./toy-jit < %s > %s-jit.out 2> %s-jit.err\n" | 
|  | % (filename, rootname, rootname) | 
|  | ) | 
|  | self.shfile.write('echo "" >> %s\n' % self.timeFile) | 
|  | self.shfile.write('echo "" >> %s\n' % self.timeFile) | 
|  |  | 
|  |  | 
|  | class KScriptGenerator: | 
|  | """Used to generate random Kaleidoscope code""" | 
|  |  | 
|  | def __init__(self, filename): | 
|  | self.kfile = open(filename, "w") | 
|  | self.nextFuncNum = 1 | 
|  | self.lastFuncNum = None | 
|  | self.callWeighting = 0.1 | 
|  | # A mapping of calls within functions with no duplicates | 
|  | self.calledFunctionTable = {} | 
|  | # A list of function calls which will actually be executed | 
|  | self.calledFunctions = [] | 
|  | # A comprehensive mapping of calls within functions | 
|  | # used for computing the total number of calls | 
|  | self.comprehensiveCalledFunctionTable = {} | 
|  | self.totalCallsExecuted = 0 | 
|  |  | 
|  | def updateTotalCallCount(self, callee): | 
|  | # Count this call | 
|  | self.totalCallsExecuted += 1 | 
|  | # Then count all the functions it calls | 
|  | if callee in self.comprehensiveCalledFunctionTable: | 
|  | for child in self.comprehensiveCalledFunctionTable[callee]: | 
|  | self.updateTotalCallCount(child) | 
|  |  | 
|  | def updateFunctionCallMap(self, caller, callee): | 
|  | """Maintains a map of functions that are called from other functions""" | 
|  | if not caller in self.calledFunctionTable: | 
|  | self.calledFunctionTable[caller] = [] | 
|  | if not callee in self.calledFunctionTable[caller]: | 
|  | self.calledFunctionTable[caller].append(callee) | 
|  | if not caller in self.comprehensiveCalledFunctionTable: | 
|  | self.comprehensiveCalledFunctionTable[caller] = [] | 
|  | self.comprehensiveCalledFunctionTable[caller].append(callee) | 
|  |  | 
|  | def updateCalledFunctionList(self, callee): | 
|  | """Maintains a list of functions that will actually be called""" | 
|  | # Update the total call count | 
|  | self.updateTotalCallCount(callee) | 
|  | # If this function is already in the list, don't do anything else | 
|  | if callee in self.calledFunctions: | 
|  | return | 
|  | # Add this function to the list of those that will be called. | 
|  | self.calledFunctions.append(callee) | 
|  | # If this function calls other functions, add them too | 
|  | if callee in self.calledFunctionTable: | 
|  | for subCallee in self.calledFunctionTable[callee]: | 
|  | self.updateCalledFunctionList(subCallee) | 
|  |  | 
|  | def setCallWeighting(self, weight): | 
|  | """Sets the probably of generating a function call""" | 
|  | self.callWeighting = weight | 
|  |  | 
|  | def writeln(self, line): | 
|  | self.kfile.write(line + "\n") | 
|  |  | 
|  | def writeComment(self, comment): | 
|  | self.writeln("# " + comment) | 
|  |  | 
|  | def writeEmptyLine(self): | 
|  | self.writeln("") | 
|  |  | 
|  | def writePredefinedFunctions(self): | 
|  | self.writeComment( | 
|  | "Define ':' for sequencing: as a low-precedence operator that ignores operands" | 
|  | ) | 
|  | self.writeComment("and just returns the RHS.") | 
|  | self.writeln("def binary : 1 (x y) y;") | 
|  | self.writeEmptyLine() | 
|  | self.writeComment("Helper functions defined within toy") | 
|  | self.writeln("extern putchard(x);") | 
|  | self.writeln("extern printd(d);") | 
|  | self.writeln("extern printlf();") | 
|  | self.writeEmptyLine() | 
|  | self.writeComment("Print the result of a function call") | 
|  | self.writeln("def printresult(N Result)") | 
|  | self.writeln("  # 'result('") | 
|  | self.writeln( | 
|  | "  putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :" | 
|  | ) | 
|  | self.writeln("  printd(N) :") | 
|  | self.writeln("  # ') = '") | 
|  | self.writeln("  putchard(41) : putchard(32) : putchard(61) : putchard(32) :") | 
|  | self.writeln("  printd(Result) :") | 
|  | self.writeln("  printlf();") | 
|  | self.writeEmptyLine() | 
|  |  | 
|  | def writeRandomOperation(self, LValue, LHS, RHS): | 
|  | shouldCallFunc = self.lastFuncNum > 2 and random.random() < self.callWeighting | 
|  | if shouldCallFunc: | 
|  | funcToCall = random.randrange(1, self.lastFuncNum - 1) | 
|  | self.updateFunctionCallMap(self.lastFuncNum, funcToCall) | 
|  | self.writeln("  %s = func%d(%s, %s) :" % (LValue, funcToCall, LHS, RHS)) | 
|  | else: | 
|  | possibleOperations = ["+", "-", "*", "/"] | 
|  | operation = random.choice(possibleOperations) | 
|  | if operation == "-": | 
|  | # Don't let our intermediate value become zero | 
|  | # This is complicated by the fact that '<' is our only comparison operator | 
|  | self.writeln("  if %s < %s then" % (LHS, RHS)) | 
|  | self.writeln("    %s = %s %s %s" % (LValue, LHS, operation, RHS)) | 
|  | self.writeln("  else if %s < %s then" % (RHS, LHS)) | 
|  | self.writeln("    %s = %s %s %s" % (LValue, LHS, operation, RHS)) | 
|  | self.writeln("  else") | 
|  | self.writeln( | 
|  | "    %s = %s %s %f :" | 
|  | % (LValue, LHS, operation, random.uniform(1, 100)) | 
|  | ) | 
|  | else: | 
|  | self.writeln("  %s = %s %s %s :" % (LValue, LHS, operation, RHS)) | 
|  |  | 
|  | def getNextFuncNum(self): | 
|  | result = self.nextFuncNum | 
|  | self.nextFuncNum += 1 | 
|  | self.lastFuncNum = result | 
|  | return result | 
|  |  | 
|  | def writeFunction(self, elements): | 
|  | funcNum = self.getNextFuncNum() | 
|  | self.writeComment("Auto-generated function number %d" % funcNum) | 
|  | self.writeln("def func%d(X Y)" % funcNum) | 
|  | self.writeln("  var temp1 = X,") | 
|  | self.writeln("      temp2 = Y,") | 
|  | self.writeln("      temp3 in") | 
|  | # Initialize the variable names to be rotated | 
|  | first = "temp3" | 
|  | second = "temp1" | 
|  | third = "temp2" | 
|  | # Write some random operations | 
|  | for i in range(elements): | 
|  | self.writeRandomOperation(first, second, third) | 
|  | # Rotate the variables | 
|  | temp = first | 
|  | first = second | 
|  | second = third | 
|  | third = temp | 
|  | self.writeln("  " + third + ";") | 
|  | self.writeEmptyLine() | 
|  |  | 
|  | def writeFunctionCall(self): | 
|  | self.writeComment("Call the last function") | 
|  | arg1 = random.uniform(1, 100) | 
|  | arg2 = random.uniform(1, 100) | 
|  | self.writeln( | 
|  | "printresult(%d, func%d(%f, %f) )" | 
|  | % (self.lastFuncNum, self.lastFuncNum, arg1, arg2) | 
|  | ) | 
|  | self.writeEmptyLine() | 
|  | self.updateCalledFunctionList(self.lastFuncNum) | 
|  |  | 
|  | def writeFinalFunctionCounts(self): | 
|  | self.writeComment( | 
|  | "Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum) | 
|  | ) | 
|  |  | 
|  |  | 
|  | def generateKScript( | 
|  | filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript | 
|  | ): | 
|  | """Generate a random Kaleidoscope script based on the given parameters""" | 
|  | print("Generating " + filename) | 
|  | print( | 
|  | "  %d functions, %d elements per function, %d functions between execution" | 
|  | % (numFuncs, elementsPerFunc, funcsBetweenExec) | 
|  | ) | 
|  | print("  Call weighting = %f" % callWeighting) | 
|  | script = KScriptGenerator(filename) | 
|  | script.setCallWeighting(callWeighting) | 
|  | script.writeComment( | 
|  | "===========================================================================" | 
|  | ) | 
|  | script.writeComment("Auto-generated script") | 
|  | script.writeComment( | 
|  | "  %d functions, %d elements per function, %d functions between execution" | 
|  | % (numFuncs, elementsPerFunc, funcsBetweenExec) | 
|  | ) | 
|  | script.writeComment("  call weighting = %f" % callWeighting) | 
|  | script.writeComment( | 
|  | "===========================================================================" | 
|  | ) | 
|  | script.writeEmptyLine() | 
|  | script.writePredefinedFunctions() | 
|  | funcsSinceLastExec = 0 | 
|  | for i in range(numFuncs): | 
|  | script.writeFunction(elementsPerFunc) | 
|  | funcsSinceLastExec += 1 | 
|  | if funcsSinceLastExec == funcsBetweenExec: | 
|  | script.writeFunctionCall() | 
|  | funcsSinceLastExec = 0 | 
|  | # Always end with a function call | 
|  | if funcsSinceLastExec > 0: | 
|  | script.writeFunctionCall() | 
|  | script.writeEmptyLine() | 
|  | script.writeFinalFunctionCounts() | 
|  | funcsCalled = len(script.calledFunctions) | 
|  | print( | 
|  | "  Called %d of %d functions, %d total" | 
|  | % (funcsCalled, numFuncs, script.totalCallsExecuted) | 
|  | ) | 
|  | timingScript.writeTimingCall( | 
|  | filename, numFuncs, funcsCalled, script.totalCallsExecuted | 
|  | ) | 
|  |  | 
|  |  | 
|  | # Execution begins here | 
|  | random.seed() | 
|  |  | 
|  | timingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt") | 
|  |  | 
|  | dataSets = [ | 
|  | (5000, 3, 50, 0.50), | 
|  | (5000, 10, 100, 0.10), | 
|  | (5000, 10, 5, 0.10), | 
|  | (5000, 10, 1, 0.0), | 
|  | (1000, 3, 10, 0.50), | 
|  | (1000, 10, 100, 0.10), | 
|  | (1000, 10, 5, 0.10), | 
|  | (1000, 10, 1, 0.0), | 
|  | (200, 3, 2, 0.50), | 
|  | (200, 10, 40, 0.10), | 
|  | (200, 10, 2, 0.10), | 
|  | (200, 10, 1, 0.0), | 
|  | ] | 
|  |  | 
|  | # Generate the code | 
|  | for (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets: | 
|  | filename = "test-%d-%d-%d-%d.k" % ( | 
|  | numFuncs, | 
|  | elementsPerFunc, | 
|  | funcsBetweenExec, | 
|  | int(callWeighting * 100), | 
|  | ) | 
|  | generateKScript( | 
|  | filename, | 
|  | numFuncs, | 
|  | elementsPerFunc, | 
|  | funcsBetweenExec, | 
|  | callWeighting, | 
|  | timingScript, | 
|  | ) | 
|  | print("All done!") |