From: Ian Lance Taylor Date: Mon, 8 Dec 2025 05:55:24 +0000 (-0800) Subject: cmd/compile: set alignment for all content-addressable symbols X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=174c92bd7428b41b9b5b59274a83f387a12261c9;p=gostls13.git cmd/compile: set alignment for all content-addressable symbols There is nothing particularly special about content-addressable symbols, it's just a place to start. This reduces the size of the tailscaled binary by about 16K. This happens mainly because before this CL the linker's symalign function kicks in for all static composite literals and PCDATA symbols, and gives them an alignment based on their size. If the size happens to be a multiple of 32, it gets an alignment of 32. That wastes space. For #6853 For #36313 Change-Id: I2f049eee8f2463dd2b5e20d7c9a270ac32a31e50 Reviewed-on: https://go-review.googlesource.com/c/go/+/727920 Reviewed-by: Keith Randall Reviewed-by: Keith Randall Reviewed-by: Cherry Mui Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI --- diff --git a/src/cmd/compile/internal/liveness/arg.go b/src/cmd/compile/internal/liveness/arg.go index 77960f5e15..33e7f1856f 100644 --- a/src/cmd/compile/internal/liveness/arg.go +++ b/src/cmd/compile/internal/liveness/arg.go @@ -316,6 +316,7 @@ func (lv *argLiveness) emit() *obj.LSym { lsym := base.Ctxt.Lookup(lv.fn.LSym.Name + ".argliveinfo") lsym.Set(obj.AttrContentAddressable, true) + lsym.Align = 1 off := objw.Uint8(lsym, 0, argOffsets[0]) // smallest offset that needs liveness info. for idx, live := range livenessMaps { diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go index 4659d10460..88e46af547 100644 --- a/src/cmd/compile/internal/liveness/plive.go +++ b/src/cmd/compile/internal/liveness/plive.go @@ -1473,6 +1473,7 @@ func (lv *Liveness) emitStackObjects() *obj.LSym { // Format must match runtime/stack.go:stackObjectRecord. x := base.Ctxt.Lookup(lv.fn.LSym.Name + ".stkobj") x.Set(obj.AttrContentAddressable, true) + x.Align = 4 lv.fn.LSym.Func().StackObjects = x off := 0 off = objw.Uintptr(x, off, uint64(len(vars))) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index ea3d0b5b94..2b3515453a 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -184,6 +184,7 @@ func dimportpath(p *types.Pkg) { ot := dnameData(s, 0, p.Path, "", nil, false, false) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) + s.Align = 1 p.Pathsym = s } @@ -308,6 +309,7 @@ func dname(name, tag string, pkg *types.Pkg, exported, embedded bool) *obj.LSym ot := dnameData(s, 0, name, tag, pkg, exported, embedded) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) + s.Align = 1 return s } @@ -1083,6 +1085,7 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) { // Nothing writes static itabs, so they are read only. objw.Global(lsym, int32(rttype.ITab.Size()+delta), int16(obj.DUPOK|obj.RODATA)) lsym.Set(obj.AttrContentAddressable, true) + lsym.Align = int16(types.PtrSize) } func WritePluginTable() { @@ -1278,6 +1281,9 @@ func dgcptrmask(t *types.Type, write bool) *obj.LSym { } objw.Global(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL) lsym.Set(obj.AttrContentAddressable, true) + // The runtime expects ptrmasks to be aligned + // as a uintptr. + lsym.Align = int16(types.PtrSize) } return lsym } diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 17feb90df7..05224601e3 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -283,6 +283,7 @@ func (s *state) emitOpenDeferInfo() { x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer") x.Set(obj.AttrContentAddressable, true) + x.Align = 1 s.curfn.LSym.Func().OpenCodedDeferInfo = x off := 0 @@ -6781,6 +6782,7 @@ func emitArgInfo(e *ssafn, f *ssa.Func, pp *objw.Progs) { // emit argument info (locations on stack) of f for traceback. func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym { x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI)) + x.Align = 1 // NOTE: do not set ContentAddressable here. This may be referenced from // assembly code by name (in this case f is a declaration). // Instead, set it in emitArgInfo above. @@ -6900,6 +6902,7 @@ func emitWrappedFuncInfo(e *ssafn, pp *objw.Progs) { x := base.Ctxt.LookupInit(fmt.Sprintf("%s.wrapinfo", wsym.Name), func(x *obj.LSym) { objw.SymPtrOff(x, 0, wsym) x.Set(obj.AttrContentAddressable, true) + x.Align = 4 }) e.curfn.LSym.Func().WrapInfo = x diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go index acafe9d339..51d576e7f2 100644 --- a/src/cmd/compile/internal/staticdata/data.go +++ b/src/cmd/compile/internal/staticdata/data.go @@ -92,6 +92,7 @@ func StringSym(pos src.XPos, s string) (data *obj.LSym) { off := dstringdata(symdata, 0, s, pos, "string") objw.Global(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) symdata.Set(obj.AttrContentAddressable, true) + symdata.Align = 1 } return symdata @@ -185,6 +186,7 @@ func fileStringSym(pos src.XPos, file string, readonly bool, hashBytes []byte) ( info.Name = file info.Size = size objw.Global(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL) + symdata.Align = 1 // Note: AttrContentAddressable cannot be set here, // because the content-addressable-handling code // does not know about file symbols. @@ -211,6 +213,7 @@ func slicedata(pos src.XPos, s string) *obj.LSym { lsym := types.LocalPkg.Lookup(symname).LinksymABI(obj.ABI0) off := dstringdata(lsym, 0, s, pos, "slice") objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL) + lsym.Align = 1 return lsym } diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go index d7ad8ee1c8..5e04d0df04 100644 --- a/src/cmd/compile/internal/staticinit/sched.go +++ b/src/cmd/compile/internal/staticinit/sched.go @@ -764,6 +764,8 @@ func StaticName(t *types.Type) *ir.Name { typecheck.Target.Externs = append(typecheck.Target.Externs, n) n.Linksym().Set(obj.AttrStatic, true) + n.Linksym().Align = int16(t.Alignment()) + return n } diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 7addf0ddad..86e4669ac5 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -40,7 +40,6 @@ import ( "math" "math/bits" "slices" - "strings" ) // ctxt7 holds state while assembling a single function. @@ -1071,44 +1070,14 @@ func (o *Optab) size(ctxt *obj.Link, p *obj.Prog) int { // to decide whether to use the unaligned/aligned forms, so o.size's result is always // in sync with the code generation decisions, because it *is* the code generation decision. align := int64(1 << sz) - if o.a1 == C_ADDR && p.From.Offset%align == 0 && symAlign(p.From.Sym) >= align || - o.a4 == C_ADDR && p.To.Offset%align == 0 && symAlign(p.To.Sym) >= align { + if o.a1 == C_ADDR && p.From.Offset%align == 0 && int64(p.From.Sym.Align) >= align || + o.a4 == C_ADDR && p.To.Offset%align == 0 && int64(p.To.Sym.Align) >= align { return 8 } } return int(o.size_) } -// symAlign returns the expected symbol alignment of the symbol s. -// This must match the linker's own default alignment decisions. -func symAlign(s *obj.LSym) int64 { - name := s.Name - switch { - case strings.HasPrefix(name, "go:string."), - strings.HasPrefix(name, "type:.namedata."), - strings.HasPrefix(name, "type:.importpath."), - strings.HasSuffix(name, ".opendefer"), - strings.HasSuffix(name, ".arginfo0"), - strings.HasSuffix(name, ".arginfo1"), - strings.HasSuffix(name, ".argliveinfo"): - // These are just bytes, or varints. - return 1 - case strings.HasPrefix(name, "gclocals·"): - // It has 32-bit fields. - return 4 - default: - switch { - case s.Size%8 == 0: - return 8 - case s.Size%4 == 0: - return 4 - case s.Size%2 == 0: - return 2 - } - } - return 1 -} - func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if ctxt.Retpoline { ctxt.Diag("-spectre=ret not supported on arm64") diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 53691ccb6c..ea1483de01 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -371,38 +371,8 @@ func (w *writer) Sym(s *LSym) { } align := uint32(s.Align) if s.ContentAddressable() && s.Size != 0 && align == 0 { - // We generally assume data symbols are naturally aligned - // (e.g. integer constants), except for strings and a few - // compiler-emitted funcdata. If we dedup a string symbol and - // a non-string symbol with the same content, we should keep - // the largest alignment. - // TODO: maybe the compiler could set the alignment for all - // data symbols more carefully. - switch { - case strings.HasPrefix(s.Name, "go:string."), - strings.HasPrefix(name, "type:.namedata."), - strings.HasPrefix(name, "type:.importpath."), - strings.HasSuffix(name, ".opendefer"), - strings.HasSuffix(name, ".arginfo0"), - strings.HasSuffix(name, ".arginfo1"), - strings.HasSuffix(name, ".argliveinfo"): - // These are just bytes, or varints. - align = 1 - case strings.HasPrefix(name, "gclocals·"): - // It has 32-bit fields. - align = 4 - default: - switch { - case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: - align = 8 - case s.Size%4 == 0: - align = 4 - case s.Size%2 == 0: - align = 2 - default: - align = 1 - } - } + // TODO: Check that alignment is set for all symbols. + w.ctxt.Diag("%s: is content-addressable but alignment is not set (size is %d)", s.Name, s.Size) } if s.Size > cutoff { w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index 1cfcde7aa5..852c7069fd 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -28,6 +28,7 @@ func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, sym := &LSym{ Type: objabi.SRODATA, Attribute: AttrContentAddressable | AttrPcdata, + Align: 1, } if dbg { @@ -335,6 +336,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { pcln.Pcdata[i] = &LSym{ Type: objabi.SRODATA, Attribute: AttrContentAddressable | AttrPcdata, + Align: 1, } } else { pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, any(uint32(i))) diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index acd9d9cf60..08508ed438 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -166,6 +166,7 @@ func (ctxt *Link) Float32Sym(f float32) *LSym { name := fmt.Sprintf("$f32.%08x%s", i, suffix) return ctxt.LookupInit(name, func(s *LSym) { s.Size = 4 + s.Align = 4 s.WriteFloat32(ctxt, 0, f) s.Type = typ s.Set(AttrLocal, true) @@ -180,6 +181,7 @@ func (ctxt *Link) Float64Sym(f float64) *LSym { name := fmt.Sprintf("$f64.%016x%s", i, suffix) return ctxt.LookupInit(name, func(s *LSym) { s.Size = 8 + s.Align = int16(ctxt.Arch.PtrSize) s.WriteFloat64(ctxt, 0, f) s.Type = typ s.Set(AttrLocal, true) @@ -193,6 +195,7 @@ func (ctxt *Link) Int32Sym(i int64) *LSym { name := fmt.Sprintf("$i32.%08x%s", uint64(i), suffix) return ctxt.LookupInit(name, func(s *LSym) { s.Size = 4 + s.Align = 4 s.WriteInt(ctxt, 0, 4, i) s.Type = typ s.Set(AttrLocal, true) @@ -206,6 +209,7 @@ func (ctxt *Link) Int64Sym(i int64) *LSym { name := fmt.Sprintf("$i64.%016x%s", uint64(i), suffix) return ctxt.LookupInit(name, func(s *LSym) { s.Size = 8 + s.Align = int16(ctxt.Arch.PtrSize) s.WriteInt(ctxt, 0, 8, i) s.Type = typ s.Set(AttrLocal, true) @@ -219,6 +223,7 @@ func (ctxt *Link) Int128Sym(hi, lo int64) *LSym { name := fmt.Sprintf("$i128.%016x%016x%s", uint64(hi), uint64(lo), suffix) return ctxt.LookupInit(name, func(s *LSym) { s.Size = 16 + s.Align = int16(ctxt.Arch.PtrSize) if ctxt.Arch.ByteOrder == binary.LittleEndian { s.WriteInt(ctxt, 0, 8, lo) s.WriteInt(ctxt, 8, 8, hi) @@ -240,6 +245,7 @@ func (ctxt *Link) GCLocalsSym(data []byte) *LSym { return ctxt.LookupInit(fmt.Sprintf("gclocals·%s", str), func(lsym *LSym) { lsym.P = data lsym.Set(AttrContentAddressable, true) + lsym.Align = 4 }) } diff --git a/src/cmd/internal/obj/x86/seh.go b/src/cmd/internal/obj/x86/seh.go index ceca949363..19ff0a4c8a 100644 --- a/src/cmd/internal/obj/x86/seh.go +++ b/src/cmd/internal/obj/x86/seh.go @@ -148,6 +148,7 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) { symname := fmt.Sprintf("%d.%s", len(buf.data), hash) return ctxt.LookupInit("go:sehuw."+symname, func(s *obj.LSym) { s.WriteBytes(ctxt, 0, buf.data) + s.Align = 4 s.Type = objabi.SSEHUNWINDINFO s.Set(obj.AttrDuplicateOK, true) s.Set(obj.AttrLocal, true)