157 lines
5.8 KiB
Go
157 lines
5.8 KiB
Go
//===- maps.go - IR generation for maps -----------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for maps.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package irgen
|
|
|
|
import (
|
|
"llvm.org/llgo/third_party/gotools/go/types"
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
)
|
|
|
|
// makeMap implements make(maptype[, initial space])
|
|
func (fr *frame) makeMap(typ types.Type, cap_ *govalue) *govalue {
|
|
// TODO(pcc): call __go_new_map_big here if needed
|
|
dyntyp := fr.types.getMapDescriptorPointer(typ)
|
|
dyntyp = fr.builder.CreateBitCast(dyntyp, llvm.PointerType(llvm.Int8Type(), 0), "")
|
|
var cap llvm.Value
|
|
if cap_ != nil {
|
|
cap = fr.convert(cap_, types.Typ[types.Uintptr]).value
|
|
} else {
|
|
cap = llvm.ConstNull(fr.types.inttype)
|
|
}
|
|
m := fr.runtime.newMap.call(fr, dyntyp, cap)
|
|
return newValue(m[0], typ)
|
|
}
|
|
|
|
// mapLookup implements v[, ok] = m[k]
|
|
func (fr *frame) mapLookup(m, k *govalue) (v *govalue, ok *govalue) {
|
|
llk := k.value
|
|
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
|
fr.builder.CreateStore(llk, pk)
|
|
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(false))[0]
|
|
attrkind := llvm.AttributeKindID("nocapture")
|
|
valptr.AddCallSiteAttribute(2, fr.types.ctx.CreateEnumAttribute(attrkind, 0))
|
|
attrkind = llvm.AttributeKindID("readonly")
|
|
valptr.AddCallSiteAttribute(2, fr.types.ctx.CreateEnumAttribute(attrkind, 0))
|
|
okbit := fr.builder.CreateIsNotNull(valptr, "")
|
|
|
|
elemtyp := m.Type().Underlying().(*types.Map).Elem()
|
|
ok = newValue(fr.builder.CreateZExt(okbit, llvm.Int8Type(), ""), types.Typ[types.Bool])
|
|
v = fr.loadOrNull(okbit, valptr, elemtyp)
|
|
return
|
|
}
|
|
|
|
// mapUpdate implements m[k] = v
|
|
func (fr *frame) mapUpdate(m, k, v *govalue) {
|
|
llk := k.value
|
|
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
|
fr.builder.CreateStore(llk, pk)
|
|
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(true))[0]
|
|
attrkind := llvm.AttributeKindID("nocapture")
|
|
valptr.AddCallSiteAttribute(2, fr.types.ctx.CreateEnumAttribute(attrkind, 0))
|
|
attrkind = llvm.AttributeKindID("readonly")
|
|
valptr.AddCallSiteAttribute(2, fr.types.ctx.CreateEnumAttribute(attrkind, 0))
|
|
|
|
elemtyp := m.Type().Underlying().(*types.Map).Elem()
|
|
llelemtyp := fr.types.ToLLVM(elemtyp)
|
|
typedvalptr := fr.builder.CreateBitCast(valptr, llvm.PointerType(llelemtyp, 0), "")
|
|
fr.builder.CreateStore(v.value, typedvalptr)
|
|
}
|
|
|
|
// mapDelete implements delete(m, k)
|
|
func (fr *frame) mapDelete(m, k *govalue) {
|
|
llk := k.value
|
|
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
|
fr.builder.CreateStore(llk, pk)
|
|
fr.runtime.mapdelete.call(fr, m.value, pk)
|
|
}
|
|
|
|
// mapIterInit creates a map iterator
|
|
func (fr *frame) mapIterInit(m *govalue) []*govalue {
|
|
// We represent an iterator as a tuple (map, *bool). The second element
|
|
// controls whether the code we generate for "next" (below) calls the
|
|
// runtime function for the first or the next element. We let the
|
|
// optimizer reorganize this into something more sensible.
|
|
isinit := fr.allocaBuilder.CreateAlloca(llvm.Int1Type(), "")
|
|
fr.builder.CreateStore(llvm.ConstNull(llvm.Int1Type()), isinit)
|
|
|
|
return []*govalue{m, newValue(isinit, types.NewPointer(types.Typ[types.Bool]))}
|
|
}
|
|
|
|
// mapIterNext advances the iterator, and returns the tuple (ok, k, v).
|
|
func (fr *frame) mapIterNext(iter []*govalue) []*govalue {
|
|
maptyp := iter[0].Type().Underlying().(*types.Map)
|
|
ktyp := maptyp.Key()
|
|
klltyp := fr.types.ToLLVM(ktyp)
|
|
vtyp := maptyp.Elem()
|
|
vlltyp := fr.types.ToLLVM(vtyp)
|
|
|
|
m, isinitptr := iter[0], iter[1]
|
|
|
|
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
|
mapiterbufty := llvm.ArrayType(i8ptr, 4)
|
|
mapiterbuf := fr.allocaBuilder.CreateAlloca(mapiterbufty, "")
|
|
mapiterbufelem0ptr := fr.builder.CreateStructGEP(mapiterbuf, 0, "")
|
|
|
|
keybuf := fr.allocaBuilder.CreateAlloca(klltyp, "")
|
|
keyptr := fr.builder.CreateBitCast(keybuf, i8ptr, "")
|
|
valbuf := fr.allocaBuilder.CreateAlloca(vlltyp, "")
|
|
valptr := fr.builder.CreateBitCast(valbuf, i8ptr, "")
|
|
|
|
isinit := fr.builder.CreateLoad(isinitptr.value, "")
|
|
|
|
initbb := llvm.AddBasicBlock(fr.function, "")
|
|
nextbb := llvm.AddBasicBlock(fr.function, "")
|
|
contbb := llvm.AddBasicBlock(fr.function, "")
|
|
|
|
fr.builder.CreateCondBr(isinit, nextbb, initbb)
|
|
|
|
fr.builder.SetInsertPointAtEnd(initbb)
|
|
fr.builder.CreateStore(llvm.ConstAllOnes(llvm.Int1Type()), isinitptr.value)
|
|
fr.runtime.mapiterinit.call(fr, m.value, mapiterbufelem0ptr)
|
|
fr.builder.CreateBr(contbb)
|
|
|
|
fr.builder.SetInsertPointAtEnd(nextbb)
|
|
fr.runtime.mapiternext.call(fr, mapiterbufelem0ptr)
|
|
fr.builder.CreateBr(contbb)
|
|
|
|
fr.builder.SetInsertPointAtEnd(contbb)
|
|
mapiterbufelem0 := fr.builder.CreateLoad(mapiterbufelem0ptr, "")
|
|
okbit := fr.builder.CreateIsNotNull(mapiterbufelem0, "")
|
|
ok := fr.builder.CreateZExt(okbit, llvm.Int8Type(), "")
|
|
|
|
loadbb := llvm.AddBasicBlock(fr.function, "")
|
|
cont2bb := llvm.AddBasicBlock(fr.function, "")
|
|
fr.builder.CreateCondBr(okbit, loadbb, cont2bb)
|
|
|
|
fr.builder.SetInsertPointAtEnd(loadbb)
|
|
fr.runtime.mapiter2.call(fr, mapiterbufelem0ptr, keyptr, valptr)
|
|
loadbb = fr.builder.GetInsertBlock()
|
|
loadedkey := fr.builder.CreateLoad(keybuf, "")
|
|
loadedval := fr.builder.CreateLoad(valbuf, "")
|
|
fr.builder.CreateBr(cont2bb)
|
|
|
|
fr.builder.SetInsertPointAtEnd(cont2bb)
|
|
k := fr.builder.CreatePHI(klltyp, "")
|
|
k.AddIncoming(
|
|
[]llvm.Value{llvm.ConstNull(klltyp), loadedkey},
|
|
[]llvm.BasicBlock{contbb, loadbb},
|
|
)
|
|
v := fr.builder.CreatePHI(vlltyp, "")
|
|
v.AddIncoming(
|
|
[]llvm.Value{llvm.ConstNull(vlltyp), loadedval},
|
|
[]llvm.BasicBlock{contbb, loadbb},
|
|
)
|
|
|
|
return []*govalue{newValue(ok, types.Typ[types.Bool]), newValue(k, ktyp), newValue(v, vtyp)}
|
|
}
|