Add integer key support to hashmap
Этот коммит содержится в:
		
							родитель
							
								
									8f7db8661b
								
							
						
					
					
						коммит
						c912091f8b
					
				
					 3 изменённых файлов: 93 добавлений и 42 удалений
				
			
		
							
								
								
									
										61
									
								
								compiler.go
									
										
									
									
									
								
							
							
						
						
									
										61
									
								
								compiler.go
									
										
									
									
									
								
							|  | @ -1100,12 +1100,20 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { | ||||||
| 		mapType := instr.Map.Type().Underlying().(*types.Map) | 		mapType := instr.Map.Type().Underlying().(*types.Map) | ||||||
| 		switch keyType := mapType.Key().Underlying().(type) { | 		switch keyType := mapType.Key().Underlying().(type) { | ||||||
| 		case *types.Basic: | 		case *types.Basic: | ||||||
|  | 			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 { | 			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") |  | ||||||
| 				params := []llvm.Value{m, key, valuePtr} | 				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, "") | 				c.builder.CreateCall(fn, params, "") | ||||||
| 				return nil | 				return nil | ||||||
| 			} else { | 			} else { | ||||||
|  | @ -1588,15 +1596,23 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | ||||||
| 		case *types.Map: | 		case *types.Map: | ||||||
| 			switch keyType := xType.Key().Underlying().(type) { | 			switch keyType := xType.Key().Underlying().(type) { | ||||||
| 			case *types.Basic: | 			case *types.Basic: | ||||||
|  | 				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 { | 				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") |  | ||||||
| 					params := []llvm.Value{value, index, mapValuePtr} | 					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, "") | 					c.builder.CreateCall(fn, params, "") | ||||||
| 					return c.builder.CreateLoad(mapValueAlloca, ""), nil | 					return c.builder.CreateLoad(mapValueAlloca, ""), nil | ||||||
| 				} else { | 				} else { | ||||||
|  | @ -1658,22 +1674,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return llvm.Value{}, err | 			return llvm.Value{}, err | ||||||
| 		} | 		} | ||||||
| 		switch keyType := mapType.Key().Underlying().(type) { | 		keySize := c.targetData.TypeAllocSize(llvmKeyType) | ||||||
| 		case *types.Basic: | 		valueSize := c.targetData.TypeAllocSize(llvmValueType) | ||||||
| 			if keyType.Kind() == types.String { | 		hashmapMake := c.mod.NamedFunction("runtime.hashmapMake") | ||||||
| 				keySize := c.targetData.TypeAllocSize(llvmKeyType) | 		llvmKeySize := llvm.ConstInt(llvm.Int8Type(), keySize, false) | ||||||
| 				valueSize := c.targetData.TypeAllocSize(llvmValueType) | 		llvmValueSize := llvm.ConstInt(llvm.Int8Type(), valueSize, false) | ||||||
| 				hashmapMake := c.mod.NamedFunction("runtime.hashmapMake") | 		hashmap := c.builder.CreateCall(hashmapMake, []llvm.Value{llvmKeySize, llvmValueSize}, "") | ||||||
| 				llvmKeySize := llvm.ConstInt(llvm.Int8Type(), keySize, false) | 		return hashmap, nil | ||||||
| 				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: | 	case *ssa.Phi: | ||||||
| 		t, err := c.getLLVMType(expr.Type()) | 		t, err := c.getLLVMType(expr.Type()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  |  | ||||||
|  | @ -30,14 +30,15 @@ type hashmapBucket struct { | ||||||
| 	// allocated but as they're of variable size they can't be shown here. | 	// 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 | // 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 | 	var result uint32 = 2166136261 // FNV offset basis | ||||||
| 	for i := 0; i < len(*s); i++ { | 	for i := uintptr(0); i < n; i++ { | ||||||
| 		result ^= uint32((*s)[i]) | 		c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i)) | ||||||
| 		result *= 16777619 // FNV prime | 		result ^= uint32(c) // XOR with byte | ||||||
|  | 		result *= 16777619  // FNV prime | ||||||
| 	} | 	} | ||||||
| 	return result | 	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. | // Set a specified key to a given value. Grow the map if necessary. | ||||||
| func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | ||||||
| 	hash := stringhash(&key) |  | ||||||
| 	numBuckets := uintptr(1) << m.bucketBits | 	numBuckets := uintptr(1) << m.bucketBits | ||||||
| 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | ||||||
| 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | 	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) | 	tophash := hashmapTopHash(hash) | ||||||
| 
 | 
 | ||||||
| 	// See whether the key already exists somewhere. | 	// See whether the key already exists somewhere. | ||||||
| 	var emptySlotKey *string | 	var emptySlotKey unsafe.Pointer | ||||||
| 	var emptySlotValue unsafe.Pointer | 	var emptySlotValue unsafe.Pointer | ||||||
| 	var emptySlotTophash *byte | 	var emptySlotTophash *byte | ||||||
| 	for bucket != nil { | 	for bucket != nil { | ||||||
| 		for i := uintptr(0); i < 8; i++ { | 		for i := uintptr(0); i < 8; i++ { | ||||||
| 			slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(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) | 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i) | ||||||
| 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | ||||||
| 			if bucket.tophash[i] == 0 && emptySlotKey == nil { | 			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 { | 			if bucket.tophash[i] == tophash { | ||||||
| 				// Could be an existing value that's the same. | 				// Could be an existing value that's the same. | ||||||
| 				if key == *slotKey { | 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||||
| 					// found same key, replace it | 					// found same key, replace it | ||||||
| 					memcpy(slotValue, value, uintptr(m.valueSize)) | 					memcpy(slotValue, value, uintptr(m.valueSize)) | ||||||
| 					return | 					return | ||||||
|  | @ -105,7 +105,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) { | ||||||
| 	} | 	} | ||||||
| 	if emptySlotKey != nil { | 	if emptySlotKey != nil { | ||||||
| 		m.count++ | 		m.count++ | ||||||
| 		*emptySlotKey = key | 		memcpy(emptySlotKey, key, uintptr(m.keySize)) | ||||||
| 		memcpy(emptySlotValue, value, uintptr(m.valueSize)) | 		memcpy(emptySlotValue, value, uintptr(m.valueSize)) | ||||||
| 		*emptySlotTophash = tophash | 		*emptySlotTophash = tophash | ||||||
| 		return | 		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. | // Get the value of a specified key, or zero the value if not found. | ||||||
| func hashmapGet(m *hashmap, key string, value unsafe.Pointer) { | func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | ||||||
| 	hash := stringhash(&key) |  | ||||||
| 	numBuckets := uintptr(1) << m.bucketBits | 	numBuckets := uintptr(1) << m.bucketBits | ||||||
| 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | ||||||
| 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | 	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 bucket != nil { | ||||||
| 		for i := uintptr(0); i < 8; i++ { | 		for i := uintptr(0); i < 8; i++ { | ||||||
| 			slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(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) | 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i) | ||||||
| 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | ||||||
| 			if bucket.tophash[i] == tophash { | 			if bucket.tophash[i] == tophash { | ||||||
| 				// This could be the key we're looking for. | 				// This could be the key we're looking for. | ||||||
| 				if key == *slotKey { | 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||||
| 					// Found the key, copy it. | 					// Found the key, copy it. | ||||||
| 					memcpy(value, slotValue, uintptr(m.valueSize)) | 					memcpy(value, slotValue, uintptr(m.valueSize)) | ||||||
| 					return | 					return | ||||||
|  | @ -150,3 +149,36 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) { | ||||||
| 	// Did not find the key. | 	// Did not find the key. | ||||||
| 	memzero(value, uintptr(m.valueSize)) | 	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{}) { | func _panic(message interface{}) { | ||||||
| 	printstring("panic: ") | 	printstring("panic: ") | ||||||
| 	printitf(message) | 	printitf(message) | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem