]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: consolidate logic for rewriting fixed loads
authorJake Bailey <jacob.b.bailey@gmail.com>
Fri, 5 Sep 2025 20:08:21 +0000 (13:08 -0700)
committerGopher Robot <gobot@golang.org>
Mon, 8 Sep 2025 20:34:03 +0000 (13:34 -0700)
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 <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Florian Lehner <lehner.florian86@gmail.com>
src/cmd/compile/internal/ssa/_gen/generic.rules
src/cmd/compile/internal/ssa/rewrite.go
src/cmd/compile/internal/ssa/rewritegeneric.go

index 6255045c6f50d5e609bc928c8ad65b27d063dad7..c0806c2243486bbf4e6355eea4709535e6067fce 100644 (file)
 (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))
 (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))
 
-// Loading constant values from dictionaries and itabs.
-(Load <typ.BytePtr> (OffPtr [off]                       (Addr {s} sb)       ) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.BytePtr> (OffPtr [off]              (Convert (Addr {s} sb) _)    ) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.BytePtr> (OffPtr [off] (ITab (IMake          (Addr {s} sb)    _))) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.BytePtr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.Uintptr> (OffPtr [off]                       (Addr {s} sb)       ) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.Uintptr> (OffPtr [off]              (Convert (Addr {s} sb) _)    ) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.Uintptr> (OffPtr [off] (ITab (IMake          (Addr {s} sb)    _))) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-(Load <typ.Uintptr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)  && isFixedSym(s, off) => (Addr {fixedSym(b.Func, s, off)} sb)
-
-// Loading constant values from dictionaries and itabs. For offset 0.
-(Load <typ.BytePtr>                       (Addr {s} sb)        _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.BytePtr>              (Convert (Addr {s} sb) _)     _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.BytePtr> (ITab (IMake          (Addr {s} sb)    _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.BytePtr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.Uintptr>                       (Addr {s} sb)        _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.Uintptr>              (Convert (Addr {s} sb) _)     _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.Uintptr> (ITab (IMake          (Addr {s} sb)    _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-(Load <typ.Uintptr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _) && isFixedSym(s, 0) => (Addr {fixedSym(b.Func, s, 0)} sb)
-
-// Loading constant values from abi.PtrType.Elem.
-(Load <t> (OffPtr [off]                       (Addr {s} sb)       ) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
-(Load <t> (OffPtr [off]              (Convert (Addr {s} sb) _)    ) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
-(Load <t> (OffPtr [off] (ITab (IMake          (Addr {s} sb)    _))) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
-(Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _) && t.IsPtr() && isPtrElem(s, off) => (Addr {ptrElem(b.Func, s, off)} sb)
-
-// Loading constant values from runtime._type.hash.
-(Load <t> (OffPtr [off]                       (Addr {sym} _)       ) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
-(Load <t> (OffPtr [off]              (Convert (Addr {sym} _) _)    ) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
-(Load <t> (OffPtr [off] (ITab (IMake          (Addr {sym} _)    _))) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
-(Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {sym} _) _) _))) _) && t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off) => (Const32 [fixed32(config, sym, off)])
+// Loading fixed addresses and constants.
+(Load                                     (Addr {s} sb)         _)  && isFixedLoad(v, s,   0) => rewriteFixedLoad(v, s, sb,   0)
+(Load                            (Convert (Addr {s} sb) _)      _)  && isFixedLoad(v, s,   0) => rewriteFixedLoad(v, s, sb,   0)
+(Load               (ITab (IMake          (Addr {s} sb)    _))  _)  && isFixedLoad(v, s,   0) => rewriteFixedLoad(v, s, sb,   0)
+(Load               (ITab (IMake (Convert (Addr {s} sb) _) _))  _)  && isFixedLoad(v, s,   0) => rewriteFixedLoad(v, s, sb,   0)
+(Load (OffPtr [off]                       (Addr {s} sb)       ) _)  && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
+(Load (OffPtr [off]              (Convert (Addr {s} sb) _)    ) _)  && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
+(Load (OffPtr [off] (ITab (IMake          (Addr {s} sb)    _))) _)  && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
+(Load (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)  && isFixedLoad(v, s, off) => rewriteFixedLoad(v, s, sb, off)
 
 // Calling cmpstring a second time with the same arguments in the
 // same memory state can reuse the results of the first call.
index a4242c2141d8f9cb8f5a02d8f4906c7c34cb1ba5..bc66c91a35db8635a5f798fc23a3da9f8ea243d0 100644 (file)
@@ -1982,108 +1982,111 @@ func symIsROZero(sym Sym) bool {
        return true
 }
 
-// isFixed32 returns true if the int32 at offset off in symbol sym
-// is known and constant.
-func isFixed32(c *Config, sym Sym, off int64) bool {
-       return isFixed(c, sym, off, 4)
-}
-
-// isFixed returns true if the range [off,off+size] of the symbol sym
-// is known and constant.
-func isFixed(c *Config, sym Sym, off, size int64) bool {
-       lsym := sym.(*obj.LSym)
-       if lsym.Extra == nil {
-               return false
-       }
-       if _, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
-               if off == 2*c.PtrSize && size == 4 {
-                       return true // type hash field
-               }
-       }
-       return false
-}
-func fixed32(c *Config, sym Sym, off int64) int32 {
+// isFixedLoad returns true if the load can be resolved to fixed address or constant,
+// and can be rewritten by rewriteFixedLoad.
+func isFixedLoad(v *Value, sym Sym, off int64) bool {
        lsym := sym.(*obj.LSym)
-       if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
-               if off == 2*c.PtrSize {
-                       return int32(types.TypeHash(ti.Type.(*types.Type)))
+       if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
+               for _, r := range lsym.R {
+                       if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
+                               return true
+                       }
                }
+               return false
        }
-       base.Fatalf("fixed32 data not known for %s:%d", sym, off)
-       return 0
-}
 
-// isPtrElem returns true if sym is an instance of abi.PtrType and off
-// is equal to the offset of its Elem field.
-func isPtrElem(sym Sym, off int64) bool {
-       lsym := sym.(*obj.LSym)
-       if strings.HasPrefix(lsym.Name, "type:*") {
-               if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
-                       t := ti.Type.(*types.Type)
-                       if t.Kind() == types.TPTR {
-                               if off == rttype.PtrType.OffsetOf("Elem") {
+       if strings.HasPrefix(lsym.Name, "type:") {
+               // Type symbols do not contain information about their fields, unlike the cases above.
+               // Hand-implement field accesses.
+               // TODO: can this be replaced with reflectdata.writeType and just use the code above?
+
+               t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
+
+               for _, f := range rttype.Type.Fields() {
+                       if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
+                               switch f.Sym.Name {
+                               case "Hash":
                                        return true
+                               default:
+                                       // fmt.Println("unknown field", f.Sym.Name)
+                                       return false
                                }
                        }
                }
+
+               if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
+                       return true
+               }
+
+               return false
        }
+
        return false
 }
-func ptrElem(f *Func, sym Sym, off int64) Sym {
+
+// rewriteFixedLoad rewrites a load to a fixed address or constant, if isFixedLoad returns true.
+func rewriteFixedLoad(v *Value, sym Sym, sb *Value, off int64) *Value {
+       b := v.Block
+       f := b.Func
+
        lsym := sym.(*obj.LSym)
-       if strings.HasPrefix(lsym.Name, "type:*") {
-               if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
-                       t := ti.Type.(*types.Type)
-                       if t.Kind() == types.TPTR {
-                               if off == rttype.PtrType.OffsetOf("Elem") {
-                                       elemSym := reflectdata.TypeLinksym(t.Elem())
-                                       reflectdata.MarkTypeSymUsedInInterface(elemSym, f.fe.Func().Linksym())
-                                       return elemSym
+       if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
+               for _, r := range lsym.R {
+                       if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
+                               if strings.HasPrefix(r.Sym.Name, "type:") {
+                                       // In case we're loading a type out of a dictionary, we need to record
+                                       // that the containing function might put that type in an interface.
+                                       // That information is currently recorded in relocations in the dictionary,
+                                       // but if we perform this load at compile time then the dictionary
+                                       // might be dead.
+                                       reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
+                               } else if strings.HasPrefix(r.Sym.Name, "go:itab") {
+                                       // Same, but if we're using an itab we need to record that the
+                                       // itab._type might be put in an interface.
+                                       reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
                                }
+                               v.reset(OpAddr)
+                               v.Aux = symToAux(r.Sym)
+                               v.AddArg(sb)
+                               return v
                        }
                }
+               base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
        }
-       base.Fatalf("ptrElem data not known for %s:%d", sym, off)
-       return nil
-}
 
-// isFixedSym returns true if the contents of sym at the given offset
-// is known and is the constant address of another symbol.
-func isFixedSym(sym Sym, off int64) bool {
-       lsym := sym.(*obj.LSym)
-       switch {
-       case lsym.Type == objabi.SRODATA:
-               // itabs, dictionaries
-       default:
-               return false
-       }
-       for _, r := range lsym.R {
-               if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
-                       return true
-               }
-       }
-       return false
-}
-func fixedSym(f *Func, sym Sym, off int64) Sym {
-       lsym := sym.(*obj.LSym)
-       for _, r := range lsym.R {
-               if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off {
-                       if strings.HasPrefix(r.Sym.Name, "type:") {
-                               // In case we're loading a type out of a dictionary, we need to record
-                               // that the containing function might put that type in an interface.
-                               // That information is currently recorded in relocations in the dictionary,
-                               // but if we perform this load at compile time then the dictionary
-                               // might be dead.
-                               reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
-                       } else if strings.HasPrefix(r.Sym.Name, "go:itab") {
-                               // Same, but if we're using an itab we need to record that the
-                               // itab._type might be put in an interface.
-                               reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.fe.Func().Linksym())
+       if strings.HasPrefix(lsym.Name, "type:") {
+               // Type symbols do not contain information about their fields, unlike the cases above.
+               // Hand-implement field accesses.
+               // TODO: can this be replaced with reflectdata.writeType and just use the code above?
+
+               t := (*lsym.Extra).(*obj.TypeInfo).Type.(*types.Type)
+
+               for _, f := range rttype.Type.Fields() {
+                       if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
+                               switch f.Sym.Name {
+                               case "Hash":
+                                       v.reset(OpConst32)
+                                       v.AuxInt = int64(types.TypeHash(t))
+                                       return v
+                               default:
+                                       base.Fatalf("unknown field %s for fixedLoad of %s at offset %d", f.Sym.Name, lsym.Name, off)
+                               }
                        }
-                       return r.Sym
                }
+
+               if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
+                       elemSym := reflectdata.TypeLinksym(t.Elem())
+                       reflectdata.MarkTypeSymUsedInInterface(elemSym, f.fe.Func().Linksym())
+                       v.reset(OpAddr)
+                       v.Aux = symToAux(elemSym)
+                       v.AddArg(sb)
+                       return v
+               }
+
+               base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
        }
-       base.Fatalf("fixedSym data not known for %s:%d", sym, off)
+
+       base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
        return nil
 }
 
index e776ea5301bb6e6306a5274dc36ff440dbb55f87..28d14e7076a434a4a421ad40b229251b64f72401 100644 (file)
@@ -14087,8 +14087,6 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
        v_1 := v.Args[1]
        v_0 := v.Args[0]
        b := v.Block
-       config := b.Func.Config
-       typ := &b.Func.Config.Types
        // match: (Load <t1> p1 (Store {t2} p2 x _))
        // cond: isSamePtr(p1, p2) && copyCompatibleType(t1, x.Type) && t1.Size() == t2.Size()
        // result: x
@@ -14673,344 +14671,26 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                v.AddArg(v0)
                return true
        }
-       // match: (Load <typ.BytePtr> (OffPtr [off] (Addr {s} sb) ) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0.Aux)
-               sb := v_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (OffPtr [off] (Convert (Addr {s} sb) _) ) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0.Aux)
-               sb := v_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0_0.Aux)
-               sb := v_0_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0_0_0 := v_0_0_0_0.Args[0]
-               if v_0_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0_0_0.Aux)
-               sb := v_0_0_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.Uintptr> (OffPtr [off] (Addr {s} sb) ) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.Uintptr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0.Aux)
-               sb := v_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.Uintptr> (OffPtr [off] (Convert (Addr {s} sb) _) ) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.Uintptr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0.Aux)
-               sb := v_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.Uintptr> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.Uintptr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0_0.Aux)
-               sb := v_0_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.Uintptr> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)
-       // cond: isFixedSym(s, off)
-       // result: (Addr {fixedSym(b.Func, s, off)} sb)
-       for {
-               if v.Type != typ.Uintptr || v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0_0_0 := v_0_0_0_0.Args[0]
-               if v_0_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0_0_0.Aux)
-               sb := v_0_0_0_0_0.Args[0]
-               if !(isFixedSym(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (Addr {s} sb) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0.Aux)
-               sb := v_0.Args[0]
-               if !(isFixedSym(s, 0)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (Convert (Addr {s} sb) _) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpConvert {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0.Aux)
-               sb := v_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (ITab (IMake (Addr {s} sb) _)) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
+       // match: (Load (Addr {s} sb) _)
+       // cond: isFixedLoad(v, s, 0)
+       // result: rewriteFixedLoad(v, s, sb, 0)
        for {
-               if v.Type != typ.BytePtr || v_0.Op != OpITab {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0.Aux)
-               sb := v_0_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.BytePtr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
-       for {
-               if v.Type != typ.BytePtr || v_0.Op != OpITab {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               s := auxToSym(v_0_0_0_0.Aux)
-               sb := v_0_0_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <typ.Uintptr> (Addr {s} sb) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
-       for {
-               if v.Type != typ.Uintptr || v_0.Op != OpAddr {
+               if v_0.Op != OpAddr {
                        break
                }
                s := auxToSym(v_0.Aux)
                sb := v_0.Args[0]
-               if !(isFixedSym(s, 0)) {
+               if !(isFixedLoad(v, s, 0)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, 0))
                return true
        }
-       // match: (Load <typ.Uintptr> (Convert (Addr {s} sb) _) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
+       // match: (Load (Convert (Addr {s} sb) _) _)
+       // cond: isFixedLoad(v, s, 0)
+       // result: rewriteFixedLoad(v, s, sb, 0)
        for {
-               if v.Type != typ.Uintptr || v_0.Op != OpConvert {
+               if v_0.Op != OpConvert {
                        break
                }
                v_0_0 := v_0.Args[0]
@@ -15019,19 +14699,17 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0.Aux)
                sb := v_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
+               if !(isFixedLoad(v, s, 0)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, 0))
                return true
        }
-       // match: (Load <typ.Uintptr> (ITab (IMake (Addr {s} sb) _)) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
+       // match: (Load (ITab (IMake (Addr {s} sb) _)) _)
+       // cond: isFixedLoad(v, s, 0)
+       // result: rewriteFixedLoad(v, s, sb, 0)
        for {
-               if v.Type != typ.Uintptr || v_0.Op != OpITab {
+               if v_0.Op != OpITab {
                        break
                }
                v_0_0 := v_0.Args[0]
@@ -15044,19 +14722,17 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0_0.Aux)
                sb := v_0_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
+               if !(isFixedLoad(v, s, 0)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, 0))
                return true
        }
-       // match: (Load <typ.Uintptr> (ITab (IMake (Convert (Addr {s} sb) _) _)) _)
-       // cond: isFixedSym(s, 0)
-       // result: (Addr {fixedSym(b.Func, s, 0)} sb)
+       // match: (Load (ITab (IMake (Convert (Addr {s} sb) _) _)) _)
+       // cond: isFixedLoad(v, s, 0)
+       // result: rewriteFixedLoad(v, s, sb, 0)
        for {
-               if v.Type != typ.Uintptr || v_0.Op != OpITab {
+               if v_0.Op != OpITab {
                        break
                }
                v_0_0 := v_0.Args[0]
@@ -15073,19 +14749,16 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0_0_0.Aux)
                sb := v_0_0_0_0.Args[0]
-               if !(isFixedSym(s, 0)) {
+               if !(isFixedLoad(v, s, 0)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(fixedSym(b.Func, s, 0))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, 0))
                return true
        }
-       // match: (Load <t> (OffPtr [off] (Addr {s} sb) ) _)
-       // cond: t.IsPtr() && isPtrElem(s, off)
-       // result: (Addr {ptrElem(b.Func, s, off)} sb)
+       // match: (Load (OffPtr [off] (Addr {s} sb) ) _)
+       // cond: isFixedLoad(v, s, off)
+       // result: rewriteFixedLoad(v, s, sb, off)
        for {
-               t := v.Type
                if v_0.Op != OpOffPtr {
                        break
                }
@@ -15096,19 +14769,16 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0.Aux)
                sb := v_0_0.Args[0]
-               if !(t.IsPtr() && isPtrElem(s, off)) {
+               if !(isFixedLoad(v, s, off)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(ptrElem(b.Func, s, off))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, off))
                return true
        }
-       // match: (Load <t> (OffPtr [off] (Convert (Addr {s} sb) _) ) _)
-       // cond: t.IsPtr() && isPtrElem(s, off)
-       // result: (Addr {ptrElem(b.Func, s, off)} sb)
+       // match: (Load (OffPtr [off] (Convert (Addr {s} sb) _) ) _)
+       // cond: isFixedLoad(v, s, off)
+       // result: rewriteFixedLoad(v, s, sb, off)
        for {
-               t := v.Type
                if v_0.Op != OpOffPtr {
                        break
                }
@@ -15123,19 +14793,16 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0_0.Aux)
                sb := v_0_0_0.Args[0]
-               if !(t.IsPtr() && isPtrElem(s, off)) {
+               if !(isFixedLoad(v, s, off)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(ptrElem(b.Func, s, off))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, off))
                return true
        }
-       // match: (Load <t> (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _)
-       // cond: t.IsPtr() && isPtrElem(s, off)
-       // result: (Addr {ptrElem(b.Func, s, off)} sb)
+       // match: (Load (OffPtr [off] (ITab (IMake (Addr {s} sb) _))) _)
+       // cond: isFixedLoad(v, s, off)
+       // result: rewriteFixedLoad(v, s, sb, off)
        for {
-               t := v.Type
                if v_0.Op != OpOffPtr {
                        break
                }
@@ -15154,19 +14821,16 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0_0_0.Aux)
                sb := v_0_0_0_0.Args[0]
-               if !(t.IsPtr() && isPtrElem(s, off)) {
+               if !(isFixedLoad(v, s, off)) {
                        break
                }
-               v.reset(OpAddr)
-               v.Aux = symToAux(ptrElem(b.Func, s, off))
-               v.AddArg(sb)
+               v.copyOf(rewriteFixedLoad(v, s, sb, off))
                return true
        }
-       // match: (Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)
-       // cond: t.IsPtr() && isPtrElem(s, off)
-       // result: (Addr {ptrElem(b.Func, s, off)} sb)
+       // match: (Load (OffPtr [off] (ITab (IMake (Convert (Addr {s} sb) _) _))) _)
+       // cond: isFixedLoad(v, s, off)
+       // result: rewriteFixedLoad(v, s, sb, off)
        for {
-               t := v.Type
                if v_0.Op != OpOffPtr {
                        break
                }
@@ -15189,120 +14853,10 @@ func rewriteValuegeneric_OpLoad(v *Value) bool {
                }
                s := auxToSym(v_0_0_0_0_0.Aux)
                sb := v_0_0_0_0_0.Args[0]
-               if !(t.IsPtr() && isPtrElem(s, off)) {
-                       break
-               }
-               v.reset(OpAddr)
-               v.Aux = symToAux(ptrElem(b.Func, s, off))
-               v.AddArg(sb)
-               return true
-       }
-       // match: (Load <t> (OffPtr [off] (Addr {sym} _) ) _)
-       // cond: t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)
-       // result: (Const32 [fixed32(config, sym, off)])
-       for {
-               t := v.Type
-               if v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpAddr {
-                       break
-               }
-               sym := auxToSym(v_0_0.Aux)
-               if !(t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)) {
-                       break
-               }
-               v.reset(OpConst32)
-               v.AuxInt = int32ToAuxInt(fixed32(config, sym, off))
-               return true
-       }
-       // match: (Load <t> (OffPtr [off] (Convert (Addr {sym} _) _) ) _)
-       // cond: t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)
-       // result: (Const32 [fixed32(config, sym, off)])
-       for {
-               t := v.Type
-               if v_0.Op != OpOffPtr {
+               if !(isFixedLoad(v, s, off)) {
                        break
                }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpAddr {
-                       break
-               }
-               sym := auxToSym(v_0_0_0.Aux)
-               if !(t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)) {
-                       break
-               }
-               v.reset(OpConst32)
-               v.AuxInt = int32ToAuxInt(fixed32(config, sym, off))
-               return true
-       }
-       // match: (Load <t> (OffPtr [off] (ITab (IMake (Addr {sym} _) _))) _)
-       // cond: t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)
-       // result: (Const32 [fixed32(config, sym, off)])
-       for {
-               t := v.Type
-               if v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               sym := auxToSym(v_0_0_0_0.Aux)
-               if !(t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)) {
-                       break
-               }
-               v.reset(OpConst32)
-               v.AuxInt = int32ToAuxInt(fixed32(config, sym, off))
-               return true
-       }
-       // match: (Load <t> (OffPtr [off] (ITab (IMake (Convert (Addr {sym} _) _) _))) _)
-       // cond: t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)
-       // result: (Const32 [fixed32(config, sym, off)])
-       for {
-               t := v.Type
-               if v_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0.AuxInt)
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpConvert {
-                       break
-               }
-               v_0_0_0_0_0 := v_0_0_0_0.Args[0]
-               if v_0_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               sym := auxToSym(v_0_0_0_0_0.Aux)
-               if !(t.IsInteger() && t.Size() == 4 && isFixed32(config, sym, off)) {
-                       break
-               }
-               v.reset(OpConst32)
-               v.AuxInt = int32ToAuxInt(fixed32(config, sym, off))
+               v.copyOf(rewriteFixedLoad(v, s, sb, off))
                return true
        }
        return false