goroutine support; llgo/ssa: memory (malloc/free)

This commit is contained in:
xushiwei
2024-06-01 15:52:54 +08:00
parent 33ba94e784
commit e5802853c0
7 changed files with 535 additions and 319 deletions

12
c/c.go
View File

@@ -63,6 +63,9 @@ func Unreachable()
//go:linkname Malloc C.malloc //go:linkname Malloc C.malloc
func Malloc(size uintptr) Pointer func Malloc(size uintptr) Pointer
//go:linkname Free C.free
func Free(ptr Pointer)
//go:linkname Memcpy C.memcpy //go:linkname Memcpy C.memcpy
func Memcpy(dst, src Pointer, n uintptr) Pointer func Memcpy(dst, src Pointer, n uintptr) Pointer
@@ -115,11 +118,20 @@ func Fwrite(data Pointer, size, count uintptr, fp FilePtr) uintptr
//go:linkname Fputc C.fputc //go:linkname Fputc C.fputc
func Fputc(c Int, fp FilePtr) Int func Fputc(c Int, fp FilePtr) Int
//go:linkname Fputs C.fputs
func Fputs(s *Char, fp FilePtr) Int
//go:linkname Fflush C.fflush
func Fflush(fp FilePtr) Int
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname Time C.time //go:linkname Time C.time
func Time(*int32) int32 func Time(*int32) int32
//go:linkname Usleep C.usleep
func Usleep(useconds Uint) Int
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type Option struct { type Option struct {

View File

@@ -39,27 +39,29 @@ _llgo_0:
%7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1 %7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1
store ptr %3, ptr %7, align 8 store ptr %3, ptr %7, align 8
%8 = load { ptr, ptr }, ptr %5, align 8 %8 = load { ptr, ptr }, ptr %5, align 8
%9 = extractvalue { ptr, ptr } %8, 1 %9 = call ptr @malloc(i64 16)
%10 = extractvalue { ptr, ptr } %8, 0 %10 = getelementptr inbounds { { ptr, ptr } }, ptr %9, i32 0, i32 0
call void %10(ptr %9) store { ptr, ptr } %8, ptr %10, align 8
%11 = alloca i8, i64 8, align 1
%12 = call i32 @pthread_create(ptr %11, ptr null, ptr @"main._llgo_routine$1", ptr %9)
br label %_llgo_3 br label %_llgo_3
_llgo_1: ; preds = %_llgo_3 _llgo_1: ; preds = %_llgo_3
%11 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 %13 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 0 %14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 0
store ptr @0, ptr %12, align 8 store ptr @0, ptr %14, align 8
%13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1 %15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 1
store i64 1, ptr %13, align 4 store i64 1, ptr %15, align 4
%14 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8 %16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %13, align 8
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %14) call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %16)
br label %_llgo_3 br label %_llgo_3
_llgo_2: ; preds = %_llgo_3 _llgo_2: ; preds = %_llgo_3
ret i32 0 ret i32 0
_llgo_3: ; preds = %_llgo_1, %_llgo_0 _llgo_3: ; preds = %_llgo_1, %_llgo_0
%15 = load i1, ptr %2, align 1 %17 = load i1, ptr %2, align 1
br i1 %15, label %_llgo_2, label %_llgo_1 br i1 %17, label %_llgo_2, label %_llgo_1
} }
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
@@ -84,6 +86,23 @@ _llgo_0:
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64)
declare ptr @malloc(i64)
define ptr @"main._llgo_routine$1"(ptr %0) {
_llgo_0:
%1 = load { { ptr, ptr } }, ptr %0, align 8
%2 = extractvalue { { ptr, ptr } } %1, 0
%3 = extractvalue { ptr, ptr } %2, 1
%4 = extractvalue { ptr, ptr } %2, 0
call void %4(ptr %3)
call void @free({ { ptr, ptr } } %1)
ret ptr null
}
declare void @free(ptr)
declare i32 @pthread_create(ptr, ptr, ptr, ptr)
declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String")
declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8)

View File

@@ -443,251 +443,6 @@ func (b Builder) UnOp(op token.Token, x Expr) (ret Expr) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
func checkExpr(v Expr, t types.Type, b Builder) Expr {
if t, ok := t.(*types.Struct); ok && isClosure(t) {
if v.kind != vkClosure {
return b.Pkg.closureStub(b, t, v)
}
}
return v
}
func needsNegativeCheck(x Expr) bool {
if x.kind == vkSigned {
if rv := x.impl.IsAConstantInt(); !rv.IsNil() && rv.SExtValue() >= 0 {
return false
}
return true
}
return false
}
func llvmParamsEx(data Expr, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
if data.IsNil() {
return llvmParams(0, vals, params, b)
}
ret = llvmParams(1, vals, params, b)
ret[0] = data.impl
return
}
func llvmParams(base int, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
n := params.Len()
if n > 0 {
ret = make([]llvm.Value, len(vals)+base)
for idx, v := range vals {
i := base + idx
if i < n {
v = checkExpr(v, params.At(i).Type(), b)
}
ret[i] = v.impl
}
}
return
}
func llvmFields(vals []Expr, t *types.Struct, b Builder) (ret []llvm.Value) {
n := t.NumFields()
if n > 0 {
ret = make([]llvm.Value, len(vals))
for i, v := range vals {
if i < n {
v = checkExpr(v, t.Field(i).Type(), b)
}
ret[i] = v.impl
}
}
return
}
// -----------------------------------------------------------------------------
// 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}
}
// 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) Builder {
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)
b.impl.CreateStore(val.impl, ptr.impl)
return b
}
func (b Builder) aggregateAlloc(t Type, flds ...llvm.Value) llvm.Value {
prog := b.Prog
size := prog.SizeOf(t)
ptr := b.InlineCall(b.Pkg.rtFunc("AllocU"), prog.IntVal(size, prog.Uintptr())).impl
tll := t.ll
impl := b.impl
for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
}
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 {
ptr := llvm.CreateAlloca(b, tll)
for i, fld := range flds {
b.CreateStore(fld, llvm.CreateStructGEP(b, tll, ptr, i))
}
return llvm.CreateLoad(b, tll, ptr)
}
// The MakeClosure instruction yields a closure value whose code is
// Fn and whose free variables' values are supplied by Bindings.
//
// Type() returns a (possibly named) *types.Signature.
//
// Example printed form:
//
// t0 = make closure anon@1.2 [x y z]
// t1 = make closure bound$(main.I).add [i]
func (b Builder) MakeClosure(fn Expr, bindings []Expr) Expr {
if debugInstr {
log.Printf("MakeClosure %v, %v\n", fn, bindings)
}
prog := b.Prog
tfn := fn.Type
sig := tfn.raw.Type.(*types.Signature)
tctx := sig.Params().At(0).Type().Underlying().(*types.Pointer).Elem().(*types.Struct)
flds := llvmFields(bindings, tctx, b)
data := b.aggregateAlloc(prog.rawType(tctx), flds...)
return b.aggregateValue(prog.Closure(tfn), fn.impl, data)
}
// -----------------------------------------------------------------------------
// 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.InlineCall(pkg.rtFunc("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...)
ret = b.InlineCall(b.Pkg.rtFunc("AllocU"), size)
ret.Type = prog.Pointer(elem)
return
}
// 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
}
// 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)
}
/*
// 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.t, n.impl)
}
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
ret.Type = b.Prog.Pointer(telem)
return
}
*/
// 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
}
// -----------------------------------------------------------------------------
// The ChangeType instruction applies to X a value-preserving type // The ChangeType instruction applies to X a value-preserving type
// change to Type(). // change to Type().
// //
@@ -876,6 +631,77 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// The Range instruction yields an iterator over the domain and range
// of X, which must be a string or map.
//
// Elements are accessed via Next.
//
// Type() returns an opaque and degenerate "rangeIter" type.
//
// Pos() returns the ast.RangeStmt.For.
//
// Example printed form:
//
// t0 = range "hello":string
func (b Builder) Range(x Expr) Expr {
switch x.kind {
case vkString:
return b.InlineCall(b.Pkg.rtFunc("NewStringIter"), x)
}
panic("todo")
}
// The Next instruction reads and advances the (map or string)
// iterator Iter and returns a 3-tuple value (ok, k, v). If the
// iterator is not exhausted, ok is true and k and v are the next
// elements of the domain and range, respectively. Otherwise ok is
// false and k and v are undefined.
//
// Components of the tuple are accessed using Extract.
//
// The IsString field distinguishes iterators over strings from those
// over maps, as the Type() alone is insufficient: consider
// map[int]rune.
//
// Type() returns a *types.Tuple for the triple (ok, k, v).
// The types of k and/or v may be types.Invalid.
//
// Example printed form:
//
// t1 = next t0
func (b Builder) Next(iter Expr, isString bool) (ret Expr) {
if isString {
return b.InlineCall(b.Pkg.rtFunc("StringIterNext"), iter)
}
panic("todo")
}
// -----------------------------------------------------------------------------
// The MakeClosure instruction yields a closure value whose code is
// Fn and whose free variables' values are supplied by Bindings.
//
// Type() returns a (possibly named) *types.Signature.
//
// Example printed form:
//
// t0 = make closure anon@1.2 [x y z]
// t1 = make closure bound$(main.I).add [i]
func (b Builder) MakeClosure(fn Expr, bindings []Expr) Expr {
if debugInstr {
log.Printf("MakeClosure %v, %v\n", fn, bindings)
}
prog := b.Prog
tfn := fn.Type
sig := tfn.raw.Type.(*types.Signature)
tctx := sig.Params().At(0).Type().Underlying().(*types.Pointer).Elem().(*types.Struct)
flds := llvmFields(bindings, tctx, b)
data := b.aggregateAllocU(prog.rawType(tctx), flds...)
return b.aggregateValue(prog.Closure(tfn), fn.impl, data)
}
// -----------------------------------------------------------------------------
// TODO(xsw): make inline call // TODO(xsw): make inline call
func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
return b.Call(fn, args...) return b.Call(fn, args...)
@@ -958,51 +784,6 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) {
return return
} }
// The Range instruction yields an iterator over the domain and range
// of X, which must be a string or map.
//
// Elements are accessed via Next.
//
// Type() returns an opaque and degenerate "rangeIter" type.
//
// Pos() returns the ast.RangeStmt.For.
//
// Example printed form:
//
// t0 = range "hello":string
func (b Builder) Range(x Expr) Expr {
switch x.kind {
case vkString:
return b.InlineCall(b.Pkg.rtFunc("NewStringIter"), x)
}
panic("todo")
}
// The Next instruction reads and advances the (map or string)
// iterator Iter and returns a 3-tuple value (ok, k, v). If the
// iterator is not exhausted, ok is true and k and v are the next
// elements of the domain and range, respectively. Otherwise ok is
// false and k and v are undefined.
//
// Components of the tuple are accessed using Extract.
//
// The IsString field distinguishes iterators over strings from those
// over maps, as the Type() alone is insufficient: consider
// map[int]rune.
//
// Type() returns a *types.Tuple for the triple (ok, k, v).
// The types of k and/or v may be types.Invalid.
//
// Example printed form:
//
// t1 = next t0
func (b Builder) Next(iter Expr, isString bool) (ret Expr) {
if isString {
return b.InlineCall(b.Pkg.rtFunc("StringIterNext"), iter)
}
panic("todo")
}
// A Builtin represents a specific use of a built-in function, e.g. len. // A Builtin represents a specific use of a built-in function, e.g. len.
// //
// Builtins are immutable values. Builtins do not have addresses. // Builtins are immutable values. Builtins do not have addresses.
@@ -1139,3 +920,62 @@ func (b Builder) PrintEx(ln bool, args ...Expr) (ret Expr) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
func checkExpr(v Expr, t types.Type, b Builder) Expr {
if t, ok := t.(*types.Struct); ok && isClosure(t) {
if v.kind != vkClosure {
return b.Pkg.closureStub(b, t, v)
}
}
return v
}
func needsNegativeCheck(x Expr) bool {
if x.kind == vkSigned {
if rv := x.impl.IsAConstantInt(); !rv.IsNil() && rv.SExtValue() >= 0 {
return false
}
return true
}
return false
}
func llvmParamsEx(data Expr, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
if data.IsNil() {
return llvmParams(0, vals, params, b)
}
ret = llvmParams(1, vals, params, b)
ret[0] = data.impl
return
}
func llvmParams(base int, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
n := params.Len()
if n > 0 {
ret = make([]llvm.Value, len(vals)+base)
for idx, v := range vals {
i := base + idx
if i < n {
v = checkExpr(v, params.At(i).Type(), b)
}
ret[i] = v.impl
}
}
return
}
func llvmFields(vals []Expr, t *types.Struct, b Builder) (ret []llvm.Value) {
n := t.NumFields()
if n > 0 {
ret = make([]llvm.Value, len(vals))
for i, v := range vals {
if i < n {
v = checkExpr(v, t.Field(i).Type(), b)
}
ret[i] = v.impl
}
}
return
}
// -----------------------------------------------------------------------------

114
ssa/goroutine.go Normal file
View File

@@ -0,0 +1,114 @@
/*
* 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"
"strconv"
"github.com/goplus/llvm"
)
// -----------------------------------------------------------------------------
// func(c.Pointer) c.Pointer
func (p Program) tyRoutine() *types.Signature {
if p.routineTy == nil {
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
params := types.NewTuple(paramPtr)
p.routineTy = types.NewSignatureType(nil, nil, nil, params, params, false)
}
return p.routineTy
}
// func(pthread *Thread, attr *Attr, routine func(c.Pointer) c.Pointer, arg c.Pointer) c.Int
func (p Program) tyPthreadCreate() *types.Signature {
if p.createThdTy == nil {
paramPPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtrPtr().raw.Type)
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
paramRoutine := types.NewParam(token.NoPos, nil, "", p.tyRoutine())
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
params := types.NewTuple(paramPPtr, paramPtr, paramRoutine, paramPtr)
results := types.NewTuple(paramCInt)
p.createThdTy = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.createThdTy
}
func (b Builder) createThread(pp, attr, routine, arg Expr) Expr {
fn := b.Pkg.cFunc("pthread_create", b.Prog.tyPthreadCreate())
return b.Call(fn, pp, attr, routine, arg)
}
// -----------------------------------------------------------------------------
// The Go instruction creates a new goroutine and calls the specified
// function within it.
//
// Example printed form:
//
// go println(t0, t1)
// go t3()
// go invoke t5.Println(...t6)
func (b Builder) Go(fn Expr, args ...Expr) {
if debugInstr {
logCall("Go", fn, args)
}
prog := b.Prog
pkg := b.Pkg
typs := make([]Type, len(args)+1)
flds := make([]llvm.Value, len(args)+1)
typs[0] = fn.Type
flds[0] = fn.impl
for i, arg := range args {
typs[i+1] = arg.Type
flds[i+1] = arg.impl
}
t := prog.Struct(typs...)
voidPtr := prog.VoidPtr()
data := Expr{b.aggregateMalloc(t, flds...), voidPtr}
size := prog.SizeOf(voidPtr)
pthd := b.Alloca(prog.IntVal(uint64(size), prog.Uintptr()))
b.createThread(pthd, prog.Null(voidPtr), pkg.routine(t, len(args)), data)
}
func (p Package) routineName() string {
p.iRoutine++
return p.Path() + "._llgo_routine$" + strconv.Itoa(p.iRoutine)
}
func (p Package) routine(t Type, n int) Expr {
prog := p.Prog
routine := p.NewFunc(p.routineName(), prog.tyRoutine(), InC)
b := routine.MakeBody(1)
param := routine.Param(0)
data := Expr{llvm.CreateLoad(b.impl, t.ll, param.impl), t}
args := make([]Expr, n)
fn := b.getField(data, 0)
for i := 0; i < n; i++ {
args[i] = b.getField(data, i+1)
}
b.Call(fn, args...)
b.free(data)
b.Return(prog.Null(prog.VoidPtr()))
return routine.Expr
}
// -----------------------------------------------------------------------------

235
ssa/memory.go Normal file
View File

@@ -0,0 +1,235 @@
/*
* 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"
)
// -----------------------------------------------------------------------------
// 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}
}
// 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) Builder {
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)
b.impl.CreateStore(val.impl, ptr.impl)
return b
}
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) 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 {
ptr := llvm.CreateAlloca(b, tll)
aggregateInit(b, ptr, tll, flds...)
return llvm.CreateLoad(b, tll, ptr)
}
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))
}
}
// -----------------------------------------------------------------------------
// 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.InlineCall(pkg.rtFunc("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
}
// 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 (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 (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)
}
// -----------------------------------------------------------------------------
/*
// 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.t, n.impl)
}
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
ret.Type = b.Prog.Pointer(telem)
return
}
*/
// 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
}
// -----------------------------------------------------------------------------

View File

@@ -163,6 +163,12 @@ type aProgram struct {
loadPyModS *types.Signature loadPyModS *types.Signature
getAttrStr *types.Signature getAttrStr *types.Signature
mallocTy *types.Signature
freeTy *types.Signature
createThdTy *types.Signature
routineTy *types.Signature
paramObjPtr_ *types.Var paramObjPtr_ *types.Var
ptrSize int ptrSize int
@@ -511,6 +517,8 @@ func (p Program) Uint64() Type {
// and unspecified other things too. // and unspecified other things too.
type aPackage struct { type aPackage struct {
mod llvm.Module mod llvm.Module
abi abi.Builder
abiTypes
vars map[string]Global vars map[string]Global
fns map[string]Function fns map[string]Function
@@ -519,8 +527,7 @@ type aPackage struct {
pymods map[string]Global pymods map[string]Global
Prog Program Prog Program
abi abi.Builder iRoutine int
abiTypes
} }
type Package = *aPackage type Package = *aPackage
@@ -532,6 +539,10 @@ func (p Package) rtFunc(fnName string) Expr {
return p.NewFunc(name, sig, InGo).Expr return p.NewFunc(name, sig, InGo).Expr
} }
func (p Package) cFunc(fullName string, sig *types.Signature) Expr {
return p.NewFunc(fullName, sig, InC).Expr
}
func (p Package) pyFunc(fullName string, sig *types.Signature) Expr { func (p Package) pyFunc(fullName string, sig *types.Signature) Expr {
p.Prog.NeedPyInit = true p.Prog.NeedPyInit = true
return p.NewFunc(fullName, sig, InC).Expr return p.NewFunc(fullName, sig, InC).Expr

View File

@@ -142,21 +142,6 @@ func (b Builder) Unreachable() {
b.impl.CreateUnreachable() b.impl.CreateUnreachable()
} }
// The Go instruction creates a new goroutine and calls the specified
// function within it.
//
// Example printed form:
//
// go println(t0, t1)
// go t3()
// go invoke t5.Println(...t6)
func (b Builder) Go(fn Expr, args ...Expr) {
if debugInstr {
logCall("Go", fn, args)
}
b.Call(fn, args...)
}
// Return emits a return instruction. // Return emits a return instruction.
func (b Builder) Return(results ...Expr) { func (b Builder) Return(results ...Expr) {
if debugInstr { if debugInstr {