
Unfortunately, while doing this I found that it doesn't actually apply in any real-world programs (tested with `make smoketest`), apparently because nil pointer checking messes with the functionattrs pass. I hope to fix that after moving to LLVM 9, which has an optimization that makes nil pointer checking easier to implement.
55 строки
1,5 КиБ
Go
55 строки
1,5 КиБ
Go
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
|
|
}
|