]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: rewrite empty makeslice to zerobase pointer
authorJorropo <jorropo.pgm@gmail.com>
Wed, 30 Nov 2022 08:45:29 +0000 (09:45 +0100)
committerKeith Randall <khr@golang.org>
Fri, 20 Jan 2023 04:57:35 +0000 (04:57 +0000)
make\(\[\][a-zA-Z0-9]+, 0\) is seen 52 times in the go source.
And at least 391 times on internet:
https://grep.app/search?q=make%5C%28%5C%5B%5C%5D%5Ba-zA-Z0-9%5D%2B%2C%200%5C%29&regexp=true
This used to compile to calling runtime.makeslice.
However we can copy what we do for []T{}, just use a zerobase pointer.

On my machine this is 10x faster (from 3ns to 0.3ns).
Note that an empty loop also runs in 0.3ns,
so this really is free when you count superscallar execution.

Change-Id: I1cfe7e69f5a7a4dabbc71912ce6a4f8a2d4a7f3c
Reviewed-on: https://go-review.googlesource.com/c/go/+/454036
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Jakub Ciolek <jakub@ciolek.dev>

src/cmd/compile/internal/ssa/_gen/generic.rules
src/cmd/compile/internal/ssa/_gen/rulegen.go
src/cmd/compile/internal/ssa/rewritegeneric.go
test/codegen/slices.go

index 0406fbbd17fbcf8b325717f8c5365be5eb7953ba..d5d4033c7bfb42747638c0ec3459496597525933 100644 (file)
   && canLoadUnaligned(config) && config.PtrSize == 8
   => (MakeResult (Eq64 (Load <typ.Int64> sptr mem) (Const64 <typ.Int64> [int64(read64(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
 
+// Recognise make([]T, 0) and replace it with a pointer to the zerobase
+(SelectN [0] call:(StaticLECall _ (Const(64|32) [0]) (Const(64|32) [0]) _))
+       && isSameCall(call.Aux, "runtime.makeslice")
+       && clobberIfDead(call)
+       => (Addr {ir.Syms.Zerobase} (SB))
+
+(SelectN [1] call:(StaticLECall _ (Const(64|32) [0]) (Const(64|32) [0]) mem))
+       && isSameCall(call.Aux, "runtime.makeslice")
+       && clobberIfDead(call)
+       => mem
+
 // Evaluate constant address comparisons.
 (EqPtr  x x) => (ConstBool [true])
 (NeqPtr x x) => (ConstBool [false])
index 2840e8f458aa9144c34948c31a25c9dd848b2d54..15be9a1c506231043fb74459404294f526097bfb 100644 (file)
@@ -585,6 +585,7 @@ func fprint(w io.Writer, n Node) {
                        "cmd/internal/obj",
                        "cmd/compile/internal/base",
                        "cmd/compile/internal/types",
+                       "cmd/compile/internal/ir",
                }, n.Arch.imports...) {
                        fmt.Fprintf(w, "import %q\n", path)
                }
index e1c65fc286f226cae0ee1fe35ef429b032596a21..6ba7fb3d5554a40cb4e3c4f20e75bdfc9ef11140 100644 (file)
@@ -4,6 +4,7 @@ package ssa
 
 import "math"
 import "cmd/compile/internal/types"
+import "cmd/compile/internal/ir"
 
 func rewriteValuegeneric(v *Value) bool {
        switch v.Op {
@@ -26379,6 +26380,7 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
        v_0 := v.Args[0]
        b := v.Block
        config := b.Func.Config
+       typ := &b.Func.Config.Types
        // match: (SelectN [0] (MakeResult x ___))
        // result: x
        for {
@@ -26409,6 +26411,104 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
                v.copyOf(z)
                return true
        }
+       // match: (SelectN [0] call:(StaticLECall _ (Const64 [0]) (Const64 [0]) _))
+       // cond: isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)
+       // result: (Addr {ir.Syms.Zerobase} (SB))
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 4 {
+                       break
+               }
+               _ = call.Args[2]
+               call_1 := call.Args[1]
+               if call_1.Op != OpConst64 || auxIntToInt64(call_1.AuxInt) != 0 {
+                       break
+               }
+               call_2 := call.Args[2]
+               if call_2.Op != OpConst64 || auxIntToInt64(call_2.AuxInt) != 0 || !(isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)) {
+                       break
+               }
+               v.reset(OpAddr)
+               v.Aux = symToAux(ir.Syms.Zerobase)
+               v0 := b.NewValue0(v.Pos, OpSB, typ.Uintptr)
+               v.AddArg(v0)
+               return true
+       }
+       // match: (SelectN [0] call:(StaticLECall _ (Const32 [0]) (Const32 [0]) _))
+       // cond: isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)
+       // result: (Addr {ir.Syms.Zerobase} (SB))
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 4 {
+                       break
+               }
+               _ = call.Args[2]
+               call_1 := call.Args[1]
+               if call_1.Op != OpConst32 || auxIntToInt32(call_1.AuxInt) != 0 {
+                       break
+               }
+               call_2 := call.Args[2]
+               if call_2.Op != OpConst32 || auxIntToInt32(call_2.AuxInt) != 0 || !(isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)) {
+                       break
+               }
+               v.reset(OpAddr)
+               v.Aux = symToAux(ir.Syms.Zerobase)
+               v0 := b.NewValue0(v.Pos, OpSB, typ.Uintptr)
+               v.AddArg(v0)
+               return true
+       }
+       // match: (SelectN [1] call:(StaticLECall _ (Const64 [0]) (Const64 [0]) mem))
+       // cond: isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)
+       // result: mem
+       for {
+               if auxIntToInt64(v.AuxInt) != 1 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 4 {
+                       break
+               }
+               mem := call.Args[3]
+               call_1 := call.Args[1]
+               if call_1.Op != OpConst64 || auxIntToInt64(call_1.AuxInt) != 0 {
+                       break
+               }
+               call_2 := call.Args[2]
+               if call_2.Op != OpConst64 || auxIntToInt64(call_2.AuxInt) != 0 || !(isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)) {
+                       break
+               }
+               v.copyOf(mem)
+               return true
+       }
+       // match: (SelectN [1] call:(StaticLECall _ (Const32 [0]) (Const32 [0]) mem))
+       // cond: isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)
+       // result: mem
+       for {
+               if auxIntToInt64(v.AuxInt) != 1 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 4 {
+                       break
+               }
+               mem := call.Args[3]
+               call_1 := call.Args[1]
+               if call_1.Op != OpConst32 || auxIntToInt32(call_1.AuxInt) != 0 {
+                       break
+               }
+               call_2 := call.Args[2]
+               if call_2.Op != OpConst32 || auxIntToInt32(call_2.AuxInt) != 0 || !(isSameCall(call.Aux, "runtime.makeslice") && clobberIfDead(call)) {
+                       break
+               }
+               v.copyOf(mem)
+               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") && 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)
index fa4142d76770ba906ad586bfa8e6f6d3176caa17..f897200fb92292aecc9c3bac910b93553334c6b0 100644 (file)
@@ -337,6 +337,12 @@ func SliceMakeCopyNoMemmoveDifferentLen(s []int) []int {
        return a
 }
 
+func SliceMakeEmptyPointerToZerobase() []int {
+       // amd64:`LEAQ.+runtime\.zerobase`
+       // amd64:-`.*runtime\.makeslice`
+       return make([]int, 0)
+}
+
 // ---------------------- //
 //   Nil check of &s[0]   //
 // ---------------------- //