1*f4a2713aSLionel Sambuc#!/usr/bin/env python 2*f4a2713aSLionel Sambuc 3*f4a2713aSLionel Sambucimport sys 4*f4a2713aSLionel Sambucimport random 5*f4a2713aSLionel Sambuc 6*f4a2713aSLionel Sambucclass TimingScriptGenerator: 7*f4a2713aSLionel Sambuc """Used to generate a bash script which will invoke the toy and time it""" 8*f4a2713aSLionel Sambuc def __init__(self, scriptname, outputname): 9*f4a2713aSLionel Sambuc self.timeFile = outputname 10*f4a2713aSLionel Sambuc self.shfile = open(scriptname, 'w') 11*f4a2713aSLionel Sambuc self.shfile.write("echo \"\" > %s\n" % self.timeFile) 12*f4a2713aSLionel Sambuc 13*f4a2713aSLionel Sambuc def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls): 14*f4a2713aSLionel Sambuc """Echo some comments and invoke both versions of toy""" 15*f4a2713aSLionel Sambuc rootname = filename 16*f4a2713aSLionel Sambuc if '.' in filename: 17*f4a2713aSLionel Sambuc rootname = filename[:filename.rfind('.')] 18*f4a2713aSLionel Sambuc self.shfile.write("echo \"%s: Calls %d of %d functions, %d total\" >> %s\n" % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile)) 19*f4a2713aSLionel Sambuc self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 20*f4a2713aSLionel Sambuc self.shfile.write("echo \"With MCJIT\" >> %s\n" % self.timeFile) 21*f4a2713aSLionel Sambuc self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"") 22*f4a2713aSLionel Sambuc self.shfile.write(" -o %s -a " % self.timeFile) 23*f4a2713aSLionel Sambuc self.shfile.write("./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (filename, rootname, rootname)) 24*f4a2713aSLionel Sambuc self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 25*f4a2713aSLionel Sambuc self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile) 26*f4a2713aSLionel Sambuc self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"") 27*f4a2713aSLionel Sambuc self.shfile.write(" -o %s -a " % self.timeFile) 28*f4a2713aSLionel Sambuc self.shfile.write("./toy-jit < %s > %s-jit.out 2> %s-jit.err\n" % (filename, rootname, rootname)) 29*f4a2713aSLionel Sambuc self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 30*f4a2713aSLionel Sambuc self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 31*f4a2713aSLionel Sambuc 32*f4a2713aSLionel Sambucclass KScriptGenerator: 33*f4a2713aSLionel Sambuc """Used to generate random Kaleidoscope code""" 34*f4a2713aSLionel Sambuc def __init__(self, filename): 35*f4a2713aSLionel Sambuc self.kfile = open(filename, 'w') 36*f4a2713aSLionel Sambuc self.nextFuncNum = 1 37*f4a2713aSLionel Sambuc self.lastFuncNum = None 38*f4a2713aSLionel Sambuc self.callWeighting = 0.1 39*f4a2713aSLionel Sambuc # A mapping of calls within functions with no duplicates 40*f4a2713aSLionel Sambuc self.calledFunctionTable = {} 41*f4a2713aSLionel Sambuc # A list of function calls which will actually be executed 42*f4a2713aSLionel Sambuc self.calledFunctions = [] 43*f4a2713aSLionel Sambuc # A comprehensive mapping of calls within functions 44*f4a2713aSLionel Sambuc # used for computing the total number of calls 45*f4a2713aSLionel Sambuc self.comprehensiveCalledFunctionTable = {} 46*f4a2713aSLionel Sambuc self.totalCallsExecuted = 0 47*f4a2713aSLionel Sambuc 48*f4a2713aSLionel Sambuc def updateTotalCallCount(self, callee): 49*f4a2713aSLionel Sambuc # Count this call 50*f4a2713aSLionel Sambuc self.totalCallsExecuted += 1 51*f4a2713aSLionel Sambuc # Then count all the functions it calls 52*f4a2713aSLionel Sambuc if callee in self.comprehensiveCalledFunctionTable: 53*f4a2713aSLionel Sambuc for child in self.comprehensiveCalledFunctionTable[callee]: 54*f4a2713aSLionel Sambuc self.updateTotalCallCount(child) 55*f4a2713aSLionel Sambuc 56*f4a2713aSLionel Sambuc def updateFunctionCallMap(self, caller, callee): 57*f4a2713aSLionel Sambuc """Maintains a map of functions that are called from other functions""" 58*f4a2713aSLionel Sambuc if not caller in self.calledFunctionTable: 59*f4a2713aSLionel Sambuc self.calledFunctionTable[caller] = [] 60*f4a2713aSLionel Sambuc if not callee in self.calledFunctionTable[caller]: 61*f4a2713aSLionel Sambuc self.calledFunctionTable[caller].append(callee) 62*f4a2713aSLionel Sambuc if not caller in self.comprehensiveCalledFunctionTable: 63*f4a2713aSLionel Sambuc self.comprehensiveCalledFunctionTable[caller] = [] 64*f4a2713aSLionel Sambuc self.comprehensiveCalledFunctionTable[caller].append(callee) 65*f4a2713aSLionel Sambuc 66*f4a2713aSLionel Sambuc def updateCalledFunctionList(self, callee): 67*f4a2713aSLionel Sambuc """Maintains a list of functions that will actually be called""" 68*f4a2713aSLionel Sambuc # Update the total call count 69*f4a2713aSLionel Sambuc self.updateTotalCallCount(callee) 70*f4a2713aSLionel Sambuc # If this function is already in the list, don't do anything else 71*f4a2713aSLionel Sambuc if callee in self.calledFunctions: 72*f4a2713aSLionel Sambuc return 73*f4a2713aSLionel Sambuc # Add this function to the list of those that will be called. 74*f4a2713aSLionel Sambuc self.calledFunctions.append(callee) 75*f4a2713aSLionel Sambuc # If this function calls other functions, add them too 76*f4a2713aSLionel Sambuc if callee in self.calledFunctionTable: 77*f4a2713aSLionel Sambuc for subCallee in self.calledFunctionTable[callee]: 78*f4a2713aSLionel Sambuc self.updateCalledFunctionList(subCallee) 79*f4a2713aSLionel Sambuc 80*f4a2713aSLionel Sambuc def setCallWeighting(self, weight): 81*f4a2713aSLionel Sambuc """ Sets the probably of generating a function call""" 82*f4a2713aSLionel Sambuc self.callWeighting = weight 83*f4a2713aSLionel Sambuc 84*f4a2713aSLionel Sambuc def writeln(self, line): 85*f4a2713aSLionel Sambuc self.kfile.write(line + '\n') 86*f4a2713aSLionel Sambuc 87*f4a2713aSLionel Sambuc def writeComment(self, comment): 88*f4a2713aSLionel Sambuc self.writeln('# ' + comment) 89*f4a2713aSLionel Sambuc 90*f4a2713aSLionel Sambuc def writeEmptyLine(self): 91*f4a2713aSLionel Sambuc self.writeln("") 92*f4a2713aSLionel Sambuc 93*f4a2713aSLionel Sambuc def writePredefinedFunctions(self): 94*f4a2713aSLionel Sambuc self.writeComment("Define ':' for sequencing: as a low-precedence operator that ignores operands") 95*f4a2713aSLionel Sambuc self.writeComment("and just returns the RHS.") 96*f4a2713aSLionel Sambuc self.writeln("def binary : 1 (x y) y;") 97*f4a2713aSLionel Sambuc self.writeEmptyLine() 98*f4a2713aSLionel Sambuc self.writeComment("Helper functions defined within toy") 99*f4a2713aSLionel Sambuc self.writeln("extern putchard(x);") 100*f4a2713aSLionel Sambuc self.writeln("extern printd(d);") 101*f4a2713aSLionel Sambuc self.writeln("extern printlf();") 102*f4a2713aSLionel Sambuc self.writeEmptyLine() 103*f4a2713aSLionel Sambuc self.writeComment("Print the result of a function call") 104*f4a2713aSLionel Sambuc self.writeln("def printresult(N Result)") 105*f4a2713aSLionel Sambuc self.writeln(" # 'result('") 106*f4a2713aSLionel Sambuc self.writeln(" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :") 107*f4a2713aSLionel Sambuc self.writeln(" printd(N) :"); 108*f4a2713aSLionel Sambuc self.writeln(" # ') = '") 109*f4a2713aSLionel Sambuc self.writeln(" putchard(41) : putchard(32) : putchard(61) : putchard(32) :") 110*f4a2713aSLionel Sambuc self.writeln(" printd(Result) :"); 111*f4a2713aSLionel Sambuc self.writeln(" printlf();") 112*f4a2713aSLionel Sambuc self.writeEmptyLine() 113*f4a2713aSLionel Sambuc 114*f4a2713aSLionel Sambuc def writeRandomOperation(self, LValue, LHS, RHS): 115*f4a2713aSLionel Sambuc shouldCallFunc = (self.lastFuncNum > 2 and random.random() < self.callWeighting) 116*f4a2713aSLionel Sambuc if shouldCallFunc: 117*f4a2713aSLionel Sambuc funcToCall = random.randrange(1, self.lastFuncNum - 1) 118*f4a2713aSLionel Sambuc self.updateFunctionCallMap(self.lastFuncNum, funcToCall) 119*f4a2713aSLionel Sambuc self.writeln(" %s = func%d(%s, %s) :" % (LValue, funcToCall, LHS, RHS)) 120*f4a2713aSLionel Sambuc else: 121*f4a2713aSLionel Sambuc possibleOperations = ["+", "-", "*", "/"] 122*f4a2713aSLionel Sambuc operation = random.choice(possibleOperations) 123*f4a2713aSLionel Sambuc if operation == "-": 124*f4a2713aSLionel Sambuc # Don't let our intermediate value become zero 125*f4a2713aSLionel Sambuc # This is complicated by the fact that '<' is our only comparison operator 126*f4a2713aSLionel Sambuc self.writeln(" if %s < %s then" % (LHS, RHS)) 127*f4a2713aSLionel Sambuc self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS)) 128*f4a2713aSLionel Sambuc self.writeln(" else if %s < %s then" % (RHS, LHS)) 129*f4a2713aSLionel Sambuc self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS)) 130*f4a2713aSLionel Sambuc self.writeln(" else") 131*f4a2713aSLionel Sambuc self.writeln(" %s = %s %s %f :" % (LValue, LHS, operation, random.uniform(1, 100))) 132*f4a2713aSLionel Sambuc else: 133*f4a2713aSLionel Sambuc self.writeln(" %s = %s %s %s :" % (LValue, LHS, operation, RHS)) 134*f4a2713aSLionel Sambuc 135*f4a2713aSLionel Sambuc def getNextFuncNum(self): 136*f4a2713aSLionel Sambuc result = self.nextFuncNum 137*f4a2713aSLionel Sambuc self.nextFuncNum += 1 138*f4a2713aSLionel Sambuc self.lastFuncNum = result 139*f4a2713aSLionel Sambuc return result 140*f4a2713aSLionel Sambuc 141*f4a2713aSLionel Sambuc def writeFunction(self, elements): 142*f4a2713aSLionel Sambuc funcNum = self.getNextFuncNum() 143*f4a2713aSLionel Sambuc self.writeComment("Auto-generated function number %d" % funcNum) 144*f4a2713aSLionel Sambuc self.writeln("def func%d(X Y)" % funcNum) 145*f4a2713aSLionel Sambuc self.writeln(" var temp1 = X,") 146*f4a2713aSLionel Sambuc self.writeln(" temp2 = Y,") 147*f4a2713aSLionel Sambuc self.writeln(" temp3 in") 148*f4a2713aSLionel Sambuc # Initialize the variable names to be rotated 149*f4a2713aSLionel Sambuc first = "temp3" 150*f4a2713aSLionel Sambuc second = "temp1" 151*f4a2713aSLionel Sambuc third = "temp2" 152*f4a2713aSLionel Sambuc # Write some random operations 153*f4a2713aSLionel Sambuc for i in range(elements): 154*f4a2713aSLionel Sambuc self.writeRandomOperation(first, second, third) 155*f4a2713aSLionel Sambuc # Rotate the variables 156*f4a2713aSLionel Sambuc temp = first 157*f4a2713aSLionel Sambuc first = second 158*f4a2713aSLionel Sambuc second = third 159*f4a2713aSLionel Sambuc third = temp 160*f4a2713aSLionel Sambuc self.writeln(" " + third + ";") 161*f4a2713aSLionel Sambuc self.writeEmptyLine() 162*f4a2713aSLionel Sambuc 163*f4a2713aSLionel Sambuc def writeFunctionCall(self): 164*f4a2713aSLionel Sambuc self.writeComment("Call the last function") 165*f4a2713aSLionel Sambuc arg1 = random.uniform(1, 100) 166*f4a2713aSLionel Sambuc arg2 = random.uniform(1, 100) 167*f4a2713aSLionel Sambuc self.writeln("printresult(%d, func%d(%f, %f) )" % (self.lastFuncNum, self.lastFuncNum, arg1, arg2)) 168*f4a2713aSLionel Sambuc self.writeEmptyLine() 169*f4a2713aSLionel Sambuc self.updateCalledFunctionList(self.lastFuncNum) 170*f4a2713aSLionel Sambuc 171*f4a2713aSLionel Sambuc def writeFinalFunctionCounts(self): 172*f4a2713aSLionel Sambuc self.writeComment("Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum)) 173*f4a2713aSLionel Sambuc 174*f4a2713aSLionel Sambucdef generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript): 175*f4a2713aSLionel Sambuc """ Generate a random Kaleidoscope script based on the given parameters """ 176*f4a2713aSLionel Sambuc print "Generating " + filename 177*f4a2713aSLionel Sambuc print(" %d functions, %d elements per function, %d functions between execution" % 178*f4a2713aSLionel Sambuc (numFuncs, elementsPerFunc, funcsBetweenExec)) 179*f4a2713aSLionel Sambuc print(" Call weighting = %f" % callWeighting) 180*f4a2713aSLionel Sambuc script = KScriptGenerator(filename) 181*f4a2713aSLionel Sambuc script.setCallWeighting(callWeighting) 182*f4a2713aSLionel Sambuc script.writeComment("===========================================================================") 183*f4a2713aSLionel Sambuc script.writeComment("Auto-generated script") 184*f4a2713aSLionel Sambuc script.writeComment(" %d functions, %d elements per function, %d functions between execution" 185*f4a2713aSLionel Sambuc % (numFuncs, elementsPerFunc, funcsBetweenExec)) 186*f4a2713aSLionel Sambuc script.writeComment(" call weighting = %f" % callWeighting) 187*f4a2713aSLionel Sambuc script.writeComment("===========================================================================") 188*f4a2713aSLionel Sambuc script.writeEmptyLine() 189*f4a2713aSLionel Sambuc script.writePredefinedFunctions() 190*f4a2713aSLionel Sambuc funcsSinceLastExec = 0 191*f4a2713aSLionel Sambuc for i in range(numFuncs): 192*f4a2713aSLionel Sambuc script.writeFunction(elementsPerFunc) 193*f4a2713aSLionel Sambuc funcsSinceLastExec += 1 194*f4a2713aSLionel Sambuc if funcsSinceLastExec == funcsBetweenExec: 195*f4a2713aSLionel Sambuc script.writeFunctionCall() 196*f4a2713aSLionel Sambuc funcsSinceLastExec = 0 197*f4a2713aSLionel Sambuc # Always end with a function call 198*f4a2713aSLionel Sambuc if funcsSinceLastExec > 0: 199*f4a2713aSLionel Sambuc script.writeFunctionCall() 200*f4a2713aSLionel Sambuc script.writeEmptyLine() 201*f4a2713aSLionel Sambuc script.writeFinalFunctionCounts() 202*f4a2713aSLionel Sambuc funcsCalled = len(script.calledFunctions) 203*f4a2713aSLionel Sambuc print " Called %d of %d functions, %d total" % (funcsCalled, numFuncs, script.totalCallsExecuted) 204*f4a2713aSLionel Sambuc timingScript.writeTimingCall(filename, numFuncs, funcsCalled, script.totalCallsExecuted) 205*f4a2713aSLionel Sambuc 206*f4a2713aSLionel Sambuc# Execution begins here 207*f4a2713aSLionel Sambucrandom.seed() 208*f4a2713aSLionel Sambuc 209*f4a2713aSLionel SambuctimingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt") 210*f4a2713aSLionel Sambuc 211*f4a2713aSLionel SambucdataSets = [(5000, 3, 50, 0.50), (5000, 10, 100, 0.10), (5000, 10, 5, 0.10), (5000, 10, 1, 0.0), 212*f4a2713aSLionel Sambuc (1000, 3, 10, 0.50), (1000, 10, 100, 0.10), (1000, 10, 5, 0.10), (1000, 10, 1, 0.0), 213*f4a2713aSLionel Sambuc ( 200, 3, 2, 0.50), ( 200, 10, 40, 0.10), ( 200, 10, 2, 0.10), ( 200, 10, 1, 0.0)] 214*f4a2713aSLionel Sambuc 215*f4a2713aSLionel Sambuc# Generate the code 216*f4a2713aSLionel Sambucfor (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets: 217*f4a2713aSLionel Sambuc filename = "test-%d-%d-%d-%d.k" % (numFuncs, elementsPerFunc, funcsBetweenExec, int(callWeighting * 100)) 218*f4a2713aSLionel Sambuc generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript) 219*f4a2713aSLionel Sambucprint "All done!" 220