def FramePush(self, param_count, scope_depth): p = param_count code = [ # 0(ebp) == return address x.movl(x.ebp, x.mem(x.esp, -(1+p)*4)), # 1 x.movl(x.esp, x.ebp), x.movl(x.ebx, x.mem(x.ebp, -(2+p)*4)), # 2 x.movl(x.edi, x.mem(x.ebp, -(3+p)*4)), # 3 x.movl(x.esi, x.mem(x.ebp, -(4+p)*4)), # 4 x.movl(x.ecx, x.mem(x.ebp, -(5+p)*4)), # 5 x.movl(x.edx, x.mem(x.ebp, -(6+p)*4)), # 6 x.movl(x.cint(4*(scope_depth-1)), x.ebx), x.movl(x.static('display', x.ebx), x.eax), x.movl(x.eax, x.mem(x.ebp, -(7+p)*4)), #7 #x.subl(x.cint(8*4), x.esp), #x.movl(x.cint(4*(scope_depth-1)), x.ebx), #x.pushl(x.static('display', x.ebx)), #x.pushl('$push_msg'), #x.call("printf"), #x.addl(x.cint(8*4 + 8), x.esp), x.movl(x.cint(4*(scope_depth-1)), x.ebx), x.movl(x.ebp, x.static('display', x.ebx)), '', ] return code
def FramePush(self, param_count, scope_depth): """Generate a framepush. @param param_count : the number of params the function recieves. @param scope_depth : the scope the depth the function was declared at. """ p = param_count code = [ # 0(ebp) == return address x.movl(x.ebp, x.mem(x.esp, -(1 + p) * 4)), # 1 x.movl(x.esp, x.ebp), x.movl(x.ebx, x.mem(x.ebp, -(2 + p) * 4)), # 2 x.movl(x.edi, x.mem(x.ebp, -(3 + p) * 4)), # 3 x.movl(x.esi, x.mem(x.ebp, -(4 + p) * 4)), # 4 x.movl(x.ecx, x.mem(x.ebp, -(5 + p) * 4)), # 5 x.movl(x.edx, x.mem(x.ebp, -(6 + p) * 4)), # 6 x.movl(x.cint(4 * (scope_depth - 1)), x.ebx), x.movl(x.static("display", x.ebx), x.eax), x.movl(x.eax, x.mem(x.ebp, -(7 + p) * 4)), # 7 # x.subl(x.cint(8*4), x.esp), # x.movl(x.cint(4*(scope_depth-1)), x.ebx), # x.pushl(x.static('display', x.ebx)), # x.pushl('$push_msg'), # x.call("printf"), # x.addl(x.cint(8*4 + 8), x.esp), x.movl(x.cint(4 * (scope_depth - 1)), x.ebx), x.movl(x.ebp, x.static("display", x.ebx)), "", ] return code
def FramePop(self, param_count, scope_depth): """Generate a framepop. @param param_count : the number of params the function recieves. @param scope_depth : the scope the depth the function was declared at. """ p = param_count code = [ "", # x.movl(x.cint(4*(scope_depth-1)), x.ebx), # x.pushl(x.static('display', x.ebx)), # x.pushl('$pop1_msg'), # x.call("printf"), # x.addl(x.cint(8), x.esp), x.movl(x.cint(4 * (scope_depth - 1)), x.ebx), x.movl(x.mem(x.ebp, -(7 + p) * 4), x.eax), x.movl(x.eax, x.static("display", x.ebx)), # x.movl(x.cint(4*(scope_depth-1)), x.ebx), # x.pushl(x.static('display', x.ebx)), # x.pushl('$pop2_msg'), # x.call("printf"), # x.addl(x.cint(8), x.esp), x.movl(x.mem(x.ebp, -(6 + p) * 4), x.edx), x.movl(x.mem(x.ebp, -(5 + p) * 4), x.ecx), x.movl(x.mem(x.ebp, -(4 + p) * 4), x.esi), x.movl(x.mem(x.ebp, -(3 + p) * 4), x.edi), x.movl(x.mem(x.ebp, -(2 + p) * 4), x.ebx), x.movl(x.ebp, x.esp), # restore stack pointer x.movl(x.mem(x.esp, -(1 + p) * 4), x.ebp), # restore base(frame) pointer ] return code
def FramePop(self, param_count, scope_depth): p = param_count code = [ '', #x.movl(x.cint(4*(scope_depth-1)), x.ebx), #x.pushl(x.static('display', x.ebx)), #x.pushl('$pop1_msg'), #x.call("printf"), #x.addl(x.cint(8), x.esp), x.movl(x.cint(4*(scope_depth-1)), x.ebx), x.movl(x.mem(x.ebp, -(7+p)*4), x.eax), x.movl(x.eax, x.static('display', x.ebx)), #x.movl(x.cint(4*(scope_depth-1)), x.ebx), #x.pushl(x.static('display', x.ebx)), #x.pushl('$pop2_msg'), #x.call("printf"), #x.addl(x.cint(8), x.esp), x.movl(x.mem(x.ebp, -(6+p)*4), x.edx), x.movl(x.mem(x.ebp, -(5+p)*4), x.ecx), x.movl(x.mem(x.ebp, -(4+p)*4), x.esi), x.movl(x.mem(x.ebp, -(3+p)*4), x.edi), x.movl(x.mem(x.ebp, -(2+p)*4), x.ebx), x.movl(x.ebp, x.esp), # restore stack pointer x.movl(x.mem(x.esp, -(1+p)*4), x.ebp), # restore base(frame) pointer ] return code
def Print(self, i): code = self.emit(x.pushl, i.a) code += [ x.pushl('$printf_msg'), x.call("printf"), x.addl(x.cint(8), x.esp), ] return code
def emit(self, inst, src, targ=None): '''emit the specified instruction with src as source operand and targ as the target operand. Handles requesting the operands from a non-local stack frame as necessary. using ebx or ecx is not allowed for either the src or targ. ''' #print inst, src, targ if isinstance(src, il.Symbol) and isinstance(targ, il.Symbol): raise Exception, 'Cannot emit an instruction with both source and target as symbols' elif isinstance(src, il.Symbol) and targ is None: if src.islocal(self.cfunc): return [ inst(x.loc(src.type)) ] return [ x.movl(x.cint(4*(src.scope_depth-1)), x.ebx), x.movl(x.static('display', x.ebx), x.ebx), inst(x.mem(x.ebx, src.type.offset)), ] elif not isinstance(src, il.Symbol) and targ is None: return [ inst(src), ] elif isinstance(src, il.Symbol) and targ is not None: if src.islocal(self.cfunc): return [ inst(x.loc(src.type), targ) ] return [ x.movl(x.cint(4*(src.scope_depth-1)), x.ebx), x.movl(x.static('display', x.ebx), x.ebx), inst(x.mem(x.ebx, src.type.offset), targ), ] elif isinstance(targ, il.Symbol): #print inst, src, targ, targ.type.basereg if targ.islocal(self.cfunc): return [ inst(src, x.loc(targ.type)) ] return [ x.movl(x.cint(4*(targ.scope_depth-1)), x.ebx), x.movl(x.static('display', x.ebx), x.ebx), x.movl(x.mem(x.ebx, targ.type.offset), x.ecx), inst(src, x.ecx), x.movl(x.ecx, x.mem(x.ebx, targ.type.offset)), ] else: return [ inst(src, targ) ]
def Op(self, i): # print i ops = {il.ADD: x.addl, il.SUB: x.subl, il.MUL: x.imull} code = [] if i.op == il.DIV: code += [x.movl(x.cint(0), x.edx)] code += self.emit(x.movl, i.a, x.eax) code += self.emit(x.divl, i.b) code += self.emit(x.movl, x.eax, i.result) else: code += self.emit(x.movl, i.a, x.eax) code += self.emit(ops[i.op], i.b, x.eax) code += self.emit(x.movl, x.eax, i.result) return code
def Return(self, i): code = list() #if len(self.code) >= 90 and len(self.code) < 110: #code = [ #(vm.IMM, 3, len(self.code), 'DEBUG'), #(vm.PRNT, 3, 0, 'DEBUG'), #(vm.PRNT, 2, 0, 'DEBUG'), #(vm.EXIT, 0, 0, 'DEBUG'), #] code += [ x.addl(x.cint(4), x.esp), x.jmp(x.mem(x.esp, -4, True)), #x.ret(), ] return code
def emit(self, inst, src, targ=None): """emit the specified instruction with src as source operand and targ as the target operand. Handles requesting the operands from a non-local stack frame as necessary. using ebx or ecx is not allowed for either the src or targ. """ ## We have several choices to make in order to emit the right inst. ## (1) does the inst only refer to symbols local to the current scope? ## (a) if yes: emit the instruction with no changes ## (b) if no: detirmine what changes have to be made. ## (2) We now know the instruction is not local. One (but not both) of ## the symbols does not come from this stack frame. That means they ## have to be fetched from the appropriate stack frame. We detirmine ## the appropriate frame to fetch (or put) the sym from(to) by ## consulting the display. ## (3) The display is a static array: ## ## +------------+------------+------------+------------+ ## scope depth | 0 | 1 | 2 | 3 | ## +------------+------------+------------+------------+ ## frame address | 0x???????? | 0x???????? | 0x???????? | 0x???????? | ## +------------+------------+------------+------------+ ## ## The 'frame address' contains the address of the the stack frame ## most recently seen function. When that function returns it will ## update the display with the previous address it contained. When ## A new function is called at that scope depth a new address will ## be placed in the display and the old will be saved. ## ## (4) Therefore to get the non-local symbols we simply need to consult ## the display to find the appropriate frame. Each symbol knows what ## scope-depth it was declared at. ## ## NB: The symbols store there scope depths starting at 1. Therefore, in ## order to index into the display one must subtract 1 from the ## stored scope depth. ## ## NB: We multiply by 4 because each address is 4 bytes wide. if isinstance(src, il.Symbol) and isinstance(targ, il.Symbol): raise Exception, ("Cannot emit an instruction with both source and target as " "symbols") elif isinstance(src, il.Symbol) and targ is None: if src.islocal(self.cfunc): return [inst(x.loc(src.type))] return [ x.movl(x.cint(4 * (src.scope_depth - 1)), x.ebx), # compute the # index into the # display and # store in ebx x.movl(x.static("display", x.ebx), x.ebx), # get the non- # local stack # pointer and # store in ebx inst(x.mem(x.ebx, src.type.offset)), # emit the inst. ] elif not isinstance(src, il.Symbol) and targ is None: return [inst(src)] elif isinstance(src, il.Symbol) and targ is not None: if src.islocal(self.cfunc): return [inst(x.loc(src.type), targ)] return [ x.movl(x.cint(4 * (src.scope_depth - 1)), x.ebx), x.movl(x.static("display", x.ebx), x.ebx), inst(x.mem(x.ebx, src.type.offset), targ), ] elif isinstance(targ, il.Symbol): # print inst, src, targ, targ.type.basereg if targ.islocal(self.cfunc): return [inst(src, x.loc(targ.type))] return [ ## This version is slightly tricky we have to fetch and store ## the contents of the variable stored in the non-local stack ## frame. x.movl(x.cint(4 * (targ.scope_depth - 1)), x.ebx), x.movl(x.static("display", x.ebx), x.ebx), x.movl(x.mem(x.ebx, targ.type.offset), x.ecx), inst(src, x.ecx), x.movl(x.ecx, x.mem(x.ebx, targ.type.offset)), ] else: return [inst(src, targ)]
def Imm(self, i): code = self.emit(x.movl, x.cint(i.a), i.result) return code
def Return(self, i): """Generate code to return from the current function""" return [x.addl(x.cint(4), x.esp), x.jmp(x.mem(x.esp, -4, True))]
def Func(self, name, main=False): """Generates x86 code for the function given by name. This is the entry point for all code generation.""" ## Get the function. func = self.functions[name] self.cfunc = func ## Generate a label for the function self.code += [x.label(name)] ## If this is not the `main` function generate a stack push if not main: self.code += self.FramePush(len(func.params), func.scope_depth) ## Move the stack pointer down. This creates the stack frame. syms = self.gather_syms(func.blks) sp_offset = self.place_symbols(syms, func) self.code += [x.subl(x.cint(sp_offset * 4), x.esp)] def block(insts): """Generate the x86 instruction for the given block of instructions. """ ## We could do something more clever than switch case here, but I ## (for now) perfer the simplicity. for i in insts: if i.op == il.PRNT: self.code += self.Print(i) elif i.op == il.IMM: self.code += self.Imm(i) elif i.op == il.GPRM: self.code += self.Gprm(i) elif i.op == il.IPRM: self.code += self.Iprm(i) elif i.op == il.OPRM: ## OPRMs always appear directly before RTRN instructions. ## Based on the way the stack frame pop is generated the pop ## must be generated before the OPRM instruction. So the ## output params can be properly returned. if not main and func.oparam_count > 0: self.code += self.FramePop(len(func.params), func.scope_depth) if func.oparam_count == 0: raise Exception, "expected no return paramters got at least 1." self.code += self.Oprm(i) elif i.op == il.RPRM: self.code += self.Rprm(i) elif i.op == il.CALL: self.code += self.Call(i) elif i.op == il.RTRN: if not main and func.oparam_count == 0: self.code += self.FramePop(len(func.params), func.scope_depth) self.code += self.Return(i) elif i.op == il.MV: self.code += self.Mv(i) elif i.op in [il.ADD, il.SUB, il.MUL, il.DIV]: self.code += self.Op(i) elif i.op in [il.IFEQ, il.IFNE, il.IFLT, il.IFLE, il.IFGT, il.IFGE]: self.code += self.BranchOp(i) elif i.op == il.J: self.code += self.J(i) elif i.op == il.NOP: self.code += self.Nop(i) else: raise Exception, il.opsr[i.op] ## generate a label for the entry block of the function. self.code += [x.label(func.entry.name)] block(self.blocks[func.entry.name].insts) # generate the function. ## for each block in the function (excluding entry and exit) generate ## the code. for b in func.blks: if b.name == func.entry.name: continue if b.name == func.exit.name: continue self.code += [x.label(b.name)] self.code += [x.nop()] block(self.blocks[b.name].insts) ## If there a distinct exit block, generate it. if func.entry.name != func.exit.name: self.code += [x.label(func.exit.name)] b = func.exit self.code += [x.nop()] block(self.blocks[b.name].insts)
def Func(self, name, main=False): #print name self.code += [ x.label(name) ] #print '->', insts func = self.functions[name] self.cfunc = func #insts = self.blocks[func.entry.name].insts self.bp_offset = 3 if not main: self.code += self.FramePush(len(func.params), func.scope_depth) syms = self.gather_syms(func.blks) #print syms fp_offset = self.place_symbols(syms, func) #print name, 'fp_offset', fp_offset self.code += [ x.subl(x.cint(fp_offset*4), x.esp) #(vm.IMM, 4, fp_offset, 'start func %s' % (name)), #(vm.ADD, 1, 4, 'fp offset add inst'), ] def block(insts): for i in insts: if i.op == il.PRNT: self.code += self.Print(i) elif i.op == il.IMM: self.code += self.Imm(i) elif i.op == il.GPRM: self.code += self.Gprm(i) elif i.op == il.IPRM: self.code += self.Iprm(i) elif i.op == il.OPRM: if not main and func.oparam_count > 0: self.code += self.FramePop(len(func.params), func.scope_depth) if func.oparam_count == 0: raise Exception, "expected no return paramters got at least 1." self.code += self.Oprm(i) elif i.op == il.RPRM: self.code += self.Rprm(i) elif i.op == il.CALL: self.code += self.Call(i) elif i.op == il.RTRN: if not main and func.oparam_count == 0: self.code += self.FramePop(len(func.params), func.scope_depth) self.code += self.Return(i) elif i.op == il.MV: self.code += self.Mv(i) elif i.op in [il.ADD, il.SUB, il.MUL, il.DIV]: self.code += self.Op(i) elif i.op in [il.IFEQ, il.IFNE, il.IFLT, il.IFLE, il.IFGT, il.IFGE]: self.code += self.BranchOp(i) elif i.op == il.J: self.code += self.J(i) elif i.op == il.NOP: self.code += self.Nop(i) else: raise Exception, il.opsr[i.op] #if i.label is not None: #print code[l] #code[l] = (code[l][0], code[l][1], code[l][2], i.label) self.code += [ x.label(func.entry.name) ] block(self.blocks[func.entry.name].insts) for b in func.blks: if b.name == func.entry.name: continue if b.name == func.exit.name: continue self.code += [ x.label(b.name) ] self.floc[b.name] = len(self.code) self.code += [ x.nop() ] block(self.blocks[b.name].insts) if func.entry.name != func.exit.name: self.code += [ x.label(func.exit.name) ] b = func.exit self.floc[b.name] = len(self.code) self.code += [ x.nop() ] block(self.blocks[b.name].insts)