From 84764bb79e98ef3734898b9764eb741aa9fc85e7 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Sun, 28 Apr 2019 20:51:34 +0900 Subject: [PATCH 01/15] Add stub implementation of emit --- src/x86/emit.rs | 394 ++++++++++++++++++++++++++++++++++++++++++++++++ src/x86/mod.rs | 1 + 2 files changed, 395 insertions(+) create mode 100644 src/x86/emit.rs diff --git a/src/x86/emit.rs b/src/x86/emit.rs new file mode 100644 index 0000000..17085c8 --- /dev/null +++ b/src/x86/emit.rs @@ -0,0 +1,394 @@ +use std::collections::HashSet; + +use id; +use id::IdGen; +use std::io::Write; +use syntax::Type; +use x86::asm; +use x86::asm::{Asm, Exp, Fundef, IdOrImm, Prog}; + +/* +open Asm + +external gethi : float -> int32 = "gethi" +external getlo : float -> int32 = "getlo" + +let stackset = ref S.empty (* すでにSaveされた変数の集合 (caml2html: emit_stackset) *) + let stackmap = ref [] (* Saveされた変数の、スタックにおける位置 (caml2html: emit_stackmap) *) + + */ + +type StackSet = HashSet; +type StackMap = Vec; +fn save(x: &str, stackset: &mut StackSet, stackmap: &mut StackMap) { + stackset.insert(x.to_string()); + if stackmap.iter().all(|y| x != y) { + stackmap.push(x.to_string()); + } +} + +fn savef(x: &str, stackset: &mut StackSet, stackmap: &mut StackMap, id_gen: &mut IdGen) { + stackset.insert(x.to_string()); + if stackmap.iter().all(|y| x != y) { + if stackmap.len() % 2 == 1 { + stackmap.push(id_gen.gen_tmp(&Type::Int)); + } + stackmap.push(x.to_string()); + stackmap.push(x.to_string()); + } +} + +fn offset(x: &str, stackmap: &StackMap) -> i32 { + 4 * stackmap.iter().position(|y| x == y).unwrap() as i32 +} + +fn stacksize(stackmap: &StackMap) -> i32 { + asm::align(4 * stackmap.len() as i32) +} + +fn pp_id_or_imm(value: &IdOrImm) -> String { + match value { + IdOrImm::V(ref x) => x.clone(), + IdOrImm::C(i) => format!("${}", i), + } +} + +/// 関数呼び出しのために引数を並べ替える(register shuffling) (caml2html: emit_shuffle) +fn shuffle(sw: &str, xys: &[(String, String)]) -> Vec<(String, String)> { + panic!(); +} +/* +let rec shuffle sw xys = + (* remove identical moves *) + let _, xys = List.partition (fun (x, y) -> x = y) xys in + (* find acyclic moves *) + match List.partition (fun (_, y) -> List.mem_assoc y xys) xys with + | [], [] -> [] + | (x, y) :: xys, [] -> (* no acyclic moves; resolve a cyclic move *) + (y, sw) :: (x, y) :: shuffle sw (List.map + (function + | (y', z) when y = y' -> (sw, z) + | yz -> yz) + xys) + | xys, acyc -> acyc @ shuffle sw xys + */ + +/// 末尾かどうかを表すデータ型 (caml2html: emit_dest) +enum Dest { + Tail, + NonTail(String), +} + +fn g(output: &mut impl Write, asm: Asm) -> Result<(), std::io::Error> { + panic!(); +} +/* +let rec g oc = function (* 命令列のアセンブリ生成 (caml2html: emit_g) *) + | dest, Ans(exp) -> g' oc (dest, exp) + | dest, Let((x, t), exp, e) -> + g' oc (NonTail(x), exp); + g oc (dest, e) + */ +fn g_exp(output: &mut impl Write, exp: Exp) -> Result<(), std::io::Error> { + panic!(); +} +/* +and g' oc = function (* 各命令のアセンブリ生成 (caml2html: emit_gprime) *) + (* 末尾でなかったら計算結果をdestにセット (caml2html: emit_nontail) *) + | NonTail(_), Nop -> () + | NonTail(x), Set(i) -> Printf.fprintf oc "\tmovl\t$%d, %s\n" i x + | NonTail(x), SetL(Id.L(y)) -> Printf.fprintf oc "\tmovl\t$%s, %s\n" y x + | NonTail(x), Mov(y) -> + if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x + | NonTail(x), Neg(y) -> + if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x; + Printf.fprintf oc "\tnegl\t%s\n" x + | NonTail(x), Add(y, z') -> + if V(x) = z' then + Printf.fprintf oc "\taddl\t%s, %s\n" y x + else + (if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x; + Printf.fprintf oc "\taddl\t%s, %s\n" (pp_id_or_imm z') x) + | NonTail(x), Sub(y, z') -> + if V(x) = z' then + (Printf.fprintf oc "\tsubl\t%s, %s\n" y x; + Printf.fprintf oc "\tnegl\t%s\n" x) + else + (if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x; + Printf.fprintf oc "\tsubl\t%s, %s\n" (pp_id_or_imm z') x) + | NonTail(x), Ld(y, V(z), i) -> Printf.fprintf oc "\tmovl\t(%s,%s,%d), %s\n" y z i x + | NonTail(x), Ld(y, C(j), i) -> Printf.fprintf oc "\tmovl\t%d(%s), %s\n" (j * i) y x + | NonTail(_), St(x, y, V(z), i) -> Printf.fprintf oc "\tmovl\t%s, (%s,%s,%d)\n" x y z i + | NonTail(_), St(x, y, C(j), i) -> Printf.fprintf oc "\tmovl\t%s, %d(%s)\n" x (j * i) y + | NonTail(x), FMovD(y) -> + if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x + | NonTail(x), FNegD(y) -> + if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\txorpd\tmin_caml_fnegd, %s\n" x + | NonTail(x), FAddD(y, z) -> + if x = z then + Printf.fprintf oc "\taddsd\t%s, %s\n" y x + else + (if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\taddsd\t%s, %s\n" z x) + | NonTail(x), FSubD(y, z) -> + if x = z then (* [XXX] ugly *) + let ss = stacksize () in + Printf.fprintf oc "\tmovsd\t%s, %d(%s)\n" z ss reg_sp; + if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\tsubsd\t%d(%s), %s\n" ss reg_sp x + else + (if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\tsubsd\t%s, %s\n" z x) + | NonTail(x), FMulD(y, z) -> + if x = z then + Printf.fprintf oc "\tmulsd\t%s, %s\n" y x + else + (if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\tmulsd\t%s, %s\n" z x) + | NonTail(x), FDivD(y, z) -> + if x = z then (* [XXX] ugly *) + let ss = stacksize () in + Printf.fprintf oc "\tmovsd\t%s, %d(%s)\n" z ss reg_sp; + if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\tdivsd\t%d(%s), %s\n" ss reg_sp x + else + (if x <> y then Printf.fprintf oc "\tmovsd\t%s, %s\n" y x; + Printf.fprintf oc "\tdivsd\t%s, %s\n" z x) + | NonTail(x), LdDF(y, V(z), i) -> Printf.fprintf oc "\tmovsd\t(%s,%s,%d), %s\n" y z i x + | NonTail(x), LdDF(y, C(j), i) -> Printf.fprintf oc "\tmovsd\t%d(%s), %s\n" (j * i) y x + | NonTail(_), StDF(x, y, V(z), i) -> Printf.fprintf oc "\tmovsd\t%s, (%s,%s,%d)\n" x y z i + | NonTail(_), StDF(x, y, C(j), i) -> Printf.fprintf oc "\tmovsd\t%s, %d(%s)\n" x (j * i) y + | NonTail(_), Comment(s) -> Printf.fprintf oc "\t# %s\n" s + (* 退避の仮想命令の実装 (caml2html: emit_save) *) + | NonTail(_), Save(x, y) when List.mem x allregs && not (S.mem y !stackset) -> + save y; + Printf.fprintf oc "\tmovl\t%s, %d(%s)\n" x (offset y) reg_sp + | NonTail(_), Save(x, y) when List.mem x allfregs && not (S.mem y !stackset) -> + savef y; + Printf.fprintf oc "\tmovsd\t%s, %d(%s)\n" x (offset y) reg_sp + | NonTail(_), Save(x, y) -> assert (S.mem y !stackset); () + (* 復帰の仮想命令の実装 (caml2html: emit_restore) *) + | NonTail(x), Restore(y) when List.mem x allregs -> + Printf.fprintf oc "\tmovl\t%d(%s), %s\n" (offset y) reg_sp x + | NonTail(x), Restore(y) -> + assert (List.mem x allfregs); + Printf.fprintf oc "\tmovsd\t%d(%s), %s\n" (offset y) reg_sp x + (* 末尾だったら計算結果を第一レジスタにセットしてret (caml2html: emit_tailret) *) + | Tail, (Nop | St _ | StDF _ | Comment _ | Save _ as exp) -> + g' oc (NonTail(Id.gentmp Type.Unit), exp); + Printf.fprintf oc "\tret\n"; + | Tail, (Set _ | SetL _ | Mov _ | Neg _ | Add _ | Sub _ | Ld _ as exp) -> + g' oc (NonTail(regs.(0)), exp); + Printf.fprintf oc "\tret\n"; + | Tail, (FMovD _ | FNegD _ | FAddD _ | FSubD _ | FMulD _ | FDivD _ | LdDF _ as exp) -> + g' oc (NonTail(fregs.(0)), exp); + Printf.fprintf oc "\tret\n"; + | Tail, (Restore(x) as exp) -> + (match locate x with + | [i] -> g' oc (NonTail(regs.(0)), exp) + | [i; j] when i + 1 = j -> g' oc (NonTail(fregs.(0)), exp) + | _ -> assert false); + Printf.fprintf oc "\tret\n"; + | Tail, IfEq(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_tail_if oc e1 e2 "je" "jne" + | Tail, IfLE(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_tail_if oc e1 e2 "jle" "jg" + | Tail, IfGE(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_tail_if oc e1 e2 "jge" "jl" + | Tail, IfFEq(x, y, e1, e2) -> + Printf.fprintf oc "\tcomisd\t%s, %s\n" y x; + g'_tail_if oc e1 e2 "je" "jne" + | Tail, IfFLE(x, y, e1, e2) -> + Printf.fprintf oc "\tcomisd\t%s, %s\n" y x; + g'_tail_if oc e1 e2 "jbe" "ja" + | NonTail(z), IfEq(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_non_tail_if oc (NonTail(z)) e1 e2 "je" "jne" + | NonTail(z), IfLE(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_non_tail_if oc (NonTail(z)) e1 e2 "jle" "jg" + | NonTail(z), IfGE(x, y', e1, e2) -> + Printf.fprintf oc "\tcmpl\t%s, %s\n" (pp_id_or_imm y') x; + g'_non_tail_if oc (NonTail(z)) e1 e2 "jge" "jl" + | NonTail(z), IfFEq(x, y, e1, e2) -> + Printf.fprintf oc "\tcomisd\t%s, %s\n" y x; + g'_non_tail_if oc (NonTail(z)) e1 e2 "je" "jne" + | NonTail(z), IfFLE(x, y, e1, e2) -> + Printf.fprintf oc "\tcomisd\t%s, %s\n" y x; + g'_non_tail_if oc (NonTail(z)) e1 e2 "jbe" "ja" + (* 関数呼び出しの仮想命令の実装 (caml2html: emit_call) *) + | Tail, CallCls(x, ys, zs) -> (* 末尾呼び出し (caml2html: emit_tailcall) *) + g'_args oc [(x, reg_cl)] ys zs; + Printf.fprintf oc "\tjmp\t*(%s)\n" reg_cl; + | Tail, CallDir(Id.L(x), ys, zs) -> (* 末尾呼び出し *) + g'_args oc [] ys zs; + Printf.fprintf oc "\tjmp\t%s\n" x; + | NonTail(a), CallCls(x, ys, zs) -> + g'_args oc [(x, reg_cl)] ys zs; + let ss = stacksize () in + if ss > 0 then Printf.fprintf oc "\taddl\t$%d, %s\n" ss reg_sp; + Printf.fprintf oc "\tcall\t*(%s)\n" reg_cl; + if ss > 0 then Printf.fprintf oc "\tsubl\t$%d, %s\n" ss reg_sp; + if List.mem a allregs && a <> regs.(0) then + Printf.fprintf oc "\tmovl\t%s, %s\n" regs.(0) a + else if List.mem a allfregs && a <> fregs.(0) then + Printf.fprintf oc "\tmovsd\t%s, %s\n" fregs.(0) a + | NonTail(a), CallDir(Id.L(x), ys, zs) -> + g'_args oc [] ys zs; + let ss = stacksize () in + if ss > 0 then Printf.fprintf oc "\taddl\t$%d, %s\n" ss reg_sp; + Printf.fprintf oc "\tcall\t%s\n" x; + if ss > 0 then Printf.fprintf oc "\tsubl\t$%d, %s\n" ss reg_sp; + if List.mem a allregs && a <> regs.(0) then + Printf.fprintf oc "\tmovl\t%s, %s\n" regs.(0) a + else if List.mem a allfregs && a <> fregs.(0) then + Printf.fprintf oc "\tmovsd\t%s, %s\n" fregs.(0) a + */ + +fn g_exp_tail_if( + output: &mut impl Write, + e1: Exp, + e2: Exp, + b: &str, + bn: &str, +) -> Result<(), std::io::Error> { + panic!(); +} +/* +and g'_tail_if oc e1 e2 b bn = + let b_else = Id.genid (b ^ "_else") in + Printf.fprintf oc "\t%s\t%s\n" bn b_else; + let stackset_back = !stackset in + g oc (Tail, e1); + Printf.fprintf oc "%s:\n" b_else; + stackset := stackset_back; + g oc (Tail, e2) +*/ +fn g_exp_non_tail_if( + output: &mut impl Write, + dest: Dest, + e1: Exp, + e2: Exp, + b: &str, + bn: &str, +) -> Result<(), std::io::Error> { + panic!(); +} +/* +and g'_non_tail_if oc dest e1 e2 b bn = + let b_else = Id.genid (b ^ "_else") in + let b_cont = Id.genid (b ^ "_cont") in + Printf.fprintf oc "\t%s\t%s\n" bn b_else; + let stackset_back = !stackset in + g oc (dest, e1); + let stackset1 = !stackset in + Printf.fprintf oc "\tjmp\t%s\n" b_cont; + Printf.fprintf oc "%s:\n" b_else; + stackset := stackset_back; + g oc (dest, e2); + Printf.fprintf oc "%s:\n" b_cont; + let stackset2 = !stackset in + stackset := S.inter stackset1 stackset2 + */ +fn g_exp_args( + output: &mut impl Write, + x_reg_cl: String, + ys: Vec, + zs: Vec, +) -> Result<(), std::io::Error> { + panic!(); +} +/* +and g'_args oc x_reg_cl ys zs = + assert (List.length ys <= Array.length regs - List.length x_reg_cl); + assert (List.length zs <= Array.length fregs); + let sw = Printf.sprintf "%d(%s)" (stacksize ()) reg_sp in + let (i, yrs) = + List.fold_left + (fun (i, yrs) y -> (i + 1, (y, regs.(i)) :: yrs)) + (0, x_reg_cl) + ys in + List.iter + (fun (y, r) -> Printf.fprintf oc "\tmovl\t%s, %s\n" y r) + (shuffle sw yrs); + let (d, zfrs) = + List.fold_left + (fun (d, zfrs) z -> (d + 1, (z, fregs.(d)) :: zfrs)) + (0, []) + zs in + List.iter + (fun (z, fr) -> Printf.fprintf oc "\tmovsd\t%s, %s\n" z fr) + (shuffle sw zfrs) + */ + +fn h( + output: &mut impl Write, + Fundef { + name: id::L(x), + args: ys, + fargs: zs, + body: e, + ret: t, + }: Fundef, + id_gen: &mut IdGen, +) -> Result<(), std::io::Error> { + panic!(); +} +/* +let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = + Printf.fprintf oc "%s:\n" x; + stackset := S.empty; + stackmap := []; + g oc (Tail, e) +*/ + +pub fn f( + output: &mut impl Write, + Prog(data, fundefs, e): Prog, + id_gen: &mut IdGen, +) -> Result<(), std::io::Error> { + panic!(); +} +/* +let f oc (Prog(data, fundefs, e)) = + Format.eprintf "generating assembly...@."; + Printf.fprintf oc ".data\n"; + Printf.fprintf oc ".balign\t8\n"; + List.iter + (fun (Id.L(x), d) -> + Printf.fprintf oc "%s:\t# %f\n" x d; + Printf.fprintf oc "\t.long\t0x%lx\n" (gethi d); + Printf.fprintf oc "\t.long\t0x%lx\n" (getlo d)) + data; + Printf.fprintf oc ".text\n"; + List.iter (fun fundef -> h oc fundef) fundefs; + Printf.fprintf oc ".globl\tmin_caml_start\n"; + Printf.fprintf oc "min_caml_start:\n"; + Printf.fprintf oc ".globl\t_min_caml_start\n"; + Printf.fprintf oc "_min_caml_start: # for cygwin\n"; + Printf.fprintf oc "\tpushl\t%%eax\n"; + Printf.fprintf oc "\tpushl\t%%ebx\n"; + Printf.fprintf oc "\tpushl\t%%ecx\n"; + Printf.fprintf oc "\tpushl\t%%edx\n"; + Printf.fprintf oc "\tpushl\t%%esi\n"; + Printf.fprintf oc "\tpushl\t%%edi\n"; + Printf.fprintf oc "\tpushl\t%%ebp\n"; + Printf.fprintf oc "\tmovl\t32(%%esp),%s\n" reg_sp; + Printf.fprintf oc "\tmovl\t36(%%esp),%s\n" regs.(0); + Printf.fprintf oc "\tmovl\t%s,%s\n" regs.(0) reg_hp; + stackset := S.empty; + stackmap := []; + g oc (NonTail(regs.(0)), e); + Printf.fprintf oc "\tpopl\t%%ebp\n"; + Printf.fprintf oc "\tpopl\t%%edi\n"; + Printf.fprintf oc "\tpopl\t%%esi\n"; + Printf.fprintf oc "\tpopl\t%%edx\n"; + Printf.fprintf oc "\tpopl\t%%ecx\n"; + Printf.fprintf oc "\tpopl\t%%ebx\n"; + Printf.fprintf oc "\tpopl\t%%eax\n"; + Printf.fprintf oc "\tret\n"; +*/ diff --git a/src/x86/mod.rs b/src/x86/mod.rs index e7e5136..2d6ab0e 100644 --- a/src/x86/mod.rs +++ b/src/x86/mod.rs @@ -1,4 +1,5 @@ pub mod asm; +pub mod emit; pub mod reg_alloc; pub mod simm; pub mod virtual_asm; From c723d4417c9ba0d268afeaf19c5b2fa2d129890a Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Mon, 25 Nov 2019 02:33:14 +0900 Subject: [PATCH 02/15] Add instr.rs (stub) --- src/x86/error.rs | 13 +++++++++++ src/x86/instr.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++- src/x86/mod.rs | 2 ++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/x86/error.rs diff --git a/src/x86/error.rs b/src/x86/error.rs new file mode 100644 index 0000000..d04c3c1 --- /dev/null +++ b/src/x86/error.rs @@ -0,0 +1,13 @@ +#[derive(Debug)] +pub struct RegisterNameError; + +#[derive(Debug)] +pub enum Error { + RegisterNameError(RegisterNameError), +} + +impl From for Error { + fn from(err: RegisterNameError) -> Self { + Error::RegisterNameError(err) + } +} diff --git a/src/x86/instr.rs b/src/x86/instr.rs index f3afd36..d963c6c 100644 --- a/src/x86/instr.rs +++ b/src/x86/instr.rs @@ -1,4 +1,60 @@ -enum Instr { +use std::convert::TryFrom; +use std::fmt; +use std::fmt::{Display, Formatter}; + +use x86::error::RegisterNameError; + +const GPRS: [&str; 8] = ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"]; + +#[derive(Debug, Clone, Copy)] +pub struct R32(usize); + +impl TryFrom<&str> for R32 { + type Error = RegisterNameError; + fn try_from(x: &str) -> Result { + for i in 0..8 { + if x == GPRS[i] { + return Ok(R32(i)); + } + } + Err(RegisterNameError) + } +} + +impl Display for R32 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let &R32(index) = self; + write!(f, "%{}", GPRS[index]) + } +} + +pub enum Instr { MovRR(R32, R32), Ret, } + +impl Display for Instr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Instr::MovRR(src, dst) => write!(f, "mov {}, {}\n", src, dst)?, + Instr::Ret => write!(f, "ret\n")?, + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use x86::error::Error; + + #[test] + fn movrr_display() -> Result<(), Error> { + let eax: R32 = TryFrom::try_from("eax")?; + let ebx: R32 = TryFrom::try_from("ebx")?; + let instr = Instr::MovRR(eax, ebx); + let repr = format!("{}", instr); + assert_eq!(repr, "mov %eax, %ebx\n"); + Ok(()) + } +} diff --git a/src/x86/mod.rs b/src/x86/mod.rs index 2d6ab0e..0a8b4e9 100644 --- a/src/x86/mod.rs +++ b/src/x86/mod.rs @@ -1,5 +1,7 @@ pub mod asm; pub mod emit; +mod error; +mod instr; pub mod reg_alloc; pub mod simm; pub mod virtual_asm; From fd2ccb55ecdd6657927871072013e5e804f0c171 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Mon, 25 Nov 2019 13:31:57 +0900 Subject: [PATCH 03/15] Make adder.ml compile && run --- assets/stub.c | 26 ++++++ assets/x86/libmincaml.S | 63 +++++++++++++ src/main.rs | 8 +- src/x86/asm.rs | 2 +- src/x86/emit.rs | 190 +++++++++++++++++++++++++++++++--------- src/x86/error.rs | 2 +- src/x86/instr.rs | 68 ++++++++++---- 7 files changed, 298 insertions(+), 61 deletions(-) create mode 100644 assets/stub.c create mode 100644 assets/x86/libmincaml.S diff --git a/assets/stub.c b/assets/stub.c new file mode 100644 index 0000000..94bd4cb --- /dev/null +++ b/assets/stub.c @@ -0,0 +1,26 @@ +// https://github.com/esumii/min-caml/blob/master/stub.c +#include +#include + +extern void min_caml_start(char *, char *); + +/* "stderr" is a macro and cannot be referred to in libmincaml.S, so + this "min_caml_stderr" is used (in place of "__iob+32") for better + portability (under SPARC emulators, for example). Thanks to Steven + Shaw for reporting the problem and proposing this solution. */ +FILE *min_caml_stderr; + +int main() { + char *hp, *sp; + + min_caml_stderr = stderr; + sp = alloca(1000000); hp = malloc(4000000); + if (hp == NULL || sp == NULL) { + fprintf(stderr, "malloc or alloca failed\n"); + return 1; + } + fprintf(stderr, "sp = %p, hp = %p\n", sp, hp); + min_caml_start(sp, hp); + + return 0; +} diff --git a/assets/x86/libmincaml.S b/assets/x86/libmincaml.S new file mode 100644 index 0000000..171c348 --- /dev/null +++ b/assets/x86/libmincaml.S @@ -0,0 +1,63 @@ +// Must conform to System V AMD64 ABI + +#if defined(__CYGWIN__) || defined(__MACH__) +#define U(x) _##x +#else +#define U(x) x +#endif +#if defined(__MACH__) +#define ALIGNSTACK0 andq $-16, %rsp +#define ALIGNSTACK1 andq $-16, %rsp; pushq %rax; pushq %rax; pushq %rax +#define ALIGNSTACK2 andq $-16, %rsp; pushq %rax; pushq %rax +#define ALIGNSTACK3 andq $-16, %rsp; pushq %rax +#else +#define ALIGNSTACK0 +#define ALIGNSTACK1 +#define ALIGNSTACK2 +#define ALIGNSTACK3 +#endif +.text +.globl min_caml_print_newline +min_caml_print_newline: + pushq %rbp + movq %rsp, %rbp + ALIGNSTACK1 + pushq $10 + call U(putchar) + movq %rbp, %rsp + popq %rbp + ret +.globl min_caml_print_int +min_caml_print_int: + pushq %rbp + movq %rsp, %rbp + ALIGNSTACK2 + movq %rax, %rsi + movq format_int@GOTPCREL(%rip), %rdi + movb $0, %al + callq U(printf) + movq %rbp, %rsp + popq %rbp + retq +.data +format_int: + .asciz "%d" +format_float: + .asciz "%lf" +.balign 8 +float_0: + .long 0x0 + .long 0x0 +float_1: + .long 0x0 + .long 0x3ff00000 +.balign 16 +.globl min_caml_fnegd +min_caml_fnegd: + .long 0 + .long 0x80000000 + .long 0 + .long 0 +.globl min_caml_hp +min_caml_hp: + .long 0x0 diff --git a/src/main.rs b/src/main.rs index ce1ed91..96caf29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -130,9 +130,15 @@ fn run(program: &[u8], output_path: &Path) -> Result<(), std::io::Error> { let reg_alloc = x86::reg_alloc::f(simm, &mut id_gen); println!("reg_alloc = {}", reg_alloc); println!(); + let emitted = x86::emit::f(reg_alloc, &mut id_gen).unwrap(); + for row in &emitted { + print!("{}", row); + } // Write to file let mut outfile = BufWriter::new(File::create(output_path)?); - write!(outfile, "{}\n", reg_alloc)?; + for row in &emitted { + write!(outfile, "{}", row)?; + } Ok(()) } diff --git a/src/x86/asm.rs b/src/x86/asm.rs index 548bf72..018ff54 100644 --- a/src/x86/asm.rs +++ b/src/x86/asm.rs @@ -247,7 +247,7 @@ pub fn seq(id_gen: &mut id::IdGen, e1: Exp, e2: Asm) -> Asm { Asm::Let(id, Type::Unit, e1, Box::new(e2)) } -const REGS: [&str; 6] = ["%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"]; +pub const REGS: [&str; 6] = ["%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi"]; pub fn regs() -> Vec { let mut res = vec!["".to_string(); 6]; diff --git a/src/x86/emit.rs b/src/x86/emit.rs index 17085c8..4916902 100644 --- a/src/x86/emit.rs +++ b/src/x86/emit.rs @@ -1,12 +1,14 @@ use std::collections::HashSet; +use std::convert::TryInto; use id; use id::IdGen; -use std::io::Write; use syntax::Type; use x86::asm; -use x86::asm::{Asm, Exp, Fundef, IdOrImm, Prog}; - +use x86::asm::{Asm, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; +use x86::error::Error; +use x86::instr::Instr; +use x86::instr::Instr::MovRR; /* open Asm @@ -18,32 +20,41 @@ let stackset = ref S.empty (* すでにSaveされた変数の集合 (caml2html: */ -type StackSet = HashSet; -type StackMap = Vec; -fn save(x: &str, stackset: &mut StackSet, stackmap: &mut StackMap) { - stackset.insert(x.to_string()); - if stackmap.iter().all(|y| x != y) { - stackmap.push(x.to_string()); - } +struct StackState { + stack_set: HashSet, + stack_map: Vec, } -fn savef(x: &str, stackset: &mut StackSet, stackmap: &mut StackMap, id_gen: &mut IdGen) { - stackset.insert(x.to_string()); - if stackmap.iter().all(|y| x != y) { - if stackmap.len() % 2 == 1 { - stackmap.push(id_gen.gen_tmp(&Type::Int)); +impl StackState { + fn new() -> Self { + StackState { + stack_set: Default::default(), + stack_map: Vec::new(), + } + } + fn save(&mut self, x: &str) { + self.stack_set.insert(x.to_string()); + if self.stack_map.iter().all(|y| x != y) { + self.stack_map.push(x.to_string()); + } + } + fn savef(&mut self, x: &str, id_gen: &mut IdGen) { + self.stack_set.insert(x.to_string()); + if self.stack_map.iter().all(|y| x != y) { + self.stack_map.push(x.to_string()); } - stackmap.push(x.to_string()); - stackmap.push(x.to_string()); } -} -fn offset(x: &str, stackmap: &StackMap) -> i32 { - 4 * stackmap.iter().position(|y| x == y).unwrap() as i32 -} + fn offset(&self, x: &str) -> Option { + self.stack_map + .iter() + .position(|y| x == y) + .map(|y| 8 * y as i32) + } -fn stacksize(stackmap: &StackMap) -> i32 { - asm::align(4 * stackmap.len() as i32) + fn stacksize(&self) -> i32 { + asm::align(8 * self.stack_map.len() as i32) + } } fn pp_id_or_imm(value: &IdOrImm) -> String { @@ -55,6 +66,12 @@ fn pp_id_or_imm(value: &IdOrImm) -> String { /// 関数呼び出しのために引数を並べ替える(register shuffling) (caml2html: emit_shuffle) fn shuffle(sw: &str, xys: &[(String, String)]) -> Vec<(String, String)> { + let xys: Vec<_> = xys + .iter() + .filter(|(ref x, ref y)| x != y) + .cloned() + .collect(); + panic!(); } /* @@ -74,13 +91,26 @@ let rec shuffle sw xys = */ /// 末尾かどうかを表すデータ型 (caml2html: emit_dest) +#[derive(Debug)] enum Dest { Tail, NonTail(String), } -fn g(output: &mut impl Write, asm: Asm) -> Result<(), std::io::Error> { - panic!(); +fn g( + output: &mut Vec, + stack_state: &mut StackState, + dest: Dest, + asm: Asm, +) -> Result<(), Error> { + match asm { + Asm::Ans(exp) => g_exp(output, stack_state, dest, exp), + Asm::Let(x, t, exp, e) => { + g_exp(output, stack_state, Dest::NonTail(x), exp)?; + g(output, stack_state, dest, *e)?; + Ok(()) + } + } } /* let rec g oc = function (* 命令列のアセンブリ生成 (caml2html: emit_g) *) @@ -89,8 +119,51 @@ let rec g oc = function (* 命令列のアセンブリ生成 (caml2html: emit_g) g' oc (NonTail(x), exp); g oc (dest, e) */ -fn g_exp(output: &mut impl Write, exp: Exp) -> Result<(), std::io::Error> { - panic!(); +fn g_exp( + output: &mut Vec, + stack_state: &mut StackState, + dest: Dest, + exp: Exp, +) -> Result<(), Error> { + use self::Dest::{NonTail, Tail}; + match (dest, exp) { + (NonTail(x), Exp::Set(i)) => output.push(Instr::MovIR(i, x.try_into()?)), + /* + | NonTail(a), CallDir(Id.L(x), ys, zs) -> + g'_args oc [] ys zs; + let ss = stacksize () in + if ss > 0 then Printf.fprintf oc "\taddl\t$%d, %s\n" ss reg_sp; + Printf.fprintf oc "\tcall\t%s\n" x; + if ss > 0 then Printf.fprintf oc "\tsubl\t$%d, %s\n" ss reg_sp; + if List.mem a allregs && a <> regs.(0) then + Printf.fprintf oc "\tmovl\t%s, %s\n" regs.(0) a + else if List.mem a allfregs && a <> fregs.(0) then + Printf.fprintf oc "\tmovsd\t%s, %s\n" fregs.(0) a + */ + (NonTail(a), Exp::CallDir(id::L(x), ys, zs)) => { + g_exp_args( + output, + stack_state, + "dummy".to_string(), + ys.into_vec(), + zs.into_vec(), + )?; + let ss = stack_state.stacksize(); + if ss > 0 { + output.push(Instr::AddIR(ss, REG_SP.try_into()?)); + } + output.push(Instr::Call(x)); + if ss > 0 { + output.push(Instr::AddIR(-ss, REG_SP.try_into()?)); + } + // TODO + if REGS[0] != a { + output.push(MovRR(REGS[0].try_into()?, a.try_into()?)); + } + } + (dest, exp) => unimplemented!("{:?} {:?}", dest, exp), + } + Ok(()) } /* and g' oc = function (* 各命令のアセンブリ生成 (caml2html: emit_gprime) *) @@ -250,12 +323,12 @@ and g' oc = function (* 各命令のアセンブリ生成 (caml2html: emit_gprim */ fn g_exp_tail_if( - output: &mut impl Write, + output: &mut Vec, e1: Exp, e2: Exp, b: &str, bn: &str, -) -> Result<(), std::io::Error> { +) -> Result<(), Error> { panic!(); } /* @@ -269,13 +342,13 @@ and g'_tail_if oc e1 e2 b bn = g oc (Tail, e2) */ fn g_exp_non_tail_if( - output: &mut impl Write, + output: &mut Vec, dest: Dest, e1: Exp, e2: Exp, b: &str, bn: &str, -) -> Result<(), std::io::Error> { +) -> Result<(), Error> { panic!(); } /* @@ -295,12 +368,14 @@ and g'_non_tail_if oc dest e1 e2 b bn = stackset := S.inter stackset1 stackset2 */ fn g_exp_args( - output: &mut impl Write, + output: &mut Vec, + stack_state: &mut StackState, x_reg_cl: String, ys: Vec, zs: Vec, -) -> Result<(), std::io::Error> { - panic!(); +) -> Result<(), Error> { + // unimplemented!("x_reg_cl = {}, ys = {:?}, zs = {:?}", x_reg_cl, ys, zs); + Ok(()) } /* and g'_args oc x_reg_cl ys zs = @@ -326,7 +401,7 @@ and g'_args oc x_reg_cl ys zs = */ fn h( - output: &mut impl Write, + output: &mut Vec, Fundef { name: id::L(x), args: ys, @@ -335,8 +410,9 @@ fn h( ret: t, }: Fundef, id_gen: &mut IdGen, -) -> Result<(), std::io::Error> { - panic!(); +) -> Result<(), Error> { + output.push(Instr::Label(x)); + g(output, &mut StackState::new(), Dest::Tail, e) } /* let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = @@ -346,12 +422,40 @@ let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = g oc (Tail, e) */ -pub fn f( - output: &mut impl Write, - Prog(data, fundefs, e): Prog, - id_gen: &mut IdGen, -) -> Result<(), std::io::Error> { - panic!(); +pub fn f(Prog(data, fundefs, e): Prog, id_gen: &mut IdGen) -> Result, Error> { + let mut instrs = Vec::new(); + instrs.push(Instr::Label(".data".to_string())); + instrs.push(Instr::BAlign(8)); + for (id::L(x), d) in data.into_vec() { + // TODO + } + for fundef in fundefs.into_vec() { + h(&mut instrs, fundef, id_gen)?; + } + instrs.push(Instr::Globl("min_caml_start".to_string())); + instrs.push(Instr::Label("min_caml_start".to_string())); + instrs.push(Instr::Globl("_min_caml_start".to_string())); + instrs.push(Instr::Label("_min_caml_start".to_string())); + let saved_regs = vec!["%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%rbp"]; + for reg in saved_regs.iter().cloned() { + instrs.push(Instr::PushQ(reg.try_into()?)) + } + /* + Printf.fprintf oc "\tmovl\t32(%%esp),%s\n" reg_sp; + Printf.fprintf oc "\tmovl\t36(%%esp),%s\n" regs.(0); + Printf.fprintf oc "\tmovl\t%s,%s\n" regs.(0) reg_hp; + */ + g( + &mut instrs, + &mut StackState::new(), + Dest::NonTail(saved_regs[0].to_string()), + e, + )?; + for reg in saved_regs.into_iter().rev() { + instrs.push(Instr::PopQ(reg.try_into()?)) + } + instrs.push(Instr::Ret); + Ok(instrs) } /* let f oc (Prog(data, fundefs, e)) = diff --git a/src/x86/error.rs b/src/x86/error.rs index d04c3c1..364274f 100644 --- a/src/x86/error.rs +++ b/src/x86/error.rs @@ -1,5 +1,5 @@ #[derive(Debug)] -pub struct RegisterNameError; +pub struct RegisterNameError(pub String); #[derive(Debug)] pub enum Error { diff --git a/src/x86/instr.rs b/src/x86/instr.rs index d963c6c..86d9ff9 100644 --- a/src/x86/instr.rs +++ b/src/x86/instr.rs @@ -1,43 +1,81 @@ -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::{Display, Formatter}; use x86::error::RegisterNameError; -const GPRS: [&str; 8] = ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"]; +const GPRS: [&str; 8] = [ + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", +]; #[derive(Debug, Clone, Copy)] -pub struct R32(usize); +pub struct R64(usize); -impl TryFrom<&str> for R32 { +impl TryFrom<&str> for R64 { type Error = RegisterNameError; fn try_from(x: &str) -> Result { for i in 0..8 { if x == GPRS[i] { - return Ok(R32(i)); + return Ok(R64(i)); } } - Err(RegisterNameError) + Err(RegisterNameError(x.to_string())) } } +impl TryFrom for R64 { + type Error = RegisterNameError; + fn try_from(x: String) -> Result { + (&x as &str).try_into() + } +} + +impl Display for R64 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let &R64(index) = self; + write!(f, "{}", GPRS[index]) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct M32(pub R64, pub i32); -impl Display for R32 { +impl Display for M32 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let &R32(index) = self; - write!(f, "%{}", GPRS[index]) + let &M32(reg, disp) = self; + write!(f, "{}({})", disp, reg) } } pub enum Instr { - MovRR(R32, R32), + // pseudo instructions + Label(String), + BAlign(i32), + Globl(String), + // instructions + MovRR(R64, R64), + MovIR(i32, R64), + MovMR(M32, R64), Ret, + PushQ(R64), + PopQ(R64), + AddIR(i32, R64), + Call(String), } impl Display for Instr { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Instr::MovRR(src, dst) => write!(f, "mov {}, {}\n", src, dst)?, - Instr::Ret => write!(f, "ret\n")?, + Instr::Label(s) => writeln!(f, "{}:", s)?, + Instr::BAlign(align) => writeln!(f, ".balign\t{}", align)?, + Instr::Globl(s) => writeln!(f, ".globl\t{}", s)?, + Instr::MovRR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, + Instr::MovIR(src, dst) => writeln!(f, " movq ${}, {}", src, dst)?, + Instr::MovMR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, + Instr::Ret => writeln!(f, " ret")?, + Instr::PushQ(reg) => writeln!(f, " pushq {}", reg)?, + Instr::PopQ(reg) => writeln!(f, " popq {}", reg)?, + Instr::AddIR(src, dst) => writeln!(f, " addq ${}, {}", src, dst)?, + Instr::Call(name) => writeln!(f, " callq {}", name)?, } Ok(()) } @@ -50,11 +88,11 @@ mod tests { #[test] fn movrr_display() -> Result<(), Error> { - let eax: R32 = TryFrom::try_from("eax")?; - let ebx: R32 = TryFrom::try_from("ebx")?; + let eax: R64 = TryFrom::try_from("%eax")?; + let ebx: R64 = TryFrom::try_from("%ebx")?; let instr = Instr::MovRR(eax, ebx); let repr = format!("{}", instr); - assert_eq!(repr, "mov %eax, %ebx\n"); + assert_eq!(repr, " mov %eax, %ebx\n"); Ok(()) } } From 785788d7a4284009ecb90ef9e20b324d5844af2b Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Mon, 25 Nov 2019 13:41:20 +0900 Subject: [PATCH 04/15] Add Makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38831e7 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +%.x: %.asm assets/x86/libmincaml.S assets/stub.c + cc assets/stub.c assets/x86/libmincaml.S $*.asm -o $*.x +%.asm: %.ml + cargo run $< $@ From 28f438239da2e42e497b6961270fec664160e028 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Tue, 26 Nov 2019 01:07:59 +0900 Subject: [PATCH 05/15] Checker script checks the equality of output --- check-test-mls.sh | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/check-test-mls.sh b/check-test-mls.sh index 67bd207..1083fa0 100755 --- a/check-test-mls.sh +++ b/check-test-mls.sh @@ -1,20 +1,34 @@ success=0 total=0 + +ARCH=${ARCH:-x86} + +NECESSARY_FILES="assets/stub.c assets/${ARCH}/libmincaml.S" + if cargo build --release; then - for i in test/*.ml; do - echo "---- ${i} ----" - cat ${i} - if cargo run --release -q -- ${i} ${i}.asm >/dev/null; then - echo "[OK] $i" - cat ${i}.asm - echo; echo - success=`expr $success + 1` - else - echo "[Error] $i" - fi - total=`expr $total + 1` - done - echo "Success rate: $success/$total" + for i in test/*.ml; do + echo "---- ${i} ----" + if cargo run --release -q -- ${i} ${i}.asm >/dev/null 2>&1; then + if cc ${i}.asm ${NECESSARY_FILES} -o ${i}.x; then + if ./${i}.x >${i}.out; then + # testrun ml + ocaml ${i} >${i}.ans + if diff ${i}.out ${i}.ans; then + echo "[OK] $i" + success=$((success+1)) + else + echo "[Output Error] ${i}" + fi + fi + else + echo "[Link Error] $i" + fi + else + echo "[Compile Error] $i" + fi + total=$((total+1)) + done + echo "Success rate: $success/$total" else echo "build error" exit 1 From fbb38e8ba1b72837f2aa7c8e7a4a6b3514394dee Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Tue, 26 Nov 2019 01:21:05 +0900 Subject: [PATCH 06/15] Fix tests --- src/x86/instr.rs | 8 ++++---- src/x86/reg_alloc.rs | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/x86/instr.rs b/src/x86/instr.rs index 86d9ff9..6149f9e 100644 --- a/src/x86/instr.rs +++ b/src/x86/instr.rs @@ -88,11 +88,11 @@ mod tests { #[test] fn movrr_display() -> Result<(), Error> { - let eax: R64 = TryFrom::try_from("%eax")?; - let ebx: R64 = TryFrom::try_from("%ebx")?; - let instr = Instr::MovRR(eax, ebx); + let rax: R64 = TryFrom::try_from("%rax")?; + let rbx: R64 = TryFrom::try_from("%rbx")?; + let instr = Instr::MovRR(rax, rbx); let repr = format!("{}", instr); - assert_eq!(repr, " mov %eax, %ebx\n"); + assert_eq!(repr, " movq %rax, %rbx\n"); Ok(()) } } diff --git a/src/x86/reg_alloc.rs b/src/x86/reg_alloc.rs index 789f012..ab27b56 100644 --- a/src/x86/reg_alloc.rs +++ b/src/x86/reg_alloc.rs @@ -532,10 +532,10 @@ mod tests { use super::*; #[test] fn test_find() { - let regenv = vec![("aa".to_string(), "%eax".to_string())] + let regenv = vec![("aa".to_string(), "%rax".to_string())] .into_iter() .collect(); - assert_eq!(find("aa", &Type::Int, ®env), Ok("%eax".to_string())); + assert_eq!(find("aa", &Type::Int, ®env), Ok("%rax".to_string())); assert_eq!( find("bb", &Type::Int, ®env), Err(NoReg("bb".to_string(), Type::Int)) @@ -543,12 +543,12 @@ mod tests { } #[test] fn test_find_p() { - let regenv = vec![("aa".to_string(), "%eax".to_string())] + let regenv = vec![("aa".to_string(), "%rax".to_string())] .into_iter() .collect(); assert_eq!( find_p(&IdOrImm::V("aa".to_string()), ®env), - Ok(IdOrImm::V("%eax".to_string())) + Ok(IdOrImm::V("%rax".to_string())) ); assert_eq!( find_p(&IdOrImm::V("bb".to_string()), ®env), @@ -561,24 +561,24 @@ mod tests { let regenv = HashMap::new(); let x = "a".to_string(); let t = Type::Int; - let preference = ["%eax".to_string()]; + let preference = ["%rax".to_string()]; assert_eq!( alloc(cont, ®env, x, t, &preference), - AllocResult::Alloc("%eax".to_string()), + AllocResult::Alloc("%rax".to_string()), ) } #[test] fn test_alloc_not_in_preference() { let cont = Asm::Ans(Exp::Mov("b".to_string())); - let regenv = vec![("b".to_string(), "%eax".to_string())] + let regenv = vec![("b".to_string(), "%rax".to_string())] .into_iter() .collect(); let x = "a".to_string(); let t = Type::Int; - let preference = ["%eax".to_string()]; + let preference = ["%rax".to_string()]; assert_eq!( alloc(cont, ®env, x, t, &preference), - AllocResult::Alloc("%ebx".to_string()), + AllocResult::Alloc("%rbx".to_string()), ) } } From e71d33958118520d32ec6699fbc651174e7a234a Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Tue, 26 Nov 2019 02:48:57 +0900 Subject: [PATCH 07/15] gcd.ml compiles && runs --- src/lib.rs | 2 +- src/util.rs | 35 ++++++++++++ src/x86/emit.rs | 141 ++++++++++++++++++++++++++++++++++++++++------- src/x86/instr.rs | 91 ++++++++++++++++++++++++++++-- 4 files changed, 243 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9131944..f9e25ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ extern crate lazy_static; extern crate ordered_float; #[macro_use] -mod util; +pub mod util; pub mod alpha; pub mod assoc; pub mod beta; diff --git a/src/util.rs b/src/util.rs index d27fdc2..b8e9b1e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; macro_rules! build_set { () => (::std::collections::HashSet::new()); ($($x:expr),+) => ({ @@ -6,3 +7,37 @@ macro_rules! build_set { h }) } + +// TODO: error handling +fn assert_assignment(desired: &[(String, String)], operations: &[(String, String)]) { + let mut counter = 0; + let mut old = HashMap::new(); + let mut new = HashMap::new(); + for &(ref src, ref dst) in desired { + old.insert(src.clone(), counter); + new.insert(dst.clone(), counter); + counter += 1; + } + for &(ref src, ref dst) in operations { + let val = old[src]; + old.insert(dst.clone(), val); + } + for (key, value) in new { + assert_eq!(old[&key], value); + } +} + +pub fn assign(a: &[(String, String)], tmp: &str) -> Vec<(String, String)> { + let mut operations = a.to_vec(); + // TODO: Ad-hoc assignment for mere swapping + if a.len() == 2 && a[0].0 == a[1].1 && a[0].1 == a[1].0 { + let (x, y) = a[0].clone(); + operations = vec![ + (x.clone(), tmp.to_string()), + (y.clone(), x.clone()), + (tmp.to_string(), y.clone()), + ]; + } + assert_assignment(a, &operations); + operations +} diff --git a/src/x86/emit.rs b/src/x86/emit.rs index 4916902..972bf28 100644 --- a/src/x86/emit.rs +++ b/src/x86/emit.rs @@ -3,12 +3,12 @@ use std::convert::TryInto; use id; use id::IdGen; -use syntax::Type; +use syntax::IntBin; use x86::asm; -use x86::asm::{Asm, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; +use x86::asm::{fregs, Asm, CompBin, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; use x86::error::Error; -use x86::instr::Instr; use x86::instr::Instr::MovRR; +use x86::instr::{cmpq, movq, subq, Instr, R64}; /* open Asm @@ -20,6 +20,7 @@ let stackset = ref S.empty (* すでにSaveされた変数の集合 (caml2html: */ +#[derive(Debug, Clone)] struct StackState { stack_set: HashSet, stack_map: Vec, @@ -71,8 +72,7 @@ fn shuffle(sw: &str, xys: &[(String, String)]) -> Vec<(String, String)> { .filter(|(ref x, ref y)| x != y) .cloned() .collect(); - - panic!(); + crate::util::assign(&xys, sw) } /* let rec shuffle sw xys = @@ -100,14 +100,15 @@ enum Dest { fn g( output: &mut Vec, stack_state: &mut StackState, + id_gen: &mut IdGen, dest: Dest, asm: Asm, ) -> Result<(), Error> { match asm { - Asm::Ans(exp) => g_exp(output, stack_state, dest, exp), + Asm::Ans(exp) => g_exp(output, stack_state, id_gen, dest, exp), Asm::Let(x, t, exp, e) => { - g_exp(output, stack_state, Dest::NonTail(x), exp)?; - g(output, stack_state, dest, *e)?; + g_exp(output, stack_state, id_gen, Dest::NonTail(x), exp)?; + g(output, stack_state, id_gen, dest, *e)?; Ok(()) } } @@ -122,12 +123,57 @@ let rec g oc = function (* 命令列のアセンブリ生成 (caml2html: emit_g) fn g_exp( output: &mut Vec, stack_state: &mut StackState, + id_gen: &mut IdGen, dest: Dest, exp: Exp, ) -> Result<(), Error> { use self::Dest::{NonTail, Tail}; match (dest, exp) { (NonTail(x), Exp::Set(i)) => output.push(Instr::MovIR(i, x.try_into()?)), + (NonTail(x), Exp::Mov(y)) => { + if x != y { + output.push(movq(IdOrImm::V(y), x)?); + } + } + /* + | NonTail(x), Sub(y, z') -> + if V(x) = z' then + (Printf.fprintf oc "\tsubl\t%s, %s\n" y x; + Printf.fprintf oc "\tnegl\t%s\n" x) + else + (if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x; + Printf.fprintf oc "\tsubl\t%s, %s\n" (pp_id_or_imm z') x) + */ + (NonTail(x), Exp::IntOp(IntBin::Sub, y, z_p)) => { + if IdOrImm::V(x.clone()) == z_p { + let xreg: R64 = x.try_into()?; + output.push(Instr::SubRR(y.try_into()?, xreg)); + output.push(Instr::NegQ(xreg)); + } else { + let xreg = x.clone().try_into()?; + if x != y { + output.push(Instr::MovRR(y.try_into()?, xreg)); + } + output.push(subq(z_p, x)?); + } + } + /* + | Tail, CallDir(Id.L(x), ys, zs) -> (* 末尾呼び出し *) + g'_args oc [] ys zs; + Printf.fprintf oc "\tjmp\t%s\n" x; + */ + (Tail, Exp::CallDir(id::L(x), ys, zs)) => { + // tail call + g_exp_args( + output, + stack_state, + id_gen, + None, + ys.into_vec(), + zs.into_vec(), + )?; + output.push(Instr::Jmp(x)); + } /* | NonTail(a), CallDir(Id.L(x), ys, zs) -> g'_args oc [] ys zs; @@ -135,16 +181,13 @@ fn g_exp( if ss > 0 then Printf.fprintf oc "\taddl\t$%d, %s\n" ss reg_sp; Printf.fprintf oc "\tcall\t%s\n" x; if ss > 0 then Printf.fprintf oc "\tsubl\t$%d, %s\n" ss reg_sp; - if List.mem a allregs && a <> regs.(0) then - Printf.fprintf oc "\tmovl\t%s, %s\n" regs.(0) a - else if List.mem a allfregs && a <> fregs.(0) then - Printf.fprintf oc "\tmovsd\t%s, %s\n" fregs.(0) a */ (NonTail(a), Exp::CallDir(id::L(x), ys, zs)) => { g_exp_args( output, stack_state, - "dummy".to_string(), + id_gen, + None, ys.into_vec(), zs.into_vec(), )?; @@ -157,10 +200,41 @@ fn g_exp( output.push(Instr::AddIR(-ss, REG_SP.try_into()?)); } // TODO + /* + if List.mem a allregs && a <> regs.(0) then + Printf.fprintf oc "\tmovl\t%s, %s\n" regs.(0) a + else if List.mem a allfregs && a <> fregs.(0) then + Printf.fprintf oc "\tmovsd\t%s, %s\n" fregs.(0) a + */ if REGS[0] != a { output.push(MovRR(REGS[0].try_into()?, a.try_into()?)); } } + /* + | Tail, (Set _ | SetL _ | Mov _ | Neg _ | Add _ | Sub _ | Ld _ as exp) -> + g' oc (NonTail(regs.(0)), exp); + Printf.fprintf oc "\tret\n"; + */ + (Tail, Exp::Mov(src)) => { + let exp = Exp::Mov(src); + g_exp( + output, + stack_state, + id_gen, + Dest::NonTail(REGS[0].to_string()), + exp, + )?; + output.push(Instr::Ret); + } + (Tail, Exp::IfComp(op, x, y_p, e1, e2)) => { + output.push(cmpq(y_p, x)?); + let (if_label, else_label) = match op { + CompBin::Eq => ("je", "jne"), + CompBin::LE => ("jle", "jg"), + CompBin::GE => ("jge", "jl"), + }; + g_exp_tail_if(output, stack_state, id_gen, *e1, *e2, if_label, else_label)?; + } (dest, exp) => unimplemented!("{:?} {:?}", dest, exp), } Ok(()) @@ -324,12 +398,21 @@ and g' oc = function (* 各命令のアセンブリ生成 (caml2html: emit_gprim fn g_exp_tail_if( output: &mut Vec, - e1: Exp, - e2: Exp, + stack_state: &mut StackState, + id_gen: &mut IdGen, + e1: Asm, + e2: Asm, b: &str, bn: &str, ) -> Result<(), Error> { - panic!(); + let stack_state_old = stack_state.clone(); + let b_else = id_gen.gen_id(&format!("{}_else", b)); + output.push(Instr::Branch(bn.to_string(), b_else.clone())); + g(output, stack_state, id_gen, Dest::Tail, e1)?; + output.push(Instr::Label(b_else)); + *stack_state = stack_state_old; + g(output, stack_state, id_gen, Dest::Tail, e2)?; + Ok(()) } /* and g'_tail_if oc e1 e2 b bn = @@ -343,13 +426,15 @@ and g'_tail_if oc e1 e2 b bn = */ fn g_exp_non_tail_if( output: &mut Vec, + stack_state: &mut StackState, + id_gen: &mut IdGen, dest: Dest, e1: Exp, e2: Exp, b: &str, bn: &str, ) -> Result<(), Error> { - panic!(); + unimplemented!("e1={:?}, e2={:?}, b={}, bn={}", e1, e2, b, bn); } /* and g'_non_tail_if oc dest e1 e2 b bn = @@ -370,11 +455,26 @@ and g'_non_tail_if oc dest e1 e2 b bn = fn g_exp_args( output: &mut Vec, stack_state: &mut StackState, - x_reg_cl: String, + id_gen: &mut IdGen, + x_reg_cl: Option, ys: Vec, zs: Vec, ) -> Result<(), Error> { - // unimplemented!("x_reg_cl = {}, ys = {:?}, zs = {:?}", x_reg_cl, ys, zs); + assert!(ys.len() <= REGS.len() - if x_reg_cl.is_some() { 1 } else { 0 }); + assert!(zs.len() <= fregs().len()); + let sw = "%r15"; // TODO register to instr + let mut swaps = vec![]; + for i in 0..ys.len() { + swaps.push((ys[i].clone(), REGS[i].to_string())); + // TODO handle x_reg_cl + } + // shuffle + let swaps = shuffle(&sw, &swaps); + for i in 0..swaps.len() { + let (x, y) = swaps[i].clone(); + output.push(Instr::MovRR(x.try_into()?, y.try_into()?)); + } + // TODO floating point numbers Ok(()) } /* @@ -412,7 +512,7 @@ fn h( id_gen: &mut IdGen, ) -> Result<(), Error> { output.push(Instr::Label(x)); - g(output, &mut StackState::new(), Dest::Tail, e) + g(output, &mut StackState::new(), id_gen, Dest::Tail, e) } /* let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = @@ -448,6 +548,7 @@ pub fn f(Prog(data, fundefs, e): Prog, id_gen: &mut IdGen) -> Result, g( &mut instrs, &mut StackState::new(), + id_gen, Dest::NonTail(saved_regs[0].to_string()), e, )?; diff --git a/src/x86/instr.rs b/src/x86/instr.rs index 6149f9e..465bb96 100644 --- a/src/x86/instr.rs +++ b/src/x86/instr.rs @@ -2,10 +2,12 @@ use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::{Display, Formatter}; -use x86::error::RegisterNameError; +use x86::asm::IdOrImm; +use x86::error::{Error, RegisterNameError}; -const GPRS: [&str; 8] = [ - "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", +const GPRS: [&str; 16] = [ + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", + "%r12", "%r13", "%r14", "%r15", ]; #[derive(Debug, Clone, Copy)] @@ -14,8 +16,8 @@ pub struct R64(usize); impl TryFrom<&str> for R64 { type Error = RegisterNameError; fn try_from(x: &str) -> Result { - for i in 0..8 { - if x == GPRS[i] { + for (i, &name) in GPRS.iter().enumerate() { + if x == name { return Ok(R64(i)); } } @@ -36,6 +38,32 @@ impl Display for R64 { } } +#[derive(Debug, Clone, Copy)] +pub enum RI64 { + R64(R64), + Imm(i32), +} + +impl TryFrom for RI64 { + type Error = RegisterNameError; + fn try_from(x: IdOrImm) -> Result { + let returned = match x { + IdOrImm::C(value) => RI64::Imm(value), + IdOrImm::V(name) => RI64::R64(name.try_into()?), + }; + Ok(returned) + } +} + +impl Display for RI64 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + RI64::R64(reg) => write!(f, "{}", reg), + RI64::Imm(value) => write!(f, "${}", value), + } + } +} + #[derive(Debug, Clone, Copy)] pub struct M32(pub R64, pub i32); @@ -58,7 +86,14 @@ pub enum Instr { Ret, PushQ(R64), PopQ(R64), + AddRR(R64, R64), AddIR(i32, R64), + SubRR(R64, R64), + SubIR(i32, R64), + NegQ(R64), + CmpQ(RI64, R64), + Branch(String, String), + Jmp(String), Call(String), } @@ -74,13 +109,59 @@ impl Display for Instr { Instr::Ret => writeln!(f, " ret")?, Instr::PushQ(reg) => writeln!(f, " pushq {}", reg)?, Instr::PopQ(reg) => writeln!(f, " popq {}", reg)?, + Instr::AddRR(src, dst) => writeln!(f, " addq {}, {}", src, dst)?, Instr::AddIR(src, dst) => writeln!(f, " addq ${}, {}", src, dst)?, + Instr::SubRR(src, dst) => writeln!(f, " subq {}, {}", src, dst)?, + Instr::SubIR(src, dst) => writeln!(f, " subq ${}, {}", src, dst)?, + Instr::NegQ(reg) => writeln!(f, " negq {}", reg)?, + Instr::CmpQ(src, dst) => writeln!(f, " cmpq {}, {}", src, dst)?, + Instr::Branch(op, to) => writeln!(f, " {} {}", op, to)?, + Instr::Jmp(to) => writeln!(f, " jmp {}", to)?, Instr::Call(name) => writeln!(f, " callq {}", name)?, } Ok(()) } } +// helper functions for instructions +pub fn movq( + src: impl TryInto, + dst: impl TryInto, +) -> Result { + let instr = match src.try_into()? { + RI64::Imm(value) => Instr::MovIR(value, dst.try_into()?), + RI64::R64(r) => Instr::MovRR(r, dst.try_into()?), + }; + Ok(instr) +} + +pub fn addq( + src: impl TryInto, + dst: impl TryInto, +) -> Result { + let instr = match src.try_into()? { + RI64::Imm(value) => Instr::AddIR(value, dst.try_into()?), + RI64::R64(r) => Instr::AddRR(r, dst.try_into()?), + }; + Ok(instr) +} +pub fn subq( + src: impl TryInto, + dst: impl TryInto, +) -> Result { + let instr = match src.try_into()? { + RI64::Imm(value) => Instr::SubIR(value, dst.try_into()?), + RI64::R64(r) => Instr::SubRR(r, dst.try_into()?), + }; + Ok(instr) +} +pub fn cmpq( + src: impl TryInto, + dst: impl TryInto, +) -> Result { + Ok(Instr::CmpQ(src.try_into()?, dst.try_into()?)) +} + #[cfg(test)] mod tests { use super::*; From 0e1a7ef5fdff8c84de46d2e1f1258dc0270e6647 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Wed, 27 Nov 2019 12:42:45 +0900 Subject: [PATCH 08/15] Rename: x86 -> x86_64 --- Makefile | 4 ++-- assets/{x86 => x86_64}/libmincaml.S | 0 src/closure.rs | 2 +- src/lib.rs | 2 +- src/main.rs | 10 +++++----- src/{x86 => x86_64}/asm.rs | 2 +- src/{x86 => x86_64}/emit.rs | 10 +++++----- src/{x86 => x86_64}/error.rs | 0 src/{x86 => x86_64}/instr.rs | 6 +++--- src/{x86 => x86_64}/mod.rs | 0 src/{x86 => x86_64}/reg_alloc.rs | 8 ++++---- src/{x86 => x86_64}/simm.rs | 8 ++++---- src/{x86 => x86_64}/virtual_asm.rs | 12 ++++++------ 13 files changed, 32 insertions(+), 32 deletions(-) rename assets/{x86 => x86_64}/libmincaml.S (100%) rename src/{x86 => x86_64}/asm.rs (99%) rename src/{x86 => x86_64}/emit.rs (99%) rename src/{x86 => x86_64}/error.rs (100%) rename src/{x86 => x86_64}/instr.rs (98%) rename src/{x86 => x86_64}/mod.rs (100%) rename src/{x86 => x86_64}/reg_alloc.rs (98%) rename src/{x86 => x86_64}/simm.rs (97%) rename src/{x86 => x86_64}/virtual_asm.rs (98%) diff --git a/Makefile b/Makefile index 38831e7..edd48bf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -%.x: %.asm assets/x86/libmincaml.S assets/stub.c - cc assets/stub.c assets/x86/libmincaml.S $*.asm -o $*.x +%.x: %.asm assets/x86_64/libmincaml.S assets/stub.c + cc assets/stub.c assets/x86_64/libmincaml.S $*.asm -o $*.x %.asm: %.ml cargo run $< $@ diff --git a/assets/x86/libmincaml.S b/assets/x86_64/libmincaml.S similarity index 100% rename from assets/x86/libmincaml.S rename to assets/x86_64/libmincaml.S diff --git a/src/closure.rs b/src/closure.rs index 390fb76..08fbc18 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -209,7 +209,7 @@ impl fmt::Display for Prog { } } -/// Used in x86/virtual_asm.rs +/// Used in x86_64/virtual_asm.rs pub fn fv(e: &Closure) -> HashSet { use self::Closure::*; macro_rules! invoke { diff --git a/src/lib.rs b/src/lib.rs index f9e25ab..cc791fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,4 +17,4 @@ pub mod k_normal; pub mod parser; pub mod syntax; pub mod typing; -pub mod x86; +pub mod x86_64; diff --git a/src/main.rs b/src/main.rs index 96caf29..848dda8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ extern crate nom; use min_caml_rust::syntax::Type; use min_caml_rust::{ - alpha, assoc, beta, closure, const_fold, elim, id, inline, k_normal, parser, typing, x86, + alpha, assoc, beta, closure, const_fold, elim, id, inline, k_normal, parser, typing, x86_64, }; use nom::Err; use std::collections::HashMap; @@ -122,15 +122,15 @@ fn run(program: &[u8], output_path: &Path) -> Result<(), std::io::Error> { } let closure = closure::f(e); println!("closure-trans = {}", closure); - let virtual_asm = x86::virtual_asm::f(closure, &mut id_gen); + let virtual_asm = x86_64::virtual_asm::f(closure, &mut id_gen); println!("virtual_asm = {}", virtual_asm); println!(); - let simm = x86::simm::f(virtual_asm); + let simm = x86_64::simm::f(virtual_asm); println!("simm = {}", simm); - let reg_alloc = x86::reg_alloc::f(simm, &mut id_gen); + let reg_alloc = x86_64::reg_alloc::f(simm, &mut id_gen); println!("reg_alloc = {}", reg_alloc); println!(); - let emitted = x86::emit::f(reg_alloc, &mut id_gen).unwrap(); + let emitted = x86_64::emit::f(reg_alloc, &mut id_gen).unwrap(); for row in &emitted { print!("{}", row); } diff --git a/src/x86/asm.rs b/src/x86_64/asm.rs similarity index 99% rename from src/x86/asm.rs rename to src/x86_64/asm.rs index 018ff54..e2da6c9 100644 --- a/src/x86/asm.rs +++ b/src/x86_64/asm.rs @@ -15,7 +15,7 @@ pub enum Asm { Let(String, Type, Exp, Box), } -// We have GE because x86 instructions are not symmetric +// We have GE because x86_64 instructions are not symmetric #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CompBin { Eq, diff --git a/src/x86/emit.rs b/src/x86_64/emit.rs similarity index 99% rename from src/x86/emit.rs rename to src/x86_64/emit.rs index 972bf28..884fc30 100644 --- a/src/x86/emit.rs +++ b/src/x86_64/emit.rs @@ -4,11 +4,11 @@ use std::convert::TryInto; use id; use id::IdGen; use syntax::IntBin; -use x86::asm; -use x86::asm::{fregs, Asm, CompBin, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; -use x86::error::Error; -use x86::instr::Instr::MovRR; -use x86::instr::{cmpq, movq, subq, Instr, R64}; +use x86_64::asm; +use x86_64::asm::{fregs, Asm, CompBin, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; +use x86_64::error::Error; +use x86_64::instr::Instr::MovRR; +use x86_64::instr::{cmpq, movq, subq, Instr, R64}; /* open Asm diff --git a/src/x86/error.rs b/src/x86_64/error.rs similarity index 100% rename from src/x86/error.rs rename to src/x86_64/error.rs diff --git a/src/x86/instr.rs b/src/x86_64/instr.rs similarity index 98% rename from src/x86/instr.rs rename to src/x86_64/instr.rs index 465bb96..1ac0f42 100644 --- a/src/x86/instr.rs +++ b/src/x86_64/instr.rs @@ -2,8 +2,8 @@ use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::{Display, Formatter}; -use x86::asm::IdOrImm; -use x86::error::{Error, RegisterNameError}; +use x86_64::asm::IdOrImm; +use x86_64::error::{Error, RegisterNameError}; const GPRS: [&str; 16] = [ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", @@ -165,7 +165,7 @@ pub fn cmpq( #[cfg(test)] mod tests { use super::*; - use x86::error::Error; + use x86_64::error::Error; #[test] fn movrr_display() -> Result<(), Error> { diff --git a/src/x86/mod.rs b/src/x86_64/mod.rs similarity index 100% rename from src/x86/mod.rs rename to src/x86_64/mod.rs diff --git a/src/x86/reg_alloc.rs b/src/x86_64/reg_alloc.rs similarity index 98% rename from src/x86/reg_alloc.rs rename to src/x86_64/reg_alloc.rs index ab27b56..f5890e8 100644 --- a/src/x86/reg_alloc.rs +++ b/src/x86_64/reg_alloc.rs @@ -1,8 +1,8 @@ use id; use id::IdGen; use syntax::{FloatBin, IntBin, Type}; -use x86::asm; -use x86::asm::{Asm, Exp, Fundef, IdOrImm, Prog}; +use x86_64::asm; +use x86_64::asm::{Asm, Exp, Fundef, IdOrImm, Prog}; use std::collections::{HashMap, HashSet}; @@ -86,7 +86,7 @@ fn g( asm: Asm, id_gen: &mut IdGen, ) -> (Asm, RegEnv) { - use x86::asm::Asm::{Ans, Let}; + use x86_64::asm::Asm::{Ans, Let}; match asm { Ans(exp) => g_exp_with_restore(dest, cont, regenv, exp, id_gen), Let(x, t, exp, e) => { @@ -390,7 +390,7 @@ fn target_args(src: &str, all: &[String], ys: &[String]) -> Vec { /// The author hasn't understood the meaning of this function. /// TODO understand this /// "register sourcing" (?) as opposed to register targeting -/// (x86の2オペランド命令のためのregister coalescing) *) +/// (x86_64の2オペランド命令のためのregister coalescing) *) fn source(t: &Type, asm: &Asm) -> Vec { match asm { Asm::Ans(ref exp) => source_exp(t, exp), diff --git a/src/x86/simm.rs b/src/x86_64/simm.rs similarity index 97% rename from src/x86/simm.rs rename to src/x86_64/simm.rs index 6078dcf..28a1486 100644 --- a/src/x86/simm.rs +++ b/src/x86_64/simm.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use syntax::IntBin; -use x86::asm; -use x86::asm::{Asm, CompBin, Exp, Fundef, IdOrImm, Prog}; +use x86_64::asm; +use x86_64::asm::{Asm, CompBin, Exp, Fundef, IdOrImm, Prog}; // Name -> Const type ConstEnv = HashMap; @@ -105,8 +105,8 @@ pub fn f(Prog(data, fundefs, e): Prog) -> Prog { #[cfg(test)] mod tests { use syntax::{IntBin, Type}; - use x86::asm::{Asm, CompBin, Exp, FCompBin, IdOrImm, Prog}; - use x86::simm; + use x86_64::asm::{Asm, CompBin, Exp, FCompBin, IdOrImm, Prog}; + use x86_64::simm; // Checks if g(a) == b. Since g is not public, a and b are wrapped in Prog. fn assert_g_works(a: Asm, b: Asm) { let e1 = Prog(Box::new([]), Box::new([]), a); diff --git a/src/x86/virtual_asm.rs b/src/x86_64/virtual_asm.rs similarity index 98% rename from src/x86/virtual_asm.rs rename to src/x86_64/virtual_asm.rs index 462b5d9..a2e07b7 100644 --- a/src/x86/virtual_asm.rs +++ b/src/x86_64/virtual_asm.rs @@ -7,8 +7,8 @@ use std::collections::HashMap; use syntax; use syntax::IntBin; use syntax::Type; -use x86::asm; -use x86::asm::{Asm, CompBin, Exp, FCompBin, Fundef, IdOrImm, Prog}; +use x86_64::asm; +use x86_64::asm::{Asm, CompBin, Exp, FCompBin, Fundef, IdOrImm, Prog}; /* * S: State @@ -139,7 +139,7 @@ fn g( }, e2, ) => { - use x86::asm::REG_HP; + use x86_64::asm::REG_HP; let mut copied_env = env.clone(); copied_env.insert(x.clone(), t.clone()); let e2p = g(data, &copied_env, *e2, id_gen); @@ -218,7 +218,7 @@ fn g( Asm::Ans(Exp::CallDir(id::L(x), intargs, floatargs)) } Closure::Tuple(xs) => { - use x86::asm::{align, REG_HP}; + use x86_64::asm::{align, REG_HP}; let y = id_gen.gen_id("t"); let xs_with_type = xs .iter() @@ -331,7 +331,7 @@ fn h( &zts, (4, inner_e), |_, z, offset, load| { - ::x86::asm::fletd(z, Exp::LdDF(x.clone(), IdOrImm::C(offset), 1), load) + ::x86_64::asm::fletd(z, Exp::LdDF(x.clone(), IdOrImm::C(offset), 1), load) }, |_, z, t, offset, load| { Asm::Let( @@ -374,7 +374,7 @@ pub fn f(closure::Prog(fundefs, e): closure::Prog, id_gen: &mut IdGen) -> Prog { #[cfg(test)] mod tests { use closure::*; - use x86::virtual_asm::*; + use x86_64::virtual_asm::*; #[test] fn test_comparison() { use std::collections::HashMap; From d30cf72a5b4634be92a9989efdf82097f2db7aac Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Wed, 27 Nov 2019 12:43:00 +0900 Subject: [PATCH 09/15] Ignore generated files --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b8797cc..5856542 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ target -*.asm \ No newline at end of file +*.asm +*.ans +*.out +*.x \ No newline at end of file From f2da1f1a04ef9f5212debfd1543e6f2f55f19e6f Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Wed, 27 Nov 2019 12:51:37 +0900 Subject: [PATCH 10/15] Fix checker --- check-test-mls.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check-test-mls.sh b/check-test-mls.sh index 1083fa0..dbfa7aa 100755 --- a/check-test-mls.sh +++ b/check-test-mls.sh @@ -1,7 +1,7 @@ success=0 total=0 -ARCH=${ARCH:-x86} +ARCH=${ARCH:-x86_64} NECESSARY_FILES="assets/stub.c assets/${ARCH}/libmincaml.S" From c1c04513b79e5f660ea21ba7c6f091bb540ac64d Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Fri, 13 Dec 2019 02:25:29 +0900 Subject: [PATCH 11/15] Make assign function work --- src/util.rs | 164 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 9 deletions(-) diff --git a/src/util.rs b/src/util.rs index b8e9b1e..f5126a2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -27,17 +27,163 @@ fn assert_assignment(desired: &[(String, String)], operations: &[(String, String } } +#[derive(PartialEq, Eq, Debug)] +enum MoveType { + /// a1 -> a2 + Single(usize, usize), + /// a1 -> a2 -> ... an -> a1 + Loop(Vec), +} + +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +enum VisitType { + NotVisited, + Visiting, + Visited, +} + +fn solve_functional_graph_dfs( + a: &[usize], + mvs: &mut Vec, + visited: &mut [VisitType], + v: usize, +) -> Option> { + if visited[v] == VisitType::Visiting { + return Some(vec![v]); + } + if visited[v] == VisitType::Visited { + return None; + } + visited[v] = VisitType::Visiting; + let sub = solve_functional_graph_dfs(a, mvs, visited, a[v]); + visited[v] = VisitType::Visited; + if let Some(mut sub) = sub { + if sub[0] == v { + sub.reverse(); + mvs.push(MoveType::Loop(sub)); + return None; + } else { + sub.push(v); + return Some(sub); + } + } + mvs.push(MoveType::Single(a[v], v)); + None +} + +/// Returns an operation sequence that achieves the desired state `a`. +fn solve_functional_graph(a: &[usize]) -> Vec { + let n = a.len(); + let mut mvs = vec![]; + let mut visited = vec![VisitType::NotVisited; n]; + for v in 0..n { + if visited[v] == VisitType::NotVisited { + let result = solve_functional_graph_dfs(a, &mut mvs, &mut visited, v); + assert_eq!(result, None); + } + } + // Reversing order is necessary. + mvs.reverse(); + mvs +} + pub fn assign(a: &[(String, String)], tmp: &str) -> Vec<(String, String)> { - let mut operations = a.to_vec(); - // TODO: Ad-hoc assignment for mere swapping - if a.len() == 2 && a[0].0 == a[1].1 && a[0].1 == a[1].0 { - let (x, y) = a[0].clone(); - operations = vec![ - (x.clone(), tmp.to_string()), - (y.clone(), x.clone()), - (tmp.to_string(), y.clone()), - ]; + // Creates a one-to-one mapping between strings and indices + let mut seen = HashMap::new(); + let mut count = 0; + let mut name_table = vec![]; + for (x, y) in a { + if !seen.contains_key(x) { + seen.insert(x.clone(), count); + name_table.push(x.clone()); + count += 1; + } + if !seen.contains_key(y) { + seen.insert(y.clone(), count); + name_table.push(y.clone()); + count += 1; + } + } + // Converts a into mapping between indices + let mut indmap: Vec = (0..count).collect(); + for (src, dst) in a { + let src_index = seen[src]; + let dst_index = seen[dst]; + indmap[dst_index] = src_index; + } + let raw_operations = solve_functional_graph(&indmap); + let mut operations = vec![]; + for op in raw_operations { + match op { + MoveType::Loop(regs) => { + // if length is 1, then no moving is required. + if regs.len() >= 2 { + operations.push((name_table[regs[0]].clone(), tmp.to_string())); + for i in (1..regs.len()).rev() { + operations.push(( + name_table[regs[i]].clone(), + name_table[regs[(i + 1) % regs.len()]].clone(), + )); + } + operations.push((tmp.to_string(), name_table[regs[1]].clone())); + } + } + MoveType::Single(src, dst) => { + operations.push((name_table[src].clone(), name_table[dst].clone())); + } + } } assert_assignment(a, &operations); operations } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn solve_functional_graph_works_correctly() { + // Simple loops + assert_eq!( + solve_functional_graph(&[0, 1, 2]), + vec![ + MoveType::Loop(vec![2]), + MoveType::Loop(vec![1]), + MoveType::Loop(vec![0]), + ] + ); + + // 0 -> 1 -> 2 + assert_eq!( + solve_functional_graph(&[0, 0, 1]), + vec![ + MoveType::Single(1, 2), + MoveType::Single(0, 1), + MoveType::Loop(vec![0]), + ] + ); + + // 0 <- 1 <- 2 + assert_eq!( + solve_functional_graph(&[1, 2, 2]), + vec![ + MoveType::Single(1, 0), + MoveType::Single(2, 1), + MoveType::Loop(vec![2]), + ] + ); + + // 0 -> 1 -> 2 -> 0 + assert_eq!( + solve_functional_graph(&[1, 2, 0]), + // The order is not relevant. + vec![MoveType::Loop(vec![1, 2, 0])], + ); + + // 0 <-> 1 -> 2 + assert_eq!( + solve_functional_graph(&[1, 0, 1]), + vec![MoveType::Single(1, 2), MoveType::Loop(vec![1, 0])] + ) + } +} From 1c87d3c40e9a2b72b5f5250840437ed65a44a687 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Fri, 13 Dec 2019 02:29:30 +0900 Subject: [PATCH 12/15] Fix assembly filename --- Makefile | 6 +++--- check-test-mls.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index edd48bf..7c4292b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -%.x: %.asm assets/x86_64/libmincaml.S assets/stub.c - cc assets/stub.c assets/x86_64/libmincaml.S $*.asm -o $*.x -%.asm: %.ml +%.x: %.S assets/x86_64/libmincaml.S assets/stub.c + cc assets/stub.c assets/x86_64/libmincaml.S $*.S -o $*.x +%.S: %.ml cargo run $< $@ diff --git a/check-test-mls.sh b/check-test-mls.sh index dbfa7aa..039e32b 100755 --- a/check-test-mls.sh +++ b/check-test-mls.sh @@ -8,8 +8,8 @@ NECESSARY_FILES="assets/stub.c assets/${ARCH}/libmincaml.S" if cargo build --release; then for i in test/*.ml; do echo "---- ${i} ----" - if cargo run --release -q -- ${i} ${i}.asm >/dev/null 2>&1; then - if cc ${i}.asm ${NECESSARY_FILES} -o ${i}.x; then + if cargo run --release -q -- ${i} ${i}.S >/dev/null 2>&1; then + if cc ${i}.S ${NECESSARY_FILES} -o ${i}.x; then if ./${i}.x >${i}.out; then # testrun ml ocaml ${i} >${i}.ans From 7baf33af0446b4a28fb776c90ee5805a2bbe47d7 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Fri, 13 Dec 2019 02:37:20 +0900 Subject: [PATCH 13/15] Fix emission format --- src/x86_64/emit.rs | 3 ++- src/x86_64/instr.rs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/x86_64/emit.rs b/src/x86_64/emit.rs index 884fc30..619e12f 100644 --- a/src/x86_64/emit.rs +++ b/src/x86_64/emit.rs @@ -524,11 +524,12 @@ let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } = pub fn f(Prog(data, fundefs, e): Prog, id_gen: &mut IdGen) -> Result, Error> { let mut instrs = Vec::new(); - instrs.push(Instr::Label(".data".to_string())); + instrs.push(Instr::Section(".data".to_string())); instrs.push(Instr::BAlign(8)); for (id::L(x), d) in data.into_vec() { // TODO } + instrs.push(Instr::Section(".text".to_string())); for fundef in fundefs.into_vec() { h(&mut instrs, fundef, id_gen)?; } diff --git a/src/x86_64/instr.rs b/src/x86_64/instr.rs index 1ac0f42..4aef6f1 100644 --- a/src/x86_64/instr.rs +++ b/src/x86_64/instr.rs @@ -79,6 +79,7 @@ pub enum Instr { Label(String), BAlign(i32), Globl(String), + Section(String), // instructions MovRR(R64, R64), MovIR(i32, R64), @@ -103,6 +104,7 @@ impl Display for Instr { Instr::Label(s) => writeln!(f, "{}:", s)?, Instr::BAlign(align) => writeln!(f, ".balign\t{}", align)?, Instr::Globl(s) => writeln!(f, ".globl\t{}", s)?, + Instr::Section(name) => writeln!(f, "{}", name)?, Instr::MovRR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, Instr::MovIR(src, dst) => writeln!(f, " movq ${}, {}", src, dst)?, Instr::MovMR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, From 15dd63d6991d805702232289f02846b0e82eae8e Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Fri, 13 Dec 2019 02:46:47 +0900 Subject: [PATCH 14/15] Install OCaml --- .travis-ocaml.sh | 337 +++++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 5 + 2 files changed, 342 insertions(+) create mode 100644 .travis-ocaml.sh diff --git a/.travis-ocaml.sh b/.travis-ocaml.sh new file mode 100644 index 0000000..df9c645 --- /dev/null +++ b/.travis-ocaml.sh @@ -0,0 +1,337 @@ +## basic OCaml and opam installation + +full_apt_version () { + package=$1 + version=$2 + case "${version}" in + latest) echo -n "${package}" ;; + *) echo -n "${package}=" + apt-cache show "$package" \ + | sed -n "s/^Version: \(${version}\)/\1/p" \ + | head -1 + esac +} + +set -uex + + +# the ocaml version to test +OCAML_VERSION=${OCAML_VERSION:-latest} +SYS_OCAML_VERSION=4.02 +# Default opam is the latest release of opam 2 +OPAM_VERSION=${OPAM_VERSION:-2} +OPAM_INIT=${OPAM_INIT:-true} + +OPAM_LATEST_RELEASE=2.0.5 + +case $OPAM_VERSION in + 2|2.0) OPAM_VERSION=$OPAM_LATEST_RELEASE;; +esac + +if [ "$TRAVIS_OS_NAME" = "osx" ] ; then + brew update &> /dev/null + BREW_OPAM_VERSION=$(brew info opam --json=v1 | sed -e 's/.*"versions":{[^}]*"stable":"//' -e 's/".*//') + if [ "$OPAM_VERSION" != "$BREW_OPAM_VERSION" ] ; then + set +x + echo -e "[\e[0;31mWARNING\e[0m] Ignored OPAM_VERSION=$OPAM_VERSION; interpreted as \"$BREW_OPAM_VERSION\"" >&2 + echo -e "[\e[0;31mWARNING\e[0m] opam 2 is installed via Homebrew" >&2 + set -x + fi + OPAM_VERSION="$BREW_OPAM_VERSION" +fi + +if [ "$OPAM_VERSION" != "$OPAM_LATEST_RELEASE" ] ; then + set +x + echo -e "[\e[0;31mWARNING\e[0m] Out-of-date opam $OPAM_VERSION requested" >&2 + echo -e "[\e[0;31mWARNING\e[0m] Latest release is $OPAM_LATEST_RELEASE" >&2 + set -x +fi + +if [ "${INSTALL_LOCAL+x}" = x ] ; then + if [ "$TRAVIS_OS_NAME" = osx ] ; then + echo INSTALL_LOCAL not permitted for macOS targets + exit 1 + fi + + case ${OPAM_VERSION} in + 2*) + if [ "${OPAM_SWITCH:=ocaml-system}" != ocaml-system ] ; then + echo "INSTALL_LOCAL requires OPAM_SWITCH=ocaml-system (or unset/null)" + exit 1 + fi ;; + *) + if [ "${OPAM_SWITCH:=system}" != system ] ; then + echo "INSTALL_LOCAL requires OPAM_SWITCH=system (or unset/null)" + exit 1 + fi ;; + esac +fi + +# the base opam repository to use for bootstrapping and catch-all namespace +case $OPAM_VERSION in + 2*) BASE_REMOTE=${BASE_REMOTE:-git://github.com/ocaml/opam-repository} ;; + *) BASE_REMOTE=${BASE_REMOTE:-git://github.com/ocaml/opam-repository#1.2} ;; +esac + +# whether we need a new gcc and binutils +UPDATE_GCC_BINUTILS=${UPDATE_GCC_BINUTILS:-"0"} + +# Install Trusty remotes +UBUNTU_TRUSTY=${UBUNTU_TRUSTY:-"0"} + +# Install XQuartz on OSX +INSTALL_XQUARTZ=${INSTALL_XQUARTZ:-"false"} + +APT_UPDATED=0 + +add_ppa () { + if [ "$TRAVIS_OS_NAME" = "linux" ] ; then + APT_UPDATED=0 + sudo add-apt-repository --yes ppa:$1 + fi +} + +apt_install () { + if [ "$TRAVIS_OS_NAME" = "linux" ] ; then + if [ "$APT_UPDATED" -eq 0 ] ; then + APT_UPDATED=1 + sudo apt-get update -qq + fi + sudo apt-get install --no-install-recommends -y "$@" + fi +} + +install_ocaml () { + apt_install \ + ocaml ocaml-base ocaml-native-compilers ocaml-compiler-libs \ + ocaml-interp ocaml-base-nox ocaml-nox +} + +install_opam2 () { + case $TRAVIS_OS_NAME in + linux) + case $TRAVIS_DIST in + precise|trusty|xenial) + add_ppa ansible/bubblewrap ;; + esac + if [ "${INSTALL_LOCAL:=0}" = 0 ] ; then + install_ocaml + fi + apt_install bubblewrap + sudo wget https://github.com/ocaml/opam/releases/download/$OPAM_VERSION/opam-$OPAM_VERSION-x86_64-linux -O /usr/local/bin/opam + sudo chmod +x /usr/local/bin/opam ;; + osx) + if [ "${INSTALL_LOCAL:=0}" = 0 ] ; then + brew install ocaml + fi + sudo curl -fsSL https://github.com/ocaml/opam/releases/download/$OPAM_VERSION/opam-$OPAM_VERSION-x86_64-macos -o /usr/local/bin/opam + sudo chmod +x /usr/local/bin/opam ;; + esac +} + +install_ppa () { + add_ppa $1 + if [ "${INSTALL_LOCAL:=0}" = 0 ] ; then + sudo apt-get -qq update + APT_UPDATED=1 + apt_install \ + "$(full_apt_version ocaml $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-base $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-native-compilers $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-compiler-libs $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-interp $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-base-nox $SYS_OCAML_VERSION)" \ + "$(full_apt_version ocaml-nox $SYS_OCAML_VERSION)" + fi + apt_install opam +} + +install_on_linux () { + case "$OCAML_VERSION,$OPAM_VERSION" in + 3.12,1.2.2) + OCAML_FULL_VERSION=3.12.1 + install_ppa avsm/ocaml42+opam12 ;; + 3.12,2*) + OCAML_FULL_VERSION=3.12.1 + install_opam2 ;; + 4.00,1.2.2) + OCAML_FULL_VERSION=4.00.1 + install_ppa avsm/ocaml42+opam12 ;; + 4.00,2*) + OCAML_FULL_VERSION=4.00.1 + install_opam2 ;; + 4.01,1.2.2) + OCAML_FULL_VERSION=4.01.0 + install_ppa avsm/ocaml42+opam12 ;; + 4.01,2*) + OCAML_FULL_VERSION=4.01.0 + install_opam2 ;; + 4.02,1.1.2) + OCAML_FULL_VERSION=4.02.3 + OPAM_SWITCH=${OPAM_SWITCH:-system} + install_ppa avsm/ocaml42+opam11 ;; + 4.02,1.2.0) + OCAML_FULL_VERSION=4.02.3 + OPAM_SWITCH=${OPAM_SWITCH:-system} + install_ppa avsm/ocaml42+opam120 ;; + 4.02,1.2.1) + OCAML_FULL_VERSION=4.02.3 + OPAM_SWITCH=${OPAM_SWITCH:-system} + install_ppa avsm/ocaml42+opam121 ;; + 4.02,1.2.2) + OCAML_FULL_VERSION=4.02.3 + OPAM_SWITCH=${OPAM_SWITCH:-system} + install_ppa avsm/ocaml42+opam12 ;; + 4.02,2*) + OCAML_FULL_VERSION=4.02.3 + install_opam2 ;; + 4.03,1.2.2) + OCAML_FULL_VERSION=4.03.0 + install_ppa avsm/ocaml42+opam12 ;; + 4.03,2*) + OCAML_FULL_VERSION=4.03.0 + install_opam2 ;; + 4.04,1.2.2) + OCAML_FULL_VERSION=4.04.2 + install_ppa avsm/ocaml42+opam12 ;; + 4.04,2*) + OCAML_FULL_VERSION=4.04.2 + install_opam2 ;; + 4.05,1.2.2) + OCAML_FULL_VERSION=4.05.0 + install_ppa avsm/ocaml42+opam12 ;; + 4.05,2*) + OCAML_FULL_VERSION=4.05.0 + install_opam2 ;; + 4.06,1.2.2) + OCAML_FULL_VERSION=4.06.1 + install_ppa avsm/ocaml42+opam12 ;; + 4.06,2*) + OCAML_FULL_VERSION=4.06.1 + install_opam2 ;; + 4.07,1.2.2) + OCAML_FULL_VERSION=4.07.1 + install_ppa avsm/ocaml42+opam12 ;; + 4.07,2*) + OCAML_FULL_VERSION=4.07.1 + install_opam2 ;; + 4.08,1.2.2) + OCAML_FULL_VERSION=4.08.1 + install_ppa avsm/ocaml42+opam12 ;; + 4.08,2*) + OCAML_FULL_VERSION=4.08.1 + install_opam2 ;; + 4.09,1.2.2) + OCAML_FULL_VERSION=4.09.0 + install_ppa avsm/ocaml42+opam12 ;; + 4.09,2*) + OCAML_FULL_VERSION=4.09.0 + install_opam2 ;; + *) echo "Unknown OCAML_VERSION=$OCAML_VERSION OPAM_VERSION=$OPAM_VERSION" + echo "(An unset OCAML_VERSION used to default to \"latest\", but you must now specify it." + echo "Try something like \"OCAML_VERSION=3.12\", \"OCAML_VERSION=4.09\", or see README-travis.md at https://github.com/ocaml/ocaml-ci-scripts )" + exit 1 ;; + esac + + TRUSTY="deb mirror://mirrors.ubuntu.com/mirrors.txt trusty main restricted universe" + + if [ "$UPDATE_GCC_BINUTILS" != "0" ] ; then + echo "installing a recent gcc and binutils (mainly to get mirage-entropy-xen working!)" + sudo add-apt-repository "${TRUSTY}" + sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test + sudo apt-get -qq update + sudo apt-get install -y gcc-4.8 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90 + sudo add-apt-repository -r "${TRUSTY}" + fi + + if [ "$UBUNTU_TRUSTY" != "0" ] ; then + echo "Adding Ubuntu Trusty mirrors" + sudo add-apt-repository "${TRUSTY}" + sudo apt-get -qq update + APT_UPDATED=1 + fi + + if [ "${INSTALL_LOCAL:=0}" != 0 ] ; then + echo -en "travis_fold:start:build.ocaml\r" + echo "Building a local OCaml; this may take a few minutes..." + wget "http://caml.inria.fr/pub/distrib/ocaml-${OCAML_FULL_VERSION%.*}/ocaml-$OCAML_FULL_VERSION.tar.gz" + tar -xzf "ocaml-$OCAML_FULL_VERSION.tar.gz" + cd "ocaml-$OCAML_FULL_VERSION" + ./configure -prefix /usr/local ${OCAML_CONFIGURE_ARGS:=--with-debug-runtime} + make world.opt + sudo make install + cd .. + echo -en "travis_fold:end:build.ocaml\r" + fi +} + +install_on_osx () { + case $INSTALL_XQUARTZ in + true) + curl -OL "http://xquartz.macosforge.org/downloads/SL/XQuartz-2.7.6.dmg" + sudo hdiutil attach XQuartz-2.7.6.dmg + sudo installer -verbose -pkg /Volumes/XQuartz-2.7.6/XQuartz.pkg -target / + ;; + esac + case "$OCAML_VERSION,$OPAM_VERSION" in + 3.12,1.2.2) OCAML_FULL_VERSION=3.12.1; brew install opam ;; + 3.12,2*) OCAML_FULL_VERSION=3.12.1; install_opam2 ;; + 4.00,1.2.2) OCAML_FULL_VERSION=4.00.1; brew install opam ;; + 4.00,2*) OCAML_FULL_VERSION=4.00.1; install_opam2 ;; + 4.01,1.2.2) OCAML_FULL_VERSION=4.01.0; brew install opam ;; + 4.01,2*) OCAML_FULL_VERSION=4.01.0; install_opam2 ;; + 4.02,1.2.2) OCAML_FULL_VERSION=4.02.3; brew install opam ;; + 4.02,2*) OCAML_FULL_VERSION=4.02.3; install_opam2 ;; + 4.03,1.2.2) OCAML_FULL_VERSION=4.03.0; brew install opam ;; + 4.03,2*) OCAML_FULL_VERSION=4.03.0; install_opam2 ;; + 4.04,1.2.2) OCAML_FULL_VERSION=4.04.2; brew install opam ;; + 4.04,2*) OCAML_FULL_VERSION=4.04.2; install_opam2 ;; + 4.05,1.2.2) OCAML_FULL_VERSION=4.05.0; brew install opam ;; + 4.05,2*) OCAML_FULL_VERSION=4.05.0; install_opam2 ;; + 4.06,1.2.2) OCAML_FULL_VERSION=4.06.1; brew install opam ;; + 4.06,2*) OCAML_FULL_VERSION=4.06.1; install_opam2 ;; + 4.07,1.2.2) OCAML_FULL_VERSION=4.07.1; brew install opam ;; + 4.07,2*) OCAML_FULL_VERSION=4.07.1; install_opam2 ;; + 4.08,1.2.2) OCAML_FULL_VERSION=4.08.1; + OPAM_SWITCH=${OPAM_SWITCH:-system}; + brew install ocaml; + brew install opam ;; + 4.08,2*) OCAML_FULL_VERSION=4.08.1; + OPAM_SWITCH=${OPAM_SWITCH:-ocaml-system}; + brew install ocaml; + install_opam2 ;; + 4.09,1.2.2) OCAML_FULL_VERSION=4.09.0; brew install opam ;; + 4.09,2*) OCAML_FULL_VERSION=4.09.0; install_opam2 ;; + *) echo "Unknown OCAML_VERSION=$OCAML_VERSION OPAM_VERSION=$OPAM_VERSION" + exit 1 ;; + esac +} + +case $TRAVIS_OS_NAME in + osx) install_on_osx ;; + linux) install_on_linux ;; +esac + +OPAM_SWITCH=${OPAM_SWITCH:-ocaml-base-compiler.$OCAML_FULL_VERSION} + +export OPAMYES=1 + +case $OPAM_INIT in + true) + opam init -a "$BASE_REMOTE" --comp="$OPAM_SWITCH" + eval $(opam config env) + ;; +esac + +echo OCAML_VERSION=$OCAML_VERSION > .travis-ocaml.env +echo OPAM_SWITCH=$OPAM_SWITCH >> .travis-ocaml.env + +if [ -x "$(command -v ocaml)" ]; then + ocaml -version +else + echo "OCaml is not yet installed" +fi + +opam --version +opam --git-version diff --git a/.travis.yml b/.travis.yml index 1cc8426..5d30df8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: rust +env: + global: + - OCAML_VERSION=4.02 matrix: include: - rust: stable @@ -9,7 +12,9 @@ matrix: env: RUST_CHANNEL=nightly allow_failures: - rust: nightly + fast_finish: true install: + - bash -ex .travis-ocaml.sh - cargo install cargo-bloat - rustup component add clippy rustfmt - if [ $RUST_CHANNEL == "nightly" ]; then From bd88a262d5851e42f9c762e4666cd716e0835460 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Sat, 14 Dec 2019 04:00:41 +0900 Subject: [PATCH 15/15] Make ack.ml pass (12/32 pass currently) --- src/x86_64/asm.rs | 4 ++- src/x86_64/emit.rs | 76 +++++++++++++++++++++++++++++++++++++++++++-- src/x86_64/instr.rs | 36 ++++++++++++++++++++- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/x86_64/asm.rs b/src/x86_64/asm.rs index e2da6c9..0e1df08 100644 --- a/src/x86_64/asm.rs +++ b/src/x86_64/asm.rs @@ -248,6 +248,8 @@ pub fn seq(id_gen: &mut id::IdGen, e1: Exp, e2: Asm) -> Asm { } pub const REGS: [&str; 6] = ["%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi"]; +// TODO: fill in +pub const FREGS: [&str; 1] = ["%xmm0"]; pub fn regs() -> Vec { let mut res = vec!["".to_string(); 6]; @@ -270,7 +272,7 @@ pub fn reg_cl() -> &'static str { REGS[REGS.len() - 1] } -pub const REG_SP: &str = "%ebp"; +pub const REG_SP: &str = "%rbp"; pub const REG_HP: &str = "min_caml_hp"; pub fn is_reg(x: &str) -> bool { diff --git a/src/x86_64/emit.rs b/src/x86_64/emit.rs index 619e12f..a7d3fcf 100644 --- a/src/x86_64/emit.rs +++ b/src/x86_64/emit.rs @@ -5,10 +5,10 @@ use id; use id::IdGen; use syntax::IntBin; use x86_64::asm; -use x86_64::asm::{fregs, Asm, CompBin, Exp, Fundef, IdOrImm, Prog, REGS, REG_SP}; +use x86_64::asm::{fregs, Asm, CompBin, Exp, Fundef, IdOrImm, Prog, FREGS, REGS, REG_SP}; use x86_64::error::Error; use x86_64::instr::Instr::MovRR; -use x86_64::instr::{cmpq, movq, subq, Instr, R64}; +use x86_64::instr::{addq, cmpq, loadmem, movq, savemem, subq, Instr, R64}; /* open Asm @@ -135,6 +135,24 @@ fn g_exp( output.push(movq(IdOrImm::V(y), x)?); } } + /* + | NonTail(x), Add(y, z') -> + if V(x) = z' then + Printf.fprintf oc "\taddl\t%s, %s\n" y x + else + (if x <> y then Printf.fprintf oc "\tmovl\t%s, %s\n" y x; + Printf.fprintf oc "\taddl\t%s, %s\n" (pp_id_or_imm z') x) + */ + (NonTail(x), Exp::IntOp(IntBin::Add, y, z_p)) => { + if IdOrImm::V(x.clone()) == z_p { + output.push(addq(y, x)?); + } else { + if x != y { + output.push(movq(y, x.clone())?); + output.push(addq(z_p, x)?); + } + } + } /* | NonTail(x), Sub(y, z') -> if V(x) = z' then @@ -157,6 +175,45 @@ fn g_exp( output.push(subq(z_p, x)?); } } + /* + (* 退避の仮想命令の実装 (caml2html: emit_save) *) + | NonTail(_), Save(x, y) when List.mem x allregs && not (S.mem y !stackset) -> + save y; + Printf.fprintf oc "\tmovl\t%s, %d(%s)\n" x (offset y) reg_sp + | NonTail(_), Save(x, y) when List.mem x allfregs && not (S.mem y !stackset) -> + savef y; + Printf.fprintf oc "\tmovsd\t%s, %d(%s)\n" x (offset y) reg_sp + | NonTail(_), Save(x, y) -> assert (S.mem y !stackset); () + */ + (NonTail(_), Exp::Save(x, y)) => { + if REGS.contains(&(x.as_ref())) && stack_state.offset(&y).is_none() { + stack_state.save(&y); + output.push(savemem(x, REG_SP, stack_state.offset(&y).unwrap())? ); + } else + if FREGS.contains(&(x.as_ref())) && stack_state.offset(&y).is_none() { + unimplemented!(); + }else { + assert!(stack_state.offset(&y).is_some()); + } + } + /* + (* 復帰の仮想命令の実装 (caml2html: emit_restore) *) + | NonTail(x), Restore(y) when List.mem x allregs -> + Printf.fprintf oc "\tmovl\t%d(%s), %s\n" (offset y) reg_sp x + | NonTail(x), Restore(y) -> + assert (List.mem x allfregs); + Printf.fprintf oc "\tmovsd\t%d(%s), %s\n" (offset y) reg_sp x + */ + (NonTail(x), Exp::Restore(y)) => { + if REGS.contains(&x.as_ref()) { + output.push(loadmem(REG_SP, stack_state.offset(&y).unwrap(), x)?); + } else + if FREGS.contains(&x.as_ref()) { + unimplemented!(); + } else { + unreachable!(); + } + } /* | Tail, CallDir(Id.L(x), ys, zs) -> (* 末尾呼び出し *) g'_args oc [] ys zs; @@ -215,6 +272,8 @@ fn g_exp( g' oc (NonTail(regs.(0)), exp); Printf.fprintf oc "\tret\n"; */ + // Because we cannot juxtapose patterns and bind them to a different name, + // we just duplicate patterns. (Tail, Exp::Mov(src)) => { let exp = Exp::Mov(src); g_exp( @@ -226,6 +285,17 @@ fn g_exp( )?; output.push(Instr::Ret); } + (Tail, Exp::IntOp(op, x, y_p)) => { + let exp = Exp::IntOp(op, x, y_p); + g_exp( + output, + stack_state, + id_gen, + Dest::NonTail(REGS[0].to_string()), + exp, + )?; + output.push(Instr::Ret); + } (Tail, Exp::IfComp(op, x, y_p, e1, e2)) => { output.push(cmpq(y_p, x)?); let (if_label, else_label) = match op { @@ -546,6 +616,8 @@ pub fn f(Prog(data, fundefs, e): Prog, id_gen: &mut IdGen) -> Result, Printf.fprintf oc "\tmovl\t36(%%esp),%s\n" regs.(0); Printf.fprintf oc "\tmovl\t%s,%s\n" regs.(0) reg_hp; */ + instrs.push(movq("%rdi".to_string(), REG_SP)?); // %rdi = sp + // TODO: %rsi = hp g( &mut instrs, &mut StackState::new(), diff --git a/src/x86_64/instr.rs b/src/x86_64/instr.rs index 4aef6f1..ff205e5 100644 --- a/src/x86_64/instr.rs +++ b/src/x86_64/instr.rs @@ -55,6 +55,13 @@ impl TryFrom for RI64 { } } +impl TryFrom for RI64 { + type Error = RegisterNameError; + fn try_from(x: String) -> Result { + Ok(RI64::R64(x.try_into()?)) + } +} + impl Display for RI64 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -64,8 +71,11 @@ impl Display for RI64 { } } +// displacement +pub type Disp = i32; + #[derive(Debug, Clone, Copy)] -pub struct M32(pub R64, pub i32); +pub struct M32(pub R64, pub Disp); impl Display for M32 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -84,6 +94,7 @@ pub enum Instr { MovRR(R64, R64), MovIR(i32, R64), MovMR(M32, R64), + MovRM(R64, M32), Ret, PushQ(R64), PopQ(R64), @@ -108,6 +119,7 @@ impl Display for Instr { Instr::MovRR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, Instr::MovIR(src, dst) => writeln!(f, " movq ${}, {}", src, dst)?, Instr::MovMR(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, + Instr::MovRM(src, dst) => writeln!(f, " movq {}, {}", src, dst)?, Instr::Ret => writeln!(f, " ret")?, Instr::PushQ(reg) => writeln!(f, " pushq {}", reg)?, Instr::PopQ(reg) => writeln!(f, " popq {}", reg)?, @@ -137,6 +149,28 @@ pub fn movq( Ok(instr) } +pub fn savemem( + src: impl TryInto, + dstreg: impl TryInto, + dstoffset: Disp, +) -> Result { + let src = src.try_into()?; + let dstreg = dstreg.try_into()?; + let instr = Instr::MovRM(src, M32(dstreg, dstoffset)); + Ok(instr) +} + +pub fn loadmem( + srcreg: impl TryInto, + srcoffset: Disp, + dst: impl TryInto, +) -> Result { + let srcreg = srcreg.try_into()?; + let dst = dst.try_into()?; + let instr = Instr::MovMR(M32(srcreg, srcoffset), dst); + Ok(instr) +} + pub fn addq( src: impl TryInto, dst: impl TryInto,