1package transform
2
3// This file contains utilities used across transforms.
4
5import (
6	"tinygo.org/x/go-llvm"
7)
8
9// Check whether all uses of this param as parameter to the call have the given
10// flag. In most cases, there will only be one use but a function could take the
11// same parameter twice, in which case both must have the flag.
12// A flag can be any enum flag, like "readonly".
13func hasFlag(call, param llvm.Value, kind string) bool {
14	fn := call.CalledValue()
15	if fn.IsAFunction().IsNil() {
16		// This is not a function but something else, like a function pointer.
17		return false
18	}
19	kindID := llvm.AttributeKindID(kind)
20	for i := 0; i < fn.ParamsCount(); i++ {
21		if call.Operand(i) != param {
22			// This is not the parameter we're checking.
23			continue
24		}
25		index := i + 1 // param attributes start at 1
26		attr := fn.GetEnumAttributeAtIndex(index, kindID)
27		if attr.IsNil() {
28			// At least one parameter doesn't have the flag (there may be
29			// multiple).
30			return false
31		}
32	}
33	return true
34}
35
36// isReadOnly returns true if the given value (which must be of pointer type) is
37// never stored to, and false if this cannot be proven.
38func isReadOnly(value llvm.Value) bool {
39	uses := getUses(value)
40	for _, use := range uses {
41		if !use.IsAGetElementPtrInst().IsNil() {
42			if !isReadOnly(use) {
43				return false
44			}
45		} else if !use.IsACallInst().IsNil() {
46			if !hasFlag(use, value, "readonly") {
47				return false
48			}
49		} else {
50			// Unknown instruction, might not be readonly.
51			return false
52		}
53	}
54	return true
55}
56