abi.Name; runtime: MakeAnyInt => MakeAnyIntptr; llgo/ssa: AllocU; builtin unsafe.String; MakeInterface; prog.PointerSize

This commit is contained in:
xushiwei
2024-05-20 08:46:39 +08:00
parent a6b8edde62
commit e61ebb4eb9
13 changed files with 342 additions and 63 deletions

73
ssa/_abi/abi.go Normal file
View File

@@ -0,0 +1,73 @@
/*
* 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 abi
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"go/types"
"hash"
)
// Builder is a helper for constructing ABI types.
type Builder struct {
h hash.Hash
buf []byte
}
// New creates a new ABI type Builder.
func New() *Builder {
h := sha256.New()
buf := make([]byte, sha256.Size)
return &Builder{h, buf}
}
// TypeName returns the ABI type name for the specified type.
func (b *Builder) TypeName(t types.Type) string {
switch t := t.(type) {
case *types.Basic:
return t.Name()
case *types.Pointer:
return "*" + b.TypeName(t.Elem())
case *types.Struct:
return b.StructName(t)
}
panic("todo")
}
// StructName returns the ABI type name for the specified struct type.
func (b *Builder) StructName(t *types.Struct) string {
hash := b.structHash(t)
return "struct$" + base64.RawURLEncoding.EncodeToString(hash)
}
func (b *Builder) structHash(t *types.Struct) []byte {
h := b.h
h.Reset()
n := t.NumFields()
fmt.Fprintln(h, "struct", n)
for i := 0; i < n; i++ {
f := t.Field(i)
name := f.Name()
if f.Embedded() {
name = "-"
}
fmt.Fprintln(h, name, b.TypeName(f.Type()))
}
return h.Sum(b.buf[:0])
}

View File

@@ -36,6 +36,9 @@ func TestFromTestdata(t *testing.T) {
func TestRuntime(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/internal/runtime", "../internal/runtime/llgo_autogen.ll")
}
func TestAbi(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll")
}

View File

@@ -665,7 +665,18 @@ func (b Builder) Alloc(elem Type, heap bool) (ret Expr) {
return
}
// Alloca allocates space for n bytes.
// AllocU allocates uninitialized space for n*sizeof(elem) bytes.
func (b Builder) AllocU(elem Type, n ...int64) (ret Expr) {
if debugInstr {
log.Printf("AllocU %v, %v\n", elem.raw.Type, n)
}
size := b.SizeOf(elem, n...)
ret = b.InlineCall(b.Pkg.rtFunc("AllocU"), size)
ret.Type = b.Prog.Pointer(elem)
return
}
// Alloca allocates uninitialized space for n bytes.
func (b Builder) Alloca(n Expr) (ret Expr) {
if debugInstr {
log.Printf("Alloca %v\n", n.impl)
@@ -984,9 +995,10 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
for i, arg := range args {
if ln && i > 0 {
b.InlineCall(b.Pkg.rtFunc("PrintString"), b.Str(" "))
// TODO(visualfc): maybe use PrintCStr is more efficient
}
var fn string
var typ Type
var typ Type // TODO(visualfc): typ uninitialized in some cases
switch arg.kind {
case vkBool:
fn = "PrintBool"
@@ -1002,6 +1014,8 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
case vkSlice:
fn = "PrintSlice"
case vkPtr, vkFuncPtr, vkFuncDecl, vkClosure, vkPyVarRef, vkPyFuncRef:
// TODO(visualfc): vkClosure is not a pointer
// TODO(visualfc): vkPyVarRef, vkPyFuncRef is pointer of pointer
fn = "PrintPointer"
typ = b.Prog.VoidPtr()
case vkString:
@@ -1037,8 +1051,11 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
}
}
}
case "String": // unsafe.String
// TODO(xsw): make this a builtin
return b.InlineCall(b.Pkg.rtFunc("NewString"), args[0], args[1])
}
panic("todo")
panic("todo: " + fn)
}
// -----------------------------------------------------------------------------

View File

@@ -27,19 +27,29 @@ import (
// -----------------------------------------------------------------------------
// AbiBasic returns the abi type of the specified basic kind.
func (b Builder) AbiBasic(kind types.BasicKind) Expr {
// abiBasic returns the abi type of the specified basic kind.
func (b Builder) abiBasic(kind types.BasicKind) Expr {
return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
}
/*
// AbiStruct returns the abi type of the specified struct type.
func (b Builder) AbiStruct(t *types.Struct) Expr {
panic("todo")
// return b.InlineCall(b.Pkg.rtFunc("Struct"), b.Prog.Val(t.NumFields()))
// abiStruct returns the abi type of the specified struct type.
func (b Builder) abiStruct(t *types.Struct) Expr {
// name := "__llgo_" + b.Prog.abi.StructName(t)
}
*/
// AbiType returns the abi type of the specified type.
func (b Builder) AbiType(t Type) Expr {
switch tx := t.raw.Type.(type) {
case *types.Basic:
return b.abiBasic(tx.Kind())
//case *types.Struct:
// return b.abiStruct(tx)
}
panic("todo")
}
// -----------------------------------------------------------------------------
// MakeInterface constructs an instance of an interface type from a
@@ -57,37 +67,66 @@ func (b Builder) AbiStruct(t *types.Struct) Expr {
// t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0
func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
raw := tinter.raw.Type
rawIntf := tinter.raw.Type.Underlying().(*types.Interface)
if debugInstr {
log.Printf("MakeInterface %v, %v\n", raw, x.impl)
log.Printf("MakeInterface %v, %v\n", rawIntf, x.impl)
}
prog := b.Prog
pkg := b.Pkg
switch tx := x.raw.Type.Underlying().(type) {
typ := x.Type
switch tx := typ.raw.Type.Underlying().(type) {
case *types.Basic:
kind := tx.Kind()
switch {
case kind >= types.Bool && kind <= types.Uintptr:
t := b.AbiBasic(kind)
tptr := prog.Uintptr()
vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
if prog.is32Bits && (kind == types.Int64 || kind == types.Uint64) {
return b.makeIntfAlloc(tinter, rawIntf, typ, x)
}
return b.makeIntfByIntptr(tinter, rawIntf, typ, x.impl)
case kind == types.Float32:
t := b.AbiBasic(kind)
tptr := prog.Uintptr()
i32 := llvm.CreateBitCast(b.impl, x.impl, prog.tyInt32()) // TODO(xsw): more effective
vptr := Expr{llvm.CreateIntCast(b.impl, i32, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
i32 := llvm.CreateBitCast(b.impl, x.impl, prog.tyInt32())
return b.makeIntfByIntptr(tinter, rawIntf, typ, i32)
case kind == types.Float64:
t := b.AbiBasic(kind)
tptr := prog.Uintptr()
vptr := Expr{llvm.CreateBitCast(b.impl, x.impl, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
if prog.is32Bits {
return b.makeIntfAlloc(tinter, rawIntf, typ, x)
}
i64 := llvm.CreateBitCast(b.impl, x.impl, prog.tyInt64())
return b.makeIntfByIntptr(tinter, rawIntf, typ, i64)
case kind == types.String:
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter}
return Expr{b.InlineCall(b.Pkg.rtFunc("MakeAnyString"), x).impl, tinter}
}
// case *types.Struct:
// t := b.AbiStruct(tx)
/* case *types.Struct:
size := int(prog.SizeOf(typ))
if size > prog.PointerSize() {
return b.makeIntfAlloc(tinter, rawIntf, typ, x)
}
tv := prog.ctx.IntType(size * 8)
iv := llvm.CreateBitCast(b.impl, x.impl, tv)
return b.makeIntfByIntptr(tinter, rawIntf, typ, iv) */
}
panic("todo")
}
func (b Builder) makeIntfAlloc(tinter Type, rawIntf *types.Interface, typ Type, x Expr) (ret Expr) {
vptr := b.AllocU(typ)
b.Store(vptr, x)
return b.makeIntfByPtr(tinter, rawIntf, typ, vptr)
}
func (b Builder) makeIntfByPtr(tinter Type, rawIntf *types.Interface, typ Type, vptr Expr) (ret Expr) {
if rawIntf.Empty() {
ret = b.InlineCall(b.Pkg.rtFunc("MakeAny"), b.AbiType(typ), vptr)
ret.Type = tinter
return
}
panic("todo")
}
func (b Builder) makeIntfByIntptr(tinter Type, rawIntf *types.Interface, typ Type, x llvm.Value) (ret Expr) {
if rawIntf.Empty() {
tptr := b.Prog.Uintptr()
x = llvm.CreateIntCast(b.impl, x, tptr.ll)
impl := b.InlineCall(b.Pkg.rtFunc("MakeAnyIntptr"), b.AbiType(typ), Expr{x, tptr}).impl
return Expr{impl, tinter}
}
panic("todo")
}

View File

@@ -99,6 +99,7 @@ type aProgram struct {
ctx llvm.Context
typs typeutil.Map // rawType -> Type
gocvt goTypes
//abi *abi.Builder
rt *types.Package
rtget func() *types.Package
@@ -158,6 +159,7 @@ type aProgram struct {
NeedRuntime bool
NeedPyInit bool
is32Bits bool
}
// A Program presents a program.
@@ -180,7 +182,12 @@ func NewProgram(target *Target) Program {
// TODO(xsw): Finalize may cause panic, so comment it.
ctx.Finalize()
*/
return &aProgram{ctx: ctx, gocvt: newGoTypes(), target: target, td: td, named: make(map[string]llvm.Type)}
is32Bits := (td.PointerSize() == 4)
return &aProgram{
ctx: ctx, gocvt: newGoTypes(), // abi: abi.New(),
target: target, td: td, is32Bits: is32Bits,
named: make(map[string]llvm.Type),
}
}
// SetPython sets the Python package.

View File

@@ -99,6 +99,10 @@ func (p Program) SizeOf(typ Type, n ...int64) uint64 {
return size
}
func (p Program) PointerSize() int {
return p.td.PointerSize()
}
func (p Program) Slice(typ Type) Type {
return p.rawType(types.NewSlice(typ.raw.Type))
}