From 90857c939338eebcd4a088241c74b6b951eb907b Mon Sep 17 00:00:00 2001 From: Larry Xue Date: Sun, 23 Aug 2020 15:34:16 -0400 Subject: [PATCH] optimized loops/if and added continue --- README.md | 6 ++-- c2logic/compiler.py | 77 ++++++++++++++++++++--------------------- c2logic/instructions.py | 7 ++-- c2logic/operations.py | 2 ++ examples/control_flow.c | 5 ++- include/mindustry.h | 2 +- requirements.txt | 1 + setup.py | 2 +- 8 files changed, 54 insertions(+), 48 deletions(-) create mode 100644 requirements.txt diff --git a/README.md b/README.md index 285c0c4..f2adf18 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,13 @@ Compiles C code to Mindustry logic. Still in beta, so compiled output may not be `python3 -m c2logic filename -O optimization_level` -where `filename` is a string and `optimization_level` is an int +where `filename` is a string and `optimization_level` is an integer. See [examples](./examples) for API sample usage. # Documentation -TODO - -See `include/mindustry.h`. +See `include/mindustry.h` for API definitions. # Unsupported Features diff --git a/c2logic/compiler.py b/c2logic/compiler.py index 74e935c..8c7333d 100644 --- a/c2logic/compiler.py +++ b/c2logic/compiler.py @@ -25,6 +25,11 @@ class Compiler(c_ast.NodeVisitor): """ def __init__(self, opt_level=0): self.opt_level = opt_level + self.functions = [] + self.curr_function: Function = None + self.loop_start: int = None + self.loop_end_jumps:list = None + #self.cond_jump_offset: int = None def compile(self, filename: str): self.functions = [] @@ -73,6 +78,25 @@ class Compiler(c_ast.NodeVisitor): else: self.push(Set(varname, "__rax")) + def push_body_jump(self): + # jump over loop/if body when cond is false + if self.opt_level >= 1 and isinstance(self.peek(), BinaryOp): + try: + self.push(RelativeJump(None, JumpCondition.from_binaryop(self.pop().inverse()))) + except KeyError: + self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) + else: + self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) + + def optimize_psuedofunc_args(self, args): + if self.opt_level >= 1: + for i, arg in reversed(list(enumerate(args))): + if self.can_avoid_indirection(arg): + args[i] = self.pop().src + else: + break + return args + #visitors def visit_FuncDef(self, node): # function definitions func_decl = node.decl.type @@ -148,47 +172,29 @@ class Compiler(c_ast.NodeVisitor): self.visit(node.init) self.loop_start = len(self.curr_function.instructions) self.visit(node.cond) - # jump over loop body when cond is false - if self.opt_level >= 1 and isinstance(self.peek(), BinaryOp): - #TODO fix this - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) - #self.push(RelativeJump(None, JumpCondition.from_binaryop(self.pop()))) - else: - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) - self.cond_jump_offset = len(self.curr_function.instructions) - 1 - self.visit(node.stmt) + self.push_body_jump() + self.loop_end_jumps = [len(self.curr_function.instructions) - 1] # also used for breaks + self.visit(node.stmt) # loop body self.visit(node.next) #jump to start of loop - self.push(RelativeJump(self.loop_start, JumpCondition("==", "0", "0"))) - self.curr_function.instructions[self.cond_jump_offset].offset = len( - self.curr_function.instructions - ) + self.push(RelativeJump(self.loop_start, JumpCondition.always)) + for offset in self.loop_end_jumps: + self.curr_function.instructions[offset].offset = len(self.curr_function.instructions) def visit_While(self, node): self.loop_start = len(self.curr_function.instructions) self.visit(node.cond) - # jump over loop body when cond is false - if self.opt_level >= 1 and isinstance(self.peek(), BinaryOp): - #TODO fix this - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) - #self.push(RelativeJump(None, JumpCondition.from_binaryop(self.pop()))) - else: - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) + self.push_body_jump() self.loop_end_jumps = [len(self.curr_function.instructions) - 1] # also used for breaks self.visit(node.stmt) #jump to start of loop - self.push(RelativeJump(self.loop_start, JumpCondition("==", "0", "0"))) + self.push(RelativeJump(self.loop_start, JumpCondition.always)) for offset in self.loop_end_jumps: self.curr_function.instructions[offset].offset = len(self.curr_function.instructions) def visit_If(self, node): self.visit(node.cond) - # jump over if body when cond is false - #TODO optimize for when cond is a binary operation - if self.opt_level >= 1 and isinstance(self.peek(), BinaryOp): - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) - else: - self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) + self.push_body_jump() cond_jump_offset = len(self.curr_function.instructions) - 1 self.visit(node.iftrue) #jump over else body from end of if body @@ -208,6 +214,9 @@ class Compiler(c_ast.NodeVisitor): self.push(RelativeJump(None, JumpCondition.always)) self.loop_end_jumps.append(len(self.curr_function.instructions) - 1) + def visit_Continue(self, node): + self.push(RelativeJump(self.loop_start, JumpCondition.always)) + def visit_FuncCall(self, node): name = node.name.name #TODO avoid duplication in psuedo-function calls @@ -239,12 +248,7 @@ class Compiler(c_ast.NodeVisitor): self.visit(arg) self.set_to_rax(f"__radar_arg{i}") args.append(f"__radar_arg{i}") - if self.opt_level >= 1: - for i, arg in reversed(list(enumerate(args))): - if self.can_avoid_indirection(arg): - args[i] = self.pop().src - else: - break + args = self.optimize_psuedofunc_args(args) self.push(Radar("__rax", *args)) #pylint: disable=no-value-for-parameter elif name == "sensor": self.visit(node.args.exprs[0]) @@ -277,12 +281,7 @@ class Compiler(c_ast.NodeVisitor): self.visit(arg) self.set_to_rax(f"__shoot_arg{i}") args.append(f"__shoot_arg{i}") - if self.opt_level >= 1: - for i, arg in reversed(list(enumerate(args))): - if self.can_avoid_indirection(arg): - args[i] = self.pop().src - else: - break + args = self.optimize_psuedofunc_args(args) self.push(Shoot(*args)) #pylint: disable=no-value-for-parameter elif name == "end": self.push(End()) diff --git a/c2logic/instructions.py b/c2logic/instructions.py index 9e5bf24..1b787bd 100644 --- a/c2logic/instructions.py +++ b/c2logic/instructions.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from .operations import binary_ops, condition_ops, unary_ops +from .operations import binary_op_inverses, binary_ops, condition_ops, unary_ops class Instruction: pass @@ -23,6 +23,9 @@ class BinaryOp(Instruction): self.op = op self.dest = dest + def inverse(self): + return BinaryOp(self.dest, self.left, self.right, binary_op_inverses[self.op]) + def __str__(self): return f"bop {binary_ops[self.op]} {self.left} {self.right} {self.dest}" @@ -53,7 +56,7 @@ JumpCondition.always = JumpCondition("==", "0", "0") class RelativeJump(Instruction): def __init__(self, offset: int, cond: JumpCondition): self.offset = offset - self.func_start = None + self.func_start: int = None self.cond = cond def __str__(self): diff --git a/c2logic/operations.py b/c2logic/operations.py index c3914d4..45514cf 100644 --- a/c2logic/operations.py +++ b/c2logic/operations.py @@ -27,3 +27,5 @@ condition_ops = { } unary_ops = {"-": "negate", "~": "not"} + +binary_op_inverses = {"==": "!=", "!=": "==", "<": ">=", "<=": ">", ">": "<=", ">=": "<"} diff --git a/examples/control_flow.c b/examples/control_flow.c index c221017..0e2eec2 100644 --- a/examples/control_flow.c +++ b/examples/control_flow.c @@ -1,8 +1,11 @@ #include "../include/mindustry.h" extern struct MindustryObject message1; void main(void) { - double i = 0; + int i = 0; while (i < 10) { + if (i % 2 == 1) { + continue; + } printd(i); i++; if (i == 4) { diff --git a/include/mindustry.h b/include/mindustry.h index fa80d30..c980e48 100644 --- a/include/mindustry.h +++ b/include/mindustry.h @@ -4,7 +4,7 @@ struct MindustryObject {}; void _asm(char* code); void print(char* s); void printd(double s); -void printflush(struct MindustryObject); +void printflush(struct MindustryObject msg_block); struct MindustryObject radar(struct MindustryObject obj, char* target1, char* target2, char* target3, char* sort, double index); diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..93a0b18 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pycparser~=2.20 \ No newline at end of file diff --git a/setup.py b/setup.py index 107b8c6..cd3d3c3 100755 --- a/setup.py +++ b/setup.py @@ -13,5 +13,5 @@ setuptools.setup( url="https://github.com/SuperStormer/c2logic", project_urls={"Source Code": "https://github.com/SuperStormer/c2logic"}, entry_points={"console_scripts": ["c2logic=c2logic:main"]}, - install_requires=["pycparser>=2.20"] + install_requires=["pycparser~=2.20"] ) \ No newline at end of file