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