Add integer key support to hashmap
Этот коммит содержится в:
		
							родитель
							
								
									8f7db8661b
								
							
						
					
					
						коммит
						c912091f8b
					
				
					 3 изменённых файлов: 93 добавлений и 42 удалений
				
			
		
							
								
								
									
										33
									
								
								compiler.go
									
										
									
									
									
								
							
							
						
						
									
										33
									
								
								compiler.go
									
										
									
									
									
								
							|  | @ -1100,12 +1100,20 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { | |||
| 		mapType := instr.Map.Type().Underlying().(*types.Map) | ||||
| 		switch keyType := mapType.Key().Underlying().(type) { | ||||
| 		case *types.Basic: | ||||
| 			if keyType.Kind() == types.String { | ||||
| 			valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value") | ||||
| 			c.builder.CreateStore(value, valueAlloca) | ||||
| 			valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr") | ||||
| 			if keyType.Kind() == types.String { | ||||
| 				params := []llvm.Value{m, key, valuePtr} | ||||
| 				fn := c.mod.NamedFunction("runtime.hashmapSet") | ||||
| 				fn := c.mod.NamedFunction("runtime.hashmapStringSet") | ||||
| 				c.builder.CreateCall(fn, params, "") | ||||
| 				return nil | ||||
| 			} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 { | ||||
| 				keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key") | ||||
| 				c.builder.CreateStore(key, keyAlloca) | ||||
| 				keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr") | ||||
| 				params := []llvm.Value{m, keyPtr, valuePtr} | ||||
| 				fn := c.mod.NamedFunction("runtime.hashmapBinarySet") | ||||
| 				c.builder.CreateCall(fn, params, "") | ||||
| 				return nil | ||||
| 			} else { | ||||
|  | @ -1588,15 +1596,23 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		case *types.Map: | ||||
| 			switch keyType := xType.Key().Underlying().(type) { | ||||
| 			case *types.Basic: | ||||
| 				if keyType.Kind() == types.String { | ||||
| 				llvmValueType, err := c.getLLVMType(expr.Type()) | ||||
| 				if err != nil { | ||||
| 					return llvm.Value{}, err | ||||
| 				} | ||||
| 				mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value") | ||||
| 				mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr") | ||||
| 				if keyType.Kind() == types.String { | ||||
| 					params := []llvm.Value{value, index, mapValuePtr} | ||||
| 					fn := c.mod.NamedFunction("runtime.hashmapGet") | ||||
| 					fn := c.mod.NamedFunction("runtime.hashmapStringGet") | ||||
| 					c.builder.CreateCall(fn, params, "") | ||||
| 					return c.builder.CreateLoad(mapValueAlloca, ""), nil | ||||
| 				} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 { | ||||
| 					keyAlloca := c.builder.CreateAlloca(index.Type(), "hashmap.key") | ||||
| 					c.builder.CreateStore(index, keyAlloca) | ||||
| 					keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr") | ||||
| 					params := []llvm.Value{value, keyPtr, mapValuePtr} | ||||
| 					fn := c.mod.NamedFunction("runtime.hashmapBinaryGet") | ||||
| 					c.builder.CreateCall(fn, params, "") | ||||
| 					return c.builder.CreateLoad(mapValueAlloca, ""), nil | ||||
| 				} else { | ||||
|  | @ -1658,9 +1674,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		if err != nil { | ||||
| 			return llvm.Value{}, err | ||||
| 		} | ||||
| 		switch keyType := mapType.Key().Underlying().(type) { | ||||
| 		case *types.Basic: | ||||
| 			if keyType.Kind() == types.String { | ||||
| 		keySize := c.targetData.TypeAllocSize(llvmKeyType) | ||||
| 		valueSize := c.targetData.TypeAllocSize(llvmValueType) | ||||
| 		hashmapMake := c.mod.NamedFunction("runtime.hashmapMake") | ||||
|  | @ -1668,12 +1681,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | |||
| 		llvmValueSize := llvm.ConstInt(llvm.Int8Type(), valueSize, false) | ||||
| 		hashmap := c.builder.CreateCall(hashmapMake, []llvm.Value{llvmKeySize, llvmValueSize}, "") | ||||
| 		return hashmap, nil | ||||
| 			} else { | ||||
| 				return llvm.Value{}, errors.New("todo: map key type: " + keyType.String()) | ||||
| 			} | ||||
| 		default: | ||||
| 			return llvm.Value{}, errors.New("todo: map key type: " + keyType.String()) | ||||
| 		} | ||||
| 	case *ssa.Phi: | ||||
| 		t, err := c.getLLVMType(expr.Type()) | ||||
| 		if err != nil { | ||||
|  |  | |||
|  | @ -30,13 +30,14 @@ type hashmapBucket struct { | |||
| 	// allocated but as they're of variable size they can't be shown here. | ||||
| } | ||||
| 
 | ||||
| // Get FNV-1a hash of this string. | ||||
| // Get FNV-1a hash of this key. | ||||
| // | ||||
| // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash | ||||
| func stringhash(s *string) uint32 { | ||||
| func hashmapHash(ptr unsafe.Pointer, n uintptr) uint32 { | ||||
| 	var result uint32 = 2166136261 // FNV offset basis | ||||
| 	for i := 0; i < len(*s); i++ { | ||||
| 		result ^= uint32((*s)[i]) | ||||
| 	for i := uintptr(0); i < n; i++ { | ||||
| 		c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i)) | ||||
| 		result ^= uint32(c) // XOR with byte | ||||
| 		result *= 16777619  // FNV prime | ||||
| 	} | ||||
| 	return result | ||||
|  | @ -65,8 +66,7 @@ func hashmapMake(keySize, valueSize uint8) *hashmap { | |||
| } | ||||
| 
 | ||||
| // Set a specified key to a given value. Grow the map if necessary. | ||||
| func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | ||||
| 	hash := stringhash(&key) | ||||
| func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | ||||
| 	numBuckets := uintptr(1) << m.bucketBits | ||||
| 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | ||||
| 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | ||||
|  | @ -76,13 +76,13 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | |||
| 	tophash := hashmapTopHash(hash) | ||||
| 
 | ||||
| 	// See whether the key already exists somewhere. | ||||
| 	var emptySlotKey *string | ||||
| 	var emptySlotKey unsafe.Pointer | ||||
| 	var emptySlotValue unsafe.Pointer | ||||
| 	var emptySlotTophash *byte | ||||
| 	for bucket != nil { | ||||
| 		for i := uintptr(0); i < 8; i++ { | ||||
| 			slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i) | ||||
| 			slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset)) | ||||
| 			slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset) | ||||
| 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i) | ||||
| 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | ||||
| 			if bucket.tophash[i] == 0 && emptySlotKey == nil { | ||||
|  | @ -94,7 +94,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | |||
| 			} | ||||
| 			if bucket.tophash[i] == tophash { | ||||
| 				// Could be an existing value that's the same. | ||||
| 				if key == *slotKey { | ||||
| 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||
| 					// found same key, replace it | ||||
| 					memcpy(slotValue, value, uintptr(m.valueSize)) | ||||
| 					return | ||||
|  | @ -105,7 +105,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | |||
| 	} | ||||
| 	if emptySlotKey != nil { | ||||
| 		m.count++ | ||||
| 		*emptySlotKey = key | ||||
| 		memcpy(emptySlotKey, key, uintptr(m.keySize)) | ||||
| 		memcpy(emptySlotValue, value, uintptr(m.valueSize)) | ||||
| 		*emptySlotTophash = tophash | ||||
| 		return | ||||
|  | @ -114,8 +114,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | |||
| } | ||||
| 
 | ||||
| // Get the value of a specified key, or zero the value if not found. | ||||
| func hashmapGet(m *hashmap, key string, value unsafe.Pointer) { | ||||
| 	hash := stringhash(&key) | ||||
| func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | ||||
| 	numBuckets := uintptr(1) << m.bucketBits | ||||
| 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | ||||
| 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | ||||
|  | @ -132,12 +131,12 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) { | |||
| 	for bucket != nil { | ||||
| 		for i := uintptr(0); i < 8; i++ { | ||||
| 			slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i) | ||||
| 			slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset)) | ||||
| 			slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset) | ||||
| 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i) | ||||
| 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | ||||
| 			if bucket.tophash[i] == tophash { | ||||
| 				// This could be the key we're looking for. | ||||
| 				if key == *slotKey { | ||||
| 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||
| 					// Found the key, copy it. | ||||
| 					memcpy(value, slotValue, uintptr(m.valueSize)) | ||||
| 					return | ||||
|  | @ -150,3 +149,36 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) { | |||
| 	// Did not find the key. | ||||
| 	memzero(value, uintptr(m.valueSize)) | ||||
| } | ||||
| 
 | ||||
| // Hashmap with plain binary data keys (not containing strings etc.). | ||||
| 
 | ||||
| func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { | ||||
| 	hash := hashmapHash(key, uintptr(m.keySize)) | ||||
| 	hashmapSet(m, key, value, hash, memequal) | ||||
| } | ||||
| 
 | ||||
| func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) { | ||||
| 	hash := hashmapHash(key, uintptr(m.keySize)) | ||||
| 	hashmapGet(m, key, value, hash, memequal) | ||||
| } | ||||
| 
 | ||||
| // Hashmap with string keys (a common case). | ||||
| 
 | ||||
| func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool { | ||||
| 	return *(*string)(x) == *(*string)(y) | ||||
| } | ||||
| 
 | ||||
| func hashmapStringHash(s string) uint32 { | ||||
| 	_s := (*_string)(unsafe.Pointer(&s)) | ||||
| 	return hashmapHash(unsafe.Pointer(_s.ptr), uintptr(_s.length)) | ||||
| } | ||||
| 
 | ||||
| func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { | ||||
| 	hash := hashmapStringHash(key) | ||||
| 	hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual) | ||||
| } | ||||
| 
 | ||||
| func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) { | ||||
| 	hash := hashmapStringHash(key) | ||||
| 	hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual) | ||||
| } | ||||
|  |  | |||
|  | @ -35,6 +35,18 @@ func memzero(ptr unsafe.Pointer, size uintptr) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Compare two same-size buffers for equality. | ||||
| func memequal(x, y unsafe.Pointer, n uintptr) bool { | ||||
| 	for i := uintptr(0); i < n; i++ { | ||||
| 		cx := *(*uint8)(unsafe.Pointer(uintptr(x) + i)) | ||||
| 		cy := *(*uint8)(unsafe.Pointer(uintptr(y) + i)) | ||||
| 		if cx != cy { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func _panic(message interface{}) { | ||||
| 	printstring("panic: ") | ||||
| 	printitf(message) | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem