|
|
|
package transform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"tinygo.org/x/go-llvm"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Return a list of values (actually, instructions) where this value is used as
|
|
|
|
// an operand.
|
|
|
|
func getUses(value llvm.Value) []llvm.Value {
|
|
|
|
if value.IsNil() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var uses []llvm.Value
|
|
|
|
use := value.FirstUse()
|
|
|
|
for !use.IsNil() {
|
|
|
|
uses = append(uses, use.User())
|
|
|
|
use = use.NextUse()
|
|
|
|
}
|
|
|
|
return uses
|
|
|
|
}
|
|
|
|
|
|
|
|
// hasUses returns whether the given value has any uses. It is equivalent to
|
|
|
|
// getUses(value) != nil but faster.
|
|
|
|
func hasUses(value llvm.Value) bool {
|
|
|
|
if value.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return !value.FirstUse().IsNil()
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
|
|
|
// contents, and returns the global and initializer type.
|
|
|
|
// Note that it is left with the default linkage etc., you should set
|
|
|
|
// linkage/constant/etc properties yourself.
|
|
|
|
func makeGlobalArray(mod llvm.Module, bufItf interface{}, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
|
|
|
|
buf := reflect.ValueOf(bufItf)
|
|
|
|
var values []llvm.Value
|
|
|
|
for i := 0; i < buf.Len(); i++ {
|
|
|
|
ch := buf.Index(i).Uint()
|
|
|
|
values = append(values, llvm.ConstInt(elementType, ch, false))
|
|
|
|
}
|
|
|
|
value := llvm.ConstArray(elementType, values)
|
|
|
|
global := llvm.AddGlobal(mod, value.Type(), name)
|
|
|
|
global.SetInitializer(value)
|
|
|
|
return value.Type(), global
|
|
|
|
}
|
|
|
|
|
|
|
|
// getGlobalBytes returns the slice contained in the array of the provided
|
|
|
|
// global. It can recover the bytes originally created using makeGlobalArray, if
|
|
|
|
// makeGlobalArray was given a byte slice.
|
|
|
|
//
|
|
|
|
// The builder parameter is only used for constant operations.
|
|
|
|
func getGlobalBytes(global llvm.Value, builder llvm.Builder) []byte {
|
|
|
|
value := global.Initializer()
|
|
|
|
buf := make([]byte, value.Type().ArrayLength())
|
|
|
|
for i := range buf {
|
|
|
|
buf[i] = byte(builder.CreateExtractValue(value, i, "").ZExtValue())
|
|
|
|
}
|
|
|
|
return buf
|
|
|
|
}
|
|
|
|
|
|
|
|
// replaceGlobalByteWithArray replaces a global integer type in the module with
|
|
|
|
// an integer array, using a GEP to make the types match. It is a convenience
|
|
|
|
// function used for creating reflection sidetables, for example.
|
|
|
|
func replaceGlobalIntWithArray(mod llvm.Module, name string, buf interface{}) llvm.Value {
|
|
|
|
oldGlobal := mod.NamedGlobal(name)
|
|
|
|
globalType, global := makeGlobalArray(mod, buf, name+".tmp", oldGlobal.GlobalValueType())
|
|
|
|
gep := llvm.ConstGEP(globalType, global, []llvm.Value{
|
|
|
|
llvm.ConstInt(mod.Context().Int32Type(), 0, false),
|
|
|
|
llvm.ConstInt(mod.Context().Int32Type(), 0, false),
|
|
|
|
})
|
|
|
|
oldGlobal.ReplaceAllUsesWith(gep)
|
|
|
|
oldGlobal.EraseFromParentAsGlobal()
|
|
|
|
global.SetName(name)
|
|
|
|
return global
|
|
|
|
}
|
|
|
|
|
|
|
|
// stripPointerCasts strips instruction pointer casts (getelementptr and
|
|
|
|
// bitcast) and returns the original value without the casts.
|
|
|
|
func stripPointerCasts(value llvm.Value) llvm.Value {
|
|
|
|
if !value.IsAConstantExpr().IsNil() {
|
|
|
|
switch value.Opcode() {
|
|
|
|
case llvm.GetElementPtr, llvm.BitCast:
|
|
|
|
return stripPointerCasts(value.Operand(0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !value.IsAInstruction().IsNil() {
|
|
|
|
switch value.InstructionOpcode() {
|
|
|
|
case llvm.GetElementPtr, llvm.BitCast:
|
|
|
|
return stripPointerCasts(value.Operand(0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|