はじめに
Goのbuiltin関数やunsafeパッケージの直接の実装はそれぞれのパッケージには関数とドキュメントしか配置されてません
これらはアセンブリで実装されているわけでもないです。 じゃあどこでこいつら実装されとるんや?というのが気になりいろいろ調べた次第
compiler実装にいる
コンパイラが動くときにbuiltinとunsafeは仮想的なパッケージが作られてる
https://github.com/golang/go/blob/go1.18/src/cmd/compile/internal/gc/main.go#L82-L87
で、初期化されてる BuiltinPkg
と UnsafePkg
はここに定義されてる
https://github.com/golang/go/blob/go1.18/src/cmd/compile/internal/types/fmt.go#L20-L30
コード上の関数と中間表現のOPはこのへんにマッピングしてある
https://github.com/golang/go/blob/go1.18/src/cmd/compile/internal/typecheck/universe.go#L32-L62
InitUniverse()
のなかのこのへんで仮想的なパッケージにマッピングされたOPをつめる
https://github.com/golang/go/blob/go1.18/src/cmd/compile/internal/typecheck/universe.go#L73-L85
で、このOPでソースコード内を絞るとswitch文とかで該当OPを評価してる処理とかがあるので、それをみればよろし
unsafe実装メモ
unsafe実装さがしメモ
unsafe.Alignof
unsafe.Sizeof
unsafe.Offsetof
はおそらくここで実装されてる
https://github.com/golang/go/blob/go1.18/src/cmd/compile/internal/typecheck/const.go#L864-L943
builtin実装メモ
exprの評価をするところが walkExpr1() ってところっぽいので大体ここにいる
walkExpr1の中でそれぞれのOPごとに評価されていくけど、builtin関数については 個別のwalk処理がある
たとえば close()
を例にとるとOPは OCLOSE
で walkExpr1のこのへん で walkClose なる close()
関数用の個別のwalkが呼ばれる
内容は以下
// walkClose walks an OCLOSE node. func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { // cannot use chanfn - closechan takes any, not chan any fn := typecheck.LookupRuntime("closechan") fn = typecheck.SubstArgTypes(fn, n.X.Type()) return mkcall1(fn, nil, init, n.X) }
mkcall1
関数は渡したfnのposを取得したりして最終的に typecheck.Call を呼び出すやつ。typecheck.Call
は関数呼び出しの中間表現を得るやつ。
なのでここでやってることは
- runtimeから
closechan
という識別子を取得 - ↑の関数をよびだす!
みたいな感じ。ようは実行時にruntimeの closechan
が呼び出されるはずなので見に行こう
https://github.com/golang/go/blob/690ac4071fa3e07113bf371c9e74394ab54d6749/src/runtime/chan.go#L356-L425
いた!お前か!これで実装が確認できるな
おまけ
とはいえソースコードからがんばって追いかけるのは面倒
unsafeはruntimeには実装がない(ひとしきり調べたけどそれっぽいのない)けど、builtin関数は最終的にruntimeの処理をよんでるっぽいので、最小のコードを書いて go tool objdump
してお茶を濁す必殺技が使える
go tool objdump
をつかったbultinの探し方は以前書いた記事の後半で触れてるので良ければそちらも見てやってください