github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/transform/util.go (about) 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 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 }