github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/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, 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  }