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