Skip to content

Commit 2d61972

Browse files
aykevldeadprogram
authored andcommitted
gc: drop support for 'precise' globals
Precise globals require a whole program optimization pass that is hard to support when building packages separately. This patch removes support for these globals by converting the last use (Linux) to use linker-defined symbols instead. For details, see: #2870
1 parent 5c488e3 commit 2d61972

File tree

9 files changed

+39
-244
lines changed

9 files changed

+39
-244
lines changed

src/runtime/gc_globals_conservative.go renamed to src/runtime/gc_globals.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
package runtime
66

7+
// This file implements markGlobals for all the files that don't have a more
8+
// specific implementation.
9+
710
// markGlobals marks all globals, which are reachable by definition.
811
//
912
// This implementation marks all globals conservatively and assumes it can use

src/runtime/gc_globals_precise.go

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/runtime/os_linux.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
//go:build linux
2-
// +build linux
1+
//go:build linux && !baremetal && !nintendoswitch && !wasi
2+
// +build linux,!baremetal,!nintendoswitch,!wasi
33

44
package runtime
55

6+
// This file is for systems that are _actually_ Linux (not systems that pretend
7+
// to be Linux, like baremetal systems).
8+
9+
import "unsafe"
10+
611
const GOOS = "linux"
712

813
const (
@@ -18,3 +23,20 @@ const (
1823
clock_REALTIME = 0
1924
clock_MONOTONIC_RAW = 4
2025
)
26+
27+
//go:extern _edata
28+
var globalsStartSymbol [0]byte
29+
30+
//go:extern _end
31+
var globalsEndSymbol [0]byte
32+
33+
// markGlobals marks all globals, which are reachable by definition.
34+
//
35+
// This implementation marks all globals conservatively and assumes it can use
36+
// linker-defined symbols for the start and end of the .data section.
37+
func markGlobals() {
38+
start := uintptr(unsafe.Pointer(&globalsStartSymbol))
39+
end := uintptr(unsafe.Pointer(&globalsEndSymbol))
40+
start = (start + unsafe.Alignof(uintptr(0)) - 1) &^ (unsafe.Alignof(uintptr(0)) - 1) // align on word boundary
41+
markRoots(start, end)
42+
}

src/runtime/os_other.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build linux && (baremetal || nintendoswitch || wasi)
2+
// +build linux
3+
// +build baremetal nintendoswitch wasi
4+
5+
// Other systems that aren't operating systems supported by the Go toolchain
6+
// need to pretend to be an existing operating system. Linux seems like a good
7+
// choice for this for its wide hardware support.
8+
9+
package runtime
10+
11+
const GOOS = "linux"

transform/gc.go

Lines changed: 0 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package transform
22

33
import (
4-
"math/big"
5-
64
"tinygo.org/x/go-llvm"
75
)
86

@@ -311,138 +309,6 @@ func MakeGCStackSlots(mod llvm.Module) bool {
311309
return true
312310
}
313311

314-
// AddGlobalsBitmap performs a few related functions. It is needed for scanning
315-
// globals on platforms where the .data/.bss section is not easily accessible by
316-
// the GC, and thus all globals that contain pointers must be made reachable by
317-
// the GC in some other way.
318-
//
319-
// First, it scans all globals, and bundles all globals that contain a pointer
320-
// into one large global (updating all uses in the process). Then it creates a
321-
// bitmap (bit vector) to locate all the pointers in this large global. This
322-
// bitmap allows the GC to know in advance where exactly all the pointers live
323-
// in the large globals bundle, to avoid false positives.
324-
func AddGlobalsBitmap(mod llvm.Module) bool {
325-
if mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() {
326-
return false // nothing to do: no GC in use
327-
}
328-
329-
ctx := mod.Context()
330-
targetData := llvm.NewTargetData(mod.DataLayout())
331-
defer targetData.Dispose()
332-
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
333-
334-
// Collect all globals that contain pointers (and thus must be scanned by
335-
// the GC).
336-
var trackedGlobals []llvm.Value
337-
var trackedGlobalTypes []llvm.Type
338-
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
339-
if global.IsDeclaration() || global.IsGlobalConstant() {
340-
continue
341-
}
342-
typ := global.Type().ElementType()
343-
ptrs := getPointerBitmap(targetData, typ, global.Name())
344-
if ptrs.BitLen() == 0 {
345-
continue
346-
}
347-
trackedGlobals = append(trackedGlobals, global)
348-
trackedGlobalTypes = append(trackedGlobalTypes, typ)
349-
}
350-
351-
// Make a new global that bundles all existing globals, and remove the
352-
// existing globals. All uses of the previous independent globals are
353-
// replaced with a GEP into the new globals bundle.
354-
globalsBundleType := ctx.StructType(trackedGlobalTypes, false)
355-
globalsBundle := llvm.AddGlobal(mod, globalsBundleType, "tinygo.trackedGlobals")
356-
globalsBundle.SetLinkage(llvm.InternalLinkage)
357-
globalsBundle.SetUnnamedAddr(true)
358-
initializer := llvm.Undef(globalsBundleType)
359-
for i, global := range trackedGlobals {
360-
initializer = llvm.ConstInsertValue(initializer, global.Initializer(), []uint32{uint32(i)})
361-
gep := llvm.ConstGEP(globalsBundle, []llvm.Value{
362-
llvm.ConstInt(ctx.Int32Type(), 0, false),
363-
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
364-
})
365-
global.ReplaceAllUsesWith(gep)
366-
global.EraseFromParentAsGlobal()
367-
}
368-
globalsBundle.SetInitializer(initializer)
369-
370-
// Update trackedGlobalsStart, which points to the globals bundle.
371-
trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, uintptrType)
372-
mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart)
373-
mod.NamedGlobal("runtime.trackedGlobalsStart").SetLinkage(llvm.InternalLinkage)
374-
375-
// Update trackedGlobalsLength, which contains the length (in words) of the
376-
// globals bundle.
377-
alignment := targetData.PrefTypeAlignment(llvm.PointerType(ctx.Int8Type(), 0))
378-
trackedGlobalsLength := llvm.ConstInt(uintptrType, targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false)
379-
mod.NamedGlobal("runtime.trackedGlobalsLength").SetLinkage(llvm.InternalLinkage)
380-
mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength)
381-
382-
// Create a bitmap (a new global) that stores for each word in the globals
383-
// bundle whether it contains a pointer. This allows globals to be scanned
384-
// precisely: no non-pointers will be considered pointers if the bit pattern
385-
// looks like one.
386-
// This code assumes that pointers are self-aligned. For example, that a
387-
// 32-bit (4-byte) pointer is also aligned to 4 bytes.
388-
bitmapBytes := getPointerBitmap(targetData, globalsBundleType, "globals bundle").Bytes()
389-
bitmapValues := make([]llvm.Value, len(bitmapBytes))
390-
for i, b := range bitmapBytes {
391-
bitmapValues[len(bitmapBytes)-i-1] = llvm.ConstInt(ctx.Int8Type(), uint64(b), false)
392-
}
393-
bitmapArray := llvm.ConstArray(ctx.Int8Type(), bitmapValues)
394-
bitmapNew := llvm.AddGlobal(mod, bitmapArray.Type(), "runtime.trackedGlobalsBitmap.tmp")
395-
bitmapOld := mod.NamedGlobal("runtime.trackedGlobalsBitmap")
396-
bitmapOld.ReplaceAllUsesWith(llvm.ConstBitCast(bitmapNew, bitmapOld.Type()))
397-
bitmapNew.SetInitializer(bitmapArray)
398-
bitmapNew.SetName("runtime.trackedGlobalsBitmap")
399-
bitmapNew.SetLinkage(llvm.InternalLinkage)
400-
401-
return true // the IR was changed
402-
}
403-
404-
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
405-
// bigint at the word offset that contains a pointer. This scan is recursive.
406-
func getPointerBitmap(targetData llvm.TargetData, typ llvm.Type, name string) *big.Int {
407-
alignment := targetData.PrefTypeAlignment(llvm.PointerType(typ.Context().Int8Type(), 0))
408-
switch typ.TypeKind() {
409-
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
410-
return big.NewInt(0)
411-
case llvm.PointerTypeKind:
412-
return big.NewInt(1)
413-
case llvm.StructTypeKind:
414-
ptrs := big.NewInt(0)
415-
for i, subtyp := range typ.StructElementTypes() {
416-
subptrs := getPointerBitmap(targetData, subtyp, name)
417-
if subptrs.BitLen() == 0 {
418-
continue
419-
}
420-
offset := targetData.ElementOffset(typ, i)
421-
if offset%uint64(alignment) != 0 {
422-
panic("precise GC: global contains unaligned pointer: " + name)
423-
}
424-
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
425-
ptrs.Or(ptrs, subptrs)
426-
}
427-
return ptrs
428-
case llvm.ArrayTypeKind:
429-
subtyp := typ.ElementType()
430-
subptrs := getPointerBitmap(targetData, subtyp, name)
431-
ptrs := big.NewInt(0)
432-
if subptrs.BitLen() == 0 {
433-
return ptrs
434-
}
435-
elementSize := targetData.TypeAllocSize(subtyp)
436-
for i := 0; i < typ.ArrayLength(); i++ {
437-
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
438-
ptrs.Or(ptrs, subptrs)
439-
}
440-
return ptrs
441-
default:
442-
panic("unknown type kind of global: " + name)
443-
}
444-
}
445-
446312
// markParentFunctions traverses all parent function calls (recursively) and
447313
// adds them to the set of marked functions. It only considers function calls:
448314
// any other uses of such a function is ignored.

transform/gc_test.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ import (
77
"tinygo.org/x/go-llvm"
88
)
99

10-
func TestAddGlobalsBitmap(t *testing.T) {
11-
t.Parallel()
12-
testTransform(t, "testdata/gc-globals", func(mod llvm.Module) {
13-
transform.AddGlobalsBitmap(mod)
14-
})
15-
}
16-
1710
func TestMakeGCStackSlots(t *testing.T) {
1811
t.Parallel()
1912
testTransform(t, "testdata/gc-stackslots", func(mod llvm.Module) {

transform/optimizer.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
189189
builder.Populate(modPasses)
190190
modPasses.Run(mod)
191191

192-
hasGCPass := AddGlobalsBitmap(mod)
193-
hasGCPass = MakeGCStackSlots(mod) || hasGCPass
192+
hasGCPass := MakeGCStackSlots(mod)
194193
if hasGCPass {
195194
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
196195
return []error{errors.New("GC pass caused a verification failure")}

transform/testdata/gc-globals.ll

Lines changed: 0 additions & 33 deletions
This file was deleted.

transform/testdata/gc-globals.out.ll

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)