build: separate compiler and libs
This commit is contained in:
367
compiler/ssa/memory.go
Normal file
367
compiler/ssa/memory.go
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (b Builder) aggregateAllocU(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.allocUninited(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) aggregateAlloca(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.Alloca(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) aggregateMalloc(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.malloc(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
// aggregateValue yields the value of the aggregate X with the fields
|
||||
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
|
||||
return Expr{aggregateValue(b.impl, t.ll, flds...), t}
|
||||
}
|
||||
|
||||
func aggregateValue(b llvm.Builder, tll llvm.Type, flds ...llvm.Value) llvm.Value {
|
||||
agg := llvm.Undef(tll)
|
||||
for i, fld := range flds {
|
||||
agg = b.CreateInsertValue(agg, fld, i, "")
|
||||
}
|
||||
return agg
|
||||
}
|
||||
|
||||
func aggregateInit(b llvm.Builder, ptr llvm.Value, tll llvm.Type, flds ...llvm.Value) {
|
||||
for i, fld := range flds {
|
||||
b.CreateStore(fld, llvm.CreateStructGEP(b, tll, ptr, i))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (b Builder) dupMalloc(v Expr) Expr {
|
||||
prog := b.Prog
|
||||
n := prog.SizeOf(v.Type)
|
||||
tptr := prog.Pointer(v.Type)
|
||||
ptr := b.malloc(prog.Val(uintptr(n))).impl
|
||||
b.Store(Expr{ptr, tptr}, v)
|
||||
return Expr{ptr, tptr}
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Alloc instruction reserves space for a variable of the given type,
|
||||
// zero-initializes it, and yields its address.
|
||||
//
|
||||
// If heap is false, Alloc zero-initializes the same local variable in
|
||||
// the call frame and returns its address; in this case the Alloc must
|
||||
// be present in Function.Locals. We call this a "local" alloc.
|
||||
//
|
||||
// If heap is true, Alloc allocates a new zero-initialized variable
|
||||
// each time the instruction is executed. We call this a "new" alloc.
|
||||
//
|
||||
// When Alloc is applied to a channel, map or slice type, it returns
|
||||
// the address of an uninitialized (nil) reference of that kind; store
|
||||
// the result of MakeSlice, MakeMap or MakeChan in that location to
|
||||
// instantiate these types.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t0 = local int
|
||||
// t1 = new int
|
||||
func (b Builder) Alloc(elem Type, heap bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloc %v, %v\n", elem.RawType(), heap)
|
||||
}
|
||||
prog := b.Prog
|
||||
pkg := b.Pkg
|
||||
size := SizeOf(prog, elem)
|
||||
if heap {
|
||||
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
|
||||
} else {
|
||||
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
|
||||
ret.impl = b.zeroinit(ret, size).impl
|
||||
}
|
||||
ret.Type = prog.Pointer(elem)
|
||||
return
|
||||
}
|
||||
|
||||
// AllocU allocates uninitialized space for n*sizeof(elem) bytes.
|
||||
func (b Builder) AllocU(elem Type, n ...int64) (ret Expr) {
|
||||
prog := b.Prog
|
||||
size := SizeOf(prog, elem, n...)
|
||||
return Expr{b.allocUninited(size).impl, prog.Pointer(elem)}
|
||||
}
|
||||
|
||||
func (b Builder) allocUninited(size Expr) (ret Expr) {
|
||||
return b.InlineCall(b.Pkg.rtFunc("AllocU"), size)
|
||||
}
|
||||
|
||||
// AllocZ allocates zero initialized space for n bytes.
|
||||
func (b Builder) AllocZ(n Expr) (ret Expr) {
|
||||
return b.InlineCall(b.Pkg.rtFunc("AllocZ"), n)
|
||||
}
|
||||
|
||||
// Alloca allocates uninitialized space for n bytes.
|
||||
func (b Builder) Alloca(n Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloca %v\n", n.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
telem := prog.tyInt8()
|
||||
ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl)
|
||||
ret.Type = prog.VoidPtr()
|
||||
return
|
||||
}
|
||||
|
||||
func (b Builder) AllocaT(t Type) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaT %v\n", t.RawType())
|
||||
}
|
||||
prog := b.Prog
|
||||
ret.impl = llvm.CreateAlloca(b.impl, t.ll)
|
||||
ret.Type = prog.Pointer(t)
|
||||
return
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// AllocaU allocates uninitialized space for n*sizeof(elem) bytes.
|
||||
func (b Builder) AllocaU(elem Type, n ...int64) (ret Expr) {
|
||||
prog := b.Prog
|
||||
size := SizeOf(prog, elem, n...)
|
||||
return Expr{b.Alloca(size).impl, prog.Pointer(elem)}
|
||||
}
|
||||
*/
|
||||
|
||||
// AllocaCStr allocates space for copy it from a Go string.
|
||||
func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaCStr %v\n", gostr.impl)
|
||||
}
|
||||
n := b.StringLen(gostr)
|
||||
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
|
||||
cstr := b.Alloca(n1)
|
||||
return b.InlineCall(b.Pkg.rtFunc("CStrCopy"), cstr, gostr)
|
||||
}
|
||||
|
||||
// func allocaCStrs(strs []string, endWithNil bool) **int8
|
||||
func (b Builder) AllocaCStrs(strs Expr, endWithNil bool) (cstrs Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaCStrs %v, %v\n", strs.impl, endWithNil)
|
||||
}
|
||||
prog := b.Prog
|
||||
n := b.SliceLen(strs)
|
||||
n1 := n
|
||||
if endWithNil {
|
||||
n1 = b.BinOp(token.ADD, n, prog.Val(1))
|
||||
}
|
||||
tcstr := prog.CStr()
|
||||
cstrs = b.ArrayAlloca(tcstr, n1)
|
||||
b.Times(n, func(i Expr) {
|
||||
pstr := b.IndexAddr(strs, i)
|
||||
s := b.Load(pstr)
|
||||
b.Store(b.Advance(cstrs, i), b.AllocaCStr(s))
|
||||
})
|
||||
if endWithNil {
|
||||
b.Store(b.Advance(cstrs, n), prog.Nil(tcstr))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (p Program) tyMalloc() *types.Signature {
|
||||
if p.mallocTy == nil {
|
||||
paramSize := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramSize)
|
||||
results := types.NewTuple(paramPtr)
|
||||
p.mallocTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.mallocTy
|
||||
}
|
||||
|
||||
func (p Program) tyFree() *types.Signature {
|
||||
if p.freeTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramPtr)
|
||||
p.freeTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.freeTy
|
||||
}
|
||||
|
||||
func (p Program) tyMemsetInline() *types.Signature {
|
||||
if p.memsetInlineTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
paramInt := types.NewParam(token.NoPos, nil, "", p.Byte().raw.Type)
|
||||
paramSize := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
|
||||
paramBool := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
params := types.NewTuple(paramPtr, paramInt, paramSize, paramBool)
|
||||
p.memsetInlineTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.memsetInlineTy
|
||||
}
|
||||
|
||||
func (b Builder) malloc(size Expr) Expr {
|
||||
fn := b.Pkg.cFunc("malloc", b.Prog.tyMalloc())
|
||||
return b.Call(fn, size)
|
||||
}
|
||||
|
||||
func (b Builder) free(ptr Expr) Expr {
|
||||
fn := b.Pkg.cFunc("free", b.Prog.tyFree())
|
||||
return b.Call(fn, ptr)
|
||||
}
|
||||
|
||||
// declare void @llvm.memset.inline.p0.p0i8.i32(ptr <dest>, i8 <val>, i32 <len>, i1 <isvolatile>)
|
||||
// declare void @llvm.memset.inline.p0.p0.i64(ptr <dest>, i8 <val>, i64 <len>, i1 <isvolatile>)
|
||||
func (b Builder) memset(ptr, val, len, isvolatile Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.memset", b.Prog.tyMemsetInline())
|
||||
b.Call(fn, ptr, val, len, isvolatile)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) zeroinit(ptr, size Expr) Expr {
|
||||
return b.memset(ptr, b.Prog.IntVal(0, b.Prog.Byte()), size, b.Prog.Val(false))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// ArrayAlloca reserves space for an array of n elements of type telem.
|
||||
func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("ArrayAlloca %v, %v\n", telem.raw.Type, n.impl)
|
||||
}
|
||||
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
|
||||
ret.Type = b.Prog.Pointer(telem)
|
||||
return
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// ArrayAlloc allocates zero initialized space for an array of n elements of type telem.
|
||||
func (b Builder) ArrayAlloc(telem Type, n Expr) (ret Expr) {
|
||||
prog := b.Prog
|
||||
elemSize := SizeOf(prog, telem)
|
||||
size := b.BinOp(token.MUL, n, elemSize)
|
||||
ret.impl = b.AllocZ(size).impl
|
||||
ret.Type = prog.Pointer(telem)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// AtomicOp is an atomic operation.
|
||||
type AtomicOp = llvm.AtomicRMWBinOp
|
||||
|
||||
const (
|
||||
OpXchg = llvm.AtomicRMWBinOpXchg
|
||||
OpAdd = llvm.AtomicRMWBinOpAdd
|
||||
OpSub = llvm.AtomicRMWBinOpSub
|
||||
OpAnd = llvm.AtomicRMWBinOpAnd
|
||||
OpNand = llvm.AtomicRMWBinOpNand
|
||||
OpOr = llvm.AtomicRMWBinOpOr
|
||||
OpXor = llvm.AtomicRMWBinOpXor
|
||||
OpMax = llvm.AtomicRMWBinOpMax
|
||||
OpMin = llvm.AtomicRMWBinOpMin
|
||||
OpUMax = llvm.AtomicRMWBinOpUMax
|
||||
OpUMin = llvm.AtomicRMWBinOpUMin
|
||||
)
|
||||
|
||||
// Atomic performs an atomic operation on the memory location pointed to by ptr.
|
||||
func (b Builder) Atomic(op AtomicOp, ptr, val Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Atomic %v, %v, %v\n", op, ptr.impl, val.impl)
|
||||
}
|
||||
t := b.Prog.Elem(ptr.Type)
|
||||
val = b.ChangeType(t, val)
|
||||
ret := b.impl.CreateAtomicRMW(op, ptr.impl, val.impl, llvm.AtomicOrderingSequentiallyConsistent, false)
|
||||
return Expr{ret, t}
|
||||
}
|
||||
|
||||
// AtomicCmpXchg performs an atomic compare-and-swap operation on the memory location pointed to by ptr.
|
||||
func (b Builder) AtomicCmpXchg(ptr, old, new Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("AtomicCmpXchg %v, %v, %v\n", ptr.impl, old.impl, new.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
t := prog.Elem(ptr.Type)
|
||||
old = b.ChangeType(t, old)
|
||||
new = b.ChangeType(t, new)
|
||||
ret := b.impl.CreateAtomicCmpXchg(
|
||||
ptr.impl, old.impl, new.impl,
|
||||
llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, false)
|
||||
return Expr{ret, prog.Struct(t, prog.Bool())}
|
||||
}
|
||||
|
||||
// Load returns the value at the pointer ptr.
|
||||
func (b Builder) Load(ptr Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Load %v\n", ptr.impl)
|
||||
}
|
||||
if ptr.kind == vkPyVarRef {
|
||||
return b.pyLoad(ptr)
|
||||
}
|
||||
telem := b.Prog.Elem(ptr.Type)
|
||||
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
|
||||
}
|
||||
|
||||
// Store stores val at the pointer ptr.
|
||||
func (b Builder) Store(ptr, val Expr) Expr {
|
||||
raw := ptr.raw.Type
|
||||
if debugInstr {
|
||||
log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
|
||||
}
|
||||
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
|
||||
return Expr{b.impl.CreateStore(val.impl, ptr.impl), b.Prog.Void()}
|
||||
}
|
||||
|
||||
// Advance returns the pointer ptr advanced by offset.
|
||||
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
|
||||
}
|
||||
var elem llvm.Type
|
||||
var prog = b.Prog
|
||||
switch t := ptr.raw.Type.(type) {
|
||||
case *types.Basic: // void
|
||||
elem = prog.tyInt8()
|
||||
default:
|
||||
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
|
||||
}
|
||||
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
|
||||
return Expr{ret, ptr.Type}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user