// Inline small or disjoint runtime.memmove calls with constant length.
 // See the comment in op Move in genericOps.go for discussion of the type.
-
+//
+// Note that we've lost any knowledge of the type and alignment requirements
+// of the source and destination. We only know the size, and that the type
+// contains no pointers.
+// The type of the move is not necessarily v.Args[0].Type().Elem()!
+// See issue 55122 for details.
+//
 // Because expand calls runs after prove, constants useful to this pattern may not appear.
 // Both versions need to exist; the memory and register variants.
 //
 (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store  _ src s3:(Store {t} _ dst mem)))))
        && sz >= 0
        && isSameCall(sym, "runtime.memmove")
-       && t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(s1, s2, s3, call)
-       => (Move {t.Elem()} [int64(sz)] dst src mem)
+       => (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
 
 // Match post-expansion calls, register version.
 (SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
        && sz >= 0
        && call.Uses == 1 // this will exclude all calls with results
        && isSameCall(sym, "runtime.memmove")
-       && dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(call)
-       => (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       => (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
 
 // Match pre-expansion calls.
 (SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
        && sz >= 0
        && call.Uses == 1 // this will exclude all calls with results
        && isSameCall(sym, "runtime.memmove")
-       && dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(call)
-       => (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       => (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
 
 // De-virtualize late-expanded interface calls into late-expanded static calls.
 // Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
 
                return true
        }
        // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
-       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
-       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                if s3.Op != OpStore {
                        break
                }
-               t := auxToType(s3.Aux)
                mem := s3.Args[2]
                dst := s3.Args[1]
-               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(t.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }
        // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
-       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
-       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                if s3.Op != OpStore {
                        break
                }
-               t := auxToType(s3.Aux)
                mem := s3.Args[2]
                dst := s3.Args[1]
-               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(t.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }
        // match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
-       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
-       // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                        break
                }
                sz := auxIntToInt64(call_2.AuxInt)
-               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
+               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(dst.Type.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }
        // match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
-       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
-       // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                        break
                }
                sz := auxIntToInt32(call_2.AuxInt)
-               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
+               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(dst.Type.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }
        // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
-       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
-       // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                        break
                }
                sz := auxIntToInt64(call_2.AuxInt)
-               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
+               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(dst.Type.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }
        // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
-       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
-       // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
+       // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
+       // result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
        for {
                if auxIntToInt64(v.AuxInt) != 0 {
                        break
                        break
                }
                sz := auxIntToInt32(call_2.AuxInt)
-               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
+               if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
                        break
                }
                v.reset(OpMove)
                v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(dst.Type.Elem())
+               v.Aux = typeToAux(types.Types[types.TUINT8])
                v.AddArg3(dst, src, mem)
                return true
        }