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  }