cmd/compile/internal/noder: encode promoted struct fields for composite literals in UIR

This change requires an encoding format change for struct literals.
Introduce a new UIR version (V3) and use the opportunity to encode
all composite literals more compactly: specifically, when we know
that (composite literal) keys are always present, avoid encoding a
bool value for each key.

Do not yet enable the new format.

For #9859.

Change-Id: Ic6dc9adb1aa494e923eadaf578f8cfc61efd5ea4
Reviewed-on: https://go-review.googlesource.com/c/go/+/754664
Reviewed-by: Mark Freeman <markfreeman@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer
2026-03-11 19:02:07 -07:00
parent 9f7e98d263
commit d9a600a73b
3 changed files with 185 additions and 13 deletions

View File

@@ -3028,21 +3028,37 @@ func (r *reader) compLit() ir.Node {
if typ.IsMap() {
rtype = r.rtype(pos)
}
isStruct := typ.Kind() == types.TSTRUCT
elems := make([]ir.Node, r.Len())
for i := range elems {
elemp := &elems[i]
if isStruct {
sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil)
*elemp, elemp = sk, &sk.Value
} else if r.Bool() {
kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
*elemp, elemp = kv, &kv.Value
var elems []ir.Node
if r.Version().Has(pkgbits.CompactCompLiterals) {
n := r.Int()
elems = make([]ir.Node, max(n, -n) /* abs(n) */)
switch typ.Kind() {
default:
base.FatalfAt(pos, "unexpected composite literal type: %v", typ)
case types.TARRAY:
r.arrayElems(n >= 0, elems)
case types.TMAP:
r.mapElems(elems)
case types.TSLICE:
r.arrayElems(n >= 0, elems)
case types.TSTRUCT:
r.structElems(typ, n >= 0, elems)
}
} else {
elems = make([]ir.Node, r.Len())
isStruct := typ.Kind() == types.TSTRUCT
for i := range elems {
elemp := &elems[i]
if isStruct {
sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil)
*elemp, elemp = sk, &sk.Value
} else if r.Bool() {
kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
*elemp, elemp = kv, &kv.Value
}
*elemp = r.expr()
}
*elemp = r.expr()
}
lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
@@ -3057,6 +3073,63 @@ func (r *reader) compLit() ir.Node {
return lit
}
func (r *reader) arrayElems(valuesOnly bool, elems []ir.Node) {
if valuesOnly {
for i := range elems {
elems[i] = r.expr()
}
return
}
// some elements may have a key
for i := range elems {
if r.Bool() {
kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
kv.Value = r.expr()
elems[i] = kv
} else {
elems[i] = r.expr()
}
}
}
func (r *reader) mapElems(elems []ir.Node) {
// all elements have a key
for i := range elems {
kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
kv.Value = r.expr()
elems[i] = kv
}
}
func (r *reader) structElems(typ *types.Type, valuesOnly bool, elems []ir.Node) {
if valuesOnly {
for i := range elems {
sk := ir.NewStructKeyExpr(r.pos(), typ.Field(i), nil)
sk.Value = r.expr()
elems[i] = sk
}
return
}
// all elements have a key
for i := range elems {
pos := r.pos()
var fld *types.Field
if n := r.Int(); n < 0 {
// embedded field
for range -n {
fld = typ.Field(r.Int())
typ = fld.Type
}
} else { // n >= 0
fld = typ.Field(n)
}
sk := ir.NewStructKeyExpr(pos, fld, nil)
sk.Value = r.expr()
elems[i] = sk
}
}
func (r *reader) funcLit() ir.Node {
r.Sync(pkgbits.SyncFuncLit)

View File

@@ -2344,6 +2344,25 @@ func (w *writer) compLit(lit *syntax.CompositeLit) {
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
typ = ptr.Elem()
}
if w.Version().Has(pkgbits.CompactCompLiterals) {
switch typ0 := typ; typ := types2.CoreType(typ).(type) {
default:
w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
case *types2.Array:
w.arrayElems(typ.Elem(), lit.ElemList)
case *types2.Map:
w.rtype(typ0)
w.mapElems(typ.Key(), typ.Elem(), lit.ElemList)
case *types2.Slice:
w.arrayElems(typ.Elem(), lit.ElemList)
case *types2.Struct:
w.structElems(typ, lit.NKeys == 0, lit.ElemList)
}
return
}
// old format
var keyType, elemType types2.Type
var structType *types2.Struct
switch typ0 := typ; typ := types2.CoreType(typ).(type) {
@@ -2386,6 +2405,76 @@ func (w *writer) compLit(lit *syntax.CompositeLit) {
}
}
func (w *writer) arrayElems(elemType types2.Type, elems []syntax.Expr) {
valuesOnly := true
for _, elem := range elems {
if _, ok := elem.(*syntax.KeyValueExpr); ok {
valuesOnly = false
break
}
}
if valuesOnly {
w.Int(len(elems))
for _, elem := range elems {
w.implicitConvExpr(elemType, elem)
}
return
}
// some elements may have a key
w.Int(-len(elems))
for _, elem := range elems {
if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
w.implicitConvExpr(nil, kv.Key)
elem = kv.Value
}
w.implicitConvExpr(elemType, elem)
}
}
func (w *writer) mapElems(keyType, valueType types2.Type, elems []syntax.Expr) {
// all elements have a key
w.Int(-len(elems))
for _, elem := range elems {
kv := elem.(*syntax.KeyValueExpr)
w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
w.implicitConvExpr(keyType, kv.Key)
w.implicitConvExpr(valueType, kv.Value)
}
}
func (w *writer) structElems(typ *types2.Struct, valuesOnly bool, elems []syntax.Expr) {
n := len(elems)
if valuesOnly {
// no element has a key
w.Int(n)
for i, elem := range elems {
w.pos(elem)
w.implicitConvExpr(typ.Field(i).Type(), elem)
}
return
}
// all elements have a key
w.Int(-n)
for _, elem := range elems {
kv := elem.(*syntax.KeyValueExpr)
w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
// TODO(gri): rather than doing this lookup again, perhaps the index should be recorded by types2
fld, index, _ := types2.LookupFieldOrMethod(typ, false, w.p.curpkg, kv.Key.(*syntax.Name).Value)
if n := len(index); n > 1 {
// embedded field
w.Int(-n)
for _, i := range index {
w.Int(i)
}
} else { // n == 1
w.Int(index[0])
}
w.implicitConvExpr(fld.Type(), kv.Value)
}
}
func (w *writer) funcLit(expr *syntax.FuncLit) {
sig := w.p.typeOf(expr).(*types2.Signature)

View File

@@ -28,6 +28,12 @@ const (
// - remove derived info "needed" bool
V2
// V3: introduces a more compact format for composite literal element lists
// - negative lengths indicate that (some) elements may have keys
// - positive lengths indicate that no element has a key
// - a negative struct field index indicates an embedded field
V3
numVersions = iota
)
@@ -61,6 +67,9 @@ const (
// whether a type was a derived type.
DerivedInfoNeeded
// Composite literals use a more compact format for element lists.
CompactCompLiterals
numFields = iota
)
@@ -68,6 +77,7 @@ const (
var introduced = [numFields]Version{
Flags: V1,
AliasTypeParamNames: V2,
CompactCompLiterals: V3,
}
// removed is the version a field was removed in or 0 for fields