aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm_ops.py5
-rw-r--r--src/asm_pass1.py9
-rw-r--r--src/asm_pass2.py3
-rw-r--r--src/asm_pass3.py2
-rw-r--r--src/asm_pass4.py9
-rw-r--r--src/ast_compiler.py108
-rw-r--r--src/bytecode_compiler.py6
7 files changed, 92 insertions, 50 deletions
diff --git a/src/asm_ops.py b/src/asm_ops.py
index e09e0fd..885c275 100644
--- a/src/asm_ops.py
+++ b/src/asm_ops.py
@@ -3,11 +3,6 @@ from asm_eval import *
from dataclasses import dataclass
@dataclass
-class Options:
- stack_pointer = "rg"
- csr_scratch = "rh"
-
-@dataclass
class Meta:
address: int
diff --git a/src/asm_pass1.py b/src/asm_pass1.py
index 5e6213a..0b06738 100644
--- a/src/asm_pass1.py
+++ b/src/asm_pass1.py
@@ -16,23 +16,15 @@ class Result1Line:
@dataclass
class Result1:
lines: list[Result1Line]
- options: Options
operations: OpExpansionDict
def pass_1(result0: Result0) -> Result1:
- options = Options()
result_lines: list[Result1Line] = []
operations: OpExpansionDict = default_expansions.copy()
for line in result0.lines:
keyword, *args = tokenize(line.line)
match keyword:
- case "@opt":
- opt_name, opt_value = args
- match opt_name:
- case "stack_pointer": options.stack_pointer = opt_value
- case "csr_scratch": options.csr_scratch = opt_value
- case _: raise Exception("Unknown @opt: " + opt_name)
case "@use":
module_name, ops = args[0].split(":")
ops_split = ops.split(",")
@@ -52,6 +44,5 @@ def pass_1(result0: Result0) -> Result1:
return Result1(
operations=operations,
- options=options,
lines=result_lines
)
diff --git a/src/asm_pass2.py b/src/asm_pass2.py
index 7272dcf..e2ad4ec 100644
--- a/src/asm_pass2.py
+++ b/src/asm_pass2.py
@@ -13,11 +13,9 @@ class Result2Line:
@dataclass
class Result2:
lines: list[Result2Line]
- options: Options
operations: OpExpansionDict
def pass_2(result1: Result1) -> Result2:
- options = Options()
result_lines: list[Result2Line] = []
for line in result1.lines:
@@ -38,6 +36,5 @@ def pass_2(result1: Result1) -> Result2:
return Result2(
operations=result1.operations,
- options=options,
lines=result_lines
)
diff --git a/src/asm_pass3.py b/src/asm_pass3.py
index 7fcb44d..9c8cb34 100644
--- a/src/asm_pass3.py
+++ b/src/asm_pass3.py
@@ -14,7 +14,6 @@ class Result3Line:
@dataclass
class Result3:
lines: list[Result3Line]
- options: Options
operations: OpExpansionDict
symbols: dict[str, int]
@@ -57,7 +56,6 @@ def pass_3(result2: Result2) -> Result3:
raise Exception(f"Overlapping segments: address 0x{result_line.address:>04x} has conflicting definitions:\n{formatted}")
return Result3(
operations=result2.operations,
- options=result2.options,
lines=result_lines,
symbols=symbols,
)
diff --git a/src/asm_pass4.py b/src/asm_pass4.py
index bb30eb7..2fff870 100644
--- a/src/asm_pass4.py
+++ b/src/asm_pass4.py
@@ -15,16 +15,9 @@ class Result4Line:
@dataclass
class Result4:
lines: list[Result4Line]
- options: Options
operations: OpExpansionDict
symbols: dict[str, int]
-def translate_opt(arg: str, options: Options) -> str:
- match arg:
- case "SP": return options.stack_pointer
- case "CSR_SCRATCH": return options.csr_scratch
- case _: return arg
-
def pass_4(result3: Result3) -> Result4:
result_lines: list[Result4Line] = []
@@ -34,7 +27,6 @@ def pass_4(result3: Result3) -> Result4:
address=line.address,
)
- args = list(map(lambda a: translate_opt(a, result3.options), args))
text = " ".join([keyword, *args])
original_text = " ".join(line.original_parts)
@@ -65,6 +57,5 @@ def pass_4(result3: Result3) -> Result4:
return Result4(
operations=result3.operations,
symbols=result3.symbols,
- options=result3.options,
lines=result_lines,
)
diff --git a/src/ast_compiler.py b/src/ast_compiler.py
index e6e78bf..d3fd70e 100644
--- a/src/ast_compiler.py
+++ b/src/ast_compiler.py
@@ -28,9 +28,8 @@ class Reg():
return f"R{self.reg}"
ALL_REGS: list[RegChar] = ["A", "B", "C", "D", "E", "F", "G", "H"]
-STACK_POINTER_REG = "G"
-CSR_SCRATCH_REG = "H"
-SPECIAL_REGS: list[RegChar] = [STACK_POINTER_REG, CSR_SCRATCH_REG]
+STACK_POINTER_REG = "H"
+SPECIAL_REGS: list[RegChar] = [STACK_POINTER_REG]
GENERIC_REGS: OrderedDict[RegChar, None] = OrderedDict()
for char in ALL_REGS:
if char not in SPECIAL_REGS:
@@ -84,6 +83,7 @@ class Compiler(ast.NodeVisitor):
self.currently_emitting_asm_list = self.program_asm
self.frame_stack = FrameStack()
+ self.tmp_stack_values_n: int = 0
self.unique_name_counter = 0
self.latest_break_target: Label | None = None
@@ -113,6 +113,14 @@ class Compiler(ast.NodeVisitor):
self.currently_emitting_asm_list.append(asm)
+ def emit_stack_push(self, reg: str):
+ self.tmp_stack_values_n += 1
+ self.emit(f"spu {reg}")
+
+ def emit_stack_pop(self, reg: str):
+ self.tmp_stack_values_n -= 1
+ self.emit(f"spo {reg}")
+
def emit_label(self, label_name: str):
label_name = label_name.lower()
self.emit(f"@label {label_name}")
@@ -219,17 +227,15 @@ class Compiler(ast.NodeVisitor):
stmts = node.body
frame_names = self.collect_local_variables(stmts)
- self.emit(f"; stack frame names: {frame_names}")
- # Move stack pointer to accommodate local variables
-
- # TODO instead of self.stack_frame_size, use a proper stack for frames.
- # That way, we can restore the top-level stack frame when returning
+ self.emit("; stack frame with offsets")
frame_bindings: dict[str, Label | StackOffset] = {}
for idx, name in enumerate(frame_names):
offset = self.frame_stack.total_offset() + idx
frame_bindings[name] = offset
+ self.emit(f"; {name} {offset}")
+
frame = Frame(
names = frame_names,
bindings = frame_bindings,
@@ -238,15 +244,19 @@ class Compiler(ast.NodeVisitor):
self.emit(f"addi SP {len(frame.names)} SP")
for stmt in stmts:
+ if type(stmt) == ast.Expr and type(stmt.value) != ast.Call:
+ self.emit("; NOP top-level expression")
+ continue
+
self.visit(stmt)
self.frame_stack.pop()
self.emit(f"subi SP {len(frame.names)} SP")
def visit_Expr(self, expr: ast.Expr):
- if self.frame_stack.size == 1 and type(expr.value) != ast.Call:
- self.emit("; NOP top-level expression")
- return
+ # if self.frame_stack.size == 1 and type(expr.value) != ast.Call:
+ # self.emit("; NOP top-level expression")
+ # return
self.visit(expr.value)
@@ -381,16 +391,62 @@ class Compiler(ast.NodeVisitor):
self.emit(f"bri zero {label_false}")
for true_branch_stmt in node.body:
+ if type(true_branch_stmt) == ast.Expr and type(true_branch_stmt.value) != ast.Call:
+ self.emit("; NOP top-level expression")
+ continue
+
self.visit(true_branch_stmt)
self.emit(f"jpi {label_end}")
self.emit_label(label_false)
for false_branch_stmt in node.orelse:
+ if type(false_branch_stmt) == ast.Expr and type(false_branch_stmt.value) != ast.Call:
+ self.emit("; NOP top-level expression")
+ continue
+
self.visit(false_branch_stmt)
self.emit_label(label_end)
+ def visit_Compare(self, node: ast.Compare):
+ if len(node.ops) > 1:
+ raise Exception("Multiple compare ops not supported")
+
+ if len(node.comparators) > 1:
+ raise Exception("Multiple comparators not supported")
+
+ op = node.ops[0]
+ left = node.left
+ right = node.comparators[0]
+
+ self.visit(left)
+ self.visit(right)
+
+ label_true = self.get_unique_name("Compare_true")
+ label_end = self.get_unique_name("Compare_end")
+
+ with self.allocated_reg() as reg_lhs, self.allocated_reg() as reg_rhs:
+ match op:
+ case ast.Lt(): # lhs < rhs
+ self.emit(f"spo {reg_rhs}")
+ self.emit(f"spo {reg_lhs}")
+
+ self.emit(f"sub {reg_lhs} {reg_rhs} {reg_lhs}")
+ self.emit(f"bri carry {label_true}")
+
+ # false branch
+ self.emit(f"ldi 0 {reg_lhs}")
+ self.emit(f"spu {reg_lhs}")
+ self.emit(f"jpi {label_end}")
+
+ # true branch
+ self.emit_label(label_true)
+ self.emit(f"ldi 1 {reg_lhs}")
+ self.emit(f"spu {reg_lhs}")
+
+ self.emit_label(label_end)
+
def visit_While(self, node: ast.While):
self.emit(f"; {node}")
@@ -409,6 +465,10 @@ class Compiler(ast.NodeVisitor):
self.emit(f"bri zero {label_else}")
for body_stmt in node.body:
+ if type(body_stmt) == ast.Expr and type(body_stmt.value) != ast.Call:
+ self.emit("; NOP top-level expression")
+ continue
+
self.visit(body_stmt)
self.emit(f"jpi {label_test}")
@@ -441,6 +501,7 @@ class Compiler(ast.NodeVisitor):
raise Exception("Lambda functions not supported. Consider using a named function instead.")
def collect_local_variables(self, stmts: list[ast.stmt]):
+ result: list[str] = []
symbols: list[str] = []
for stmt in stmts:
match stmt:
@@ -455,8 +516,11 @@ class Compiler(ast.NodeVisitor):
symbols += self.collect_local_variables(fb)
case other: pass
- # remove duplicates
- return list(set(symbols))
+ for symbol in symbols:
+ if symbol not in result:
+ result.append(symbol)
+
+ return result
def visit_FunctionDef(self, node: ast.FunctionDef):
prev_currently_emitting_asm_list = self.currently_emitting_asm_list
@@ -478,16 +542,19 @@ class Compiler(ast.NodeVisitor):
return_address_name = self.get_unique_name("return_address")
local_var_names = self.collect_local_variables(fn_stmts)
frame_names = [return_address_name] + fn_params + local_var_names
- self.emit(f"; stack frame names: {frame_names}")
# Move stack pointer to accommodate local variables
- self.emit(f"addi SP {len(local_var_names)} SP")
+ if len(local_var_names) > 0:
+ self.emit(f"addi SP {len(local_var_names)} SP")
+ self.emit("; stack frame with offsets")
frame_bindings: dict[str, Label | StackOffset] = {}
for idx, name in enumerate(frame_names):
offset = self.frame_stack.total_offset() + idx
frame_bindings[name] = offset
+ self.emit(f"; {name} {offset}")
+
frame = Frame(
names = frame_names,
bindings = frame_bindings
@@ -509,21 +576,24 @@ class Compiler(ast.NodeVisitor):
frame = self.frame_stack.peek()
ret = node.value
+ if ret is not None:
+ self.visit(ret)
with self.allocated_reg() as reg1, self.allocated_reg() as reg2:
- print(frame.names)
ret_addr_offset = self.frame_stack.total_offset() - frame.size()
self.emit(f"ldi vt_stack_addr {reg1}") # vector table address of stack segment address
self.emit(f"ldr {reg1} {reg1}") # stack segment address
self.emit(f"ldi {ret_addr_offset} {reg2}") # stack offset
self.emit(f"add {reg1} {reg2} {reg1}") # stack address of return address = stack segment start + stack offset
+ self.emit(f"; stack address of return address is stored in reg {reg1}")
if ret is not None:
- self.visit(ret)
self.emit(f"spo {reg2}")
else:
self.emit(f"ldi 0 {reg2}")
+ self.emit(f"; return value is stored in reg {reg2}")
+
self.emit(f"mov {reg1} SP") # reset stack pointer to return address position (i.e. start of stack frame)
self.emit(f"ldr {reg1} {reg1}") # return address
@@ -609,10 +679,12 @@ class Compiler(ast.NodeVisitor):
if type(offset) != StackOffset:
raise Exception(f"Invalid address {offset} for name {name}. Can only assign to stack offsets.")
- self.emit(f"; assigning {name} to stack segment + offset {offset}")
+ self.emit(f"; assigning {name} at stack segment + {offset}")
with self.allocated_reg() as reg1, self.allocated_reg() as reg2:
+ self.emit(f"; evaluating value to be assigned")
self.visit(value)
+ self.emit(f"; assigning value to stack segment + {offset}")
self.emit(f"ldi vt_stack_addr {reg1}")
self.emit(f"ldr {reg1} {reg1}")
self.emit(f"ldi {offset} {reg2}")
diff --git a/src/bytecode_compiler.py b/src/bytecode_compiler.py
index 2a5bc82..0492ddb 100644
--- a/src/bytecode_compiler.py
+++ b/src/bytecode_compiler.py
@@ -94,10 +94,8 @@ def emit_serialize_const(const: Any):
print(f"warn: cannot serialize {type(const)} ({const}), emitting zero")
emit(" 0")
-STACK_POINTER_REG = Reg('G')
-CSR_SCRATCH_REG = Reg('H')
-emit(f"@opt stack_pointer {STACK_POINTER_REG}")
-emit(f"@opt csr_scratch {CSR_SCRATCH_REG}")
+STACK_POINTER_REG = Reg('H')
+emit(f"@let SP {STACK_POINTER_REG}")
emit("@use ext_std:*")
emit("@include bootstrap")