@@ -113,7 +113,7 @@ func runHarness(t *testing.T, harnessPath string, exePath string, fcn string) st
113113 if err := cmd .Run (); err != nil {
114114 t .Fatalf ("running 'harness -m %s -f %s': %v" , exePath , fcn , err )
115115 }
116- return strings .TrimSpace (string ( b . Bytes () ))
116+ return strings .TrimSpace (b . String ( ))
117117}
118118
119119// gobuild is a helper to build a Go program from source code,
@@ -151,6 +151,7 @@ import (
151151 "context"
152152 "strings"
153153 "fmt"
154+ "net/http"
154155)
155156
156157var G int
@@ -207,11 +208,109 @@ func (a Address) String() string {
207208 return sb.String()
208209}
209210
211+ //go:noinline
212+ func Issue65405(a int, b string) (int, error) {
213+ http.Handle("/", http.StripPrefix("/static/", http.FileServer(http.Dir("./output"))))
214+ return a + len(b), nil
215+ }
216+
217+ //go:noinline
218+ func RegisterLivenessNamedRetParam() (result int) {
219+ // This function demonstrates the register reuse issue.
220+ // The return value 'result' should only be valid in its register
221+ // after it's actually set, not throughout the entire function.
222+
223+ // Early in the function, do some work that uses registers
224+ x := 42
225+ y := 100
226+ z := x * y // Register allocator may use RAX here for multiplication
227+
228+ // Do more work that might reuse the return register
229+ for i := 0; i < 10; i++ {
230+ z += i // More register usage
231+ }
232+
233+ // Only NOW do we actually set the return value
234+ result = z // Return value is only valid in RAX from here
235+
236+ // A debugger querying 'result' before this point would get
237+ // incorrect values if the location list claims it's in RAX
238+ // for the entire function
239+ return result
240+ }
241+
242+ //go:noinline
243+ func RegisterLivenessUnnamedRetParam() int {
244+ x := 42
245+ y := 100
246+ z := x * y
247+
248+ for i := 0; i < 10; i++ {
249+ z += i // More register usage
250+ }
251+
252+ return z
253+ }
254+
255+ //go:noinline
256+ func multiReturn() (int, int, int, string, float64) {
257+ return 1, 2, 3, "test", 4.5
258+ }
259+
260+ //go:noinline
261+ func singleReturn() int {
262+ return 42
263+ }
264+
265+ //go:noinline
266+ func multiReturnStmts(i int) int {
267+ if i < 10 {
268+ return 55
269+ }
270+ i += 100
271+ i *= i
272+ return i
273+ }
274+
275+ //go:noinline
276+ func ManyArgsWithNamedReturns(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p int) (sum int, product int) {
277+ // Use all arguments in computations
278+ sum = a + b + c + d + e + f + g + h
279+ product = 1
280+
281+ // More operations to ensure all args are used
282+ temp1 := i * j
283+ temp2 := k * l
284+ temp3 := m * n
285+ temp4 := o * p
286+
287+ sum += temp1 + temp2 + temp3 + temp4
288+
289+ // Compute product using all arguments
290+ product = (a + 1) * (b + 1) * (c + 1) * (d + 1)
291+ product += (e + f) * (g + h) * (i + j) * (k + l)
292+ product += (m + n) * (o + p)
293+
294+ // Final adjustments
295+ sum = sum * 2
296+ product = product / 2
297+
298+ return sum, product
299+ }
300+
210301func main() {
211302 Issue47354("poo")
212303 var d DB
213304 d.Issue46845(context.Background(), nil, func(error) {}, "foo", nil)
214305 Issue72053()
306+ _, _ = Issue65405(42, "test")
307+ a, b, _, _, _ := multiReturn()
308+ f := singleReturn()
309+ _ = a + b + f
310+ _ = RegisterLivenessNamedRetParam()
311+ _ = RegisterLivenessUnnamedRetParam()
312+ _, _ = ManyArgsWithNamedReturns(1, 2, 3, 4, 5, 6, a, b, f, 1, 2, 3, 4, 5, 6, 7)
313+ _ = multiReturnStmts(a+b)
215314}
216315
217316`
@@ -243,8 +342,10 @@ func testIssue46845(t *testing.T, harnessPath string, ppath string) {
2433424: in-param "release" loc="{ [0: S=0 RSI] }"
2443435: in-param "query" loc="{ [0: S=8 R8] [1: S=8 R9] }"
2453446: in-param "args" loc="{ [0: S=8 addr=0x1000] [1: S=8 addr=0x1008] [2: S=8 addr=0x1010] }"
246- 7: out-param "res" loc="<not available>"
247- 8: out-param "err" loc="<not available>"
345+ 7: out-param "res" at RET[0] loc="addr=f98"
346+ 7: out-param "res" at RET[1] loc="addr=f98"
347+ 8: out-param "err" at RET[0] loc="addr=fa8"
348+ 8: out-param "err" at RET[1] loc="addr=fa8"
248349` ,
249350 "arm64" : `
2503511: in-param "db" loc="{ [0: S=0 R0] }"
@@ -261,7 +362,7 @@ func testIssue46845(t *testing.T, harnessPath string, ppath string) {
261362 got := runHarness (t , harnessPath , ppath , "main." + fname )
262363 want := strings .TrimSpace (expected [runtime .GOARCH ])
263364 if got != want {
264- t .Errorf ("failed Issue47354 arch %s:\n got: %s\n want: %s" ,
365+ t .Errorf ("failed Issue46845 arch %s:\n got: %s\n want: %s" ,
265366 runtime .GOARCH , got , want )
266367 }
267368}
@@ -270,7 +371,7 @@ func testIssue72053(t *testing.T, harnessPath string, ppath string) {
270371 testenv .NeedsGo1Point (t , 25 )
271372 testenv .NeedsArch (t , "amd64" )
272373
273- want := "1: in-param \" a\" loc=\" { [0: S=1 RAX] [1: S=7 addr=0x0] [2: S=8 RBX] [3: S=8 RCX] }\" \n 2: out-param \" ~r0\" loc=\" addr=fa8\" "
374+ want := "1: in-param \" a\" loc=\" { [0: S=1 RAX] [1: S=7 addr=0x0] [2: S=8 RBX] [3: S=8 RCX] }\" \n 2: out-param \" ~r0\" at RET[0] loc=\" addr=fa8\" "
274375 got := runHarness (t , harnessPath , ppath , "main.Address.String" )
275376 if got != want {
276377 t .Errorf ("failed Issue72053 arch %s:\n got: %q\n want: %q" ,
@@ -300,6 +401,182 @@ func testRuntimeThrow(t *testing.T, harnessPath, nooptHarnessPath, ppath string)
300401 }
301402}
302403
404+ func testIssue65405 (t * testing.T , harnessPath string , ppath string ) {
405+ // Test that function parameters have location lists
406+ expected := map [string ]string {
407+ "amd64" : `1: in-param "a" loc="{ [0: S=0 RAX] }"
408+ 2: in-param "b" loc="{ [0: S=8 RBX] [1: S=8 RCX] }"
409+ 3: out-param "~r0" at RET[0] loc="{ [0: S=0 RAX] }"
410+ 4: out-param "~r1" at RET[0] loc="{ [0: S=8 RBX] [1: S=8 RCX] }"` ,
411+ "arm64" : `1: in-param "a" loc="{ [0: S=0 R0] }"
412+ 2: in-param "b" loc="{ [0: S=8 R1] [1: S=8 R2] }"
413+ 3: out-param "~r0" at RET[0] loc="{ [0: S=0 R0] }"
414+ 4: out-param "~r1" at RET[0] loc="{ [0: S=8 R1] [1: S=8 R2] }"` ,
415+ }
416+ fname := "Issue65405"
417+ got := runHarness (t , harnessPath , ppath , "main." + fname )
418+ want := expected [runtime .GOARCH ]
419+ if got != want {
420+ t .Errorf ("failed Issue65405 arch %s:\n got: %q\n want: %q" ,
421+ runtime .GOARCH , got , want )
422+ }
423+ }
424+
425+ func testReturnValueRegisters (t * testing.T , harnessPath string , ppath string ) {
426+ // Test return value register assignments for multiReturn function
427+ // Verify that return values follow ABI conventions
428+ expected := map [string ]string {
429+ "amd64" : `1: out-param "~r0" at RET[0] loc="{ [0: S=0 RAX] }"
430+ 2: out-param "~r1" at RET[0] loc="{ [0: S=0 RBX] }"
431+ 3: out-param "~r2" at RET[0] loc="{ [0: S=0 RCX] }"
432+ 4: out-param "~r3" at RET[0] loc="{ [0: S=8 RDI] [1: S=8 RSI] }"
433+ 5: out-param "~r4" at RET[0] loc="{ [0: S=0 X0] }"` ,
434+ "arm64" : `1: out-param "~r0" at RET[0] loc="{ [0: S=0 R0] }"
435+ 2: out-param "~r1" at RET[0] loc="{ [0: S=0 R1] }"
436+ 3: out-param "~r2" at RET[0] loc="{ [0: S=0 R2] }"
437+ 4: out-param "~r3" at RET[0] loc="{ [0: S=8 R3] [1: S=8 R4] }"
438+ 5: out-param "~r4" at RET[0] loc="{ [0: S=0 F0] }"` ,
439+ }
440+ fname := "multiReturn"
441+ got := runHarness (t , harnessPath , ppath , "main." + fname )
442+ want := expected [runtime .GOARCH ]
443+ if got != want {
444+ t .Errorf ("failed ReturnValueRegisters arch %s:\n got: %q\n want: %q" ,
445+ runtime .GOARCH , got , want )
446+ }
447+ }
448+
449+ func testRegisterLivenessNamedRetParam (t * testing.T , harnessPath , nooptHarnessPath , ppath , nooppath string ) {
450+ fname := "RegisterLivenessNamedRetParam"
451+ expected := map [string ]string {
452+ "amd64" : `1: out-param "result" at RET[0] loc="{ [0: S=0 RAX] }"` ,
453+ "arm64" : `` ,
454+ }
455+
456+ // Test only optimized builds (non-optimized results are passed on the stack)
457+ testCases := []struct {
458+ name string
459+ harness string
460+ binary string
461+ }{
462+ {"optimized" , harnessPath , ppath },
463+ }
464+
465+ for _ , tc := range testCases {
466+ t .Run (tc .name , func (t * testing.T ) {
467+ got := runHarness (t , tc .harness , tc .binary , "main." + fname )
468+ want := expected [runtime .GOARCH ]
469+ if got != want {
470+ t .Fatalf ("return parameter 'result' has incorrect location in %s build:\n got:\n %s\n want:\n %s" , tc .name , got , want )
471+ }
472+ })
473+ }
474+ }
475+
476+ func testLivenessForUnnamedRetParams (t * testing.T , harnessPath , nooptHarnessPath , ppath , nooppath string ) {
477+ // This test verifies that return parameters have precise location lists.
478+ // Return parameters should only be valid in their ABI register after assignment,
479+ // not throughout the entire function. The location list should start from the
480+ // assignment point, not from function entry.
481+
482+ fname := "RegisterLivenessUnnamedRetParam"
483+ expected := map [string ]string {
484+ "amd64" : `1: out-param "~r0" at RET[0] loc="{ [0: S=0 RAX] }"` ,
485+ "arm64" : `` ,
486+ }
487+
488+ // Test only optimized builds (non-optimized results are passed on the stack)
489+ testCases := []struct {
490+ name string
491+ harness string
492+ binary string
493+ }{
494+ {"optimized" , harnessPath , ppath },
495+ }
496+
497+ for _ , tc := range testCases {
498+ t .Run (tc .name , func (t * testing.T ) {
499+ got := runHarness (t , tc .harness , tc .binary , "main." + fname )
500+ want := expected [runtime .GOARCH ]
501+ if got != want {
502+ t .Fatalf ("return parameter 'result' has incorrect location in %s build:\n got:\n %s\n want:\n %s" , tc .name , got , want )
503+ }
504+ })
505+ }
506+ }
507+
508+ func testManyArgsWithNamedReturns (t * testing.T , harnessPath , nooptHarnessPath , ppath , nooppath string ) {
509+ fname := "ManyArgsWithNamedReturns"
510+ expected := map [string ]string {
511+ "amd64" : `1: in-param "a" loc="{ [0: S=0 RAX] }"
512+ 2: in-param "b" loc="{ [0: S=0 RBX] }"
513+ 3: in-param "c" loc="{ [0: S=0 RCX] }"
514+ 4: in-param "d" loc="{ [0: S=0 RDI] }"
515+ 5: in-param "e" loc="{ [0: S=0 RSI] }"
516+ 6: in-param "f" loc="{ [0: S=0 R8] }"
517+ 7: in-param "g" loc="{ [0: S=0 R9] }"
518+ 8: in-param "h" loc="{ [0: S=0 R10] }"
519+ 9: in-param "i" loc="{ [0: S=0 R11] }"
520+ 10: in-param "j" loc="addr=1000"
521+ 11: in-param "k" loc="addr=1008"
522+ 12: in-param "l" loc="addr=1010"
523+ 13: in-param "m" loc="addr=1018"
524+ 14: in-param "n" loc="addr=1020"
525+ 15: in-param "o" loc="addr=1028"
526+ 16: in-param "p" loc="addr=1030"
527+ 17: out-param "sum" at RET[0] loc="{ [0: S=0 RAX] }"
528+ 18: out-param "product" at RET[0] loc="{ [0: S=0 RBX] }"` ,
529+ "arm64" : `` ,
530+ }
531+
532+ testCases := []struct {
533+ name string
534+ harness string
535+ binary string
536+ }{
537+ {"optimized" , harnessPath , ppath },
538+ }
539+
540+ for _ , tc := range testCases {
541+ t .Run (tc .name , func (t * testing.T ) {
542+ got := runHarness (t , tc .harness , tc .binary , "main." + fname )
543+ want := expected [runtime .GOARCH ]
544+ if got != want {
545+ t .Fatalf ("return parameter 'result' has incorrect location in %s build:\n got:\n %s\n want:\n %s" , tc .name , got , want )
546+ }
547+ })
548+ }
549+ }
550+
551+ func testMultipleReturnStmts (t * testing.T , harnessPath , nooptHarnessPath , ppath , nooppath string ) {
552+ fname := "multiReturnStmts"
553+ expected := map [string ]string {
554+ "amd64" : `1: in-param "i" loc="{ [0: S=0 RAX] }"
555+ 2: out-param "~r0" at RET[0] loc="{ [0: S=0 RAX] }"
556+ 2: out-param "~r0" at RET[1] loc="{ [0: S=0 RAX] }"` ,
557+ "arm64" : `` ,
558+ }
559+
560+ // Test only optimized builds (non-optimized results are passed on the stack)
561+ testCases := []struct {
562+ name string
563+ harness string
564+ binary string
565+ }{
566+ {"optimized" , harnessPath , ppath },
567+ }
568+
569+ for _ , tc := range testCases {
570+ t .Run (tc .name , func (t * testing.T ) {
571+ got := runHarness (t , tc .harness , tc .binary , "main." + fname )
572+ want := expected [runtime .GOARCH ]
573+ if got != want {
574+ t .Fatalf ("return parameter 'result' has incorrect location in %s build:\n got:\n %s\n want:\n %s" , tc .name , got , want )
575+ }
576+ })
577+ }
578+ }
579+
303580func TestDwarfVariableLocations (t * testing.T ) {
304581 testenv .NeedsGo1Point (t , 18 )
305582 testenv .MustHaveGoBuild (t )
@@ -354,4 +631,28 @@ func TestDwarfVariableLocations(t *testing.T) {
354631 t .Parallel ()
355632 testRuntimeThrow (t , harnessPath , nooptHarnessPath , ppath )
356633 })
634+ t .Run ("Issue65405" , func (t * testing.T ) {
635+ t .Parallel ()
636+ testIssue65405 (t , harnessPath , ppath )
637+ })
638+ t .Run ("ReturnValueRegisters" , func (t * testing.T ) {
639+ t .Parallel ()
640+ testReturnValueRegisters (t , harnessPath , ppath )
641+ })
642+ t .Run ("RegisterLivenessNamedRetParam" , func (t * testing.T ) {
643+ t .Parallel ()
644+ testRegisterLivenessNamedRetParam (t , harnessPath , nooptHarnessPath , ppath , nooppath )
645+ })
646+ t .Run ("RegisterLivenessUnnamedRetParam" , func (t * testing.T ) {
647+ t .Parallel ()
648+ testLivenessForUnnamedRetParams (t , harnessPath , nooptHarnessPath , ppath , nooppath )
649+ })
650+ t .Run ("ManyArgsWithNamedReturns" , func (t * testing.T ) {
651+ t .Parallel ()
652+ testManyArgsWithNamedReturns (t , harnessPath , nooptHarnessPath , ppath , nooppath )
653+ })
654+ t .Run ("multiReturnStmts" , func (t * testing.T ) {
655+ t .Parallel ()
656+ testMultipleReturnStmts (t , harnessPath , nooptHarnessPath , ppath , nooppath )
657+ })
357658}
0 commit comments