@@ -19,33 +19,34 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
1919 return
2020 }
2121
22- // Sometimes, the index can be e.g. an uint8 or int8, and we have to
23- // correctly extend that type.
2422 if index .Type ().IntTypeWidth () < arrayLen .Type ().IntTypeWidth () {
23+ // Sometimes, the index can be e.g. an uint8 or int8, and we have to
24+ // correctly extend that type.
2525 if indexType .(* types.Basic ).Info ()& types .IsUnsigned == 0 {
2626 index = c .builder .CreateZExt (index , arrayLen .Type (), "" )
2727 } else {
2828 index = c .builder .CreateSExt (index , arrayLen .Type (), "" )
2929 }
30+ } else if index .Type ().IntTypeWidth () > arrayLen .Type ().IntTypeWidth () {
31+ // The index is bigger than the array length type, so extend it.
32+ arrayLen = c .builder .CreateZExt (arrayLen , index .Type (), "" )
3033 }
3134
32- // Optimize away trivial cases.
33- // LLVM would do this anyway with interprocedural optimizations, but it
34- // helps to see cases where bounds check elimination would really help.
35- if index .IsConstant () && arrayLen .IsConstant () && ! arrayLen .IsUndef () {
36- index := index .SExtValue ()
37- arrayLen := arrayLen .SExtValue ()
38- if index >= 0 && index < arrayLen {
39- return
40- }
41- }
35+ faultBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "lookup.outofbounds" )
36+ nextBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "lookup.next" )
37+ frame .blockExits [frame .currentBlock ] = nextBlock // adjust outgoing block for phi nodes
4238
43- if index .Type ().IntTypeWidth () > c .intType .IntTypeWidth () {
44- // Index is too big for the regular bounds check. Use the one for int64.
45- c .createRuntimeCall ("lookupBoundsCheckLong" , []llvm.Value {arrayLen , index }, "" )
46- } else {
47- c .createRuntimeCall ("lookupBoundsCheck" , []llvm.Value {arrayLen , index }, "" )
48- }
39+ // Now do the bounds check: index >= arrayLen
40+ outOfBounds := c .builder .CreateICmp (llvm .IntUGE , index , arrayLen , "" )
41+ c .builder .CreateCondBr (outOfBounds , faultBlock , nextBlock )
42+
43+ // Fail: this is a nil pointer, exit with a panic.
44+ c .builder .SetInsertPointAtEnd (faultBlock )
45+ c .createRuntimeCall ("lookuppanic" , nil , "" )
46+ c .builder .CreateUnreachable ()
47+
48+ // Ok: this is a valid pointer.
49+ c .builder .SetInsertPointAtEnd (nextBlock )
4950}
5051
5152// emitSliceBoundsCheck emits a bounds check before a slicing operation to make
@@ -57,27 +58,51 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
5758 return
5859 }
5960
60- uintptrWidth := c .uintptrType .IntTypeWidth ()
61- if low .Type ().IntTypeWidth () > uintptrWidth || high .Type ().IntTypeWidth () > uintptrWidth {
62- if low .Type ().IntTypeWidth () < 64 {
63- if lowType .Info ()& types .IsUnsigned != 0 {
64- low = c .builder .CreateZExt (low , c .ctx .Int64Type (), "" )
65- } else {
66- low = c .builder .CreateSExt (low , c .ctx .Int64Type (), "" )
67- }
61+ // Extend the capacity integer to be at least as wide as low and high.
62+ capacityType := capacity .Type ()
63+ if low .Type ().IntTypeWidth () > capacityType .IntTypeWidth () {
64+ capacityType = low .Type ()
65+ }
66+ if high .Type ().IntTypeWidth () > capacityType .IntTypeWidth () {
67+ capacityType = high .Type ()
68+ }
69+ if capacityType != capacity .Type () {
70+ capacity = c .builder .CreateZExt (capacity , capacityType , "" )
71+ }
72+
73+ // Extend low and high to be the same size as capacity.
74+ if low .Type ().IntTypeWidth () < capacityType .IntTypeWidth () {
75+ if lowType .Info ()& types .IsUnsigned != 0 {
76+ low = c .builder .CreateZExt (low , capacityType , "" )
77+ } else {
78+ low = c .builder .CreateSExt (low , capacityType , "" )
6879 }
69- if high . Type (). IntTypeWidth () < 64 {
70- if highType . Info () & types . IsUnsigned != 0 {
71- high = c . builder . CreateZExt ( high , c . ctx . Int64Type (), "" )
72- } else {
73- high = c . builder . CreateSExt ( high , c . ctx . Int64Type (), "" )
74- }
80+ }
81+ if high . Type (). IntTypeWidth () < capacityType . IntTypeWidth () {
82+ if highType . Info () & types . IsUnsigned != 0 {
83+ high = c . builder . CreateZExt ( high , capacityType , "" )
84+ } else {
85+ high = c . builder . CreateSExt ( high , capacityType , "" )
7586 }
76- // TODO: 32-bit or even 16-bit slice bounds checks for 8-bit platforms
77- c .createRuntimeCall ("sliceBoundsCheck64" , []llvm.Value {capacity , low , high }, "" )
78- } else {
79- c .createRuntimeCall ("sliceBoundsCheck" , []llvm.Value {capacity , low , high }, "" )
8087 }
88+
89+ faultBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "slice.outofbounds" )
90+ nextBlock := c .ctx .AddBasicBlock (frame .fn .LLVMFn , "slice.next" )
91+ frame .blockExits [frame .currentBlock ] = nextBlock // adjust outgoing block for phi nodes
92+
93+ // Now do the bounds check: low > high || high > capacity
94+ outOfBounds1 := c .builder .CreateICmp (llvm .IntUGT , low , high , "slice.lowhigh" )
95+ outOfBounds2 := c .builder .CreateICmp (llvm .IntUGT , high , capacity , "slice.highcap" )
96+ outOfBounds := c .builder .CreateOr (outOfBounds1 , outOfBounds2 , "slice.outofbounds" )
97+ c .builder .CreateCondBr (outOfBounds , faultBlock , nextBlock )
98+
99+ // Fail: this is a nil pointer, exit with a panic.
100+ c .builder .SetInsertPointAtEnd (faultBlock )
101+ c .createRuntimeCall ("slicepanic" , nil , "" )
102+ c .builder .CreateUnreachable ()
103+
104+ // Ok: this is a valid pointer.
105+ c .builder .SetInsertPointAtEnd (nextBlock )
81106}
82107
83108// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
0 commit comments