diff --git a/cl/_testgo/struczero/in.go b/cl/_testgo/struczero/in.go new file mode 100644 index 00000000..9a8db831 --- /dev/null +++ b/cl/_testgo/struczero/in.go @@ -0,0 +1,17 @@ +package main + +type bar struct { + pb *byte + f float32 +} + +func Foo(v any) (ret bar, ok bool) { + ret, ok = v.(bar) + return +} + +func main() { + ret, ok := Foo(nil) + println("Hi") + println(ret.pb, ret.f, ok) +} diff --git a/cl/_testgo/struczero/out.ll b/cl/_testgo/struczero/out.ll new file mode 100644 index 00000000..1ccc2962 --- /dev/null +++ b/cl/_testgo/struczero/out.ll @@ -0,0 +1,182 @@ +; ModuleID = 'main' +source_filename = "main" + +%main.bar = type { ptr, float } +%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr } +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%"github.com/goplus/llgo/internal/abi.StructField" = type { %"github.com/goplus/llgo/internal/abi.Name", ptr, i64 } +%"github.com/goplus/llgo/internal/abi.Name" = type { ptr } +%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } + +@"main.init$guard" = global ptr null +@main.bar = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [3 x i8] c"Hi\00", align 1 +@1 = private unnamed_addr constant [3 x i8] c"pb\00", align 1 +@"*_llgo_byte" = linkonce global ptr null +@2 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@3 = private unnamed_addr constant [2 x i8] c"f\00", align 1 +@4 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@5 = private unnamed_addr constant [5 x i8] c"main\00", align 1 +@6 = private unnamed_addr constant [9 x i8] c"main.bar\00", align 1 + +define { %main.bar, i1 } @main.Foo(%"github.com/goplus/llgo/internal/runtime.eface" %0) { +_llgo_0: + %1 = extractvalue %"github.com/goplus/llgo/internal/runtime.eface" %0, 0 + %2 = load ptr, ptr @main.bar, align 8 + %3 = icmp eq ptr %1, %2 + %4 = extractvalue %"github.com/goplus/llgo/internal/runtime.eface" %0, 1 + %5 = load %main.bar, ptr %4, align 8 + %6 = alloca { %main.bar, i1 }, align 8 + %7 = getelementptr inbounds { %main.bar, i1 }, ptr %6, i32 0, i32 0 + store %main.bar %5, ptr %7, align 8 + %8 = getelementptr inbounds { %main.bar, i1 }, ptr %6, i32 0, i32 1 + store i1 true, ptr %8, align 1 + %9 = load { %main.bar, i1 }, ptr %6, align 8 + %10 = alloca { %main.bar, i1 }, align 8 + %11 = getelementptr inbounds { %main.bar, i1 }, ptr %10, i32 0, i32 0 + store { ptr, double } zeroinitializer, ptr %11, align 8 + %12 = getelementptr inbounds { %main.bar, i1 }, ptr %10, i32 0, i32 1 + store i1 false, ptr %12, align 1 + %13 = load { %main.bar, i1 }, ptr %10, align 8 + %14 = select i1 %3, { %main.bar, i1 } %9, { %main.bar, i1 } %13 + %15 = extractvalue { %main.bar, i1 } %14, 0 + %16 = extractvalue { %main.bar, i1 } %14, 1 + %mrv = insertvalue { %main.bar, i1 } poison, %main.bar %15, 0 + %mrv1 = insertvalue { %main.bar, i1 } %mrv, i1 %16, 1 + ret { %main.bar, i1 } %mrv1 +} + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + call void @"main.init$abi"() + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define i32 @main(i32 %0, ptr %1) { +_llgo_0: + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = alloca %main.bar, align 8 + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16) + %4 = call { %main.bar, i1 } @main.Foo(%"github.com/goplus/llgo/internal/runtime.eface" zeroinitializer) + %5 = extractvalue { %main.bar, i1 } %4, 0 + store %main.bar %5, ptr %3, align 8 + %6 = extractvalue { %main.bar, i1 } %4, 1 + %7 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 0 + store ptr @0, ptr %8, align 8 + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 1 + store i64 2, ptr %9, align 4 + %10 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %7, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %10) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %11 = getelementptr inbounds %main.bar, ptr %3, i32 0, i32 0 + %12 = load ptr, ptr %11, align 8 + %13 = getelementptr inbounds %main.bar, ptr %3, i32 0, i32 1 + %14 = load float, ptr %13, align 4 + call void @"github.com/goplus/llgo/internal/runtime.PrintPointer"(ptr %12) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + %15 = fpext float %14 to double + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %15) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %6) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64) + +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.PrintPointer"(ptr) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1) + +define void @"main.init$abi"() { +_llgo_0: + %0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0 + store ptr @1, ptr %1, align 8 + %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1 + store i64 2, ptr %2, align 4 + %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 + %4 = load ptr, ptr @"*_llgo_byte", align 8 + %5 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 0 + store ptr @2, ptr %6, align 8 + %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 1 + store i64 0, ptr %7, align 4 + %8 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %5, align 8 + %9 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" %3, ptr %4, i64 0, %"github.com/goplus/llgo/internal/runtime.String" %8, i1 false, i1 false) + %10 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %10, i32 0, i32 0 + store ptr @3, ptr %11, align 8 + %12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %10, i32 0, i32 1 + store i64 1, ptr %12, align 4 + %13 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %10, align 8 + %14 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 13) + %15 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 0 + store ptr @4, ptr %16, align 8 + %17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 1 + store i64 0, ptr %17, align 4 + %18 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8 + %19 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" %13, ptr %14, i64 8, %"github.com/goplus/llgo/internal/runtime.String" %18, i1 false, i1 false) + %20 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %21 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 0 + store ptr @5, ptr %21, align 8 + %22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 1 + store i64 4, ptr %22, align 4 + %23 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %20, align 8 + %24 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 48) + %25 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %24, i64 0 + store %"github.com/goplus/llgo/internal/abi.StructField" %9, ptr %25, align 8 + %26 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %24, i64 1 + store %"github.com/goplus/llgo/internal/abi.StructField" %19, ptr %26, align 8 + %27 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 + %28 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %27, i32 0, i32 0 + store ptr %24, ptr %28, align 8 + %29 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %27, i32 0, i32 1 + store i64 2, ptr %29, align 4 + %30 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %27, i32 0, i32 2 + store i64 2, ptr %30, align 4 + %31 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %27, align 8 + %32 = call ptr @"github.com/goplus/llgo/internal/runtime.Struct"(%"github.com/goplus/llgo/internal/runtime.String" %23, %"github.com/goplus/llgo/internal/runtime.Slice" %31) + %33 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %34 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %33, i32 0, i32 0 + store ptr @6, ptr %34, align 8 + %35 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %33, i32 0, i32 1 + store i64 8, ptr %35, align 4 + %36 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %33, align 8 + %37 = call ptr @"github.com/goplus/llgo/internal/runtime.Named"(%"github.com/goplus/llgo/internal/runtime.String" %36, ptr %32) + store ptr %37, ptr @main.bar, align 8 + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.Struct"(i64, %"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.Slice") + +declare %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String", ptr, i64, %"github.com/goplus/llgo/internal/runtime.String", i1, i1) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Named"(%"github.com/goplus/llgo/internal/runtime.String", ptr) diff --git a/internal/runtime/llgo_autogen.lla b/internal/runtime/llgo_autogen.lla index 06b2a807..52761dea 100644 Binary files a/internal/runtime/llgo_autogen.lla and b/internal/runtime/llgo_autogen.lla differ diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index d5186fe7..e28dd638 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -124,4 +124,10 @@ func Pointer(elem *Type) *Type { return &ret.Type } +// Named returns a named type. +func Named(name string, typ *Type) *Type { + ret := *typ // TODO(xsw): named type + return &ret +} + // ----------------------------------------------------------------------------- diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 042755f8..b7dd63b4 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -111,31 +111,53 @@ func (b *Builder) Init(pkg string) { } // TypeName returns the ABI type name for the specified type. -func (b *Builder) TypeName(t types.Type) (ret string, private bool) { +func (b *Builder) TypeName(t types.Type) (ret string, pub bool) { switch t := t.(type) { case *types.Basic: - return BasicName(t), false + return BasicName(t), true case *types.Pointer: - ret, private = b.TypeName(t.Elem()) - return "*" + ret, private + ret, pub = b.TypeName(t.Elem()) + return "*" + ret, pub case *types.Struct: return b.StructName(t) + case *types.Named: + return NamedName(t), false // all named types are private } panic("todo") } +// PathOf returns the package path of the specified package. +func PathOf(pkg *types.Package) string { + if pkg.Name() == "main" { + return "main" + } + return pkg.Path() +} + +// FullName returns the full name of a package member. +func FullName(pkg *types.Package, name string) string { + return PathOf(pkg) + "." + name +} + +// NamedName returns the ABI type name for the specified named type. +func NamedName(t *types.Named) string { + o := t.Obj() + return FullName(o.Pkg(), o.Name()) +} + +// BasicName returns the ABI type name for the specified basic type. func BasicName(t *types.Basic) string { return "_llgo_" + t.Name() } // StructName returns the ABI type name for the specified struct type. -func (b *Builder) StructName(t *types.Struct) (ret string, private bool) { +func (b *Builder) StructName(t *types.Struct) (ret string, pub bool) { hash, private := b.structHash(t) hashStr := base64.RawURLEncoding.EncodeToString(hash) if private { - return b.Pkg + ".struct$" + hashStr, true + return b.Pkg + ".struct$" + hashStr, false } - return "_llgo_struct$" + hashStr, false + return "_llgo_struct$" + hashStr, true } func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { @@ -152,8 +174,8 @@ func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { if f.Embedded() { name = "-" } - ft, fpriv := b.TypeName(f.Type()) - if fpriv { + ft, pub := b.TypeName(f.Type()) + if !pub { private = true } fmt.Fprintln(h, name, ft) diff --git a/ssa/cl_test.go b/ssa/cl_test.go index 513944c1..469d246a 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -26,7 +26,7 @@ import ( ) func TestFromTestgo(t *testing.T) { - cltest.FromDir(t, "", "../cl/_testgo", false) + cltest.FromDir(t, "struczero", "../cl/_testgo", false) } func TestFromTestpy(t *testing.T) { diff --git a/ssa/interface.go b/ssa/interface.go index 4478d200..1991d352 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -32,14 +32,17 @@ import ( func (b Builder) abiBasic(t *types.Basic) Expr { /* TODO(xsw): - name := abi.BasicName(t) - g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr()) - return b.Load(g.Expr) + return b.abiExtern(abi.BasicName(t)) */ kind := int(abi.BasicKind(t)) return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(kind)) } +func (b Builder) abiExtern(name string) Expr { + g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr()) + return b.Load(g.Expr) +} + func (b Builder) abiTypeOf(t types.Type) Expr { switch t := t.(type) { case *types.Basic: @@ -48,10 +51,18 @@ func (b Builder) abiTypeOf(t types.Type) Expr { return b.abiPointerOf(t) case *types.Struct: return b.abiStructOf(t) + case *types.Named: + return b.abiNamedOf(t) } panic("todo") } +func (b Builder) abiNamedOf(t *types.Named) Expr { + under := b.abiTypeOf(t.Underlying()) + name := abi.NamedName(t) + return b.Call(b.Pkg.rtFunc("Named"), b.Str(name), under) +} + func (b Builder) abiPointerOf(t *types.Pointer) Expr { elem := b.abiTypeOf(t.Elem()) return b.Call(b.Pkg.rtFunc("Pointer"), elem) @@ -71,7 +82,7 @@ func (b Builder) abiStructOf(t *types.Struct) Expr { off := uintptr(prog.OffsetOf(typ, i)) flds[i] = b.structField(sfAbi, prog, f, off, t.Tag(i)) } - pkgPath := b.Str(pkg.abi.Pkg) + pkgPath := b.Str(pkg.Path()) params := strucAbi.raw.Type.(*types.Signature).Params() tSlice := prog.rawType(params.At(params.Len() - 1).Type().(*types.Slice)) fldSlice := b.SliceLit(tSlice, flds...) @@ -89,17 +100,27 @@ func (b Builder) structField(sfAbi Expr, prog Program, f *types.Var, offset uint // abiType returns the abi type of the specified type. func (b Builder) abiType(t types.Type) Expr { - if tx, ok := t.(*types.Basic); ok { + var name string + var pub bool + var pkg = b.Pkg + switch tx := t.(type) { + case *types.Basic: return b.abiBasic(tx) + case *types.Named: + o := tx.Obj() + oPkgPath := abi.PathOf(o.Pkg()) + name = oPkgPath + "." + o.Name() + if oPkgPath != pkg.Path() { + return b.abiExtern(name) + } + default: + name, pub = pkg.abi.TypeName(t) } - pkg := b.Pkg - name, private := pkg.abi.TypeName(t) g := pkg.VarOf(name) if g == nil { prog := b.Prog g = pkg.doNewVar(name, prog.AbiTypePtrPtr()) g.Init(prog.Null(g.Type)) - pub := !private if pub { g.impl.SetLinkage(llvm.LinkOnceAnyLinkage) } diff --git a/ssa/package.go b/ssa/package.go index eea49fe1..6eb26db1 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -551,6 +551,11 @@ func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { // ----------------------------------------------------------------------------- +// Path returns the package path. +func (p Package) Path() string { + return p.abi.Pkg +} + // String returns a string representation of the package. func (p Package) String() string { return p.mod.String() @@ -564,7 +569,7 @@ func (p Package) AfterInit(b Builder, ret BasicBlock) { b.SetBlockEx(ret, afterInit, false) if doAbiInit { sigAbiInit := types.NewSignatureType(nil, nil, nil, nil, nil, false) - fn := p.NewFunc(p.abi.Pkg+".init$abi", sigAbiInit, InC) + fn := p.NewFunc(p.Path()+".init$abi", sigAbiInit, InC) fnb := fn.MakeBody(1) for _, abiInit := range p.abiini { abiInit(unsafe.Pointer(fnb)) diff --git a/ssa/type.go b/ssa/type.go index 21c8c780..800f0755 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -20,6 +20,7 @@ import ( "fmt" "go/types" + "github.com/goplus/llgo/ssa/abi" "github.com/goplus/llvm" ) @@ -401,20 +402,19 @@ func (p Program) toNamed(raw *types.Named) Type { } } +// NameOf returns the full name of a named type. func NameOf(typ *types.Named) string { - obj := typ.Obj() - return FullName(obj.Pkg(), obj.Name()) + return abi.NamedName(typ) } +// FullName returns the full name of a package member. func FullName(pkg *types.Package, name string) string { - return PathOf(pkg) + "." + name + return abi.FullName(pkg, name) } +// PathOf returns the package path of the specified package. func PathOf(pkg *types.Package) string { - if pkg.Name() == "main" { - return "main" - } - return pkg.Path() + return abi.PathOf(pkg) } // -----------------------------------------------------------------------------