goroutine support; llgo/ssa: memory (malloc/free)
This commit is contained in:
12
c/c.go
12
c/c.go
@@ -63,6 +63,9 @@ func Unreachable()
|
||||
//go:linkname Malloc C.malloc
|
||||
func Malloc(size uintptr) Pointer
|
||||
|
||||
//go:linkname Free C.free
|
||||
func Free(ptr Pointer)
|
||||
|
||||
//go:linkname Memcpy C.memcpy
|
||||
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
|
||||
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
|
||||
func Time(*int32) int32
|
||||
|
||||
//go:linkname Usleep C.usleep
|
||||
func Usleep(useconds Uint) Int
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Option struct {
|
||||
|
||||
@@ -39,27 +39,29 @@ _llgo_0:
|
||||
%7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1
|
||||
store ptr %3, ptr %7, align 8
|
||||
%8 = load { ptr, ptr }, ptr %5, align 8
|
||||
%9 = extractvalue { ptr, ptr } %8, 1
|
||||
%10 = extractvalue { ptr, ptr } %8, 0
|
||||
call void %10(ptr %9)
|
||||
%9 = call ptr @malloc(i64 16)
|
||||
%10 = getelementptr inbounds { { ptr, ptr } }, ptr %9, i32 0, i32 0
|
||||
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
|
||||
|
||||
_llgo_1: ; preds = %_llgo_3
|
||||
%11 = 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
|
||||
store ptr @0, ptr %12, align 8
|
||||
%13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1
|
||||
store i64 1, ptr %13, align 4
|
||||
%14 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %14)
|
||||
%13 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 0
|
||||
store ptr @0, ptr %14, align 8
|
||||
%15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %13, i32 0, i32 1
|
||||
store i64 1, ptr %15, align 4
|
||||
%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" %16)
|
||||
br label %_llgo_3
|
||||
|
||||
_llgo_2: ; preds = %_llgo_3
|
||||
ret i32 0
|
||||
|
||||
_llgo_3: ; preds = %_llgo_1, %_llgo_0
|
||||
%15 = load i1, ptr %2, align 1
|
||||
br i1 %15, label %_llgo_2, label %_llgo_1
|
||||
%17 = load i1, ptr %2, align 1
|
||||
br i1 %17, label %_llgo_2, label %_llgo_1
|
||||
}
|
||||
|
||||
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 @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.PrintByte"(i8)
|
||||
|
||||
420
ssa/expr.go
420
ssa/expr.go
@@ -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
|
||||
// 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
|
||||
func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
|
||||
return b.Call(fn, args...)
|
||||
@@ -958,51 +784,6 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) {
|
||||
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.
|
||||
//
|
||||
// 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
114
ssa/goroutine.go
Normal 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
235
ssa/memory.go
Normal 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
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -163,6 +163,12 @@ type aProgram struct {
|
||||
loadPyModS *types.Signature
|
||||
getAttrStr *types.Signature
|
||||
|
||||
mallocTy *types.Signature
|
||||
freeTy *types.Signature
|
||||
|
||||
createThdTy *types.Signature
|
||||
routineTy *types.Signature
|
||||
|
||||
paramObjPtr_ *types.Var
|
||||
|
||||
ptrSize int
|
||||
@@ -511,6 +517,8 @@ func (p Program) Uint64() Type {
|
||||
// and unspecified other things too.
|
||||
type aPackage struct {
|
||||
mod llvm.Module
|
||||
abi abi.Builder
|
||||
abiTypes
|
||||
|
||||
vars map[string]Global
|
||||
fns map[string]Function
|
||||
@@ -519,8 +527,7 @@ type aPackage struct {
|
||||
pymods map[string]Global
|
||||
Prog Program
|
||||
|
||||
abi abi.Builder
|
||||
abiTypes
|
||||
iRoutine int
|
||||
}
|
||||
|
||||
type Package = *aPackage
|
||||
@@ -532,6 +539,10 @@ func (p Package) rtFunc(fnName string) 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 {
|
||||
p.Prog.NeedPyInit = true
|
||||
return p.NewFunc(fullName, sig, InC).Expr
|
||||
|
||||
@@ -142,21 +142,6 @@ func (b Builder) Unreachable() {
|
||||
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.
|
||||
func (b Builder) Return(results ...Expr) {
|
||||
if debugInstr {
|
||||
|
||||
Reference in New Issue
Block a user