Repository navigation
cmd/compile: composite literal fields built by inlined constructors are not initialized in place #81649
Description
Go version
go version go1.28-devel_ffc306b417 2026-09-21 16:13:07 -0700 linux/amd64
Output of go env in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v4'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN='/home/gopher/go/bin'
GODEBUG=''
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2170641377=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/gopher/repro/go.mod'
GOOS='linux'
GOPACKAGESDRIVER=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/gopher/tip'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/home/gopher/.config/go/telemetry'
GOTOOLCHAIN='local'
GOTOOLDIR='/home/gopher/tip/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.28-devel_ffc306b417 2026-09-21 16:13:07 -0700'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
A code generator in CUE emits a large table of struct values, writing each field as a call to a one-line constructor such as func cstr(v string) sval { return sval{kind: concrete, value: v} }. Rewriting every one of those calls as the struct literal it returns — a no-op at the source level, since the constructors are trivially inlinable — took that function from 123KiB of machine code down to 52KiB, and 136KiB off the cue binary.
package main
type kind uint8
const (
unset kind = iota
concrete
dflt
)
type sval struct {
kind kind
value string
domain []string
excluded []string
}
// One-line constructors, of the kind a generator emits.
func cstr(v string) sval { return sval{kind: concrete, value: v} }
func dstr(v string) sval { return sval{kind: dflt, value: v} }
type entry struct {
encoding sval
interpretation sval
form sval
}
// The two are marked noinline only so that they can be measured separately.
//go:noinline
func withCalls() *entry {
return &entry{encoding: cstr("enc"), interpretation: dstr("interp"), form: cstr("form")}
}
//go:noinline
func withLits() *entry {
return &entry{
encoding: sval{kind: concrete, value: "enc"},
interpretation: sval{kind: dflt, value: "interp"},
form: sval{kind: concrete, value: "form"},
}
}
var sink *entry
func main() {
sink = withCalls()
sink = withLits()
}
go build -o repro .
go tool nm -size repro | grep main.with
What did you see happen?
The two functions compute the same value, but the one that goes through the constructors is almost five times as much code:
47a180 533 T main.withCalls
47a3a0 112 T main.withLits
The constructors are inlined, but the inlined result is not used to initialize the destination field: each sval is zeroed and filled in a stack temporary, and the temporary is then copied into the heap object. withLits writes the fields straight into the new object.
The overhead is per field, so it scales with the size of the table, which is how one generated table came to be the largest function in the cue binary.
What did you expect to see?
withCalls and withLits to compile to the same code. A call whose body is a single return T{...} is inlined before walk, so by the time the composite literal is lowered the compiler has the field values in hand, and nothing about the semantics of the two spellings differs.
Constructor functions like these are idiomatic, and generated code leans on them heavily, so the cost is easy to pay without noticing.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
mvdan commentedon Sep 22, 2026
For further context, the commit which removed the func calls in CUE was cue-lang/cue@befe85b, which saved the ~135KiB in binary size.
gabyhelp commentedon Sep 22, 2026
Related Issues
Related Code Changes
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
prattmic commentedon Sep 29, 2026
cc @golang/compiler