mirror of
https://github.com/golang/go
synced 2024-09-05 07:54:46 +00:00
cmd/compile: implement shifts by signed amounts
Allow shifts by signed amounts. Panic if the shift amount is negative. TODO: We end up doing two compares per shift, see Ian's comment https://github.com/golang/go/issues/19113#issuecomment-443241799 that we could do it with a single comparison in the normal case. The prove pass mostly handles this code well. For instance, it removes the <0 check for cases like this: if s >= 0 { _ = x << s } _ = x << len(a) This case isn't handled well yet: _ = x << (y & 0xf) I'll do followon CLs for unhandled cases as needed. Update #19113 R=go1.13 Change-Id: I839a5933d94b54ab04deb9dd5149f32c51c90fa1 Reviewed-on: https://go-review.googlesource.com/c/158719 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
parent
e1acd854f7
commit
585c9e8412
|
@ -13,6 +13,7 @@ var runtimeDecls = [...]struct {
|
||||||
{"panicindex", funcTag, 5},
|
{"panicindex", funcTag, 5},
|
||||||
{"panicslice", funcTag, 5},
|
{"panicslice", funcTag, 5},
|
||||||
{"panicdivide", funcTag, 5},
|
{"panicdivide", funcTag, 5},
|
||||||
|
{"panicshift", funcTag, 5},
|
||||||
{"panicmakeslicelen", funcTag, 5},
|
{"panicmakeslicelen", funcTag, 5},
|
||||||
{"throwinit", funcTag, 5},
|
{"throwinit", funcTag, 5},
|
||||||
{"panicwrap", funcTag, 5},
|
{"panicwrap", funcTag, 5},
|
||||||
|
|
|
@ -18,6 +18,7 @@ func newobject(typ *byte) *any
|
||||||
func panicindex()
|
func panicindex()
|
||||||
func panicslice()
|
func panicslice()
|
||||||
func panicdivide()
|
func panicdivide()
|
||||||
|
func panicshift()
|
||||||
func panicmakeslicelen()
|
func panicmakeslicelen()
|
||||||
func throwinit()
|
func throwinit()
|
||||||
func panicwrap()
|
func panicwrap()
|
||||||
|
|
|
@ -296,6 +296,7 @@ var (
|
||||||
msanwrite,
|
msanwrite,
|
||||||
newproc,
|
newproc,
|
||||||
panicdivide,
|
panicdivide,
|
||||||
|
panicshift,
|
||||||
panicdottypeE,
|
panicdottypeE,
|
||||||
panicdottypeI,
|
panicdottypeI,
|
||||||
panicindex,
|
panicindex,
|
||||||
|
|
|
@ -84,6 +84,7 @@ func initssaconfig() {
|
||||||
panicnildottype = sysfunc("panicnildottype")
|
panicnildottype = sysfunc("panicnildottype")
|
||||||
panicoverflow = sysfunc("panicoverflow")
|
panicoverflow = sysfunc("panicoverflow")
|
||||||
panicslice = sysfunc("panicslice")
|
panicslice = sysfunc("panicslice")
|
||||||
|
panicshift = sysfunc("panicshift")
|
||||||
raceread = sysfunc("raceread")
|
raceread = sysfunc("raceread")
|
||||||
racereadrange = sysfunc("racereadrange")
|
racereadrange = sysfunc("racereadrange")
|
||||||
racewrite = sysfunc("racewrite")
|
racewrite = sysfunc("racewrite")
|
||||||
|
@ -2128,7 +2129,13 @@ func (s *state) expr(n *Node) *ssa.Value {
|
||||||
case OLSH, ORSH:
|
case OLSH, ORSH:
|
||||||
a := s.expr(n.Left)
|
a := s.expr(n.Left)
|
||||||
b := s.expr(n.Right)
|
b := s.expr(n.Right)
|
||||||
return s.newValue2(s.ssaShiftOp(n.Op, n.Type, n.Right.Type), a.Type, a, b)
|
bt := b.Type
|
||||||
|
if bt.IsSigned() {
|
||||||
|
cmp := s.newValue2(s.ssaOp(OGE, bt), types.Types[TBOOL], b, s.zeroVal(bt))
|
||||||
|
s.check(cmp, panicshift)
|
||||||
|
bt = bt.ToUnsigned()
|
||||||
|
}
|
||||||
|
return s.newValue2(s.ssaShiftOp(n.Op, n.Type, bt), a.Type, a, b)
|
||||||
case OANDAND, OOROR:
|
case OANDAND, OOROR:
|
||||||
// To implement OANDAND (and OOROR), we introduce a
|
// To implement OANDAND (and OOROR), we introduce a
|
||||||
// new temporary variable to hold the result. The
|
// new temporary variable to hold the result. The
|
||||||
|
|
|
@ -660,8 +660,8 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
r = defaultlit(r, types.Types[TUINT])
|
r = defaultlit(r, types.Types[TUINT])
|
||||||
n.Right = r
|
n.Right = r
|
||||||
t := r.Type
|
t := r.Type
|
||||||
if !t.IsInteger() || t.IsSigned() {
|
if !t.IsInteger() {
|
||||||
yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type)
|
yyerror("invalid operation: %v (shift count type %v, must be integer)", n, r.Type)
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
|
@ -1115,7 +1115,8 @@ func needRaceCleanup(sym interface{}, v *Value) bool {
|
||||||
case OpStaticCall:
|
case OpStaticCall:
|
||||||
switch v.Aux.(fmt.Stringer).String() {
|
switch v.Aux.(fmt.Stringer).String() {
|
||||||
case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicindex",
|
case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicindex",
|
||||||
"runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap":
|
"runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap",
|
||||||
|
"runtime.panicshift":
|
||||||
// Check for racefuncenter will encounter racefuncexit and vice versa.
|
// Check for racefuncenter will encounter racefuncexit and vice versa.
|
||||||
// Allow calls to panic*
|
// Allow calls to panic*
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -968,7 +968,7 @@ func isZeroArgRuntimeCall(s *obj.LSym) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch s.Name {
|
switch s.Name {
|
||||||
case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap":
|
case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap", "runtime.panicshift":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -23,16 +23,16 @@ func panicCheckMalloc(err error) {
|
||||||
|
|
||||||
var indexError = error(errorString("index out of range"))
|
var indexError = error(errorString("index out of range"))
|
||||||
|
|
||||||
// The panicindex, panicslice, and panicdivide functions are called by
|
// The panic{index,slice,divide,shift} functions are called by
|
||||||
// code generated by the compiler for out of bounds index expressions,
|
// code generated by the compiler for out of bounds index expressions,
|
||||||
// out of bounds slice expressions, and division by zero. The
|
// out of bounds slice expressions, division by zero, and shift by negative.
|
||||||
// panicdivide (again), panicoverflow, panicfloat, and panicmem
|
// The panicdivide (again), panicoverflow, panicfloat, and panicmem
|
||||||
// functions are called by the signal handler when a signal occurs
|
// functions are called by the signal handler when a signal occurs
|
||||||
// indicating the respective problem.
|
// indicating the respective problem.
|
||||||
//
|
//
|
||||||
// Since panicindex and panicslice are never called directly, and
|
// Since panic{index,slice,shift} are never called directly, and
|
||||||
// since the runtime package should never have an out of bounds slice
|
// since the runtime package should never have an out of bounds slice
|
||||||
// or array reference, if we see those functions called from the
|
// or array reference or negative shift, if we see those functions called from the
|
||||||
// runtime package we turn the panic into a throw. That will dump the
|
// runtime package we turn the panic into a throw. That will dump the
|
||||||
// entire runtime stack for easier debugging.
|
// entire runtime stack for easier debugging.
|
||||||
|
|
||||||
|
@ -68,6 +68,16 @@ func panicoverflow() {
|
||||||
panic(overflowError)
|
panic(overflowError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var shiftError = error(errorString("negative shift amount"))
|
||||||
|
|
||||||
|
func panicshift() {
|
||||||
|
if hasPrefix(funcname(findfunc(getcallerpc())), "runtime.") {
|
||||||
|
throw(string(shiftError.(errorString)))
|
||||||
|
}
|
||||||
|
panicCheckMalloc(shiftError)
|
||||||
|
panic(shiftError)
|
||||||
|
}
|
||||||
|
|
||||||
var floatError = error(errorString("floating point error"))
|
var floatError = error(errorString("floating point error"))
|
||||||
|
|
||||||
func panicfloat() {
|
func panicfloat() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// errorcheck
|
// compile
|
||||||
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -7,8 +7,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var s int = 0;
|
var s int = 0
|
||||||
var x int = 0;
|
var x int = 0
|
||||||
x = x << s; // ERROR "illegal|inval|shift"
|
x = x << s // as of 1.13, these are ok
|
||||||
x = x >> s; // ERROR "illegal|inval|shift"
|
x = x >> s // as of 1.13, these are ok
|
||||||
}
|
}
|
||||||
|
|
108
test/fixedbugs/issue19113.go
Normal file
108
test/fixedbugs/issue19113.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
var tests = []interface{}{
|
||||||
|
func(x int, s int) int {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x int, s int64) int {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x int, s int32) int {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x int, s int16) int {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x int, s int8) int {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x int, s int) int {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x int, s int64) int {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x int, s int32) int {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x int, s int16) int {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x int, s int8) int {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x uint, s int) uint {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x uint, s int64) uint {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x uint, s int32) uint {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x uint, s int16) uint {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x uint, s int8) uint {
|
||||||
|
return x << s
|
||||||
|
},
|
||||||
|
func(x uint, s int) uint {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x uint, s int64) uint {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x uint, s int32) uint {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x uint, s int16) uint {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
func(x uint, s int8) uint {
|
||||||
|
return x >> s
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for _, t := range tests {
|
||||||
|
runTest(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTest(f reflect.Value) {
|
||||||
|
xt := f.Type().In(0)
|
||||||
|
st := f.Type().In(1)
|
||||||
|
|
||||||
|
for _, x := range []int{1, 0, -1} {
|
||||||
|
for _, s := range []int{-99, -64, -63, -32, -31, -16, -15, -8, -7, -1, 0, 1, 7, 8, 15, 16, 31, 32, 63, 64, 99} {
|
||||||
|
args := []reflect.Value{
|
||||||
|
reflect.ValueOf(x).Convert(xt),
|
||||||
|
reflect.ValueOf(s).Convert(st),
|
||||||
|
}
|
||||||
|
if s < 0 {
|
||||||
|
shouldPanic(func() {
|
||||||
|
f.Call(args)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
f.Call(args) // should not panic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldPanic(f func()) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
panic("did not panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f()
|
||||||
|
}
|
Loading…
Reference in a new issue