Skip to content

Commit 3024785

Browse files
committed
cmd/compile,runtime: remember idx+len for bounds check failure with less code
Currently we must put the index and length into specific registers so we can call into the runtime to report a bounds check failure. So a typical bounds check call is something like: MOVD R3, R0 MOVD R7, R1 CALL runtime.panicIndex or, if for instance the index is constant, MOVD $7, R0 MOVD R9, R1 CALL runtime.panicIndex Sometimes the MOVD can be avoided, if the value happens to be in the right register already. But that's not terribly common, and doesn't work at all for constants. Let's get rid of those MOVD instructions. They pollute the instruction cache and are almost never executed. Instead, we'll encode in a PCDATA table where the runtime should find the index and length. The table encodes, for each index and length, whether it is a constant or in a register, and which register or constant it is. That way, we can avoid all those useless MOVDs. Instead, we can figure out the index and length at runtime. This makes the bounds panic path slower, but that's a good tradeoff. We can encode registers 0-15 and constants 0-31. Anything outside that range still needs to use an explicit instruction. This CL is the foundation, followon CLs will move each architecture to the new strategy. Change-Id: I705c511e546e6aac59fed922a8eaed4585e96820 Reviewed-on: https://go-review.googlesource.com/c/go/+/682396 Reviewed-by: Michael Knyszek <[email protected]> Reviewed-by: David Chase <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 741a19a commit 3024785

File tree

9 files changed

+287
-2
lines changed

9 files changed

+287
-2
lines changed

src/cmd/compile/internal/ir/symtab.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ type symsStruct struct {
3737
Msanmove *obj.LSym
3838
Newobject *obj.LSym
3939
Newproc *obj.LSym
40+
PanicBounds *obj.LSym
41+
PanicExtend *obj.LSym
4042
Panicdivide *obj.LSym
4143
Panicshift *obj.LSym
4244
PanicdottypeE *obj.LSym

src/cmd/compile/internal/ssa/_gen/rulegen.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,13 @@ func (u *unusedInspector) node(node ast.Node) {
540540
}
541541
}
542542
case *ast.BasicLit:
543+
case *ast.CompositeLit:
544+
for _, e := range node.Elts {
545+
u.node(e)
546+
}
547+
case *ast.KeyValueExpr:
548+
u.node(node.Key)
549+
u.node(node.Value)
543550
case *ast.ValueSpec:
544551
u.exprs(node.Values)
545552
default:
@@ -1431,7 +1438,8 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi
14311438
func opHasAuxInt(op opData) bool {
14321439
switch op.aux {
14331440
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64",
1434-
"SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop":
1441+
"SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop",
1442+
"PanicBoundsC", "PanicBoundsCC":
14351443
return true
14361444
}
14371445
return false
@@ -1440,7 +1448,7 @@ func opHasAuxInt(op opData) bool {
14401448
func opHasAux(op opData) bool {
14411449
switch op.aux {
14421450
case "String", "Sym", "SymOff", "Call", "CallOff", "SymValAndOff", "Typ", "TypSize",
1443-
"S390XCCMask", "S390XRotateParams":
1451+
"S390XCCMask", "S390XRotateParams", "PanicBoundsC", "PanicBoundsCC":
14441452
return true
14451453
}
14461454
return false
@@ -1795,6 +1803,10 @@ func (op opData) auxType() string {
17951803
return "s390x.CCMask"
17961804
case "S390XRotateParams":
17971805
return "s390x.RotateParams"
1806+
case "PanicBoundsC":
1807+
return "PanicBoundsC"
1808+
case "PanicBoundsCC":
1809+
return "PanicBoundsCC"
17981810
default:
17991811
return "invalid"
18001812
}
@@ -1835,6 +1847,8 @@ func (op opData) auxIntType() string {
18351847
return "flagConstant"
18361848
case "ARM64BitField":
18371849
return "arm64BitField"
1850+
case "PanicBoundsC", "PanicBoundsCC":
1851+
return "int64"
18381852
default:
18391853
return "invalid"
18401854
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ func checkFunc(f *Func) {
215215
f.Fatalf("bad FlagConstant AuxInt value for %v", v)
216216
}
217217
canHaveAuxInt = true
218+
case auxPanicBoundsC, auxPanicBoundsCC:
219+
canHaveAux = true
220+
canHaveAuxInt = true
218221
default:
219222
f.Fatalf("unknown aux type for %s", v.Op)
220223
}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package ssa
66

77
import (
88
"cmd/compile/internal/abi"
9+
"cmd/compile/internal/base"
910
"cmd/compile/internal/ir"
1011
"cmd/compile/internal/types"
1112
"cmd/internal/obj"
1213
"fmt"
14+
rtabi "internal/abi"
1315
"strings"
1416
)
1517

@@ -365,6 +367,9 @@ const (
365367
auxCall // aux is a *ssa.AuxCall
366368
auxCallOff // aux is a *ssa.AuxCall, AuxInt is int64 param (in+out) size
367369

370+
auxPanicBoundsC // constant for a bounds failure
371+
auxPanicBoundsCC // two constants for a bounds failure
372+
368373
// architecture specific aux types
369374
auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt
370375
auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount
@@ -523,6 +528,50 @@ func boundsABI(b int64) int {
523528
}
524529
}
525530

531+
// Returns the bounds error code needed by the runtime, and
532+
// whether the x field is signed.
533+
func (b BoundsKind) Code() (rtabi.BoundsErrorCode, bool) {
534+
switch b {
535+
case BoundsIndex:
536+
return rtabi.BoundsIndex, true
537+
case BoundsIndexU:
538+
return rtabi.BoundsIndex, false
539+
case BoundsSliceAlen:
540+
return rtabi.BoundsSliceAlen, true
541+
case BoundsSliceAlenU:
542+
return rtabi.BoundsSliceAlen, false
543+
case BoundsSliceAcap:
544+
return rtabi.BoundsSliceAcap, true
545+
case BoundsSliceAcapU:
546+
return rtabi.BoundsSliceAcap, false
547+
case BoundsSliceB:
548+
return rtabi.BoundsSliceB, true
549+
case BoundsSliceBU:
550+
return rtabi.BoundsSliceB, false
551+
case BoundsSlice3Alen:
552+
return rtabi.BoundsSlice3Alen, true
553+
case BoundsSlice3AlenU:
554+
return rtabi.BoundsSlice3Alen, false
555+
case BoundsSlice3Acap:
556+
return rtabi.BoundsSlice3Acap, true
557+
case BoundsSlice3AcapU:
558+
return rtabi.BoundsSlice3Acap, false
559+
case BoundsSlice3B:
560+
return rtabi.BoundsSlice3B, true
561+
case BoundsSlice3BU:
562+
return rtabi.BoundsSlice3B, false
563+
case BoundsSlice3C:
564+
return rtabi.BoundsSlice3C, true
565+
case BoundsSlice3CU:
566+
return rtabi.BoundsSlice3C, false
567+
case BoundsConvert:
568+
return rtabi.BoundsConvert, false
569+
default:
570+
base.Fatalf("bad bounds kind %d", b)
571+
return 0, false
572+
}
573+
}
574+
526575
// arm64BitField is the GO type of ARM64BitField auxInt.
527576
// if x is an ARM64BitField, then width=x&0xff, lsb=(x>>8)&0xff, and
528577
// width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,3 +2673,32 @@ func flagify(v *Value) bool {
26732673
v.AddArg(inner)
26742674
return true
26752675
}
2676+
2677+
// PanicBoundsC contains a constant for a bounds failure.
2678+
type PanicBoundsC struct {
2679+
C int64
2680+
}
2681+
2682+
// PanicBoundsCC contains 2 constants for a bounds failure.
2683+
type PanicBoundsCC struct {
2684+
Cx int64
2685+
Cy int64
2686+
}
2687+
2688+
func (p PanicBoundsC) CanBeAnSSAAux() {
2689+
}
2690+
func (p PanicBoundsCC) CanBeAnSSAAux() {
2691+
}
2692+
2693+
func auxToPanicBoundsC(i Aux) PanicBoundsC {
2694+
return i.(PanicBoundsC)
2695+
}
2696+
func auxToPanicBoundsCC(i Aux) PanicBoundsCC {
2697+
return i.(PanicBoundsCC)
2698+
}
2699+
func panicBoundsCToAux(p PanicBoundsC) Aux {
2700+
return p
2701+
}
2702+
func panicBoundsCCToAux(p PanicBoundsCC) Aux {
2703+
return p
2704+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ func InitConfig() {
137137
ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite")
138138
ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject")
139139
ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc")
140+
ir.Syms.PanicBounds = typecheck.LookupRuntimeFunc("panicBounds")
141+
ir.Syms.PanicExtend = typecheck.LookupRuntimeFunc("panicExtend")
140142
ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide")
141143
ir.Syms.PanicdottypeE = typecheck.LookupRuntimeFunc("panicdottypeE")
142144
ir.Syms.PanicdottypeI = typecheck.LookupRuntimeFunc("panicdottypeI")

src/internal/abi/bounds.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,96 @@ const (
1818
BoundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen)
1919
BoundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen)
2020
BoundsConvert // (*[x]T)(s), 0 <= x <= len(s) failed
21+
numBoundsCodes
2122
)
23+
24+
const (
25+
BoundsMaxReg = 15
26+
BoundsMaxConst = 31
27+
)
28+
29+
// Here's how we encode PCDATA_PanicBounds entries:
30+
31+
// We allow 16 registers (0-15) and 32 constants (0-31).
32+
// Encode the following constant c:
33+
// bits use
34+
// -----------------------------
35+
// 0 x is in a register
36+
// 1 y is in a register
37+
//
38+
// if x is in a register
39+
// 2 x is signed
40+
// [3:6] x's register number
41+
// else
42+
// [2:6] x's constant value
43+
//
44+
// if y is in a register
45+
// [7:10] y's register number
46+
// else
47+
// [7:11] y's constant value
48+
//
49+
// The final integer is c * numBoundsCode + code
50+
51+
// TODO: 32-bit
52+
53+
// Encode bounds failure information into an integer for PCDATA_PanicBounds.
54+
// Register numbers must be in 0-15. Constants must be in 0-31.
55+
func BoundsEncode(code BoundsErrorCode, signed, xIsReg, yIsReg bool, xVal, yVal int) int {
56+
c := int(0)
57+
if xIsReg {
58+
c |= 1 << 0
59+
if signed {
60+
c |= 1 << 2
61+
}
62+
if xVal < 0 || xVal > BoundsMaxReg {
63+
panic("bad xReg")
64+
}
65+
c |= xVal << 3
66+
} else {
67+
if xVal < 0 || xVal > BoundsMaxConst {
68+
panic("bad xConst")
69+
}
70+
c |= xVal << 2
71+
}
72+
if yIsReg {
73+
c |= 1 << 1
74+
if yVal < 0 || yVal > BoundsMaxReg {
75+
panic("bad yReg")
76+
}
77+
c |= yVal << 7
78+
} else {
79+
if yVal < 0 || yVal > BoundsMaxConst {
80+
panic("bad yConst")
81+
}
82+
c |= yVal << 7
83+
}
84+
return c*int(numBoundsCodes) + int(code)
85+
}
86+
func BoundsDecode(v int) (code BoundsErrorCode, signed, xIsReg, yIsReg bool, xVal, yVal int) {
87+
code = BoundsErrorCode(v % int(numBoundsCodes))
88+
c := v / int(numBoundsCodes)
89+
xIsReg = c&1 != 0
90+
c >>= 1
91+
yIsReg = c&1 != 0
92+
c >>= 1
93+
if xIsReg {
94+
signed = c&1 != 0
95+
c >>= 1
96+
xVal = c & 0xf
97+
c >>= 4
98+
} else {
99+
xVal = c & 0x1f
100+
c >>= 5
101+
}
102+
if yIsReg {
103+
yVal = c & 0xf
104+
c >>= 4
105+
} else {
106+
yVal = c & 0x1f
107+
c >>= 5
108+
}
109+
if c != 0 {
110+
panic("BoundsDecode decoding error")
111+
}
112+
return
113+
}

src/internal/abi/symtab.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const (
7979
PCDATA_StackMapIndex = 1
8080
PCDATA_InlTreeIndex = 2
8181
PCDATA_ArgLiveIndex = 3
82+
PCDATA_PanicBounds = 4
8283

8384
FUNCDATA_ArgsPointerMaps = 0
8485
FUNCDATA_LocalsPointerMaps = 1

src/runtime/panic.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,99 @@ func panicSlice3C(x int, y int)
225225
func panicSlice3CU(x uint, y int)
226226
func panicSliceConvert(x int, y int)
227227

228+
func panicBounds() // in asm_GOARCH.s files, called from generated code
229+
func panicExtend() // in asm_GOARCH.s files, called from generated code (on 32-bit archs)
230+
func panicBounds64(pc uintptr, regs *[16]int64) { // called from panicBounds on 64-bit archs
231+
f := findfunc(pc)
232+
v := pcdatavalue(f, abi.PCDATA_PanicBounds, pc-1)
233+
234+
code, signed, xIsReg, yIsReg, xVal, yVal := abi.BoundsDecode(int(v))
235+
236+
if code == abi.BoundsIndex {
237+
panicCheck1(pc, "index out of range")
238+
} else {
239+
panicCheck1(pc, "slice bounds out of range")
240+
}
241+
242+
var e boundsError
243+
e.code = code
244+
e.signed = signed
245+
if xIsReg {
246+
e.x = regs[xVal]
247+
} else {
248+
e.x = int64(xVal)
249+
}
250+
if yIsReg {
251+
e.y = int(regs[yVal])
252+
} else {
253+
e.y = yVal
254+
}
255+
panic(e)
256+
}
257+
258+
func panicBounds32(pc uintptr, regs *[16]int32) { // called from panicBounds on 32-bit archs
259+
f := findfunc(pc)
260+
v := pcdatavalue(f, abi.PCDATA_PanicBounds, pc-1)
261+
262+
code, signed, xIsReg, yIsReg, xVal, yVal := abi.BoundsDecode(int(v))
263+
264+
if code == abi.BoundsIndex {
265+
panicCheck1(pc, "index out of range")
266+
} else {
267+
panicCheck1(pc, "slice bounds out of range")
268+
}
269+
270+
var e boundsError
271+
e.code = code
272+
e.signed = signed
273+
if xIsReg {
274+
if signed {
275+
e.x = int64(regs[xVal])
276+
} else {
277+
e.x = int64(uint32(regs[xVal]))
278+
}
279+
} else {
280+
e.x = int64(xVal)
281+
}
282+
if yIsReg {
283+
e.y = int(regs[yVal])
284+
} else {
285+
e.y = yVal
286+
}
287+
panic(e)
288+
}
289+
290+
func panicBounds32X(pc uintptr, regs *[16]int32) { // called from panicExtend on 32-bit archs
291+
f := findfunc(pc)
292+
v := pcdatavalue(f, abi.PCDATA_PanicBounds, pc-1)
293+
294+
code, signed, xIsReg, yIsReg, xVal, yVal := abi.BoundsDecode(int(v))
295+
296+
if code == abi.BoundsIndex {
297+
panicCheck1(pc, "index out of range")
298+
} else {
299+
panicCheck1(pc, "slice bounds out of range")
300+
}
301+
302+
var e boundsError
303+
e.code = code
304+
e.signed = signed
305+
if xIsReg {
306+
// Our 4-bit register numbers are actually 2 2-bit register numbers.
307+
lo := xVal & 3
308+
hi := xVal >> 2
309+
e.x = int64(regs[hi])<<32 + int64(uint32(regs[lo]))
310+
} else {
311+
e.x = int64(xVal)
312+
}
313+
if yIsReg {
314+
e.y = int(regs[yVal])
315+
} else {
316+
e.y = yVal
317+
}
318+
panic(e)
319+
}
320+
228321
var shiftError = error(errorString("negative shift amount"))
229322

230323
//go:yeswritebarrierrec

0 commit comments

Comments
 (0)