From df43cf033f41d784a9f263b932eb8c859132eec4 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Fri, 24 Jun 2016 15:06:17 -0400 Subject: [PATCH] [dev.ssa] cmd/compile: optimize NilCheck in SSA for ARM Like AMD64, don't issue NilCheck instruction if the subsequent block has a load or store at the same address. Pass test/nilptr3_ssa.go. Updates #15365. Change-Id: Ic88780dab8c4893c57d1c95f663760cc185fe51e Reviewed-on: https://go-review.googlesource.com/24451 Reviewed-by: David Chase Run-TryBot: David Chase --- src/cmd/compile/internal/arm/ssa.go | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go index 843486bb23..ae7dbc8620 100644 --- a/src/cmd/compile/internal/arm/ssa.go +++ b/src/cmd/compile/internal/arm/ssa.go @@ -52,6 +52,10 @@ var ssaRegToReg = []int16{ 0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case. } +// Smallest possible faulting page at address zero, +// see ../../../../runtime/internal/sys/arch_arm.go +const minZeroPage = 4096 + // loadByType returns the load instruction of the given type. func loadByType(t ssa.Type) obj.As { if t.IsFloat() { @@ -514,6 +518,63 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) p.To.Offset = v.AuxInt case ssa.OpARMLoweredNilCheck: + // Optimization - if the subsequent block has a load or store + // at the same address, we don't need to issue this instruction. + mem := v.Args[1] + for _, w := range v.Block.Succs[0].Block().Values { + if w.Op == ssa.OpPhi { + if w.Type.IsMemory() { + mem = w + } + continue + } + if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() { + // w doesn't use a store - can't be a memory op. + continue + } + if w.Args[len(w.Args)-1] != mem { + v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w) + } + switch w.Op { + case ssa.OpARMMOVBload, ssa.OpARMMOVBUload, ssa.OpARMMOVHload, ssa.OpARMMOVHUload, + ssa.OpARMMOVWload, ssa.OpARMMOVFload, ssa.OpARMMOVDload, + ssa.OpARMMOVBstore, ssa.OpARMMOVHstore, ssa.OpARMMOVWstore, + ssa.OpARMMOVFstore, ssa.OpARMMOVDstore: + // arg0 is ptr, auxint is offset + if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage { + if gc.Debug_checknil != 0 && int(v.Line) > 1 { + gc.Warnl(v.Line, "removed nil check") + } + return + } + case ssa.OpARMDUFFZERO, ssa.OpARMLoweredZero: + // arg0 is ptr + if w.Args[0] == v.Args[0] { + if gc.Debug_checknil != 0 && int(v.Line) > 1 { + gc.Warnl(v.Line, "removed nil check") + } + return + } + case ssa.OpARMDUFFCOPY, ssa.OpARMLoweredMove: + // arg0 is dst ptr, arg1 is src ptr + if w.Args[0] == v.Args[0] || w.Args[1] == v.Args[0] { + if gc.Debug_checknil != 0 && int(v.Line) > 1 { + gc.Warnl(v.Line, "removed nil check") + } + return + } + default: + } + if w.Type.IsMemory() { + if w.Op == ssa.OpVarDef || w.Op == ssa.OpVarKill || w.Op == ssa.OpVarLive { + // these ops are OK + mem = w + continue + } + // We can't delay the nil check past the next store. + break + } + } // Issue a load which will fault if arg is nil. p := gc.Prog(arm.AMOVB) p.From.Type = obj.TYPE_MEM