Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions builder/testdata/binary-size.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
target package opt code rodata data bss
hifive1b examples/echo z 4558 402 0 2276
microbit examples/serial z 3001 439 8 2272
wioterminal examples/pininterrupt z 7866 1686 136 7280
pico examples/serial z 7737 1071 112 5616
pico examples/serial 2 16346 862 108 5616
pico2 examples/serial z 6532 1172 112 5608
pico2 examples/serial 2 15957 979 108 5608
wioterminal examples/pininterrupt z 7848 1688 136 7280
pico examples/serial z 7721 1071 112 5616
pico examples/serial 2 16362 862 108 5616
pico2 examples/serial z 6514 1174 112 5608
pico2 examples/serial 2 15975 977 108 5608
esp32c3-generic examples/serial z 5844 772 0 8664
esp32c3-generic examples/serial 2 9956 604 8 8672
cortex-m-qemu ./testdata/size-slice z 3087 793 4 4168
cortex-m-qemu ./testdata/size-slice 2 7331 341 4 4168
cortex-m-qemu ./testdata/size-defer z 3782 818 4 4176
cortex-m-qemu ./testdata/size-contains z 3093 763 4 4200
pico2 ./testdata/size-contains z 6690 1158 112 5632
pico2 ./testdata/size-os z 6201 1207 128 5592
pico2 ./testdata/size-os 2 15272 1016 128 5592
cortex-m-qemu ./testdata/size-corpus z 9907 1301 80 4188
cortex-m-qemu ./testdata/size-corpus 2 17482 862 72 4188
riscv-qemu ./testdata/size-corpus z 17540 1028 88 16660
pico2 ./testdata/size-contains z 6672 1160 112 5632
pico2 ./testdata/size-os z 6199 1209 128 5592
pico2 ./testdata/size-os 2 15290 1014 128 5592
cortex-m-qemu ./testdata/size-corpus z 9891 1301 80 4188
cortex-m-qemu ./testdata/size-corpus 2 17466 862 72 4188
riscv-qemu ./testdata/size-corpus z 17508 1028 88 16660
riscv-qemu ./testdata/size-corpus 2 25160 744 80 16644
22 changes: 15 additions & 7 deletions compiler/asserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
// normal meaning) and for creating a new slice, where 'capacity' means the
// biggest possible slice capacity, 'low' means len and 'high' means cap. The
// logic is the same in both cases.
func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) {
//
// highLeMax must only be true if high is the length and max is the capacity of
// the same slice, so high > max cannot happen.
func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic, highLeMax bool) {
if b.info.nobounds {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
Expand All @@ -68,12 +71,17 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
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")
outOfBounds2 := b.CreateICmp(llvm.IntUGT, high, max, "slice.highmax")
outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap")
outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax")
outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap")
// Now do the bounds check: low > high || high > max || max > capacity.
// Checks that can never fail are left out.
outOfBounds := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
if !highLeMax && high != max {
outOfBounds2 := b.CreateICmp(llvm.IntUGT, high, max, "slice.highmax")
outOfBounds = b.CreateOr(outOfBounds, outOfBounds2, "slice.lowmax")
}
if max != capacity {
outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap")
outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap")
}
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2860,7 +2860,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
capType := expr.Cap.Type().Underlying().(*types.Basic)
maxAllocationSize := b.maxSliceAllocationSize(llvmElemType)
maxSizeValue := llvm.ConstInt(b.uintptrType, maxAllocationSize, false)
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType, false)

// Allocate the backing array.
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
Expand Down Expand Up @@ -2970,7 +2970,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
}

b.createNilCheck(expr.X, value, "slice")
b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType)
b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType, false)

// Truncate ints bigger than uintptr. This is after the bounds
// check so it's safe.
Expand Down Expand Up @@ -3010,7 +3010,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
max = oldCap
}

b.createSliceBoundsCheck(oldCap, low, high, max, lowType, highType, maxType)
b.createSliceBoundsCheck(oldCap, low, high, max, lowType, highType, maxType, expr.High == nil && expr.Max == nil)

// Truncate ints bigger than uintptr. This is after the bounds
// check so it's safe.
Expand Down Expand Up @@ -3054,7 +3054,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
high = oldLen
}

b.createSliceBoundsCheck(oldLen, low, high, high, lowType, highType, maxType)
b.createSliceBoundsCheck(oldLen, low, high, high, lowType, highType, maxType, false)

// Truncate ints bigger than uintptr. This is after the bounds
// check so it's safe.
Expand Down
14 changes: 14 additions & 0 deletions compiler/testdata/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ func sliceCopy(dst, src []int) int {
return copy(dst, src)
}

// Slicing a slice only emits the bounds checks that can fail.

func sliceFrom(ints []int, low int) {
_ = ints[low:]
}

func sliceRange(ints []int, low, high int) {
_ = ints[low:high]
}

func sliceRangeMax(ints []int, low, high, max int) {
_ = ints[low:high:max]
}

// Test bounds checking in *ssa.MakeSlice instruction.

func makeByteSlice(len int) []byte {
Expand Down
61 changes: 59 additions & 2 deletions compiler/testdata/slice.ll
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,65 @@ declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4

; Function Attrs: nounwind
define hidden void @main.sliceFrom(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %low, ptr %context) unnamed_addr #1 {
entry:
%slice.lowhigh = icmp ugt i32 %low, %ints.len
br i1 %slice.lowhigh, label %slice.throw, label %slice.next

slice.next: ; preds = %entry
ret void

slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5
br label %unwind.return

unwind.return: ; preds = %slice.throw
ret void
}

declare void @runtime.slicePanic(ptr) #0

; Function Attrs: nounwind
define hidden void @main.sliceRange(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %low, i32 %high, ptr %context) unnamed_addr #1 {
entry:
%slice.lowhigh = icmp ugt i32 %low, %high
%slice.highmax = icmp ugt i32 %high, %ints.cap
%slice.lowmax = or i1 %slice.lowhigh, %slice.highmax
br i1 %slice.lowmax, label %slice.throw, label %slice.next

slice.next: ; preds = %entry
ret void

slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5
br label %unwind.return

unwind.return: ; preds = %slice.throw
ret void
}

; Function Attrs: nounwind
define hidden void @main.sliceRangeMax(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %low, i32 %high, i32 %max, ptr %context) unnamed_addr #1 {
entry:
%slice.lowhigh = icmp ugt i32 %low, %high
%slice.highmax = icmp ugt i32 %high, %max
%slice.lowmax = or i1 %slice.lowhigh, %slice.highmax
%slice.maxcap = icmp ugt i32 %max, %ints.cap
%slice.lowcap = or i1 %slice.lowmax, %slice.maxcap
br i1 %slice.lowcap, label %slice.throw, label %slice.next

slice.next: ; preds = %entry
ret void

slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5
br label %unwind.return

unwind.return: ; preds = %slice.throw
ret void
}

; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
Expand All @@ -124,8 +183,6 @@ unwind.return: ; preds = %slice.throw
ret { ptr, i32, i32 } undef
}

declare void @runtime.slicePanic(ptr) #0

; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 {
entry:
Expand Down
8 changes: 4 additions & 4 deletions compiler/testdata/string.ll
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ slice.next: ; preds = %entry
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 0, i32 %a.cap, ptr undef) #4
%1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #4
br i1 false, label %slice.throw, label %slice.next5
br i1 false, label %slice.throw, label %slice.next3

slice.next5: ; preds = %slice.next
slice.next3: ; preds = %slice.next
call void @runtime.trackPointer(ptr %b.data, ptr nonnull %stackalloc, ptr undef) #4
%2 = extractvalue %runtime._string %0, 0
%3 = extractvalue %runtime._string %0, 1
Expand Down Expand Up @@ -339,9 +339,9 @@ slice.next: ; preds = %entry
%1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #4
%slice.highmax1 = icmp ult i32 %b.cap, 2
br i1 %slice.highmax1, label %slice.throw, label %slice.next5
br i1 %slice.highmax1, label %slice.throw, label %slice.next3

slice.next5: ; preds = %slice.next
slice.next3: ; preds = %slice.next
call void @runtime.trackPointer(ptr %b.data, ptr nonnull %stackalloc, ptr undef) #4
%2 = extractvalue %runtime._string %0, 0
%3 = extractvalue %runtime._string %0, 1
Expand Down
27 changes: 27 additions & 0 deletions testdata/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ func main() {
println("len:", len(rs))

testRangeLoops()
testSliceBounds()
}

// Range loops evaluate the slice once and keep their own copy of the index.
Expand Down Expand Up @@ -241,6 +242,32 @@ func testRangeLoops() {
println("range nested:", sum)
}

var sliceSink []byte

func slicePanics(name string, f func()) {
defer func() {
println(name, recover() != nil)
}()
f()
}

// Test that slicing a slice still panics for every out of range case.
func testSliceBounds() {
b := make([]byte, 10, 20)
low, high, max := makeInt(5), makeInt(15), makeInt(20)
slicePanics("b[len:]", func() { sliceSink = b[makeInt(10):] })
slicePanics("b[len+1:]", func() { sliceSink = b[makeInt(11):] })
slicePanics("b[:cap]", func() { sliceSink = b[:makeInt(20)] })
slicePanics("b[:cap+1]", func() { sliceSink = b[:makeInt(21)] })
slicePanics("b[low:high]", func() { sliceSink = b[low:high] })
slicePanics("b[high:low]", func() { sliceSink = b[high:low] })
slicePanics("b[low:high:max]", func() { sliceSink = b[low:high:max] })
slicePanics("b[low:high:cap+1]", func() { sliceSink = b[low:high:makeInt(21)] })
slicePanics("b[low:max:high]", func() { sliceSink = b[low:max:high] })
slicePanics("b[-1:]", func() { sliceSink = b[makeInt(-1):] })
slicePanics("b[:-1]", func() { sliceSink = b[:makeInt(-1)] })
}

func printslice(name string, s []int) {
print(name, ": len=", len(s), " cap=", cap(s), " data:")
for _, n := range s {
Expand Down
11 changes: 11 additions & 0 deletions testdata/slice.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ range reassign: 70 0
range continue break: 8
range write: 51
range nested: 10
b[len:] false
b[len+1:] true
b[:cap] false
b[:cap+1] true
b[low:high] false
b[high:low] true
b[low:high:max] false
b[low:high:cap+1] true
b[low:max:high] true
b[-1:] true
b[:-1] true
Loading