Haxe 3.0 でx86_64の簡単なコンパイラを作ってみた。
四則演算を複数行って出力出来るだけですけど、
HaxeをC++に変換して実行すると、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使ってたので、自然に使い始めてしまいました。