Home | History | Annotate | Download | only in ssa
      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This file contains some utility functions to help define Funcs for testing.
      6 // As an example, the following func
      7 //
      8 //   b1:
      9 //     v1 = InitMem <mem>
     10 //     Plain -> b2
     11 //   b2:
     12 //     Exit v1
     13 //   b3:
     14 //     v2 = Const <bool> [true]
     15 //     If v2 -> b3 b2
     16 //
     17 // can be defined as
     18 //
     19 //   fun := Fun("entry",
     20 //       Bloc("entry",
     21 //           Valu("mem", OpInitMem, types.TypeMem, 0, nil),
     22 //           Goto("exit")),
     23 //       Bloc("exit",
     24 //           Exit("mem")),
     25 //       Bloc("deadblock",
     26 //          Valu("deadval", OpConstBool, c.config.Types.Bool, 0, true),
     27 //          If("deadval", "deadblock", "exit")))
     28 //
     29 // and the Blocks or Values used in the Func can be accessed
     30 // like this:
     31 //   fun.blocks["entry"] or fun.values["deadval"]
     32 
     33 package ssa
     34 
     35 // TODO(matloob): Choose better names for Fun, Bloc, Goto, etc.
     36 // TODO(matloob): Write a parser for the Func disassembly. Maybe
     37 //                the parser can be used instead of Fun.
     38 
     39 import (
     40 	"cmd/compile/internal/types"
     41 	"cmd/internal/src"
     42 	"fmt"
     43 	"reflect"
     44 	"testing"
     45 )
     46 
     47 // Compare two Funcs for equivalence. Their CFGs must be isomorphic,
     48 // and their values must correspond.
     49 // Requires that values and predecessors are in the same order, even
     50 // though Funcs could be equivalent when they are not.
     51 // TODO(matloob): Allow values and predecessors to be in different
     52 // orders if the CFG are otherwise equivalent.
     53 func Equiv(f, g *Func) bool {
     54 	valcor := make(map[*Value]*Value)
     55 	var checkVal func(fv, gv *Value) bool
     56 	checkVal = func(fv, gv *Value) bool {
     57 		if fv == nil && gv == nil {
     58 			return true
     59 		}
     60 		if valcor[fv] == nil && valcor[gv] == nil {
     61 			valcor[fv] = gv
     62 			valcor[gv] = fv
     63 			// Ignore ids. Ops and Types are compared for equality.
     64 			// TODO(matloob): Make sure types are canonical and can
     65 			// be compared for equality.
     66 			if fv.Op != gv.Op || fv.Type != gv.Type || fv.AuxInt != gv.AuxInt {
     67 				return false
     68 			}
     69 			if !reflect.DeepEqual(fv.Aux, gv.Aux) {
     70 				// This makes the assumption that aux values can be compared
     71 				// using DeepEqual.
     72 				// TODO(matloob): Aux values may be *gc.Sym pointers in the near
     73 				// future. Make sure they are canonical.
     74 				return false
     75 			}
     76 			if len(fv.Args) != len(gv.Args) {
     77 				return false
     78 			}
     79 			for i := range fv.Args {
     80 				if !checkVal(fv.Args[i], gv.Args[i]) {
     81 					return false
     82 				}
     83 			}
     84 		}
     85 		return valcor[fv] == gv && valcor[gv] == fv
     86 	}
     87 	blkcor := make(map[*Block]*Block)
     88 	var checkBlk func(fb, gb *Block) bool
     89 	checkBlk = func(fb, gb *Block) bool {
     90 		if blkcor[fb] == nil && blkcor[gb] == nil {
     91 			blkcor[fb] = gb
     92 			blkcor[gb] = fb
     93 			// ignore ids
     94 			if fb.Kind != gb.Kind {
     95 				return false
     96 			}
     97 			if len(fb.Values) != len(gb.Values) {
     98 				return false
     99 			}
    100 			for i := range fb.Values {
    101 				if !checkVal(fb.Values[i], gb.Values[i]) {
    102 					return false
    103 				}
    104 			}
    105 			if len(fb.Succs) != len(gb.Succs) {
    106 				return false
    107 			}
    108 			for i := range fb.Succs {
    109 				if !checkBlk(fb.Succs[i].b, gb.Succs[i].b) {
    110 					return false
    111 				}
    112 			}
    113 			if len(fb.Preds) != len(gb.Preds) {
    114 				return false
    115 			}
    116 			for i := range fb.Preds {
    117 				if !checkBlk(fb.Preds[i].b, gb.Preds[i].b) {
    118 					return false
    119 				}
    120 			}
    121 			return true
    122 
    123 		}
    124 		return blkcor[fb] == gb && blkcor[gb] == fb
    125 	}
    126 
    127 	return checkBlk(f.Entry, g.Entry)
    128 }
    129 
    130 // fun is the return type of Fun. It contains the created func
    131 // itself as well as indexes from block and value names into the
    132 // corresponding Blocks and Values.
    133 type fun struct {
    134 	f      *Func
    135 	blocks map[string]*Block
    136 	values map[string]*Value
    137 }
    138 
    139 var emptyPass pass = pass{
    140 	name: "empty pass",
    141 }
    142 
    143 // Fun takes the name of an entry bloc and a series of Bloc calls, and
    144 // returns a fun containing the composed Func. entry must be a name
    145 // supplied to one of the Bloc functions. Each of the bloc names and
    146 // valu names should be unique across the Fun.
    147 func (c *Conf) Fun(entry string, blocs ...bloc) fun {
    148 	f := NewFunc(c.Frontend())
    149 	f.Config = c.config
    150 	// TODO: Either mark some SSA tests as t.Parallel,
    151 	// or set up a shared Cache and Reset it between tests.
    152 	// But not both.
    153 	f.Cache = new(Cache)
    154 	f.pass = &emptyPass
    155 
    156 	blocks := make(map[string]*Block)
    157 	values := make(map[string]*Value)
    158 	// Create all the blocks and values.
    159 	for _, bloc := range blocs {
    160 		b := f.NewBlock(bloc.control.kind)
    161 		blocks[bloc.name] = b
    162 		for _, valu := range bloc.valus {
    163 			// args are filled in the second pass.
    164 			values[valu.name] = b.NewValue0IA(src.NoXPos, valu.op, valu.t, valu.auxint, valu.aux)
    165 		}
    166 	}
    167 	// Connect the blocks together and specify control values.
    168 	f.Entry = blocks[entry]
    169 	for _, bloc := range blocs {
    170 		b := blocks[bloc.name]
    171 		c := bloc.control
    172 		// Specify control values.
    173 		if c.control != "" {
    174 			cval, ok := values[c.control]
    175 			if !ok {
    176 				f.Fatalf("control value for block %s missing", bloc.name)
    177 			}
    178 			b.SetControl(cval)
    179 		}
    180 		// Fill in args.
    181 		for _, valu := range bloc.valus {
    182 			v := values[valu.name]
    183 			for _, arg := range valu.args {
    184 				a, ok := values[arg]
    185 				if !ok {
    186 					b.Fatalf("arg %s missing for value %s in block %s",
    187 						arg, valu.name, bloc.name)
    188 				}
    189 				v.AddArg(a)
    190 			}
    191 		}
    192 		// Connect to successors.
    193 		for _, succ := range c.succs {
    194 			b.AddEdgeTo(blocks[succ])
    195 		}
    196 	}
    197 	return fun{f, blocks, values}
    198 }
    199 
    200 // Bloc defines a block for Fun. The bloc name should be unique
    201 // across the containing Fun. entries should consist of calls to valu,
    202 // as well as one call to Goto, If, or Exit to specify the block kind.
    203 func Bloc(name string, entries ...interface{}) bloc {
    204 	b := bloc{}
    205 	b.name = name
    206 	seenCtrl := false
    207 	for _, e := range entries {
    208 		switch v := e.(type) {
    209 		case ctrl:
    210 			// there should be exactly one Ctrl entry.
    211 			if seenCtrl {
    212 				panic(fmt.Sprintf("already seen control for block %s", name))
    213 			}
    214 			b.control = v
    215 			seenCtrl = true
    216 		case valu:
    217 			b.valus = append(b.valus, v)
    218 		}
    219 	}
    220 	if !seenCtrl {
    221 		panic(fmt.Sprintf("block %s doesn't have control", b.name))
    222 	}
    223 	return b
    224 }
    225 
    226 // Valu defines a value in a block.
    227 func Valu(name string, op Op, t *types.Type, auxint int64, aux interface{}, args ...string) valu {
    228 	return valu{name, op, t, auxint, aux, args}
    229 }
    230 
    231 // Goto specifies that this is a BlockPlain and names the single successor.
    232 // TODO(matloob): choose a better name.
    233 func Goto(succ string) ctrl {
    234 	return ctrl{BlockPlain, "", []string{succ}}
    235 }
    236 
    237 // If specifies a BlockIf.
    238 func If(cond, sub, alt string) ctrl {
    239 	return ctrl{BlockIf, cond, []string{sub, alt}}
    240 }
    241 
    242 // Exit specifies a BlockExit.
    243 func Exit(arg string) ctrl {
    244 	return ctrl{BlockExit, arg, []string{}}
    245 }
    246 
    247 // Eq specifies a BlockAMD64EQ.
    248 func Eq(cond, sub, alt string) ctrl {
    249 	return ctrl{BlockAMD64EQ, cond, []string{sub, alt}}
    250 }
    251 
    252 // bloc, ctrl, and valu are internal structures used by Bloc, Valu, Goto,
    253 // If, and Exit to help define blocks.
    254 
    255 type bloc struct {
    256 	name    string
    257 	control ctrl
    258 	valus   []valu
    259 }
    260 
    261 type ctrl struct {
    262 	kind    BlockKind
    263 	control string
    264 	succs   []string
    265 }
    266 
    267 type valu struct {
    268 	name   string
    269 	op     Op
    270 	t      *types.Type
    271 	auxint int64
    272 	aux    interface{}
    273 	args   []string
    274 }
    275 
    276 func TestArgs(t *testing.T) {
    277 	c := testConfig(t)
    278 	fun := c.Fun("entry",
    279 		Bloc("entry",
    280 			Valu("a", OpConst64, c.config.Types.Int64, 14, nil),
    281 			Valu("b", OpConst64, c.config.Types.Int64, 26, nil),
    282 			Valu("sum", OpAdd64, c.config.Types.Int64, 0, nil, "a", "b"),
    283 			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    284 			Goto("exit")),
    285 		Bloc("exit",
    286 			Exit("mem")))
    287 	sum := fun.values["sum"]
    288 	for i, name := range []string{"a", "b"} {
    289 		if sum.Args[i] != fun.values[name] {
    290 			t.Errorf("arg %d for sum is incorrect: want %s, got %s",
    291 				i, sum.Args[i], fun.values[name])
    292 		}
    293 	}
    294 }
    295 
    296 func TestEquiv(t *testing.T) {
    297 	cfg := testConfig(t)
    298 	equivalentCases := []struct{ f, g fun }{
    299 		// simple case
    300 		{
    301 			cfg.Fun("entry",
    302 				Bloc("entry",
    303 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    304 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    305 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
    306 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    307 					Goto("exit")),
    308 				Bloc("exit",
    309 					Exit("mem"))),
    310 			cfg.Fun("entry",
    311 				Bloc("entry",
    312 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    313 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    314 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
    315 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    316 					Goto("exit")),
    317 				Bloc("exit",
    318 					Exit("mem"))),
    319 		},
    320 		// block order changed
    321 		{
    322 			cfg.Fun("entry",
    323 				Bloc("entry",
    324 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    325 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    326 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
    327 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    328 					Goto("exit")),
    329 				Bloc("exit",
    330 					Exit("mem"))),
    331 			cfg.Fun("entry",
    332 				Bloc("exit",
    333 					Exit("mem")),
    334 				Bloc("entry",
    335 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    336 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    337 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
    338 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    339 					Goto("exit"))),
    340 		},
    341 	}
    342 	for _, c := range equivalentCases {
    343 		if !Equiv(c.f.f, c.g.f) {
    344 			t.Error("expected equivalence. Func definitions:")
    345 			t.Error(c.f.f)
    346 			t.Error(c.g.f)
    347 		}
    348 	}
    349 
    350 	differentCases := []struct{ f, g fun }{
    351 		// different shape
    352 		{
    353 			cfg.Fun("entry",
    354 				Bloc("entry",
    355 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    356 					Goto("exit")),
    357 				Bloc("exit",
    358 					Exit("mem"))),
    359 			cfg.Fun("entry",
    360 				Bloc("entry",
    361 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    362 					Exit("mem"))),
    363 		},
    364 		// value order changed
    365 		{
    366 			cfg.Fun("entry",
    367 				Bloc("entry",
    368 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    369 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    370 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    371 					Exit("mem"))),
    372 			cfg.Fun("entry",
    373 				Bloc("entry",
    374 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    375 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    376 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    377 					Exit("mem"))),
    378 		},
    379 		// value auxint different
    380 		{
    381 			cfg.Fun("entry",
    382 				Bloc("entry",
    383 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    384 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    385 					Exit("mem"))),
    386 			cfg.Fun("entry",
    387 				Bloc("entry",
    388 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    389 					Valu("a", OpConst64, cfg.config.Types.Int64, 26, nil),
    390 					Exit("mem"))),
    391 		},
    392 		// value aux different
    393 		{
    394 			cfg.Fun("entry",
    395 				Bloc("entry",
    396 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    397 					Valu("a", OpConst64, cfg.config.Types.Int64, 0, 14),
    398 					Exit("mem"))),
    399 			cfg.Fun("entry",
    400 				Bloc("entry",
    401 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    402 					Valu("a", OpConst64, cfg.config.Types.Int64, 0, 26),
    403 					Exit("mem"))),
    404 		},
    405 		// value args different
    406 		{
    407 			cfg.Fun("entry",
    408 				Bloc("entry",
    409 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    410 					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
    411 					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
    412 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
    413 					Exit("mem"))),
    414 			cfg.Fun("entry",
    415 				Bloc("entry",
    416 					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    417 					Valu("a", OpConst64, cfg.config.Types.Int64, 0, nil),
    418 					Valu("b", OpConst64, cfg.config.Types.Int64, 14, nil),
    419 					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "b", "a"),
    420 					Exit("mem"))),
    421 		},
    422 	}
    423 	for _, c := range differentCases {
    424 		if Equiv(c.f.f, c.g.f) {
    425 			t.Error("expected difference. Func definitions:")
    426 			t.Error(c.f.f)
    427 			t.Error(c.g.f)
    428 		}
    429 	}
    430 }
    431 
    432 // TestConstCache ensures that the cache will not return
    433 // reused free'd values with a non-matching AuxInt
    434 func TestConstCache(t *testing.T) {
    435 	c := testConfig(t)
    436 	f := c.Fun("entry",
    437 		Bloc("entry",
    438 			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    439 			Exit("mem")))
    440 	v1 := f.f.ConstBool(src.NoXPos, c.config.Types.Bool, false)
    441 	v2 := f.f.ConstBool(src.NoXPos, c.config.Types.Bool, true)
    442 	f.f.freeValue(v1)
    443 	f.f.freeValue(v2)
    444 	v3 := f.f.ConstBool(src.NoXPos, c.config.Types.Bool, false)
    445 	v4 := f.f.ConstBool(src.NoXPos, c.config.Types.Bool, true)
    446 	if v3.AuxInt != 0 {
    447 		t.Errorf("expected %s to have auxint of 0\n", v3.LongString())
    448 	}
    449 	if v4.AuxInt != 1 {
    450 		t.Errorf("expected %s to have auxint of 1\n", v4.LongString())
    451 	}
    452 
    453 }
    454 
    455 // opcodeMap returns a map from opcode to the number of times that opcode
    456 // appears in the function.
    457 func opcodeMap(f *Func) map[Op]int {
    458 	m := map[Op]int{}
    459 	for _, b := range f.Blocks {
    460 		for _, v := range b.Values {
    461 			m[v.Op]++
    462 		}
    463 	}
    464 	return m
    465 }
    466 
    467 // opcodeCounts checks that the number of opcodes listed in m agree with the
    468 // number of opcodes that appear in the function.
    469 func checkOpcodeCounts(t *testing.T, f *Func, m map[Op]int) {
    470 	n := opcodeMap(f)
    471 	for op, cnt := range m {
    472 		if n[op] != cnt {
    473 			t.Errorf("%s appears %d times, want %d times", op, n[op], cnt)
    474 		}
    475 	}
    476 }
    477