mirror of
https://github.com/golang/go.git
synced 2025-10-20 03:23:18 +00:00

Currently, we remove stores to local variables that are not read. We don't do that for arguments. But arguments and locals are essentially the same. Arguments are passed by value, and are not expected to be read in the caller's frame. So we can remove the writes to them as well. One exception is the cgo_unsafe_arg directive, which makes all the arguments effectively address-taken. cgo_unsafe_arg implies ABI0, so we just skip ABI0 functions' arguments. Cherry-picked from the dev.simd branch. This CL is not necessarily SIMD specific. Apply early to reduce risk. Change-Id: I8999fc50da6a87f22c1ec23e9a0c15483b6f7df8 Reviewed-on: https://go-review.googlesource.com/c/go/+/705815 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Junyang Shao <shaojunyang@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/708865
176 lines
4 KiB
Go
176 lines
4 KiB
Go
// asmcheck
|
|
|
|
// Copyright 2018 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.
|
|
|
|
package codegen
|
|
|
|
import (
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
// This file contains code generation tests related to the use of the
|
|
// stack.
|
|
|
|
// Check that stack stores are optimized away.
|
|
|
|
// 386:"TEXT\t.*, [$]0-"
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
// arm:"TEXT\t.*, [$]-4-"
|
|
// arm64:"TEXT\t.*, [$]0-"
|
|
// mips:"TEXT\t.*, [$]-4-"
|
|
// ppc64x:"TEXT\t.*, [$]0-"
|
|
// s390x:"TEXT\t.*, [$]0-"
|
|
func StackStore() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
|
|
type T struct {
|
|
A, B, C, D int // keep exported fields
|
|
x, y, z int // reset unexported fields
|
|
}
|
|
|
|
// Check that large structs are cleared directly (issue #24416).
|
|
|
|
// 386:"TEXT\t.*, [$]0-"
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
// arm:"TEXT\t.*, [$]0-" (spills return address)
|
|
// arm64:"TEXT\t.*, [$]0-"
|
|
// mips:"TEXT\t.*, [$]-4-"
|
|
// ppc64x:"TEXT\t.*, [$]0-"
|
|
// s390x:"TEXT\t.*, [$]0-"
|
|
func ZeroLargeStruct(x *T) {
|
|
t := T{}
|
|
*x = t
|
|
}
|
|
|
|
// Check that structs are partially initialised directly (issue #24386).
|
|
|
|
// Notes:
|
|
// - 386 fails due to spilling a register
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
// arm:"TEXT\t.*, [$]0-" (spills return address)
|
|
// arm64:"TEXT\t.*, [$]0-"
|
|
// ppc64x:"TEXT\t.*, [$]0-"
|
|
// s390x:"TEXT\t.*, [$]0-"
|
|
// Note: that 386 currently has to spill a register.
|
|
func KeepWanted(t *T) {
|
|
*t = T{A: t.A, B: t.B, C: t.C, D: t.D}
|
|
}
|
|
|
|
// Check that small array operations avoid using the stack (issue #15925).
|
|
|
|
// Notes:
|
|
// - 386 fails due to spilling a register
|
|
// - arm & mips fail due to softfloat calls
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
// arm64:"TEXT\t.*, [$]0-"
|
|
// ppc64x:"TEXT\t.*, [$]0-"
|
|
// s390x:"TEXT\t.*, [$]0-"
|
|
func ArrayAdd64(a, b [4]float64) [4]float64 {
|
|
return [4]float64{a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]}
|
|
}
|
|
|
|
// Check that small array initialization avoids using the stack.
|
|
|
|
// 386:"TEXT\t.*, [$]0-"
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
// arm:"TEXT\t.*, [$]0-" (spills return address)
|
|
// arm64:"TEXT\t.*, [$]0-"
|
|
// mips:"TEXT\t.*, [$]-4-"
|
|
// ppc64x:"TEXT\t.*, [$]0-"
|
|
// s390x:"TEXT\t.*, [$]0-"
|
|
func ArrayInit(i, j int) [4]int {
|
|
return [4]int{i, 0, j, 0}
|
|
}
|
|
|
|
// Check that assembly output has matching offset and base register
|
|
// (issue #21064).
|
|
|
|
func check_asmout(b [2]int) int {
|
|
runtime.GC() // use some frame
|
|
// amd64:`.*b\+24\(SP\)`
|
|
// arm:`.*b\+4\(FP\)`
|
|
return b[1]
|
|
}
|
|
|
|
// Check that simple functions get promoted to nosplit, even when
|
|
// they might panic in various ways. See issue 31219.
|
|
// amd64:"TEXT\t.*NOSPLIT.*"
|
|
func MightPanic(a []int, i, j, k, s int) {
|
|
_ = a[i] // panicIndex
|
|
_ = a[i:j] // panicSlice
|
|
_ = a[i:j:k] // also panicSlice
|
|
_ = i << s // panicShift
|
|
_ = i / j // panicDivide
|
|
}
|
|
|
|
// Put a defer in a loop, so second defer is not open-coded
|
|
func Defer() {
|
|
for i := 0; i < 2; i++ {
|
|
defer func() {}()
|
|
}
|
|
// amd64:`CALL\truntime\.deferprocStack`
|
|
defer func() {}()
|
|
}
|
|
|
|
// Check that stack slots are shared among values of the same
|
|
// type, but not pointer-identical types. See issue 65783.
|
|
|
|
func spillSlotReuse() {
|
|
// The return values of getp1 and getp2 need to be
|
|
// spilled around the calls to nopInt. Make sure that
|
|
// spill slot gets reused.
|
|
|
|
//arm64:`.*autotmp_2-8\(SP\)`
|
|
getp1()[nopInt()] = 0
|
|
//arm64:`.*autotmp_2-8\(SP\)`
|
|
getp2()[nopInt()] = 0
|
|
}
|
|
|
|
// Check that no stack frame space is needed for simple slice initialization with underlying structure.
|
|
type mySlice struct {
|
|
array unsafe.Pointer
|
|
len int
|
|
cap int
|
|
}
|
|
|
|
// amd64:"TEXT\t.*, [$]0-"
|
|
func sliceInit(base uintptr) []uintptr {
|
|
const ptrSize = 8
|
|
size := uintptr(4096)
|
|
bitmapSize := size / ptrSize / 8
|
|
elements := int(bitmapSize / ptrSize)
|
|
var sl mySlice
|
|
sl = mySlice{
|
|
unsafe.Pointer(base + size - bitmapSize),
|
|
elements,
|
|
elements,
|
|
}
|
|
// amd64:-"POPQ",-"SP"
|
|
return *(*[]uintptr)(unsafe.Pointer(&sl))
|
|
}
|
|
|
|
//go:noinline
|
|
func nopInt() int {
|
|
return 0
|
|
}
|
|
|
|
//go:noinline
|
|
func getp1() *[4]int {
|
|
return nil
|
|
}
|
|
|
|
//go:noinline
|
|
func getp2() *[4]int {
|
|
return nil
|
|
}
|
|
|
|
// Store to an argument without read can be removed.
|
|
func storeArg(a [2]int) {
|
|
// amd64:-`MOVQ\t\$123,.*\.a\+\d+\(SP\)`
|
|
a[1] = 123
|
|
}
|