github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/func_test.go (about)

     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  }