| // run |
| |
| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // TODO: disable test for ppc64le/dynlink? See cmd/compile/internal/reader/noder.go:addTailCall. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "runtime" |
| "time" |
| ) |
| |
| type I interface { |
| foo() time.Duration // foo returns its running time |
| } |
| |
| type base struct { |
| } |
| |
| func (b *base) foo() time.Duration { |
| t := time.Now() |
| var pcs [10]uintptr |
| runtime.Callers(1, pcs[:]) |
| return time.Since(t) |
| } |
| |
| type wrap struct { |
| I |
| data int |
| } |
| |
| // best runs f a bunch of times, picks the shortest returned duration. |
| func best(f func() time.Duration) time.Duration { |
| m := f() |
| for range 9 { |
| m = min(m, f()) |
| } |
| return m |
| } |
| |
| func main() { |
| if runtime.GOARCH == "wasm" { |
| // TODO: upgrade wasm to do indirect tail calls |
| return |
| } |
| var i I = &base{} |
| for x := range 1000 { |
| i = &wrap{I: i, data: x} |
| } |
| short := best(i.foo) |
| for x := range 9000 { |
| i = &wrap{I: i, data: x} |
| } |
| long := best(i.foo) |
| |
| ratio := long.Seconds() / short.Seconds() |
| |
| // Running time should be independent of the number of wrappers. |
| // Prior to the fix for 75764, it was linear in the number of wrappers. |
| // Pre-fix, we get ratios typically in the 7.0-10.0 range. |
| // Post-fix, it is in the 1.0-1.5 range. |
| allowed := 5.0 |
| if ratio >= allowed { |
| fmt.Printf("short: %v\nlong: %v\nratio: %v\nallowed: %v\n", short, long, ratio, allowed) |
| } |
| } |