Source file test/fixedbugs/issue80188.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  package main
     8  
     9  import "runtime"
    10  
    11  type T struct {
    12  	a, b, c, d int
    13  }
    14  
    15  func (t *T) sum() int {
    16  	return t.a + t.b + t.c + t.d
    17  }
    18  
    19  //go:noinline
    20  func newT(x int) *T {
    21  	t := new(T)
    22  	t.a = x
    23  	t.b = x
    24  	t.c = x
    25  	t.d = x
    26  	return t
    27  }
    28  
    29  //go:noinline
    30  func intptr() *int {
    31  	return &g
    32  }
    33  
    34  var g int
    35  
    36  //go:noinline
    37  func f(x int) []*T {
    38  	const N = 40
    39  	var q [N]*T
    40  	for i := range N {
    41  		q[i] = newT(x)
    42  	}
    43  	var a [2]*int
    44  	for i := range a {
    45  		a[i] = intptr()
    46  	}
    47  
    48  	// reserve 2 registers we will later drop.
    49  	b := a[0]
    50  	c := a[1]
    51  
    52  	// Load pointers into registers (or spill slots)
    53  	p0 := q[0]
    54  	p1 := q[1]
    55  	p2 := q[2]
    56  	p3 := q[3]
    57  	p4 := q[4]
    58  	p5 := q[5]
    59  	p6 := q[6]
    60  	p7 := q[7]
    61  	p8 := q[8]
    62  	p9 := q[9]
    63  	p10 := q[10]
    64  	p11 := q[11]
    65  	p12 := q[12]
    66  	p13 := q[13]
    67  	p14 := q[14]
    68  	p15 := q[15]
    69  	p16 := q[16]
    70  	p17 := q[17]
    71  	p18 := q[18]
    72  	p19 := q[19]
    73  	p20 := q[20]
    74  	p21 := q[21]
    75  	p22 := q[22]
    76  	p23 := q[23]
    77  	p24 := q[24]
    78  	p25 := q[25]
    79  	p26 := q[26]
    80  	p27 := q[27]
    81  	p28 := q[28]
    82  	p29 := q[29]
    83  	p30 := q[30]
    84  	p31 := q[31]
    85  	p32 := q[32]
    86  	p33 := q[33]
    87  	p34 := q[34]
    88  	p35 := q[35]
    89  	p36 := q[36]
    90  	p37 := q[37]
    91  	p38 := q[38]
    92  	p39 := q[39]
    93  
    94  	// q is dead at this point. The only live reference to
    95  	// the objects allocated by newT are in the pXX variables.
    96  	// But if async preempted, we will scan the frame conservatively,
    97  	// which will find dead entries in q. Remove them.
    98  	q[0] = nil
    99  	q[1] = nil
   100  	q[2] = nil
   101  	q[3] = nil
   102  	q[4] = nil
   103  	q[5] = nil
   104  	q[6] = nil
   105  	q[7] = nil
   106  	q[8] = nil
   107  	q[9] = nil
   108  	q[10] = nil
   109  	q[11] = nil
   110  	q[12] = nil
   111  	q[13] = nil
   112  	q[14] = nil
   113  	q[15] = nil
   114  	q[16] = nil
   115  	q[17] = nil
   116  	q[18] = nil
   117  	q[19] = nil
   118  	q[20] = nil
   119  	q[21] = nil
   120  	q[22] = nil
   121  	q[23] = nil
   122  	q[24] = nil
   123  	q[25] = nil
   124  	q[26] = nil
   125  	q[27] = nil
   126  	q[28] = nil
   127  	q[29] = nil
   128  	q[30] = nil
   129  	q[31] = nil
   130  	q[32] = nil
   131  	q[33] = nil
   132  	q[34] = nil
   133  	q[35] = nil
   134  	q[36] = nil
   135  	q[37] = nil
   136  	q[38] = nil
   137  	q[39] = nil
   138  
   139  	// Some pXX is held in R30.
   140  	// If async preemption happens here, the pointer in R30
   141  	// will not get scanned and might cause the pointed-to
   142  	// object to be collected prematurely. It does not live
   143  	// anywhere else in this frame, and we will not scan
   144  	// this frame again later.
   145  	//
   146  	// Note that the write barrier has to be off for some of
   147  	// the newT calls for objects not to be allocated black.
   148  	// But it must be on here to get an async preempt.
   149  	// It must be off again by the final copy to not get put
   150  	// in a write barrier buffer somewhere.
   151  
   152  	// delay here to encourage async preemption
   153  	*b = 0
   154  	*c = 0
   155  	// The 2 registers holding b,c are now free. They
   156  	// can be used to implement this loop without spilling
   157  	// R30 to get a free register.
   158  	for range 10000 {
   159  	}
   160  
   161  	// Store registers back to stack memory (with no write barriers)
   162  	var z [N]*T
   163  	z[0] = p0
   164  	z[1] = p1
   165  	z[2] = p2
   166  	z[3] = p3
   167  	z[4] = p4
   168  	z[5] = p5
   169  	z[6] = p6
   170  	z[7] = p7
   171  	z[8] = p8
   172  	z[9] = p9
   173  	z[10] = p10
   174  	z[11] = p11
   175  	z[12] = p12
   176  	z[13] = p13
   177  	z[14] = p14
   178  	z[15] = p15
   179  	z[16] = p16
   180  	z[17] = p17
   181  	z[18] = p18
   182  	z[19] = p19
   183  	z[20] = p20
   184  	z[21] = p21
   185  	z[22] = p22
   186  	z[23] = p23
   187  	z[24] = p24
   188  	z[25] = p25
   189  	z[26] = p26
   190  	z[27] = p27
   191  	z[28] = p28
   192  	z[29] = p29
   193  	z[30] = p30
   194  	z[31] = p31
   195  	z[32] = p32
   196  	z[33] = p33
   197  	z[34] = p34
   198  	z[35] = p35
   199  	z[36] = p36
   200  	z[37] = p37
   201  	z[38] = p38
   202  	z[39] = p39
   203  
   204  	// Delay again, hoping GC finishes before we publish
   205  	// the unscanned pointer via the write barrier.
   206  	for range 10000 {
   207  	}
   208  
   209  	// Copy results to heap to return.
   210  	r := new([N]*T)
   211  	*r = z
   212  	return r[:]
   213  }
   214  
   215  func main() {
   216  	runtime.GOMAXPROCS(2)
   217  	c := make(chan bool)
   218  	for range 4 {
   219  		go goroutine(c)
   220  	}
   221  	for range 4 {
   222  		<-c
   223  	}
   224  }
   225  func goroutine(c chan bool) {
   226  	var all [][]*T
   227  	for x := range 10000 {
   228  		all = append(all, f(x))
   229  	}
   230  	for x, a := range all {
   231  		for _, t := range a {
   232  			if t.a != x || t.b != x || t.c != x || t.d != x {
   233  				panic("bad")
   234  			}
   235  		}
   236  	}
   237  	c <- true
   238  }
   239  

View as plain text