Browse Source

added functions

rlbr-dev
Larry Xue 5 years ago
parent
commit
4351d39489
  1. 98
      c2logic/compiler.py
  2. 2
      c2logic/consts.py
  3. 34
      c2logic/instructions.py

98
c2logic/compiler.py

@ -1,13 +1,20 @@
from pycparser.c_ast import Compound, Constant, DeclList, Enum, FileAST, FuncDecl, Struct, TypeDecl
from .instructions import BinaryOp, Enable, End, JumpCondition, Print, PrintFlush, Radar, RawAsm, RelativeJump, Sensor, Shoot, UnaryOp, Instruction, Set, Noop
from pycparser import c_ast, parse_file
from dataclasses import dataclass from dataclasses import dataclass
from .operations import func_unary_ops, func_binary_ops
from pycparser import c_ast, parse_file
from pycparser.c_ast import (
Compound, Constant, DeclList, Enum, FileAST, FuncDecl, Struct, TypeDecl
)
from .consts import builtins, func_binary_ops, func_unary_ops
from .instructions import (
BinaryOp, Enable, End, FunctionCall, Instruction, JumpCondition, Print, PrintFlush, Radar,
RawAsm, RelativeJump, Return, Sensor, Set, Shoot, UnaryOp
)
@dataclass @dataclass
class Function(): class Function():
name: str name: str
args: list
params: list
instructions: list instructions: list
start: int start: int
@ -40,20 +47,29 @@ class Compiler(c_ast.NodeVisitor):
ast = parse_file(filename, use_cpp=True, cpp_args=["-I", "include/"]) ast = parse_file(filename, use_cpp=True, cpp_args=["-I", "include/"])
self.visit(ast) self.visit(ast)
#TODO actually handle functions properly #TODO actually handle functions properly
out = []
offset = 0
init_call = FunctionCall("main")
preamble = [Set("__retaddr", "2"), init_call, End()]
offset = len(preamble)
#set function starts
for function in self.functions.values(): for function in self.functions.values():
function.start = offset function.start = offset
offset += len(function.instructions) offset += len(function.instructions)
#rewrite relative jumps and func calls
init_call.func_start = self.functions["main"].start
for function in self.functions.values(): for function in self.functions.values():
instructions = function.instructions instructions = function.instructions
out2 = []
for instruction in instructions: for instruction in instructions:
if isinstance(instruction, RelativeJump): if isinstance(instruction, RelativeJump):
instruction.func_start = function.start instruction.func_start = function.start
out2.append(str(instruction))
out.append("\n".join(out2))
return "\n\n".join(out)
if isinstance(instruction, FunctionCall):
instruction.func_start = self.functions[instruction.func_name].start
out = ["\n".join(map(str, preamble))]
out.extend(
"\n".join(map(str, function.instructions)) for function in self.functions.values()
)
return "\n".join(out)
#utilities #utilities
def push(self, instruction: Instruction): def push(self, instruction: Instruction):
@ -65,6 +81,9 @@ class Compiler(c_ast.NodeVisitor):
def peek(self): def peek(self):
return self.curr_function.instructions[-1] return self.curr_function.instructions[-1]
def curr_offset(self):
return len(self.curr_function.instructions) - 1
def can_avoid_indirection(self, var="__rax"): def can_avoid_indirection(self, var="__rax"):
top = self.peek() top = self.peek()
return self.opt_level >= 1 and isinstance(top, Set) and top.dest == var return self.opt_level >= 1 and isinstance(top, Set) and top.dest == var
@ -90,18 +109,24 @@ class Compiler(c_ast.NodeVisitor):
self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) self.push(RelativeJump(None, JumpCondition("==", "__rax", "0")))
def start_loop(self, cond): def start_loop(self, cond):
self.loop_start = len(self.curr_function.instructions)
self.loop_start = self.curr_offset() + 1
self.visit(cond) self.visit(cond)
self.push_body_jump() self.push_body_jump()
self.loop_end_jumps = [len(self.curr_function.instructions) - 1] # also used for breaks
self.loop_end_jumps = [self.curr_offset()] # also used for breaks
def end_loop(self): def end_loop(self):
self.push(RelativeJump(self.loop_start, JumpCondition.always)) self.push(RelativeJump(self.loop_start, JumpCondition.always))
for offset in self.loop_end_jumps: for offset in self.loop_end_jumps:
self.curr_function.instructions[offset].offset = len(self.curr_function.instructions)
self.curr_function.instructions[offset].offset = self.curr_offset() + 1
self.loop_start = None self.loop_start = None
self.loop_end_jumps = None self.loop_end_jumps = None
def push_ret(self):
if self.curr_function.name == "main":
self.push(End())
else:
self.push(Return())
def optimize_psuedofunc_args(self, args): def optimize_psuedofunc_args(self, args):
if self.opt_level >= 1: if self.opt_level >= 1:
for i, arg in reversed(list(enumerate(args))): for i, arg in reversed(list(enumerate(args))):
@ -115,23 +140,28 @@ class Compiler(c_ast.NodeVisitor):
def visit_FuncDef(self, node): # function definitions def visit_FuncDef(self, node): # function definitions
func_name = node.decl.name func_name = node.decl.name
func_decl = node.decl.type func_decl = node.decl.type
args = [arg_decl.name for arg_decl in func_decl.args.params]
params = [param_decl.name for param_decl in func_decl.args.params]
self.curr_function = Function(func_name, args, [], None)
self.curr_function = Function(func_name, params, [], None)
self.visit(node.body) self.visit(node.body)
#in case for loop is the last thing in a function to ensure the jump target is valid
if self.loop_start is not None:
self.push(Noop())
#implicit return
#needed unconditionally in case loop/if body is at end of function
self.push(Set("__rax", "null"))
self.push(Return())
self.functions[func_name] = self.curr_function self.functions[func_name] = self.curr_function
def visit_Decl(self, node): def visit_Decl(self, node):
if isinstance(node.type, TypeDecl): # variable declaration if isinstance(node.type, TypeDecl): # variable declaration
#TODO fix local/global split
if node.init is not None: if node.init is not None:
self.visit(node.init) self.visit(node.init)
self.set_to_rax(node.name) self.set_to_rax(node.name)
elif isinstance(node.type, FuncDecl): elif isinstance(node.type, FuncDecl):
#TODO actually process func declarations
pass
if node.name not in builtins + func_unary_ops + func_binary_ops:
#create placeholder function for forward declarations
self.functions[node.name] = Function(
node.name, [param_decl.name for param_decl in node.type.args.params], [], None
)
elif isinstance(node.type, Struct): elif isinstance(node.type, Struct):
if node.type.name != "MindustryObject": if node.type.name != "MindustryObject":
#TODO structs #TODO structs
@ -209,7 +239,7 @@ class Compiler(c_ast.NodeVisitor):
def visit_DoWhile(self, node): def visit_DoWhile(self, node):
#jump over the condition on the first iterattion #jump over the condition on the first iterattion
self.push(RelativeJump(None, JumpCondition.always)) self.push(RelativeJump(None, JumpCondition.always))
init_jump_offset = len(self.curr_function.instructions) - 1
init_jump_offset = self.curr_offset()
self.start_loop(node.cond) self.start_loop(node.cond)
self.curr_function.instructions[init_jump_offset].offset = len( self.curr_function.instructions[init_jump_offset].offset = len(
self.curr_function.instructions self.curr_function.instructions
@ -220,12 +250,12 @@ class Compiler(c_ast.NodeVisitor):
def visit_If(self, node): def visit_If(self, node):
self.visit(node.cond) self.visit(node.cond)
self.push_body_jump() self.push_body_jump()
cond_jump_offset = len(self.curr_function.instructions) - 1
cond_jump_offset = self.curr_offset()
self.visit(node.iftrue) self.visit(node.iftrue)
#jump over else body from end of if body #jump over else body from end of if body
if node.iffalse is not None: if node.iffalse is not None:
self.push(RelativeJump(None, JumpCondition.always)) self.push(RelativeJump(None, JumpCondition.always))
cond_jump_offset2 = len(self.curr_function.instructions) - 1
cond_jump_offset2 = self.curr_offset()
self.curr_function.instructions[cond_jump_offset].offset = len( self.curr_function.instructions[cond_jump_offset].offset = len(
self.curr_function.instructions self.curr_function.instructions
) )
@ -237,11 +267,15 @@ class Compiler(c_ast.NodeVisitor):
def visit_Break(self, node): def visit_Break(self, node):
self.push(RelativeJump(None, JumpCondition.always)) self.push(RelativeJump(None, JumpCondition.always))
self.loop_end_jumps.append(len(self.curr_function.instructions) - 1)
self.loop_end_jumps.append(self.curr_offset())
def visit_Continue(self, node): def visit_Continue(self, node):
self.push(RelativeJump(self.loop_start, JumpCondition.always)) self.push(RelativeJump(self.loop_start, JumpCondition.always))
def visit_Return(self, node):
self.visit(node.expr)
self.push(Return())
def visit_FuncCall(self, node): def visit_FuncCall(self, node):
name = node.name.name name = node.name.name
args = node.args.exprs args = node.args.exprs
@ -249,7 +283,7 @@ class Compiler(c_ast.NodeVisitor):
if name == "asm": if name == "asm":
arg = args[0] arg = args[0]
if not isinstance(arg, Constant) or arg.type != "string": if not isinstance(arg, Constant) or arg.type != "string":
raise TypeError("Non-string argument to _asm", node)
raise TypeError("Non-string argument to asm", node)
self.push(RawAsm(arg.value[1:-1])) self.push(RawAsm(arg.value[1:-1]))
elif name in ("print", "printd"): elif name in ("print", "printd"):
self.visit(args[0]) self.visit(args[0])
@ -329,7 +363,15 @@ class Compiler(c_ast.NodeVisitor):
else: else:
self.push(UnaryOp("__rax", "__rax", name)) self.push(UnaryOp("__rax", "__rax", name))
else: else:
raise NotImplementedError(node)
try:
func = self.functions[name]
except KeyError:
raise ValueError(f"{name} is not a function")
for param, arg in zip(func.params, args):
self.visit(arg)
self.set_to_rax(param)
self.push(Set("__retaddr", self.curr_offset() + 2))
self.push(FunctionCall(name))
def generic_visit(self, node): def generic_visit(self, node):
if isinstance(node, (FileAST, Compound, DeclList)): if isinstance(node, (FileAST, Compound, DeclList)):
@ -347,4 +389,4 @@ def main():
print(Compiler(args.optimization_level).compile(args.file), file=args.output) print(Compiler(args.optimization_level).compile(args.file), file=args.output)
if __name__ == "__main__": if __name__ == "__main__":
main()
main()

c2logic/operations.py → c2logic/consts.py

34
c2logic/instructions.py

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from .operations import binary_op_inverses, binary_ops, condition_ops, unary_ops
from .consts import binary_op_inverses, binary_ops, condition_ops, unary_ops
class Instruction: class Instruction:
pass pass
@ -9,7 +9,7 @@ class Noop(Instruction):
return "noop" return "noop"
class Set(Instruction): class Set(Instruction):
def __init__(self, dest, src):
def __init__(self, dest: str, src: str):
self.src = src self.src = src
self.dest = dest self.dest = dest
@ -17,7 +17,7 @@ class Set(Instruction):
return f"set {self.dest} {self.src}" return f"set {self.dest} {self.src}"
class BinaryOp(Instruction): class BinaryOp(Instruction):
def __init__(self, dest, left, right, op):
def __init__(self, dest: str, left: str, right: str, op: str):
self.left = left self.left = left
self.right = right self.right = right
self.op = op self.op = op
@ -30,7 +30,7 @@ class BinaryOp(Instruction):
return f"op {binary_ops[self.op]} {self.dest} {self.left} {self.right}" return f"op {binary_ops[self.op]} {self.dest} {self.left} {self.right}"
class UnaryOp(Instruction): class UnaryOp(Instruction):
def __init__(self, dest, src, op):
def __init__(self, dest: str, src: str, op: str):
self.src = src self.src = src
self.dest = dest self.dest = dest
self.op = op self.op = op
@ -62,22 +62,36 @@ class RelativeJump(Instruction):
def __str__(self): def __str__(self):
return f"jump {self.func_start+self.offset} {self.cond}" return f"jump {self.func_start+self.offset} {self.cond}"
class FunctionCall(Instruction):
def __init__(self, func_name: str):
self.func_name = func_name
self.func_start: int = None
def __str__(self):
return f"jump {self.func_start} {JumpCondition.always}"
class Return(Instruction):
def __str__(self):
return "set @counter __retaddr"
class Print(Instruction): class Print(Instruction):
def __init__(self, val):
def __init__(self, val: str):
self.val = val self.val = val
def __str__(self): def __str__(self):
return f"print {self.val}" return f"print {self.val}"
class PrintFlush(Instruction): class PrintFlush(Instruction):
def __init__(self, val):
def __init__(self, val: str):
self.val = val self.val = val
def __str__(self): def __str__(self):
return f"printflush {self.val}" return f"printflush {self.val}"
class Radar(Instruction): class Radar(Instruction):
def __init__(self, dest, src, target1, target2, target3, sort, index):
def __init__(
self, dest: str, src: str, target1: str, target2: str, target3: str, sort: str, index: str
):
self.src = src self.src = src
self.dest = dest self.dest = dest
self.target1 = target1 self.target1 = target1
@ -90,7 +104,7 @@ class Radar(Instruction):
return f"radar {self.target1} {self.target2} {self.target3} {self.sort} {self.src} {self.index} {self.dest}" return f"radar {self.target1} {self.target2} {self.target3} {self.sort} {self.src} {self.index} {self.dest}"
class Sensor(Instruction): class Sensor(Instruction):
def __init__(self, dest, src, prop):
def __init__(self, dest: str, src: str, prop: str):
self.dest = dest self.dest = dest
self.src = src self.src = src
self.prop = prop self.prop = prop
@ -99,7 +113,7 @@ class Sensor(Instruction):
return f"sensor {self.dest} {self.src} @{self.prop}" return f"sensor {self.dest} {self.src} @{self.prop}"
class Enable(Instruction): class Enable(Instruction):
def __init__(self, obj, enabled):
def __init__(self, obj: str, enabled: str):
self.obj = obj self.obj = obj
self.enabled = enabled self.enabled = enabled
@ -107,7 +121,7 @@ class Enable(Instruction):
return f"control enabled {self.obj} {self.enabled} 0 0 0" return f"control enabled {self.obj} {self.enabled} 0 0 0"
class Shoot(Instruction): class Shoot(Instruction):
def __init__(self, obj, x, y, shoot):
def __init__(self, obj: str, x: str, y: str, shoot: str):
self.obj = obj self.obj = obj
self.x = x self.x = x
self.y = y self.y = y

Loading…
Cancel
Save