diff options
author | Ayke van Laethem <[email protected]> | 2020-04-12 14:09:12 +0200 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2020-05-03 15:43:45 +0200 |
commit | ca7b95039d688bc72fd39b82f54d776c6813c900 (patch) | |
tree | 5d771dce03103992877b590533a86850baf7e085 | |
parent | 7939c060ce88df506f7ff3778b9a7a341d644a8d (diff) | |
download | tinygo-ca7b95039d688bc72fd39b82f54d776c6813c900.tar.gz tinygo-ca7b95039d688bc72fd39b82f54d776c6813c900.zip |
interp: do not rely on fixed type namesbuilder-parallel
After merging multiple LLVM modules into one, types may have been
renamed. Therefore, the interp package should not rely on types to have
any particular name.
-rw-r--r-- | interp/frame.go | 3 | ||||
-rw-r--r-- | interp/values.go | 70 |
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)} |