From ca7b95039d688bc72fd39b82f54d776c6813c900 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 12 Apr 2020 14:09:12 +0200 Subject: [PATCH] interp: do not rely on fixed type names 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. --- interp/frame.go | 3 ++- 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)}