]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: set alignment for all content-addressable symbols
authorIan Lance Taylor <iant@golang.org>
Mon, 8 Dec 2025 05:55:24 +0000 (21:55 -0800)
committerGopher Robot <gobot@golang.org>
Thu, 12 Feb 2026 17:27:34 +0000 (09:27 -0800)
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 <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/liveness/arg.go
src/cmd/compile/internal/liveness/plive.go
src/cmd/compile/internal/reflectdata/reflect.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/staticdata/data.go
src/cmd/compile/internal/staticinit/sched.go
src/cmd/internal/obj/arm64/asm7.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/pcln.go
src/cmd/internal/obj/sym.go
src/cmd/internal/obj/x86/seh.go

index 77960f5e15a1f9314f51c638379f84ed5059834e..33e7f1856f1fa766f930a86720f7042b34dccaaf 100644 (file)
@@ -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 {
index 4659d1046070e48752afb0c1931fb8558459ddb9..88e46af5473b9321c6c855b8e22f72bf8c2c0326 100644 (file)
@@ -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)))
index ea3d0b5b9482c3596c84332d310c6ca69b2f80b2..2b3515453a1ceed96ed1e284c2e008929cb68649 100644 (file)
@@ -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
 }
index 17feb90df7de1bb25cb94af1c6a0ca77d5cd02f3..05224601e3ab70696fcb7a8ab9bceb4640eed408 100644 (file)
@@ -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
 
index acafe9d3393e85a8bb36d6726ea42dbb5f5f9dae..51d576e7f2c86ec5b9b74867bb79449b696297ce 100644 (file)
@@ -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
 }
index d7ad8ee1c8ccf0092caba9a356503bcef94dd884..5e04d0df0451d4bc66d43f5bfe1c86e834692ed8 100644 (file)
@@ -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
 }
 
index 7addf0ddadf28a6ae35d2635366ad4b36d5cc224..86e4669ac5053a2e2a41eb886a7b4b4931007d4e 100644 (file)
@@ -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")
index 53691ccb6c6421c11696bc9ebe90806257c60671..ea1483de014c153d9916df977acf4c473611dd4e 100644 (file)
@@ -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)
index 1cfcde7aa5b60df36e12b04bdbb1cccac42f7438..852c7069fdc92c84da7490980e5abb2ae180c0fd 100644 (file)
@@ -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)))
index acd9d9cf6039ff2c2517b21d14ca4cca85d351f0..08508ed438ba2254e59901920f443f68e4998be8 100644 (file)
@@ -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
        })
 }
 
index ceca94936367a2bb25a090dde37c8567b12824fc..19ff0a4c8a79764f136c42eb7759ecd15916d79c 100644 (file)
@@ -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)