@@ -19,33 +19,34 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
19
19
return
20
20
}
21
21
22
- // Sometimes, the index can be e.g. an uint8 or int8, and we have to
23
- // correctly extend that type.
24
22
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.
25
25
if indexType .(* types.Basic ).Info ()& types .IsUnsigned == 0 {
26
26
index = c .builder .CreateZExt (index , arrayLen .Type (), "" )
27
27
} else {
28
28
index = c .builder .CreateSExt (index , arrayLen .Type (), "" )
29
29
}
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 (), "" )
30
33
}
31
34
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
42
38
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 )
49
50
}
50
51
51
52
// 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
57
58
return
58
59
}
59
60
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 , "" )
68
79
}
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 , "" )
75
86
}
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 }, "" )
80
87
}
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 )
81
106
}
82
107
83
108
// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
0 commit comments