Skip to content

Commit 585c9e8

Browse files
committed
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 #19113 (comment) 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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Josh Bleecher Snyder <[email protected]>
1 parent e1acd85 commit 585c9e8

File tree

10 files changed

+144
-15
lines changed

10 files changed

+144
-15
lines changed

src/cmd/compile/internal/gc/builtin.go

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/gc/builtin/runtime.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func newobject(typ *byte) *any
1818
func panicindex()
1919
func panicslice()
2020
func panicdivide()
21+
func panicshift()
2122
func panicmakeslicelen()
2223
func throwinit()
2324
func panicwrap()

src/cmd/compile/internal/gc/go.go

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ var (
296296
msanwrite,
297297
newproc,
298298
panicdivide,
299+
panicshift,
299300
panicdottypeE,
300301
panicdottypeI,
301302
panicindex,

src/cmd/compile/internal/gc/ssa.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func initssaconfig() {
8484
panicnildottype = sysfunc("panicnildottype")
8585
panicoverflow = sysfunc("panicoverflow")
8686
panicslice = sysfunc("panicslice")
87+
panicshift = sysfunc("panicshift")
8788
raceread = sysfunc("raceread")
8889
racereadrange = sysfunc("racereadrange")
8990
racewrite = sysfunc("racewrite")
@@ -2128,7 +2129,13 @@ func (s *state) expr(n *Node) *ssa.Value {
21282129
case OLSH, ORSH:
21292130
a := s.expr(n.Left)
21302131
b := s.expr(n.Right)
2131-
return s.newValue2(s.ssaShiftOp(n.Op, n.Type, n.Right.Type), a.Type, a, b)
2132+
bt := b.Type
2133+
if bt.IsSigned() {
2134+
cmp := s.newValue2(s.ssaOp(OGE, bt), types.Types[TBOOL], b, s.zeroVal(bt))
2135+
s.check(cmp, panicshift)
2136+
bt = bt.ToUnsigned()
2137+
}
2138+
return s.newValue2(s.ssaShiftOp(n.Op, n.Type, bt), a.Type, a, b)
21322139
case OANDAND, OOROR:
21332140
// To implement OANDAND (and OOROR), we introduce a
21342141
// new temporary variable to hold the result. The

src/cmd/compile/internal/gc/typecheck.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,8 @@ func typecheck1(n *Node, top int) (res *Node) {
660660
r = defaultlit(r, types.Types[TUINT])
661661
n.Right = r
662662
t := r.Type
663-
if !t.IsInteger() || t.IsSigned() {
664-
yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type)
663+
if !t.IsInteger() {
664+
yyerror("invalid operation: %v (shift count type %v, must be integer)", n, r.Type)
665665
n.Type = nil
666666
return n
667667
}

src/cmd/compile/internal/ssa/rewrite.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,8 @@ func needRaceCleanup(sym interface{}, v *Value) bool {
11151115
case OpStaticCall:
11161116
switch v.Aux.(fmt.Stringer).String() {
11171117
case "runtime.racefuncenter", "runtime.racefuncexit", "runtime.panicindex",
1118-
"runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap":
1118+
"runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap",
1119+
"runtime.panicshift":
11191120
// Check for racefuncenter will encounter racefuncexit and vice versa.
11201121
// Allow calls to panic*
11211122
default:

src/cmd/internal/obj/x86/obj6.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ func isZeroArgRuntimeCall(s *obj.LSym) bool {
968968
return false
969969
}
970970
switch s.Name {
971-
case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap":
971+
case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap", "runtime.panicshift":
972972
return true
973973
}
974974
return false

src/runtime/panic.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ func panicCheckMalloc(err error) {
2323

2424
var indexError = error(errorString("index out of range"))
2525

26-
// The panicindex, panicslice, and panicdivide functions are called by
26+
// The panic{index,slice,divide,shift} functions are called by
2727
// code generated by the compiler for out of bounds index expressions,
28-
// out of bounds slice expressions, and division by zero. The
29-
// panicdivide (again), panicoverflow, panicfloat, and panicmem
28+
// out of bounds slice expressions, division by zero, and shift by negative.
29+
// The panicdivide (again), panicoverflow, panicfloat, and panicmem
3030
// functions are called by the signal handler when a signal occurs
3131
// indicating the respective problem.
3232
//
33-
// Since panicindex and panicslice are never called directly, and
33+
// Since panic{index,slice,shift} are never called directly, and
3434
// since the runtime package should never have an out of bounds slice
35-
// or array reference, if we see those functions called from the
35+
// or array reference or negative shift, if we see those functions called from the
3636
// runtime package we turn the panic into a throw. That will dump the
3737
// entire runtime stack for easier debugging.
3838

@@ -68,6 +68,16 @@ func panicoverflow() {
6868
panic(overflowError)
6969
}
7070

71+
var shiftError = error(errorString("negative shift amount"))
72+
73+
func panicshift() {
74+
if hasPrefix(funcname(findfunc(getcallerpc())), "runtime.") {
75+
throw(string(shiftError.(errorString)))
76+
}
77+
panicCheckMalloc(shiftError)
78+
panic(shiftError)
79+
}
80+
7181
var floatError = error(errorString("floating point error"))
7282

7383
func panicfloat() {

test/fixedbugs/bug073.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// errorcheck
1+
// compile
22

33
// Copyright 2009 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
@@ -7,8 +7,8 @@
77
package main
88

99
func main() {
10-
var s int = 0;
11-
var x int = 0;
12-
x = x << s; // ERROR "illegal|inval|shift"
13-
x = x >> s; // ERROR "illegal|inval|shift"
10+
var s int = 0
11+
var x int = 0
12+
x = x << s // as of 1.13, these are ok
13+
x = x >> s // as of 1.13, these are ok
1414
}

test/fixedbugs/issue19113.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// run
2+
3+
// Copyright 2019 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import "reflect"
10+
11+
var tests = []interface{}{
12+
func(x int, s int) int {
13+
return x << s
14+
},
15+
func(x int, s int64) int {
16+
return x << s
17+
},
18+
func(x int, s int32) int {
19+
return x << s
20+
},
21+
func(x int, s int16) int {
22+
return x << s
23+
},
24+
func(x int, s int8) int {
25+
return x << s
26+
},
27+
func(x int, s int) int {
28+
return x >> s
29+
},
30+
func(x int, s int64) int {
31+
return x >> s
32+
},
33+
func(x int, s int32) int {
34+
return x >> s
35+
},
36+
func(x int, s int16) int {
37+
return x >> s
38+
},
39+
func(x int, s int8) int {
40+
return x >> s
41+
},
42+
func(x uint, s int) uint {
43+
return x << s
44+
},
45+
func(x uint, s int64) uint {
46+
return x << s
47+
},
48+
func(x uint, s int32) uint {
49+
return x << s
50+
},
51+
func(x uint, s int16) uint {
52+
return x << s
53+
},
54+
func(x uint, s int8) uint {
55+
return x << s
56+
},
57+
func(x uint, s int) uint {
58+
return x >> s
59+
},
60+
func(x uint, s int64) uint {
61+
return x >> s
62+
},
63+
func(x uint, s int32) uint {
64+
return x >> s
65+
},
66+
func(x uint, s int16) uint {
67+
return x >> s
68+
},
69+
func(x uint, s int8) uint {
70+
return x >> s
71+
},
72+
}
73+
74+
func main() {
75+
for _, t := range tests {
76+
runTest(reflect.ValueOf(t))
77+
}
78+
}
79+
80+
func runTest(f reflect.Value) {
81+
xt := f.Type().In(0)
82+
st := f.Type().In(1)
83+
84+
for _, x := range []int{1, 0, -1} {
85+
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} {
86+
args := []reflect.Value{
87+
reflect.ValueOf(x).Convert(xt),
88+
reflect.ValueOf(s).Convert(st),
89+
}
90+
if s < 0 {
91+
shouldPanic(func() {
92+
f.Call(args)
93+
})
94+
} else {
95+
f.Call(args) // should not panic
96+
}
97+
}
98+
}
99+
}
100+
101+
func shouldPanic(f func()) {
102+
defer func() {
103+
if recover() == nil {
104+
panic("did not panic")
105+
}
106+
}()
107+
f()
108+
}

0 commit comments

Comments
 (0)