Browse Source

optimized loops/if and added continue

rlbr-dev
Larry Xue 5 years ago
parent
commit
90857c9393
  1. 6
      README.md
  2. 77
      c2logic/compiler.py
  3. 7
      c2logic/instructions.py
  4. 2
      c2logic/operations.py
  5. 5
      examples/control_flow.c
  6. 2
      include/mindustry.h
  7. 1
      requirements.txt
  8. 2
      setup.py

6
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

77
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())

7
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):

2
c2logic/operations.py

@ -27,3 +27,5 @@ condition_ops = {
}
unary_ops = {"-": "negate", "~": "not"}
binary_op_inverses = {"==": "!=", "!=": "==", "<": ">=", "<=": ">", ">": "<=", ">=": "<"}

5
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) {

2
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);

1
requirements.txt

@ -0,0 +1 @@
pycparser~=2.20

2
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"]
)
Loading…
Cancel
Save