Skip to content

Commit

Permalink
go/ir: don't use CompositeValue for arrays with missing values
Browse files Browse the repository at this point in the history
Don't blow up memory usage trying to create a CompositeValue for
[]int{1<<31: 1}.

Updates: gh-1393
  • Loading branch information
dominikh committed Apr 8, 2023
1 parent a897bf7 commit 90a075d
Showing 1 changed file with 66 additions and 33 deletions.
99 changes: 66 additions & 33 deletions go/ir/builder.go
Expand Up @@ -1145,16 +1145,13 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
// x = T{a: x.a}
//
// all the reads must occur before all the writes. This is implicitly handled by the write buffering effected by
// compositeElement.
// compositeElement and explicitly by the storebuf for when we don't use CompositeValue.
//
// A CompositeLit may have pointer type only in the recursive (nested)
// case when the type name is implicit. e.g. in []*T{{}}, the inner
// literal has type *T behaves like &T{}.
// In that case, addr must hold a T, not a *T.
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
// Even though we no longer need storebuf for nested composite literals (because compositeElements act as buffers
// themselves), we still need storebuf for things like multiple assignment, e.g. 't.F1, t.F2 = T2{1}, T2{t.F1.X}'

typ := deref(fn.Pkg.typeOf(e))
switch t := typeutil.CoreType(typ).(type) {
case *types.Struct:
Expand Down Expand Up @@ -1220,41 +1217,77 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
final = zc
}
} else {
v := &CompositeValue{
Values: make([]Value, at.Len()),
}
zc := emitConst(fn, zeroConst(at.Elem()))
for i := range v.Values {
v.Values[i] = zc
}
v.setType(at)
if at.Len() == int64(len(e.Elts)) {
// The literal specifies all elements, so we can use a composite value
v := &CompositeValue{
Values: make([]Value, at.Len()),
}
zc := emitConst(fn, zeroConst(at.Elem()))
for i := range v.Values {
v.Values[i] = zc
}
v.setType(at)

var idx *Const
for _, e := range e.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
idx = b.expr(fn, kv.Key).(*Const)
e = kv.Value
} else {
var idxval int64
if idx != nil {
idxval = idx.Int64() + 1
var idx *Const
for _, e := range e.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
idx = b.expr(fn, kv.Key).(*Const)
e = kv.Value
} else {
var idxval int64
if idx != nil {
idxval = idx.Int64() + 1
}
idx = emitConst(fn, intConst(idxval)).(*Const)
}
idx = emitConst(fn, intConst(idxval)).(*Const)
}

iaddr := &compositeElement{
cv: v,
idx: int(idx.Int64()),
t: at.Elem(),
expr: e,
iaddr := &compositeElement{
cv: v,
idx: int(idx.Int64()),
t: at.Elem(),
expr: e,
}

b.assign(fn, iaddr, e, true, sb, e)
v.Bitmap.SetBit(&v.Bitmap, int(idx.Int64()), 1)
v.NumSet++
}
final = v
fn.emit(v, e)
} else {
// Not all elements are specified. Populate the array with a series of stores, to guard against literals
// like []int{1<<62: 1}.
if !isZero {
// memclear
sb.store(&address{array, nil}, zeroValue(fn, deref(array.Type()), e), e)
}

b.assign(fn, iaddr, e, true, sb, e)
v.Bitmap.SetBit(&v.Bitmap, int(idx.Int64()), 1)
v.NumSet++
var idx *Const
for _, e := range e.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
idx = b.expr(fn, kv.Key).(*Const)
e = kv.Value
} else {
var idxval int64
if idx != nil {
idxval = idx.Int64() + 1
}
idx = emitConst(fn, intConst(idxval)).(*Const)
}
iaddr := &IndexAddr{
X: array,
Index: idx,
}
iaddr.setType(types.NewPointer(at.Elem()))
fn.emit(iaddr, e)
if t != at { // slice
// backing array is unaliased => storebuf not needed.
b.assign(fn, &address{addr: iaddr, expr: e}, e, true, nil, e)
} else {
b.assign(fn, &address{addr: iaddr, expr: e}, e, true, sb, e)
}
}
}
final = v
fn.emit(v, e)
}
if t != at { // slice
if final != nil {
Expand Down

0 comments on commit 90a075d

Please sign in to comment.