diff options
author | Ayke van Laethem <[email protected]> | 2021-11-11 20:24:47 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-11-13 11:04:24 +0100 |
commit | c1d697f86886a29e46148e80a4cd796598d0d5fd (patch) | |
tree | a4f6c059412ed009a9b041a34c7c1e1d84ade2c6 /compiler/asserts.go | |
parent | 335fb71d2ff561fb02bf4d3dd16588b07a76abe6 (diff) | |
download | tinygo-c1d697f86886a29e46148e80a4cd796598d0d5fd.tar.gz tinygo-c1d697f86886a29e46148e80a4cd796598d0d5fd.zip |
compiler: fix indices into strings and arrays
This PR fixes two bugs at once:
1. Indices were incorrectly extended to a bigger type. Specifically,
unsigned integers were sign extended and signed integers were zero
extended. This commit swaps them around.
2. The getelementptr instruction was given the raw index, even if it
was a uint8 for example. However, getelementptr assumes the indices
are signed, and therefore an index of uint8(200) was interpreted as
an index of int8(-56).
Diffstat (limited to 'compiler/asserts.go')
-rw-r--r-- | compiler/asserts.go | 75 |
1 files changed, 26 insertions, 49 deletions
diff --git a/compiler/asserts.go b/compiler/asserts.go index 92083bc6f..acd605fab 100644 --- a/compiler/asserts.go +++ b/compiler/asserts.go @@ -15,22 +15,16 @@ import ( // createLookupBoundsCheck emits a bounds check before doing a lookup into a // slice. This is required by the Go language spec: an index out of bounds must // cause a panic. -func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) { +// The caller should make sure that index is at least as big as arrayLen. +func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) { if b.info.nobounds { // The //go:nobounds pragma was added to the function to avoid bounds // checking. return } - if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() { - // Sometimes, the index can be e.g. an uint8 or int8, and we have to - // correctly extend that type. - if indexType.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 { - index = b.CreateZExt(index, arrayLen.Type(), "") - } else { - index = b.CreateSExt(index, arrayLen.Type(), "") - } - } else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() { + // Extend arrayLen if it's too small. + if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() { // The index is bigger than the array length type, so extend it. arrayLen = b.CreateZExt(arrayLen, index.Type(), "") } @@ -70,27 +64,9 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo } // Extend low and high to be the same size as capacity. - if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() { - if lowType.Info()&types.IsUnsigned != 0 { - low = b.CreateZExt(low, capacityType, "") - } else { - low = b.CreateSExt(low, capacityType, "") - } - } - if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() { - if highType.Info()&types.IsUnsigned != 0 { - high = b.CreateZExt(high, capacityType, "") - } else { - high = b.CreateSExt(high, capacityType, "") - } - } - if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() { - if maxType.Info()&types.IsUnsigned != 0 { - max = b.CreateZExt(max, capacityType, "") - } else { - max = b.CreateSExt(max, capacityType, "") - } - } + low = b.extendInteger(low, lowType, capacityType) + high = b.extendInteger(high, highType, capacityType) + max = b.extendInteger(max, maxType, capacityType) // Now do the bounds check: low > high || high > capacity outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh") @@ -125,13 +101,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas // using an unsiged greater than. // Make sure the len value is at least as big as a uintptr. - if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { - if lenType.Info()&types.IsUnsigned != 0 { - len = b.CreateZExt(len, b.uintptrType, "") - } else { - len = b.CreateSExt(len, b.uintptrType, "") - } - } + len = b.extendInteger(len, lenType, b.uintptrType) // Determine the maximum slice size, and therefore the maximum value of the // len parameter. @@ -159,17 +129,8 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, return } - // Check whether the bufSize parameter must be cast to a wider integer for - // comparison. - if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { - if bufSizeType.Info()&types.IsUnsigned != 0 { - // Unsigned, so zero-extend to uint type. - bufSize = b.CreateZExt(bufSize, b.intType, "") - } else { - // Signed, so sign-extend to int type. - bufSize = b.CreateSExt(bufSize, b.intType, "") - } - } + // Make sure bufSize is at least as big as maxBufSize (an uintptr). + bufSize = b.extendInteger(bufSize, bufSizeType, b.uintptrType) // Calculate (^uintptr(0)) >> 1, which is the max value that fits in an // uintptr if uintptrs were signed. @@ -294,3 +255,19 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc // Ok: assert didn't trigger so continue normally. b.SetInsertPointAtEnd(nextBlock) } + +// extendInteger extends the value to at least targetType using a zero or sign +// extend. The resulting value is not truncated: it may still be bigger than +// targetType. +func (b *builder) extendInteger(value llvm.Value, valueType types.Type, targetType llvm.Type) llvm.Value { + if value.Type().IntTypeWidth() < targetType.IntTypeWidth() { + if valueType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 { + // Unsigned, so zero-extend to the target type. + value = b.CreateZExt(value, targetType, "") + } else { + // Signed, so sign-extend to the target type. + value = b.CreateSExt(value, targetType, "") + } + } + return value +} |