diff --git a/generators/x86_64.py b/generators/x86_64.py index e6d11e4..e704b08 100644 --- a/generators/x86_64.py +++ b/generators/x86_64.py @@ -429,4 +429,4 @@ class X86_64Generator(Generator): f.write("section .text\n") optimizer = X86_64Optimizer(self.lines) - f.write(optimizer.peephole()) \ No newline at end of file + f.writelines(optimizer.peephole()) \ No newline at end of file diff --git a/optimizers/optimizer.py b/optimizers/optimizer.py index e334197..05a3c2c 100644 --- a/optimizers/optimizer.py +++ b/optimizers/optimizer.py @@ -1,11 +1,81 @@ +from typing import List, Dict, NamedTuple, Union +import re + + +class Instruction(NamedTuple): + opcode: str + operands: List[str] + +class PeepholeRule(NamedTuple): + match: List[Instruction] + replace: List[Instruction] + class Optimizer: - def __init__(self, lines: list[str]): + def __init__(self, lines: list[str], window_size: int = 5): self.lines = lines self.final_str = "" - # sliding window pos - self.pos = 0 + # sliding window + self.window_size = window_size + self.rules: list[PeepholeRule] = [] + + def match_instruction(self, pattern: Instruction, instr: Instruction, bindings: Dict[str, str]) -> bool: + if pattern.opcode != instr.opcode: + return False + if len(pattern.operands) != len(instr.operands): + return False + + for p_op, i_op in zip(pattern.operands, instr.operands): + if p_op.isidentifier(): # wildcard like 'x' + if p_op in bindings: + if bindings[p_op] != i_op: + return False + else: + bindings[p_op] = i_op + else: + if p_op != i_op: + return False + return True + + def match_window(self, patterns: List[Instruction], window: List[Instruction]) -> Union[Dict[str, str], None]: + if len(patterns) != len(window): + return None + bindings = {} + for p, i in zip(patterns, window): + if not self.match_instruction(p, i, bindings): + return None + return bindings + def parse_instruction(self, line: str) -> Instruction: + # Assumes format: OPCODE operand1, operand2 + parts = line.strip().split(None, 1) + opcode = parts[0] + operands = re.split(r'\s*,\s*', parts[1]) if len(parts) > 1 else [] + return Instruction(opcode, operands) + + def apply_replacement(self, replacements: List[Instruction], bindings: dict[str, str]) -> List[Instruction]: + result = [] + for pattern in replacements: + operands = [bindings.get(op, op) for op in pattern.operands] + result.append(Instruction(pattern.opcode, operands)) + return result def peephole(self): - pass \ No newline at end of file + self.lines = [self.parse_instruction(line) for line in self.lines] + + i = 0 + while i < len(self.lines): + matched = False + for rule in self.rules: + window = self.lines[i:i+len(rule.match)] + bindings = self.match_window(rule.match, window) + if bindings is not None: + replacement_instrs = self.apply_replacement(rule.replace, bindings) + self.lines[i:i + len(rule.match)] = replacement_instrs + matched = True + break + if not matched: + i += 1 + + self.lines = [f"{line.opcode} {', '.join(line.operands)}\n" for line in self.lines] + return self.lines \ No newline at end of file diff --git a/optimizers/x86_64.py b/optimizers/x86_64.py index bda678f..709b628 100644 --- a/optimizers/x86_64.py +++ b/optimizers/x86_64.py @@ -1,11 +1,59 @@ -from optimizers.optimizer import Optimizer +from optimizers.optimizer import Optimizer, PeepholeRule, Instruction class X86_64Optimizer(Optimizer): - def peephole(self): - lines = [] - - for line in self.lines: - lines.append(line) - - self.final_str = ''.join(lines) - return self.final_str \ No newline at end of file + def __init__(self, lines, window_size = 5): + super().__init__(lines, window_size) + self.rules = [ + PeepholeRule( + match=[ + Instruction("setg",["al"]), + Instruction("movzx", ["rax", "al"]), + Instruction("push", ["rax"]), + Instruction("mov", ["eax", "x"]), + Instruction("test", ["eax", "eax"]), + Instruction("jnz", ["y"]) + ], + replace=[ + Instruction("jg", ["y"]) + ] + ), + PeepholeRule( + match=[ + Instruction("setl",["al"]), + Instruction("movzx", ["rax", "al"]), + Instruction("push", ["rax"]), + Instruction("mov", ["eax", "x"]), + Instruction("test", ["eax", "eax"]), + Instruction("jnz", ["y"]) + ], + replace=[ + Instruction("jl", ["y"]) + ] + ), + PeepholeRule( + match=[ + Instruction("sete",["al"]), + Instruction("movzx", ["rax", "al"]), + Instruction("push", ["rax"]), + Instruction("mov", ["eax", "x"]), + Instruction("test", ["eax", "eax"]), + Instruction("jnz", ["y"]) + ], + replace=[ + Instruction("je", ["y"]) + ] + ), + PeepholeRule( + match=[ + Instruction("setne",["al"]), + Instruction("movzx", ["rax", "al"]), + Instruction("push", ["rax"]), + Instruction("mov", ["eax", "x"]), + Instruction("test", ["eax", "eax"]), + Instruction("jnz", ["y"]) + ], + replace=[ + Instruction("jne", ["y"]) + ] + ), + ] \ No newline at end of file diff --git a/out b/out index 07f4e0f..a9a5b34 100644 Binary files a/out and b/out differ diff --git a/out.asm b/out.asm index 8d2d1a9..e8f9ea2 100644 --- a/out.asm +++ b/out.asm @@ -1,27 +1,34 @@ ; ~~~ Auto generated by the GroundPY compiler for Linux x86_64 targets. ~~~ section .data +.LC0: db "yes", 10, 0 +.LC1: equ $ - .LC0 +.LC2: db "no", 10, 0 +.LC3: equ $ - .LC2 section .text global _start -_start: - mov rax, 0 - push rax - mov rax, 0 - push rax - .loop: - mov rax, [rsp + 8] - add rax, 1 - mov QWORD [rsp + 8], rax - mov rax, 100000000 - mov rbx, [rsp + 8] - cmp rax, rbx - setg al - movzx rax, al - mov QWORD [rsp + 0], rax - mov eax, [rsp + 0] - test eax, eax - jnz .loop - mov rax, 60 - mov rdi, [rsp + 0] - syscall - \ No newline at end of file +_start: +mov rax, 123 +push rax +mov rax, 100000000 +mov rbx, [rsp + 0] +cmp rax, rbx +jg .yes +.yes: +mov rsi, .LC0 +mov rdx, .LC1 +mov rax, 1 +mov rdi, 1 +syscall +mov rax, 60 +mov rdi, 0 +syscall +.no: +mov rsi, .LC2 +mov rdx, .LC3 +mov rax, 1 +mov rdi, 1 +syscall +mov rax, 60 +mov rdi, 0 +syscall diff --git a/test2.grnd b/test2.grnd index 4e4bd78..4966a87 100644 --- a/test2.grnd +++ b/test2.grnd @@ -1,7 +1,9 @@ -set &x 0 -set &cond 0 -@loop -add $x 1 &x +set &x 123 greater 100000000 $x &cond -if $cond %loop -end $cond \ No newline at end of file +if $cond %yes +@yes +stdout "yes\n" +end 0 +@no +stdout "no\n" +end 0 \ No newline at end of file