mirror of
https://github.com/golang/go.git
synced 2026-07-30 09:38:37 +08:00
cmd/compile: generalize staticuint64s sharing to larger integer constant types
Optimize interface conversion of small integer constants of any size (values in range [0, 255]) by redirecting their references to direct offsets into runtime.staticuint64s during the walk lowering phase. This avoids generating redundant static temporary backing variables (stmp symbols) in the read-only data segment, allowing the linker's dead-code elimination pass to discard them. Consolidate the compile-time constant lookup with the dynamic size-1 variable path into a single switch case block in dataWord(), preserving the evaluation order of expressions with side effects. Fixes #37612 Change-Id: I81bd20bc4e98f690aad67804e23b104dfe2a6ed8 Reviewed-on: https://go-review.googlesource.com/c/go/+/802320 Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
591ed42daf
commit
ee64db65cf
@ -152,26 +152,35 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
|
||||
|
||||
// Try a bunch of cases to avoid an allocation.
|
||||
var value ir.Node
|
||||
byteVal, isConst := smallIntConst(n)
|
||||
switch {
|
||||
case fromType.Size() == 0:
|
||||
// n is zero-sized. Use zerobase.
|
||||
diagnose("using global for zero-sized interface value", n)
|
||||
cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
|
||||
value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
|
||||
case isBool || fromType.Size() == 1 && isInteger:
|
||||
// n is a bool/byte. Use staticuint64s[n * 8] on little-endian
|
||||
// and staticuint64s[n * 8 + 7] on big-endian.
|
||||
diagnose("using global for single-byte interface value", n)
|
||||
n = cheapExpr(n, init)
|
||||
n = soleComponent(init, n)
|
||||
case isBool || isInteger && (fromType.Size() == 1 || isConst):
|
||||
// n is a bool, a single-byte integer, or a compile-time constant in [0, 255].
|
||||
// Use staticuint64s[n * 8] on little-endian and staticuint64s[n * 8 + 7] on big-endian.
|
||||
diagnose("using global for small integer/boolean interface value", n)
|
||||
if isConst {
|
||||
n = ir.NewBasicLit(base.Pos, types.Types[types.TUINT8], constant.MakeInt64(int64(byteVal)))
|
||||
} else {
|
||||
n = cheapExpr(n, init)
|
||||
n = soleComponent(init, n)
|
||||
base.Assert(fromType.Size() == 1)
|
||||
}
|
||||
|
||||
// byteindex widens n so that the multiplication doesn't overflow.
|
||||
index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(base.Pos, 3))
|
||||
if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
|
||||
index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(base.Pos, 7))
|
||||
index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(base.Pos, 8-fromType.Size()))
|
||||
}
|
||||
// The actual type is [256]uint64, but we use [256*8]uint8 so we can address
|
||||
// individual bytes.
|
||||
staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
|
||||
t := types.NewArray(types.Types[types.TUINT8], 256*8)
|
||||
types.CalcSize(t)
|
||||
staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, t)
|
||||
xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
|
||||
xe.SetBounded(true)
|
||||
value = xe
|
||||
@ -244,6 +253,65 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
|
||||
return safeExpr(walkExpr(typecheck.Expr(call), init), init)
|
||||
}
|
||||
|
||||
// smallIntConst returns the byte value of n if it is a compile-time
|
||||
// constant in the range [0, 255] (either a literal or a readonly global stmp).
|
||||
func smallIntConst(n ir.Node) (byte, bool) {
|
||||
if ir.IsConst(n, constant.Int) {
|
||||
val := ir.Int64Val(n)
|
||||
if val >= 0 && val <= 255 {
|
||||
return byte(val), true
|
||||
}
|
||||
}
|
||||
if ir.IsConst(n, constant.Bool) {
|
||||
if ir.BoolVal(n) {
|
||||
return 1, true
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
|
||||
if n.Op() == ir.ONAME && n.Name().Readonly() {
|
||||
t := n.Type()
|
||||
if !t.IsBoolean() && !t.IsInteger() {
|
||||
return 0, false
|
||||
}
|
||||
size := t.Size()
|
||||
|
||||
sym := n.Name().Linksym()
|
||||
if sym == nil {
|
||||
return 0, false
|
||||
}
|
||||
p := sym.P
|
||||
var byteVal byte
|
||||
|
||||
// Determine the Least Significant Byte (LSB) index based on architecture.
|
||||
lsbIdx := int64(0)
|
||||
if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
|
||||
lsbIdx = size - 1
|
||||
}
|
||||
|
||||
if lsbIdx < int64(len(p)) {
|
||||
byteVal = p[lsbIdx]
|
||||
}
|
||||
|
||||
// Ensure all non-LSB bytes in the integer representation are zero (value fits in a byte).
|
||||
for i := int64(0); i < size; i++ {
|
||||
if i == lsbIdx {
|
||||
continue
|
||||
}
|
||||
var b byte
|
||||
if i < int64(len(p)) {
|
||||
b = p[i]
|
||||
}
|
||||
if b != 0 {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return byteVal, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
|
||||
func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
|
||||
a := typecheck.NodNil()
|
||||
|
||||
@ -1,22 +1,103 @@
|
||||
// asmcheck
|
||||
|
||||
package codegen
|
||||
|
||||
// Copyright 2020 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
|
||||
|
||||
func booliface() interface{} {
|
||||
// amd64:`LEAQ runtime.staticuint64s\+8\(SB\)`
|
||||
// 386:`LEAL\sruntime.staticuint64s\+8\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+8\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+15\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+15\(SB\),\sR\d+`
|
||||
return true
|
||||
}
|
||||
|
||||
func smallint8iface() interface{} {
|
||||
// amd64:`LEAQ runtime.staticuint64s\+2024\(SB\)`
|
||||
// 386:`LEAL\sruntime.staticuint64s\+2024\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+2024\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+2031\(SB\)`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+2031\(SB\)`
|
||||
return int8(-3)
|
||||
}
|
||||
|
||||
func smalluint8iface() interface{} {
|
||||
// amd64:`LEAQ runtime.staticuint64s\+24\(SB\)`
|
||||
// 386:`LEAL\sruntime.staticuint64s\+24\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+24\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+31\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+31\(SB\),\sR\d+`
|
||||
return uint8(3)
|
||||
}
|
||||
|
||||
func smallintiface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+8\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+8\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+12\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+8\(SB\),\sR\d+`
|
||||
return 1
|
||||
}
|
||||
|
||||
func smallint16iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+1016\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+1016\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+1022\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+1022\(SB\),\sR\d+`
|
||||
return int16(127)
|
||||
}
|
||||
|
||||
func smallint32iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+2040\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+2040\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+2044\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+2044\(SB\),\sR\d+`
|
||||
return int32(255)
|
||||
}
|
||||
|
||||
func smallint64iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+2040\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+2040\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+2040\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+2040\(SB\),\sR\d+`
|
||||
return int64(255)
|
||||
}
|
||||
|
||||
func smalluintface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+16\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+16\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+20\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+16\(SB\),\sR\d+`
|
||||
return uint(2)
|
||||
}
|
||||
|
||||
func smalluintptriface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+24\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+24\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+28\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+24\(SB\),\sR\d+`
|
||||
return uintptr(3)
|
||||
}
|
||||
|
||||
func smalluint16iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+80\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+80\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+86\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+86\(SB\),\sR\d+`
|
||||
return uint16(10)
|
||||
}
|
||||
|
||||
func smalluint32iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+8\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+8\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+12\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+12\(SB\),\sR\d+`
|
||||
return uint32(1)
|
||||
}
|
||||
|
||||
func smalluint64iface() interface{} {
|
||||
// 386:`LEAL\sruntime.staticuint64s\+56\(SB\)`
|
||||
// amd64:`LEAQ\sruntime.staticuint64s\+56\(SB\)`
|
||||
// mips:`MOVW\s[$]runtime.staticuint64s\+56\(SB\),\sR\d+`
|
||||
// mips64:`MOVV\s[$]runtime.staticuint64s\+56\(SB\),\sR\d+`
|
||||
return uint64(7)
|
||||
}
|
||||
|
||||
@ -82,37 +82,37 @@ func string11() {
|
||||
}
|
||||
|
||||
func integer1() {
|
||||
sink = 42 // ERROR "using global for interface value"
|
||||
sink = 42 // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func integer2() {
|
||||
v := 42
|
||||
sink = v // ERROR "using global for interface value"
|
||||
sink = v // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func integer3() {
|
||||
sink = 0 // ERROR "using global for interface value"
|
||||
sink = 0 // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func integer4a() {
|
||||
v := 0
|
||||
sink = v // ERROR "using global for interface value"
|
||||
sink = v // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func integer4b() {
|
||||
v := uint8(0)
|
||||
sink = v // ERROR "using global for single-byte interface value"
|
||||
sink = v // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func integer5() {
|
||||
var a any = 42 // ERROR "using global for interface value"
|
||||
var a any = 42 // ERROR "using global for small integer/boolean interface value"
|
||||
_ = a
|
||||
}
|
||||
|
||||
func integer6() {
|
||||
var a any
|
||||
v := 42
|
||||
a = v // ERROR "using global for interface value"
|
||||
a = v // ERROR "using global for small integer/boolean interface value"
|
||||
_ = a
|
||||
}
|
||||
|
||||
@ -131,69 +131,69 @@ func escapes(m M) {
|
||||
}
|
||||
|
||||
func named1a() {
|
||||
sink = MyInt(42) // ERROR "using global for interface value"
|
||||
sink = MyInt(42) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named1b() {
|
||||
escapes(MyInt(42)) // ERROR "using global for interface value"
|
||||
escapes(MyInt(42)) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named2a() {
|
||||
v := MyInt(0)
|
||||
sink = v // ERROR "using global for interface value"
|
||||
sink = v // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named2b() {
|
||||
v := MyInt(42)
|
||||
escapes(v) // ERROR "using global for interface value"
|
||||
escapes(v) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named2c() {
|
||||
v := 42
|
||||
sink = MyInt(v) // ERROR "using global for interface value"
|
||||
sink = MyInt(v) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named2d() {
|
||||
v := 42
|
||||
escapes(MyInt(v)) // ERROR "using global for interface value"
|
||||
escapes(MyInt(v)) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
func named3a() {
|
||||
sink = MyInt(42) // ERROR "using global for interface value"
|
||||
sink = MyInt(42) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named3b() {
|
||||
escapes(MyInt(0)) // ERROR "using global for interface value"
|
||||
escapes(MyInt(0)) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named4a() {
|
||||
v := MyInt(0)
|
||||
sink = v // ERROR "using global for interface value"
|
||||
sink = v // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named4b() {
|
||||
v := MyInt(0)
|
||||
escapes(v) // ERROR "using global for interface value"
|
||||
escapes(v) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named4c() {
|
||||
v := 0
|
||||
sink = MyInt(v) // ERROR "using global for interface value"
|
||||
sink = MyInt(v) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named4d() {
|
||||
v := 0
|
||||
escapes(MyInt(v)) // ERROR "using global for interface value"
|
||||
escapes(MyInt(v)) // ERROR "using global for small integer/boolean interface value"
|
||||
}
|
||||
|
||||
func named5() {
|
||||
var a any = MyInt(42) // ERROR "using global for interface value"
|
||||
var a any = MyInt(42) // ERROR "using global for small integer/boolean interface value"
|
||||
_ = a
|
||||
}
|
||||
|
||||
func named6() {
|
||||
var a any
|
||||
v := MyInt(42)
|
||||
a = v // ERROR "using global for interface value"
|
||||
a = v // ERROR "using global for small integer/boolean interface value"
|
||||
_ = a
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user