mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-31 00:57:48 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1e69bbc13 | |||
| 1b4d71bd3d | |||
| 43b3bb6e83 |
@@ -151,11 +151,15 @@ func (c *Compiler) TargetData() llvm.TargetData {
|
|||||||
func (c *Compiler) selectGC() string {
|
func (c *Compiler) selectGC() string {
|
||||||
gc := c.GC
|
gc := c.GC
|
||||||
if gc == "" {
|
if gc == "" {
|
||||||
gc = "dumb"
|
gc = "leaking"
|
||||||
}
|
}
|
||||||
return gc
|
return gc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) gcIsPrecise() bool {
|
||||||
|
return c.GC == "precise"
|
||||||
|
}
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
// fails (in any stage).
|
// fails (in any stage).
|
||||||
func (c *Compiler) Compile(mainPath string) []error {
|
func (c *Compiler) Compile(mainPath string) []error {
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) addGlobalsBitmap() {
|
||||||
|
if c.mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() {
|
||||||
|
return // nothing to do: no GC in use
|
||||||
|
}
|
||||||
|
|
||||||
|
var trackedGlobals []llvm.Value
|
||||||
|
var trackedGlobalTypes []llvm.Type
|
||||||
|
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
|
if global.IsDeclaration() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typ := global.Type().ElementType()
|
||||||
|
ptrs := c.getPointerBitmap(typ, global.Name())
|
||||||
|
if ptrs.BitLen() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
trackedGlobals = append(trackedGlobals, global)
|
||||||
|
trackedGlobalTypes = append(trackedGlobalTypes, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
globalsBundleType := c.ctx.StructType(trackedGlobalTypes, false)
|
||||||
|
globalsBundle := llvm.AddGlobal(c.mod, globalsBundleType, "tinygo.trackedGlobals")
|
||||||
|
globalsBundle.SetLinkage(llvm.InternalLinkage)
|
||||||
|
globalsBundle.SetUnnamedAddr(true)
|
||||||
|
initializer := llvm.Undef(globalsBundleType)
|
||||||
|
for i, global := range trackedGlobals {
|
||||||
|
initializer = llvm.ConstInsertValue(initializer, global.Initializer(), []uint32{uint32(i)})
|
||||||
|
gep := llvm.ConstGEP(globalsBundle, []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
||||||
|
})
|
||||||
|
global.ReplaceAllUsesWith(gep)
|
||||||
|
global.EraseFromParentAsGlobal()
|
||||||
|
}
|
||||||
|
globalsBundle.SetInitializer(initializer)
|
||||||
|
|
||||||
|
trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, c.uintptrType)
|
||||||
|
c.mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart)
|
||||||
|
|
||||||
|
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||||
|
trackedGlobalsLength := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false)
|
||||||
|
c.mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength)
|
||||||
|
|
||||||
|
bitmapBytes := c.getPointerBitmap(globalsBundleType, "globals bundle").Bytes()
|
||||||
|
bitmapValues := make([]llvm.Value, len(bitmapBytes))
|
||||||
|
for i, b := range bitmapBytes {
|
||||||
|
bitmapValues[len(bitmapBytes)-i-1] = llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)
|
||||||
|
}
|
||||||
|
bitmapArray := llvm.ConstArray(llvm.ArrayType(c.ctx.Int8Type(), len(bitmapBytes)), bitmapValues)
|
||||||
|
bitmapNew := llvm.AddGlobal(c.mod, bitmapArray.Type(), "runtime.trackedGlobalsBitmap.tmp")
|
||||||
|
bitmapOld := c.mod.NamedGlobal("runtime.trackedGlobalsBitmap")
|
||||||
|
bitmapOld.ReplaceAllUsesWith(bitmapNew)
|
||||||
|
bitmapNew.SetInitializer(bitmapArray)
|
||||||
|
bitmapNew.SetName("runtime.trackedGlobalsBitmap")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) getPointerBitmap(typ llvm.Type, name string) *big.Int {
|
||||||
|
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||||
|
switch typ.TypeKind() {
|
||||||
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
|
return big.NewInt(0)
|
||||||
|
case llvm.PointerTypeKind:
|
||||||
|
return big.NewInt(1)
|
||||||
|
case llvm.StructTypeKind:
|
||||||
|
ptrs := big.NewInt(0)
|
||||||
|
for i, subtyp := range typ.StructElementTypes() {
|
||||||
|
subptrs := c.getPointerBitmap(subtyp, name)
|
||||||
|
if subptrs.BitLen() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
offset := c.targetData.ElementOffset(typ, i)
|
||||||
|
if offset%uint64(alignment) != 0 {
|
||||||
|
panic("precise GC: global contains unaligned pointer: " + name)
|
||||||
|
}
|
||||||
|
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
|
}
|
||||||
|
return ptrs
|
||||||
|
case llvm.ArrayTypeKind:
|
||||||
|
subtyp := typ.ElementType()
|
||||||
|
subptrs := c.getPointerBitmap(subtyp, name)
|
||||||
|
ptrs := big.NewInt(0)
|
||||||
|
if subptrs.BitLen() == 0 {
|
||||||
|
return ptrs
|
||||||
|
}
|
||||||
|
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||||
|
for i := 0; i < typ.ArrayLength(); i++ {
|
||||||
|
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||||
|
ptrs.Or(ptrs, subptrs)
|
||||||
|
}
|
||||||
|
return ptrs
|
||||||
|
default:
|
||||||
|
panic("unknown type kind of global: " + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
goPasses := llvm.NewPassManager()
|
goPasses := llvm.NewPassManager()
|
||||||
defer goPasses.Dispose()
|
defer goPasses.Dispose()
|
||||||
goPasses.AddGlobalOptimizerPass()
|
goPasses.AddGlobalOptimizerPass()
|
||||||
|
goPasses.AddGlobalDCEPass()
|
||||||
goPasses.AddConstantPropagationPass()
|
goPasses.AddConstantPropagationPass()
|
||||||
goPasses.AddAggressiveDCEPass()
|
goPasses.AddAggressiveDCEPass()
|
||||||
goPasses.AddFunctionAttrsPass()
|
goPasses.AddFunctionAttrsPass()
|
||||||
@@ -114,6 +115,13 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
builder.Populate(modPasses)
|
builder.Populate(modPasses)
|
||||||
modPasses.Run(c.mod)
|
modPasses.Run(c.mod)
|
||||||
|
|
||||||
|
if c.gcIsPrecise() {
|
||||||
|
c.addGlobalsBitmap()
|
||||||
|
if err := c.Verify(); err != nil {
|
||||||
|
return errors.New("GC pass caused a verification failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
return llvm.ConstPointerNull(c.i8ptrType)
|
return llvm.ConstPointerNull(c.i8ptrType)
|
||||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
return c.builder.CreateBitCast(values[0], c.i8ptrType, "pack.ptr")
|
return c.builder.CreateBitCast(values[0], c.i8ptrType, "pack.ptr")
|
||||||
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) && !c.gcIsPrecise() {
|
||||||
// Packed data fits in a pointer, so store it directly inside the
|
// Packed data fits in a pointer, so store it directly inside the
|
||||||
// pointer.
|
// pointer.
|
||||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
@@ -57,7 +57,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
return c.builder.CreateLoad(packedAlloc, "")
|
return c.builder.CreateLoad(packedAlloc, "")
|
||||||
} else {
|
} else {
|
||||||
// Get the original heap allocation pointer, which already is an *i8.
|
// Get the original heap allocation pointer, which already is an *i8.
|
||||||
return packedHeapAlloc
|
return c.builder.CreateBitCast(packedAlloc, c.i8ptrType, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||||
// A single pointer is always stored directly.
|
// A single pointer is always stored directly.
|
||||||
return []llvm.Value{c.builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
return []llvm.Value{c.builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||||
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) && !c.gcIsPrecise() {
|
||||||
// Packed data stored directly in pointer.
|
// Packed data stored directly in pointer.
|
||||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||||
// Keep this cast in SSA form.
|
// Keep this cast in SSA form.
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ func handleCompilerError(err error) {
|
|||||||
func main() {
|
func main() {
|
||||||
outpath := flag.String("o", "", "output filename")
|
outpath := flag.String("o", "", "output filename")
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
||||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
t.Log("running tests for emulated cortex-m3...")
|
t.Log("running tests for emulated cortex-m3...")
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
|
if path == "testdata/reflect.go" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
runTest(path, tmpdir, "qemu", t)
|
runTest(path, tmpdir, "qemu", t)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// +build gc.marksweep
|
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
// This memory manager is a textbook mark/sweep implementation, heavily inspired
|
// This memory manager is a textbook mark/sweep implementation, heavily inspired
|
||||||
@@ -168,42 +166,9 @@ func (b gcBlock) unmark() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the memory allocator.
|
|
||||||
// No memory may be allocated before this is called. That means the runtime and
|
|
||||||
// any packages the runtime depends upon may not allocate memory during package
|
|
||||||
// initialization.
|
|
||||||
func init() {
|
|
||||||
totalSize := heapEnd - heapStart
|
|
||||||
|
|
||||||
// Allocate some memory to keep 2 bits of information about every block.
|
|
||||||
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
|
||||||
|
|
||||||
// Align the pool.
|
|
||||||
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
|
||||||
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
|
||||||
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
|
||||||
endBlock = gcBlock(numBlocks)
|
|
||||||
if gcDebug {
|
|
||||||
println("heapStart: ", heapStart)
|
|
||||||
println("heapEnd: ", heapEnd)
|
|
||||||
println("total size: ", totalSize)
|
|
||||||
println("metadata size: ", metadataSize)
|
|
||||||
println("poolStart: ", poolStart)
|
|
||||||
println("# of blocks: ", numBlocks)
|
|
||||||
println("# of block states:", metadataSize*blocksPerStateByte)
|
|
||||||
}
|
|
||||||
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
|
||||||
// sanity check
|
|
||||||
runtimePanic("gc: metadata array is too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set all block states to 'free'.
|
|
||||||
memzero(unsafe.Pointer(heapStart), metadataSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// alloc tries to find some free space on the heap, possibly doing a garbage
|
// alloc tries to find some free space on the heap, possibly doing a garbage
|
||||||
// collection cycle if needed. If no space is free, it panics.
|
// collection cycle if needed. If no space is free, it panics.
|
||||||
func alloc(size uintptr) unsafe.Pointer {
|
func heapAlloc(size uintptr) unsafe.Pointer {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return unsafe.Pointer(&zeroSizedAlloc)
|
return unsafe.Pointer(&zeroSizedAlloc)
|
||||||
}
|
}
|
||||||
@@ -275,53 +240,6 @@ func free(ptr unsafe.Pointer) {
|
|||||||
// TODO: free blocks on request, when the compiler knows they're unused.
|
// TODO: free blocks on request, when the compiler knows they're unused.
|
||||||
}
|
}
|
||||||
|
|
||||||
// GC performs a garbage collection cycle.
|
|
||||||
func GC() {
|
|
||||||
if gcDebug {
|
|
||||||
println("running collection cycle...")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark phase: mark all reachable objects, recursively.
|
|
||||||
markRoots(globalsStart, globalsEnd)
|
|
||||||
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
|
||||||
|
|
||||||
// Sweep phase: free all non-marked objects and unmark marked objects for
|
|
||||||
// the next collection cycle.
|
|
||||||
sweep()
|
|
||||||
|
|
||||||
// Show how much has been sweeped, for debugging.
|
|
||||||
if gcDebug {
|
|
||||||
dumpHeap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// markRoots reads all pointers from start to end (exclusive) and if they look
|
|
||||||
// like a heap pointer and are unmarked, marks them and scans that object as
|
|
||||||
// well (recursively). The start and end parameters must be valid pointers and
|
|
||||||
// must be aligned.
|
|
||||||
func markRoots(start, end uintptr) {
|
|
||||||
if gcDebug {
|
|
||||||
println("mark from", start, "to", end, int(end-start))
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
|
||||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
|
||||||
if looksLikePointer(root) {
|
|
||||||
block := blockFromAddr(root)
|
|
||||||
head := block.findHead()
|
|
||||||
if head.state() != blockStateMark {
|
|
||||||
if gcDebug {
|
|
||||||
println("found unmarked pointer", root, "at address", addr)
|
|
||||||
}
|
|
||||||
head.setState(blockStateMark)
|
|
||||||
next := block.findNext()
|
|
||||||
// TODO: avoid recursion as much as possible
|
|
||||||
markRoots(head.address(), next.address())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sweep goes through all memory and frees unmarked memory.
|
// Sweep goes through all memory and frees unmarked memory.
|
||||||
func sweep() {
|
func sweep() {
|
||||||
freeCurrentObject := false
|
freeCurrentObject := false
|
||||||
@@ -347,10 +265,8 @@ func sweep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// looksLikePointer returns whether this could be a pointer. Currently, it
|
// addressOnHeap returns whether this address points into the heap.
|
||||||
// simply returns whether it lies anywhere in the heap. Go allows interior
|
func addressOnHeap(ptr uintptr) bool {
|
||||||
// pointers so we can't check alignment or anything like that.
|
|
||||||
func looksLikePointer(ptr uintptr) bool {
|
|
||||||
return ptr >= poolStart && ptr < heapEnd
|
return ptr >= poolStart && ptr < heapEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// +build gc.conservative
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initialize the memory allocator.
|
||||||
|
// No memory may be allocated before this is called. That means the runtime and
|
||||||
|
// any packages the runtime depends upon may not allocate memory during package
|
||||||
|
// initialization.
|
||||||
|
func init() {
|
||||||
|
totalSize := heapEnd - heapStart
|
||||||
|
|
||||||
|
// Allocate some memory to keep 2 bits of information about every block.
|
||||||
|
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
||||||
|
|
||||||
|
// Align the pool.
|
||||||
|
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
||||||
|
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
||||||
|
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
||||||
|
endBlock = gcBlock(numBlocks)
|
||||||
|
if gcDebug {
|
||||||
|
println("heapStart: ", heapStart)
|
||||||
|
println("heapEnd: ", heapEnd)
|
||||||
|
println("total size: ", totalSize)
|
||||||
|
println("metadata size: ", metadataSize)
|
||||||
|
println("poolStart: ", poolStart)
|
||||||
|
println("# of blocks: ", numBlocks)
|
||||||
|
println("# of block states:", metadataSize*blocksPerStateByte)
|
||||||
|
}
|
||||||
|
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
||||||
|
// sanity check
|
||||||
|
runtimePanic("gc: metadata array is too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all block states to 'free'.
|
||||||
|
memzero(unsafe.Pointer(heapStart), metadataSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func alloc(size uintptr) unsafe.Pointer {
|
||||||
|
return heapAlloc(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC performs a garbage collection cycle.
|
||||||
|
func GC() {
|
||||||
|
if gcDebug {
|
||||||
|
println("running collection cycle...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark phase: mark all reachable objects, recursively.
|
||||||
|
markRoots(globalsStart, globalsEnd)
|
||||||
|
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
||||||
|
|
||||||
|
// Sweep phase: free all non-marked objects and unmark marked objects for
|
||||||
|
// the next collection cycle.
|
||||||
|
sweep()
|
||||||
|
|
||||||
|
// Show how much has been sweeped, for debugging.
|
||||||
|
if gcDebug {
|
||||||
|
dumpHeap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// markRoots reads all pointers from start to end (exclusive) and if they look
|
||||||
|
// like a heap pointer and are unmarked, marks them and scans that object as
|
||||||
|
// well (recursively). The start and end parameters must be valid pointers and
|
||||||
|
// must be aligned.
|
||||||
|
func markRoots(start, end uintptr) {
|
||||||
|
if gcDebug {
|
||||||
|
println("mark from", start, "to", end, int(end-start))
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
||||||
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
|
if addressOnHeap(root) {
|
||||||
|
block := blockFromAddr(root)
|
||||||
|
head := block.findHead()
|
||||||
|
if head.state() != blockStateMark {
|
||||||
|
if gcDebug {
|
||||||
|
println("found unmarked pointer", root, "at address", addr)
|
||||||
|
}
|
||||||
|
head.setState(blockStateMark)
|
||||||
|
next := block.findNext()
|
||||||
|
// TODO: avoid recursion as much as possible
|
||||||
|
markRoots(head.address(), next.address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build gc.dumb
|
// +build gc.leaking
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -30,18 +30,6 @@ func alloc(size uintptr) unsafe.Pointer {
|
|||||||
return unsafe.Pointer(addr)
|
return unsafe.Pointer(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func free(ptr unsafe.Pointer) {
|
|
||||||
// Memory is never freed.
|
|
||||||
}
|
|
||||||
|
|
||||||
func GC() {
|
func GC() {
|
||||||
// No-op.
|
// No-op.
|
||||||
}
|
}
|
||||||
|
|
||||||
func KeepAlive(x interface{}) {
|
|
||||||
// Unimplemented. Only required with SetFinalizer().
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
|
||||||
// Unimplemented.
|
|
||||||
}
|
|
||||||
@@ -12,18 +12,6 @@ import (
|
|||||||
|
|
||||||
func alloc(size uintptr) unsafe.Pointer
|
func alloc(size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
func free(ptr unsafe.Pointer) {
|
|
||||||
// Nothing to free when nothing gets allocated.
|
|
||||||
}
|
|
||||||
|
|
||||||
func GC() {
|
func GC() {
|
||||||
// Unimplemented.
|
// Unimplemented.
|
||||||
}
|
}
|
||||||
|
|
||||||
func KeepAlive(x interface{}) {
|
|
||||||
// Unimplemented. Only required with SetFinalizer().
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
|
||||||
// Unimplemented.
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// +build gc.precise
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsStart
|
||||||
|
var trackedGlobalsStart uintptr
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsLength
|
||||||
|
var trackedGlobalsLength uintptr
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsBitmap
|
||||||
|
var trackedGlobalsBitmap [0]uint8
|
||||||
|
|
||||||
|
// Initialize the memory allocator.
|
||||||
|
// No memory may be allocated before this is called. That means the runtime and
|
||||||
|
// any packages the runtime depends upon may not allocate memory during package
|
||||||
|
// initialization.
|
||||||
|
func init() {
|
||||||
|
totalSize := heapEnd - heapStart
|
||||||
|
|
||||||
|
// Allocate some memory to keep 2 bits of information about every block.
|
||||||
|
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
||||||
|
|
||||||
|
// Align the pool.
|
||||||
|
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
||||||
|
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
||||||
|
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
||||||
|
endBlock = gcBlock(numBlocks)
|
||||||
|
if gcDebug {
|
||||||
|
println("heapStart: ", heapStart)
|
||||||
|
println("heapEnd: ", heapEnd)
|
||||||
|
println("total size: ", totalSize)
|
||||||
|
println("metadata size: ", metadataSize)
|
||||||
|
println("poolStart: ", poolStart)
|
||||||
|
println("# of blocks: ", numBlocks)
|
||||||
|
println("# of block states:", metadataSize*blocksPerStateByte)
|
||||||
|
}
|
||||||
|
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
||||||
|
// sanity check
|
||||||
|
runtimePanic("gc: metadata array is too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all block states to 'free'.
|
||||||
|
memzero(unsafe.Pointer(heapStart), metadataSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func alloc(size uintptr) unsafe.Pointer {
|
||||||
|
GC()
|
||||||
|
return heapAlloc(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC performs a garbage collection cycle.
|
||||||
|
func GC() {
|
||||||
|
if gcDebug {
|
||||||
|
println("\nrunning collection cycle...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark phase: mark all reachable objects, recursively.
|
||||||
|
markGlobals()
|
||||||
|
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
||||||
|
|
||||||
|
// Sweep phase: free all non-marked objects and unmark marked objects for
|
||||||
|
// the next collection cycle.
|
||||||
|
sweep()
|
||||||
|
|
||||||
|
// Show how much has been sweeped, for debugging.
|
||||||
|
if gcDebug {
|
||||||
|
dumpHeap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nobounds
|
||||||
|
func markGlobals() {
|
||||||
|
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
||||||
|
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||||
|
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
||||||
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
|
markRoot(addr, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// markRoots reads all pointers from start to end (exclusive) and if they look
|
||||||
|
// like a heap pointer and are unmarked, marks them and scans that object as
|
||||||
|
// well (recursively). The start and end parameters must be valid pointers and
|
||||||
|
// must be aligned.
|
||||||
|
func markRoots(start, end uintptr) {
|
||||||
|
if gcDebug {
|
||||||
|
println("mark from", start, "to", end, int(end-start))
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
||||||
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
|
markRoot(addr, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func markRoot(addr, root uintptr) {
|
||||||
|
if addressOnHeap(root) {
|
||||||
|
block := blockFromAddr(root)
|
||||||
|
head := block.findHead()
|
||||||
|
if head.state() != blockStateMark {
|
||||||
|
if gcDebug {
|
||||||
|
println("found unmarked pointer", root, "at address", addr)
|
||||||
|
}
|
||||||
|
head.setState(blockStateMark)
|
||||||
|
next := block.findNext()
|
||||||
|
// TODO: avoid recursion as much as possible
|
||||||
|
markRoots(head.address(), next.address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"goos": "linux",
|
"goos": "linux",
|
||||||
"goarch": "arm",
|
"goarch": "arm",
|
||||||
"compiler": "clang",
|
"compiler": "clang",
|
||||||
"gc": "marksweep",
|
"gc": "precise",
|
||||||
"linker": "ld.lld",
|
"linker": "ld.lld",
|
||||||
"rtlib": "compiler-rt",
|
"rtlib": "compiler-rt",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
|
|||||||
Reference in New Issue
Block a user