cmd/compile: add structs.HostLayout

This is for the proposal, plus a few bug fixes
that would/will be necessary when this is put into
actual use.

Fixes #66408.
Updates #63131.

Change-Id: I3a66e09d707dd579c59f155e7f53367f41214c30
Reviewed-on: https://go-review.googlesource.com/c/go/+/578355
Reviewed-by: Austin Clements <austin@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: David Chase <drchase@google.com>
This commit is contained in:
David Chase 2024-04-11 17:22:53 -04:00 committed by Gopher Robot
parent ecad164da7
commit 22344e11f2
11 changed files with 75 additions and 6 deletions

1
api/next/66408.txt Normal file
View File

@ -0,0 +1 @@
pkg structs, type HostLayout struct #66408

View File

@ -0,0 +1,12 @@
### New structs package
The new [structs](/pkg/structs) package provides
types for struct fields that modify properties of
the containing struct type such as memory layout.
In this release, the only such type is
[`HostLayout`](/pkg/structs#HostLayout)
which indicates that a structure with a field of that
type has a layout that conforms to host platform
expectations.

View File

@ -0,0 +1 @@
<!-- This is a new package; covered in 6-stdlib/3-structs.md. -->

View File

@ -141,7 +141,7 @@ func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64)
} }
typs := make([]*types.Type, 0, l) typs := make([]*types.Type, 0, l)
offs := make([]int64, 0, l) offs := make([]int64, 0, l)
offs, _ = appendParamOffsets(offs, 0, pa.Type) offs, _ = appendParamOffsets(offs, 0, pa.Type) // 0 is aligned for everything.
return appendParamTypes(typs, pa.Type), offs return appendParamTypes(typs, pa.Type), offs
} }
@ -193,8 +193,8 @@ func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
// appendParamOffsets appends the offset(s) of type t, starting from "at", // appendParamOffsets appends the offset(s) of type t, starting from "at",
// to input offsets, and returns the longer slice and the next unused offset. // to input offsets, and returns the longer slice and the next unused offset.
// at should already be aligned for t.
func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) { func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) {
at = align(at, t)
w := t.Size() w := t.Size()
if w == 0 { if w == 0 {
return offsets, at return offsets, at
@ -210,11 +210,15 @@ func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int6
typ := t.Kind() typ := t.Kind()
switch typ { switch typ {
case types.TARRAY: case types.TARRAY:
te := t.Elem()
for i := int64(0); i < t.NumElem(); i++ { for i := int64(0); i < t.NumElem(); i++ {
offsets, at = appendParamOffsets(offsets, at, t.Elem()) at = align(at, te)
offsets, at = appendParamOffsets(offsets, at, te)
} }
case types.TSTRUCT: case types.TSTRUCT:
at0 := at
for i, f := range t.Fields() { for i, f := range t.Fields() {
at = at0 + f.Offset // Fields may be over-aligned, see wasm32.
offsets, at = appendParamOffsets(offsets, at, f.Type) offsets, at = appendParamOffsets(offsets, at, f.Type)
if f.Type.Size() == 0 && i == t.NumFields()-1 { if f.Type.Size() == 0 && i == t.NumFields()-1 {
at++ // last field has zero width at++ // last field has zero width
@ -668,12 +672,13 @@ func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
if len(types) != nr { if len(types) != nr {
panic("internal error") panic("internal error")
} }
offsets, _ := appendParamOffsets([]int64{}, 0, pa.Type)
off := int64(0) off := int64(0)
for idx, t := range types { for idx, t := range types {
ts := t.Size() ts := t.Size()
off += int64(ts) off += int64(ts)
if idx < len(types)-1 { if idx < len(types)-1 {
noff := align(off, types[idx+1]) noff := offsets[idx+1]
if noff != off { if noff != off {
padding[idx] = uint64(noff - off) padding[idx] = uint64(noff - off)
} }

View File

@ -23,8 +23,8 @@ func init() {
types.PtrSize = 8 types.PtrSize = 8
types.RegSize = 8 types.RegSize = 8
types.MaxWidth = 1 << 50 types.MaxWidth = 1 << 50
typecheck.InitUniverse()
base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}} base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}}
typecheck.InitUniverse()
} }
func TestEqStructCost(t *testing.T) { func TestEqStructCost(t *testing.T) {

View File

@ -13,6 +13,7 @@ import (
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/pgo" "cmd/internal/pgo"
"cmd/internal/src" "cmd/internal/src"
"cmd/internal/sys"
"testing" "testing"
) )
@ -23,8 +24,8 @@ func init() {
types.PtrSize = 8 types.PtrSize = 8
types.RegSize = 8 types.RegSize = 8
types.MaxWidth = 1 << 50 types.MaxWidth = 1 << 50
base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}}
typecheck.InitUniverse() typecheck.InitUniverse()
base.Ctxt = &obj.Link{}
base.Debug.PGODebug = 3 base.Debug.PGODebug = 3
} }

View File

@ -5,10 +5,13 @@
package inlheur package inlheur
import ( import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir" "cmd/compile/internal/ir"
"cmd/compile/internal/typecheck" "cmd/compile/internal/typecheck"
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"cmd/internal/sys"
"go/constant" "go/constant"
"testing" "testing"
) )
@ -21,6 +24,8 @@ func init() {
types.PtrSize = 8 types.PtrSize = 8
types.RegSize = 8 types.RegSize = 8
types.MaxWidth = 1 << 50 types.MaxWidth = 1 << 50
base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}}
typecheck.InitUniverse() typecheck.InitUniverse()
local = types.NewPkg("", "") local = types.NewPkg("", "")
fsym := &types.Sym{ fsym := &types.Sym{

View File

@ -7,6 +7,7 @@ package ssa
import ( import (
"testing" "testing"
"cmd/compile/internal/base"
"cmd/compile/internal/ir" "cmd/compile/internal/ir"
"cmd/compile/internal/typecheck" "cmd/compile/internal/typecheck"
"cmd/compile/internal/types" "cmd/compile/internal/types"
@ -15,6 +16,7 @@ import (
"cmd/internal/obj/s390x" "cmd/internal/obj/s390x"
"cmd/internal/obj/x86" "cmd/internal/obj/x86"
"cmd/internal/src" "cmd/internal/src"
"cmd/internal/sys"
) )
var CheckFunc = checkFunc var CheckFunc = checkFunc
@ -115,6 +117,7 @@ func init() {
types.RegSize = 8 types.RegSize = 8
types.MaxWidth = 1 << 50 types.MaxWidth = 1 << 50
base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}}
typecheck.InitUniverse() typecheck.InitUniverse()
testTypes.SetTypPtrs() testTypes.SetTypPtrs()
} }

View File

@ -39,6 +39,7 @@ var stdPkgs = []string{
"sort", "sort",
"strconv", "strconv",
"strings", "strings",
"structs",
"sync", "sync",
"syscall", "syscall",
"testing", "testing",

10
src/structs/doc.go Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2024 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 structs defines marker types that can be used as struct fields
// to modify the properties of a struct.
//
// By convention, a marker type should be used as the type of a field
// named "_", placed at the beginning of a struct type definition.
package structs

30
src/structs/hostlayout.go Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2024 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 structs
// HostLayout marks a struct as using host memory layout. A struct with a
// field of type HostLayout will be laid out in memory according to host
// expectations, generally following the host's C ABI.
//
// HostLayout does not affect layout within any other struct-typed fields
// of the containing struct, nor does it affect layout of structs
// containing the struct marked as host layout.
//
// By convention, HostLayout should be used as the type of a field
// named "_", placed at the beginning of the struct type definition.
type HostLayout struct {
_ hostLayout // prevent accidental conversion with plain struct{}
}
// We use an unexported type within the exported type to give the marker
// type itself, rather than merely its name, a recognizable identity in
// the type system. The main consequence of this is that a user can give
// the type a new name and it will still have the same properties, e.g.,
//
// type HL structs.HostLayout
//
// It also prevents unintentional conversion of struct{} to a named marker type.
type hostLayout struct {
}