示例#1
0
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)
				}
			}
		}
	}
}
示例#2
0
// 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
					}
				}
			}
		}
	}
}