From 917021c8451f10dcf8cd8870a0861671b662513e Mon Sep 17 00:00:00 2001 From: Bohdan Date: Tue, 5 Oct 2021 20:47:31 +0300 Subject: [PATCH 01/24] Add basic traslator to rust First test 'stack' works --- porth.py | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- test.py | 17 ++- 2 files changed, 373 insertions(+), 8 deletions(-) diff --git a/porth.py b/porth.py index 212dfbcc..a277577b 100755 --- a/porth.py +++ b/porth.py @@ -1404,6 +1404,345 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): out.write("args_ptr: resq 1\n") out.write("mem: resb %d\n" % MEM_CAPACITY) + +def generate_rust(program: Program, out_file_path: str): + var_id = 0 + stack: List[int] = [] + with open(out_file_path, "w") as out: + out.write("#![feature(asm)]\n") + out.write("macro_rules! syscall3 {\n") + out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" in(\"rsi\") $arg1,\n") + out.write(" in(\"rdx\") $arg2,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + + out.write("fn main() {\n") + + for ip in range(len(program)): + op = program[ip] + assert len(OpType) == 8, "Exhaustive ops handling in generate_rust" + if op.typ == OpType.PUSH_INT: + assert isinstance(op.operand, int), "This could be a bug in the compilation step" + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = %d;\n" % (var, op.operand)) + stack.append(var) + elif op.typ == OpType.PUSH_STR: + assert isinstance(op.operand, str), "This could be a bug in the compilation step" + var = var_id; + var_id += 1 + out.write(" let v%d: &'static str = r#\"%s\"#;\n" % (var, op.operand)) + str_const = var + + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) + str_len = var + + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) + str_addr = var + + stack.append(str_len) + stack.append(str_addr) + elif op.typ == OpType.IF: + assert False, "todo" + out.write(" ;; -- if --\n") + out.write(" pop rax\n") + out.write(" test rax, rax\n") + assert isinstance(op.operand, int), "This could be a bug in the compilation step" + out.write(" jz addr_%d\n" % op.operand) + elif op.typ == OpType.ELSE: + assert False, "todo" + out.write(" ;; -- else --\n") + assert isinstance(op.operand, int), "This could be a bug in the compilation step" + out.write(" jmp addr_%d\n" % op.operand) + elif op.typ == OpType.END: + assert False, "todo" + assert isinstance(op.operand, int), "This could be a bug in the compilation step" + out.write(" ;; -- end --\n") + if ip + 1 != op.operand: + out.write(" jmp addr_%d\n" % op.operand) + elif op.typ == OpType.WHILE: + assert False, "todo" + out.write(" ;; -- while --\n") + elif op.typ == OpType.DO: + assert False, "todo" + out.write(" ;; -- do --\n") + out.write(" pop rax\n") + out.write(" test rax, rax\n") + assert isinstance(op.operand, int), "This could be a bug in the compilation step" + out.write(" jz addr_%d\n" % op.operand) + elif op.typ == OpType.INTRINSIC: + assert len(Intrinsic) == 34, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + if op.operand == Intrinsic.PLUS: + assert False, "todo" + out.write(" ;; -- plus --\n") + out.write(" pop rax\n") + out.write(" pop rbx\n") + out.write(" add rax, rbx\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.MINUS: + assert False, "todo" + out.write(" ;; -- minus --\n") + out.write(" pop rax\n") + out.write(" pop rbx\n") + out.write(" sub rbx, rax\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.MUL: + assert False, "todo" + out.write(" ;; -- mul --\n") + out.write(" pop rax\n") + out.write(" pop rbx\n") + out.write(" mul rbx\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.DIVMOD: + assert False, "todo" + out.write(" ;; -- mod --\n") + out.write(" xor rdx, rdx\n") + out.write(" pop rbx\n") + out.write(" pop rax\n") + out.write(" div rbx\n") + out.write(" push rax\n"); + out.write(" push rdx\n"); + elif op.operand == Intrinsic.SHR: + assert False, "todo" + out.write(" ;; -- shr --\n") + out.write(" pop rcx\n") + out.write(" pop rbx\n") + out.write(" shr rbx, cl\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.SHL: + assert False, "todo" + out.write(" ;; -- shl --\n") + out.write(" pop rcx\n") + out.write(" pop rbx\n") + out.write(" shl rbx, cl\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.BOR: + assert False, "todo" + out.write(" ;; -- bor --\n") + out.write(" pop rax\n") + out.write(" pop rbx\n") + out.write(" or rbx, rax\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.BAND: + assert False, "todo" + out.write(" ;; -- band --\n") + out.write(" pop rax\n") + out.write(" pop rbx\n") + out.write(" and rbx, rax\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.PRINT: + var = stack.pop() + out.write(" println!(\"{}\", v%d);\n" % var) + elif op.operand == Intrinsic.EQ: + assert False, "todo" + out.write(" ;; -- equal -- \n") + out.write(" mov rcx, 0\n"); + out.write(" mov rdx, 1\n"); + out.write(" pop rax\n"); + out.write(" pop rbx\n"); + out.write(" cmp rax, rbx\n"); + out.write(" cmove rcx, rdx\n"); + out.write(" push rcx\n") + elif op.operand == Intrinsic.GT: + assert False, "todo" + out.write(" ;; -- gt --\n") + out.write(" mov rcx, 0\n"); + out.write(" mov rdx, 1\n"); + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" cmp rax, rbx\n"); + out.write(" cmovg rcx, rdx\n"); + out.write(" push rcx\n") + elif op.operand == Intrinsic.LT: + assert False, "todo" + out.write(" ;; -- gt --\n") + out.write(" mov rcx, 0\n"); + out.write(" mov rdx, 1\n"); + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" cmp rax, rbx\n"); + out.write(" cmovl rcx, rdx\n"); + out.write(" push rcx\n") + elif op.operand == Intrinsic.GE: + assert False, "todo" + out.write(" ;; -- gt --\n") + out.write(" mov rcx, 0\n"); + out.write(" mov rdx, 1\n"); + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" cmp rax, rbx\n"); + out.write(" cmovge rcx, rdx\n"); + out.write(" push rcx\n") + elif op.operand == Intrinsic.LE: + assert False, "todo" + out.write(" ;; -- gt --\n") + out.write(" mov rcx, 0\n"); + out.write(" mov rdx, 1\n"); + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" cmp rax, rbx\n"); + out.write(" cmovle rcx, rdx\n"); + out.write(" push rcx\n") + elif op.operand == Intrinsic.NE: + assert False, "todo" + out.write(" ;; -- ne --\n") + out.write(" mov rcx, 0\n") + out.write(" mov rdx, 1\n") + out.write(" pop rbx\n") + out.write(" pop rax\n") + out.write(" cmp rax, rbx\n") + out.write(" cmovne rcx, rdx\n") + out.write(" push rcx\n") + elif op.operand == Intrinsic.DUP: + arg = stack.pop() + stack.append(arg); + stack.append(arg); + elif op.operand == Intrinsic.SWAP: + arg0 = stack.pop() + arg1 = stack.pop() + stack.append(arg0); + stack.append(arg1); + elif op.operand == Intrinsic.DROP: + var = stack.pop(); + out.write(" let _ = v%d;\n" % var) + elif op.operand == Intrinsic.OVER: + arg0 = stack.pop() + arg1 = stack.pop() + stack.append(arg1); + stack.append(arg0); + stack.append(arg1); + elif op.operand == Intrinsic.MEM: + assert False, "todo" + out.write(" ;; -- mem --\n") + out.write(" push mem\n") + elif op.operand == Intrinsic.LOAD: + assert False, "todo" + out.write(" ;; -- load --\n") + out.write(" pop rax\n") + out.write(" xor rbx, rbx\n") + out.write(" mov bl, [rax]\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.STORE: + assert False, "todo" + out.write(" ;; -- store --\n") + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" mov [rax], bl\n"); + elif op.operand == Intrinsic.ARGC: + assert False, "todo" + out.write(" ;; -- argc --\n") + out.write(" mov rax, [args_ptr]\n") + out.write(" mov rax, [rax]\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.ARGV: + assert False, "todo" + out.write(" ;; -- argv --\n") + out.write(" mov rax, [args_ptr]\n") + out.write(" add rax, 8\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.LOAD64: + assert False, "todo" + out.write(" ;; -- load --\n") + out.write(" pop rax\n") + out.write(" xor rbx, rbx\n") + out.write(" mov rbx, [rax]\n") + out.write(" push rbx\n") + elif op.operand == Intrinsic.STORE64: + assert False, "todo" + out.write(" ;; -- store --\n") + out.write(" pop rbx\n"); + out.write(" pop rax\n"); + out.write(" mov [rax], rbx\n"); + elif op.operand == Intrinsic.CAST_PTR: + assert False, "todo" + out.write(" ;; -- cast(ptr) --\n") + elif op.operand == Intrinsic.SYSCALL0: + assert False, "todo" + out.write(" ;; -- syscall0 --\n") + out.write(" pop rax\n") + out.write(" syscall\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.SYSCALL1: + assert False, "todo" + out.write(" ;; -- syscall1 --\n") + out.write(" pop rax\n") + out.write(" pop rdi\n") + out.write(" syscall\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.SYSCALL2: + assert False, "todo" + out.write(" ;; -- syscall2 -- \n") + out.write(" pop rax\n"); + out.write(" pop rdi\n"); + out.write(" pop rsi\n"); + out.write(" syscall\n"); + out.write(" push rax\n") + elif op.operand == Intrinsic.SYSCALL3: + num = stack.pop(); + arg0 = stack.pop(); + arg1 = stack.pop(); + arg2 = stack.pop(); + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = syscall3!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) + stack.append(var) + elif op.operand == Intrinsic.SYSCALL4: + assert False, "todo" + out.write(" ;; -- syscall4 --\n") + out.write(" pop rax\n") + out.write(" pop rdi\n") + out.write(" pop rsi\n") + out.write(" pop rdx\n") + out.write(" pop r10\n") + out.write(" syscall\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.SYSCALL5: + assert False, "todo" + out.write(" ;; -- syscall5 --\n") + out.write(" pop rax\n") + out.write(" pop rdi\n") + out.write(" pop rsi\n") + out.write(" pop rdx\n") + out.write(" pop r10\n") + out.write(" pop r8\n") + out.write(" syscall\n") + out.write(" push rax\n") + elif op.operand == Intrinsic.SYSCALL6: + assert False, "todo" + out.write(" ;; -- syscall6 --\n") + out.write(" pop rax\n") + out.write(" pop rdi\n") + out.write(" pop rsi\n") + out.write(" pop rdx\n") + out.write(" pop r10\n") + out.write(" pop r8\n") + out.write(" pop r9\n") + out.write(" syscall\n") + out.write(" push rax\n") + else: + assert False, "unreachable" + else: + assert False, "unreachable" + + out.write("}") + + assert len(Keyword) == 7, "Exhaustive KEYWORD_NAMES definition." KEYWORD_NAMES = { 'if': Keyword.IF, @@ -1800,7 +2139,7 @@ def usage(compiler_name: str): if not unsafe: type_check_program(program) simulate_little_endian_linux(program, [program_path] + argv) - elif subcommand == "com": + elif subcommand == "com" or subcommand == "translate": silent = False run = False output_path = None @@ -1849,18 +2188,31 @@ def usage(compiler_name: str): basepath = path.join(basedir, basename) if not silent: - print("[INFO] Generating %s" % (basepath + ".asm")) + if subcommand == "com": + print("[INFO] Generating %s" % (basepath + ".asm")) + elif subcommand == "translate": + print("[INFO] Generating %s" % (basepath + ".rs")) include_paths.append(path.dirname(program_path)) program = compile_file_to_program(program_path, include_paths, expansion_limit); if not unsafe: type_check_program(program) - generate_nasm_linux_x86_64(program, basepath + ".asm") - cmd_call_echoed(["nasm", "-felf64", basepath + ".asm"], silent) - cmd_call_echoed(["ld", "-o", basepath, basepath + ".o"], silent) + + if subcommand == "com": + generate_nasm_linux_x86_64(program, basepath + ".asm") + cmd_call_echoed(["nasm", "-felf64", basepath + ".asm"], silent) + cmd_call_echoed(["ld", "-o", basepath, basepath + ".o"], silent) + elif subcommand == "translate": + generate_rust(program, basepath + ".rs") + cmd_call_echoed(["rustc", "-o", basepath + "_rs", basepath + ".rs"], silent) + else: + assert False, "unreacheble" if run: - exit(cmd_call_echoed([basepath] + argv, silent)) + if subcommand == "com": + exit(cmd_call_echoed([basepath] + argv, silent)) + elif subcommand == "translate": + exit(cmd_call_echoed([basepath+"_rs"] + argv, silent)) elif subcommand == "help": usage(compiler_name) exit(0) diff --git a/test.py b/test.py index f8154690..b55c3244 100755 --- a/test.py +++ b/test.py @@ -80,6 +80,7 @@ def save_test_case(file_path: str, class RunStats: sim_failed: int = 0 com_failed: int = 0 + translate_failed: int = 0 ignored: int = 0 def run_test_for_file(file_path: str, stats: RunStats = RunStats()): @@ -117,6 +118,18 @@ def run_test_for_file(file_path: str, stats: RunStats = RunStats()): print(" stdout: \n%s" % com.stdout.decode("utf-8")) print(" stderr: \n%s" % com.stderr.decode("utf-8")) stats.com_failed += 1 + translate = cmd_run_echoed([sys.executable, "./porth.py", "translate", "-r", "-s", file_path, *tc.argv], input=tc.stdin, capture_output=True) + if translate.returncode != tc.returncode or translate.stdout != tc.stdout or translate.stderr != tc.stderr: + print("[ERROR] Unexpected translation output") + print(" Expected:") + print(" return code: %s" % tc.returncode) + print(" stdout: \n%s" % tc.stdout.decode("utf-8")) + print(" stderr: \n%s" % tc.stderr.decode("utf-8")) + print(" Actual:") + print(" return code: %s" % com.returncode) + print(" stdout: \n%s" % com.stdout.decode("utf-8")) + print(" stderr: \n%s" % com.stderr.decode("utf-8")) + stats.translate_failed += 1 else: print('[WARNING] Could not find any input/output data for %s. Ignoring testing. Only checking if it compiles.' % file_path) com = cmd_run_echoed([sys.executable, "./porth.py", "com", file_path]) @@ -130,8 +143,8 @@ def run_test_for_folder(folder: str): if entry.is_file() and entry.path.endswith(PORTH_EXT): run_test_for_file(entry.path, stats) print() - print("Simulation failed: %d, Compilation failed: %d, Ignored: %d" % (stats.sim_failed, stats.com_failed, stats.ignored)) - if stats.sim_failed != 0 or stats.com_failed != 0: + print("Simulation failed: %d, Compilation failed: %d, Translate failed: %d, Ignored: %d" % (stats.sim_failed, stats.com_failed, stats.translate_failed stats.ignored)) + if stats.sim_failed != 0 or stats.com_failed != 0 or stats.translate_failed: exit(1) def update_input_for_file(file_path: str, argv: List[str]): From 1d89e44b6fadca73eceb1ff3968373bc335782ce Mon Sep 17 00:00:00 2001 From: Bohdan Date: Tue, 5 Oct 2021 21:17:00 +0300 Subject: [PATCH 02/24] Translator: Implements arithmetic operations arithmetics test passes --- porth.py | 60 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/porth.py b/porth.py index a277577b..b30d838f 100755 --- a/porth.py +++ b/porth.py @@ -1489,35 +1489,43 @@ def generate_rust(program: Program, out_file_path: str): elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 34, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: - assert False, "todo" - out.write(" ;; -- plus --\n") - out.write(" pop rax\n") - out.write(" pop rbx\n") - out.write(" add rax, rbx\n") - out.write(" push rax\n") + op0 = stack.pop() + op1 = stack.pop() + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d + v%d;\n" % (var, op1, op0)) + stack.append(var) elif op.operand == Intrinsic.MINUS: - assert False, "todo" - out.write(" ;; -- minus --\n") - out.write(" pop rax\n") - out.write(" pop rbx\n") - out.write(" sub rbx, rax\n") - out.write(" push rbx\n") + op0 = stack.pop() + op1 = stack.pop() + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d - v%d;\n" % (var, op1, op0)) + stack.append(var) elif op.operand == Intrinsic.MUL: - assert False, "todo" - out.write(" ;; -- mul --\n") - out.write(" pop rax\n") - out.write(" pop rbx\n") - out.write(" mul rbx\n") - out.write(" push rax\n") + op0 = stack.pop() + op1 = stack.pop() + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d * v%d;\n" % (var, op1, op0)) + stack.append(var) elif op.operand == Intrinsic.DIVMOD: - assert False, "todo" - out.write(" ;; -- mod --\n") - out.write(" xor rdx, rdx\n") - out.write(" pop rbx\n") - out.write(" pop rax\n") - out.write(" div rbx\n") - out.write(" push rax\n"); - out.write(" push rdx\n"); + op0 = stack.pop() + op1 = stack.pop() + + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d / v%d;\n" % (var, op1, op0)) + div = var + + var = var_id; + var_id += 1 + out.write(" let v%d: u64 = v%d %% v%d;\n" % (var, op1, op0)) + mod = var + + stack.append(div) + stack.append(mod) + elif op.operand == Intrinsic.SHR: assert False, "todo" out.write(" ;; -- shr --\n") From 250384ba5d4ebd2217b0992c44d236fb51b8da7b Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 11:46:36 +0300 Subject: [PATCH 03/24] Translator: if and while done --- porth.py | 168 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 50 deletions(-) diff --git a/porth.py b/porth.py index b30d838f..d44a5e23 100755 --- a/porth.py +++ b/porth.py @@ -11,6 +11,7 @@ from copy import copy from time import sleep import traceback +from io import StringIO PORTH_EXT = '.porth' DEFAULT_EXPANSION_LIMIT=1000 @@ -1407,9 +1408,13 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): def generate_rust(program: Program, out_file_path: str): var_id = 0 + offset = 1 stack: List[int] = [] + contexts: List[Triple[OpType, List[int], List[int]]] = [] + buffers = [] with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") + out.write("#![allow(unused_assignments)]\n") out.write("macro_rules! syscall3 {\n") out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr) => {{\n") out.write(" unsafe {\n") @@ -1437,55 +1442,124 @@ def generate_rust(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the compilation step" var = var_id; var_id += 1 - out.write(" let v%d: u64 = %d;\n" % (var, op.operand)) + out.write((" " * offset) + "let v%d: u64 = %d;\n" % (var, op.operand)) stack.append(var) elif op.typ == OpType.PUSH_STR: assert isinstance(op.operand, str), "This could be a bug in the compilation step" var = var_id; var_id += 1 - out.write(" let v%d: &'static str = r#\"%s\"#;\n" % (var, op.operand)) + out.write((" " * offset) + "let v%d: &'static str = r#\"%s\"#;\n" % (var, op.operand)) str_const = var var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) + out.write((" " * offset) + "let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) str_len = var var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) + out.write((" " * offset) + "let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) str_addr = var stack.append(str_len) stack.append(str_addr) elif op.typ == OpType.IF: - assert False, "todo" - out.write(" ;; -- if --\n") - out.write(" pop rax\n") - out.write(" test rax, rax\n") - assert isinstance(op.operand, int), "This could be a bug in the compilation step" - out.write(" jz addr_%d\n" % op.operand) + cond = stack.pop() + new_stack: List[int] = [] + for v in stack: + new_v = var_id + var_id += 1 + out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v)) + new_stack.append(new_v) + contexts.append((OpType.IF, copy(new_stack), [])) + stack = new_stack + for (new_v, new_v_val) in zip(new_stack, stack): + out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + buffers.append(out) + out = StringIO() + + out.write((" " * offset) + "if v%d {\n" % cond) + offset += 1 elif op.typ == OpType.ELSE: - assert False, "todo" - out.write(" ;; -- else --\n") + prev_op, prev_context, _ = contexts.pop() + # Sync context + for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + if curr != prev: + out.write((" " * offset) + "v%d = v%d;\n" % (prev, curr)) + curr = prev + + old_out = out + out = buffers.pop() + + + new_stack_entries = [] + # Handle new stack entries + for new_v_val in stack[len(prev_context):]: + new_v = var_id + var_id += 1 + out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) + new_stack_entries.append(new_v) + out.write(old_out.getvalue()) + old_out.close() + + for (new_v_val, new_v) in zip(stack[len(prev_context):], new_stack_entries): + out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + + stack = copy(prev_context) + + offset -= 1 + out.write((" " * offset) + "}\n") + out.write((" " * offset) + "else {\n") + offset += 1 + contexts.append((OpType.ELSE, prev_context, new_stack_entries)) assert isinstance(op.operand, int), "This could be a bug in the compilation step" - out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.END: - assert False, "todo" + prev_op, prev_context, new_stack_entries = contexts.pop() + + # Sync context + for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + if curr != prev: + out.write((" " * offset) + "v%d = v%d;\n" % (prev, curr)) + + if prev_op == OpType.IF or prev_op == OpType.WHILE: + old_out = out + out = buffers.pop(); + out.write(old_out.getvalue()) + old_out.close() + stack = copy(prev_context) + elif prev_op == OpType.ELSE: + # Handle new stack entries + new_stack = copy(prev_context) + for (new_v, new_v_val) in zip(new_stack_entries, stack[len(prev_context):]): + out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + new_stack.append(new_v) + stack = new_stack + else: + assert False, "unreachable" + + + offset -= 1 + out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" - out.write(" ;; -- end --\n") - if ip + 1 != op.operand: - out.write(" jmp addr_%d\n" % op.operand) elif op.typ == OpType.WHILE: - assert False, "todo" - out.write(" ;; -- while --\n") + new_stack: List[int] = [] + for v in stack: + new_v = var_id + var_id += 1 + out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v)) + new_stack.append(new_v) + contexts.append((OpType.WHILE, copy(new_stack), [])) + stack = new_stack + buffers.append(out) + out = StringIO() + + out.write((" " * offset) + "loop {\n") + offset += 1 elif op.typ == OpType.DO: - assert False, "todo" - out.write(" ;; -- do --\n") - out.write(" pop rax\n") - out.write(" test rax, rax\n") + out.write((" " * offset) + "if !v%d {\n" % stack.pop()) + out.write((" " * (offset + 1)) + "break\n") + out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" - out.write(" jz addr_%d\n" % op.operand) elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 34, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: @@ -1493,21 +1567,21 @@ def generate_rust(program: Program, out_file_path: str): op1 = stack.pop() var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d + v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d: u64 = v%d + v%d;\n" % (var, op1, op0)) stack.append(var) elif op.operand == Intrinsic.MINUS: op0 = stack.pop() op1 = stack.pop() var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d - v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d: u64 = v%d - v%d;\n" % (var, op1, op0)) stack.append(var) elif op.operand == Intrinsic.MUL: op0 = stack.pop() op1 = stack.pop() var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d * v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d: u64 = v%d * v%d;\n" % (var, op1, op0)) stack.append(var) elif op.operand == Intrinsic.DIVMOD: op0 = stack.pop() @@ -1515,12 +1589,12 @@ def generate_rust(program: Program, out_file_path: str): var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d / v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d: u64 = v%d / v%d;\n" % (var, op1, op0)) div = var var = var_id; var_id += 1 - out.write(" let v%d: u64 = v%d %% v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d: u64 = v%d %% v%d;\n" % (var, op1, op0)) mod = var stack.append(div) @@ -1556,17 +1630,14 @@ def generate_rust(program: Program, out_file_path: str): out.write(" push rbx\n") elif op.operand == Intrinsic.PRINT: var = stack.pop() - out.write(" println!(\"{}\", v%d);\n" % var) + out.write((" " * offset) + "println!(\"{}\", v%d);\n" % var) elif op.operand == Intrinsic.EQ: - assert False, "todo" - out.write(" ;; -- equal -- \n") - out.write(" mov rcx, 0\n"); - out.write(" mov rdx, 1\n"); - out.write(" pop rax\n"); - out.write(" pop rbx\n"); - out.write(" cmp rax, rbx\n"); - out.write(" cmove rcx, rdx\n"); - out.write(" push rcx\n") + op0 = stack.pop() + op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d == v%d;\n" % (var, op1, op0)) + stack.append(var) elif op.operand == Intrinsic.GT: assert False, "todo" out.write(" ;; -- gt --\n") @@ -1578,15 +1649,12 @@ def generate_rust(program: Program, out_file_path: str): out.write(" cmovg rcx, rdx\n"); out.write(" push rcx\n") elif op.operand == Intrinsic.LT: - assert False, "todo" - out.write(" ;; -- gt --\n") - out.write(" mov rcx, 0\n"); - out.write(" mov rdx, 1\n"); - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" cmp rax, rbx\n"); - out.write(" cmovl rcx, rdx\n"); - out.write(" push rcx\n") + op0 = stack.pop() + op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d < v%d;\n" % (var, op1, op0)) + stack.append(var) elif op.operand == Intrinsic.GE: assert False, "todo" out.write(" ;; -- gt --\n") @@ -1628,7 +1696,7 @@ def generate_rust(program: Program, out_file_path: str): stack.append(arg1); elif op.operand == Intrinsic.DROP: var = stack.pop(); - out.write(" let _ = v%d;\n" % var) + out.write((" " * offset) + "let _ = v%d;\n" % var) elif op.operand == Intrinsic.OVER: arg0 = stack.pop() arg1 = stack.pop() @@ -1708,7 +1776,7 @@ def generate_rust(program: Program, out_file_path: str): arg2 = stack.pop(); var = var_id; var_id += 1 - out.write(" let v%d: u64 = syscall3!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) + out.write((" " * offset) + "let v%d: u64 = syscall3!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) stack.append(var) elif op.operand == Intrinsic.SYSCALL4: assert False, "todo" From bc6db42e55f3837a828788dc69f965359798e65e Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 12:43:13 +0300 Subject: [PATCH 04/24] Translator: Print booleans as numbers --- porth.py | 156 ++++++++++++++++++++++++++----------------------------- 1 file changed, 75 insertions(+), 81 deletions(-) diff --git a/porth.py b/porth.py index d44a5e23..d733788a 100755 --- a/porth.py +++ b/porth.py @@ -1409,12 +1409,12 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): def generate_rust(program: Program, out_file_path: str): var_id = 0 offset = 1 - stack: List[int] = [] + stack: List[Triple[DataType, int]] = [] contexts: List[Triple[OpType, List[int], List[int]]] = [] buffers = [] with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") - out.write("#![allow(unused_assignments)]\n") + out.write("#![allow(unused)]\n") out.write("macro_rules! syscall3 {\n") out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr) => {{\n") out.write(" unsafe {\n") @@ -1443,7 +1443,7 @@ def generate_rust(program: Program, out_file_path: str): var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: u64 = %d;\n" % (var, op.operand)) - stack.append(var) + stack.append((DataType.INT, var)) elif op.typ == OpType.PUSH_STR: assert isinstance(op.operand, str), "This could be a bug in the compilation step" var = var_id; @@ -1461,20 +1461,21 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) str_addr = var - stack.append(str_len) - stack.append(str_addr) + stack.append((DataType.INT, str_len)) + stack.append((DataType.PTR, str_addr)) elif op.typ == OpType.IF: - cond = stack.pop() - new_stack: List[int] = [] + _, cond = stack.pop() + new_stack: List[Triple[DataType,int]] = [] for v in stack: new_v = var_id var_id += 1 - out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v)) - new_stack.append(new_v) + out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[0])) + new_stack.append((v[0], new_v)) contexts.append((OpType.IF, copy(new_stack), [])) - stack = new_stack for (new_v, new_v_val) in zip(new_stack, stack): - out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) + stack = new_stack + buffers.append(out) out = StringIO() @@ -1498,12 +1499,12 @@ def generate_rust(program: Program, out_file_path: str): new_v = var_id var_id += 1 out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) - new_stack_entries.append(new_v) + new_stack_entries.append((new_v_val[0], new_v)) out.write(old_out.getvalue()) old_out.close() for (new_v_val, new_v) in zip(stack[len(prev_context):], new_stack_entries): - out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) stack = copy(prev_context) @@ -1519,7 +1520,7 @@ def generate_rust(program: Program, out_file_path: str): # Sync context for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): if curr != prev: - out.write((" " * offset) + "v%d = v%d;\n" % (prev, curr)) + out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) if prev_op == OpType.IF or prev_op == OpType.WHILE: old_out = out @@ -1531,7 +1532,7 @@ def generate_rust(program: Program, out_file_path: str): # Handle new stack entries new_stack = copy(prev_context) for (new_v, new_v_val) in zip(new_stack_entries, stack[len(prev_context):]): - out.write((" " * offset) + "v%d = v%d;\n" % (new_v, new_v_val)) + out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) new_stack.append(new_v) stack = new_stack else: @@ -1546,8 +1547,8 @@ def generate_rust(program: Program, out_file_path: str): for v in stack: new_v = var_id var_id += 1 - out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v)) - new_stack.append(new_v) + out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[1])) + new_stack.append((v[0], new_v)) contexts.append((OpType.WHILE, copy(new_stack), [])) stack = new_stack buffers.append(out) @@ -1556,36 +1557,36 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "loop {\n") offset += 1 elif op.typ == OpType.DO: - out.write((" " * offset) + "if !v%d {\n" % stack.pop()) + out.write((" " * offset) + "if !v%d {\n" % stack.pop()[1]) out.write((" " * (offset + 1)) + "break\n") out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" elif op.typ == OpType.INTRINSIC: assert len(Intrinsic) == 34, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: u64 = v%d + v%d;\n" % (var, op1, op0)) - stack.append(var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MINUS: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: u64 = v%d - v%d;\n" % (var, op1, op0)) - stack.append(var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MUL: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: u64 = v%d * v%d;\n" % (var, op1, op0)) - stack.append(var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.DIVMOD: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 @@ -1597,8 +1598,8 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "let v%d: u64 = v%d %% v%d;\n" % (var, op1, op0)) mod = var - stack.append(div) - stack.append(mod) + stack.append((DataType.INT, div)) + stack.append((DataType.INT, mod)) elif op.operand == Intrinsic.SHR: assert False, "todo" @@ -1629,62 +1630,55 @@ def generate_rust(program: Program, out_file_path: str): out.write(" and rbx, rax\n") out.write(" push rbx\n") elif op.operand == Intrinsic.PRINT: - var = stack.pop() - out.write((" " * offset) + "println!(\"{}\", v%d);\n" % var) + typ, var = stack.pop() + if typ == DataType.INT or typ == DataType.PTR: + out.write((" " * offset) + "println!(\"{}\", v%d);\n" % var) + elif typ == DataType.BOOL: + out.write((" " * offset) + "println!(\"{}\", v%d as u64);\n" % var) + else: + assert False, "unreachable" elif op.operand == Intrinsic.EQ: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: bool = v%d == v%d;\n" % (var, op1, op0)) - stack.append(var) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GT: - assert False, "todo" - out.write(" ;; -- gt --\n") - out.write(" mov rcx, 0\n"); - out.write(" mov rdx, 1\n"); - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" cmp rax, rbx\n"); - out.write(" cmovg rcx, rdx\n"); - out.write(" push rcx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d > v%d;\n" % (var, op1, op0)) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LT: - op0 = stack.pop() - op1 = stack.pop() + _, op0 = stack.pop() + _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: bool = v%d < v%d;\n" % (var, op1, op0)) - stack.append(var) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GE: - assert False, "todo" - out.write(" ;; -- gt --\n") - out.write(" mov rcx, 0\n"); - out.write(" mov rdx, 1\n"); - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" cmp rax, rbx\n"); - out.write(" cmovge rcx, rdx\n"); - out.write(" push rcx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d >= v%d;\n" % (var, op1, op0)) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LE: - assert False, "todo" - out.write(" ;; -- gt --\n") - out.write(" mov rcx, 0\n"); - out.write(" mov rdx, 1\n"); - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" cmp rax, rbx\n"); - out.write(" cmovle rcx, rdx\n"); - out.write(" push rcx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d <= v%d;\n" % (var, op1, op0)) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.NE: - assert False, "todo" - out.write(" ;; -- ne --\n") - out.write(" mov rcx, 0\n") - out.write(" mov rdx, 1\n") - out.write(" pop rbx\n") - out.write(" pop rax\n") - out.write(" cmp rax, rbx\n") - out.write(" cmovne rcx, rdx\n") - out.write(" push rcx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: bool = v%d != v%d;\n" % (var, op1, op0)) + stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.DUP: arg = stack.pop() stack.append(arg); @@ -1695,7 +1689,7 @@ def generate_rust(program: Program, out_file_path: str): stack.append(arg0); stack.append(arg1); elif op.operand == Intrinsic.DROP: - var = stack.pop(); + _, var = stack.pop(); out.write((" " * offset) + "let _ = v%d;\n" % var) elif op.operand == Intrinsic.OVER: arg0 = stack.pop() @@ -1770,14 +1764,14 @@ def generate_rust(program: Program, out_file_path: str): out.write(" syscall\n"); out.write(" push rax\n") elif op.operand == Intrinsic.SYSCALL3: - num = stack.pop(); - arg0 = stack.pop(); - arg1 = stack.pop(); - arg2 = stack.pop(); + _, num = stack.pop(); + _, arg0 = stack.pop(); + _, arg1 = stack.pop(); + _, arg2 = stack.pop(); var = var_id; var_id += 1 out.write((" " * offset) + "let v%d: u64 = syscall3!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) - stack.append(var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL4: assert False, "todo" out.write(" ;; -- syscall4 --\n") From e2a857875b8451f280144db2af7d817ec1fbd4b2 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 12:58:48 +0300 Subject: [PATCH 05/24] Translator: Implement bit operations --- porth.py | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/porth.py b/porth.py index d733788a..91d6f316 100755 --- a/porth.py +++ b/porth.py @@ -1602,33 +1602,33 @@ def generate_rust(program: Program, out_file_path: str): stack.append((DataType.INT, mod)) elif op.operand == Intrinsic.SHR: - assert False, "todo" - out.write(" ;; -- shr --\n") - out.write(" pop rcx\n") - out.write(" pop rbx\n") - out.write(" shr rbx, cl\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = v%d >> v%d;\n" % (var, op1, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SHL: - assert False, "todo" - out.write(" ;; -- shl --\n") - out.write(" pop rcx\n") - out.write(" pop rbx\n") - out.write(" shl rbx, cl\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = v%d << v%d;\n" % (var, op1, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.BOR: - assert False, "todo" - out.write(" ;; -- bor --\n") - out.write(" pop rax\n") - out.write(" pop rbx\n") - out.write(" or rbx, rax\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = v%d | v%d;\n" % (var, op1, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.BAND: - assert False, "todo" - out.write(" ;; -- band --\n") - out.write(" pop rax\n") - out.write(" pop rbx\n") - out.write(" and rbx, rax\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + _, op1 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = v%d & v%d;\n" % (var, op1, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.PRINT: typ, var = stack.pop() if typ == DataType.INT or typ == DataType.PTR: From 1e57eefdec2231d3f3a747750786a6713f6d2277 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 14:14:44 +0300 Subject: [PATCH 06/24] Translator memory implemented --- porth.py | 81 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/porth.py b/porth.py index 91d6f316..352036b2 100755 --- a/porth.py +++ b/porth.py @@ -1432,7 +1432,43 @@ def generate_rust(program: Program, out_file_path: str): out.write(" }\n") out.write(" }}\n") out.write("}\n") + out.write("macro_rules! mem {\n") + out.write(" ($memory: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" (&$memory).as_ptr() as usize as u64\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + out.write("macro_rules! load8 {\n") + out.write(" ($addr: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" (*($addr as *const u8)) as u64\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + out.write("macro_rules! store8 {\n") + out.write(" ($addr: expr, $value: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" *($addr as *mut u8) = $value as u8;\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + out.write("macro_rules! load64 {\n") + out.write(" ($addr: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" *($addr as *const u64)\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + out.write("macro_rules! store64 {\n") + out.write(" ($addr: expr, $value: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" *($addr as *mut u64) = $value;\n") + out.write(" }\n") + out.write(" }}\n") + out.write("}\n") + out.write("static mut MEMORY: [u8;640_000] = [0;640_000];\n") out.write("fn main() {\n") for ip in range(len(program)): @@ -1698,22 +1734,20 @@ def generate_rust(program: Program, out_file_path: str): stack.append(arg0); stack.append(arg1); elif op.operand == Intrinsic.MEM: - assert False, "todo" - out.write(" ;; -- mem --\n") - out.write(" push mem\n") + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = mem!(MEMORY);\n" % var) + stack.append((DataType.PTR, var)) elif op.operand == Intrinsic.LOAD: - assert False, "todo" - out.write(" ;; -- load --\n") - out.write(" pop rax\n") - out.write(" xor rbx, rbx\n") - out.write(" mov bl, [rax]\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = load8!(v%d);\n" % (var, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.STORE: - assert False, "todo" - out.write(" ;; -- store --\n") - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" mov [rax], bl\n"); + _, op0 = stack.pop() + _, op1 = stack.pop() + out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.ARGC: assert False, "todo" out.write(" ;; -- argc --\n") @@ -1727,18 +1761,15 @@ def generate_rust(program: Program, out_file_path: str): out.write(" add rax, 8\n") out.write(" push rax\n") elif op.operand == Intrinsic.LOAD64: - assert False, "todo" - out.write(" ;; -- load --\n") - out.write(" pop rax\n") - out.write(" xor rbx, rbx\n") - out.write(" mov rbx, [rax]\n") - out.write(" push rbx\n") + _, op0 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = load64!(v%d);\n" % (var, op0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.STORE64: - assert False, "todo" - out.write(" ;; -- store --\n") - out.write(" pop rbx\n"); - out.write(" pop rax\n"); - out.write(" mov [rax], rbx\n"); + _, op0 = stack.pop() + _, op1 = stack.pop() + out.write((" " * offset) + "store64!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.CAST_PTR: assert False, "todo" out.write(" ;; -- cast(ptr) --\n") From b89d62f90dc39b2a56eb2851cac09237ca04c11c Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 15:38:43 +0300 Subject: [PATCH 07/24] Translator: Fix control flow --- porth.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/porth.py b/porth.py index 352036b2..6a699ea6 100755 --- a/porth.py +++ b/porth.py @@ -1505,11 +1505,13 @@ def generate_rust(program: Program, out_file_path: str): for v in stack: new_v = var_id var_id += 1 - out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[0])) new_stack.append((v[0], new_v)) contexts.append((OpType.IF, copy(new_stack), [])) + + out.write((" " * offset) + "//Storing stack\n") for (new_v, new_v_val) in zip(new_stack, stack): - out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) + out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v[1], new_v_val[1])) + stack = new_stack buffers.append(out) @@ -1519,11 +1521,12 @@ def generate_rust(program: Program, out_file_path: str): offset += 1 elif op.typ == OpType.ELSE: prev_op, prev_context, _ = contexts.pop() - # Sync context + + out.write((" " * offset) + "//Sync stack\n") for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): if curr != prev: - out.write((" " * offset) + "v%d = v%d;\n" % (prev, curr)) - curr = prev + out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) + stack[:len(prev_context)] = copy(prev_context) old_out = out out = buffers.pop() @@ -1531,16 +1534,19 @@ def generate_rust(program: Program, out_file_path: str): new_stack_entries = [] # Handle new stack entries + out.write((" " * (offset - 1)) + "//New stack entries\n") for new_v_val in stack[len(prev_context):]: new_v = var_id var_id += 1 out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) new_stack_entries.append((new_v_val[0], new_v)) + out.write(old_out.getvalue()) old_out.close() for (new_v_val, new_v) in zip(stack[len(prev_context):], new_stack_entries): out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) + stack = copy(prev_context) @@ -1548,33 +1554,41 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "}\n") out.write((" " * offset) + "else {\n") offset += 1 - contexts.append((OpType.ELSE, prev_context, new_stack_entries)) + contexts.append((OpType.ELSE, copy(prev_context), new_stack_entries)) assert isinstance(op.operand, int), "This could be a bug in the compilation step" elif op.typ == OpType.END: prev_op, prev_context, new_stack_entries = contexts.pop() - # Sync context - for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): - if curr != prev: - out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) if prev_op == OpType.IF or prev_op == OpType.WHILE: old_out = out out = buffers.pop(); out.write(old_out.getvalue()) old_out.close() + + for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + if curr != prev: + out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) + stack = copy(prev_context) elif prev_op == OpType.ELSE: # Handle new stack entries + new_stack = copy(prev_context) for (new_v, new_v_val) in zip(new_stack_entries, stack[len(prev_context):]): out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) new_stack.append(new_v) - stack = new_stack + + out.write((" " * offset) + "//Sync stack\n") + for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + if curr != prev: + out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) + + stack = copy(prev_context) + stack = new_stack else: assert False, "unreachable" - offset -= 1 out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" From 62f7d41d52e3a7d5675967dd62c25350974e3836 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 16:42:15 +0300 Subject: [PATCH 08/24] Translator: All support for all syscalls --- porth.py | 198 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 144 insertions(+), 54 deletions(-) diff --git a/porth.py b/porth.py index 6a699ea6..0995d785 100755 --- a/porth.py +++ b/porth.py @@ -1415,7 +1415,46 @@ def generate_rust(program: Program, out_file_path: str): with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") out.write("#![allow(unused)]\n") - out.write("macro_rules! syscall3 {\n") + out.write("macro_rules! syscall {\n") + out.write(" ($num: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }};\n") + out.write(" ($num: expr, $arg0: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }};\n") + out.write(" ($num: expr, $arg0: expr, $arg1: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" in(\"rsi\") $arg1,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }};\n") out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr) => {{\n") out.write(" unsafe {\n") out.write(" let ret : u64;\n") @@ -1430,6 +1469,57 @@ def generate_rust(program: Program, out_file_path: str): out.write(" );\n") out.write(" ret\n") out.write(" }\n") + out.write(" }};\n") + out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr, $arg3: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" in(\"rsi\") $arg1,\n") + out.write(" in(\"rdx\") $arg2,\n") + out.write(" in(\"r10\") $arg3,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }};\n") + out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr, $arg3: expr, $arg4: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" in(\"rsi\") $arg1,\n") + out.write(" in(\"rdx\") $arg2,\n") + out.write(" in(\"r10\") $arg3,\n") + out.write(" in(\"r8\") $arg4,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") + out.write(" }};\n") + out.write(" ($num: expr, $arg0: expr, $arg1: expr, $arg2: expr, $arg3: expr, $arg4: expr, $arg5: expr) => {{\n") + out.write(" unsafe {\n") + out.write(" let ret : u64;\n") + out.write(" asm!(\"syscall\",\n") + out.write(" in(\"rax\") $num,\n") + out.write(" in(\"rdi\") $arg0,\n") + out.write(" in(\"rsi\") $arg1,\n") + out.write(" in(\"rdx\") $arg2,\n") + out.write(" in(\"r10\") $arg3,\n") + out.write(" in(\"r8\") $arg4,\n") + out.write(" in(\"r9\") $arg5,\n") + out.write(" out(\"rcx\") _,\n") + out.write(" out(\"r11\") _,\n") + out.write(" lateout(\"rax\") ret\n") + out.write(" );\n") + out.write(" ret\n") + out.write(" }\n") out.write(" }}\n") out.write("}\n") out.write("macro_rules! mem {\n") @@ -1788,68 +1878,68 @@ def generate_rust(program: Program, out_file_path: str): assert False, "todo" out.write(" ;; -- cast(ptr) --\n") elif op.operand == Intrinsic.SYSCALL0: - assert False, "todo" - out.write(" ;; -- syscall0 --\n") - out.write(" pop rax\n") - out.write(" syscall\n") - out.write(" push rax\n") + _, num = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d);\n" % (var, num)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL1: - assert False, "todo" - out.write(" ;; -- syscall1 --\n") - out.write(" pop rax\n") - out.write(" pop rdi\n") - out.write(" syscall\n") - out.write(" push rax\n") + _, num = stack.pop() + _, arg0 = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d);\n" % (var, num, arg0)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL2: - assert False, "todo" - out.write(" ;; -- syscall2 -- \n") - out.write(" pop rax\n"); - out.write(" pop rdi\n"); - out.write(" pop rsi\n"); - out.write(" syscall\n"); - out.write(" push rax\n") + _, num = stack.pop() + _, arg0 = stack.pop() + _, arg1 = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d);\n" % (var, num, arg0, arg1)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL3: - _, num = stack.pop(); - _, arg0 = stack.pop(); - _, arg1 = stack.pop(); - _, arg2 = stack.pop(); + _, num = stack.pop() + _, arg0 = stack.pop() + _, arg1 = stack.pop() + _, arg2 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = syscall3!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL4: - assert False, "todo" - out.write(" ;; -- syscall4 --\n") - out.write(" pop rax\n") - out.write(" pop rdi\n") - out.write(" pop rsi\n") - out.write(" pop rdx\n") - out.write(" pop r10\n") - out.write(" syscall\n") - out.write(" push rax\n") + _, num = stack.pop(); + _, arg0 = stack.pop() + _, arg1 = stack.pop() + _, arg2 = stack.pop() + _, arg3 = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL5: - assert False, "todo" - out.write(" ;; -- syscall5 --\n") - out.write(" pop rax\n") - out.write(" pop rdi\n") - out.write(" pop rsi\n") - out.write(" pop rdx\n") - out.write(" pop r10\n") - out.write(" pop r8\n") - out.write(" syscall\n") - out.write(" push rax\n") + _, num = stack.pop() + _, arg0 = stack.pop() + _, arg1 = stack.pop() + _, arg2 = stack.pop() + _, arg3 = stack.pop() + _, arg4 = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3, arg4)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL6: - assert False, "todo" - out.write(" ;; -- syscall6 --\n") - out.write(" pop rax\n") - out.write(" pop rdi\n") - out.write(" pop rsi\n") - out.write(" pop rdx\n") - out.write(" pop r10\n") - out.write(" pop r8\n") - out.write(" pop r9\n") - out.write(" syscall\n") - out.write(" push rax\n") + _, num = stack.pop(); + _, arg0 = stack.pop(); + _, arg1 = stack.pop(); + _, arg2 = stack.pop(); + _, arg3 = stack.pop(); + _, arg4 = stack.pop(); + _, arg5 = stack.pop(); + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3, arg4, arg5)) + stack.append((DataType.INT, var)) else: assert False, "unreachable" else: From d44195ecedeb1b90a437fd5b4c39ef8c5adf8972 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Wed, 6 Oct 2021 17:11:08 +0300 Subject: [PATCH 09/24] Translator: Remove redundant type annotations --- porth.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/porth.py b/porth.py index 0995d785..e423fc47 100755 --- a/porth.py +++ b/porth.py @@ -1708,21 +1708,21 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = v%d + v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d + v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MINUS: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = v%d - v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d - v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MUL: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = v%d * v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d * v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.DIVMOD: _, op0 = stack.pop() @@ -1730,12 +1730,12 @@ def generate_rust(program: Program, out_file_path: str): var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = v%d / v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d / v%d;\n" % (var, op1, op0)) div = var var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = v%d %% v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d %% v%d;\n" % (var, op1, op0)) mod = var stack.append((DataType.INT, div)) @@ -1782,42 +1782,42 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d == v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d == v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GT: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d > v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d > v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LT: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d < v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d < v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d >= v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d >= v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d <= v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d <= v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.NE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: bool = v%d != v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d != v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.DUP: arg = stack.pop() From e97fe0729efe2768e3e9aab34212ec958e85ec7b Mon Sep 17 00:00:00 2001 From: Bohdan Date: Thu, 7 Oct 2021 03:02:44 +0300 Subject: [PATCH 10/24] Translator: Implement command line arguments --- porth.py | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/porth.py b/porth.py index e423fc47..abd9cf50 100755 --- a/porth.py +++ b/porth.py @@ -1559,8 +1559,30 @@ def generate_rust(program: Program, out_file_path: str): out.write("}\n") out.write("static mut MEMORY: [u8;640_000] = [0;640_000];\n") + out.write("static mut MEMORY_POINTER: u64 = 0;\n") out.write("fn main() {\n") + out.write(" let args = std::env::args();\n") + out.write(" let argc = args.len() as u64;\n") + out.write(" let argv;\n") + out.write(" unsafe {\n") + out.write(" MEMORY_POINTER = MEMORY.as_ptr() as usize as u64;\n") + out.write(" argv = MEMORY_POINTER;\n") + out.write(" //Allocate memory for argv pointers\n") + out.write(" MEMORY_POINTER += 8 * argc as u64;\n") + out.write(" for (n, arg) in args.enumerate() {\n") + out.write(" let arg = arg.to_string();\n") + out.write(" let bs = arg.as_bytes();\n") + out.write(" *(MEMORY[8*n..].as_ptr() as *mut u64) = MEMORY_POINTER;\n") + out.write(" MEMORY[(MEMORY_POINTER as usize - MEMORY.as_ptr() as usize)..][..bs.len() as usize].copy_from_slice(bs);\n") + out.write("\n") + out.write(" MEMORY_POINTER += bs.len() as u64 /* null termination */ + 1;\n") + out.write(" }\n") + out.write(" }\n") + out.write(" let user_memory = unsafe {MEMORY_POINTER} as usize as u64;\n") + out.write(" \n\n\n\n") + + for ip in range(len(program)): op = program[ip] assert len(OpType) == 8, "Exhaustive ops handling in generate_rust" @@ -1840,11 +1862,11 @@ def generate_rust(program: Program, out_file_path: str): elif op.operand == Intrinsic.MEM: var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = mem!(MEMORY);\n" % var) + out.write((" " * offset) + "let v%d = user_memory;\n" % var) stack.append((DataType.PTR, var)) elif op.operand == Intrinsic.LOAD: _, op0 = stack.pop() - var = var_id; + var = var_id var_id += 1 out.write((" " * offset) + "let v%d = load8!(v%d);\n" % (var, op0)) stack.append((DataType.INT, var)) @@ -1853,17 +1875,15 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.ARGC: - assert False, "todo" - out.write(" ;; -- argc --\n") - out.write(" mov rax, [args_ptr]\n") - out.write(" mov rax, [rax]\n") - out.write(" push rax\n") + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d = argc;\n" % var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.ARGV: - assert False, "todo" - out.write(" ;; -- argv --\n") - out.write(" mov rax, [args_ptr]\n") - out.write(" add rax, 8\n") - out.write(" push rax\n") + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d = argv;\n" % var) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.LOAD64: _, op0 = stack.pop() var = var_id; @@ -1875,8 +1895,8 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() out.write((" " * offset) + "store64!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.CAST_PTR: - assert False, "todo" - out.write(" ;; -- cast(ptr) --\n") + # Do nothing + pass elif op.operand == Intrinsic.SYSCALL0: _, num = stack.pop() var = var_id From 31dfb1967b6a5dc0a824a9a74eda5478995370e3 Mon Sep 17 00:00:00 2001 From: Bohdan Date: Thu, 7 Oct 2021 17:11:32 +0300 Subject: [PATCH 11/24] Translator: while loop handled correctly --- porth.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/porth.py b/porth.py index abd9cf50..681d3267 100755 --- a/porth.py +++ b/porth.py @@ -1711,15 +1711,45 @@ def generate_rust(program: Program, out_file_path: str): var_id += 1 out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[1])) new_stack.append((v[0], new_v)) - contexts.append((OpType.WHILE, copy(new_stack), [])) stack = new_stack + contexts.append((OpType.WHILE, copy(stack), [])) buffers.append(out) out = StringIO() out.write((" " * offset) + "loop {\n") offset += 1 elif op.typ == OpType.DO: - out.write((" " * offset) + "if !v%d {\n" % stack.pop()[1]) + + if_arg = stack.pop() + _, while_context, _ = contexts.pop() + + old_out = out + out = buffers.pop(); + + while_vars = map(lambda x: x[1], while_context) + new_stack: List[int] = [] + for v in stack: + if v[1] in while_vars: + new_stack.append(v) + else: + new_v = var_id + var_id += 1 + out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) + new_stack.append((v[0], new_v)) + contexts.append((OpType.WHILE, copy(new_stack), [])) + + out.write(old_out.getvalue()) + old_out.close() + + for (const, mut) in zip(stack, new_stack): + out.write((" " * offset) + "v%d = v%d;\n" % (mut[1], const[1])) + + stack = new_stack + + buffers.append(out) + out = StringIO() + + out.write((" " * offset) + "if !v%d {\n" % if_arg[1]) out.write((" " * (offset + 1)) + "break\n") out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" From 40af6461b23b928960ac328636d3b55105e210ca Mon Sep 17 00:00:00 2001 From: Bohdan Date: Thu, 7 Oct 2021 17:19:52 +0300 Subject: [PATCH 12/24] Translator: Handle negative constants --- porth.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/porth.py b/porth.py index 681d3267..5bc64a49 100755 --- a/porth.py +++ b/porth.py @@ -1590,7 +1590,10 @@ def generate_rust(program: Program, out_file_path: str): assert isinstance(op.operand, int), "This could be a bug in the compilation step" var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: u64 = %d;\n" % (var, op.operand)) + if op.operand < 0: + out.write((" " * offset) + "let v%d: u64 = %d as i64 as u64;\n" % (var, op.operand)) + else: + out.write((" " * offset) + "let v%d: u64 = %d;\n" % (var, op.operand)) stack.append((DataType.INT, var)) elif op.typ == OpType.PUSH_STR: assert isinstance(op.operand, str), "This could be a bug in the compilation step" From ebd102a787fbc8bb35b425276beed247e32960a2 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Fri, 8 Oct 2021 10:15:45 +0300 Subject: [PATCH 13/24] Translator: cleanup redundant initializations --- porth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/porth.py b/porth.py index 5bc64a49..fad716fa 100755 --- a/porth.py +++ b/porth.py @@ -1653,7 +1653,7 @@ def generate_rust(program: Program, out_file_path: str): for new_v_val in stack[len(prev_context):]: new_v = var_id var_id += 1 - out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) + out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack_entries.append((new_v_val[0], new_v)) out.write(old_out.getvalue()) @@ -1737,7 +1737,7 @@ def generate_rust(program: Program, out_file_path: str): else: new_v = var_id var_id += 1 - out.write((" " * (offset - 1)) + "let mut v%d = Default::default();\n" % new_v) + out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack.append((v[0], new_v)) contexts.append((OpType.WHILE, copy(new_stack), [])) From 63bab87ae5c6b762e1f22006ddb17a0f4611bf11 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Fri, 8 Oct 2021 10:16:36 +0300 Subject: [PATCH 14/24] Translator: Enable optimization for rustc --- porth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/porth.py b/porth.py index fad716fa..355eb686 100755 --- a/porth.py +++ b/porth.py @@ -2463,7 +2463,7 @@ def usage(compiler_name: str): cmd_call_echoed(["ld", "-o", basepath, basepath + ".o"], silent) elif subcommand == "translate": generate_rust(program, basepath + ".rs") - cmd_call_echoed(["rustc", "-o", basepath + "_rs", basepath + ".rs"], silent) + cmd_call_echoed(["rustc", "-C", "opt-level=3", "-o", basepath + "_rs", basepath + ".rs"], silent) else: assert False, "unreacheble" if run: From 598d4fdb8c556a2b6b96c5298acee149b3a7d920 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Sun, 10 Oct 2021 17:27:07 +0300 Subject: [PATCH 15/24] WIP --- porth.py | 78 +++++++++++++++++++++++++++++------------------ tests/while.porth | 4 --- tests/while.txt | 18 ----------- 3 files changed, 48 insertions(+), 52 deletions(-) delete mode 100644 tests/while.porth delete mode 100644 tests/while.txt diff --git a/porth.py b/porth.py index 355eb686..89f4d401 100755 --- a/porth.py +++ b/porth.py @@ -1409,9 +1409,9 @@ def generate_nasm_linux_x86_64(program: Program, out_file_path: str): def generate_rust(program: Program, out_file_path: str): var_id = 0 offset = 1 - stack: List[Triple[DataType, int]] = [] - contexts: List[Triple[OpType, List[int], List[int]]] = [] - buffers = [] + stack: List[Tuple[DataType, int]] = [] + contexts: List[Tuple[OpType, List[int]]] = [] # (op, stack) + buffers: List[Union[TextIO, StringIO]] = [] with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") out.write("#![allow(unused)]\n") @@ -1616,15 +1616,15 @@ def generate_rust(program: Program, out_file_path: str): stack.append((DataType.PTR, str_addr)) elif op.typ == OpType.IF: _, cond = stack.pop() - new_stack: List[Triple[DataType,int]] = [] + new_stack: List[Tuple[DataType,int]] = [] for v in stack: new_v = var_id var_id += 1 new_stack.append((v[0], new_v)) - contexts.append((OpType.IF, copy(new_stack), [])) + contexts.append((OpType.IF, copy(new_stack))) out.write((" " * offset) + "//Storing stack\n") - for (new_v, new_v_val) in zip(new_stack, stack): + for new_v, new_v_val in zip(new_stack, stack): out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v[1], new_v_val[1])) stack = new_stack @@ -1635,13 +1635,13 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "if v%d {\n" % cond) offset += 1 elif op.typ == OpType.ELSE: - prev_op, prev_context, _ = contexts.pop() + prev_op, start_if_stack = contexts.pop() out.write((" " * offset) + "//Sync stack\n") - for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + for prev, curr in zip(start_if_stack, stack[:len(start_if_stack)]): if curr != prev: out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) - stack[:len(prev_context)] = copy(prev_context) + stack[:len(start_if_stack)] = copy(start_if_stack) old_out = out out = buffers.pop() @@ -1650,57 +1650,66 @@ def generate_rust(program: Program, out_file_path: str): new_stack_entries = [] # Handle new stack entries out.write((" " * (offset - 1)) + "//New stack entries\n") - for new_v_val in stack[len(prev_context):]: + for new_v_val in stack[len(start_if_stack):]: new_v = var_id var_id += 1 out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack_entries.append((new_v_val[0], new_v)) + assert isinstance(old_out, StringIO), "Should be StringIO" out.write(old_out.getvalue()) old_out.close() - for (new_v_val, new_v) in zip(stack[len(prev_context):], new_stack_entries): + for new_v_val, new_v in zip(stack[len(start_if_stack):], new_stack_entries): out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) - stack = copy(prev_context) + stack = copy(start_if_stack) offset -= 1 out.write((" " * offset) + "}\n") out.write((" " * offset) + "else {\n") offset += 1 - contexts.append((OpType.ELSE, copy(prev_context), new_stack_entries)) + contexts.append((OpType.ELSE, copy(start_if_stack)+new_stack_entries)) assert isinstance(op.operand, int), "This could be a bug in the compilation step" elif op.typ == OpType.END: - prev_op, prev_context, new_stack_entries = contexts.pop() + prev_op, prev_stack = contexts.pop() - if prev_op == OpType.IF or prev_op == OpType.WHILE: + if prev_op == OpType.IF or prev_op == OpType.DO: old_out = out out = buffers.pop(); + assert isinstance(old_out, StringIO), "Should be StringIO" out.write(old_out.getvalue()) old_out.close() - for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): + out.write((" " * offset) + "//END: %s\n" % ('if' if prev_op == OpType.IF else 'do')) + out.write((" " * offset) + "//Sync stack\n//Prev: %s\n//Curr: %s\n" % (prev_stack, stack)) + for prev, curr in zip(prev_stack, stack[:len(prev_stack)]): if curr != prev: out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) - stack = copy(prev_context) + stack = copy(prev_stack) + elif prev_op == OpType.ELSE: - # Handle new stack entries + else_end_stack = stack + else_start_stack = prev_stack - new_stack = copy(prev_context) - for (new_v, new_v_val) in zip(new_stack_entries, stack[len(prev_context):]): - out.write((" " * offset) + "v%d = v%d;\n" % (new_v[1], new_v_val[1])) - new_stack.append(new_v) - + # Sync existing entries + existing = min(len(else_stack_start), len(else_stack_end)) out.write((" " * offset) + "//Sync stack\n") - for (prev, curr) in zip(prev_context, stack[:len(prev_context)]): - if curr != prev: - out.write((" " * offset) + "v%d = v%d;\n" % (prev[1], curr[1])) + for i in range(0, existing): + start = else_stack_start[i][1] + end = else_stack_end[i][1] + #if start != end: + out.write((" " * offset) + "v%d = v%d;\n" % (start, end)) + + # Drop dropped entries + out.write((" " * offset) + "//Drop dropped entries\n") + if len(else_end_stack) < len(else_start_stack): + for i in range(len(else_end_stack), len(else_start_stack)): + out.write((" " * offset) + "let _ = v%d;\n" % else_start_stack[i][1]) - stack = copy(prev_context) - stack = new_stack else: assert False, "unreachable" @@ -1739,12 +1748,15 @@ def generate_rust(program: Program, out_file_path: str): var_id += 1 out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack.append((v[0], new_v)) - contexts.append((OpType.WHILE, copy(new_stack), [])) + + out.write((" " * (offset - 1)) + "//New Stack: %s\n" % new_stack) + contexts.append((OpType.DO, copy(new_stack), [])) + assert isinstance(old_out, StringIO), "Should be StringIO" out.write(old_out.getvalue()) old_out.close() - for (const, mut) in zip(stack, new_stack): + for const, mut in zip(stack, new_stack): out.write((" " * offset) + "v%d = v%d;\n" % (mut[1], const[1])) stack = new_stack @@ -2000,6 +2012,12 @@ def generate_rust(program: Program, out_file_path: str): out.write("}") + if len(stack) != 0: + assert False, "Stack is not empty" + if len(contexts) != 0: + assert False, "Contexts is not empty" + + assert len(Keyword) == 7, "Exhaustive KEYWORD_NAMES definition." KEYWORD_NAMES = { diff --git a/tests/while.porth b/tests/while.porth deleted file mode 100644 index ba469a83..00000000 --- a/tests/while.porth +++ /dev/null @@ -1,4 +0,0 @@ -10 while dup 0 > do - dup print - 1 - -end drop diff --git a/tests/while.txt b/tests/while.txt deleted file mode 100644 index 64c18fc5..00000000 --- a/tests/while.txt +++ /dev/null @@ -1,18 +0,0 @@ -:i argc 0 -:b stdin 0 - -:i returncode 0 -:b stdout 21 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 - -:b stderr 0 - From 978ed9f8207c1f8ac49311f476049fa6fca98230 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Sun, 10 Oct 2021 17:32:19 +0300 Subject: [PATCH 16/24] Update gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7600bbdd..a34d77a7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ test __pycache__/ *.asm *.o -porth \ No newline at end of file +porth +*.rs +*_rs From efa96533ec6be45750604e375da2e1341a1ec854 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Sun, 10 Oct 2021 18:21:30 +0300 Subject: [PATCH 17/24] WIP --- porth.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/porth.py b/porth.py index 89f4d401..7d727821 100755 --- a/porth.py +++ b/porth.py @@ -1696,20 +1696,26 @@ def generate_rust(program: Program, out_file_path: str): else_start_stack = prev_stack # Sync existing entries - existing = min(len(else_stack_start), len(else_stack_end)) + existing = min(len(else_start_stack), len(else_end_stack)) out.write((" " * offset) + "//Sync stack\n") for i in range(0, existing): - start = else_stack_start[i][1] - end = else_stack_end[i][1] + start = else_start_stack[i][1] + end = else_end_stack[i][1] #if start != end: out.write((" " * offset) + "v%d = v%d;\n" % (start, end)) + # Drop dropped entries out.write((" " * offset) + "//Drop dropped entries\n") if len(else_end_stack) < len(else_start_stack): for i in range(len(else_end_stack), len(else_start_stack)): out.write((" " * offset) + "let _ = v%d;\n" % else_start_stack[i][1]) + # Set previus variables + stack = copy(else_start_stack) + if len(else_end_stack) < len(else_start_stack): + stack = stack[:len(else_end_stack)] + else: assert False, "unreachable" @@ -1724,7 +1730,7 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[1])) new_stack.append((v[0], new_v)) stack = new_stack - contexts.append((OpType.WHILE, copy(stack), [])) + contexts.append((OpType.WHILE, copy(stack))) buffers.append(out) out = StringIO() @@ -1733,7 +1739,7 @@ def generate_rust(program: Program, out_file_path: str): elif op.typ == OpType.DO: if_arg = stack.pop() - _, while_context, _ = contexts.pop() + _, while_context = contexts.pop() old_out = out out = buffers.pop(); @@ -1750,7 +1756,7 @@ def generate_rust(program: Program, out_file_path: str): new_stack.append((v[0], new_v)) out.write((" " * (offset - 1)) + "//New Stack: %s\n" % new_stack) - contexts.append((OpType.DO, copy(new_stack), [])) + contexts.append((OpType.DO, copy(new_stack))) assert isinstance(old_out, StringIO), "Should be StringIO" out.write(old_out.getvalue()) From 66139fddd238dff71343e7b116c366f90e22da21 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 01:45:16 +0300 Subject: [PATCH 18/24] Translator: fix typo --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index b55c3244..78640885 100755 --- a/test.py +++ b/test.py @@ -143,7 +143,7 @@ def run_test_for_folder(folder: str): if entry.is_file() and entry.path.endswith(PORTH_EXT): run_test_for_file(entry.path, stats) print() - print("Simulation failed: %d, Compilation failed: %d, Translate failed: %d, Ignored: %d" % (stats.sim_failed, stats.com_failed, stats.translate_failed stats.ignored)) + print("Simulation failed: %d, Compilation failed: %d, Translate failed: %d, Ignored: %d" % (stats.sim_failed, stats.com_failed, stats.translate_failed, stats.ignored)) if stats.sim_failed != 0 or stats.com_failed != 0 or stats.translate_failed: exit(1) From 2d7ae39d444341046f0ec5e6435eff5dbc65478e Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 01:42:40 +0300 Subject: [PATCH 19/24] Translator: All comparisons in porth in signed --- porth.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/porth.py b/porth.py index 7d727821..f759f54e 100755 --- a/porth.py +++ b/porth.py @@ -1862,28 +1862,28 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d > v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d as i64 > v%d as i64;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LT: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d < v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d as i64 < v%d as i64;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d >= v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d as i64 >= v%d as i64;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d <= v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d as i64 <= v%d as i64;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.NE: _, op0 = stack.pop() From b3f4194fc6abfb58c497f90b88902c260b037707 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 04:42:42 +0300 Subject: [PATCH 20/24] Translator: Implement intrinsics FORTH_{LOAD,LOAD64,STORE,STORE64}, ROT, NOT --- porth.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/porth.py b/porth.py index f759f54e..f8487c97 100755 --- a/porth.py +++ b/porth.py @@ -1775,7 +1775,7 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "}\n") assert isinstance(op.operand, int), "This could be a bug in the compilation step" elif op.typ == OpType.INTRINSIC: - assert len(Intrinsic) == 34, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" + assert len(Intrinsic) == 41, "Exhaustive intrinsic handling in generate_nasm_linux_x86_64()" if op.operand == Intrinsic.PLUS: _, op0 = stack.pop() _, op1 = stack.pop() @@ -1828,20 +1828,26 @@ def generate_rust(program: Program, out_file_path: str): var_id += 1 out.write((" " * offset) + "let v%d = v%d << v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) - elif op.operand == Intrinsic.BOR: + elif op.operand == Intrinsic.OR: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d = v%d | v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) - elif op.operand == Intrinsic.BAND: + elif op.operand == Intrinsic.AND: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 out.write((" " * offset) + "let v%d = v%d & v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) + elif op.operand == Intrinsic.NOT: + _, op = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = !v%d;\n" % (var, op)) + stack.append((DataType.INT, var)) elif op.operand == Intrinsic.PRINT: typ, var = stack.pop() if typ == DataType.INT or typ == DataType.PTR: @@ -1862,28 +1868,28 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d as i64 > v%d as i64;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = (v%d as i64) > (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LT: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d as i64 < v%d as i64;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = (v%d as i64) < (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d as i64 >= v%d as i64;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = (v%d as i64) >= (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LE: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d as i64 <= v%d as i64;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = (v%d as i64) <= (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.NE: _, op0 = stack.pop() @@ -1910,6 +1916,13 @@ def generate_rust(program: Program, out_file_path: str): stack.append(arg1); stack.append(arg0); stack.append(arg1); + elif op.operand == Intrinsic.ROT: + arg0 = stack.pop() + arg1 = stack.pop() + arg2 = stack.pop() + stack.append(arg1); + stack.append(arg0); + stack.append(arg2); elif op.operand == Intrinsic.MEM: var = var_id; var_id += 1 @@ -1925,6 +1938,16 @@ def generate_rust(program: Program, out_file_path: str): _, op0 = stack.pop() _, op1 = stack.pop() out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op1, op0)) + elif op.operand == Intrinsic.FORTH_LOAD: + _, op0 = stack.pop() + var = var_id + var_id += 1 + out.write((" " * offset) + "let v%d = load8!(v%d);\n" % (var, op0)) + stack.append((DataType.INT, var)) + elif op.operand == Intrinsic.FORTH_STORE: + _, op0 = stack.pop() + _, op1 = stack.pop() + out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op0, op1)) elif op.operand == Intrinsic.ARGC: var = var_id var_id += 1 @@ -1945,9 +1968,38 @@ def generate_rust(program: Program, out_file_path: str): _, op0 = stack.pop() _, op1 = stack.pop() out.write((" " * offset) + "store64!(v%d, v%d);\n" % (op1, op0)) + elif op.operand == Intrinsic.FORTH_LOAD64: + _, op0 = stack.pop() + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d = load64!(v%d);\n" % (var, op0)) + stack.append((DataType.INT, var)) + elif op.operand == Intrinsic.FORTH_STORE64: + _, op0 = stack.pop() + _, op1 = stack.pop() + out.write((" " * offset) + "store64!(v%d, v%d);\n" % (op0, op1)) elif op.operand == Intrinsic.CAST_PTR: # Do nothing pass + elif op.operand == Intrinsic.HERE: + here_string = "%s:%d:%d" % op.token.loc + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: &'static str = r#\"%s\"#;\n" % (var, here_string)) + str_const = var + + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) + str_len = var + + var = var_id; + var_id += 1 + out.write((" " * offset) + "let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) + str_addr = var + + stack.append((DataType.INT, str_len)) + stack.append((DataType.PTR, str_addr)) elif op.operand == Intrinsic.SYSCALL0: _, num = stack.pop() var = var_id From ce05fe4cf32d8d45b6f180edd2e1671dc8908e5e Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 05:01:47 +0300 Subject: [PATCH 21/24] Translator: Fix test runner --- test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test.py b/test.py index 78640885..5e13a388 100755 --- a/test.py +++ b/test.py @@ -126,9 +126,9 @@ def run_test_for_file(file_path: str, stats: RunStats = RunStats()): print(" stdout: \n%s" % tc.stdout.decode("utf-8")) print(" stderr: \n%s" % tc.stderr.decode("utf-8")) print(" Actual:") - print(" return code: %s" % com.returncode) - print(" stdout: \n%s" % com.stdout.decode("utf-8")) - print(" stderr: \n%s" % com.stderr.decode("utf-8")) + print(" return code: %s" % translate.returncode) + print(" stdout: \n%s" % translate.stdout.decode("utf-8")) + print(" stderr: \n%s" % translate.stderr.decode("utf-8")) stats.translate_failed += 1 else: print('[WARNING] Could not find any input/output data for %s. Ignoring testing. Only checking if it compiles.' % file_path) From 96654932e3544a7df56ec7f1d0ae531a47d7252d Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 14:07:44 +0300 Subject: [PATCH 22/24] Translator: Add and sub operations is allow to wrap --- porth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/porth.py b/porth.py index f8487c97..bb1297ee 100755 --- a/porth.py +++ b/porth.py @@ -1781,14 +1781,14 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d + v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d.wrapping_add(v%d);\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MINUS: _, op0 = stack.pop() _, op1 = stack.pop() var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d = v%d - v%d;\n" % (var, op1, op0)) + out.write((" " * offset) + "let v%d = v%d.wrapping_sub(v%d);\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MUL: _, op0 = stack.pop() From 6fef6ee81b01e01004a63babd1ca636df40a7d3b Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 14:39:01 +0300 Subject: [PATCH 23/24] Translator: Proper escaping for string constants --- porth.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/porth.py b/porth.py index bb1297ee..c44a296c 100755 --- a/porth.py +++ b/porth.py @@ -1412,6 +1412,9 @@ def generate_rust(program: Program, out_file_path: str): stack: List[Tuple[DataType, int]] = [] contexts: List[Tuple[OpType, List[int]]] = [] # (op, stack) buffers: List[Union[TextIO, StringIO]] = [] + + escape = lambda s: s.replace("\n", "\\n").replace("\"", "\\\"").replace("\0", "\\0") + with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") out.write("#![allow(unused)]\n") @@ -1599,7 +1602,7 @@ def generate_rust(program: Program, out_file_path: str): assert isinstance(op.operand, str), "This could be a bug in the compilation step" var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: &'static str = r#\"%s\"#;\n" % (var, op.operand)) + out.write((" " * offset) + "let v%d: &'static str = \"%s\";\n" % (var, escape(op.operand))) str_const = var var = var_id; @@ -1982,10 +1985,10 @@ def generate_rust(program: Program, out_file_path: str): # Do nothing pass elif op.operand == Intrinsic.HERE: - here_string = "%s:%d:%d" % op.token.loc + here_string = escape("%s:%d:%d" % op.token.loc) var = var_id; var_id += 1 - out.write((" " * offset) + "let v%d: &'static str = r#\"%s\"#;\n" % (var, here_string)) + out.write((" " * offset) + "let v%d: &'static str = \"%s\";\n" % (var, here_string)) str_const = var var = var_id; From 7a4134b2e207b8d21e1dac70a093baaa6a0cae30 Mon Sep 17 00:00:00 2001 From: Bohdan Fedorov Date: Mon, 11 Oct 2021 14:54:33 +0300 Subject: [PATCH 24/24] Translator: Cleanup variable allocation --- porth.py | 129 ++++++++++++++++++++----------------------------------- 1 file changed, 47 insertions(+), 82 deletions(-) diff --git a/porth.py b/porth.py index c44a296c..e450ebc2 100755 --- a/porth.py +++ b/porth.py @@ -1415,6 +1415,12 @@ def generate_rust(program: Program, out_file_path: str): escape = lambda s: s.replace("\n", "\\n").replace("\"", "\\\"").replace("\0", "\\0") + def alloc_var(): + nonlocal var_id + var = var_id + var_id += 1 + return var + with open(out_file_path, "w") as out: out.write("#![feature(asm)]\n") out.write("#![allow(unused)]\n") @@ -1591,8 +1597,7 @@ def generate_rust(program: Program, out_file_path: str): assert len(OpType) == 8, "Exhaustive ops handling in generate_rust" if op.typ == OpType.PUSH_INT: assert isinstance(op.operand, int), "This could be a bug in the compilation step" - var = var_id; - var_id += 1 + var = alloc_var() if op.operand < 0: out.write((" " * offset) + "let v%d: u64 = %d as i64 as u64;\n" % (var, op.operand)) else: @@ -1600,18 +1605,15 @@ def generate_rust(program: Program, out_file_path: str): stack.append((DataType.INT, var)) elif op.typ == OpType.PUSH_STR: assert isinstance(op.operand, str), "This could be a bug in the compilation step" - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: &'static str = \"%s\";\n" % (var, escape(op.operand))) str_const = var - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) str_len = var - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) str_addr = var @@ -1621,8 +1623,7 @@ def generate_rust(program: Program, out_file_path: str): _, cond = stack.pop() new_stack: List[Tuple[DataType,int]] = [] for v in stack: - new_v = var_id - var_id += 1 + new_v = alloc_var() new_stack.append((v[0], new_v)) contexts.append((OpType.IF, copy(new_stack))) @@ -1654,8 +1655,7 @@ def generate_rust(program: Program, out_file_path: str): # Handle new stack entries out.write((" " * (offset - 1)) + "//New stack entries\n") for new_v_val in stack[len(start_if_stack):]: - new_v = var_id - var_id += 1 + new_v = alloc_var() out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack_entries.append((new_v_val[0], new_v)) @@ -1728,8 +1728,7 @@ def generate_rust(program: Program, out_file_path: str): elif op.typ == OpType.WHILE: new_stack: List[int] = [] for v in stack: - new_v = var_id - var_id += 1 + new_v = alloc_var() out.write((" " * offset) + "let mut v%d = v%d;\n" % (new_v, v[1])) new_stack.append((v[0], new_v)) stack = new_stack @@ -1753,8 +1752,7 @@ def generate_rust(program: Program, out_file_path: str): if v[1] in while_vars: new_stack.append(v) else: - new_v = var_id - var_id += 1 + new_v = alloc_var() out.write((" " * (offset - 1)) + "let mut v%d;\n" % new_v) new_stack.append((v[0], new_v)) @@ -1782,35 +1780,30 @@ def generate_rust(program: Program, out_file_path: str): if op.operand == Intrinsic.PLUS: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d.wrapping_add(v%d);\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MINUS: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d.wrapping_sub(v%d);\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.MUL: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d * v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.DIVMOD: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d / v%d;\n" % (var, op1, op0)) div = var - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d %% v%d;\n" % (var, op1, op0)) mod = var @@ -1820,35 +1813,30 @@ def generate_rust(program: Program, out_file_path: str): elif op.operand == Intrinsic.SHR: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d >> v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SHL: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d << v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.OR: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d | v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.AND: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d & v%d;\n" % (var, op1, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.NOT: _, op = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = !v%d;\n" % (var, op)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.PRINT: @@ -1862,43 +1850,37 @@ def generate_rust(program: Program, out_file_path: str): elif op.operand == Intrinsic.EQ: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d == v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GT: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = (v%d as i64) > (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LT: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = (v%d as i64) < (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.GE: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = (v%d as i64) >= (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.LE: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = (v%d as i64) <= (v%d as i64);\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.NE: _, op0 = stack.pop() _, op1 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = v%d != v%d;\n" % (var, op1, op0)) stack.append((DataType.BOOL, var)) elif op.operand == Intrinsic.DUP: @@ -1927,14 +1909,12 @@ def generate_rust(program: Program, out_file_path: str): stack.append(arg0); stack.append(arg2); elif op.operand == Intrinsic.MEM: - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = user_memory;\n" % var) stack.append((DataType.PTR, var)) elif op.operand == Intrinsic.LOAD: _, op0 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = load8!(v%d);\n" % (var, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.STORE: @@ -1943,8 +1923,7 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.FORTH_LOAD: _, op0 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = load8!(v%d);\n" % (var, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.FORTH_STORE: @@ -1952,19 +1931,16 @@ def generate_rust(program: Program, out_file_path: str): _, op1 = stack.pop() out.write((" " * offset) + "store8!(v%d, v%d);\n" % (op0, op1)) elif op.operand == Intrinsic.ARGC: - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = argc;\n" % var) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.ARGV: - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = argv;\n" % var) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.LOAD64: _, op0 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = load64!(v%d);\n" % (var, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.STORE64: @@ -1973,8 +1949,7 @@ def generate_rust(program: Program, out_file_path: str): out.write((" " * offset) + "store64!(v%d, v%d);\n" % (op1, op0)) elif op.operand == Intrinsic.FORTH_LOAD64: _, op0 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d = load64!(v%d);\n" % (var, op0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.FORTH_STORE64: @@ -1986,18 +1961,15 @@ def generate_rust(program: Program, out_file_path: str): pass elif op.operand == Intrinsic.HERE: here_string = escape("%s:%d:%d" % op.token.loc) - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: &'static str = \"%s\";\n" % (var, here_string)) str_const = var - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = v%d.len() as u64;\n" % (var, str_const)) str_len = var - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = v%d.as_ptr() as usize as u64;\n" % (var, str_const)) str_addr = var @@ -2005,23 +1977,20 @@ def generate_rust(program: Program, out_file_path: str): stack.append((DataType.PTR, str_addr)) elif op.operand == Intrinsic.SYSCALL0: _, num = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d);\n" % (var, num)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL1: _, num = stack.pop() _, arg0 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d);\n" % (var, num, arg0)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL2: _, num = stack.pop() _, arg0 = stack.pop() _, arg1 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d);\n" % (var, num, arg0, arg1)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL3: @@ -2029,8 +1998,7 @@ def generate_rust(program: Program, out_file_path: str): _, arg0 = stack.pop() _, arg1 = stack.pop() _, arg2 = stack.pop() - var = var_id; - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL4: @@ -2039,8 +2007,7 @@ def generate_rust(program: Program, out_file_path: str): _, arg1 = stack.pop() _, arg2 = stack.pop() _, arg3 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL5: @@ -2050,8 +2017,7 @@ def generate_rust(program: Program, out_file_path: str): _, arg2 = stack.pop() _, arg3 = stack.pop() _, arg4 = stack.pop() - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3, arg4)) stack.append((DataType.INT, var)) elif op.operand == Intrinsic.SYSCALL6: @@ -2062,8 +2028,7 @@ def generate_rust(program: Program, out_file_path: str): _, arg3 = stack.pop(); _, arg4 = stack.pop(); _, arg5 = stack.pop(); - var = var_id - var_id += 1 + var = alloc_var() out.write((" " * offset) + "let v%d: u64 = syscall!(v%d, v%d, v%d, v%d, v%d, v%d, v%d);\n" % (var, num, arg0, arg1, arg2, arg3, arg4, arg5)) stack.append((DataType.INT, var)) else: