gc.go raw

   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 pointer
  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  }
 110