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