Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 238 lines (177 sloc) 5.661 kb
#!/usr/bin/env python
import argparse
import sys
import pyc_ast
import pyc_asm_list
import pyc_var_analyzer
import pyc_reg_allocator
import pyc_asm_nodes
import compiler
from pyc_log import *
import os.path
import time
g_phases = ["ss-list", "asm-node-list"]
def opt_parser():
global g_phases
parser = argparse.ArgumentParser(description='compile python to x86 assembly.')
parser.add_argument('file', nargs='?', type=argparse.FileType('r'), help='file to compile')
parser.add_argument('-c', '--code', help='code to compile')
parser.add_argument('-o', '--output', help='output file.')
parser.add_argument('-v', '--verbose', action='store_true', help='print debug output.')
parser.add_argument('-p', '--phase', help=('stop and print intermediate compiler result. phases: %s' % ", ".join(g_phases)) )
return parser
class MissingOption(Exception):
pass
def validate(options):
if options.file == None and options.code == None:
raise MissingOption('specify either file or code to compile')
def run(options):
global g_phases
#print options
validate(options)
if options.verbose == True:
log_set_verbose()
else:
log_set_quiet()
options.src_type = 'cmdline'
options.src = options.code
if options.src == None:
options.src = options.file.read()
options.src_type = 'file'
if isinstance(options.src, basestring) != True:
raise Exception('invalid src variable. type: %s' % (options.src.__class__.__name__) )
if (not options.phase is None) and options.phase not in g_phases:
raise Exception('invalid phase variable: %s' % options.phase)
log(options)
#import parser only now because we want to know if
#we are verbose or not first.
import pyc_parser
as_tree = pyc_parser.parse(options.src)
log( lambda : repr(as_tree))
log( lambda : pyc_ast.str(as_tree))
t0 = time.time()
ss_list = pyc_ast.to_ss_list(as_tree)
log("se list: ")
ss_list_str = lambda : "\n".join([repr(ss) for ss in ss_list])
if options.phase == 'ss-list':
print ss_list_str()
exit(0)
log(ss_list_str)
asm_list = pyc_asm_list.from_ss_list(ss_list)
asm_list = reduce(lambda a,b: a + b, asm_list, [])
asm_list_str = lambda : "\n".join([repr(n) for n in asm_list])
if options.phase == 'asm-node-list':
print asm_list_str()
exit(0)
log(asm_list_str)
t_flatten = time.time()
#print "flatten time: %d" % (t_flatten - t0)
more_alloc_needed = 1
adjusted_asm_list = asm_list
allocator = pyc_reg_allocator.Allocator()
alloc_timeout = 20
while more_alloc_needed:
log("analyze asm nodes and assign memory locations")
t0 = time.time()
live_list, graph = pyc_var_analyzer.interference_graph(adjusted_asm_list)
t_graph = time.time()
#print "graph time: %d" % (t_graph - t0)
t0 = time.time()
allocator.allocate(graph, alloc_timeout)
t_alloc = time.time()
#print "alloc time: %d" % (t_alloc - t0)
#we only want to restrict the time of the first pass
#because the subsequent passes may have vars which need
#registers
alloc_timeout = 0
log( lambda : "mem allocation offsets:\n\t%s" % str(allocator.symtbl) )
(more_alloc_needed, adjusted_asm_list) = pyc_reg_allocator.adjust(adjusted_asm_list, allocator.symtbl)
log( lambda : "adjusted asm list (more_alloc? = %d):\n\t%s" % (more_alloc_needed, "\n\t".join([("%s" % repr(x) ) for x in adjusted_asm_list])) )
insns = []
insns.extend(asm_prefix())
stacksize = allocator.symtbl.stack()
align = 16
if stacksize > 0:
insns.append("subl\t$%s, %%esp" % (stacksize + (align - (stacksize % align))) )
insns.extend(format_insn_nodes(adjusted_asm_list, ss_list, allocator.symtbl) )
insns.extend(asm_suffix())
output(options, insns)
def format_insn_nodes(ins_nodes, ss_list, symtbl):
result = []
previous = None
f = open("/tmp/asdf", "w")
#for ss in ss_list:
# if isinstance(ss, compiler.ast.Assign):
# exec(pyc_ast.assign_to_py(ss))
for ins in ins_nodes:
if not isinstance(ins, pyc_asm_nodes.Inst):
raise Exception("expected instruction node")
patched = pyc_reg_allocator.patch_insn(ins, symtbl)
if patched.is_noop():
continue
s = str(patched)
if previous is None or previous.origin != ins.origin:
val = ""
#if isinstance(ins.origin, compiler.ast.Assign):
# x = eval(ins.origin.nodes[0].name)
# val = "(%s = %s) " % (ins.origin.nodes[0].name, x)
# f.write("%s\n" % x)
s = "%-20s #%s%s" % (s, val, repr(ins.origin))
if False: #(not previous is None) and isinstance(previous.origin, compiler.ast.Assign):
result.extend([
"push %ecx",
"push %edx",
"push %eax",
"push %s" % pyc_reg_allocator.index_to_loc(symtbl[previous.origin.nodes[0].name]),
"call print_int_nl",
"pop %eax",
"pop %eax",
"pop %edx",
"pop %ecx"
])
previous = ins
result.append(s)
f.close()
return result
def asm_headers():
return [
".text",
".globl main",
".type main, @function",
"main:"
]
def asm_prefix():
return [
"pushl %ebp",
"movl %esp, %ebp"
]
def asm_suffix():
return [
"movl $0, %eax",
"leave",
"ret"
]
def output(options, insns):
if options.src_type == 'cmdline' or options.output == '-':
for hdr in asm_headers():
print hdr
for ins in insns:
print "\t" + ins
else:
output_fname = "%s.s" % os.path.splitext(options.file.name)[0]
ofile = open(output_fname, 'w')
for hdr in asm_headers():
ofile.write(hdr + "\n")
for ins in insns:
ofile.write("\t" + ins + "\n")
ofile.close()
if __name__ == "__main__":
opt_p = opt_parser()
options = opt_p.parse_args()
try:
run(options)
except MissingOption as e:
print e
print
opt_p.print_help()
exit(-1)
Jump to Line
Something went wrong with that request. Please try again.