Browse Source

fixed bug with loop cond jump

rlbr-dev
Larry Xue 5 years ago
parent
commit
f8adc5919b
  1. 20
      README.md
  2. 54
      c2logic/compiler.py
  3. 14
      examples/control_flow.c
  4. 2
      setup.py

20
README.md

@ -9,3 +9,23 @@ Compiles C code to Mindustry logic.
where `filename` is a string and `optimization_level` is an int where `filename` is a string and `optimization_level` is an int
See [examples](./examples) for API sample usage. See [examples](./examples) for API sample usage.
# Documentation
TODO
See `include/mindustry.h`.
# Unsupported Features
- drawing
- getlink
- memory cell read/write
- do-while
- continue
- goto
- actual functions
- structs
- enums
Some of these features may be worked around using `_asm()`.

54
c2logic/compiler.py

@ -1,14 +1,7 @@
from pycparser.c_ast import Compound, Constant, DeclList, Enum, FileAST, FuncDecl, Struct, TypeDecl from pycparser.c_ast import Compound, Constant, DeclList, Enum, FileAST, FuncDecl, Struct, TypeDecl
from .instructions import BinaryOp, Enable, JumpCondition, Print, PrintFlush, Radar, RawAsm, RelativeJump, Sensor, Shoot, UnaryOp, Instruction, Set, Noop from .instructions import BinaryOp, Enable, JumpCondition, Print, PrintFlush, Radar, RawAsm, RelativeJump, Sensor, Shoot, UnaryOp, Instruction, Set, Noop
from pycparser import c_parser, c_ast, parse_file
from pycparser import c_ast, parse_file
from dataclasses import dataclass from dataclasses import dataclass
"""
@dataclass
class LoopState():
start: int
end: int
cond_jump_offset: int
"""
@dataclass @dataclass
class Function(): class Function():
@ -29,8 +22,6 @@ class Compiler(c_ast.NodeVisitor):
__rax: similar to x86 rax __rax: similar to x86 rax
__rbx: stores left hand side of binary ops to avoid clobbering by the right side __rbx: stores left hand side of binary ops to avoid clobbering by the right side
__retaddr: stores return address of func call __retaddr: stores return address of func call
optimization levels:
1: assignments set directly to the variable instead of indirectly through __rax
""" """
def __init__(self, opt_level=0): def __init__(self, opt_level=0):
self.opt_level = opt_level self.opt_level = opt_level
@ -148,6 +139,7 @@ class Compiler(c_ast.NodeVisitor):
varname = node.expr.name varname = node.expr.name
self.push(Set("__rax", varname)) self.push(Set("__rax", varname))
self.push(BinaryOp(varname, varname, "1", node.op[1])) self.push(BinaryOp(varname, varname, "1", node.op[1]))
#TODO preincrement/predecrement
else: else:
self.visit(node.expr) self.visit(node.expr)
self.push(UnaryOp("__rax", "__rax", node.op)) self.push(UnaryOp("__rax", "__rax", node.op))
@ -157,8 +149,10 @@ class Compiler(c_ast.NodeVisitor):
self.loop_start = len(self.curr_function.instructions) self.loop_start = len(self.curr_function.instructions)
self.visit(node.cond) self.visit(node.cond)
# jump over loop body when cond is false # jump over loop body when cond is false
if isinstance(self.peek(), BinaryOp):
self.push(RelativeJump(None, JumpCondition.from_binaryop(self.pop())))
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: else:
self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) self.push(RelativeJump(None, JumpCondition("==", "__rax", "0")))
self.cond_jump_offset = len(self.curr_function.instructions) - 1 self.cond_jump_offset = len(self.curr_function.instructions) - 1
@ -174,8 +168,10 @@ class Compiler(c_ast.NodeVisitor):
self.loop_start = len(self.curr_function.instructions) self.loop_start = len(self.curr_function.instructions)
self.visit(node.cond) self.visit(node.cond)
# jump over loop body when cond is false # jump over loop body when cond is false
if isinstance(self.peek(), BinaryOp):
self.push(RelativeJump(None, JumpCondition.from_binaryop(self.pop())))
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: else:
self.push(RelativeJump(None, JumpCondition("==", "__rax", "0"))) self.push(RelativeJump(None, JumpCondition("==", "__rax", "0")))
self.loop_end_jumps = [len(self.curr_function.instructions) - 1] # also used for breaks self.loop_end_jumps = [len(self.curr_function.instructions) - 1] # also used for breaks
@ -189,10 +185,10 @@ class Compiler(c_ast.NodeVisitor):
self.visit(node.cond) self.visit(node.cond)
# jump over if body when cond is false # jump over if body when cond is false
#TODO optimize for when cond is a binary operation #TODO optimize for when cond is a binary operation
if isinstance(self.peek(), BinaryOp):
self.push(RelativeJump(None, JumpCondition("!=", "__rax", "0")))
if self.opt_level >= 1 and isinstance(self.peek(), BinaryOp):
self.push(RelativeJump(None, JumpCondition("==", "__rax", "0")))
else: else:
self.push(RelativeJump(None, JumpCondition("!=", "__rax", "0")))
self.push(RelativeJump(None, JumpCondition("==", "__rax", "0")))
cond_jump_offset = len(self.curr_function.instructions) - 1 cond_jump_offset = len(self.curr_function.instructions) - 1
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
@ -242,12 +238,12 @@ class Compiler(c_ast.NodeVisitor):
self.visit(arg) self.visit(arg)
self.set_to_rax(f"__radar_arg{i}") self.set_to_rax(f"__radar_arg{i}")
args.append(f"__radar_arg{i}") args.append(f"__radar_arg{i}")
for i, arg in reversed(list(enumerate(args))):
if self.can_avoid_indirection(arg):
args[i] = self.pop().src
else:
break
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
self.push(Radar("__rax", *args)) #pylint: disable=no-value-for-parameter self.push(Radar("__rax", *args)) #pylint: disable=no-value-for-parameter
elif name == "sensor": elif name == "sensor":
self.visit(node.args.exprs[0]) self.visit(node.args.exprs[0])
@ -280,12 +276,12 @@ class Compiler(c_ast.NodeVisitor):
self.visit(arg) self.visit(arg)
self.set_to_rax(f"__shoot_arg{i}") self.set_to_rax(f"__shoot_arg{i}")
args.append(f"__shoot_arg{i}") args.append(f"__shoot_arg{i}")
for i, arg in reversed(list(enumerate(args))):
if self.can_avoid_indirection(arg):
args[i] = self.pop().src
else:
break
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
self.push(Shoot(*args)) #pylint: disable=no-value-for-parameter self.push(Shoot(*args)) #pylint: disable=no-value-for-parameter
else: else:

14
examples/control_flow.c

@ -3,13 +3,17 @@ extern struct MindustryObject message1;
void main(void) { void main(void) {
double i = 0; double i = 0;
while (i < 10) { while (i < 10) {
if (i == 3) {
print("is 3");
} else {
print("not 3");
}
printd(i);
i++;
} }
print("\n");
i = 0;
printd(i); printd(i);
if (i == 3) {
print(" is 3");
} else {
print(" is not 3");
}
print("\n"); print("\n");
printflush(message1); printflush(message1);
} }

2
setup.py

@ -4,7 +4,7 @@ with open("README.md", "r") as f:
setuptools.setup( setuptools.setup(
name="c2logic", name="c2logic",
version="0.1", version="0.1",
descripton="Compiles C code to mindustry logic.",
descripton="Compiles C code to Mindustry logic.",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
packages=["c2logic"], packages=["c2logic"],

Loading…
Cancel
Save