# 5.6 Unit Test

IR Tree 构建逻辑的 UT 比前面章节的要麻烦一些，因为其不仅依赖前面的词法分析、语法分析、类型检查等所有步骤，还要依赖整个编译器的全局环境初始化。

编译器的初始化工作在文件 `cmd/compile/internal/gc/main.go` 中的 `Main` 函数中完成，词法分析之前的所有逻辑都是在做初始化的工作，分割点如下所示：

```go
func Main(archInit func(*ssagen.ArchInfo)) {
    // 初始化代码，此处省略

    // 词法分析、语法分析、类型检查
    noder.LoadPackage(flag.Args())

    // 编译阶段后续步骤，此处省略
}
```

编译器的初始化工作涉及很多方面，例如与架构体系相关的初始化（例如 amd64, x86 等）、内置函数及类型、默认配置等，但也有很多代码是搭建 UT 环境不需要的，例如解析编译参数、对编译过程进行 profile 等。为了搭建 IR Tree 的 UT 环境，我们需要做如下工作：

* 精简后的初始化代码
* 语法解析、类型检查等步骤
* Dump IR Tree 的逻辑

我们创建 UT 文件 `cmd/compile/internal/noder/irgen_test.go`, 其内容如下：

```go
// file: cmd/compile/internal/noder/irgen_test.go

package noder

import (
    "bufio"
    "cmd/compile/internal/amd64"
    "cmd/compile/internal/base"
    "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

    ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe")

    ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
    ir.Pkgs.Runtime.Prefix = "runtime"

    ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
    ir.Pkgs.Itab.Prefix = "go.itab" // not go%2eitab

    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

import "fmt"

func genericFun[T any](i, j T)  {
    fmt.Printf("i: %v, j: %v\n",i, j)
}

func main() {
   genericFun(5,6)
   genericFun[float32](10.5, 11.8)
}
`
    f, err := parseSrc("generic_testTypecheckConst", 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})

    dumpIrTree(typecheck.Target.Decls)
}
```

其中 `init` 为精简之后的编译器初始化逻辑， `parseSrc` 用来完成语法分析， `defaultImporter` 用来在类型检查时加载包。运行 `TestIrTree` 将会得到最终的 IR Tree 结构信息。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/golang-bian-yi-qi-ir-tree/5.6-unit-test.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
