1 package transform
2 3 // This file contains utilities used across transforms.
4 5 import (
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".
13 func 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.
38 func isReadOnly(value llvm.Value) bool {
39 uses := getUses(value)
40 for _, use := range uses {
41 switch {
42 case !use.IsAGetElementPtrInst().IsNil():
43 if !isReadOnly(use) {
44 return false
45 }
46 case !use.IsACallInst().IsNil():
47 if !hasFlag(use, value, "readonly") {
48 return false
49 }
50 case !use.IsALoadInst().IsNil():
51 // Loads are read-only.
52 default:
53 // Unknown instruction, might not be readonly.
54 return false
55 }
56 }
57 return true
58 }
59