Source file src/cmd/compile/internal/ssa/regalloc_test.go

     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  package ssa
     6  
     7  import (
     8  	"cmd/compile/internal/types"
     9  	"testing"
    10  )
    11  
    12  func TestLiveControlOps(t *testing.T) {
    13  	c := testConfig(t)
    14  	f := c.Fun("entry",
    15  		Bloc("entry",
    16  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    17  			Valu("x", OpAMD64MOVLconst, c.config.Types.Int8, 1, nil),
    18  			Valu("y", OpAMD64MOVLconst, c.config.Types.Int8, 2, nil),
    19  			Valu("a", OpAMD64TESTB, types.TypeFlags, 0, nil, "x", "y"),
    20  			Valu("b", OpAMD64TESTB, types.TypeFlags, 0, nil, "y", "x"),
    21  			Eq("a", "if", "exit"),
    22  		),
    23  		Bloc("if",
    24  			Eq("b", "plain", "exit"),
    25  		),
    26  		Bloc("plain",
    27  			Goto("exit"),
    28  		),
    29  		Bloc("exit",
    30  			Exit("mem"),
    31  		),
    32  	)
    33  	flagalloc(f.f)
    34  	regalloc(f.f)
    35  	checkFunc(f.f)
    36  }
    37  
    38  // Test to make sure G register is never reloaded from spill (spill of G is okay)
    39  // See #25504
    40  func TestNoGetgLoadReg(t *testing.T) {
    41  	/*
    42  		Original:
    43  		func fff3(i int) *g {
    44  			gee := getg()
    45  			if i == 0 {
    46  				fff()
    47  			}
    48  			return gee // here
    49  		}
    50  	*/
    51  	c := testConfigARM64(t)
    52  	f := c.Fun("b1",
    53  		Bloc("b1",
    54  			Valu("v1", OpInitMem, types.TypeMem, 0, nil),
    55  			Valu("v6", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
    56  			Valu("v8", OpGetG, c.config.Types.Int64.PtrTo(), 0, nil, "v1"),
    57  			Valu("v11", OpARM64CMPconst, types.TypeFlags, 0, nil, "v6"),
    58  			Eq("v11", "b2", "b4"),
    59  		),
    60  		Bloc("b4",
    61  			Goto("b3"),
    62  		),
    63  		Bloc("b3",
    64  			Valu("v14", OpPhi, types.TypeMem, 0, nil, "v1", "v12"),
    65  			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    66  			Valu("v16", OpARM64MOVDstore, types.TypeMem, 0, nil, "v8", "sb", "v14"),
    67  			Exit("v16"),
    68  		),
    69  		Bloc("b2",
    70  			Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "v1"),
    71  			Goto("b3"),
    72  		),
    73  	)
    74  	regalloc(f.f)
    75  	checkFunc(f.f)
    76  	// Double-check that we never restore to the G register. Regalloc should catch it, but check again anyway.
    77  	r := f.f.RegAlloc
    78  	for _, b := range f.blocks {
    79  		for _, v := range b.Values {
    80  			if v.Op == OpLoadReg && r[v.ID].String() == "g" {
    81  				t.Errorf("Saw OpLoadReg targeting g register: %s", v.LongString())
    82  			}
    83  		}
    84  	}
    85  }
    86  
    87  // Test to make sure we don't push spills into loops.
    88  // See issue #19595.
    89  func TestSpillWithLoop(t *testing.T) {
    90  	c := testConfig(t)
    91  	f := c.Fun("entry",
    92  		Bloc("entry",
    93  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    94  			Valu("ptr", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64)),
    95  			Valu("cond", OpArg, c.config.Types.Bool, 0, c.Temp(c.config.Types.Bool)),
    96  			Valu("ld", OpAMD64MOVQload, c.config.Types.Int64, 0, nil, "ptr", "mem"), // this value needs a spill
    97  			Goto("loop"),
    98  		),
    99  		Bloc("loop",
   100  			Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"),
   101  			Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "memphi"),
   102  			Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"),
   103  			Eq("test", "next", "exit"),
   104  		),
   105  		Bloc("next",
   106  			Goto("loop"),
   107  		),
   108  		Bloc("exit",
   109  			Valu("store", OpAMD64MOVQstore, types.TypeMem, 0, nil, "ptr", "ld", "call"),
   110  			Exit("store"),
   111  		),
   112  	)
   113  	regalloc(f.f)
   114  	checkFunc(f.f)
   115  	for _, v := range f.blocks["loop"].Values {
   116  		if v.Op == OpStoreReg {
   117  			t.Errorf("spill inside loop %s", v.LongString())
   118  		}
   119  	}
   120  }
   121  
   122  func TestSpillMove1(t *testing.T) {
   123  	c := testConfig(t)
   124  	f := c.Fun("entry",
   125  		Bloc("entry",
   126  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   127  			Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
   128  			Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())),
   129  			Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
   130  			Goto("loop1"),
   131  		),
   132  		Bloc("loop1",
   133  			Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
   134  			Eq("a", "loop2", "exit1"),
   135  		),
   136  		Bloc("loop2",
   137  			Eq("a", "loop1", "exit2"),
   138  		),
   139  		Bloc("exit1",
   140  			// store before call, y is available in a register
   141  			Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"),
   142  			Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem2"),
   143  			Exit("mem3"),
   144  		),
   145  		Bloc("exit2",
   146  			// store after call, y must be loaded from a spill location
   147  			Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
   148  			Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
   149  			Exit("mem5"),
   150  		),
   151  	)
   152  	flagalloc(f.f)
   153  	regalloc(f.f)
   154  	checkFunc(f.f)
   155  	// Spill should be moved to exit2.
   156  	if numSpills(f.blocks["loop1"]) != 0 {
   157  		t.Errorf("spill present from loop1")
   158  	}
   159  	if numSpills(f.blocks["loop2"]) != 0 {
   160  		t.Errorf("spill present in loop2")
   161  	}
   162  	if numSpills(f.blocks["exit1"]) != 0 {
   163  		t.Errorf("spill present in exit1")
   164  	}
   165  	if numSpills(f.blocks["exit2"]) != 1 {
   166  		t.Errorf("spill missing in exit2")
   167  	}
   168  
   169  }
   170  
   171  func TestSpillMove2(t *testing.T) {
   172  	c := testConfig(t)
   173  	f := c.Fun("entry",
   174  		Bloc("entry",
   175  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   176  			Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)),
   177  			Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())),
   178  			Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"),
   179  			Goto("loop1"),
   180  		),
   181  		Bloc("loop1",
   182  			Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"),
   183  			Eq("a", "loop2", "exit1"),
   184  		),
   185  		Bloc("loop2",
   186  			Eq("a", "loop1", "exit2"),
   187  		),
   188  		Bloc("exit1",
   189  			// store after call, y must be loaded from a spill location
   190  			Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
   191  			Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"),
   192  			Exit("mem3"),
   193  		),
   194  		Bloc("exit2",
   195  			// store after call, y must be loaded from a spill location
   196  			Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
   197  			Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"),
   198  			Exit("mem5"),
   199  		),
   200  	)
   201  	flagalloc(f.f)
   202  	regalloc(f.f)
   203  	checkFunc(f.f)
   204  	// There should be a spill in loop1, and nowhere else.
   205  	// TODO: resurrect moving spills out of loops? We could put spills at the start of both exit1 and exit2.
   206  	if numSpills(f.blocks["loop1"]) != 1 {
   207  		t.Errorf("spill missing from loop1")
   208  	}
   209  	if numSpills(f.blocks["loop2"]) != 0 {
   210  		t.Errorf("spill present in loop2")
   211  	}
   212  	if numSpills(f.blocks["exit1"]) != 0 {
   213  		t.Errorf("spill present in exit1")
   214  	}
   215  	if numSpills(f.blocks["exit2"]) != 0 {
   216  		t.Errorf("spill present in exit2")
   217  	}
   218  
   219  }
   220  
   221  func numSpills(b *Block) int {
   222  	n := 0
   223  	for _, v := range b.Values {
   224  		if v.Op == OpStoreReg {
   225  			n++
   226  		}
   227  	}
   228  	return n
   229  }
   230  

View as plain text