diff --git a/cl/_testgo/errors/in.go b/cl/_testgo/errors/in.go new file mode 100644 index 00000000..ada33367 --- /dev/null +++ b/cl/_testgo/errors/in.go @@ -0,0 +1,21 @@ +package main + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func New(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +func main() { + err := New("an error") + println(err, err.Error()) +} diff --git a/cl/_testgo/errors/out.ll b/cl/_testgo/errors/out.ll new file mode 100644 index 00000000..2e203fa4 --- /dev/null +++ b/cl/_testgo/errors/out.ll @@ -0,0 +1,213 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%main.errorString = type { %"github.com/goplus/llgo/internal/runtime.String" } +%"github.com/goplus/llgo/internal/abi.StructField" = type { %"github.com/goplus/llgo/internal/runtime.String", ptr, i64, %"github.com/goplus/llgo/internal/runtime.String", i1 } +%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } +%"github.com/goplus/llgo/internal/abi.Imethod" = type { %"github.com/goplus/llgo/internal/runtime.String", ptr } + +@"main.init$guard" = global ptr null +@"*main.errorString" = global ptr null +@"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU" = linkonce global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [9 x i8] c"an error\00", align 1 +@1 = private unnamed_addr constant [2 x i8] c"s\00", align 1 +@2 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@3 = private unnamed_addr constant [5 x i8] c"main\00", align 1 +@4 = private unnamed_addr constant [5 x i8] c"main\00", align 1 +@5 = private unnamed_addr constant [17 x i8] c"main.errorString\00", align 1 +@6 = private unnamed_addr constant [6 x i8] c"Error\00", align 1 +@"_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to" = linkonce global ptr null +@7 = private unnamed_addr constant [5 x i8] c"main\00", align 1 + +define %"github.com/goplus/llgo/internal/runtime.iface" @main.New(%"github.com/goplus/llgo/internal/runtime.String" %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16) + %2 = getelementptr inbounds %main.errorString, ptr %1, i32 0, i32 0 + store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %2, align 8 + %3 = load ptr, ptr @"*main.errorString", align 8 + %4 = load ptr, ptr @"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU", align 8 + %5 = call ptr @"github.com/goplus/llgo/internal/runtime.NewItab"(ptr %4, ptr %3) + %6 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8 + %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, i32 0, i32 0 + store ptr %5, ptr %7, align 8 + %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, i32 0, i32 1 + store ptr %1, ptr %8, align 8 + %9 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, align 8 + ret %"github.com/goplus/llgo/internal/runtime.iface" %9 +} + +define %"github.com/goplus/llgo/internal/runtime.String" @"(*main.errorString).Error"(ptr %0) { +_llgo_0: + %1 = getelementptr inbounds %main.errorString, ptr %0, i32 0, i32 0 + %2 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8 + ret %"github.com/goplus/llgo/internal/runtime.String" %2 +} + +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 %"github.com/goplus/llgo/internal/runtime.String", align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0 + store ptr @0, ptr %3, align 8 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1 + store i64 8, ptr %4, align 4 + %5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8 + %6 = call %"github.com/goplus/llgo/internal/runtime.iface" @main.New(%"github.com/goplus/llgo/internal/runtime.String" %5) + %7 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %6, 0 + %8 = getelementptr ptr, ptr %7, i64 3 + %9 = load ptr, ptr %8, align 8 + %10 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %6, 1 + %11 = alloca { ptr, ptr }, align 8 + %12 = getelementptr inbounds { ptr, ptr }, ptr %11, i32 0, i32 0 + store ptr %9, ptr %12, align 8 + %13 = getelementptr inbounds { ptr, ptr }, ptr %11, i32 0, i32 1 + store ptr %10, ptr %13, align 8 + %14 = load { ptr, ptr }, ptr %11, align 8 + %15 = extractvalue { ptr, ptr } %14, 1 + %16 = extractvalue { ptr, ptr } %14, 0 + %17 = call %"github.com/goplus/llgo/internal/runtime.String" %16(ptr %15) + call void @"github.com/goplus/llgo/internal/runtime.PrintIface"(%"github.com/goplus/llgo/internal/runtime.iface" %6) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %17) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.NewItab"(ptr, ptr) + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.PrintIface"(%"github.com/goplus/llgo/internal/runtime.iface") + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") + +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 1, ptr %2, align 4 + %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 + %4 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24) + %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) + %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 4, 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.AllocU"(i64 56) + %15 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %14, i64 0 + store %"github.com/goplus/llgo/internal/abi.StructField" %9, ptr %15, align 8 + %16 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 + %17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %16, i32 0, i32 0 + store ptr %14, ptr %17, align 8 + %18 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %16, i32 0, i32 1 + store i64 1, ptr %18, align 4 + %19 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %16, i32 0, i32 2 + store i64 1, ptr %19, align 4 + %20 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %16, align 8 + %21 = call ptr @"github.com/goplus/llgo/internal/runtime.Struct"(%"github.com/goplus/llgo/internal/runtime.String" %13, i64 16, %"github.com/goplus/llgo/internal/runtime.Slice" %20) + %22 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %23 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %22, i32 0, i32 0 + store ptr @4, ptr %23, align 8 + %24 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %22, i32 0, i32 1 + store i64 4, ptr %24, align 4 + %25 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %22, align 8 + %26 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 0 + store ptr @5, ptr %27, align 8 + %28 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 1 + store i64 16, ptr %28, align 4 + %29 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %26, align 8 + %30 = call ptr @"github.com/goplus/llgo/internal/runtime.Named"(%"github.com/goplus/llgo/internal/runtime.String" %25, %"github.com/goplus/llgo/internal/runtime.String" %29, ptr %21, { ptr, i64, i64 } zeroinitializer) + %31 = call ptr @"github.com/goplus/llgo/internal/runtime.PointerTo"(ptr %30) + store ptr %31, ptr @"*main.errorString", align 8 + %32 = load ptr, ptr @"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU", align 8 + %33 = icmp eq ptr %32, null + br i1 %33, label %_llgo_1, label %_llgo_2 + +_llgo_1: ; preds = %_llgo_0 + %34 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %35 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %34, i32 0, i32 0 + store ptr @6, ptr %35, align 8 + %36 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %34, i32 0, i32 1 + store i64 5, ptr %36, align 4 + %37 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %34, align 8 + %38 = load ptr, ptr @"_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to", align 8 + %39 = alloca %"github.com/goplus/llgo/internal/abi.Imethod", align 8 + %40 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Imethod", ptr %39, i32 0, i32 0 + store %"github.com/goplus/llgo/internal/runtime.String" %37, ptr %40, align 8 + %41 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Imethod", ptr %39, i32 0, i32 1 + store ptr %38, ptr %41, align 8 + %42 = load %"github.com/goplus/llgo/internal/abi.Imethod", ptr %39, align 8 + %43 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 24) + %44 = getelementptr %"github.com/goplus/llgo/internal/abi.Imethod", ptr %43, i64 0 + store %"github.com/goplus/llgo/internal/abi.Imethod" %42, ptr %44, align 8 + %45 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 + %46 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %45, i32 0, i32 0 + store ptr %43, ptr %46, align 8 + %47 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %45, i32 0, i32 1 + store i64 1, ptr %47, align 4 + %48 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %45, i32 0, i32 2 + store i64 1, ptr %48, align 4 + %49 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %45, align 8 + %50 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %51 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %50, i32 0, i32 0 + store ptr @7, ptr %51, align 8 + %52 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %50, i32 0, i32 1 + store i64 4, ptr %52, align 4 + %53 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %50, align 8 + %54 = call ptr @"github.com/goplus/llgo/internal/runtime.Interface"(%"github.com/goplus/llgo/internal/runtime.String" %53, %"github.com/goplus/llgo/internal/runtime.Slice" %49) + store ptr %54, ptr @"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU", align 8 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.Struct"(%"github.com/goplus/llgo/internal/runtime.String", i64, %"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) + +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", %"github.com/goplus/llgo/internal/runtime.String", ptr, %"github.com/goplus/llgo/internal/runtime.Slice") + +declare ptr @"github.com/goplus/llgo/internal/runtime.PointerTo"(ptr) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Interface"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.Slice") diff --git a/cl/_testgo/struczero/out.ll b/cl/_testgo/struczero/out.ll index 8299781b..20514bcb 100644 --- a/cl/_testgo/struczero/out.ll +++ b/cl/_testgo/struczero/out.ll @@ -20,7 +20,7 @@ source_filename = "main" @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 [5 x i8] c"main\00", align 1 +@6 = private unnamed_addr constant [39 x i8] c"github.com/goplus/llgo/cl/internal/foo\00", align 1 @7 = private unnamed_addr constant [43 x i8] c"github.com/goplus/llgo/cl/internal/foo.Foo\00", align 1 @8 = private unnamed_addr constant [3 x i8] c"pb\00", align 1 @9 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 @@ -257,7 +257,7 @@ _llgo_1: ; preds = %_llgo_0 %36 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %35, i32 0, i32 0 store ptr @6, ptr %36, align 8 %37 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %35, i32 0, i32 1 - store i64 4, ptr %37, align 4 + store i64 38, ptr %37, align 4 %38 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %35, align 8 %39 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 %40 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %39, i32 0, i32 0 diff --git a/cl/_testrt/any/out.ll b/cl/_testrt/any/out.ll index 612c682e..50a3b9db 100644 --- a/cl/_testrt/any/out.ll +++ b/cl/_testrt/any/out.ll @@ -115,7 +115,7 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 %2 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 3) - %3 = call ptr @"github.com/goplus/llgo/internal/runtime.Pointer"(ptr %2) + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.PointerTo"(ptr %2) store ptr %3, ptr @"*_llgo_int8", align 8 br label %_llgo_2 @@ -123,4 +123,4 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -declare ptr @"github.com/goplus/llgo/internal/runtime.Pointer"(ptr) +declare ptr @"github.com/goplus/llgo/internal/runtime.PointerTo"(ptr) diff --git a/cl/compile.go b/cl/compile.go index 7105730d..f5708851 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -558,6 +558,13 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue switch v := iv.(type) { case *ssa.Call: cv := v.Call.Value + if mthd := v.Call.Method; mthd != nil { + o := p.compileValue(b, cv) + fn := b.Imethod(o, v.Call.Method) + args := p.compileValues(b, v.Call.Args, fnNormal) + ret = b.Call(fn, args...) + break + } kind := p.funcKind(cv) if kind == fnIgnore { return diff --git a/cl/compile_test.go b/cl/compile_test.go index 2d601ae2..48639cf5 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -28,6 +28,10 @@ func testCompile(t *testing.T, src, expected string) { cltest.TestCompileEx(t, src, "foo.go", expected) } +func TestFromTestgo(t *testing.T) { + cltest.FromDir(t, "", "./_testgo", false) +} + func TestFromTestpy(t *testing.T) { cltest.FromDir(t, "", "./_testpy", false) } diff --git a/internal/abi/llgo_autogen.lla b/internal/abi/llgo_autogen.lla index 2eb60535..fefd4872 100644 Binary files a/internal/abi/llgo_autogen.lla and b/internal/abi/llgo_autogen.lla differ diff --git a/internal/runtime/llgo_autogen.lla b/internal/runtime/llgo_autogen.lla index 6b788455..349f274c 100644 Binary files a/internal/runtime/llgo_autogen.lla and b/internal/runtime/llgo_autogen.lla differ diff --git a/internal/runtime/z_closure.go b/internal/runtime/z_closure.go deleted file mode 100644 index d47818b3..00000000 --- a/internal/runtime/z_closure.go +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 runtime - -/* -import ( - "unsafe" -) - -// Closure represents a closure. -type Closure struct { - f unsafe.Pointer - data unsafe.Pointer // means no context if data is nil -} - -// NewClosure creates a closure. -func NewClosure(f, data unsafe.Pointer) Closure { - return Closure{f, data} -} - -// ClosureF returns the function of a closure. -func ClosureF(c Closure) unsafe.Pointer { - return c.f -} - -// ClosureData returns the data of a closure. -func ClosureData(c Closure) unsafe.Pointer { - return c.data -} -*/ diff --git a/internal/runtime/z_face.go b/internal/runtime/z_face.go index c7611fd3..149c06c6 100644 --- a/internal/runtime/z_face.go +++ b/internal/runtime/z_face.go @@ -43,6 +43,8 @@ type ( Itab = itab ) +type Imethod = abi.Imethod +type Method = abi.Method type FuncType = abi.FuncType type InterfaceType = abi.InterfaceType @@ -54,14 +56,105 @@ func ToEface(i Iface) Eface { // ----------------------------------------------------------------------------- const ( - typeHdrSize = unsafe.Sizeof(abi.Type{}) - funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{}) - uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{}) - methodSize = unsafe.Sizeof(abi.Method{}) - pointerSize = unsafe.Sizeof(uintptr(0)) - itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize + typeHdrSize = unsafe.Sizeof(abi.Type{}) + arrayTypeHdrSize = unsafe.Sizeof(abi.ArrayType{}) + chanTypeHdrSize = unsafe.Sizeof(abi.ChanType{}) + funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{}) + interfaceTypeHdrSize = unsafe.Sizeof(abi.InterfaceType{}) + mapTypeHdrSize = unsafe.Sizeof(abi.MapType{}) + ptrTypeHdrSize = unsafe.Sizeof(abi.PtrType{}) + sliceTypeHdrSize = unsafe.Sizeof(abi.SliceType{}) + structTypeHdrSize = unsafe.Sizeof(abi.StructType{}) + uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{}) + methodSize = unsafe.Sizeof(abi.Method{}) + pointerSize = unsafe.Sizeof(uintptr(0)) + itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize ) +var hdrSizes = [...]uintptr{ + arrayTypeHdrSize, + chanTypeHdrSize, + funcTypeHdrSize, + interfaceTypeHdrSize, + mapTypeHdrSize, + ptrTypeHdrSize, + sliceTypeHdrSize, + typeHdrSize, + structTypeHdrSize, +} + +func hdrSizeOf(kind abi.Kind) uintptr { + if kind >= abi.Array && kind <= abi.Struct { + return hdrSizes[kind-abi.Array] + } + return typeHdrSize +} + +// Named returns a named type. +func Named(pkgPath, name string, underlying *Type, methods []abi.Method) *Type { + tflag := underlying.TFlag + if tflag&abi.TFlagUncommon != 0 { + panic("runtime: underlying type is already named") + } + + kind := underlying.Kind() + n := len(methods) + if kind == abi.Interface { + if n > 0 { + panic("runtime: interface type cannot have methods") + } + ret := *underlying.InterfaceType() + ret.PkgPath_ = pkgPath + ret.Str_ = name + return &ret.Type + } + + baseSize := hdrSizeOf(kind) + extraSize := uintptr(0) + if kind == abi.Func { + f := underlying.FuncType() + extraSize = uintptr(f.In()+f.Out()) * pointerSize + } + + size := baseSize + extraSize + if n > 0 || pkgPath != "" { + size += uncommonTypeHdrSize + uintptr(n)*methodSize + tflag |= abi.TFlagUncommon + } + + ptr := AllocU(size) + c.Memcpy(ptr, unsafe.Pointer(underlying), baseSize) + + ret := (*Type)(ptr) + ret.TFlag = tflag | abi.TFlagNamed + ret.Str_ = name + + xcount := 0 + for _, m := range methods { + if !m.Exported() { + break + } + xcount++ + } + uncommon := (*abi.UncommonType)(c.Advance(ptr, int(baseSize))) + uncommon.PkgPath_ = pkgPath + uncommon.Mcount = uint16(n) + uncommon.Xcount = uint16(xcount) + uncommon.Moff = uint32(uncommonTypeHdrSize + extraSize) + + extraOff := int(baseSize + uncommonTypeHdrSize) + if extraSize > 0 { + src := c.Advance(unsafe.Pointer(underlying), int(baseSize)) + dest := c.Advance(unsafe.Pointer(ptr), extraOff) + c.Memcpy(dest, src, extraSize) + extraOff += int(extraSize) + } + + data := (*abi.Method)(c.Advance(ptr, extraOff)) + copy(unsafe.Slice(data, n), methods) + return ret +} + // Func returns a function type. func Func(in, out []*Type, variadic bool) *FuncType { n := len(in) + len(out) @@ -85,61 +178,8 @@ func Func(in, out []*Type, variadic bool) *FuncType { return ret } -// Imethod returns an interface method. -func Imethod(name string, typ *FuncType) abi.Imethod { - return abi.Imethod{ - Name_: name, - Typ_: typ, - } -} - -// Method returns a method. -func Method(name string, typ *FuncType, ifn, tfn abi.Text) abi.Method { - return abi.Method{ - Name_: name, - Mtyp_: typ, - Ifn_: ifn, - Tfn_: tfn, - } -} - -// Named returns a named type. -func Named(pkgPath, name string, underlying *Type, methods []abi.Method) *Type { - tflag := underlying.TFlag - size := typeHdrSize - n := len(methods) - if n > 0 || pkgPath != "" { - size += uncommonTypeHdrSize + uintptr(n)*methodSize - tflag |= abi.TFlagUncommon - } - ptr := AllocU(size) - - ret := (*Type)(ptr) - *ret = *underlying - ret.TFlag = tflag | abi.TFlagNamed - ret.Str_ = name - - xcount := 0 - for _, m := range methods { - if !m.Exported() { - break - } - xcount++ - } - - uncommon := (*abi.UncommonType)(c.Advance(ptr, int(typeHdrSize))) - uncommon.PkgPath_ = pkgPath - uncommon.Mcount = uint16(n) - uncommon.Xcount = uint16(xcount) - uncommon.Moff = uint32(uncommonTypeHdrSize) - - data := (*abi.Method)(c.Advance(ptr, int(typeHdrSize+uncommonTypeHdrSize))) - copy(unsafe.Slice(data, n), methods) - return ret -} - // Interface returns an interface type. -func Interface(pkgPath string, methods []abi.Imethod) *Type { +func Interface(pkgPath string, methods []abi.Imethod) *InterfaceType { ret := &abi.InterfaceType{ Type: Type{ Size_: unsafe.Sizeof(eface{}), @@ -149,7 +189,7 @@ func Interface(pkgPath string, methods []abi.Imethod) *Type { PkgPath_: pkgPath, Methods: methods, } - return &ret.Type + return ret } // NewItab returns a new itab. @@ -158,7 +198,7 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab { size := itabHdrSize + uintptr(n)*pointerSize ptr := AllocU(size) - ret := (*Itab)(ptr) + ret := (*itab)(ptr) ret.inter = inter ret._type = typ ret.hash = typ.Hash diff --git a/internal/runtime/z_print.go b/internal/runtime/z_print.go index 27a47ac7..8edbbb69 100644 --- a/internal/runtime/z_print.go +++ b/internal/runtime/z_print.go @@ -69,3 +69,7 @@ func PrintSlice(s Slice) { func PrintEface(e Eface) { print("(", e._type, ",", e.data, ")") } + +func PrintIface(i Iface) { + print("(", i.tab, ",", i.data, ")") +} diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index d39ad954..3693a8f4 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -112,17 +112,22 @@ func Struct(pkgPath string, size uintptr, fields ...abi.StructField) *Type { // ----------------------------------------------------------------------------- -// Pointer returns a pointer type. -func Pointer(elem *Type) *Type { - ret := &abi.PtrType{ - Type: Type{ - Size_: unsafe.Sizeof(uintptr(0)), - Hash: uint32(abi.Pointer), // TODO(xsw): hash - Kind_: uint8(abi.Pointer), - }, - Elem: elem, +// PointerTo returns the pointer type with element elem. +func PointerTo(elem *Type) *Type { + ret := elem.PtrToThis_ + if ret == nil { + ptr := &abi.PtrType{ + Type: Type{ + Size_: unsafe.Sizeof(uintptr(0)), + Hash: uint32(abi.Pointer), // TODO(xsw): hash + Kind_: uint8(abi.Pointer), + }, + Elem: elem, + } + ret = &ptr.Type + elem.PtrToThis_ = ret } - return &ret.Type + return ret } // ----------------------------------------------------------------------------- diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 48badc0c..e6da942e 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -22,6 +22,7 @@ import ( "fmt" "go/types" "hash" + "log" "github.com/goplus/llgo/internal/abi" ) @@ -92,7 +93,6 @@ func KindOf(raw types.Type, lvl int, is32Bits bool) (Kind, types.Type, int) { // Builder is a helper for constructing ABI types. type Builder struct { - h hash.Hash buf []byte Pkg string } @@ -106,7 +106,6 @@ func New(pkg string) *Builder { func (b *Builder) Init(pkg string) { b.Pkg = pkg - b.h = sha256.New() b.buf = make([]byte, sha256.Size) } @@ -120,11 +119,19 @@ func (b *Builder) TypeName(t types.Type) (ret string, pub bool) { return "*" + ret, pub case *types.Struct: return b.StructName(t) + case *types.Signature: + return b.FuncName(t), true case *types.Named: o := t.Obj() return TypeName(o), o.Exported() + case *types.Interface: + if t.Empty() { + return "_llgo_any", true + } + return b.InterfaceName(t) } - panic("todo") + log.Panicf("todo: %T\n", t) + return } // PathOf returns the package path of the specified package. @@ -150,6 +157,57 @@ func BasicName(t *types.Basic) string { return "_llgo_" + t.Name() } +// FuncName returns the ABI type name for the specified function type. +func (b *Builder) FuncName(t *types.Signature) string { + hash := b.funcHash(t) + hashStr := base64.RawURLEncoding.EncodeToString(hash) + return "_llgo_func$" + hashStr +} + +func (b *Builder) funcHash(t *types.Signature) []byte { + h := sha256.New() + params, results := t.Params(), t.Results() + fmt.Fprintln(h, "func", params.Len(), results.Len(), t.Variadic()) + b.tuple(h, params) + b.tuple(h, results) + return h.Sum(b.buf[:0]) +} + +func (b *Builder) tuple(h hash.Hash, t *types.Tuple) { + n := t.Len() + for i := 0; i < n; i++ { + v := t.At(i) + ft, _ := b.TypeName(v.Type()) + fmt.Fprintln(h, ft) + } +} + +// InterfaceName returns the ABI type name for the specified interface type. +func (b *Builder) InterfaceName(t *types.Interface) (ret string, pub bool) { + hash, private := b.interfaceHash(t) + hashStr := base64.RawURLEncoding.EncodeToString(hash) + if private { + return b.Pkg + ".iface$" + hashStr, false + } + return "_llgo_iface$" + hashStr, true +} + +func (b *Builder) interfaceHash(t *types.Interface) (ret []byte, private bool) { + h := sha256.New() + n := t.NumMethods() + fmt.Fprintln(h, "interface", n) + for i := 0; i < n; i++ { + m := t.Method(i) + if !m.Exported() { + private = true + } + ft := b.FuncName(m.Type().(*types.Signature)) + fmt.Fprintln(h, m.Name(), ft) + } + ret = h.Sum(b.buf[:0]) + return +} + // StructName returns the ABI type name for the specified struct type. func (b *Builder) StructName(t *types.Struct) (ret string, pub bool) { hash, private := b.structHash(t) @@ -161,8 +219,7 @@ func (b *Builder) StructName(t *types.Struct) (ret string, pub bool) { } func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { - h := b.h - h.Reset() + h := sha256.New() n := t.NumFields() fmt.Fprintln(h, "struct", n) for i := 0; i < n; i++ { @@ -174,10 +231,7 @@ func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { if f.Embedded() { name = "-" } - ft, pub := b.TypeName(f.Type()) - if !pub { - private = true - } + ft, _ := b.TypeName(f.Type()) fmt.Fprintln(h, name, ft) } ret = h.Sum(b.buf[:0]) diff --git a/ssa/expr.go b/ssa/expr.go index 0a4d47ea..bdaa64df 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -193,12 +193,6 @@ func (b Builder) Str(v string) (ret Expr) { return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()} } -func (b Builder) pkgName(pkgPath string) Expr { - // TODO(xsw): use a global cache - // return b.Call(b.Pkg.rtFunc("NewPkgName"), b.Str(pkgPath)) - return b.Str(pkgPath) -} - // unsafeString(data *byte, size int) string func (b Builder) unsafeString(data, size llvm.Value) Expr { prog := b.Prog @@ -906,7 +900,7 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { return b.Call(fn, args...) } -// The Call instruction represents a function or method call. +// The Call instruction represents a function call. // // The Call instruction yields the function result if there is exactly // one. Otherwise it returns a tuple, the components of which are @@ -916,7 +910,6 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { // // t2 = println(t0, t1) // t4 = t3() -// t7 = invoke t5.Println(...t6) func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { if debugInstr { var b bytes.Buffer @@ -954,7 +947,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { sig = raw.(*types.Signature) ll = fn.ll default: - panic("unreachable") + log.Panicf("unreachable: %d(%T)\n", kind, raw) } ret.Type = prog.retType(sig) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b)) @@ -1077,17 +1070,18 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { typ = prog.Float64() case vkSlice: fn = "PrintSlice" - case vkPtr, vkFuncPtr, vkFuncDecl: - fn = "PrintPointer" - typ = prog.VoidPtr() case vkClosure: arg = b.Field(arg, 0) + fallthrough + case vkPtr, vkFuncPtr, vkFuncDecl: fn = "PrintPointer" typ = prog.VoidPtr() case vkString: fn = "PrintString" case vkEface: fn = "PrintEface" + case vkIface: + fn = "PrintIface" // case vkComplex: // fn = "PrintComplex" default: diff --git a/ssa/interface.go b/ssa/interface.go index 217d231a..86fa5762 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -55,28 +55,79 @@ func (b Builder) abiTypeOf(t types.Type) Expr { return b.abiStructOf(t) case *types.Named: return b.abiNamedOf(t) + case *types.Interface: + return b.abiInterfaceOf(t) + case *types.Signature: + return b.abiFuncOf(t) } panic("todo") } +func (b Builder) abiTupleOf(t *types.Tuple) Expr { + n := t.Len() + prog := b.Prog + tSlice := prog.Slice(prog.AbiTypePtr()) + tuple := make([]Expr, n) + for i := 0; i < n; i++ { + tuple[i] = b.abiType(t.At(i).Type()) + } + return b.SliceLit(tSlice, tuple...) +} + +// func Func(in, out []*Type, variadic bool) +func (b Builder) abiFuncOf(sig *types.Signature) Expr { + prog := b.Prog + pkg := b.Pkg + fn := pkg.rtFunc("Func") + params := b.abiTupleOf(sig.Params()) + results := b.abiTupleOf(sig.Results()) + variadic := prog.Val(sig.Variadic()) + return b.Call(fn, params, results, variadic) +} + +// Imethod{name string, typ *FuncType} +func (b Builder) abiImethodOf(m *types.Func) Expr { + prog := b.Prog + name := b.Str(m.Name()) + typ := b.abiType(m.Type()) + return b.aggregateValue(prog.rtType("Imethod"), name.impl, typ.impl) +} + +// func Interface(pkgPath string, methods []abi.Imethod) +func (b Builder) abiInterfaceOf(t *types.Interface) Expr { + prog := b.Prog + n := t.NumMethods() + methods := make([]Expr, n) + for i := 0; i < n; i++ { + m := t.Method(i) + methods[i] = b.abiImethodOf(m) + } + pkg := b.Pkg + fn := pkg.rtFunc("Interface") + pkgPath := pkg.Path() + tSlice := lastParamType(prog, fn) + methodSlice := b.SliceLit(tSlice, methods...) + return b.Call(fn, b.Str(pkgPath), methodSlice) +} + // func Named(pkgPath, name string, underlying *Type, methods []abi.Method) func (b Builder) abiNamedOf(t *types.Named) Expr { under := b.abiTypeOf(t.Underlying()) + path := abi.PathOf(t.Obj().Pkg()) name := NameOf(t) prog := b.Prog pkg := b.Pkg - pkgPath := b.pkgName(pkg.Path()) fn := pkg.rtFunc("Named") tSlice := lastParamType(prog, fn) // TODO(xsw): methods methods := prog.Zero(tSlice) - return b.Call(fn, pkgPath, b.Str(name), under, methods) + return b.Call(fn, b.Str(path), b.Str(name), under, methods) } func (b Builder) abiPointerOf(t *types.Pointer) Expr { elem := b.abiTypeOf(t.Elem()) - return b.Call(b.Pkg.rtFunc("Pointer"), elem) + return b.Call(b.Pkg.rtFunc("PointerTo"), elem) } // func Struct(pkgPath string, size uintptr, fields []abi.StructField) @@ -93,7 +144,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.pkgName(pkg.Path()) + pkgPath := b.Str(pkg.Path()) tSlice := lastParamType(prog, strucAbi) fldSlice := b.SliceLit(tSlice, flds...) size := prog.IntVal(prog.SizeOf(typ), prog.Uintptr()) @@ -156,6 +207,50 @@ func (b Builder) unsafeEface(t, data llvm.Value) llvm.Value { return aggregateValue(b.impl, b.Prog.rtEface(), t, data) } +// unsafeIface(itab *runtime.Itab, data unsafe.Pointer) Eface +func (b Builder) unsafeIface(itab, data llvm.Value) llvm.Value { + return aggregateValue(b.impl, b.Prog.rtIface(), itab, data) +} + +// func NewItab(tintf *InterfaceType, typ *Type) *runtime.Itab +func (b Builder) newItab(tintf, typ Expr) Expr { + return b.Call(b.Pkg.rtFunc("NewItab"), tintf, typ) +} + +func (b Builder) unsafeInterface(rawIntf *types.Interface, t Expr, data llvm.Value) llvm.Value { + if rawIntf.Empty() { + return b.unsafeEface(t.impl, data) + } + tintf := b.abiType(rawIntf) + itab := b.newItab(tintf, t) + return b.unsafeIface(itab.impl, data) +} + +func iMethodOf(rawIntf *types.Interface, method *types.Func) int { + name := method.Name() + n := rawIntf.NumMethods() + for i := 0; i < n; i++ { + m := rawIntf.Method(i) + if m.Name() == name { + // TODO(xsw): check signature + return i + } + } + return -1 +} + +// Imethod returns closure of an interface method. +func (b Builder) Imethod(intf Expr, method *types.Func) Expr { + prog := b.Prog + rawIntf := intf.raw.Type.Underlying().(*types.Interface) + i := iMethodOf(rawIntf, method) + impl := intf.impl + itab := Expr{b.faceItab(impl), prog.VoidPtrPtr()} + pfn := b.Advance(itab, prog.IntVal(uint64(i+3), prog.Int())) + tclosure := prog.Type(method.Type(), InGo) + return b.aggregateValue(tclosure, b.Load(pfn).impl, b.faceData(impl)) +} + // ----------------------------------------------------------------------------- // MakeInterface constructs an instance of an interface type from a @@ -185,7 +280,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { case abi.Indirect: vptr := b.AllocU(typ) b.Store(vptr, x) - return Expr{b.unsafeEface(tabi.impl, vptr.impl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } ximpl := x.impl if lvl > 0 { @@ -194,7 +289,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { var u llvm.Value switch kind { case abi.Pointer: - return Expr{b.unsafeEface(tabi.impl, ximpl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter} case abi.Integer: tu := prog.Uintptr() u = llvm.CreateIntCast(b.impl, ximpl, tu.ll) @@ -205,7 +300,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { panic("todo") } data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr()) - return Expr{b.unsafeEface(tabi.impl, data), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, data), tinter} } func (b Builder) valFromData(typ Type, data llvm.Value) Expr { @@ -357,6 +452,10 @@ func (b Builder) faceData(x llvm.Value) llvm.Value { return llvm.CreateExtractValue(b.impl, x, 1) } +func (b Builder) faceItab(x llvm.Value) llvm.Value { + return llvm.CreateExtractValue(b.impl, x, 0) +} + func (b Builder) faceAbiType(x Expr) Expr { if x.kind == vkIface { panic("todo") diff --git a/ssa/package.go b/ssa/package.go index 5df247db..dd562f4b 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -125,12 +125,14 @@ type aProgram struct { rtStringTy llvm.Type rtEfaceTy llvm.Type + rtIfaceTy llvm.Type rtSliceTy llvm.Type rtMapTy llvm.Type anyTy Type voidTy Type voidPtr Type + voidPPtr Type boolTy Type cstrTy Type cintTy Type @@ -149,7 +151,6 @@ type aProgram struct { pyObjPPtr Type abiTyptr Type abiTypptr Type - //efaceTy Type pyImpTy *types.Signature pyNewList *types.Signature @@ -256,6 +257,13 @@ func (p Program) rtEface() llvm.Type { return p.rtEfaceTy } +func (p Program) rtIface() llvm.Type { + if p.rtIfaceTy.IsNil() { + p.rtIfaceTy = p.rtType("Iface").ll + } + return p.rtIfaceTy +} + func (p Program) rtMap() llvm.Type { if p.rtMapTy.IsNil() { p.rtMapTy = p.rtType("Map").ll @@ -364,6 +372,13 @@ func (p Program) VoidPtr() Type { return p.voidPtr } +func (p Program) VoidPtrPtr() Type { + if p.voidPPtr == nil { + p.voidPPtr = p.rawType(types.NewPointer(types.Typ[types.UnsafePointer])) + } + return p.voidPPtr +} + // Bool returns bool type. func (p Program) Bool() Type { if p.boolTy == nil { diff --git a/ssa/type.go b/ssa/type.go index 2b1f3608..6f8b2161 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -274,6 +274,7 @@ func (p Program) toType(raw types.Type) Type { if t.Empty() { return &aType{p.rtEface(), typ, vkEface} } + return &aType{p.rtIface(), typ, vkIface} case *types.Slice: return &aType{p.rtSlice(), typ, vkSlice} case *types.Map: diff --git a/ssa/type_cvt.go b/ssa/type_cvt.go index 9bf505b6..aa578b5d 100644 --- a/ssa/type_cvt.go +++ b/ssa/type_cvt.go @@ -66,27 +66,6 @@ func (p Program) FuncDecl(sig *types.Signature, bg Background) Type { return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl} } -/* -// cvtCxFunc converts a C extended function type into raw type. -func cvtCxFunc(sig *types.Signature, recv *types.Var) *types.Signature { - if sig.Variadic() { - // convert printf-like function type - tParams := sig.Params() - n := tParams.Len() - params := make([]*types.Var, n) - n-- - for i := 0; i < n; i++ { - params[i] = tParams.At(i) - } - params[n] = VArg() - sig = types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), true) - panic("todo") - } - sig = FuncAddCtx(recv, sig) - return sig -} -*/ - // Closure creates a closture type for a function. func (p Program) Closure(fn Type) Type { sig := fn.raw.Type.(*types.Signature)