def extractStmts(exp): stmts = [] if classGuard(exp, BinOp, Dictionary, List, UnaryOp): for child in exp: if classGuard(child, Statement, FunctionCall): stmts.append(child) else: stmts.append(extractStmts(child)) return flatten(stmts)
def propagateValuesPrime(node, consts, gather = True): # Memorize or replace symbol values, as appropriate. if gather: if isinstance(node, Assign) and isinstance(node.var, Symbol) and classGuard(node.exp, Boolean, Integer, Name, Symbol): consts[node.var] = node.exp elif isinstance(node, Phi) and len(node.srcs) == 1 and consts.has_key(node.srcs[0]): consts[node.target] = consts[node.srcs[0]] elif isinstance(node, Symbol) and consts.has_key(node): return consts[node] # Values in Phi nodes should never be replaced. if isinstance(node, While): # This looks all weird because we have to change the order of the # While node's children to propigate values appropriately. children = node.getChildren() children = children[1:] + children[0:1] newChildren = [propagateValuesPrime(child, consts, gather) for child in children] newChildren = newChildren[-1:] + newChildren[0:-1] node.setChildren(newChildren) elif not isinstance(node, Phi): newChildren = [propagateValuesPrime(child, consts, gather) for child in node] node.setChildren(newChildren) return consts if isinstance(node, Module) else node
def precolor(node): if classGuard(node, Assign, Phi): sym = extractSymbol(node) if not sym.has_key('precolor'): sym['precolor'] = None elif isinstance(node, Div) and isinstance(node.left, Symbol): node.left['precolor'] = eax elif isinstance(node, Function): # This clause pre-colors function arguments with their position on # the stack relative to the %ebp register. The first argument # starts 8 bytes above the base pointer. Each successive argument is # 4 bytes above that. offset = 8 for sym in node.argSymbols: sym['precolor'] = Mem(offset, 'up', True) #Advance to the next argument offset. offset += 4 elif isinstance(node, Return): # Values are returned from functions in the %eax register. node['precolor'] = eax for child in node: precolor(child)
def reads(node): if classGuard(node, Assign, Phi): sym = extractSymbol(node) if not sym.has_key('reads'): sym['reads'] = 0 elif isinstance(node, Function): for sym in node.argSymbols: sym['reads'] = 0 elif isinstance(node, Symbol): # This is kind of ugly, but because of Python's scoping rules we can # read from a variable before it is 'in scope.' if node.has_key('reads'): node['reads'] += 1 else: node['reads'] = 1 elif isinstance(node, Subscript) and isinstance(node.symbol, Symbol): # This little hack is here to take care of cases where subscripts are # applied to literal values. After the flatten transformation this # branch will be taken whenever we see a subscript. node.symbol['reads'] += 1 for child in node: reads(child)
def eliminateDeadStores(node, klass = 0): newChildren = [] if isinstance(node, Class): klass += 1 for child in node: newChildren.append(eliminateDeadStores(child, klass)) node.setChildren(flatten(newChildren)) if isinstance(node, Assign): sym = extractSymbol(node.var) if sym['reads'] == 0 and klass == 0: if classGuard(node.exp, Class, FunctionCall): return node else: return None else: return node elif isinstance(node, Phi) and node.target['reads'] == 0: return None else: return node
def eliminateDeadStores(node, klass=0): newChildren = [] if isinstance(node, Class): klass += 1 for child in node: newChildren.append(eliminateDeadStores(child, klass)) node.setChildren(flatten(newChildren)) if isinstance(node, Assign): sym = extractSymbol(node.var) if sym['reads'] == 0 and klass == 0: if classGuard(node.exp, Class, FunctionCall): return node else: return None else: return node elif isinstance(node, Phi) and node.target['reads'] == 0: return None else: return node
def precolor(node): if classGuard(node, Assign, Phi): sym = extractSymbol(node) if not sym.has_key('precolor'): sym['precolor'] = None elif isinstance(node, Div) and isinstance(node.left, Symbol): node.left['precolor'] = eax elif isinstance(node, Function): # This clause pre-colors function arguments with their position on # the stack relative to the %ebp register. The first argument # starts 8 bytes above the base pointer. Each successive argument is # 4 bytes above that. offset = 8 for sym in node.argSymbols: sym['precolor'] = Mem(offset, 'up', True) #Advance to the next argument offset. offset += 4 elif isinstance(node, Return): # Values are returned from functions in the %eax register. node['precolor'] = eax for child in node: precolor(child)
def flatten(node, st=None, inPlace=False): newChildren = [] newInPlace = None preStmts = [] # Setup flattening for this node's children. if util.classGuard(node, Assign, BasicBlock, Module): newInPlace = node.__class__ elif isinstance(node, Function): st = node.st # Flatten our children. if isinstance(node, While): if not isinstance(node.cond, Symbol): condBody, node.cond = flatten(node.cond, st) node.condBody = BasicBlock(condBody) bodyPreStmts, node.body = flatten(node.body, st) preStmts.append(bodyPreStmts) elif isinstance(node, If): for child in node: childPreStmts, newChild = flatten(child, st, newInPlace) preStmts.append(childPreStmts) newChildren.append(newChild) # Set our new child nodes. newChildren = util.flatten(newChildren) node.setChildren(newChildren) else: for child in node: childPreStmts, newChild = flatten(child, st, newInPlace) if isinstance(node, BasicBlock): newChildren.append(childPreStmts) newChildren.append(newChild) else: preStmts.append(childPreStmts) newChildren.append(newChild) # Set our new child nodes. newChildren = util.flatten(newChildren) node.setChildren(newChildren) # Flatten the current node. if classGuard(node, BinOp, FunctionCall, IfExp, UnaryOp) and not inPlace: sym = st.getTemp() preStmts.append(Assign(sym, node)) node = sym # Flatten our list of pre-statements. preStmts = util.flatten(preStmts) return node if isinstance(node, Module) else (preStmts, node)
def flatten(node, st = None, inPlace = False): newChildren = [] newInPlace = None preStmts = [] # Setup flattening for this node's children. if util.classGuard(node, Assign, BasicBlock, Module): newInPlace = node.__class__ elif isinstance(node, Function): st = node.st # Flatten our children. if isinstance(node, While): if not isinstance(node.cond, Symbol): condBody, node.cond = flatten(node.cond, st) node.condBody = BasicBlock(condBody) bodyPreStmts, node.body = flatten(node.body, st) preStmts.append(bodyPreStmts) elif isinstance(node, If): for child in node: childPreStmts, newChild = flatten(child, st, newInPlace) preStmts.append(childPreStmts) newChildren.append(newChild) # Set our new child nodes. newChildren = util.flatten(newChildren) node.setChildren(newChildren) else: for child in node: childPreStmts, newChild = flatten(child, st, newInPlace) if isinstance(node, BasicBlock): newChildren.append(childPreStmts) newChildren.append(newChild) else: preStmts.append(childPreStmts) newChildren.append(newChild) # Set our new child nodes. newChildren = util.flatten(newChildren) node.setChildren(newChildren) # Flatten the current node. if classGuard(node, BinOp, FunctionCall, IfExp, UnaryOp) and not inPlace: sym = st.getTemp() preStmts.append(Assign(sym, node)) node = sym # Flatten our list of pre-statements. preStmts = util.flatten(preStmts) return node if isinstance(node, Module) else (preStmts, node)
def eliminateDeadCode(node): newChildren = [] for child in node: newChild = eliminateDeadCode(child) if isinstance(node, BasicBlock): if classGuard(newChild, Class, Function, FunctionCall, SetAttr, Statement): newChildren.append(newChild) elif isinstance(newChild, BasicBlock): newChildren.append(newChild.children) elif isinstance(newChild, Return): newChildren.append(newChild) # All nodes after the Return node will be discarded due to # unreachability. break else: # Anything that reaches here is an expression outside of a # statement, and therefor has no effect on the program. # Therefor we remove any nested statements from the # expression then throw it away. newChildren.append(extractStmts(newChild)) else: newChildren.append(newChild) node.setChildren(flatten(newChildren)) if classGuard(node, If, IfExp) and isinstance(node.cond, Literal): # If statements or expressions with a literal conditional value can # be eliminated and replaced with the appropriate BasicBlock's # children. return node.then if node.cond.value else node.els else: return node
def liveness(node, alive=[]): if isinstance(node, Module): alive = [] if not classGuard(node, Name, String, Symbol): node['pre-alive'] = set(alive) if isinstance(node, Function): for sym in node.argSymbols: #Functions might have arguments that are never read. if sym['reads'] > 0: sym['tmp'] = sym['reads'] alive.append(sym) for child in node: liveness(child, alive) if classGuard(node, Assign, Phi): sym = extractSymbol(node) if not sym.has_key('tmp'): sym['tmp'] = sym['reads'] alive.append(sym) elif isinstance(node, Symbol) or (isinstance(node, Subscript) and isinstance(node.symbol, Symbol)): sym = extractSymbol(node) if sym.has_key('tmp'): sym['tmp'] -= 1 if sym['tmp'] == 0: alive.remove(sym) else: sym['tmp'] = sym['reads'] alive.append(sym) if not classGuard(node, Name, String, Symbol): node['post-alive'] = set(alive)
def move(src, dest): if config.arch == 'x86': from assembler.x86.ib import TwoOp, OneOp elif config.arch == 'x86_64': from assembler.x86_64.ib import TwoOp, OneOp if isinstance(src, Immediate): dest.tagged = src.packed elif classGuard(src, Label, Mem, Register): dest.tagged = src.tagged return TwoOp('mov', src, dest)
def scope(node, outerScope = None): if isinstance(node, Function): outerScope = node.name # Main doesn't have any arguments, so all function arguments can be # marked 'local' for sym in node.argSymbols: sym['scope'] = 'local' elif classGuard(node, Assign, Phi): sym = extractSymbol(node) sym['scope'] = 'global' if outerScope.name == 'main' else 'local' for child in node: scope(child, outerScope)
def propagateValuesPrime(node, consts, gather=True): # Memorize or replace symbol values, as appropriate. if gather: if isinstance(node, Assign) and isinstance(node.var, Symbol) and classGuard( node.exp, Boolean, Integer, Name, Symbol): consts[node.var] = node.exp elif isinstance(node, Phi) and len(node.srcs) == 1 and consts.has_key( node.srcs[0]): consts[node.target] = consts[node.srcs[0]] elif isinstance(node, Symbol) and consts.has_key(node): return consts[node] # Values in Phi nodes should never be replaced. if isinstance(node, While): # This looks all weird because we have to change the order of the # While node's children to propigate values appropriately. children = node.getChildren() children = children[1:] + children[0:1] newChildren = [ propagateValuesPrime(child, consts, gather) for child in children ] newChildren = newChildren[-1:] + newChildren[0:-1] node.setChildren(newChildren) elif not isinstance(node, Phi): newChildren = [ propagateValuesPrime(child, consts, gather) for child in node ] node.setChildren(newChildren) return consts if isinstance(node, Module) else node
def selectInstructions(node, cf, dest=None): # Error out if we receive a complex node that should have been flattened # or translated out before instruction selection. if not node.isSimple(): raise Exception( 'Non-simple node passed to instruction selection pass.') if isinstance(node, ast.Assign): # The destination is a name, so we need to translate it. dest = selectInstructions(node.var, cf) if isinstance(node.exp, ast.Name): code = Block('') # The source is a name, so we need to translate it. src = selectInstructions(node.exp, cf) if isinstance(src, Mem) and isinstance(dest, Mem): tmpColor = getTempColor(cf, node) code.append(move(src, tmpColor)) code.append(move(tmpColor, dest)) elif src != dest: code.append(move(src, dest)) return code elif isinstance(node.exp, ast.Integer): src = selectInstructions(node.exp, cf) src = pack(src, INT) return move(src, dest) else: # Here the right side of the assignment is a complex expression. # We will select instructions for it, giving the Symbol # representing the variable's location as the destination. return selectInstructions(node.exp, cf, dest) elif isinstance(node, ast.BasicBlock): code = Block() for child in node: code.append(selectInstructions(child, cf)) return code elif isinstance(node, ast.BinOp): code = Block() # The left and right operands need to be translated, but the # destination is already a Symbol, origLeft = left = selectInstructions(node.left, cf) origRight = right = selectInstructions(node.right, cf) ######### # Setup # ######### # This code will make sure that both the right and left parameters are # in registers. # tmpColor0 will be a register, and hold the final result of our # computation. The result may need to be moved if tmpColor0 is not # the destination. tmpColor0 = getTmpColor(cf, node) if isinstance(dest, Mem) else dest # The default tag for a result will be an int. If it already is # tagged, as will be the case for logical instructions or Add, it # won't end up being tagged. tmpColor0.tag = INT # Get the left operand into a register. if isinstance(left, Mem): if right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 else: tmpColor1 = getTmpColor( cf, node) if tmpColor0 == dest else tmpColor0 code.append(move(left, tmpColor1)) left = tmpColor1 elif isinstance(left, Immediate): if isinstance(node, ast.Div): # Move our dividend into rax and padd rdx. code.append(move(left, rax)) code.append(move(Immediate(left.value >> 31), rdx)) left = rax elif isinstance(node, ast.Sub): tmpColor1 = getTmpColor( cf, node) if right == tmpColor0 else tmpColor0 # Move the right operand to another register if it is in # tmpColor0. if right == tmpColor0: code.append(move(right, tmpColor1)) right = tmpColor1 # Move the left hand immediate into the destination register. code.append(move(left, tmpColor0)) left = tmpColor0 elif right != dest: # If the left hand value is an immediate and this is an add # or multiply operation the right hand value needs to be in # the destination register. code.append(move(right, tmpColor0)) right = tmpColor0 elif isinstance(left, Register): if isinstance(node, ast.Div) and left != rax: # Move our dividend into rax and padd rdx. code.append(move(left, rax)) code.append(move(left, rdx)) code.append(TwoOp('sar', Immediate(31), rdx)) left = rax elif left != dest: code.append(move(left, tmpColor0)) left = tmpColor0 # Get the right operand into a register. if isinstance(right, Mem): if left != tmpColor0 and classGuard( node, Add, Mul) and not isinstance(node, Logical): code.append(move(right, tmpColor0)) left = right right = tmpColor0 else: tmpColor1 = getTmpColor(cf, node, tmpColor0) code.append(move(right, tmpColor1)) right = tmpColor1 elif isinstance(right, Immediate) and isinstance(node, ast.Div) and \ not ((right.value % 2) == 0 and (right.value / 2) < 63): # If the right hand side is an immediate it needs to be # placed into a register for divide operations. tmpColor1 = getTmpColor(cf, node) if left == tmpColor0 else tmpColor0 code.append(move(right, tmpColor1)) right = tmpColor1 # Untag the left operand if it isn't an immediate. if isinstance(left, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(left)) # Untag the right operand if it isn't an immediate. if isinstance(right, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(right)) ############# # End Setup # ############# if isinstance(node, ast.Add): # The right value is never going to be an immediate due to our # constant folding transformation. if isinstance(left, Immediate): # This value hasn't been untagged yet. code.append(untag(tmpColor0)) if left.value == 1: code.append(OneOp('inc', tmpColor0)) else: code.append(TwoOp('add', left, tmpColor0)) else: # Build the case where we need to call the add function. case0 = Block() case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', ast.Name('add'), None)) case0.append(TwoOp('sub', Immediate(8), rsp)) case0.append(tag(rax, OBJ)) if rax != dest: case0.append(move(rax, tmpColor0)) # Build the case where we are adding two integers. case1 = Block() # Untag values as necessary. case1.append(untag(left)) case1.append(untag(right)) # Decide which operand to add to which. if left == tmpColor0: case1.append(TwoOp('add', right, tmpColor0)) else: case1.append(TwoOp('add', left, tmpColor0)) # Tag the result of the integer addition. case1.append(tag(tmpColor0, INT)) # Re-tag the operands. case1.append(tag(left, INT)) case1.append(tag(right, INT)) code.append( buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Div): if isinstance(right, Immediate) and ( right.value % 2) == 0 and (right.value / 2) < 63: # We can shift to the right instead of dividing. dist = right.value / 2 code.append(TwoOp('sar', Immediate(dist), tmpColor0)) else: code.append(OneOp('idiv', right, None)) if tmpColor0 != rax: code.append(move(rax, tmpColor0)) elif isinstance(node, ast.Mul): if isinstance(left, Immediate) and ( left.value % 2) == 0 and (left.value / 2) < 63: # We can shift to the left instead of multiplying. dist = left.value / 2 code.append(TwoOp('sal', Immediate(dist), tmpColor0)) elif right == dest or isinstance(left, Immediate): code.append(TwoOp('imul', left, tmpColor0)) else: code.append(TwoOp('imul', right, tmpColor0)) elif isinstance(node, ast.Sub): if isinstance(right, Immediate) and right == 1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('sub', right, tmpColor0)) elif classGuard(node, ast.And, ast.Or): jmp = 'jl' if isinstance(node, ast.And) else 'jg' if left == dest: case0 = move(right, tmpColor0) case1 = None else: case0 = move(right, tmpColor0) case1 = move(left, tmpColor0) code.append(buildITE(left, case0, case1, FALS, jmp, True)) elif classGuard(node, ast.Eq, ast.Ne): case0 = Block() if isinstance(node, ast.Eq): funName = ast.Name('equal') jmp = 'jz' else: funName = ast.Name('not_equal') jmp = 'jnz' # Build the case where we need to call the equal function. case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', funName, None)) case0.append(TwoOp('sub', Immediate(8), esp)) case0.append(tag(rax, BOOL)) case0.append(move(rax, tmpColor0)) case2 = move(TRU, tmpColor0) case3 = move(FALS, tmpColor0) # Build the case where we are comparing two integers or # booleans. case1 = buildITE(right, case2, case3, left, jmp) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Is): case0 = move(FALS, tmpColor0) case1 = move(TRU, tmpColor0) code.append(buildITE(right, case0, case1, left)) ########### # Cleanup # ########### # Re-tag left, right, and result appropriately. code.append(tag(tmpColor0)) if isinstance(origLeft, Register) and origLeft in toColors( node['post-alive']): code.append(tag(origLeft)) if isinstance(origRight, Register) and origRight in toColors( node['post-alive']): code.append(tag(origRight)) # Move the result. if tmpColor0 != dest: code.append(move(tmpColor0, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.Boolean): if isinstance(node, ast.Tru): return TRU elif isinstance(node, ast.Fals): return FALS elif isinstance(node, ast.FunctionCall): code = Block() # Save any caller saved registers currently in use. saveColors = toColors(node['post-alive']) saveRegs(code, caller, saveColors) # Either move our arguments into their registers, or push them onto # the stack. addSize = 0 index = 0 for arg in node.args: src = selectInstructions(arg, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) reg = None if index < len(args): reg = args[index] if reg != src: code.append(move(src, reg)) elif not reg: code.append(OneOp("push", src)) addSize += 8 index += 1 # Clear rax's tagged and tag values. rax.clear() #Make the function call. code.append(OneOp("call", node.name, None)) # Restore the stack. if addSize > 0: code.append(TwoOp("add", Immediate(addSize), rsp)) # Tag the result if necessary. code.append(tag(rax, node.tag)) # Move the result into the proper destination. if dest and dest != rax: code.append(move(rax, dest)) # Restore any caller saved registers that are in use. restoreRegs(code, caller, saveColors) return code elif isinstance(node, ast.If): cond = selectInstructions(node.cond, cf) then = selectInstructions(node.then, cf) els = selectInstructions(node.els, cf) return buildITE(cond, then, els) elif isinstance(node, ast.Integer): return Immediate(node.value) elif isinstance(node, ast.Module): code = Block() code.header = "# x86_64\n" code.header += ".globl main\n" code.header += "main:\n" usedColors = toColors(node.collectSymbols()) # Save any callee saved registers we used. saveRegs(code, callee, usedColors) # Expand the stack. if cf.offset > 0: code.append(TwoOp('sub', Immediate(cf.offset), Register('rsp'))) # Append the module's code. code.append(selectInstructions(node.block, cf)) endBlock = Block() # Restore any callee saved registers we used. restoreRegs(endBlock, callee, usedColors) # Put our exit value in %rax endBlock.append(TwoOp('mov', Immediate(0), rax)) # Return endBlock.append(Instruction("ret")) code.append(endBlock) return code elif isinstance(node, ast.Symbol): if node.has_key('color'): return node['color'] else: raise Exception("Uncolored symbol ({0}) encountered.".format(node)) elif isinstance(node, ast.UnaryOp): code = Block() src = selectInstructions(node.operand, cf) ######### # Setup # ######### tmpColor = getTmpColor(cf, node) if isinstance(dest, Mem) else dest if src != tmpColor: code.append(move(src, tmpColor)) # Untag the argument. code.append(untag(tmpColor)) ############# # End Setup # ############# if isinstance(node, ast.Negate): code.append(OneOp('neg', tmpColor)) # Tell the cleanup code to tag the result as an integer. tmpColor.tagged = False tmpColor.tag = INT elif isinstance(node, ast.Not): case0 = move(FALS, tmpColor) case1 = move(TRU, tmpColor) code.append(buildITE(src, case0, case1, FALS, 'jle')) ########### # Cleanup # ########### # Tag the result. code.append(tag(tmpColor)) if tmpColor != dest: code.append(move(tmpColor, dest)) ############### # End Cleanup # ############### return code
def selectInstructions(node, cf, dest = None): global shifts # Error out if we receive a complex node that should have been flattened # or translated out before instruction selection. if not node.isSimple(): raise Exception('Non-simple node passed to instruction selection pass.') if isinstance(node, ast.Assign): # The destination is a name, so we need to translate it. dest = selectInstructions(node.var, cf) if isinstance(node.exp, ast.Symbol): code = Block('') # The source is a name, so we need to translate it. src = selectInstructions(node.exp, cf) if isinstance(src, Mem) and isinstance(dest, Mem): tmpColor = getTmpColor(cf, node) code.append(move(src, tmpColor)) code.append(move(tmpColor, dest)) elif src != dest: code.append(move(src, dest)) return code elif classGuard(node.exp, ast.Boolean, ast.Integer): src = selectInstructions(node.exp, cf) if isinstance(node.exp, ast.Integer): src = pack(src, INT) return move(src, dest) else: # Here the right side of the assignment is a complex expression. # We will select instructions for it, giving the Symbol # representing the variable's location as the destination. return selectInstructions(node.exp, cf, dest) elif isinstance(node, ast.BasicBlock): code = Block() for child in node: code.append(selectInstructions(child, cf)) return code elif isinstance(node, ast.BinOp): code = Block() # The left and right operands need to be translated, but the # destination is already a Symbol, origLeft = left = selectInstructions(node.left, cf) origRight = right = selectInstructions(node.right, cf) ######### # Setup # ######### # This code will make sure that both the right and left parameters are # in registers. # tmpColor0 will be a register, and hold the final result of our # computation. The result may need to be moved if tmpColor0 is not # the destination. tmpColor0 = getTmpColor(cf, node) if isinstance(dest, Mem) else dest # The default tag for a result will be an int. If it is already # tagged, as will be the case for logical instructions or Add, it won't # end up being tagged. tmpColor0.tag = INT # Get the left operand into a register. if classGuard(left, Mem, Label): if right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 else: tmpColor1 = getTmpColor(cf, node) if tmpColor0 == dest else tmpColor0 code.append(move(left, tmpColor1)) left = tmpColor1 elif isinstance(left, Immediate): if isinstance(node, ast.Div): # Move our dividend into %eax. code.append(move(left, eax)) left = eax elif isinstance(node, ast.Sub): tmpColor1 = getTmpColor(cf, node) if right == tmpColor0 else tmpColor0 # Move the right operand to another register if it is in # tmpColor0. if right == tmpColor0: code.append(move(right, tmpColor1)) right = tmpColor1 # Move the left hand immediate into the destination register. code.append(move(left, tmpColor0)) left = tmpColor0 elif right != dest: # If the left hand value is an immediate and this is an add # or multiply operation the right hand value needs to be in # the destination register. code.append(move(right, tmpColor0)) right = tmpColor0 elif isinstance(left, Register): if isinstance(node, ast.Div) and left != eax: # Move our dividend into eax. code.append(move(left, eax)) left = eax elif left != dest and right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 # Get the right operand into a register. if classGuard(right, Mem, Label): if left != tmpColor0 and classGuard(node, ast.Add, ast.Mul) and not isinstance(node, ast.Logical): code.append(move(right, tmpColor0)) left = right right = tmpColor0 else: tmpColor1 = getTmpColor(cf, node, tmpColor0) code.append(move(right, tmpColor1)) right = tmpColor1 elif isinstance(right, Immediate) and isinstance(node, ast.Div) and right.value not in shifts: # If the right hand side is an immediate it needs to be # placed into a register for divide operations. tmpColor1 = getTmpColor(cf, node) if left == tmpColor0 else tmpColor0 code.append(move(right, tmpColor1)) right = tmpColor1 # Untag the left operand if it isn't an immediate. if isinstance(left, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(left)) # Untag the right operand if it isn't an immediate. if isinstance(right, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(right)) ############# # End Setup # ############# if isinstance(node, ast.Add): # The right value is never going to be an immediate due to our # constant folding transformation. if isinstance(left, Immediate): #This value hasn't been untagged yet. code.append(untag(tmpColor0)) if left.value == 1: code.append(OneOp('inc', tmpColor0)) elif left.value == -1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('add', left, tmpColor0)) else: # Build the case where we need to call the add function. case0 = Block() case0.append(untag(left, OBJ)) case0.append(untag(right, OBJ)) case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', ast.Name('add'), None)) case0.append(TwoOp('add', Immediate(8), esp)) case0.append(tag(eax, OBJ)) case0.append(tag(left, OBJ)) case0.append(tag(right, OBJ)) if eax != dest: case0.append(move(eax, tmpColor0)) # Build the case where we are adding two integers. case1 = Block() # Untag values as necessary. case1.append(untag(left)) case1.append(untag(right)) # Decide which operand to add to which. if left == tmpColor0: case1.append(TwoOp('add', right, tmpColor0)) else: case1.append(TwoOp('add', left, tmpColor0)) # Tag the result of the integer addition. case1.append(tag(tmpColor0, INT)) # Re-tag the operands. case1.append(tag(left, INT)) case1.append(tag(right, INT)) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Div): if isinstance(right, Immediate) and right.value in shifts: # We can shift to the right instead of dividing. dist = shifts[right.value] code.append(TwoOp('sar', Immediate(dist), eax)) else: # Prepare out %edx. code.append(Instruction('cltd')) code.append(OneOp('idiv', right)) if tmpColor0 != eax: code.append(move(eax, tmpColor0)) elif isinstance(node, ast.Mul): if isinstance(left, Immediate) and left.value in shifts: # We can shift to the left instead of multiplying. dist = shifts[left.value] code.append(TwoOp('sal', Immediate(dist), tmpColor0)) elif right == dest or isinstance(left, Immediate): code.append(TwoOp('imul', left, tmpColor0)) else: code.append(TwoOp('imul', right, tmpColor0)) code.append(tag(right, INT)) elif isinstance(node, ast.Sub): if isinstance(right, Immediate) and right.value == 1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('sub', right, tmpColor0)) elif classGuard(node, ast.And, ast.Or): jmp = 'jl' if isinstance(node, ast.And) else 'jg' if left == dest: case0 = move(right, tmpColor0) case1 = None else: case0 = move(right, tmpColor0) case1 = move(left, tmpColor0) code.append(buildITE(left, case0, case1, FALS, jmp, True)) elif classGuard(node, ast.Eq, ast.Ne): case0 = Block() if isinstance(node, ast.Eq): funName = ast.Name('equal') jmp = 'jne' else: funName = ast.Name('not_equal') jmp = 'je' # Build the case where we need to call the equal function. if isinstance(left, Register): case0.append(untag(left, OBJ)) if isinstance(right, Register): case0.append(untag(right, OBJ)) case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', funName, None)) case0.append(TwoOp('add', Immediate(8), esp)) case0.append(tag(eax, BOOL)) if isinstance(left, Register) and left != eax: case0.append(tag(left)) if isinstance(right, Register) and right != eax: case0.append(tag(right)) if tmpColor0 != eax: case0.append(move(eax, tmpColor0)) case2 = move(TRU, tmpColor0) case3 = move(FALS, tmpColor0) # Pack left and right immediates for comparison. if isinstance(left, Immediate): left = pack(right, INT) if isinstance(right, Immediate): right = pack(right, INT) # Build the case where we are comparing two integers or # booleans. case1 = buildITE(right, case2, case3, left, jmp) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Is): case0 = move(FALS, tmpColor0) case1 = move(TRU, tmpColor0) code.append(buildITE(right, case0, case1, left)) ########### # Cleanup # ########### # Re-tag left, right, and result appropriately. code.append(tag(tmpColor0)) if isinstance(origLeft, Register) and origLeft in toColors(node['post-alive']): code.append(tag(origLeft)) if isinstance(origRight, Register) and origRight in toColors(node['post-alive']): code.append(tag(origRight)) # Move the result. if tmpColor0 != dest: code.append(move(tmpColor0, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.Boolean): if isinstance(node, ast.Tru): return TRU elif isinstance(node, ast.Fals): return FALS elif isinstance(node, ast.Function): code = Block() # Mark that the arguments are taged. #~for color in toColors(node.argSymbols): #~color.tagged = True code.append(Directive("globl {0}".format(node.name), False)) code.append(Directive('align 4', False)) code.append(Label(node.name, '')) # Push the old base pointer onto the stack. code.append(OneOp('push', ebp)) # Make the old stack pointer the new base pointer. code.append(move(esp, ebp)) usedColors = toColors(node.collectSymbols()) # Save any callee saved registers we used. saveRegs(code, callee, usedColors) # Expand the stack. foo = cf.offset - 4 if foo > 0: code.append(TwoOp('sub', Immediate(foo), esp)) # Append the module's code. code.append(selectInstructions(node.block, cf)) endBlock = Block() # Restore the stack. if foo > 0: endBlock.append(TwoOp('add', Immediate(foo), esp)) # Restore any callee saved registers we used. restoreRegs(endBlock, callee, usedColors) # Restore the %esp and %ebp registers. endBlock.append(Instruction('leave')) # Return endBlock.append(Instruction('ret')) code.append(endBlock) return code elif isinstance(node, ast.FunctionCall): code = Block() # Save any caller saved registers that are in use after this call. saveColors = toColors(node['post-alive']) saveRegs(code, caller, saveColors) addSize = 0 args = list(node.args) args.reverse() for arg in args: src = selectInstructions(arg, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) elif isinstance(src, ast.Name) or (isinstance(src, Label) and src.reference): src = '$' + str(src) code.append(OneOp('push', src)) addSize += 4 # Clear eax's tagged and tag values. eax.clear() # Make the function call. if isinstance(node.name, ast.Name): # This is the direct call case. code.append(OneOp('call', node.name, None)) else: # This is the case when we recieved the function pointer as an # argument. This can either result in a direct or an indirect # call. src = selectInstructions(node.name, cf) if isinstance(src, Mem) or src == eax: tmpColor = getTmpColor(cf, node) code.append(move(src, tmpColor)) src = tmpColor # This is the case where we need to unpack the closure. case0 = Block() case0.append(OneOp('push', src)) case0.append(OneOp('call', ast.Name('get_free_vars'), None)) case0.append(TwoOp('add', Immediate(4), esp)) case0.append(OneOp('push', eax)) case0.append(OneOp('push', src)) case0.append(OneOp('call', ast.Name('get_fun_ptr'), None)) case0.append(TwoOp('add', Immediate(4), esp)) case0.append(OneOp('call', '*' + str(eax), None)) # The case where it is a direct call (made indirectly). case1 = OneOp('call', '*' + str(src), None) code.append(buildITE(src, case0, case1, TAG_MASK, 'je', True)) #~name = '*' + str(node.name['color']) if isinstance(node.name, Symbol) else node.name #~code.append(OneOp('call', name, None)) # Restore the stack. if addSize > 0: code.append(TwoOp('add', Immediate(addSize), esp)) # Tag the result if necessary. code.append(tag(eax, node.tag)) # Move the result into the proper destination. if dest and dest != eax: code.append(move(eax, dest)) # Restore any caller saved registers that are in use. restoreRegs(code, caller, saveColors) return code elif isinstance(node, ast.If): cond = selectInstructions(node.cond, cf) then = selectInstructions(node.then, cf) els = selectInstructions(node.els, cf) thenSyms = node.then.collectSymbols('r') elseSyms = node.els.collectSymbols('r') # Insert Phi moves. for phi in node.jn: for sym in phi.srcs: if sym['color'] != phi.target['color']: (then if sym in thenSyms else els).append(move(sym['color'], phi.target['color'])) # Here we compare the conditional to False, and if it is less then or # equal to False (either False or 0) we will go to the else case. return buildITE(cond, then, els, FALS, 'jle') elif isinstance(node, ast.Integer): return Immediate(node.value) elif isinstance(node, ast.Module): code = Block("# x86\n") data = Block("# Data\n") funs = Block("\n# Functions\n") # Define our data section. for sym in node.collectSymbols(): if sym['heapify'] and sym not in node.strings.values(): data.append(Directive("globl {0}".format(sym['color']), False)) data.append(Directive('data')) data.append(Directive('align 4')) data.append(Directive("size {0}, 4".format(sym['color']))) data.append(sym['color']) data.append(Directive('long 1')) data.append(Directive('section .data')) # Add any strings we may need to define. for string in node.strings: sym = node.strings[string] data.append(sym['color']) data.append(Directive("string \"{0}\"".format(string))) data.append(Directive('text')) # Define our functions. for fun in node.functions: funs.append(selectInstructions(fun, cf)) # Concatenate our code. code.append(data) code.append(funs) return code elif isinstance(node, ast.Name): return node elif isinstance(node, ast.Return): code = Block() src = selectInstructions(node.value, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) elif isinstance(src, ast.Name): src = '$' + str(src) if src != eax: code.append(move(src, eax)) return code elif isinstance(node, ast.String): return node['color'] elif isinstance(node, ast.Symbol): if node.has_key('color'): return node['color'] else: raise Exception("Uncolored symbol ({0}) encountered.".format(node)) elif isinstance(node, ast.UnaryOp): code = Block() src = selectInstructions(node.operand, cf) ######### # Setup # ######### tmpColor = getTmpColor(cf, node) if isinstance(dest, Mem) else dest if src != tmpColor: code.append(move(src, tmpColor)) # Untag the argument. code.append(untag(tmpColor)) ############# # End Setup # ############# if isinstance(node, ast.Negate): code.append(OneOp('neg', tmpColor)) # Tell the cleanup code to tag the result as an integer. tmpColor.tagged = False tmpColor.tag = INT elif isinstance(node, ast.Not): case0 = move(FALS, tmpColor) case1 = move(TRU, tmpColor) code.append(buildITE(src, case0, case1, FALS, 'jle')) ########### # Cleanup # ########### # Tag the result. code.append(tag(tmpColor)) if tmpColor != dest: code.append(move(tmpColor, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.While): code = Block() cond = selectInstructions(node.cond, cf) condBody = selectInstructions(node.condBody, cf) body = selectInstructions(node.body, cf) # Insert Phi moves. for phi in node.jn.phis: for src in phi: if src['color'] != phi.target['color']: (code if src in node['pre-alive'] else body).append(move(src['color'], phi.target['color'])) l0 = getLabel() l1 = getLabel() # Initial jump to the cond block. code.append(OneOp('jmp', l1, None)) # The body label and the body. code.append(l0) code.append(body) # Cond label and cond body. code.append(l1) code.append(condBody) # Test to see if we should loop. code.append(TwoOp('cmp', FALS, cond)) code.append(OneOp('jg', l0, None)) return code
def selectInstructions(node, cf, dest = None): # Error out if we receive a complex node that should have been flattened # or translated out before instruction selection. if not node.isSimple(): raise Exception('Non-simple node passed to instruction selection pass.') if isinstance(node, ast.Assign): # The destination is a name, so we need to translate it. dest = selectInstructions(node.var, cf) if isinstance(node.exp, ast.Name): code = Block('') # The source is a name, so we need to translate it. src = selectInstructions(node.exp, cf) if isinstance(src, Mem) and isinstance(dest, Mem): tmpColor = getTempColor(cf, node) code.append(move(src, tmpColor)) code.append(move(tmpColor, dest)) elif src != dest: code.append(move(src, dest)) return code elif isinstance(node.exp, ast.Integer): src = selectInstructions(node.exp, cf) src = pack(src, INT) return move(src, dest) else: # Here the right side of the assignment is a complex expression. # We will select instructions for it, giving the Symbol # representing the variable's location as the destination. return selectInstructions(node.exp, cf, dest) elif isinstance(node, ast.BasicBlock): code = Block() for child in node: code.append(selectInstructions(child, cf)) return code elif isinstance(node, ast.BinOp): code = Block() # The left and right operands need to be translated, but the # destination is already a Symbol, origLeft = left = selectInstructions(node.left, cf) origRight = right = selectInstructions(node.right, cf) ######### # Setup # ######### # This code will make sure that both the right and left parameters are # in registers. # tmpColor0 will be a register, and hold the final result of our # computation. The result may need to be moved if tmpColor0 is not # the destination. tmpColor0 = getTmpColor(cf, node) if isinstance(dest, Mem) else dest # The default tag for a result will be an int. If it already is # tagged, as will be the case for logical instructions or Add, it # won't end up being tagged. tmpColor0.tag = INT # Get the left operand into a register. if isinstance(left, Mem): if right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 else: tmpColor1 = getTmpColor(cf, node) if tmpColor0 == dest else tmpColor0 code.append(move(left, tmpColor1)) left = tmpColor1 elif isinstance(left, Immediate): if isinstance(node, ast.Div): # Move our dividend into rax and padd rdx. code.append(move(left, rax)) code.append(move(Immediate(left.value >> 31), rdx)) left = rax elif isinstance(node, ast.Sub): tmpColor1 = getTmpColor(cf, node) if right == tmpColor0 else tmpColor0 # Move the right operand to another register if it is in # tmpColor0. if right == tmpColor0: code.append(move(right, tmpColor1)) right = tmpColor1 # Move the left hand immediate into the destination register. code.append(move(left, tmpColor0)) left = tmpColor0 elif right != dest: # If the left hand value is an immediate and this is an add # or multiply operation the right hand value needs to be in # the destination register. code.append(move(right, tmpColor0)) right = tmpColor0 elif isinstance(left, Register): if isinstance(node, ast.Div) and left != rax: # Move our dividend into rax and padd rdx. code.append(move(left, rax)) code.append(move(left, rdx)) code.append(TwoOp('sar', Immediate(31), rdx)) left = rax elif left != dest: code.append(move(left, tmpColor0)) left = tmpColor0 # Get the right operand into a register. if isinstance(right, Mem): if left != tmpColor0 and classGuard(node, Add, Mul) and not isinstance(node, Logical): code.append(move(right, tmpColor0)) left = right right = tmpColor0 else: tmpColor1 = getTmpColor(cf, node, tmpColor0) code.append(move(right, tmpColor1)) right = tmpColor1 elif isinstance(right, Immediate) and isinstance(node, ast.Div) and \ not ((right.value % 2) == 0 and (right.value / 2) < 63): # If the right hand side is an immediate it needs to be # placed into a register for divide operations. tmpColor1 = getTmpColor(cf, node) if left == tmpColor0 else tmpColor0 code.append(move(right, tmpColor1)) right = tmpColor1 # Untag the left operand if it isn't an immediate. if isinstance(left, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(left)) # Untag the right operand if it isn't an immediate. if isinstance(right, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(right)) ############# # End Setup # ############# if isinstance(node, ast.Add): # The right value is never going to be an immediate due to our # constant folding transformation. if isinstance(left, Immediate): # This value hasn't been untagged yet. code.append(untag(tmpColor0)) if left.value == 1: code.append(OneOp('inc', tmpColor0)) else: code.append(TwoOp('add', left, tmpColor0)) else: # Build the case where we need to call the add function. case0 = Block() case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', ast.Name('add'), None)) case0.append(TwoOp('sub', Immediate(8), rsp)) case0.append(tag(rax, OBJ)) if rax != dest: case0.append(move(rax, tmpColor0)) # Build the case where we are adding two integers. case1 = Block() # Untag values as necessary. case1.append(untag(left)) case1.append(untag(right)) # Decide which operand to add to which. if left == tmpColor0: case1.append(TwoOp('add', right, tmpColor0)) else: case1.append(TwoOp('add', left, tmpColor0)) # Tag the result of the integer addition. case1.append(tag(tmpColor0, INT)) # Re-tag the operands. case1.append(tag(left, INT)) case1.append(tag(right, INT)) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Div): if isinstance(right, Immediate) and (right.value % 2) == 0 and (right.value / 2) < 63: # We can shift to the right instead of dividing. dist = right.value / 2 code.append(TwoOp('sar', Immediate(dist), tmpColor0)) else: code.append(OneOp('idiv', right, None)) if tmpColor0 != rax: code.append(move(rax, tmpColor0)) elif isinstance(node, ast.Mul): if isinstance(left, Immediate) and (left.value % 2) == 0 and (left.value / 2) < 63: # We can shift to the left instead of multiplying. dist = left.value / 2 code.append(TwoOp('sal', Immediate(dist), tmpColor0)) elif right == dest or isinstance(left, Immediate): code.append(TwoOp('imul', left, tmpColor0)) else: code.append(TwoOp('imul', right, tmpColor0)) elif isinstance(node, ast.Sub): if isinstance(right, Immediate) and right == 1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('sub', right, tmpColor0)) elif classGuard(node, ast.And, ast.Or): jmp = 'jl' if isinstance(node, ast.And) else 'jg' if left == dest: case0 = move(right, tmpColor0) case1 = None else: case0 = move(right, tmpColor0) case1 = move(left, tmpColor0) code.append(buildITE(left, case0, case1, FALS, jmp, True)) elif classGuard(node, ast.Eq, ast.Ne): case0 = Block() if isinstance(node, ast.Eq): funName = ast.Name('equal') jmp = 'jz' else: funName = ast.Name('not_equal') jmp = 'jnz' # Build the case where we need to call the equal function. case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', funName, None)) case0.append(TwoOp('sub', Immediate(8), esp)) case0.append(tag(rax, BOOL)) case0.append(move(rax, tmpColor0)) case2 = move(TRU, tmpColor0) case3 = move(FALS, tmpColor0) # Build the case where we are comparing two integers or # booleans. case1 = buildITE(right, case2, case3, left, jmp) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Is): case0 = move(FALS, tmpColor0) case1 = move(TRU, tmpColor0) code.append(buildITE(right, case0, case1, left)) ########### # Cleanup # ########### # Re-tag left, right, and result appropriately. code.append(tag(tmpColor0)) if isinstance(origLeft, Register) and origLeft in toColors(node['post-alive']): code.append(tag(origLeft)) if isinstance(origRight, Register) and origRight in toColors(node['post-alive']): code.append(tag(origRight)) # Move the result. if tmpColor0 != dest: code.append(move(tmpColor0, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.Boolean): if isinstance(node, ast.Tru): return TRU elif isinstance(node, ast.Fals): return FALS elif isinstance(node, ast.FunctionCall): code = Block() # Save any caller saved registers currently in use. saveColors = toColors(node['post-alive']) saveRegs(code, caller, saveColors) # Either move our arguments into their registers, or push them onto # the stack. addSize = 0 index = 0 for arg in node.args: src = selectInstructions(arg, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) reg = None if index < len(args): reg = args[index] if reg != src: code.append(move(src, reg)) elif not reg: code.append(OneOp("push", src)) addSize += 8 index += 1 # Clear rax's tagged and tag values. rax.clear() #Make the function call. code.append(OneOp("call", node.name, None)) # Restore the stack. if addSize > 0: code.append(TwoOp("add", Immediate(addSize), rsp)) # Tag the result if necessary. code.append(tag(rax, node.tag)) # Move the result into the proper destination. if dest and dest != rax: code.append(move(rax, dest)) # Restore any caller saved registers that are in use. restoreRegs(code, caller, saveColors) return code elif isinstance(node, ast.If): cond = selectInstructions(node.cond, cf) then = selectInstructions(node.then, cf) els = selectInstructions(node.els, cf) return buildITE(cond, then, els) elif isinstance(node, ast.Integer): return Immediate(node.value) elif isinstance(node, ast.Module): code = Block() code.header = "# x86_64\n" code.header += ".globl main\n" code.header += "main:\n" usedColors = toColors(node.collectSymbols()) # Save any callee saved registers we used. saveRegs(code, callee, usedColors) # Expand the stack. if cf.offset > 0: code.append(TwoOp('sub', Immediate(cf.offset), Register('rsp'))) # Append the module's code. code.append(selectInstructions(node.block, cf)) endBlock = Block() # Restore any callee saved registers we used. restoreRegs(endBlock, callee, usedColors) # Put our exit value in %rax endBlock.append(TwoOp('mov', Immediate(0), rax)) # Return endBlock.append(Instruction("ret")) code.append(endBlock) return code elif isinstance(node, ast.Symbol): if node.has_key('color'): return node['color'] else: raise Exception("Uncolored symbol ({0}) encountered.".format(node)) elif isinstance(node, ast.UnaryOp): code = Block() src = selectInstructions(node.operand, cf) ######### # Setup # ######### tmpColor = getTmpColor(cf, node) if isinstance(dest, Mem) else dest if src != tmpColor: code.append(move(src, tmpColor)) # Untag the argument. code.append(untag(tmpColor)) ############# # End Setup # ############# if isinstance(node, ast.Negate): code.append(OneOp('neg', tmpColor)) # Tell the cleanup code to tag the result as an integer. tmpColor.tagged = False tmpColor.tag = INT elif isinstance(node, ast.Not): case0 = move(FALS, tmpColor) case1 = move(TRU, tmpColor) code.append(buildITE(src, case0, case1, FALS, 'jle')) ########### # Cleanup # ########### # Tag the result. code.append(tag(tmpColor)) if tmpColor != dest: code.append(move(tmpColor, dest)) ############### # End Cleanup # ############### return code
def selectInstructions(node, cf, dest=None): global shifts # Error out if we receive a complex node that should have been flattened # or translated out before instruction selection. if not node.isSimple(): raise Exception( 'Non-simple node passed to instruction selection pass.') if isinstance(node, ast.Assign): # The destination is a name, so we need to translate it. dest = selectInstructions(node.var, cf) if isinstance(node.exp, ast.Symbol): code = Block('') # The source is a name, so we need to translate it. src = selectInstructions(node.exp, cf) if isinstance(src, Mem) and isinstance(dest, Mem): tmpColor = getTmpColor(cf, node) code.append(move(src, tmpColor)) code.append(move(tmpColor, dest)) elif src != dest: code.append(move(src, dest)) return code elif classGuard(node.exp, ast.Boolean, ast.Integer): src = selectInstructions(node.exp, cf) if isinstance(node.exp, ast.Integer): src = pack(src, INT) return move(src, dest) else: # Here the right side of the assignment is a complex expression. # We will select instructions for it, giving the Symbol # representing the variable's location as the destination. return selectInstructions(node.exp, cf, dest) elif isinstance(node, ast.BasicBlock): code = Block() for child in node: code.append(selectInstructions(child, cf)) return code elif isinstance(node, ast.BinOp): code = Block() # The left and right operands need to be translated, but the # destination is already a Symbol, origLeft = left = selectInstructions(node.left, cf) origRight = right = selectInstructions(node.right, cf) ######### # Setup # ######### # This code will make sure that both the right and left parameters are # in registers. # tmpColor0 will be a register, and hold the final result of our # computation. The result may need to be moved if tmpColor0 is not # the destination. tmpColor0 = getTmpColor(cf, node) if isinstance(dest, Mem) else dest # The default tag for a result will be an int. If it is already # tagged, as will be the case for logical instructions or Add, it won't # end up being tagged. tmpColor0.tag = INT # Get the left operand into a register. if classGuard(left, Mem, Label): if right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 else: tmpColor1 = getTmpColor( cf, node) if tmpColor0 == dest else tmpColor0 code.append(move(left, tmpColor1)) left = tmpColor1 elif isinstance(left, Immediate): if isinstance(node, ast.Div): # Move our dividend into %eax. code.append(move(left, eax)) left = eax elif isinstance(node, ast.Sub): tmpColor1 = getTmpColor( cf, node) if right == tmpColor0 else tmpColor0 # Move the right operand to another register if it is in # tmpColor0. if right == tmpColor0: code.append(move(right, tmpColor1)) right = tmpColor1 # Move the left hand immediate into the destination register. code.append(move(left, tmpColor0)) left = tmpColor0 elif right != dest: # If the left hand value is an immediate and this is an add # or multiply operation the right hand value needs to be in # the destination register. code.append(move(right, tmpColor0)) right = tmpColor0 elif isinstance(left, Register): if isinstance(node, ast.Div) and left != eax: # Move our dividend into eax. code.append(move(left, eax)) left = eax elif left != dest and right != dest: code.append(move(left, tmpColor0)) left = tmpColor0 # Get the right operand into a register. if classGuard(right, Mem, Label): if left != tmpColor0 and classGuard( node, ast.Add, ast.Mul) and not isinstance(node, ast.Logical): code.append(move(right, tmpColor0)) left = right right = tmpColor0 else: tmpColor1 = getTmpColor(cf, node, tmpColor0) code.append(move(right, tmpColor1)) right = tmpColor1 elif isinstance(right, Immediate) and isinstance( node, ast.Div) and right.value not in shifts: # If the right hand side is an immediate it needs to be # placed into a register for divide operations. tmpColor1 = getTmpColor(cf, node) if left == tmpColor0 else tmpColor0 code.append(move(right, tmpColor1)) right = tmpColor1 # Untag the left operand if it isn't an immediate. if isinstance(left, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(left)) # Untag the right operand if it isn't an immediate. if isinstance(right, Color) and classGuard(node, ast.Div, ast.Mul, ast.Sub): code.append(untag(right)) ############# # End Setup # ############# if isinstance(node, ast.Add): # The right value is never going to be an immediate due to our # constant folding transformation. if isinstance(left, Immediate): #This value hasn't been untagged yet. code.append(untag(tmpColor0)) if left.value == 1: code.append(OneOp('inc', tmpColor0)) elif left.value == -1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('add', left, tmpColor0)) else: # Build the case where we need to call the add function. case0 = Block() case0.append(untag(left, OBJ)) case0.append(untag(right, OBJ)) case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', ast.Name('add'), None)) case0.append(TwoOp('add', Immediate(8), esp)) case0.append(tag(eax, OBJ)) case0.append(tag(left, OBJ)) case0.append(tag(right, OBJ)) if eax != dest: case0.append(move(eax, tmpColor0)) # Build the case where we are adding two integers. case1 = Block() # Untag values as necessary. case1.append(untag(left)) case1.append(untag(right)) # Decide which operand to add to which. if left == tmpColor0: case1.append(TwoOp('add', right, tmpColor0)) else: case1.append(TwoOp('add', left, tmpColor0)) # Tag the result of the integer addition. case1.append(tag(tmpColor0, INT)) # Re-tag the operands. case1.append(tag(left, INT)) case1.append(tag(right, INT)) code.append( buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Div): if isinstance(right, Immediate) and right.value in shifts: # We can shift to the right instead of dividing. dist = shifts[right.value] code.append(TwoOp('sar', Immediate(dist), eax)) else: # Prepare out %edx. code.append(Instruction('cltd')) code.append(OneOp('idiv', right)) if tmpColor0 != eax: code.append(move(eax, tmpColor0)) elif isinstance(node, ast.Mul): if isinstance(left, Immediate) and left.value in shifts: # We can shift to the left instead of multiplying. dist = shifts[left.value] code.append(TwoOp('sal', Immediate(dist), tmpColor0)) elif right == dest or isinstance(left, Immediate): code.append(TwoOp('imul', left, tmpColor0)) else: code.append(TwoOp('imul', right, tmpColor0)) code.append(tag(right, INT)) elif isinstance(node, ast.Sub): if isinstance(right, Immediate) and right.value == 1: code.append(OneOp('dec', tmpColor0)) else: code.append(TwoOp('sub', right, tmpColor0)) elif classGuard(node, ast.And, ast.Or): jmp = 'jl' if isinstance(node, ast.And) else 'jg' if left == dest: case0 = move(right, tmpColor0) case1 = None else: case0 = move(right, tmpColor0) case1 = move(left, tmpColor0) code.append(buildITE(left, case0, case1, FALS, jmp, True)) elif classGuard(node, ast.Eq, ast.Ne): case0 = Block() if isinstance(node, ast.Eq): funName = ast.Name('equal') jmp = 'jne' else: funName = ast.Name('not_equal') jmp = 'je' # Build the case where we need to call the equal function. if isinstance(left, Register): case0.append(untag(left, OBJ)) if isinstance(right, Register): case0.append(untag(right, OBJ)) case0.append(OneOp('push', right)) case0.append(OneOp('push', left)) case0.append(OneOp('call', funName, None)) case0.append(TwoOp('add', Immediate(8), esp)) case0.append(tag(eax, BOOL)) if isinstance(left, Register) and left != eax: case0.append(tag(left)) if isinstance(right, Register) and right != eax: case0.append(tag(right)) if tmpColor0 != eax: case0.append(move(eax, tmpColor0)) case2 = move(TRU, tmpColor0) case3 = move(FALS, tmpColor0) # Pack left and right immediates for comparison. if isinstance(left, Immediate): left = pack(right, INT) if isinstance(right, Immediate): right = pack(right, INT) # Build the case where we are comparing two integers or # booleans. case1 = buildITE(right, case2, case3, left, jmp) code.append(buildITE(tmpColor0, case0, case1, TAG_MASK, 'je', True)) elif isinstance(node, ast.Is): case0 = move(FALS, tmpColor0) case1 = move(TRU, tmpColor0) code.append(buildITE(right, case0, case1, left)) ########### # Cleanup # ########### # Re-tag left, right, and result appropriately. code.append(tag(tmpColor0)) if isinstance(origLeft, Register) and origLeft in toColors( node['post-alive']): code.append(tag(origLeft)) if isinstance(origRight, Register) and origRight in toColors( node['post-alive']): code.append(tag(origRight)) # Move the result. if tmpColor0 != dest: code.append(move(tmpColor0, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.Boolean): if isinstance(node, ast.Tru): return TRU elif isinstance(node, ast.Fals): return FALS elif isinstance(node, ast.Function): code = Block() # Mark that the arguments are taged. #~for color in toColors(node.argSymbols): #~color.tagged = True code.append(Directive("globl {0}".format(node.name), False)) code.append(Directive('align 4', False)) code.append(Label(node.name, '')) # Push the old base pointer onto the stack. code.append(OneOp('push', ebp)) # Make the old stack pointer the new base pointer. code.append(move(esp, ebp)) usedColors = toColors(node.collectSymbols()) # Save any callee saved registers we used. saveRegs(code, callee, usedColors) # Expand the stack. foo = cf.offset - 4 if foo > 0: code.append(TwoOp('sub', Immediate(foo), esp)) # Append the module's code. code.append(selectInstructions(node.block, cf)) endBlock = Block() # Restore the stack. if foo > 0: endBlock.append(TwoOp('add', Immediate(foo), esp)) # Restore any callee saved registers we used. restoreRegs(endBlock, callee, usedColors) # Restore the %esp and %ebp registers. endBlock.append(Instruction('leave')) # Return endBlock.append(Instruction('ret')) code.append(endBlock) return code elif isinstance(node, ast.FunctionCall): code = Block() # Save any caller saved registers that are in use after this call. saveColors = toColors(node['post-alive']) saveRegs(code, caller, saveColors) addSize = 0 args = list(node.args) args.reverse() for arg in args: src = selectInstructions(arg, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) elif isinstance(src, ast.Name) or (isinstance(src, Label) and src.reference): src = '$' + str(src) code.append(OneOp('push', src)) addSize += 4 # Clear eax's tagged and tag values. eax.clear() # Make the function call. if isinstance(node.name, ast.Name): # This is the direct call case. code.append(OneOp('call', node.name, None)) else: # This is the case when we recieved the function pointer as an # argument. This can either result in a direct or an indirect # call. src = selectInstructions(node.name, cf) if isinstance(src, Mem) or src == eax: tmpColor = getTmpColor(cf, node) code.append(move(src, tmpColor)) src = tmpColor # This is the case where we need to unpack the closure. case0 = Block() case0.append(OneOp('push', src)) case0.append(OneOp('call', ast.Name('get_free_vars'), None)) case0.append(TwoOp('add', Immediate(4), esp)) case0.append(OneOp('push', eax)) case0.append(OneOp('push', src)) case0.append(OneOp('call', ast.Name('get_fun_ptr'), None)) case0.append(TwoOp('add', Immediate(4), esp)) case0.append(OneOp('call', '*' + str(eax), None)) # The case where it is a direct call (made indirectly). case1 = OneOp('call', '*' + str(src), None) code.append(buildITE(src, case0, case1, TAG_MASK, 'je', True)) #~name = '*' + str(node.name['color']) if isinstance(node.name, Symbol) else node.name #~code.append(OneOp('call', name, None)) # Restore the stack. if addSize > 0: code.append(TwoOp('add', Immediate(addSize), esp)) # Tag the result if necessary. code.append(tag(eax, node.tag)) # Move the result into the proper destination. if dest and dest != eax: code.append(move(eax, dest)) # Restore any caller saved registers that are in use. restoreRegs(code, caller, saveColors) return code elif isinstance(node, ast.If): cond = selectInstructions(node.cond, cf) then = selectInstructions(node.then, cf) els = selectInstructions(node.els, cf) thenSyms = node.then.collectSymbols('r') elseSyms = node.els.collectSymbols('r') # Insert Phi moves. for phi in node.jn: for sym in phi.srcs: if sym['color'] != phi.target['color']: (then if sym in thenSyms else els).append( move(sym['color'], phi.target['color'])) # Here we compare the conditional to False, and if it is less then or # equal to False (either False or 0) we will go to the else case. return buildITE(cond, then, els, FALS, 'jle') elif isinstance(node, ast.Integer): return Immediate(node.value) elif isinstance(node, ast.Module): code = Block("# x86\n") data = Block("# Data\n") funs = Block("\n# Functions\n") # Define our data section. for sym in node.collectSymbols(): if sym['heapify'] and sym not in node.strings.values(): data.append(Directive("globl {0}".format(sym['color']), False)) data.append(Directive('data')) data.append(Directive('align 4')) data.append(Directive("size {0}, 4".format(sym['color']))) data.append(sym['color']) data.append(Directive('long 1')) data.append(Directive('section .data')) # Add any strings we may need to define. for string in node.strings: sym = node.strings[string] data.append(sym['color']) data.append(Directive("string \"{0}\"".format(string))) data.append(Directive('text')) # Define our functions. for fun in node.functions: funs.append(selectInstructions(fun, cf)) # Concatenate our code. code.append(data) code.append(funs) return code elif isinstance(node, ast.Name): return node elif isinstance(node, ast.Return): code = Block() src = selectInstructions(node.value, cf) # Pack immediates if they haven't been packed yet. if isinstance(src, Immediate): src = pack(src, INT) elif isinstance(src, ast.Name): src = '$' + str(src) if src != eax: code.append(move(src, eax)) return code elif isinstance(node, ast.String): return node['color'] elif isinstance(node, ast.Symbol): if node.has_key('color'): return node['color'] else: raise Exception("Uncolored symbol ({0}) encountered.".format(node)) elif isinstance(node, ast.UnaryOp): code = Block() src = selectInstructions(node.operand, cf) ######### # Setup # ######### tmpColor = getTmpColor(cf, node) if isinstance(dest, Mem) else dest if src != tmpColor: code.append(move(src, tmpColor)) # Untag the argument. code.append(untag(tmpColor)) ############# # End Setup # ############# if isinstance(node, ast.Negate): code.append(OneOp('neg', tmpColor)) # Tell the cleanup code to tag the result as an integer. tmpColor.tagged = False tmpColor.tag = INT elif isinstance(node, ast.Not): case0 = move(FALS, tmpColor) case1 = move(TRU, tmpColor) code.append(buildITE(src, case0, case1, FALS, 'jle')) ########### # Cleanup # ########### # Tag the result. code.append(tag(tmpColor)) if tmpColor != dest: code.append(move(tmpColor, dest)) ############### # End Cleanup # ############### return code elif isinstance(node, ast.While): code = Block() cond = selectInstructions(node.cond, cf) condBody = selectInstructions(node.condBody, cf) body = selectInstructions(node.body, cf) # Insert Phi moves. for phi in node.jn.phis: for src in phi: if src['color'] != phi.target['color']: (code if src in node['pre-alive'] else body).append( move(src['color'], phi.target['color'])) l0 = getLabel() l1 = getLabel() # Initial jump to the cond block. code.append(OneOp('jmp', l1, None)) # The body label and the body. code.append(l0) code.append(body) # Cond label and cond body. code.append(l1) code.append(condBody) # Test to see if we should loop. code.append(TwoOp('cmp', FALS, cond)) code.append(OneOp('jg', l0, None)) return code
def append(self, inst): if (isinstance(inst, Block) and len(inst.insts) > 0) or classGuard( inst, Directive, Instruction, Label): self.insts.append(inst)