mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types, types2: correct alignment of atomic.Int64
atomic.Int64 has special logic in the compiler to ensure it's 8-byte aligned on 32-bit architectures. The equivalent logic is missing in go/types, which means the compiler and go/types can come to different conclusions about the layout of types. Fix this by mirroring the compiler's logic into go/types. Fixes #53884. Change-Id: I3f58a56babb76634839a161ca174c8f085fe3ba4 Reviewed-on: https://go-review.googlesource.com/c/go/+/417555 Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
4651ebf961
commit
2aa473cc54
5 changed files with 128 additions and 2 deletions
|
|
@ -169,6 +169,8 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
|
||||||
}
|
}
|
||||||
// Special case: sync/atomic.align64 is an empty struct we recognize
|
// Special case: sync/atomic.align64 is an empty struct we recognize
|
||||||
// as a signal that the struct it contains must be 64-bit-aligned.
|
// as a signal that the struct it contains must be 64-bit-aligned.
|
||||||
|
//
|
||||||
|
// This logic is duplicated in go/types and cmd/compile/internal/types2.
|
||||||
if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isAtomicStdPkg(t.Sym().Pkg) {
|
if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isAtomicStdPkg(t.Sym().Pkg) {
|
||||||
maxalign = 8
|
maxalign = 8
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,17 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||||
return s.Alignof(t.elem)
|
return s.Alignof(t.elem)
|
||||||
case *Struct:
|
case *Struct:
|
||||||
|
if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
|
||||||
|
// Special case: sync/atomic.align64 is an
|
||||||
|
// empty struct we recognize as a signal that
|
||||||
|
// the struct it contains must be
|
||||||
|
// 64-bit-aligned.
|
||||||
|
//
|
||||||
|
// This logic is equivalent to the logic in
|
||||||
|
// cmd/compile/internal/types/size.go:calcStructOffset
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
// field f of x, but at least 1."
|
// field f of x, but at least 1."
|
||||||
|
|
@ -93,6 +104,18 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSyncAtomicAlign64(T Type) bool {
|
||||||
|
named, ok := T.(*Named)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
obj := named.Obj()
|
||||||
|
return obj.Name() == "align64" &&
|
||||||
|
obj.Pkg() != nil &&
|
||||||
|
(obj.Pkg().Path() == "sync/atomic" ||
|
||||||
|
obj.Pkg().Path() == "runtime/internal/atomic")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
||||||
offsets := make([]int64, len(fields))
|
offsets := make([]int64, len(fields))
|
||||||
var o int64
|
var o int64
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,15 @@ import (
|
||||||
|
|
||||||
// findStructType typechecks src and returns the first struct type encountered.
|
// findStructType typechecks src and returns the first struct type encountered.
|
||||||
func findStructType(t *testing.T, src string) *types2.Struct {
|
func findStructType(t *testing.T, src string) *types2.Struct {
|
||||||
|
return findStructTypeConfig(t, src, &types2.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func findStructTypeConfig(t *testing.T, src string, conf *types2.Config) *types2.Struct {
|
||||||
f, err := parseSrc("x.go", src)
|
f, err := parseSrc("x.go", src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)}
|
info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)}
|
||||||
var conf types2.Config
|
|
||||||
_, err = conf.Check("x", []*syntax.File{f}, &info)
|
_, err = conf.Check("x", []*syntax.File{f}, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -105,3 +108,39 @@ const _ = unsafe.Offsetof(struct{ x int64 }{}.x)
|
||||||
_ = conf.Sizes.Alignof(tv.Type)
|
_ = conf.Sizes.Alignof(tv.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue #53884.
|
||||||
|
func TestAtomicAlign(t *testing.T) {
|
||||||
|
const src = `
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
var s struct {
|
||||||
|
x int32
|
||||||
|
y atomic.Int64
|
||||||
|
z int64
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
want := []int64{0, 8, 16}
|
||||||
|
for _, arch := range []string{"386", "amd64"} {
|
||||||
|
t.Run(arch, func(t *testing.T) {
|
||||||
|
conf := types2.Config{
|
||||||
|
Importer: defaultImporter(),
|
||||||
|
Sizes: types2.SizesFor("gc", arch),
|
||||||
|
}
|
||||||
|
ts := findStructTypeConfig(t, src, &conf)
|
||||||
|
var fields []*types2.Var
|
||||||
|
// Make a copy manually :(
|
||||||
|
for i := 0; i < ts.NumFields(); i++ {
|
||||||
|
fields = append(fields, ts.Field(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
offsets := conf.Sizes.Offsetsof(fields)
|
||||||
|
if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] {
|
||||||
|
t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,17 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||||
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||||
return s.Alignof(t.elem)
|
return s.Alignof(t.elem)
|
||||||
case *Struct:
|
case *Struct:
|
||||||
|
if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
|
||||||
|
// Special case: sync/atomic.align64 is an
|
||||||
|
// empty struct we recognize as a signal that
|
||||||
|
// the struct it contains must be
|
||||||
|
// 64-bit-aligned.
|
||||||
|
//
|
||||||
|
// This logic is equivalent to the logic in
|
||||||
|
// cmd/compile/internal/types/size.go:calcStructOffset
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
// field f of x, but at least 1."
|
// field f of x, but at least 1."
|
||||||
|
|
@ -93,6 +104,18 @@ func (s *StdSizes) Alignof(T Type) int64 {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSyncAtomicAlign64(T Type) bool {
|
||||||
|
named, ok := T.(*Named)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
obj := named.Obj()
|
||||||
|
return obj.Name() == "align64" &&
|
||||||
|
obj.Pkg() != nil &&
|
||||||
|
(obj.Pkg().Path() == "sync/atomic" ||
|
||||||
|
obj.Pkg().Path() == "runtime/internal/atomic")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
||||||
offsets := make([]int64, len(fields))
|
offsets := make([]int64, len(fields))
|
||||||
var o int64
|
var o int64
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,16 @@ import (
|
||||||
|
|
||||||
// findStructType typechecks src and returns the first struct type encountered.
|
// findStructType typechecks src and returns the first struct type encountered.
|
||||||
func findStructType(t *testing.T, src string) *types.Struct {
|
func findStructType(t *testing.T, src string) *types.Struct {
|
||||||
|
return findStructTypeConfig(t, src, &types.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func findStructTypeConfig(t *testing.T, src string, conf *types.Config) *types.Struct {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, "x.go", src, 0)
|
f, err := parser.ParseFile(fset, "x.go", src, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
|
info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
|
||||||
var conf types.Config
|
|
||||||
_, err = conf.Check("x", fset, []*ast.File{f}, &info)
|
_, err = conf.Check("x", fset, []*ast.File{f}, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -110,3 +113,39 @@ const _ = unsafe.Offsetof(struct{ x int64 }{}.x)
|
||||||
_ = conf.Sizes.Alignof(tv.Type)
|
_ = conf.Sizes.Alignof(tv.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue #53884.
|
||||||
|
func TestAtomicAlign(t *testing.T) {
|
||||||
|
const src = `
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
var s struct {
|
||||||
|
x int32
|
||||||
|
y atomic.Int64
|
||||||
|
z int64
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
want := []int64{0, 8, 16}
|
||||||
|
for _, arch := range []string{"386", "amd64"} {
|
||||||
|
t.Run(arch, func(t *testing.T) {
|
||||||
|
conf := types.Config{
|
||||||
|
Importer: importer.Default(),
|
||||||
|
Sizes: types.SizesFor("gc", arch),
|
||||||
|
}
|
||||||
|
ts := findStructTypeConfig(t, src, &conf)
|
||||||
|
var fields []*types.Var
|
||||||
|
// Make a copy manually :(
|
||||||
|
for i := 0; i < ts.NumFields(); i++ {
|
||||||
|
fields = append(fields, ts.Field(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
offsets := conf.Sizes.Offsetsof(fields)
|
||||||
|
if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] {
|
||||||
|
t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue