util.go raw

   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