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