aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--interp/frame.go3
-rw-r--r--interp/values.go70
2 files changed, 52 insertions, 21 deletions
diff --git a/interp/frame.go b/interp/frame.go
index ed7c3f98c..b487334e3 100644
--- a/interp/frame.go
+++ b/interp/frame.go
@@ -283,6 +283,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
PkgName: fr.packagePath,
KeySize: int(keySize),
ValueSize: int(valueSize),
+ MapType: inst.Type().ElementType(),
}
case callee.Name() == "runtime.hashmapStringSet":
// set a string key in the map
@@ -350,7 +351,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
- stringType := fr.Mod.GetTypeByName("runtime._string")
+ stringType := inst.Type()
retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
retLen := llvm.ConstInt(stringType.StructElementTypes()[1], uint64(len(result)), false)
ret := llvm.ConstNull(stringType)
diff --git a/interp/values.go b/interp/values.go
index a6e48c358..454e6305e 100644
--- a/interp/values.go
+++ b/interp/values.go
@@ -178,6 +178,8 @@ type MapValue struct {
ValueSize int
KeyType llvm.Type
ValueType llvm.Type
+ MapType llvm.Type // *%runtime.hashmap
+ keyVariant string
}
func (v *MapValue) newBucket() llvm.Value {
@@ -221,27 +223,32 @@ func (v *MapValue) Value() llvm.Value {
var keyBuf []byte
llvmKey := key.Value()
llvmValue := v.Values[i].Value()
- if key.Type().TypeKind() == llvm.StructTypeKind && key.Type().StructName() == "runtime._string" {
+ switch v.keyVariant {
+ case "string":
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
keyPtrVal := v.Eval.getValue(keyPtr)
keyBuf = getStringBytes(keyPtrVal, keyLen)
- } else if key.Type().TypeKind() == llvm.IntegerTypeKind {
- keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
- n := key.Value().ZExtValue()
- for i := range keyBuf {
- keyBuf[i] = byte(n)
- n >>= 8
+ case "binary":
+ if key.Type().TypeKind() == llvm.IntegerTypeKind {
+ keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
+ n := key.Value().ZExtValue()
+ for i := range keyBuf {
+ keyBuf[i] = byte(n)
+ n >>= 8
+ }
+ } else if key.Type().TypeKind() == llvm.ArrayTypeKind &&
+ key.Type().ElementType().TypeKind() == llvm.IntegerTypeKind &&
+ key.Type().ElementType().IntTypeWidth() == 8 {
+ keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
+ for i := range keyBuf {
+ keyBuf[i] = byte(llvm.ConstExtractValue(llvmKey, []uint32{uint32(i)}).ZExtValue())
+ }
+ } else {
+ panic("interp: map key type not implemented: " + key.Type().String())
}
- } else if key.Type().TypeKind() == llvm.ArrayTypeKind &&
- key.Type().ElementType().TypeKind() == llvm.IntegerTypeKind &&
- key.Type().ElementType().IntTypeWidth() == 8 {
- keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
- for i := range keyBuf {
- keyBuf[i] = byte(llvm.ConstExtractValue(llvmKey, []uint32{uint32(i)}).ZExtValue())
- }
- } else {
- panic("interp: map key type not implemented: " + key.Type().String())
+ default:
+ panic("interp: map key variant: " + v.keyVariant)
}
hash := v.hash(keyBuf)
@@ -291,7 +298,7 @@ func (v *MapValue) Value() llvm.Value {
// Type returns type runtime.hashmap, which is the actual hashmap type.
func (v *MapValue) Type() llvm.Type {
- return v.Eval.Mod.GetTypeByName("runtime.hashmap")
+ return v.MapType
}
func (v *MapValue) IsConstant() bool {
@@ -314,12 +321,30 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
return nil, errors.New("interp: GEP on a map")
}
+// setKeyVariant sets the key variant as a result of storing to the hashmap
+// (string, binary, or interface). The way that TinyGo is structured, the key
+// variant is not known until there is a store to the hashmap.
+// The key variant has to be known when lowering the hashmap to its final form,
+// to correctly calculate the hash of a key (for example, a string key must
+// calculate the hash over the string contents).
+func (v *MapValue) setKeyVariant(keyVariant string) {
+ if v.keyVariant == "" {
+ v.keyVariant = keyVariant
+ return
+ }
+ if v.keyVariant != keyVariant {
+ // Valid IR will not cause this panic to occur.
+ panic("MapValue store with inconsistent key type")
+ }
+}
+
// PutString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
+ v.setKeyVariant("string")
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
@@ -336,9 +361,13 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
}
}
- keyType := v.Eval.Mod.GetTypeByName("runtime._string")
- v.KeyType = keyType
- key := llvm.ConstNull(keyType)
+ if v.KeyType.IsNil() {
+ v.KeyType = v.Eval.Mod.Context().StructType([]llvm.Type{
+ keyBuf.Type(),
+ keyLen.Type(),
+ }, false)
+ }
+ key := llvm.ConstNull(v.KeyType)
key = llvm.ConstInsertValue(key, keyBuf.Value(), []uint32{0})
key = llvm.ConstInsertValue(key, keyLen.Value(), []uint32{1})
@@ -352,6 +381,7 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
+ v.setKeyVariant("binary")
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}