Skip to content

Commit b915e14

Browse files
jakebaileygopherbot
authored andcommitted
cmd/compile: consolidate logic for rewriting fixed loads
Many CLs have worked with this bit of code, extending the cases more and more for various fixed addresses and constants. But, I find that it's getting duplicitive, and I don't find the current setup very clear that something like isFixed32 _only_ works for a specific element within the type data. This CL rewrites these rules (pun unintended) into a single set of rewrite rules with shared logic, which stops hardcoding offsets and type compatibility checks. This should open the door to optimizing further type:... field loads, of which most can be done entirely statically but are not yet today outside Hash and Elem. Passes toolstash -cmp. Change-Id: I754138ce1785c6036eada9ed53f0ce2ad2a58b63 Reviewed-on: https://go-review.googlesource.com/c/go/+/701297 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]> Auto-Submit: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Mark Freeman <[email protected]> Reviewed-by: Florian Lehner <[email protected]>
1 parent 06e791c commit b915e14

File tree

3 files changed

+138
-603
lines changed

3 files changed

+138
-603
lines changed

src/cmd/compile/internal/ssa/_gen/generic.rules

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2757,37 +2757,15 @@
27572757
(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 2 && d.Type.Size() == 2 => (RotateLeft(64|32|16|8) x (Add16 <c.Type> c d))
27582758
(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 1 && d.Type.Size() == 1 => (RotateLeft(64|32|16|8) x (Add8 <c.Type> c d))
27592759

2760-
// Loading constant values from dictionaries and itabs.
2761-
(Load <typ.BytePtr> (OffPtr [off] (Addr {s} sb) ) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2762-
(Load <typ.BytePtr> (OffPtr [off] (Convert (Addr {s} sb) _) ) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2763-
(Load <typ.BytePtr> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2764-
(Load <typ.BytePtr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2765-
(Load <typ.Uintptr> (OffPtr [off] (Addr {s} sb) ) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2766-
(Load <typ.Uintptr> (OffPtr [off] (Convert (Addr {s} sb) _) ) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2767-
(Load <typ.Uintptr> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2768-
(Load <typ.Uintptr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _) && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
2769-
2770-
// Loading constant values from dictionaries and itabs. For offset 0.
2771-
(Load <typ.BytePtr> (Addr {s} sb) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2772-
(Load <typ.BytePtr> (Convert (Addr {s} sb) _) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2773-
(Load <typ.BytePtr> (ITab (IMake (Addr {s} sb) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2774-
(Load <typ.BytePtr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2775-
(Load <typ.Uintptr> (Addr {s} sb) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2776-
(Load <typ.Uintptr> (Convert (Addr {s} sb) _) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2777-
(Load <typ.Uintptr> (ITab (IMake (Addr {s} sb) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2778-
(Load <typ.Uintptr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
2779-
2780-
// Loading constant values from abi.PtrType.Elem.
2781-
(Load <t> (OffPtr [off] (Addr {s} sb) ) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
2782-
(Load <t> (OffPtr [off] (Convert (Addr {s} sb) _) ) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
2783-
(Load <t> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
2784-
(Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
2785-
2786-
// Loading constant values from runtime._type.hash.
2787-
(Load <t> (OffPtr [off] (Addr {sym} _) ) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
2788-
(Load <t> (OffPtr [off] (Convert (Addr {sym} _) _) ) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
2789-
(Load <t> (OffPtr [off] (ITab (IMake (Addr {sym} _) _))) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
2790-
(Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {sym} _) _) _))) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
2760+
// Loading fixed addresses and constants.
2761+
(Load (Addr {s} sb) _) && isFixedLoad(v, s, 0) => rewriteFixedLoad(v, s, sb, 0)
2762+
(Load (Convert (Addr {s} sb) _) _) && isFixedLoad(v, s, 0) => rewriteFixedLoad(v, s, sb, 0)
2763+
(Load (ITab (IMake (Addr {s} sb) _)) _) && isFixedLoad(v, s, 0) => rewriteFixedLoad(v, s, sb, 0)
2764+
(Load (ITab (IMake (Convert (Addr {s} sb) _) _)) _) && isFixedLoad(v, s, 0) => rewriteFixedLoad(v, s, sb, 0)
2765+
(Load (OffPtr [off] (Addr {s} sb) ) _) && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
2766+
(Load (OffPtr [off] (Convert (Addr {s} sb) _) ) _) && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
2767+
(Load (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _) && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
2768+
(Load (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _) && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
27912769

27922770
// Calling cmpstring a second time with the same arguments in the
27932771
// same memory state can reuse the results of the first call.

src/cmd/compile/internal/ssa/rewrite.go

Lines changed: 85 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,108 +1982,111 @@ func symIsROZero(sym Sym) bool {
19821982
return true
19831983
}
19841984

1985-
// isFixed32 returns true if the int32 at offset off in symbol sym
1986-
// is known and constant.
1987-
func isFixed32(c *Config, sym Sym, off int64) bool {
1988-
return isFixed(c, sym, off, 4)
1989-
}
1990-
1991-
// isFixed returns true if the range [off,off+size] of the symbol sym
1992-
// is known and constant.
1993-
func isFixed(c *Config, sym Sym, off, size int64) bool {
1994-
lsym := sym.(*obj.LSym)
1995-
if lsym.Extra == nil {
1996-
return false
1997-
}
1998-
if _, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
1999-
if off == 2*c.PtrSize && size == 4 {
2000-
return true // type hash field
2001-
}
2002-
}
2003-
return false
2004-
}
2005-
func fixed32(c *Config, sym Sym, off int64) int32 {
1985+
// isFixedLoad returns true if the load can be resolved to fixed address or constant,
1986+
// and can be rewritten by rewriteFixedLoad.
1987+
func isFixedLoad(v *Value, sym Sym, off int64) bool {
20061988
lsym := sym.(*obj.LSym)
2007-
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
2008-
if off == 2*c.PtrSize {
2009-
return int32(types.TypeHash(ti.Type.(*types.Type)))
1989+
if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
1990+
for _, r := range lsym.R {
1991+
if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
1992+
return true
1993+
}
20101994
}
1995+
return false
20111996
}
2012-
base.Fatalf("fixed32 data not known for %s:%d", sym, off)
2013-
return 0
2014-
}
20151997

2016-
// isPtrElem returns true if sym is an instance of abi.PtrType and off
2017-
// is equal to the offset of its Elem field.
2018-
func isPtrElem(sym Sym, off int64) bool {
2019-
lsym := sym.(*obj.LSym)
2020-
if strings.HasPrefix(lsym.Name, "type:*") {
2021-
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
2022-
t := ti.Type.(*types.Type)
2023-
if t.Kind() == types.TPTR {
2024-
if off == rttype.PtrType.OffsetOf("Elem") {
1998+
if strings.HasPrefix(lsym.Name, "type:") {
1999+
// Type symbols do not contain information about their fields, unlike the cases above.
2000+
// Hand-implement field accesses.
2001+
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
2002+
2003+
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
2004+
2005+
for _, f := range rttype.Type.Fields() {
2006+
if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
2007+
switch f.Sym.Name {
2008+
case "Hash":
20252009
return true
2010+
default:
2011+
// fmt.Println("unknown field", f.Sym.Name)
2012+
return false
20262013
}
20272014
}
20282015
}
2016+
2017+
if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
2018+
return true
2019+
}
2020+
2021+
return false
20292022
}
2023+
20302024
return false
20312025
}
2032-
func ptrElem(f *Func, sym Sym, off int64) Sym {
2026+
2027+
// rewriteFixedLoad rewrites a load to a fixed address or constant, if isFixedLoad returns true.
2028+
func rewriteFixedLoad(v *Value, sym Sym, sb *Value, off int64) *Value {
2029+
b := v.Block
2030+
f := b.Func
2031+
20332032
lsym := sym.(*obj.LSym)
2034-
if strings.HasPrefix(lsym.Name, "type:*") {
2035-
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
2036-
t := ti.Type.(*types.Type)
2037-
if t.Kind() == types.TPTR {
2038-
if off == rttype.PtrType.OffsetOf("Elem") {
2039-
elemSym := reflectdata.TypeLinksym(t.Elem())
2040-
reflectdata.MarkTypeSymUsedInInterface(elemSym, f.fe.Func().Linksym())
2041-
return elemSym
2033+
if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
2034+
for _, r := range lsym.R {
2035+
if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
2036+
if strings.HasPrefix(r.Sym.Name, "type:") {
2037+
// In case we're loading a type out of a dictionary, we need to record
2038+
// that the containing function might put that type in an interface.
2039+
// That information is currently recorded in relocations in the dictionary,
2040+
// but if we perform this load at compile time then the dictionary
2041+
// might be dead.
2042+
reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
2043+
} else if strings.HasPrefix(r.Sym.Name, "go:itab") {
2044+
// Same, but if we're using an itab we need to record that the
2045+
// itab._type might be put in an interface.
2046+
reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
20422047
}
2048+
v.reset(OpAddr)
2049+
v.Aux = symToAux(r.Sym)
2050+
v.AddArg(sb)
2051+
return v
20432052
}
20442053
}
2054+
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
20452055
}
2046-
base.Fatalf("ptrElem data not known for %s:%d", sym, off)
2047-
return nil
2048-
}
20492056

2050-
// isFixedSym returns true if the contents of sym at the given offset
2051-
// is known and is the constant address of another symbol.
2052-
func isFixedSym(sym Sym, off int64) bool {
2053-
lsym := sym.(*obj.LSym)
2054-
switch {
2055-
case lsym.Type == objabi.SRODATA:
2056-
// itabs, dictionaries
2057-
default:
2058-
return false
2059-
}
2060-
for _, r := range lsym.R {
2061-
if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
2062-
return true
2063-
}
2064-
}
2065-
return false
2066-
}
2067-
func fixedSym(f *Func, sym Sym, off int64) Sym {
2068-
lsym := sym.(*obj.LSym)
2069-
for _, r := range lsym.R {
2070-
if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off {
2071-
if strings.HasPrefix(r.Sym.Name, "type:") {
2072-
// In case we're loading a type out of a dictionary, we need to record
2073-
// that the containing function might put that type in an interface.
2074-
// That information is currently recorded in relocations in the dictionary,
2075-
// but if we perform this load at compile time then the dictionary
2076-
// might be dead.
2077-
reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
2078-
} else if strings.HasPrefix(r.Sym.Name, "go:itab") {
2079-
// Same, but if we're using an itab we need to record that the
2080-
// itab._type might be put in an interface.
2081-
reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
2057+
if strings.HasPrefix(lsym.Name, "type:") {
2058+
// Type symbols do not contain information about their fields, unlike the cases above.
2059+
// Hand-implement field accesses.
2060+
// TODO: can this be replaced with reflectdata.writeType and just use the code above?
2061+
2062+
t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
2063+
2064+
for _, f := range rttype.Type.Fields() {
2065+
if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
2066+
switch f.Sym.Name {
2067+
case "Hash":
2068+
v.reset(OpConst32)
2069+
v.AuxInt = int64(types.TypeHash(t))
2070+
return v
2071+
default:
2072+
base.Fatalf("unknown field %s for fixedLoad of %s at offset %d", f.Sym.Name, lsym.Name, off)
2073+
}
20822074
}
2083-
return r.Sym
20842075
}
2076+
2077+
if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
2078+
elemSym := reflectdata.TypeLinksym(t.Elem())
2079+
reflectdata.MarkTypeSymUsedInInterface(elemSym, f.fe.Func().Linksym())
2080+
v.reset(OpAddr)
2081+
v.Aux = symToAux(elemSym)
2082+
v.AddArg(sb)
2083+
return v
2084+
}
2085+
2086+
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
20852087
}
2086-
base.Fatalf("fixedSym data not known for %s:%d", sym, off)
2088+
2089+
base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
20872090
return nil
20882091
}
20892092

0 commit comments

Comments
 (0)