Go编译器定制简介
chai2010
chai2010
gcc
命令将一个整数编译为一个程序, 程序的返回值返回这个整数值
比如 42
翻译为以下的 C 语言程序:
int main() { return 42; }
也可以翻译为以下的 X86 汇编程序:
.intel_syntax noprefix .globl _main _main: mov rax, 42 ; ret
也可以翻译为 LLVM 跨平台的汇编程序:
define i32 @main() { ret i32 42 ; }
Go语言重新实现如下人肉编译器:
func main() { code, _ := io.ReadAll(os.Stdin) compile(string(code)) } func compile(code string) { output := fmt.Sprintf(tmpl, code) os.WriteFile("a.out.ll", []byte(output), 0666) exec.Command("clang", "-o", "a.out", "a.out.ll").Run() } const tmpl = ` define i32 @main() { ret i32 %v } `
编译 echo 123 | go run main.go
, 执行 ./a.out
, 看结果 echo $?
s
是从最后一个 0
到结尾的内容, 第1个 printf 打印 s
数组, 第2个打印后面部分可能是最短的C自重写代码:
main(a){printf(a="main(a){printf(a=%c%s%c,34,a,34);}",34,a,34);}
rsc 给了Go的版本(Go已经自举, 大家可以放心用了):
package main import "fmt" func main() { fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60) } var q = `package main import "fmt" func main() { fmt.Printf("%s%c%s%c\n", q, 0x60, q, 0x60) } var q = `
token.Pos
表示一个指针, 0 对应 token.NoPos
空指针func main() { fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hello.go", src, parser.AllErrors) if err != nil { log.Fatal(err) } pkg, err := new(types.Config).Check("hello.go", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) } _ = pkg } const src = `package main func main() { var _ = "a" + 1 } `
go/types.Config.Check
可为 AST 每个表达式节点标注类型信息func main() { fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hello.go", src, parser.AllErrors) if err != nil { log.Fatal(err) } info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Scopes: make(map[ast.Node]*types.Scope), } conf := types.Config{Importer: nil} pkg, err := conf.Check("hello.go", fset, []*ast.File{f}, info) if err != nil { log.Fatal(err) } _ = pkg }
func main() { fset := token.NewFileSet() f, _ := parser.ParseFile(fset, "hello.go", src, parser.AllErrors) info := &types.Info{} conf := types.Config{Importer: nil} pkg, _ := conf.Check("hello.go", fset, []*ast.File{f}, info) var ssaProg = ssa.NewProgram(fset, ssa.SanityCheckFunctions) var ssaPkg = ssaProg.CreatePackage(pkg, []*ast.File{f}, info, true) ssaPkg.Build() ssaPkg.WriteTo(os.Stdout) ssaPkg.Func("init").WriteTo(os.Stdout) ssaPkg.Func("main").WriteTo(os.Stdout) }
const src = ` package main var s = "hello ssa" func main() { for i := 0; i < 3; i++ { println(s) } } `
package hello.go: func init func() var init$guard bool func main func() # Name: hello.go.main # Package: hello.go # Location: hello.go:4:6 func main(): 0: entry P:0 S:1 jump 3 1: for.body P:1 S:1 t0 = println("hello ssa -- chai...":string) () t1 = t2 + 1:int int jump 3 2: for.done P:1 S:0 return 3: for.loop P:2 S:2 t2 = phi [0: 0:int, 1: t1] #i int t3 = t2 < 3:int bool if t3 goto 1 else 2
go/*
包太好用µGo 是 Go 语言的子集(不含标准库部分), 可以直接作为Go代码编译执行.
func main() { for n := 2; n <= 30; n = n + 1 { var isPrime int = 1 for i := 2; i*i <= n; i = i + 1 { if x := n % i; x == 0 { isPrime = 0 } } if isPrime != 0 { println(n) } } }
go get github.com/wa-lang/ugo
ugo run hello.ugo