Commit 3c1a4c19 authored by Keith Randall's avatar Keith Randall

cmd/compile: don't nilcheck newobject and return values from mapaccess{1,2}

They are guaranteed to be non-nil, no point in inserting
nil checks for them.

Fixes #15390

Change-Id: I3b9a0f2319affc2139dcc446d0a56c6785ae5a86
Reviewed-on: https://go-review.googlesource.com/22291Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
parent 32302d62
...@@ -978,7 +978,11 @@ func Agenr(n *Node, a *Node, res *Node) { ...@@ -978,7 +978,11 @@ func Agenr(n *Node, a *Node, res *Node) {
case OIND: case OIND:
Cgenr(n.Left, a, res) Cgenr(n.Left, a, res)
if !n.Left.NonNil {
Cgen_checknil(a) Cgen_checknil(a)
} else if Debug_checknil != 0 && n.Lineno > 1 {
Warnl(n.Lineno, "removed nil check")
}
case OINDEX: case OINDEX:
if Ctxt.Arch.Family == sys.ARM { if Ctxt.Arch.Family == sys.ARM {
...@@ -1587,7 +1591,11 @@ func Agen(n *Node, res *Node) { ...@@ -1587,7 +1591,11 @@ func Agen(n *Node, res *Node) {
case OIND: case OIND:
Cgen(nl, res) Cgen(nl, res)
if !nl.NonNil {
Cgen_checknil(res) Cgen_checknil(res)
} else if Debug_checknil != 0 && n.Lineno > 1 {
Warnl(n.Lineno, "removed nil check")
}
case ODOT: case ODOT:
Agen(nl, res) Agen(nl, res)
...@@ -1597,7 +1605,11 @@ func Agen(n *Node, res *Node) { ...@@ -1597,7 +1605,11 @@ func Agen(n *Node, res *Node) {
case ODOTPTR: case ODOTPTR:
Cgen(nl, res) Cgen(nl, res)
if !nl.NonNil {
Cgen_checknil(res) Cgen_checknil(res)
} else if Debug_checknil != 0 && n.Lineno > 1 {
Warnl(n.Lineno, "removed nil check")
}
if n.Xoffset != 0 { if n.Xoffset != 0 {
addOffset(res, n.Xoffset) addOffset(res, n.Xoffset)
} }
...@@ -1658,7 +1670,11 @@ func Igen(n *Node, a *Node, res *Node) { ...@@ -1658,7 +1670,11 @@ func Igen(n *Node, a *Node, res *Node) {
case ODOTPTR: case ODOTPTR:
Cgenr(n.Left, a, res) Cgenr(n.Left, a, res)
if !n.Left.NonNil {
Cgen_checknil(a) Cgen_checknil(a)
} else if Debug_checknil != 0 && n.Lineno > 1 {
Warnl(n.Lineno, "removed nil check")
}
a.Op = OINDREG a.Op = OINDREG
a.Xoffset += n.Xoffset a.Xoffset += n.Xoffset
a.Type = n.Type a.Type = n.Type
......
...@@ -319,6 +319,12 @@ func Jconv(n *Node, flag FmtFlag) string { ...@@ -319,6 +319,12 @@ func Jconv(n *Node, flag FmtFlag) string {
if n.Assigned { if n.Assigned {
buf.WriteString(" assigned") buf.WriteString(" assigned")
} }
if n.Bounded {
buf.WriteString(" bounded")
}
if n.NonNil {
buf.WriteString(" nonnil")
}
if c == 0 && n.Used { if c == 0 && n.Used {
fmt.Fprintf(&buf, " used(%v)", n.Used) fmt.Fprintf(&buf, " used(%v)", n.Used)
......
...@@ -1938,8 +1938,7 @@ func (s *state) expr(n *Node) *ssa.Value { ...@@ -1938,8 +1938,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
case OIND: case OIND:
p := s.expr(n.Left) p := s.exprPtr(n.Left, false, n.Lineno)
s.nilCheck(p)
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case ODOT: case ODOT:
...@@ -1952,8 +1951,7 @@ func (s *state) expr(n *Node) *ssa.Value { ...@@ -1952,8 +1951,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case ODOTPTR: case ODOTPTR:
p := s.expr(n.Left) p := s.exprPtr(n.Left, false, n.Lineno)
s.nilCheck(p)
p = s.newValue1I(ssa.OpOffPtr, p.Type, n.Xoffset, p) p = s.newValue1I(ssa.OpOffPtr, p.Type, n.Xoffset, p)
return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
...@@ -2778,19 +2776,12 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { ...@@ -2778,19 +2776,12 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Elem()), a, i) return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Elem()), a, i)
} }
case OIND: case OIND:
p := s.expr(n.Left) return s.exprPtr(n.Left, bounded, n.Lineno)
if !bounded {
s.nilCheck(p)
}
return p
case ODOT: case ODOT:
p := s.addr(n.Left, bounded) p := s.addr(n.Left, bounded)
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p) return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case ODOTPTR: case ODOTPTR:
p := s.expr(n.Left) p := s.exprPtr(n.Left, bounded, n.Lineno)
if !bounded {
s.nilCheck(p)
}
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p) return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case OCLOSUREVAR: case OCLOSUREVAR:
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
...@@ -2892,6 +2883,19 @@ func canSSAType(t *Type) bool { ...@@ -2892,6 +2883,19 @@ func canSSAType(t *Type) bool {
} }
} }
// exprPtr evaluates n to a pointer and nil-checks it.
func (s *state) exprPtr(n *Node, bounded bool, lineno int32) *ssa.Value {
p := s.expr(n)
if bounded || n.NonNil {
if s.f.Config.Debug_checknil() && lineno > 1 {
s.f.Config.Warnl(lineno, "removed nil check")
}
return p
}
s.nilCheck(p)
return p
}
// nilCheck generates nil pointer checking code. // nilCheck generates nil pointer checking code.
// Starts a new block on return, unless nil checks are disabled. // Starts a new block on return, unless nil checks are disabled.
// Used only for automatically inserted nil checks, // Used only for automatically inserted nil checks,
......
...@@ -54,6 +54,7 @@ type Node struct { ...@@ -54,6 +54,7 @@ type Node struct {
Addable bool // addressable Addable bool // addressable
Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN
Bounded bool // bounds check unnecessary Bounded bool // bounds check unnecessary
NonNil bool // guaranteed to be non-nil
Class Class // PPARAM, PAUTO, PEXTERN, etc Class Class // PPARAM, PAUTO, PEXTERN, etc
Embedded uint8 // ODCLFIELD embedded type Embedded uint8 // ODCLFIELD embedded type
Colas bool // OAS resulting from := Colas bool // OAS resulting from :=
......
...@@ -886,6 +886,7 @@ opswitch: ...@@ -886,6 +886,7 @@ opswitch:
if !isblank(a) { if !isblank(a) {
var_ := temp(Ptrto(t.Val())) var_ := temp(Ptrto(t.Val()))
var_.Typecheck = 1 var_.Typecheck = 1
var_.NonNil = true // mapaccess always returns a non-nil pointer
n.List.SetIndex(0, var_) n.List.SetIndex(0, var_)
n = walkexpr(n, init) n = walkexpr(n, init)
init.Append(n) init.Append(n)
...@@ -895,8 +896,6 @@ opswitch: ...@@ -895,8 +896,6 @@ opswitch:
n = typecheck(n, Etop) n = typecheck(n, Etop)
n = walkexpr(n, init) n = walkexpr(n, init)
// TODO: ptr is always non-nil, so disable nil check for this OIND op.
case ODELETE: case ODELETE:
init.AppendNodes(&n.Ninit) init.AppendNodes(&n.Ninit)
map_ := n.List.First() map_ := n.List.First()
...@@ -1224,7 +1223,6 @@ opswitch: ...@@ -1224,7 +1223,6 @@ opswitch:
// standard version takes key by reference. // standard version takes key by reference.
// orderexpr made sure key is addressable. // orderexpr made sure key is addressable.
key = Nod(OADDR, n.Right, nil) key = Nod(OADDR, n.Right, nil)
p = "mapaccess1" p = "mapaccess1"
} }
...@@ -1235,6 +1233,7 @@ opswitch: ...@@ -1235,6 +1233,7 @@ opswitch:
z := zeroaddr(w) z := zeroaddr(w)
n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key, z) n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key, z)
} }
n.NonNil = true // mapaccess always returns a non-nil pointer
n = Nod(OIND, n, nil) n = Nod(OIND, n, nil)
n.Type = t.Val() n.Type = t.Val()
n.Typecheck = 1 n.Typecheck = 1
...@@ -2015,7 +2014,9 @@ func callnew(t *Type) *Node { ...@@ -2015,7 +2014,9 @@ func callnew(t *Type) *Node {
dowidth(t) dowidth(t)
fn := syslook("newobject") fn := syslook("newobject")
fn = substArgTypes(fn, t) fn = substArgTypes(fn, t)
return mkcall1(fn, Ptrto(t), nil, typename(t)) v := mkcall1(fn, Ptrto(t), nil, typename(t))
v.NonNil = true
return v
} }
func iscallret(n *Node) bool { func iscallret(n *Node) bool {
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
package ssa package ssa
// TODO: return value from newobject/newarray is non-nil.
// nilcheckelim eliminates unnecessary nil checks. // nilcheckelim eliminates unnecessary nil checks.
func nilcheckelim(f *Func) { func nilcheckelim(f *Func) {
// A nil check is redundant if the same nil check was successful in a // A nil check is redundant if the same nil check was successful in a
......
...@@ -193,3 +193,24 @@ func f4(x *[10]int) { ...@@ -193,3 +193,24 @@ func f4(x *[10]int) {
x = y x = y
_ = &x[9] // ERROR "removed repeated nil check" _ = &x[9] // ERROR "removed repeated nil check"
} }
func m1(m map[int][80]byte) byte {
v := m[3] // ERROR "removed nil check"
return v[5]
}
func m2(m map[int][800]byte) byte {
v := m[3] // ERROR "removed nil check"
return v[5]
}
func m3(m map[int][80]byte) (byte, bool) {
v, ok := m[3] // ERROR "removed nil check"
return v[5], ok
}
func m4(m map[int][800]byte) (byte, bool) {
v, ok := m[3] // ERROR "removed nil check"
return v[5], ok
}
func p1() byte {
p := new([100]byte)
return p[5] // ERROR "removed nil check"
}
...@@ -207,3 +207,24 @@ func f6(p, q *T) { ...@@ -207,3 +207,24 @@ func f6(p, q *T) {
x := *p // ERROR "removed nil check" x := *p // ERROR "removed nil check"
*q = x // ERROR "removed nil check" *q = x // ERROR "removed nil check"
} }
func m1(m map[int][80]byte) byte {
v := m[3] // ERROR "removed nil check"
return v[5]
}
func m2(m map[int][800]byte) byte {
v := m[3] // ERROR "removed nil check"
return v[5]
}
func m3(m map[int][80]byte) (byte, bool) {
v, ok := m[3] // ERROR "removed nil check"
return v[5], ok
}
func m4(m map[int][800]byte) (byte, bool) {
v, ok := m[3] // ERROR "removed nil check"
return v[5], ok
}
func p1() byte {
p := new([100]byte)
return p[5] // ERROR "removed nil check"
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment