github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/gc.go (about) 1 package compiler 2 3 // This file provides IR transformations necessary for precise and portable 4 // garbage collectors. 5 6 import ( 7 "go/token" 8 9 "golang.org/x/tools/go/ssa" 10 "tinygo.org/x/go-llvm" 11 ) 12 13 // trackExpr inserts pointer tracking intrinsics for the GC if the expression is 14 // one of the expressions that need this. 15 func (b *builder) trackExpr(expr ssa.Value, value llvm.Value) { 16 // There are uses of this expression, Make sure the pointers 17 // are tracked during GC. 18 switch expr := expr.(type) { 19 case *ssa.Alloc, *ssa.MakeChan, *ssa.MakeMap: 20 // These values are always of pointer type in IR. 21 b.trackPointer(value) 22 case *ssa.Call, *ssa.Convert, *ssa.MakeClosure, *ssa.MakeInterface, *ssa.MakeSlice, *ssa.Next: 23 if !value.IsNil() { 24 b.trackValue(value) 25 } 26 case *ssa.Select: 27 if alloca, ok := b.selectRecvBuf[expr]; ok { 28 if alloca.IsAUndefValue().IsNil() { 29 b.trackPointer(alloca) 30 } 31 } 32 case *ssa.UnOp: 33 switch expr.Op { 34 case token.MUL: 35 // Pointer dereference. 36 b.trackValue(value) 37 case token.ARROW: 38 // Channel receive operator. 39 // It's not necessary to look at commaOk here, because in that 40 // case it's just an aggregate and trackValue will extract the 41 // pointer in there (if there is one). 42 b.trackValue(value) 43 } 44 case *ssa.BinOp: 45 switch expr.Op { 46 case token.ADD: 47 // String concatenation. 48 b.trackValue(value) 49 } 50 } 51 } 52 53 // trackValue locates pointers in a value (possibly an aggregate) and tracks the 54 // individual pointers 55 func (b *builder) trackValue(value llvm.Value) { 56 typ := value.Type() 57 switch typ.TypeKind() { 58 case llvm.PointerTypeKind: 59 b.trackPointer(value) 60 case llvm.StructTypeKind: 61 if !typeHasPointers(typ) { 62 return 63 } 64 numElements := typ.StructElementTypesCount() 65 for i := 0; i < numElements; i++ { 66 subValue := b.CreateExtractValue(value, i, "") 67 b.trackValue(subValue) 68 } 69 case llvm.ArrayTypeKind: 70 if !typeHasPointers(typ) { 71 return 72 } 73 numElements := typ.ArrayLength() 74 for i := 0; i < numElements; i++ { 75 subValue := b.CreateExtractValue(value, i, "") 76 b.trackValue(subValue) 77 } 78 } 79 } 80 81 // trackPointer creates a call to runtime.trackPointer, bitcasting the poitner 82 // first if needed. The input value must be of LLVM pointer type. 83 func (b *builder) trackPointer(value llvm.Value) { 84 b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") 85 } 86 87 // typeHasPointers returns whether this type is a pointer or contains pointers. 88 // If the type is an aggregate type, it will check whether there is a pointer 89 // inside. 90 func typeHasPointers(t llvm.Type) bool { 91 switch t.TypeKind() { 92 case llvm.PointerTypeKind: 93 return true 94 case llvm.StructTypeKind: 95 for _, subType := range t.StructElementTypes() { 96 if typeHasPointers(subType) { 97 return true 98 } 99 } 100 return false 101 case llvm.ArrayTypeKind: 102 if typeHasPointers(t.ElementType()) { 103 return true 104 } 105 return false 106 default: 107 return false 108 } 109 }