Skip to content

Commit 267aee1

Browse files
committed
runtime (gc): restructure blocks GC metadata
Originally, the blocks GC simply stored a 2-bit state value for each block with 4 options: - umarked head - marked head - tail (continuation of an object) - free The GC cycled the blocks through these states appropriately. Then the allocator would search for appropriate ranges of free blocks. This design resulted in excessive memory fragmentation due to the way that the allocator had to search for free ranges. To fix this issue, we created a data structure to track the free ranges that is rebuilt after every GC. This mostly fixed the memory fragmentation issue. The other issue with this original approach is that it resulted in quadratic performance degredation when scanning free lists. To solve this, we added a header to each heap object to form a linked stack. This ensured that each object only needed to be visited once. As these improvements were made, TinyGo began practically supporting larger and larger heaps. The current structure where we loop over individual blocks is no longer efficient. We need to change the metadata to support more efficient traversal. This commit changes the per-block metadata into a pair of bitmaps: an "ends" bitmap and a "visited" bitmap. The "ends" bitmap is used by the marking and sweeping logic to find the end (containing the header) of an object. The "visited" bitmap is to track blocks which have been visited by mark, including both ends and non-ends. Most operations can be performed by scanning over these bitmaps rather than looping over individual blocks. The "visited" bitmap also fixes the last remaining case for quadratic performance degredation. In the event that many pointers referred to the start of a large object, the marking code would scan across the whole object to find the end every time. The new marking code adds every block between the marked address and the end to the bitmap. Subsequent marks to the same object will detect the already-visited tail and stop early.
1 parent 6f686d1 commit 267aee1

11 files changed

Lines changed: 611 additions & 461 deletions

File tree

builder/sizes_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
4242
// This is a small number of very diverse targets that we want to test.
4343
tests := []sizeTest{
4444
// microcontrollers
45-
{"hifive1b", "examples/echo", 3705, 299, 0, 2252},
46-
{"microbit", "examples/serial", 2736, 356, 8, 2248},
47-
{"wioterminal", "examples/pininterrupt", 7960, 1652, 132, 7480},
45+
{"hifive1b", "examples/echo", 3799, 297, 0, 2260},
46+
{"microbit", "examples/serial", 2802, 354, 8, 2256},
47+
{"wioterminal", "examples/pininterrupt", 8048, 1652, 132, 7488},
4848

4949
// TODO: also check wasm. Right now this is difficult, because
5050
// wasm binaries are run through wasm-opt and therefore the

compiler/asserts.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
9393
// and unsafe.String. This function must panic if the ptr/len parameters are
9494
// invalid.
9595
func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value, elementType llvm.Type, lenType *types.Basic) {
96+
if b.info.nobounds {
97+
// Function disabled bounds checking - skip conversion check.
98+
return
99+
}
100+
96101
// From the documentation of unsafe.Slice and unsafe.String:
97102
// > At run time, if len is negative, or if ptr is nil and len is not
98103
// > zero, a run-time panic occurs.
@@ -162,6 +167,11 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
162167
// It has no effect in well-behaved programs, but makes sure no uncaught nil
163168
// pointer dereferences exist in valid Go code.
164169
func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix string) {
170+
if b.info.nobounds {
171+
// Function disabled bounds checking - skip nil check.
172+
return
173+
}
174+
165175
// Check whether we need to emit this check at all.
166176
if !ptr.IsAGlobalValue().IsNil() {
167177
return

0 commit comments

Comments
 (0)