From afd3d4034870d9e1314588b6aac34b11b85d91c9 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 15:40:24 +0800 Subject: [PATCH 01/10] llgo/ssa: vkFuncDecl/vkFuncPtr/vkClosure --- internal/runtime/c/c.go | 3 ++ internal/runtime/z_closure.go | 32 ++++++++++++++++++ ssa/expr.go | 2 +- ssa/package.go | 21 ++++++++---- ssa/type.go | 61 +++++++++++++++++++++++++---------- 5 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 internal/runtime/z_closure.go diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index ca111468..ac88b11a 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -63,6 +63,9 @@ func Malloc(size uintptr) Pointer //go:linkname Memcpy C.memcpy func Memcpy(dst, src Pointer, n uintptr) Pointer +//go:linkname Memset C.memset +func Memset(s Pointer, c Int, n uintptr) Pointer + //go:linkname Printf C.printf func Printf(format *Char, __llgo_va_list ...any) Int diff --git a/internal/runtime/z_closure.go b/internal/runtime/z_closure.go new file mode 100644 index 00000000..41899815 --- /dev/null +++ b/internal/runtime/z_closure.go @@ -0,0 +1,32 @@ +/* + * 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} +} diff --git a/ssa/expr.go b/ssa/expr.go index 5200bff4..3cadd364 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -979,7 +979,7 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { case *types.Slice: return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg) case *types.Basic: - if t.Info()&types.IsString != 0 { + if t.Kind() == types.String { return b.InlineCall(b.fn.pkg.rtFunc("StringLen"), arg) } } diff --git a/ssa/package.go b/ssa/package.go index 900c7da9..985a155b 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -17,7 +17,6 @@ package ssa import ( - "go/constant" "go/types" "runtime" @@ -116,10 +115,11 @@ type aProgram struct { voidType llvm.Type voidPtrTy llvm.Type - rtStringTy llvm.Type - rtIfaceTy llvm.Type - rtSliceTy llvm.Type - rtMapTy llvm.Type + rtClosureTy llvm.Type + rtStringTy llvm.Type + rtIfaceTy llvm.Type + rtSliceTy llvm.Type + rtMapTy llvm.Type anyTy Type voidTy Type @@ -185,6 +185,13 @@ func (p Program) rtType(name string) Type { return p.Type(p.rtNamed(name)) } +func (p Program) rtClosure() llvm.Type { + if p.rtClosureTy.IsNil() { + p.rtClosureTy = p.rtType("Closure").ll + } + return p.rtClosureTy +} + func (p Program) rtIface() llvm.Type { if p.rtIfaceTy.IsNil() { p.rtIfaceTy = p.rtType("Interface").ll @@ -305,10 +312,12 @@ type aPackage struct { type Package = *aPackage +/* // NewConst creates a new named constant. func (p Package) NewConst(name string, val constant.Value) NamedConst { return &aNamedConst{} } +*/ // NewVar creates a new global variable. func (p Package) NewVar(name string, typ types.Type) Global { @@ -329,7 +338,7 @@ func (p Package) NewFunc(name string, sig *types.Signature) Function { if v, ok := p.fns[name]; ok { return v } - t := p.prog.llvmSignature(sig, false) + t := p.prog.llvmFuncDecl(sig) fn := llvm.AddFunction(p.mod, name, t.ll) ret := newFunction(fn, t, p, p.prog) p.fns[name] = ret diff --git a/ssa/type.go b/ssa/type.go index c33b1e5d..d2e764e2 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -40,12 +40,17 @@ const ( vkString vkBool vkPtr - vkFunc + vkFuncDecl // func decl + vkFuncPtr // func ptr in C + vkClosure // func ptr in Go vkTuple vkDelayExpr = -1 vkPhisExpr = -2 ) +// CFuncPtr represents a C function pointer. +type CFuncPtr types.Signature + // ----------------------------------------------------------------------------- const ( @@ -97,10 +102,17 @@ func methodToFunc(sig *types.Signature) *types.Signature { // ----------------------------------------------------------------------------- +// CType convert a cdecl type into Go type. +func CType(typ types.Type) types.Type { + panic("todo") +} + +// ----------------------------------------------------------------------------- + type aType struct { ll llvm.Type t types.Type - kind valueKind + kind valueKind // value kind of llvm.Type } type Type = *aType @@ -128,9 +140,11 @@ func (p Program) Field(typ Type, i int) Type { } func (p Program) Type(typ types.Type) Type { + /* TODO(xsw): no need? if sig, ok := typ.(*types.Signature); ok { // should methodToFunc return p.llvmSignature(sig, true) } + */ if v := p.typs.At(typ); v != nil { return v.(Type) } @@ -139,14 +153,9 @@ func (p Program) Type(typ types.Type) Type { return ret } -func (p Program) llvmSignature(sig *types.Signature, isPtr bool) Type { +func (p Program) llvmFuncDecl(sig *types.Signature) Type { sig = methodToFunc(sig) - if v := p.typs.At(sig); v != nil { - return v.(Type) - } - ret := p.toLLVMFunc(sig, isPtr) - p.typs.Set(sig, ret) - return ret + return p.toLLVMFunc(sig, false, true) // don't save func decl to cache } func (p Program) tyVoidPtr() llvm.Type { @@ -262,6 +271,8 @@ func (p Program) toLLVMType(typ types.Type) Type { return p.toLLVMStruct(t) case *types.Named: return p.toLLVMNamed(t) + case *types.Signature: + return p.toLLVMFunc(t, false, false) case *types.Array: elem := p.Type(t.Elem()) return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} @@ -307,12 +318,17 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) { return } -func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type { +func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type { + var hasVArg bool + var kind valueKind + var ft llvm.Type tParams := sig.Params() n := tParams.Len() - hasVArg := HasVArg(tParams, n) - if hasVArg { - n-- + if inC { + hasVArg = HasVArg(tParams, n) + if hasVArg { + n-- + } } params := p.toLLVMTypes(tParams, n) out := sig.Results() @@ -325,11 +341,19 @@ func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type { default: ret = p.toLLVMTuple(out) } - ft := llvm.FunctionType(ret, params, hasVArg) - if isPtr { - ft = llvm.PointerType(ft, 0) + if inC || isDecl { + ft = llvm.FunctionType(ret, params, hasVArg) + if isDecl { + kind = vkFuncDecl + } else { + ft = llvm.PointerType(ft, 0) + kind = vkFuncPtr + } + } else { + ft = p.rtClosure() + kind = vkClosure } - return &aType{ft, sig, vkFunc} + return &aType{ft, sig, kind} } func (p Program) retType(sig *types.Signature) Type { @@ -349,6 +373,9 @@ func (p Program) toLLVMNamed(typ *types.Named) Type { case *types.Struct: name := NameOf(typ) return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid} + case *types.Signature: + inC := true // TODO(xsw): how to check in C + return p.toLLVMFunc(t, inC, false) default: return p.Type(t) } From 4a5c8d3fbb441870a3b0ad9897be51bb0ed3f50a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 16:00:31 +0800 Subject: [PATCH 02/10] cl: call llgo/ssa.CType/CFuncDecl --- cl/compile.go | 10 ++++++++-- cl/import.go | 12 +++++++++--- ssa/type.go | 47 ++++++++++++++++++++++++++--------------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index 917335dc..e9f7bbd3 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -177,15 +177,18 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) { // Global variable. func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { typ := gbl.Type() - name, isDef := p.varName(gbl.Pkg.Pkg, gbl) + name, vtype := p.varName(gbl.Pkg.Pkg, gbl) if ignoreName(name) || checkCgo(gbl.Name()) { return } if debugInstr { log.Println("==> NewVar", name, typ) } + if vtype == cVar { + typ = llssa.CType(typ) + } g := pkg.NewVar(name, typ) - if isDef { + if vtype == goVar { g.Init(p.prog.Null(g.Type)) } } @@ -200,6 +203,9 @@ func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa if debugInstr { log.Println("==> NewFunc", name, "type:", sig.Recv(), sig) } + if ftype == cFunc { + sig = llssa.CFuncDecl(sig) + } fn := pkg.NewFunc(name, sig) p.inits = append(p.inits, func() { p.fn = fn diff --git a/cl/import.go b/cl/import.go index e02e5d34..463988eb 100644 --- a/cl/import.go +++ b/cl/import.go @@ -220,12 +220,18 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (s return name, goFunc } -func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, isDef bool) { +const ( + ignoredVar = iota + goVar + cVar +) + +func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) { name := llssa.FullName(pkg, v.Name()) if v, ok := p.link[name]; ok { - return v, false + return v, cVar } - return name, true + return name, goVar } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. diff --git a/ssa/type.go b/ssa/type.go index d2e764e2..c87618e0 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -102,11 +102,16 @@ func methodToFunc(sig *types.Signature) *types.Signature { // ----------------------------------------------------------------------------- -// CType convert a cdecl type into Go type. +// CType convert a C type into Go. func CType(typ types.Type) types.Type { panic("todo") } +// CFuncDecl convert a C function decl into Go signature. +func CFuncDecl(sig *types.Signature) *types.Signature { + panic("todo") +} + // ----------------------------------------------------------------------------- type aType struct { @@ -319,29 +324,29 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) { } func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type { - var hasVArg bool var kind valueKind var ft llvm.Type - tParams := sig.Params() - n := tParams.Len() - if inC { - hasVArg = HasVArg(tParams, n) - if hasVArg { - n-- + if isDecl || inC { + var tParams = sig.Params() + var n = tParams.Len() + var hasVArg bool + if inC { + hasVArg = HasVArg(tParams, n) + if hasVArg { + n-- + } + } + params := p.toLLVMTypes(tParams, n) + out := sig.Results() + var ret llvm.Type + switch nret := out.Len(); nret { + case 0: + ret = p.tyVoid() + case 1: + ret = p.Type(out.At(0).Type()).ll + default: + ret = p.toLLVMTuple(out) } - } - params := p.toLLVMTypes(tParams, n) - out := sig.Results() - var ret llvm.Type - switch nret := out.Len(); nret { - case 0: - ret = p.tyVoid() - case 1: - ret = p.Type(out.At(0).Type()).ll - default: - ret = p.toLLVMTuple(out) - } - if inC || isDecl { ft = llvm.FunctionType(ret, params, hasVArg) if isDecl { kind = vkFuncDecl From 133d41d7484ad627924b636463aececefeea4575 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 16:51:01 +0800 Subject: [PATCH 03/10] llgo/ssa: CType, CFuncDecl --- ssa/type.go | 49 +++---------------- ssa/type_c.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 42 deletions(-) create mode 100644 ssa/type_c.go diff --git a/ssa/type.go b/ssa/type.go index c87618e0..93feee6b 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -48,27 +48,8 @@ const ( vkPhisExpr = -2 ) -// CFuncPtr represents a C function pointer. -type CFuncPtr types.Signature - // ----------------------------------------------------------------------------- -const ( - NameValist = "__llgo_va_list" -) - -func VArg() *types.Var { - return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid]) -} - -func IsVArg(arg *types.Var) bool { - return arg.Name() == NameValist -} - -func HasVArg(t *types.Tuple, n int) bool { - return n > 0 && IsVArg(t.At(n-1)) -} - func indexType(t types.Type) types.Type { switch t := t.(type) { case *types.Slice: @@ -102,18 +83,6 @@ func methodToFunc(sig *types.Signature) *types.Signature { // ----------------------------------------------------------------------------- -// CType convert a C type into Go. -func CType(typ types.Type) types.Type { - panic("todo") -} - -// CFuncDecl convert a C function decl into Go signature. -func CFuncDecl(sig *types.Signature) *types.Signature { - panic("todo") -} - -// ----------------------------------------------------------------------------- - type aType struct { ll llvm.Type t types.Type @@ -278,6 +247,8 @@ func (p Program) toLLVMType(typ types.Type) Type { return p.toLLVMNamed(t) case *types.Signature: return p.toLLVMFunc(t, false, false) + case *CFuncPtr: + return p.toLLVMFunc((*types.Signature)(t), true, false) case *types.Array: elem := p.Type(t.Elem()) return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} @@ -327,14 +298,11 @@ func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type { var kind valueKind var ft llvm.Type if isDecl || inC { - var tParams = sig.Params() - var n = tParams.Len() - var hasVArg bool - if inC { - hasVArg = HasVArg(tParams, n) - if hasVArg { - n-- - } + tParams := sig.Params() + n := tParams.Len() + hasVArg := HasVArg(tParams, n) + if hasVArg { + n-- } params := p.toLLVMTypes(tParams, n) out := sig.Results() @@ -378,9 +346,6 @@ func (p Program) toLLVMNamed(typ *types.Named) Type { case *types.Struct: name := NameOf(typ) return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid} - case *types.Signature: - inC := true // TODO(xsw): how to check in C - return p.toLLVMFunc(t, inC, false) default: return p.Type(t) } diff --git a/ssa/type_c.go b/ssa/type_c.go new file mode 100644 index 00000000..e4d982f5 --- /dev/null +++ b/ssa/type_c.go @@ -0,0 +1,128 @@ +/* + * 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/types" + +// ----------------------------------------------------------------------------- + +const ( + NameValist = "__llgo_va_list" +) + +func VArg() *types.Var { + return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid]) +} + +func IsVArg(arg *types.Var) bool { + return arg.Name() == NameValist +} + +func HasVArg(t *types.Tuple, n int) bool { + return n > 0 && IsVArg(t.At(n-1)) +} + +// ----------------------------------------------------------------------------- + +// CFuncPtr represents a C function pointer. +type CFuncPtr types.Signature + +func (t *CFuncPtr) String() string { return (*types.Signature)(t).String() } +func (t *CFuncPtr) Underlying() types.Type { return (*types.Signature)(t) } + +// ----------------------------------------------------------------------------- + +// CType convert a C type into Go. +func CType(typ types.Type) types.Type { + t, _ := cvtCType(typ) + return t +} + +// CFuncDecl convert a C function decl into Go signature. +func CFuncDecl(sig *types.Signature) *types.Signature { + hasVArg := sig.Variadic() + params, cvt1 := cvtTuple(sig.Params(), hasVArg) + results, cvt2 := cvtTuple(sig.Results(), false) + if cvt1 || cvt2 { + return types.NewSignatureType(nil, nil, nil, params, results, hasVArg) + } + return sig +} + +func cvtCType(typ types.Type) (types.Type, bool) { + switch t := typ.(type) { + case *types.Basic: + case *types.Pointer: + if elem, cvt := cvtCType(t.Elem()); cvt { + return types.NewPointer(elem), true + } + case *types.Struct: + return cvtCStruct(t) + case *types.Signature: + t = CFuncDecl(t) + return (*CFuncPtr)(t), true + case *types.Array: + if elem, cvt := cvtCType(t.Elem()); cvt { + return types.NewArray(elem, t.Len()), true + } + default: + panic("unreachable") + } + return typ, false +} + +func cvtTuple(t *types.Tuple, hasVArg bool) (*types.Tuple, bool) { + n := t.Len() + vars := make([]*types.Var, n) + needcvt := false + if hasVArg { + n-- + vars[n] = t.At(n) + } + for i := 0; i < n; i++ { + v := t.At(i) + if t, cvt := cvtCType(v.Type()); cvt { + v = types.NewParam(v.Pos(), v.Pkg(), v.Name(), t) + needcvt = true + } + vars[i] = v + } + if needcvt { + return types.NewTuple(vars...), true + } + return t, false +} + +func cvtCStruct(typ *types.Struct) (*types.Struct, bool) { + n := typ.NumFields() + flds := make([]*types.Var, n) + needcvt := false + for i := 0; i < n; i++ { + f := typ.Field(i) + if t, cvt := cvtCType(f.Type()); cvt { + f = types.NewField(f.Pos(), f.Pkg(), f.Name(), t, f.Anonymous()) + needcvt = true + } + flds[i] = f + } + if needcvt { + return types.NewStruct(flds, nil), true + } + return typ, false +} + +// ----------------------------------------------------------------------------- From 8eeac8a26d40a75d2b7758264a88e10e80770bbf Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 17:03:12 +0800 Subject: [PATCH 04/10] cvtCType bugfix --- cl/_testrt/gblarray/out.ll | 3 ++- ssa/type_c.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cl/_testrt/gblarray/out.ll b/cl/_testrt/gblarray/out.ll index c03b7420..ab730de7 100644 --- a/cl/_testrt/gblarray/out.ll +++ b/cl/_testrt/gblarray/out.ll @@ -1,7 +1,8 @@ ; ModuleID = 'main' source_filename = "main" -%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 } +%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, %"github.com/goplus/llgo/internal/runtime.Closure", ptr, i32, i32 } +%"github.com/goplus/llgo/internal/runtime.Closure" = type { ptr, ptr } @main.basicTypes = global ptr null @"main.init$guard" = global ptr null diff --git a/ssa/type_c.go b/ssa/type_c.go index e4d982f5..ff217cb3 100644 --- a/ssa/type_c.go +++ b/ssa/type_c.go @@ -16,7 +16,9 @@ package ssa -import "go/types" +import ( + "go/types" +) // ----------------------------------------------------------------------------- @@ -72,6 +74,10 @@ func cvtCType(typ types.Type) (types.Type, bool) { } case *types.Struct: return cvtCStruct(t) + case *types.Named: + if _, cvt := cvtCType(t.Underlying()); cvt { + panic("todo: named type") + } case *types.Signature: t = CFuncDecl(t) return (*CFuncPtr)(t), true From 29fad7b397797c72512156011b227cba07f6d51c Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 17:08:44 +0800 Subject: [PATCH 05/10] TestCFuncPtr --- internal/abi/llgo_autogen.ll | 5 +++-- internal/runtime/llgo_autogen.ll | 14 +++++++++++++- ssa/ssa_test.go | 9 +++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/internal/abi/llgo_autogen.ll b/internal/abi/llgo_autogen.ll index 8018f1be..085e8b04 100644 --- a/internal/abi/llgo_autogen.ll +++ b/internal/abi/llgo_autogen.ll @@ -2,13 +2,14 @@ source_filename = "github.com/goplus/llgo/internal/abi" %"github.com/goplus/llgo/internal/abi.ArrayType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, i64 } -%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 } +%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, %"github.com/goplus/llgo/internal/runtime.Closure", ptr, i32, i32 } +%"github.com/goplus/llgo/internal/runtime.Closure" = type { ptr, ptr } %"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 } %"github.com/goplus/llgo/internal/abi.FuncType" = type { %"github.com/goplus/llgo/internal/abi.Type", i16, i16 } %"github.com/goplus/llgo/internal/abi.InterfaceType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" } %"github.com/goplus/llgo/internal/abi.Name" = type { ptr } %"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } -%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, ptr, i8, i8, i16, i32 } +%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, %"github.com/goplus/llgo/internal/runtime.Closure", i8, i8, i16, i32 } %"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr } %"github.com/goplus/llgo/internal/abi.SliceType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr } %"github.com/goplus/llgo/internal/abi.StructType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" } diff --git a/internal/runtime/llgo_autogen.ll b/internal/runtime/llgo_autogen.ll index bf1294e7..c242c8c2 100644 --- a/internal/runtime/llgo_autogen.ll +++ b/internal/runtime/llgo_autogen.ll @@ -4,8 +4,9 @@ source_filename = "github.com/goplus/llgo/internal/runtime" %"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } %"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } %"github.com/goplus/llgo/internal/runtime.itab" = type { ptr, ptr, i32, [4 x i8], [1 x i64] } +%"github.com/goplus/llgo/internal/runtime.Closure" = type { ptr, ptr } %"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } -%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 } +%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, %"github.com/goplus/llgo/internal/runtime.Closure", ptr, i32, i32 } %"github.com/goplus/llgo/internal/runtime.hmap" = type { i64, i8, i8, i16, i32, ptr, ptr, i64, ptr } @"github.com/goplus/llgo/internal/runtime.TyAny" = global ptr null @@ -211,6 +212,17 @@ _llgo_0: ret ptr %0 } +define %"github.com/goplus/llgo/internal/runtime.Closure" @"github.com/goplus/llgo/internal/runtime.NewClosure"(ptr %0, ptr %1) { +_llgo_0: + %2 = alloca %"github.com/goplus/llgo/internal/runtime.Closure", align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Closure", ptr %2, i32 0, i32 0 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Closure", ptr %2, i32 0, i32 1 + store ptr %0, ptr %3, align 8 + store ptr %1, ptr %4, align 8 + %5 = load %"github.com/goplus/llgo/internal/runtime.Closure", ptr %2, align 8 + ret %"github.com/goplus/llgo/internal/runtime.Closure" %5 +} + define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) { _llgo_0: %3 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index ceaea2e4..ff8c21c3 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -30,6 +30,15 @@ func TestMakeInterface(t *testing.T) { } */ +func TestCFuncPtr(t *testing.T) { + sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + csig := (*CFuncPtr)(sig) + _ = csig.String() + if csig.Underlying() != sig { + t.Fatal("TestCFuncPtr failed") + } +} + func TestUserdefExpr(t *testing.T) { a := delayExprTy(nil) b := &phisExprTy{} From 13a1c8ac4b8c1d3ef2f479a59de38c60f612faf6 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 17:39:37 +0800 Subject: [PATCH 06/10] github.com/goplus/llgo/internal/typeutil.Map --- internal/aliases/aliases.go | 32 ++ internal/aliases/aliases_go121.go | 31 ++ internal/aliases/aliases_go122.go | 63 ++++ internal/typeparams/normalize.go | 218 +++++++++++++ internal/typeparams/termlist.go | 163 ++++++++++ internal/typeparams/typeterm.go | 169 ++++++++++ internal/typeutil/map.go | 518 ++++++++++++++++++++++++++++++ ssa/package.go | 2 +- 8 files changed, 1195 insertions(+), 1 deletion(-) create mode 100644 internal/aliases/aliases.go create mode 100644 internal/aliases/aliases_go121.go create mode 100644 internal/aliases/aliases_go122.go create mode 100644 internal/typeparams/normalize.go create mode 100644 internal/typeparams/termlist.go create mode 100644 internal/typeparams/typeterm.go create mode 100644 internal/typeutil/map.go diff --git a/internal/aliases/aliases.go b/internal/aliases/aliases.go new file mode 100644 index 00000000..c24c2eee --- /dev/null +++ b/internal/aliases/aliases.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package aliases + +import ( + "go/token" + "go/types" +) + +// Package aliases defines backward compatible shims +// for the types.Alias type representation added in 1.22. +// This defines placeholders for x/tools until 1.26. + +// NewAlias creates a new TypeName in Package pkg that +// is an alias for the type rhs. +// +// The enabled parameter determines whether the resulting [TypeName]'s +// type is an [types.Alias]. Its value must be the result of a call to +// [Enabled], which computes the effective value of +// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled +// function is expensive and should be called once per task (e.g. +// package import), not once per call to NewAlias. +func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName { + if enabled { + tname := types.NewTypeName(pos, pkg, name, nil) + newAlias(tname, rhs) + return tname + } + return types.NewTypeName(pos, pkg, name, rhs) +} diff --git a/internal/aliases/aliases_go121.go b/internal/aliases/aliases_go121.go new file mode 100644 index 00000000..c027b9f3 --- /dev/null +++ b/internal/aliases/aliases_go121.go @@ -0,0 +1,31 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.22 +// +build !go1.22 + +package aliases + +import ( + "go/types" +) + +// Alias is a placeholder for a go/types.Alias for <=1.21. +// It will never be created by go/types. +type Alias struct{} + +func (*Alias) String() string { panic("unreachable") } +func (*Alias) Underlying() types.Type { panic("unreachable") } +func (*Alias) Obj() *types.TypeName { panic("unreachable") } +func Rhs(alias *Alias) types.Type { panic("unreachable") } + +// Unalias returns the type t for go <=1.21. +func Unalias(t types.Type) types.Type { return t } + +func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") } + +// Enabled reports whether [NewAlias] should create [types.Alias] types. +// +// Before go1.22, this function always returns false. +func Enabled() bool { return false } diff --git a/internal/aliases/aliases_go122.go b/internal/aliases/aliases_go122.go new file mode 100644 index 00000000..b3299548 --- /dev/null +++ b/internal/aliases/aliases_go122.go @@ -0,0 +1,63 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 +// +build go1.22 + +package aliases + +import ( + "go/ast" + "go/parser" + "go/token" + "go/types" +) + +// Alias is an alias of types.Alias. +type Alias = types.Alias + +// Rhs returns the type on the right-hand side of the alias declaration. +func Rhs(alias *Alias) types.Type { + if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok { + return alias.Rhs() // go1.23+ + } + + // go1.22's Alias didn't have the Rhs method, + // so Unalias is the best we can do. + return Unalias(alias) +} + +// Unalias is a wrapper of types.Unalias. +func Unalias(t types.Type) types.Type { return types.Unalias(t) } + +// newAlias is an internal alias around types.NewAlias. +// Direct usage is discouraged as the moment. +// Try to use NewAlias instead. +func newAlias(tname *types.TypeName, rhs types.Type) *Alias { + a := types.NewAlias(tname, rhs) + // TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect. + Unalias(a) + return a +} + +// Enabled reports whether [NewAlias] should create [types.Alias] types. +// +// This function is expensive! Call it sparingly. +func Enabled() bool { + // The only reliable way to compute the answer is to invoke go/types. + // We don't parse the GODEBUG environment variable, because + // (a) it's tricky to do so in a manner that is consistent + // with the godebug package; in particular, a simple + // substring check is not good enough. The value is a + // rightmost-wins list of options. But more importantly: + // (b) it is impossible to detect changes to the effective + // setting caused by os.Setenv("GODEBUG"), as happens in + // many tests. Therefore any attempt to cache the result + // is just incorrect. + fset := token.NewFileSet() + f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0) + pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil) + _, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias) + return enabled +} diff --git a/internal/typeparams/normalize.go b/internal/typeparams/normalize.go new file mode 100644 index 00000000..93c80fdc --- /dev/null +++ b/internal/typeparams/normalize.go @@ -0,0 +1,218 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "errors" + "fmt" + "go/types" + "os" + "strings" +) + +//go:generate go run copytermlist.go + +const debug = false + +var ErrEmptyTypeSet = errors.New("empty type set") + +// StructuralTerms returns a slice of terms representing the normalized +// structural type restrictions of a type parameter, if any. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration +// +// type T[P interface{~int; m()}] int +// +// the structural restriction of the type parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// StructuralTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, StructuralTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the +// constraint interface is invalid, exceeds complexity bounds, or has an empty +// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. +// +// StructuralTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { + constraint := tparam.Constraint() + if constraint == nil { + return nil, fmt.Errorf("%s has nil constraint", tparam) + } + iface, _ := constraint.Underlying().(*types.Interface) + if iface == nil { + return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) + } + return InterfaceTermSet(iface) +} + +// InterfaceTermSet computes the normalized terms for a constraint interface, +// returning an error if the term set cannot be computed or is empty. In the +// latter case, the error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { + return computeTermSet(iface) +} + +// UnionTermSet computes the normalized terms for a union, returning an error +// if the term set cannot be computed or is empty. In the latter case, the +// error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func UnionTermSet(union *types.Union) ([]*types.Term, error) { + return computeTermSet(union) +} + +func computeTermSet(typ types.Type) ([]*types.Term, error) { + tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) + if err != nil { + return nil, err + } + if tset.terms.isEmpty() { + return nil, ErrEmptyTypeSet + } + if tset.terms.isAll() { + return nil, nil + } + var terms []*types.Term + for _, term := range tset.terms { + terms = append(terms, types.NewTerm(term.tilde, term.typ)) + } + return terms, nil +} + +// A termSet holds the normalized set of terms for a given type. +// +// The name termSet is intentionally distinct from 'type set': a type set is +// all types that implement a type (and includes method restrictions), whereas +// a term set just represents the structural restrictions on a type. +type termSet struct { + complete bool + terms termlist +} + +func indentf(depth int, format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) +} + +func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { + if t == nil { + panic("nil type") + } + + if debug { + indentf(depth, "%s", t.String()) + defer func() { + if err != nil { + indentf(depth, "=> %s", err) + } else { + indentf(depth, "=> %s", res.terms.String()) + } + }() + } + + const maxTermCount = 100 + if tset, ok := seen[t]; ok { + if !tset.complete { + return nil, fmt.Errorf("cycle detected in the declaration of %s", t) + } + return tset, nil + } + + // Mark the current type as seen to avoid infinite recursion. + tset := new(termSet) + defer func() { + tset.complete = true + }() + seen[t] = tset + + switch u := t.Underlying().(type) { + case *types.Interface: + // The term set of an interface is the intersection of the term sets of its + // embedded types. + tset.terms = allTermlist + for i := 0; i < u.NumEmbeddeds(); i++ { + embedded := u.EmbeddedType(i) + if _, ok := embedded.Underlying().(*types.TypeParam); ok { + return nil, fmt.Errorf("invalid embedded type %T", embedded) + } + tset2, err := computeTermSetInternal(embedded, seen, depth+1) + if err != nil { + return nil, err + } + tset.terms = tset.terms.intersect(tset2.terms) + } + case *types.Union: + // The term set of a union is the union of term sets of its terms. + tset.terms = nil + for i := 0; i < u.Len(); i++ { + t := u.Term(i) + var terms termlist + switch t.Type().Underlying().(type) { + case *types.Interface: + tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) + if err != nil { + return nil, err + } + terms = tset2.terms + case *types.TypeParam, *types.Union: + // A stand-alone type parameter or union is not permitted as union + // term. + return nil, fmt.Errorf("invalid union term %T", t) + default: + if t.Type() == types.Typ[types.Invalid] { + continue + } + terms = termlist{{t.Tilde(), t.Type()}} + } + tset.terms = tset.terms.union(terms) + if len(tset.terms) > maxTermCount { + return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) + } + } + case *types.TypeParam: + panic("unreachable") + default: + // For all other types, the term set is just a single non-tilde term + // holding the type itself. + if u != types.Typ[types.Invalid] { + tset.terms = termlist{{false, t}} + } + } + return tset, nil +} + +// under is a facade for the go/types internal function of the same name. It is +// used by typeterm.go. +func under(t types.Type) types.Type { + return t.Underlying() +} diff --git a/internal/typeparams/termlist.go b/internal/typeparams/termlist.go new file mode 100644 index 00000000..cbd12f80 --- /dev/null +++ b/internal/typeparams/termlist.go @@ -0,0 +1,163 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import ( + "bytes" + "go/types" +) + +// A termlist represents the type set represented by the union +// t1 βˆͺ y2 βˆͺ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "βˆ…" + } + var buf bytes.Buffer + for i, x := range xl { + if i > 0 { + buf.WriteString(" | ") + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓀 term, the entire list is 𝓀. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓀 term, the entire list is 𝓀. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓀 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// union returns the union xl βˆͺ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t types.Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y βŠ† xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl βŠ† yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/internal/typeparams/typeterm.go b/internal/typeparams/typeterm.go new file mode 100644 index 00000000..7350bb70 --- /dev/null +++ b/internal/typeparams/typeterm.go @@ -0,0 +1,169 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import "go/types" + +// A term describes elementary type sets: +// +// βˆ…: (*term)(nil) == βˆ… // set of no types (empty set) +// 𝓀: &term{} == 𝓀 // set of all types (𝓀niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +type term struct { + tilde bool // valid if typ != nil + typ types.Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "βˆ…" + case x.typ == nil: + return "𝓀" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // βˆ… βŠ‚ x, y βŠ‚ 𝓀 + + return x.tilde == y.tilde && types.Identical(x.typ, y.typ) +} + +// union returns the union x βˆͺ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // βˆ… βˆͺ βˆ… == βˆ… + case x == nil: + return y, nil // βˆ… βˆͺ y == y + case y == nil: + return x, nil // x βˆͺ βˆ… == x + case x.typ == nil: + return x, nil // 𝓀 βˆͺ y == 𝓀 + case y.typ == nil: + return y, nil // x βˆͺ 𝓀 == 𝓀 + } + // βˆ… βŠ‚ x, y βŠ‚ 𝓀 + + if x.disjoint(y) { + return x, y // x βˆͺ y == (x, y) if x ∩ y == βˆ… + } + // x.typ == y.typ + + // ~t βˆͺ ~t == ~t + // ~t βˆͺ T == ~t + // T βˆͺ ~t == ~t + // T βˆͺ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // βˆ… ∩ y == βˆ… and ∩ βˆ… == βˆ… + case x.typ == nil: + return y // 𝓀 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓀 == x + } + // βˆ… βŠ‚ x, y βŠ‚ 𝓀 + + if x.disjoint(y) { + return nil // x ∩ y == βˆ… if x ∩ y == βˆ… + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t types.Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ βˆ… == false + case x.typ == nil: + return true // t ∈ 𝓀 == true + } + // βˆ… βŠ‚ x βŠ‚ 𝓀 + + u := t + if x.tilde { + u = under(u) + } + return types.Identical(x.typ, u) +} + +// subsetOf reports whether x βŠ† y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // βˆ… βŠ† y == true + case y == nil: + return false // x βŠ† βˆ… == false since x != βˆ… + case y.typ == nil: + return true // x βŠ† 𝓀 == true + case x.typ == nil: + return false // 𝓀 βŠ† y == false since y != 𝓀 + } + // βˆ… βŠ‚ x, y βŠ‚ 𝓀 + + if x.disjoint(y) { + return false // x βŠ† y == false if x ∩ y == βˆ… + } + // x.typ == y.typ + + // ~t βŠ† ~t == true + // ~t βŠ† T == false + // T βŠ† ~t == true + // T βŠ† T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == βˆ…. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !types.Identical(ux, uy) +} diff --git a/internal/typeutil/map.go b/internal/typeutil/map.go new file mode 100644 index 00000000..5c52363a --- /dev/null +++ b/internal/typeutil/map.go @@ -0,0 +1,518 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typeutil defines various utilities for types, such as Map, +// a mapping from types.Type to interface{} values. +package typeutil // import "golang.org/x/tools/go/types/typeutil" + +import ( + "bytes" + "fmt" + "go/types" + "reflect" + + "github.com/goplus/llgo/internal/aliases" + "github.com/goplus/llgo/internal/typeparams" +) + +// Map is a hash-table-based mapping from types (types.Type) to +// arbitrary interface{} values. The concrete types that implement +// the Type interface are pointers. Since they are not canonicalized, +// == cannot be used to check for equivalence, and thus we cannot +// simply use a Go map. +// +// Just as with map[K]V, a nil *Map is a valid empty map. +// +// Not thread-safe. +type Map struct { + hasher Hasher // shared by many Maps + table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused + length int // number of map entries +} + +// entry is an entry (key/value association) in a hash bucket. +type entry struct { + key types.Type + value interface{} +} + +// SetHasher sets the hasher used by Map. +// +// All Hashers are functionally equivalent but contain internal state +// used to cache the results of hashing previously seen types. +// +// A single Hasher created by MakeHasher() may be shared among many +// Maps. This is recommended if the instances have many keys in +// common, as it will amortize the cost of hash computation. +// +// A Hasher may grow without bound as new types are seen. Even when a +// type is deleted from the map, the Hasher never shrinks, since other +// types in the map may reference the deleted type indirectly. +// +// Hashers are not thread-safe, and read-only operations such as +// Map.Lookup require updates to the hasher, so a full Mutex lock (not a +// read-lock) is require around all Map operations if a shared +// hasher is accessed from multiple threads. +// +// If SetHasher is not called, the Map will create a private hasher at +// the first call to Insert. +func (m *Map) SetHasher(hasher Hasher) { + m.hasher = hasher +} + +// Delete removes the entry with the given key, if any. +// It returns true if the entry was found. +func (m *Map) Delete(key types.Type) bool { + if m != nil && m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + for i, e := range bucket { + if e.key != nil && types.Identical(key, e.key) { + // We can't compact the bucket as it + // would disturb iterators. + bucket[i] = entry{} + m.length-- + return true + } + } + } + return false +} + +// At returns the map entry for the given key. +// The result is nil if the entry is not present. +func (m *Map) At(key types.Type) interface{} { + if m != nil && m.table != nil { + for _, e := range m.table[m.hasher.Hash(key)] { + if e.key != nil && types.Identical(key, e.key) { + return e.value + } + } + } + return nil +} + +// Set sets the map entry for key to val, +// and returns the previous entry, if any. +func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { + if m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + var hole *entry + for i, e := range bucket { + if e.key == nil { + hole = &bucket[i] + } else if types.Identical(key, e.key) { + prev = e.value + bucket[i].value = value + return + } + } + + if hole != nil { + *hole = entry{key, value} // overwrite deleted entry + } else { + m.table[hash] = append(bucket, entry{key, value}) + } + } else { + if m.hasher.memo == nil { + m.hasher = MakeHasher() + } + hash := m.hasher.Hash(key) + m.table = map[uint32][]entry{hash: {entry{key, value}}} + } + + m.length++ + return +} + +// Len returns the number of map entries. +func (m *Map) Len() int { + if m != nil { + return m.length + } + return 0 +} + +// Iterate calls function f on each entry in the map in unspecified order. +// +// If f should mutate the map, Iterate provides the same guarantees as +// Go maps: if f deletes a map entry that Iterate has not yet reached, +// f will not be invoked for it, but if f inserts a map entry that +// Iterate has not yet reached, whether or not f will be invoked for +// it is unspecified. +func (m *Map) Iterate(f func(key types.Type, value interface{})) { + if m != nil { + for _, bucket := range m.table { + for _, e := range bucket { + if e.key != nil { + f(e.key, e.value) + } + } + } + } +} + +// Keys returns a new slice containing the set of map keys. +// The order is unspecified. +func (m *Map) Keys() []types.Type { + keys := make([]types.Type, 0, m.Len()) + m.Iterate(func(key types.Type, _ interface{}) { + keys = append(keys, key) + }) + return keys +} + +func (m *Map) toString(values bool) string { + if m == nil { + return "{}" + } + var buf bytes.Buffer + fmt.Fprint(&buf, "{") + sep := "" + m.Iterate(func(key types.Type, value interface{}) { + fmt.Fprint(&buf, sep) + sep = ", " + fmt.Fprint(&buf, key) + if values { + fmt.Fprintf(&buf, ": %q", value) + } + }) + fmt.Fprint(&buf, "}") + return buf.String() +} + +// String returns a string representation of the map's entries. +// Values are printed using fmt.Sprintf("%v", v). +// Order is unspecified. +func (m *Map) String() string { + return m.toString(true) +} + +// KeysString returns a string representation of the map's key set. +// Order is unspecified. +func (m *Map) KeysString() string { + return m.toString(false) +} + +//////////////////////////////////////////////////////////////////////// +// Hasher + +// A Hasher maps each type to its hash value. +// For efficiency, a hasher uses memoization; thus its memory +// footprint grows monotonically over time. +// Hashers are not thread-safe. +// Hashers have reference semantics. +// Call MakeHasher to create a Hasher. +type Hasher struct { + memo map[types.Type]uint32 + + // ptrMap records pointer identity. + ptrMap map[interface{}]uint32 + + // sigTParams holds type parameters from the signature being hashed. + // Signatures are considered identical modulo renaming of type parameters, so + // within the scope of a signature type the identity of the signature's type + // parameters is just their index. + // + // Since the language does not currently support referring to uninstantiated + // generic types or functions, and instantiated signatures do not have type + // parameter lists, we should never encounter a second non-empty type + // parameter list when hashing a generic signature. + sigTParams *types.TypeParamList +} + +// MakeHasher returns a new Hasher instance. +func MakeHasher() Hasher { + return Hasher{ + memo: make(map[types.Type]uint32), + ptrMap: make(map[interface{}]uint32), + sigTParams: nil, + } +} + +// Hash computes a hash value for the given type t such that +// Identical(t, t') => Hash(t) == Hash(t'). +func (h Hasher) Hash(t types.Type) uint32 { + hash, ok := h.memo[t] + if !ok { + hash = h.hashFor(t) + h.memo[t] = hash + } + return hash +} + +// hashString computes the Fowler–Noll–Vo hash of s. +func hashString(s string) uint32 { + var h uint32 + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +// hashFor computes the hash of t. +func (h Hasher) hashFor(t types.Type) uint32 { + // See Identical for rationale. + switch t := t.(type) { + case *types.Basic: + return uint32(t.Kind()) + + case *aliases.Alias: + return h.Hash(t.Underlying()) + + case *types.Array: + return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) + + case *types.Slice: + return 9049 + 2*h.Hash(t.Elem()) + + case *types.Struct: + var hash uint32 = 9059 + for i, n := 0, t.NumFields(); i < n; i++ { + f := t.Field(i) + if f.Anonymous() { + hash += 8861 + } + hash += hashString(t.Tag(i)) + hash += hashString(f.Name()) // (ignore f.Pkg) + hash += h.Hash(f.Type()) + } + return hash + + case *types.Pointer: + return 9067 + 2*h.Hash(t.Elem()) + + case *types.Signature: + var hash uint32 = 9091 + if t.Variadic() { + hash *= 8863 + } + + // Use a separate hasher for types inside of the signature, where type + // parameter identity is modified to be (index, constraint). We must use a + // new memo for this hasher as type identity may be affected by this + // masking. For example, in func[T any](*T), the identity of *T depends on + // whether we are mapping the argument in isolation, or recursively as part + // of hashing the signature. + // + // We should never encounter a generic signature while hashing another + // generic signature, but defensively set sigTParams only if h.mask is + // unset. + tparams := t.TypeParams() + if h.sigTParams == nil && tparams.Len() != 0 { + h = Hasher{ + // There may be something more efficient than discarding the existing + // memo, but it would require detecting whether types are 'tainted' by + // references to type parameters. + memo: make(map[types.Type]uint32), + // Re-using ptrMap ensures that pointer identity is preserved in this + // hasher. + ptrMap: h.ptrMap, + sigTParams: tparams, + } + } + + for i := 0; i < tparams.Len(); i++ { + tparam := tparams.At(i) + hash += 7 * h.Hash(tparam.Constraint()) + } + + return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) + + case *types.Union: + return h.hashUnion(t) + + case *types.Interface: + // Interfaces are identical if they have the same set of methods, with + // identical names and types, and they have the same set of type + // restrictions. See go/types.identical for more details. + var hash uint32 = 9103 + + // Hash methods. + for i, n := 0, t.NumMethods(); i < n; i++ { + // Method order is not significant. + // Ignore m.Pkg(). + m := t.Method(i) + // Use shallow hash on method signature to + // avoid anonymous interface cycles. + hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type()) + } + + // Hash type restrictions. + terms, err := typeparams.InterfaceTermSet(t) + // if err != nil t has invalid type restrictions. + if err == nil { + hash += h.hashTermSet(terms) + } + + return hash + + case *types.Map: + return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) + + case *types.Chan: + return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) + + case *types.Named: + hash := h.hashPtr(t.Obj()) + targs := t.TypeArgs() + for i := 0; i < targs.Len(); i++ { + targ := targs.At(i) + hash += 2 * h.Hash(targ) + } + return hash + + case *types.TypeParam: + return h.hashTypeParam(t) + + case *types.Tuple: + return h.hashTuple(t) + } + + panic(fmt.Sprintf("%T: %v", t, t)) +} + +func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { + // See go/types.identicalTypes for rationale. + n := tuple.Len() + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 3 * h.Hash(tuple.At(i).Type()) + } + return hash +} + +func (h Hasher) hashUnion(t *types.Union) uint32 { + // Hash type restrictions. + terms, err := typeparams.UnionTermSet(t) + // if err != nil t has invalid type restrictions. Fall back on a non-zero + // hash. + if err != nil { + return 9151 + } + return h.hashTermSet(terms) +} + +func (h Hasher) hashTermSet(terms []*types.Term) uint32 { + hash := 9157 + 2*uint32(len(terms)) + for _, term := range terms { + // term order is not significant. + termHash := h.Hash(term.Type()) + if term.Tilde() { + termHash *= 9161 + } + hash += 3 * termHash + } + return hash +} + +// hashTypeParam returns a hash of the type parameter t, with a hash value +// depending on whether t is contained in h.sigTParams. +// +// If h.sigTParams is set and contains t, then we are in the process of hashing +// a signature, and the hash value of t must depend only on t's index and +// constraint: signatures are considered identical modulo type parameter +// renaming. To avoid infinite recursion, we only hash the type parameter +// index, and rely on types.Identical to handle signatures where constraints +// are not identical. +// +// Otherwise the hash of t depends only on t's pointer identity. +func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 { + if h.sigTParams != nil { + i := t.Index() + if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) { + return 9173 + 3*uint32(i) + } + } + return h.hashPtr(t.Obj()) +} + +// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that +// pointers values are not dependent on the GC. +func (h Hasher) hashPtr(ptr interface{}) uint32 { + if hash, ok := h.ptrMap[ptr]; ok { + return hash + } + hash := uint32(reflect.ValueOf(ptr).Pointer()) + h.ptrMap[ptr] = hash + return hash +} + +// shallowHash computes a hash of t without looking at any of its +// element Types, to avoid potential anonymous cycles in the types of +// interface methods. +// +// When an unnamed non-empty interface type appears anywhere among the +// arguments or results of an interface method, there is a potential +// for endless recursion. Consider: +// +// type X interface { m() []*interface { X } } +// +// The problem is that the Methods of the interface in m's result type +// include m itself; there is no mention of the named type X that +// might help us break the cycle. +// (See comment in go/types.identical, case *Interface, for more.) +func (h Hasher) shallowHash(t types.Type) uint32 { + // t is the type of an interface method (Signature), + // its params or results (Tuples), or their immediate + // elements (mostly Slice, Pointer, Basic, Named), + // so there's no need to optimize anything else. + switch t := t.(type) { + case *aliases.Alias: + return h.shallowHash(t.Underlying()) + + case *types.Signature: + var hash uint32 = 604171 + if t.Variadic() { + hash *= 971767 + } + // The Signature/Tuple recursion is always finite + // and invariably shallow. + return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results()) + + case *types.Tuple: + n := t.Len() + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 53471161 * h.shallowHash(t.At(i).Type()) + } + return hash + + case *types.Basic: + return 45212177 * uint32(t.Kind()) + + case *types.Array: + return 1524181 + 2*uint32(t.Len()) + + case *types.Slice: + return 2690201 + + case *types.Struct: + return 3326489 + + case *types.Pointer: + return 4393139 + + case *types.Union: + return 562448657 + + case *types.Interface: + return 2124679 // no recursion here + + case *types.Map: + return 9109 + + case *types.Chan: + return 9127 + + case *types.Named: + return h.hashPtr(t.Obj()) + + case *types.TypeParam: + return h.hashPtr(t.Obj()) + } + panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) +} diff --git a/ssa/package.go b/ssa/package.go index 985a155b..0d3800cb 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -20,8 +20,8 @@ import ( "go/types" "runtime" + "github.com/goplus/llgo/internal/typeutil" "github.com/goplus/llvm" - "golang.org/x/tools/go/types/typeutil" ) const ( From 236debab33f42c4306e3bf51a248e779bc32f801 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 18:02:09 +0800 Subject: [PATCH 07/10] CFuncPtr.Hash --- internal/typeutil/map.go | 79 ++++++++++++++++++++++------------------ ssa/type_c.go | 6 +++ 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/internal/typeutil/map.go b/internal/typeutil/map.go index 5c52363a..855ab4c9 100644 --- a/internal/typeutil/map.go +++ b/internal/typeutil/map.go @@ -4,7 +4,7 @@ // Package typeutil defines various utilities for types, such as Map, // a mapping from types.Type to interface{} values. -package typeutil // import "golang.org/x/tools/go/types/typeutil" +package typeutil import ( "bytes" @@ -253,6 +253,44 @@ func hashString(s string) uint32 { return h } +func HashSig(h Hasher, t *types.Signature) uint32 { + var hash uint32 = 9091 + if t.Variadic() { + hash *= 8863 + } + + // Use a separate hasher for types inside of the signature, where type + // parameter identity is modified to be (index, constraint). We must use a + // new memo for this hasher as type identity may be affected by this + // masking. For example, in func[T any](*T), the identity of *T depends on + // whether we are mapping the argument in isolation, or recursively as part + // of hashing the signature. + // + // We should never encounter a generic signature while hashing another + // generic signature, but defensively set sigTParams only if h.mask is + // unset. + tparams := t.TypeParams() + if h.sigTParams == nil && tparams.Len() != 0 { + h = Hasher{ + // There may be something more efficient than discarding the existing + // memo, but it would require detecting whether types are 'tainted' by + // references to type parameters. + memo: make(map[types.Type]uint32), + // Re-using ptrMap ensures that pointer identity is preserved in this + // hasher. + ptrMap: h.ptrMap, + sigTParams: tparams, + } + } + + for i := 0; i < tparams.Len(); i++ { + tparam := tparams.At(i) + hash += 7 * h.Hash(tparam.Constraint()) + } + + return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) +} + // hashFor computes the hash of t. func (h Hasher) hashFor(t types.Type) uint32 { // See Identical for rationale. @@ -286,41 +324,7 @@ func (h Hasher) hashFor(t types.Type) uint32 { return 9067 + 2*h.Hash(t.Elem()) case *types.Signature: - var hash uint32 = 9091 - if t.Variadic() { - hash *= 8863 - } - - // Use a separate hasher for types inside of the signature, where type - // parameter identity is modified to be (index, constraint). We must use a - // new memo for this hasher as type identity may be affected by this - // masking. For example, in func[T any](*T), the identity of *T depends on - // whether we are mapping the argument in isolation, or recursively as part - // of hashing the signature. - // - // We should never encounter a generic signature while hashing another - // generic signature, but defensively set sigTParams only if h.mask is - // unset. - tparams := t.TypeParams() - if h.sigTParams == nil && tparams.Len() != 0 { - h = Hasher{ - // There may be something more efficient than discarding the existing - // memo, but it would require detecting whether types are 'tainted' by - // references to type parameters. - memo: make(map[types.Type]uint32), - // Re-using ptrMap ensures that pointer identity is preserved in this - // hasher. - ptrMap: h.ptrMap, - sigTParams: tparams, - } - } - - for i := 0; i < tparams.Len(); i++ { - tparam := tparams.At(i) - hash += 7 * h.Hash(tparam.Constraint()) - } - - return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) + return HashSig(h, t) case *types.Union: return h.hashUnion(t) @@ -370,6 +374,9 @@ func (h Hasher) hashFor(t types.Type) uint32 { case *types.Tuple: return h.hashTuple(t) + + case interface{ Hash(h Hasher) uint32 }: + return t.Hash(h) } panic(fmt.Sprintf("%T: %v", t, t)) diff --git a/ssa/type_c.go b/ssa/type_c.go index ff217cb3..4dbe193d 100644 --- a/ssa/type_c.go +++ b/ssa/type_c.go @@ -18,6 +18,8 @@ package ssa import ( "go/types" + + "github.com/goplus/llgo/internal/typeutil" ) // ----------------------------------------------------------------------------- @@ -46,6 +48,10 @@ type CFuncPtr types.Signature func (t *CFuncPtr) String() string { return (*types.Signature)(t).String() } func (t *CFuncPtr) Underlying() types.Type { return (*types.Signature)(t) } +func (t *CFuncPtr) Hash(h typeutil.Hasher) uint32 { + return typeutil.HashSig(h, (*types.Signature)(t))*13 + 97 +} + // ----------------------------------------------------------------------------- // CType convert a C type into Go. From 330cb22351ef422b69c892990f25ea36ede3e8fc Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 18:35:14 +0800 Subject: [PATCH 08/10] TestErrCompileInstrOrValue --- cl/builtin_test.go | 12 ++++++++++++ cl/compile.go | 13 +++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index da94ef47..4eb7ded8 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -25,6 +25,18 @@ import ( "golang.org/x/tools/go/ssa" ) +func TestErrCompileInstrOrValue(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("compileInstrOrValue: no error?") + } + }() + ctx := &context{ + bvals: make(map[ssa.Value]llssa.Expr), + } + ctx.compileInstrOrValue(nil, &ssa.Call{}, true) +} + func TestErrAdvance(t *testing.T) { defer func() { if r := recover(); r == nil { diff --git a/cl/compile.go b/cl/compile.go index e9f7bbd3..06e077fc 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -587,14 +587,11 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } } case *ssa.Function: - panic("unreachable") - /* - fn, ftype := p.funcOf(v) - if ftype >= llgoInstrBase { - panic("can't use llgo instruction as a value") - } - return fn.Expr - */ + fn, ftype := p.funcOf(v) + if ftype >= llgoInstrBase { + panic("can't use llgo instruction as a value") + } + return fn.Expr case *ssa.Global: g := p.varOf(v) return g.Expr From 91d012d33db9c60db5823d0ab52358282f4e3d05 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 19:02:17 +0800 Subject: [PATCH 09/10] TestCvtCType --- cl/_testrt/cvar/in.go | 19 +++++++++++++++++++ cl/_testrt/cvar/out.ll | 30 ++++++++++++++++++++++++++++++ ssa/ssa_test.go | 17 +++++++++++++++++ ssa/type_c.go | 2 +- 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 cl/_testrt/cvar/in.go create mode 100644 cl/_testrt/cvar/out.ll diff --git a/cl/_testrt/cvar/in.go b/cl/_testrt/cvar/in.go new file mode 100644 index 00000000..7f99e7b0 --- /dev/null +++ b/cl/_testrt/cvar/in.go @@ -0,0 +1,19 @@ +package main + +import _ "unsafe" + +//go:linkname barX _bar_x +var barX struct { + Arr [16]int8 + Callbacks [2]func() +} + +//go:linkname barY _bar_y +var barY struct { + Arr [16]int8 +} + +func main() { + _ = barX + _ = barY +} diff --git a/cl/_testrt/cvar/out.ll b/cl/_testrt/cvar/out.ll new file mode 100644 index 00000000..0c10977a --- /dev/null +++ b/cl/_testrt/cvar/out.ll @@ -0,0 +1,30 @@ +; ModuleID = 'main' +source_filename = "main" + +@_bar_x = external global ptr +@_bar_y = external global ptr +@"main.init$guard" = global ptr null + +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 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %0 = load { [16 x i8], [2 x ptr] }, ptr @_bar_x, align 8 + %1 = load { [16 x i8] }, ptr @_bar_y, align 1 + ret void +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index ff8c21c3..3ccbe147 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -30,6 +30,23 @@ func TestMakeInterface(t *testing.T) { } */ +func TestCvtCType(t *testing.T) { + test := func(typ types.Type) { + defer func() { + if r := recover(); r == nil { + t.Log("cvtCType: no error?") + } + }() + cvtCType(typ) + } + test(types.NewInterfaceType(nil, nil)) + + a := types.NewTypeName(0, nil, "a", nil) + sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + named := types.NewNamed(a, sig, nil) + test(named) +} + func TestCFuncPtr(t *testing.T) { sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) csig := (*CFuncPtr)(sig) diff --git a/ssa/type_c.go b/ssa/type_c.go index 4dbe193d..045644fe 100644 --- a/ssa/type_c.go +++ b/ssa/type_c.go @@ -82,7 +82,7 @@ func cvtCType(typ types.Type) (types.Type, bool) { return cvtCStruct(t) case *types.Named: if _, cvt := cvtCType(t.Underlying()); cvt { - panic("todo: named type") + panic("don't define named type") } case *types.Signature: t = CFuncDecl(t) From d87ce1a124960495b443b3c589c2a6dc9fae72f0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 3 May 2024 19:05:49 +0800 Subject: [PATCH 10/10] cl: qsort --- cl/_testrt/qsort/in.go | 20 +++++++++++++ cl/_testrt/qsort/out.ll | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 cl/_testrt/qsort/in.go create mode 100644 cl/_testrt/qsort/out.ll diff --git a/cl/_testrt/qsort/in.go b/cl/_testrt/qsort/in.go new file mode 100644 index 00000000..b4c65417 --- /dev/null +++ b/cl/_testrt/qsort/in.go @@ -0,0 +1,20 @@ +package main + +import ( + "unsafe" + + "github.com/goplus/llgo/internal/runtime/c" +) + +//go:linkname qsort C.qsort +func qsort(base c.Pointer, count, elem uintptr, compar func(a, b c.Pointer) c.Int) + +func main() { + a := [...]int{100, 8, 23, 2, 7} + qsort(c.Pointer(&a[0]), 5, unsafe.Sizeof(0), func(a, b c.Pointer) c.Int { + return c.Int(*(*int)(a) - *(*int)(b)) + }) + for _, v := range a { + c.Printf(c.Str("%d\n"), v) + } +} diff --git a/cl/_testrt/qsort/out.ll b/cl/_testrt/qsort/out.ll new file mode 100644 index 00000000..fc5a788d --- /dev/null +++ b/cl/_testrt/qsort/out.ll @@ -0,0 +1,64 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 + +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 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 40) + %1 = getelementptr inbounds i64, ptr %0, i64 0 + %2 = getelementptr inbounds i64, ptr %0, i64 1 + %3 = getelementptr inbounds i64, ptr %0, i64 2 + %4 = getelementptr inbounds i64, ptr %0, i64 3 + %5 = getelementptr inbounds i64, ptr %0, i64 4 + store i64 100, ptr %1, align 4 + store i64 8, ptr %2, align 4 + store i64 23, ptr %3, align 4 + store i64 2, ptr %4, align 4 + store i64 7, ptr %5, align 4 + %6 = getelementptr inbounds i64, ptr %0, i64 0 + call void @qsort(ptr %6, i64 5, i64 8, ptr @"main.main$1") + %7 = load [5 x i64], ptr %0, align 4 + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %8 = phi i64 [ -1, %_llgo_0 ], [ %9, %_llgo_2 ] + %9 = add i64 %8, 1 + %10 = icmp slt i64 %9, 5 + br i1 %10, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %11 = getelementptr inbounds i64, ptr %0, i64 %9 + %12 = load i64, ptr %11, align 4 + %13 = call i32 (ptr, ...) @printf(ptr @0, i64 %12) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + ret void +} + +declare void @qsort(ptr, i64, i64, ptr) + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64) + +declare i32 @"main.main$1"(ptr, ptr) + +declare i32 @printf(ptr, ...)