8.4.5 Unit Test
内联的 UT 可以参考IR Tree, 因为内联操作修改的便是 IR Tree. 例如如果我们想要测试如下代码:
package p
func C() {
println("C")
D()
}
func D() {
println("D")
C()
}
func main() {
C()
}
为了方便,这里我们将所有的测试代码拷贝到测试文件 cmd/compile/internal/noder/inl_test.go
, 完整内容如下:
package noder
import (
"bufio"
"cmd/compile/internal/amd64"
"cmd/compile/internal/base"
"cmd/compile/internal/deadcode"
"cmd/compile/internal/escape"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/compile/internal/types2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"fmt"
"os"
"strings"
"testing"
)
func parseSrc(path, src string) (*syntax.File, error) {
var mode syntax.Mode
mode = syntax.AllowGenerics | syntax.CheckBranches
errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, mode)
}
func defaultImporter() *gcimports {
return &gcimports{
packages: map[string]*types2.Package{},
}
}
func init() {
amd64.Init(&ssagen.Arch)
base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
base.Ctxt.DiagFunc = base.Errorf
base.Ctxt.DiagFlush = base.FlushErrors
base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin
types.LocalPkg = types.NewPkg("", "")
types.LocalPkg.Prefix = "\"\""
types.LocalPkg.Height = types.MaxPkgHeight
types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin
// pseudo-package, accessed by import "unsafe"
ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe")
ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
ir.Pkgs.Runtime.Prefix = "runtime"
// pseudo-packages used in symbol tables
ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
ir.Pkgs.Itab.Prefix = "go.itab" // not go%2eitab
// pseudo-package used for methods with anonymous receivers
ir.Pkgs.Go = types.NewPkg("go", "")
base.DebugSSA = ssa.PhaseOption
ssagen.Arch.LinkArch.Init(base.Ctxt)
ir.EscFmt = escape.Fmt
ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
inline.SSADumpInline = ssagen.DumpInline
ssagen.InitEnv()
ssagen.InitTables()
types.PtrSize = ssagen.Arch.LinkArch.PtrSize
types.RegSize = ssagen.Arch.LinkArch.RegSize
types.MaxWidth = ssagen.Arch.MAXWIDTH
typecheck.Target = new(ir.Package)
typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) }
typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock?
base.AutogeneratedPos = base.Ctxt.PosTable.XPos(src.MakePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0))
typecheck.InitUniverse()
}
func dumpIrTree(nodes []ir.Node) {
for _, n := range nodes {
s := fmt.Sprintf("\nafter noder2 %v", n)
ir.Dump(s, n)
}
}
func TestIrTree(t *testing.T) {
code := `
package p
func C( ) {
println("C")
D()
}
func D( ) {
println("D")
C()
}
func main() {
C()
}
`
base.Flag.LowerM = 2
f, err := parseSrc("Inline", code)
if err != nil {
panic(err)
}
var conf types2.Config
conf.Trace = false
conf.Importer = defaultImporter()
conf.Error = func(err error) {
fmt.Printf("Typecheck Error: %v\n", err)
}
info := types2.Info{
Types: make(map[syntax.Expr]types2.TypeAndValue),
Defs: make(map[*syntax.Name]types2.Object),
Uses: make(map[*syntax.Name]types2.Object),
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
Implicits: make(map[syntax.Node]types2.Object),
Scopes: make(map[syntax.Node]*types2.Scope),
Inferred: make(map[syntax.Expr]types2.Inferred),
}
pkg, err := conf.Check("<no package>", []*syntax.File{f}, &info)
if err != nil {
panic(err)
}
var m posMap
g := irgen{
target: typecheck.Target,
self: pkg,
info: &info,
posMap: m,
objs: make(map[types2.Object]*ir.Name),
typs: make(map[types2.Type]*types.Type),
}
p := &noder{
err: make(chan syntax.Error),
trackScopes: base.Flag.Dwarf,
file: f,
}
g.generate([]*noder{p})
println("Before Inline:")
dumpIrTree(typecheck.Target.Decls)
inline.InlinePackage()
println("After Inline:")
dumpIrTree(typecheck.Target.Decls)
}
运行该测试用例,内联后的函数 C 与 D 的结构与前文我们分析的一致:
after noder2 C [0xc0001ec2c0] . DCLFUNC tc(1) Iota:-1 ABI:ABIInternal InlinabilityChecked FUNC-func() # Inline:4 . DCLFUNC-body . . PRINTN tc(1) Use:3 # Inline:5 . . PRINTN-Args . . . LITERAL-“C” tc(1) string # Inline:5 . . CALLFUNC tc(1) Use:3 # Inline:6 . . . NAME-p.D tc(1) Class:PFUNC Offset:0 FUNC-func() # Inline:9 after noder2 D [0xc0001ec420] . DCLFUNC tc(1) Iota:-1 Label:1 ABI:ABIInternal InlinabilityChecked FUNC-func() # Inline:9 . DCLFUNC-body . . PRINTN tc(1) Use:3 # Inline:10 . . PRINTN-Args . . . LITERAL-“D” tc(1) string # Inline:10 . . BLOCK # Inline:11 . . BLOCK-List . . . INLMARK # +Inline:11 . . . PRINTN tc(1) Use:3 # Inline:5 . . . PRINTN-Args . . . . LITERAL-“C” tc(1) string # Inline:5 . . . CALLFUNC tc(1) Use:3 # Inline:6 . . . . NAME-p.D tc(1) Class:PFUNC Offset:0 FUNC-func() # Inline:9 . . . LABEL # Inline:11 p..i0
读者可以尝试着调试更加复杂的代码,以便查看编译器在内联时如何处理参数以及返回值的,这里不再赘述。
最后更新于