Source file test/fixedbugs/spillreload_arm64_pair.go

     1  // run
     2  
     3  // Copyright 2026 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Regression coverage for the late spill/reload pair coalescer on arm64
     8  // (cmd/compile/internal/arm64.pairSpills). When the coalescer fused two
     9  // adjacent AMOVD reloads into a single LDP, paths that branched directly to
    10  // the second reload would silently skip the fused load and observe a stale
    11  // or uninitialized register. The fix records branch and jump-table targets
    12  // before fusing and refuses to fuse when the second instruction is one.
    13  // These functions exercise patterns that produce strictly-adjacent reloads
    14  // and conditional branches over calls, including the shape from
    15  // runtime.schedule.
    16  //
    17  // The check is end-to-end: each function returns a value that depends on
    18  // every spilled variable, so a missing reload produces a wrong result and
    19  // the program panics.
    20  
    21  package main
    22  
    23  import "fmt"
    24  
    25  //go:noinline
    26  func sink(int) {}
    27  
    28  //go:noinline
    29  func sink2(int, int) {}
    30  
    31  func callTwoVars(p, q *int) (int, int) {
    32  	a := *p
    33  	b := *q
    34  	sink(0)
    35  	return a, b
    36  }
    37  
    38  func condCallTwoVars(c bool, p, q *int) (int, int) {
    39  	a := *p
    40  	b := *q
    41  	if c {
    42  		sink(0)
    43  	}
    44  	return a, b
    45  }
    46  
    47  // condCallThreeVars mimics the shape that produced the original
    48  // miscompile: a conditional call surrounded by values that need to
    49  // survive it, with the join landing on the second of two adjacent
    50  // reloads.
    51  func condCallThreeVars(c bool, p, q, r *int) (int, int, int) {
    52  	a := *p
    53  	b := *q
    54  	d := *r
    55  	if c {
    56  		sink2(a, b)
    57  	}
    58  	return a, b, d
    59  }
    60  
    61  func loopReload(p []int) int {
    62  	s := 0
    63  	for _, v := range p {
    64  		sink(v)
    65  		s += v
    66  	}
    67  	return s
    68  }
    69  
    70  func nestedConds(c1, c2 bool, p, q, r *int) (int, int, int) {
    71  	a := *p
    72  	b := *q
    73  	d := *r
    74  	if c1 {
    75  		sink(a)
    76  		if c2 {
    77  			sink(b)
    78  		}
    79  	}
    80  	return a, b, d
    81  }
    82  
    83  func main() {
    84  	x, y, z := 7, 11, 13
    85  	if a, b := callTwoVars(&x, &y); a != 7 || b != 11 {
    86  		panic(fmt.Sprintf("callTwoVars = %d, %d", a, b))
    87  	}
    88  	for _, c := range []bool{false, true} {
    89  		if a, b := condCallTwoVars(c, &x, &y); a != 7 || b != 11 {
    90  			panic(fmt.Sprintf("condCallTwoVars(%v) = %d, %d", c, a, b))
    91  		}
    92  		if a, b, d := condCallThreeVars(c, &x, &y, &z); a != 7 || b != 11 || d != 13 {
    93  			panic(fmt.Sprintf("condCallThreeVars(%v) = %d, %d, %d", c, a, b, d))
    94  		}
    95  	}
    96  	for _, c1 := range []bool{false, true} {
    97  		for _, c2 := range []bool{false, true} {
    98  			if a, b, d := nestedConds(c1, c2, &x, &y, &z); a != 7 || b != 11 || d != 13 {
    99  				panic(fmt.Sprintf("nestedConds(%v,%v) = %d, %d, %d", c1, c2, a, b, d))
   100  			}
   101  		}
   102  	}
   103  	if s := loopReload([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); s != 55 {
   104  		panic(fmt.Sprintf("loopReload = %d", s))
   105  	}
   106  }
   107  

View as plain text