compiler, runtime: implement delete builtin

This commit is contained in:
Ayke van Laethem
2018-10-20 16:18:55 +02:00
parent 7f60dd79ee
commit c0c1ccb381
6 changed files with 179 additions and 73 deletions
+12 -47
View File
@@ -1559,28 +1559,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
return err
}
mapType := instr.Map.Type().Underlying().(*types.Map)
switch keyType := mapType.Key().Underlying().(type) {
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.Info()&types.IsString != 0 {
params := []llvm.Value{m, key, valuePtr}
c.createRuntimeCall("hashmapStringSet", 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}
c.createRuntimeCall("hashmapBinarySet", params, "")
return nil
} else {
return errors.New("todo: map update key type: " + keyType.String())
}
default:
return errors.New("todo: map update key type: " + keyType.String())
}
return c.emitMapUpdate(mapType.Key(), m, key, value)
case *ssa.Panic:
value, err := c.parseExpr(frame, instr.X)
if err != nil {
@@ -1718,6 +1697,16 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
srcBuf = c.builder.CreateBitCast(srcBuf, c.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false)
return c.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
case "delete":
m, err := c.parseExpr(frame, args[0])
if err != nil {
return llvm.Value{}, err
}
key, err := c.parseExpr(frame, args[1])
if err != nil {
return llvm.Value{}, err
}
return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key)
case "len":
value, err := c.parseExpr(frame, args[0])
if err != nil {
@@ -2284,31 +2273,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
bufPtr := c.builder.CreateGEP(buf, []llvm.Value{index}, "")
return c.builder.CreateLoad(bufPtr, ""), nil
case *types.Map:
switch keyType := xType.Key().Underlying().(type) {
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.Info()&types.IsString != 0 {
params := []llvm.Value{value, index, mapValuePtr}
c.createRuntimeCall("hashmapStringGet", 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}
c.createRuntimeCall("hashmapBinaryGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), nil
} else {
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
}
default:
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
}
return c.emitMapLookup(xType.Key(), expr.Type(), value, index)
default:
panic("unknown lookup type: " + expr.String())
}
+107
View File
@@ -0,0 +1,107 @@
package compiler
// This file emits the correct map intrinsics for map operations.
import (
"errors"
"go/types"
"github.com/aykevl/go-llvm"
)
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value) (llvm.Value, error) {
switch keyType := keyType.Underlying().(type) {
case *types.Basic:
llvmValueType, err := c.getLLVMType(valueType)
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.Info()&types.IsString != 0 {
params := []llvm.Value{m, key, mapValuePtr}
c.createRuntimeCall("hashmapStringGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), 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, mapValuePtr}
c.createRuntimeCall("hashmapBinaryGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), nil
} else {
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
}
default:
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
}
}
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) error {
switch keyType := keyType.Underlying().(type) {
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.Info()&types.IsString != 0 {
params := []llvm.Value{m, key, valuePtr}
c.createRuntimeCall("hashmapStringSet", 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}
c.createRuntimeCall("hashmapBinarySet", params, "")
return nil
} else {
return errors.New("todo: map update key type: " + keyType.String())
}
default:
return errors.New("todo: map update key type: " + keyType.String())
}
}
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error {
switch keyType := keyType.Underlying().(type) {
case *types.Basic:
if keyType.Info()&types.IsString != 0 {
params := []llvm.Value{m, key}
c.createRuntimeCall("hashmapStringDelete", 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}
c.createRuntimeCall("hashmapBinaryDelete", params, "")
return nil
} else {
return errors.New("todo: map lookup key type: " + keyType.String())
}
default:
return errors.New("todo: map delete key type: " + keyType.String())
}
}
// Get FNV-1a hash of this string.
//
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
func hashmapHash(data []byte) uint32 {
var result uint32 = 2166136261 // FNV offset basis
for _, c := range data {
result ^= uint32(c)
result *= 16777619 // FNV prime
}
return result
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
if tophash < 1 {
// 0 means empty slot, so make it bigger.
tophash += 1
}
return tophash
}
-26
View File
@@ -1,26 +0,0 @@
package compiler
// This file contains functions that are also used by the runtime. These must be
// kept in sync.
// Get FNV-1a hash of this string.
//
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
func hashmapHash(data []byte) uint32 {
var result uint32 = 2166136261 // FNV offset basis
for _, c := range data {
result ^= uint32(c)
result *= 16777619 // FNV prime
}
return result
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
if tophash < 1 {
// 0 means empty slot, so make it bigger.
tophash += 1
}
return tophash
}