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