aboutsummaryrefslogtreecommitdiffhomepage
path: root/transform/util.go
blob: 9923669d1756e413d4d4b9722db877de59c3b88c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package transform

// This file contains utilities used across transforms.

import (
	"tinygo.org/x/go-llvm"
)

// Check whether all uses of this param as parameter to the call have the given
// flag. In most cases, there will only be one use but a function could take the
// same parameter twice, in which case both must have the flag.
// A flag can be any enum flag, like "readonly".
func hasFlag(call, param llvm.Value, kind string) bool {
	fn := call.CalledValue()
	if fn.IsAFunction().IsNil() {
		// This is not a function but something else, like a function pointer.
		return false
	}
	kindID := llvm.AttributeKindID(kind)
	for i := 0; i < fn.ParamsCount(); i++ {
		if call.Operand(i) != param {
			// This is not the parameter we're checking.
			continue
		}
		index := i + 1 // param attributes start at 1
		attr := fn.GetEnumAttributeAtIndex(index, kindID)
		if attr.IsNil() {
			// At least one parameter doesn't have the flag (there may be
			// multiple).
			return false
		}
	}
	return true
}

// isReadOnly returns true if the given value (which must be of pointer type) is
// never stored to, and false if this cannot be proven.
func isReadOnly(value llvm.Value) bool {
	uses := getUses(value)
	for _, use := range uses {
		if !use.IsAGetElementPtrInst().IsNil() {
			if !isReadOnly(use) {
				return false
			}
		} else if !use.IsACallInst().IsNil() {
			if !hasFlag(use, value, "readonly") {
				return false
			}
		} else {
			// Unknown instruction, might not be readonly.
			return false
		}
	}
	return true
}