github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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  	"github.com/gagliardetto/golang-go/cmd/compile/internal/types"
    41  	"github.com/gagliardetto/golang-go/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  	f.cachedLineStarts = newXposmap(map[int]lineRange{0: {0, 100}, 1: {0, 100}, 2: {0, 100}, 3: {0, 100}, 4: {0, 100}})
   156  
   157  	blocks := make(map[string]*Block)
   158  	values := make(map[string]*Value)
   159  	// Create all the blocks and values.
   160  	for _, bloc := range blocs {
   161  		b := f.NewBlock(bloc.control.kind)
   162  		blocks[bloc.name] = b
   163  		for _, valu := range bloc.valus {
   164  			// args are filled in the second pass.
   165  			values[valu.name] = b.NewValue0IA(src.NoXPos, valu.op, valu.t, valu.auxint, valu.aux)
   166  		}
   167  	}
   168  	// Connect the blocks together and specify control values.
   169  	f.Entry = blocks[entry]
   170  	for _, bloc := range blocs {
   171  		b := blocks[bloc.name]
   172  		c := bloc.control
   173  		// Specify control values.
   174  		if c.control != "" {
   175  			cval, ok := values[c.control]
   176  			if !ok {
   177  				f.Fatalf("control value for block %s missing", bloc.name)
   178  			}
   179  			b.SetControl(cval)
   180  		}
   181  		// Fill in args.
   182  		for _, valu := range bloc.valus {
   183  			v := values[valu.name]
   184  			for _, arg := range valu.args {
   185  				a, ok := values[arg]
   186  				if !ok {
   187  					b.Fatalf("arg %s missing for value %s in block %s",
   188  						arg, valu.name, bloc.name)
   189  				}
   190  				v.AddArg(a)
   191  			}
   192  		}
   193  		// Connect to successors.
   194  		for _, succ := range c.succs {
   195  			b.AddEdgeTo(blocks[succ])
   196  		}
   197  	}
   198  	return fun{f, blocks, values}
   199  }
   200  
   201  // Bloc defines a block for Fun. The bloc name should be unique
   202  // across the containing Fun. entries should consist of calls to valu,
   203  // as well as one call to Goto, If, or Exit to specify the block kind.
   204  func Bloc(name string, entries ...interface{}) bloc {
   205  	b := bloc{}
   206  	b.name = name
   207  	seenCtrl := false
   208  	for _, e := range entries {
   209  		switch v := e.(type) {
   210  		case ctrl:
   211  			// there should be exactly one Ctrl entry.
   212  			if seenCtrl {
   213  				panic(fmt.Sprintf("already seen control for block %s", name))
   214  			}
   215  			b.control = v
   216  			seenCtrl = true
   217  		case valu:
   218  			b.valus = append(b.valus, v)
   219  		}
   220  	}
   221  	if !seenCtrl {
   222  		panic(fmt.Sprintf("block %s doesn't have control", b.name))
   223  	}
   224  	return b
   225  }
   226  
   227  // Valu defines a value in a block.
   228  func Valu(name string, op Op, t *types.Type, auxint int64, aux interface{}, args ...string) valu {
   229  	return valu{name, op, t, auxint, aux, args}
   230  }
   231  
   232  // Goto specifies that this is a BlockPlain and names the single successor.
   233  // TODO(matloob): choose a better name.
   234  func Goto(succ string) ctrl {
   235  	return ctrl{BlockPlain, "", []string{succ}}
   236  }
   237  
   238  // If specifies a BlockIf.
   239  func If(cond, sub, alt string) ctrl {
   240  	return ctrl{BlockIf, cond, []string{sub, alt}}
   241  }
   242  
   243  // Exit specifies a BlockExit.
   244  func Exit(arg string) ctrl {
   245  	return ctrl{BlockExit, arg, []string{}}
   246  }
   247  
   248  // Eq specifies a BlockAMD64EQ.
   249  func Eq(cond, sub, alt string) ctrl {
   250  	return ctrl{BlockAMD64EQ, cond, []string{sub, alt}}
   251  }
   252  
   253  // bloc, ctrl, and valu are internal structures used by Bloc, Valu, Goto,
   254  // If, and Exit to help define blocks.
   255  
   256  type bloc struct {
   257  	name    string
   258  	control ctrl
   259  	valus   []valu
   260  }
   261  
   262  type ctrl struct {
   263  	kind    BlockKind
   264  	control string
   265  	succs   []string
   266  }
   267  
   268  type valu struct {
   269  	name   string
   270  	op     Op
   271  	t      *types.Type
   272  	auxint int64
   273  	aux    interface{}
   274  	args   []string
   275  }
   276  
   277  func TestArgs(t *testing.T) {
   278  	c := testConfig(t)
   279  	fun := c.Fun("entry",
   280  		Bloc("entry",
   281  			Valu("a", OpConst64, c.config.Types.Int64, 14, nil),
   282  			Valu("b", OpConst64, c.config.Types.Int64, 26, nil),
   283  			Valu("sum", OpAdd64, c.config.Types.Int64, 0, nil, "a", "b"),
   284  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   285  			Goto("exit")),
   286  		Bloc("exit",
   287  			Exit("mem")))
   288  	sum := fun.values["sum"]
   289  	for i, name := range []string{"a", "b"} {
   290  		if sum.Args[i] != fun.values[name] {
   291  			t.Errorf("arg %d for sum is incorrect: want %s, got %s",
   292  				i, sum.Args[i], fun.values[name])
   293  		}
   294  	}
   295  }
   296  
   297  func TestEquiv(t *testing.T) {
   298  	cfg := testConfig(t)
   299  	equivalentCases := []struct{ f, g fun }{
   300  		// simple case
   301  		{
   302  			cfg.Fun("entry",
   303  				Bloc("entry",
   304  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   305  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   306  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
   307  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   308  					Goto("exit")),
   309  				Bloc("exit",
   310  					Exit("mem"))),
   311  			cfg.Fun("entry",
   312  				Bloc("entry",
   313  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   314  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   315  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
   316  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   317  					Goto("exit")),
   318  				Bloc("exit",
   319  					Exit("mem"))),
   320  		},
   321  		// block order changed
   322  		{
   323  			cfg.Fun("entry",
   324  				Bloc("entry",
   325  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   326  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   327  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
   328  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   329  					Goto("exit")),
   330  				Bloc("exit",
   331  					Exit("mem"))),
   332  			cfg.Fun("entry",
   333  				Bloc("exit",
   334  					Exit("mem")),
   335  				Bloc("entry",
   336  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   337  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   338  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
   339  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   340  					Goto("exit"))),
   341  		},
   342  	}
   343  	for _, c := range equivalentCases {
   344  		if !Equiv(c.f.f, c.g.f) {
   345  			t.Error("expected equivalence. Func definitions:")
   346  			t.Error(c.f.f)
   347  			t.Error(c.g.f)
   348  		}
   349  	}
   350  
   351  	differentCases := []struct{ f, g fun }{
   352  		// different shape
   353  		{
   354  			cfg.Fun("entry",
   355  				Bloc("entry",
   356  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   357  					Goto("exit")),
   358  				Bloc("exit",
   359  					Exit("mem"))),
   360  			cfg.Fun("entry",
   361  				Bloc("entry",
   362  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   363  					Exit("mem"))),
   364  		},
   365  		// value order changed
   366  		{
   367  			cfg.Fun("entry",
   368  				Bloc("entry",
   369  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   370  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   371  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   372  					Exit("mem"))),
   373  			cfg.Fun("entry",
   374  				Bloc("entry",
   375  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   376  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   377  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   378  					Exit("mem"))),
   379  		},
   380  		// value auxint different
   381  		{
   382  			cfg.Fun("entry",
   383  				Bloc("entry",
   384  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   385  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   386  					Exit("mem"))),
   387  			cfg.Fun("entry",
   388  				Bloc("entry",
   389  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   390  					Valu("a", OpConst64, cfg.config.Types.Int64, 26, nil),
   391  					Exit("mem"))),
   392  		},
   393  		// value aux different
   394  		{
   395  			cfg.Fun("entry",
   396  				Bloc("entry",
   397  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   398  					Valu("a", OpConst64, cfg.config.Types.Int64, 0, 14),
   399  					Exit("mem"))),
   400  			cfg.Fun("entry",
   401  				Bloc("entry",
   402  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   403  					Valu("a", OpConst64, cfg.config.Types.Int64, 0, 26),
   404  					Exit("mem"))),
   405  		},
   406  		// value args different
   407  		{
   408  			cfg.Fun("entry",
   409  				Bloc("entry",
   410  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   411  					Valu("a", OpConst64, cfg.config.Types.Int64, 14, nil),
   412  					Valu("b", OpConst64, cfg.config.Types.Int64, 26, nil),
   413  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "a", "b"),
   414  					Exit("mem"))),
   415  			cfg.Fun("entry",
   416  				Bloc("entry",
   417  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   418  					Valu("a", OpConst64, cfg.config.Types.Int64, 0, nil),
   419  					Valu("b", OpConst64, cfg.config.Types.Int64, 14, nil),
   420  					Valu("sum", OpAdd64, cfg.config.Types.Int64, 0, nil, "b", "a"),
   421  					Exit("mem"))),
   422  		},
   423  	}
   424  	for _, c := range differentCases {
   425  		if Equiv(c.f.f, c.g.f) {
   426  			t.Error("expected difference. Func definitions:")
   427  			t.Error(c.f.f)
   428  			t.Error(c.g.f)
   429  		}
   430  	}
   431  }
   432  
   433  // TestConstCache ensures that the cache will not return
   434  // reused free'd values with a non-matching AuxInt
   435  func TestConstCache(t *testing.T) {
   436  	c := testConfig(t)
   437  	f := c.Fun("entry",
   438  		Bloc("entry",
   439  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   440  			Exit("mem")))
   441  	v1 := f.f.ConstBool(c.config.Types.Bool, false)
   442  	v2 := f.f.ConstBool(c.config.Types.Bool, true)
   443  	f.f.freeValue(v1)
   444  	f.f.freeValue(v2)
   445  	v3 := f.f.ConstBool(c.config.Types.Bool, false)
   446  	v4 := f.f.ConstBool(c.config.Types.Bool, true)
   447  	if v3.AuxInt != 0 {
   448  		t.Errorf("expected %s to have auxint of 0\n", v3.LongString())
   449  	}
   450  	if v4.AuxInt != 1 {
   451  		t.Errorf("expected %s to have auxint of 1\n", v4.LongString())
   452  	}
   453  
   454  }
   455  
   456  // opcodeMap returns a map from opcode to the number of times that opcode
   457  // appears in the function.
   458  func opcodeMap(f *Func) map[Op]int {
   459  	m := map[Op]int{}
   460  	for _, b := range f.Blocks {
   461  		for _, v := range b.Values {
   462  			m[v.Op]++
   463  		}
   464  	}
   465  	return m
   466  }
   467  
   468  // opcodeCounts checks that the number of opcodes listed in m agree with the
   469  // number of opcodes that appear in the function.
   470  func checkOpcodeCounts(t *testing.T, f *Func, m map[Op]int) {
   471  	n := opcodeMap(f)
   472  	for op, cnt := range m {
   473  		if n[op] != cnt {
   474  			t.Errorf("%s appears %d times, want %d times", op, n[op], cnt)
   475  		}
   476  	}
   477  }