func TestSwitches(t *testing.T) { conf := loader.Config{ParserMode: parser.ParseComments} f, err := conf.ParseFile("testdata/switches.go", nil) if err != nil { t.Error(err) return } conf.CreateFromFiles("main", f) iprog, err := conf.Load() if err != nil { t.Error(err) return } prog := ssa.Create(iprog, 0) mainPkg := prog.Package(iprog.Created[0].Pkg) mainPkg.Build() for _, mem := range mainPkg.Members { if fn, ok := mem.(*ssa.Function); ok { if fn.Synthetic != "" { continue // e.g. init() } // Each (multi-line) "switch" comment within // this function must match the printed form // of a ConstSwitch. var wantSwitches []string for _, c := range f.Comments { if fn.Syntax().Pos() <= c.Pos() && c.Pos() < fn.Syntax().End() { text := strings.TrimSpace(c.Text()) if strings.HasPrefix(text, "switch ") { wantSwitches = append(wantSwitches, text) } } } switches := ssautil.Switches(fn) if len(switches) != len(wantSwitches) { t.Errorf("in %s, found %d switches, want %d", fn, len(switches), len(wantSwitches)) } for i, sw := range switches { got := sw.String() if i >= len(wantSwitches) { continue } want := wantSwitches[i] if got != want { t.Errorf("in %s, found switch %d: got <<%s>>, want <<%s>>", fn, i, got, want) } } } } }
// transformSwitches replaces the final If statement in start blocks // with a high-level switch instruction, and erases chained condition // blocks. func (fr *frame) transformSwitches(f *ssa.Function) { for _, sw := range ssautil.Switches(f) { if sw.ConstCases == nil { // TODO(axw) investigate switch // on hashes in type switches. continue } if !isInteger(sw.X.Type()) && !isBoolean(sw.X.Type()) { // LLVM switches can only operate on integers. continue } instr := &switchInstr{Switch: sw} sw.Start.Instrs[len(sw.Start.Instrs)-1] = instr for _, c := range sw.ConstCases[1:] { fr.blocks[c.Block.Index].EraseFromParent() fr.blocks[c.Block.Index] = llvm.BasicBlock{} } // Fix predecessors in successor blocks for fixupPhis. cases, duplicates := dedupConstCases(fr, instr.ConstCases) for _, c := range cases { for _, succ := range c.Block.Succs { for i, pred := range succ.Preds { if pred == c.Block { succ.Preds[i] = sw.Start break } } } } // Remove redundant edges corresponding to duplicate cases // that will not feature in the LLVM switch instruction. for _, c := range duplicates { for _, succ := range c.Block.Succs { for i, pred := range succ.Preds { if pred == c.Block { head := succ.Preds[:i] tail := succ.Preds[i+1:] succ.Preds = append(head, tail...) removePhiEdge(succ, i) break } } } } } }