Haxe 3.0 でx86_64の簡単なコンパイラを作ってみた。

四則演算を複数行って出力出来るだけですけど、
HaxeC++に変換して実行すると、x86_64のアセンブラを吐き出し、gccを呼んでアセンブラアセンブルしてます。

/**
 * Haxeでosx用のx86_64用のコンパイラのコア
 */
import haxe.io.Eof;
import sys.io.File;
using haxe.Int64;

class Main {
  static function main() {
    var a:Int64 = Int64.ofInt(123);

    // printl(1)
    // printl(2+3)
    // printl((2+3)*2)
    var ast = EBlock([
        EPrint(EInt(1)),
        EPrint(EAdd(EInt(2), EInt(3))),
        EPrint(EMul(EAdd(EInt(2), EInt(3)),EInt(2)))
    ]);
    var codes = Compiler.compile(ast);
    Sys.println("# localSize="+ Env.localSize);
    Sys.println("# l=" + codes);
    Emitter.emit("e.s", codes);
    Sys.println(Util.exec("gcc e.s"));
    Sys.println(Util.exec("./a.out"));

  }
}

enum E {
  EBlock(b:Array<E>);
  EInt(i:Int);
  EAdd(a:E, b:E);
  EMul(a:E, b:E);
  EPrint(a:E);
}

class Compiler {
  static var buf = new Array<E2>();
  public static function compile(e:E):Array<E2> {
    f(e);
    return buf;
  }

  static function f(e:E) {
    switch(e) {
      case EBlock(b): for(e in b) { f(e); }
      case EInt(i): buf.push(E2Ldc2w(i));
      case EAdd(a,b): f(a); f(b); buf.push(E2Add);
      case EMul(a,b): f(a); f(b); buf.push(E2Mul);
      case EPrint(a): f(a); buf.push(E2Print);
    }
  }
}

enum E2 {
  E2Add;
  E2Sub;
  E2Mul;
  E2Div;
  E2Print;
  E2Ldc2w(i:Int);
}

class Env {
  static var addrs:Map<String,Int> = new haxe.ds.StringMap<Int>();

  public static var localSize:Int = 0;
  static function add(id:String) {
    localSize += 8;
    addrs[id] = localSize;
  }
}


class Emitter {

  public static function align16(a:Int):Int {
    return Std.int((a+15) / 16) * 16;
  }

  public static function emit(file:String, l:Array<E2>) {
    Asm.open(file);
    Asm.out(".section","__TEXT,__text,regular,pure_instructions");
    Asm.out(".globl","_printl");
    Asm.out(".align","4, 0x90");
    Asm.label("_printl:");
    Asm.out("pushq","%rbp");
    Asm.out("movq","%rsp, %rbp");
    Asm.out("subq","$16, %rsp");
    Asm.out("movq","%rdi, -8(%rbp)");
    Asm.out("movq","-8(%rbp), %rax");
    Asm.out("xorb","%cl, %cl");
    Asm.out("leaq","L_.str(%rip), %rdx");
    Asm.out("movq","%rdx, %rdi");
    Asm.out("movq","%rax, %rsi");
    Asm.out("movb","%cl, %al");
    Asm.out("callq","_printf");
    Asm.out("addq","$16, %rsp");
    Asm.out("popq","%rbp");
    Asm.out("ret");

    Asm.out(".globl","_main");
    Asm.out(".align","4, 0x90");
    Asm.label("_main:");
    Asm.out("pushq", "%rbp");
    Asm.out("movq", "%rsp, %rbp");
    Asm.out("subq","$"+align16(Env.localSize)+", %rsp");
    var bin = function(f) {
        Asm.out("popq", "%rcx");
        Asm.out("popq", "%rax");
        f();
        Asm.out("pushq", "%rax");
    };
    for(i in l) {
      switch(i) {
      case E2Ldc2w(a):
        Asm.out("movq", "$" + a + ", %rax");
        Asm.out("pushq", "%rax");
      case E2Add: bin(function(){Asm.out("addq", "%rcx, %rax");});
      case E2Sub: bin(function(){Asm.out("subq", "%rcx, %rax");});
      case E2Mul: bin(function(){Asm.out("imulq", "%rcx, %rax");});
      case E2Div:
        bin(function(){
          Asm.out("cltd");
          Asm.out("idivq", "%rcx");
        });
      case E2Print:
        Asm.out("popq", "%rdi");
        Asm.out("call", "_printl");
      }
    }

    Asm.out("movl", "$0, -8(%rbp)");
    Asm.out("movl", "-8(%rbp), %eax");
    Asm.out("movl", "%eax, -4(%rbp)");
    Asm.out("movl", "-4(%rbp), %eax");
    Asm.out("addq", "$"+align16(Env.localSize)+", %rsp");
    Asm.out("popq", "%rbp");
    Asm.out("ret");
    Asm.out(".section", "__TEXT,__cstring,cstring_literals");
    Asm.label("L_.str:");
    Asm.out(".asciz","\"%d\n\"");
    Asm.close();
  }
}

class Util {
  public static function readAll(file:String):String {
    return sys.io.File.getContent(file);
  }
  public static function exec(cmd:String):{code:Int,err:String,out:String} {
    var r = ~/\s+/g;
    var cmds = r.split(cmd);
    var c = cmds.shift();
    var proc = new sys.io.Process(c,cmds);
    return {
     code:proc.exitCode(),
     err:proc.stderr.readAll().toString(),
     out:proc.stdout.readAll().toString(),
    }
  }

  static var id = 0;
  public static function genid(s:String):String {
    id += 1;
    return s + id;
  }

}

class Asm {
  static var indent:String = "";
  public static var fout:sys.io.FileOutput;

  public static function open(fname:String) {
    Asm.fout = sys.io.File.write(fname, false);
    indent = "\t";
  }

  public static function label(s:String) {
    indent = "";
    out(s);
    indent = "\t";
  }

  public static function out(s:String, n:String = "") {
    var v = indent + s + "\t" + n + "\n";
    fout.writeString(v);
  }
  public static function close() {
    fout.close();
  }
}

こんなMakefile作って動かしてみてます。

t/Main: Main.hx
	haxe -cpp t -main Main
clean:
	rm -rf t
run:
	cd t; ./Main
remove:
	rm t/a.out t/e.c

なんか、SML#でMakefile使ってたので、自然に使い始めてしまいました。