Source file src/cmd/compile/internal/ssacompile/mem2reg_test.go

     1  // Copyright 2026 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 ssacompile
     6  
     7  import (
     8  	"testing"
     9  	"unsafe"
    10  )
    11  
    12  // Single-block mem2reg test
    13  //
    14  //go:noinline
    15  func justReturn0() int {
    16  	var x, y, z int
    17  	xp := &x
    18  	yp := &y
    19  	zp := &z
    20  	*xp = 0
    21  	*yp = 1
    22  	*zp = 2
    23  	*yp = 3
    24  	*zp = 4
    25  	*yp = 5
    26  	valX1 := *xp
    27  	*zp = valX1 - 1
    28  	*xp = 1
    29  	*yp = 2
    30  	*xp = 3
    31  	*yp = 4
    32  	*xp = 5
    33  	valZ1 := *zp
    34  	*yp = valZ1 + 1
    35  	*zp = 1
    36  	*xp = 2
    37  	*zp = 3
    38  	*xp = 4
    39  	*zp = 5
    40  	valY1 := *yp
    41  	return valY1
    42  }
    43  
    44  // Control flow: if/else
    45  //
    46  //go:noinline
    47  func ifElseAddr(c bool) int {
    48  	x := 1
    49  	p := &x
    50  	if c {
    51  		*p = 2
    52  	} else {
    53  		*p = 3
    54  	}
    55  	return *p
    56  }
    57  
    58  //go:noinline
    59  func ifElseReg(c bool) int {
    60  	if c {
    61  		return 2
    62  	}
    63  	return 3
    64  }
    65  
    66  // Loop
    67  //
    68  //go:noinline
    69  func loopAddr() int {
    70  	sum := 0
    71  	p := &sum
    72  	for i := 0; i < 10; i++ {
    73  		*p += i
    74  	}
    75  	return *p
    76  }
    77  
    78  //go:noinline
    79  func loopReg() int {
    80  	sum := 0
    81  	for i := 0; i < 10; i++ {
    82  		sum += i
    83  	}
    84  	return sum
    85  }
    86  
    87  // Nested Loop
    88  //
    89  //go:noinline
    90  func nestedLoopAddr() int {
    91  	sum := 0
    92  	p := &sum
    93  	for i := 0; i < 10; i++ {
    94  		for j := 0; j < 10; j++ {
    95  			*p += i + j
    96  		}
    97  	}
    98  	return *p
    99  }
   100  
   101  //go:noinline
   102  func nestedLoopReg() int {
   103  	sum := 0
   104  	for i := 0; i < 10; i++ {
   105  		for j := 0; j < 10; j++ {
   106  			sum += i + j
   107  		}
   108  	}
   109  	return sum
   110  }
   111  
   112  // Switch Case
   113  //
   114  //go:noinline
   115  func switchCaseAddr(x int) int {
   116  	res := 0
   117  	p := &res
   118  	switch x {
   119  	case 0:
   120  		*p = 10
   121  	case 1:
   122  		*p = 20
   123  	default:
   124  		*p = 30
   125  	}
   126  	return *p
   127  }
   128  
   129  //go:noinline
   130  func switchCaseReg(x int) int {
   131  	switch x {
   132  	case 0:
   133  		return 10
   134  	case 1:
   135  		return 20
   136  	default:
   137  		return 30
   138  	}
   139  }
   140  
   141  // Goto
   142  //
   143  //go:noinline
   144  func gotoAddr(n int) int {
   145  	sum := 0
   146  	p := &sum
   147  	i := 0
   148  loop:
   149  	if i >= n {
   150  		goto end
   151  	}
   152  	*p += i
   153  	i++
   154  	goto loop
   155  end:
   156  	return *p
   157  }
   158  
   159  //go:noinline
   160  func gotoReg(n int) int {
   161  	sum := 0
   162  	i := 0
   163  loop:
   164  	if i >= n {
   165  		goto end
   166  	}
   167  	sum += i
   168  	i++
   169  	goto loop
   170  end:
   171  	return sum
   172  }
   173  
   174  // Complex: merges loops, switch, if-else, and closures.
   175  //
   176  //go:noinline
   177  func complexAddr(n int) int {
   178  	res := 0
   179  	p := &res
   180  
   181  	// Closure that might be inlined
   182  	inc := func(val int) {
   183  		*p += val
   184  	}
   185  
   186  	for i := 0; i < n; i++ {
   187  		// Switch with control flow
   188  		switch i % 3 {
   189  		case 0:
   190  			*p += i
   191  		case 1:
   192  			if i > 5 {
   193  				*p -= i
   194  			} else {
   195  				inc(i) // Call closure
   196  			}
   197  		default:
   198  			// Nested loop
   199  			for j := 0; j < 2; j++ {
   200  				*p += j
   201  			}
   202  		}
   203  	}
   204  	return *p
   205  }
   206  
   207  //go:noinline
   208  func complexReg(n int) int {
   209  	res := 0
   210  
   211  	inc := func(val int) {
   212  		res += val
   213  	}
   214  
   215  	for i := 0; i < n; i++ {
   216  		switch i % 3 {
   217  		case 0:
   218  			res += i
   219  		case 1:
   220  			if i > 5 {
   221  				res -= i
   222  			} else {
   223  				inc(i)
   224  			}
   225  		default:
   226  			for j := 0; j < 2; j++ {
   227  				res += j
   228  			}
   229  		}
   230  	}
   231  	return res
   232  }
   233  
   234  //go:noinline
   235  func unsafeTypedStoreAddr(c bool) int64 {
   236  	// The float64-typed store through x's address must not flow into
   237  	// an int64 phi for x; mem2reg has to leave x in memory (or
   238  	// reinterpret), on every architecture.
   239  	var x int64 = 7
   240  	if c {
   241  		*(*float64)(unsafe.Pointer(&x)) = 1.5
   242  	}
   243  	return x
   244  }
   245  
   246  //go:noinline
   247  func unsafeTypedStoreReg(c bool) int64 {
   248  	x := int64(7)
   249  	if c {
   250  		x = int64(0x3ff8000000000000) // bits of float64(1.5)
   251  	}
   252  	return x
   253  }
   254  
   255  func TestMem2Reg(t *testing.T) {
   256  	if r := justReturn0(); r != 0 {
   257  		t.Errorf("justReturn0() = %d, want 0", r)
   258  	}
   259  	if got, want := ifElseAddr(true), ifElseReg(true); got != want {
   260  		t.Errorf("ifElse(true): got %d, want %d", got, want)
   261  	}
   262  	if got, want := ifElseAddr(false), ifElseReg(false); got != want {
   263  		t.Errorf("ifElse(false): got %d, want %d", got, want)
   264  	}
   265  
   266  	if got, want := loopAddr(), loopReg(); got != want {
   267  		t.Errorf("loop: got %d, want %d", got, want)
   268  	}
   269  	if got, want := nestedLoopAddr(), nestedLoopReg(); got != want {
   270  		t.Errorf("nestedLoop: got %d, want %d", got, want)
   271  	}
   272  	for _, x := range []int{0, 1, 2} {
   273  		if got, want := switchCaseAddr(x), switchCaseReg(x); got != want {
   274  			t.Errorf("switchCase(%d): got %d, want %d", x, got, want)
   275  		}
   276  	}
   277  	if got, want := gotoAddr(10), gotoReg(10); got != want {
   278  		t.Errorf("goto: got %d, want %d", got, want)
   279  	}
   280  	if got, want := complexAddr(20), complexReg(20); got != want {
   281  		t.Errorf("complex: got %d, want %d", got, want)
   282  	}
   283  	for _, c := range []bool{true, false} {
   284  		if got, want := unsafeTypedStoreAddr(c), unsafeTypedStoreReg(c); got != want {
   285  			t.Errorf("unsafeTypedStore(%v): got %#x, want %#x", c, got, want)
   286  		}
   287  	}
   288  }
   289  

View as plain text