diff --git a/cl/_testcgo/any/in.go b/cl/_testcgo/any/in.go new file mode 100644 index 00000000..0c75725e --- /dev/null +++ b/cl/_testcgo/any/in.go @@ -0,0 +1,16 @@ +package main + +import _ "unsafe" + +func incVal(a any) int { + return a.(int) + 1 +} + +//go:linkname printf C.printf +func printf(format *int8, __llgo_va_list ...any) + +var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0} + +func main() { + printf(&format[0], incVal(100)) +} diff --git a/cl/_testcgo/any/out.ll b/cl/_testcgo/any/out.ll new file mode 100644 index 00000000..e69de29b diff --git a/cl/compile.go b/cl/compile.go index 5a3a602d..58aa435a 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -329,6 +329,9 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l return } panic("todo") + case *ssa.TypeAssert: + x := p.compileValue(b, v.X) + ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk) default: panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) } diff --git a/cl/compile_test.go b/cl/compile_test.go index 671f8cba..249d8274 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -28,7 +28,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestcgo(t *testing.T) { - cltest.FromDir(t, "", "./_testcgo", true) + cltest.FromDir(t, "any", "./_testcgo", true) } func TestFromTestdata(t *testing.T) { diff --git a/internal/build/build.go b/internal/build/build.go index 20bc0bf2..0c97023f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -19,6 +19,7 @@ package build import ( "fmt" "go/token" + "go/types" "log" "os" "os/exec" @@ -112,6 +113,11 @@ func Do(args []string, conf *Config) { } prog := llssa.NewProgram(nil) + prog.SetRuntime(func() *types.Package { + rt, err := packages.Load(cfg, "github.com/goplus/llgo/internal/runtime") + check(err) + return rt[0].Types + }) mode := conf.Mode if mode == ModeBuild && len(initial) == 1 { mode = ModeInstall diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 9eca9f8d..203ed23e 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -17,6 +17,7 @@ package llgen import ( + "go/types" "os" "github.com/goplus/llgo/cl" @@ -48,6 +49,12 @@ func GenFromFile(inFile string) string { ssaPkg.Build() prog := llssa.NewProgram(nil) + prog.SetRuntime(func() *types.Package { + rt, err := packages.Load(cfg, "github.com/goplus/llgo/internal/runtime") + check(err) + return rt[0].Types + }) + ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax) check(err) diff --git a/internal/runtime/slice.go b/internal/runtime/slice.go new file mode 100644 index 00000000..21caa126 --- /dev/null +++ b/internal/runtime/slice.go @@ -0,0 +1,15 @@ +// Copyright 2009 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 runtime + +import ( + "unsafe" +) + +type slice struct { + array unsafe.Pointer + len int + cap int +} diff --git a/internal/runtime/z_iface.go b/internal/runtime/z_iface.go index 87689b2b..b3ae4d05 100644 --- a/internal/runtime/z_iface.go +++ b/internal/runtime/z_iface.go @@ -26,8 +26,6 @@ type Interface = iface type InterfaceType = abi.InterfaceType -type Type = abi.Type - var ( TyAny = &InterfaceType{} ) diff --git a/internal/runtime/z_slice.go b/internal/runtime/z_slice.go new file mode 100644 index 00000000..b6d5cbf5 --- /dev/null +++ b/internal/runtime/z_slice.go @@ -0,0 +1,23 @@ +/* + * 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 + +type Slice = slice + +func MakeEmptySlice() Slice { + return Slice{nil, 0, 0} +} diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go new file mode 100644 index 00000000..41b4f53d --- /dev/null +++ b/internal/runtime/z_type.go @@ -0,0 +1,93 @@ +/* + * 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 ( + "go/types" + "unsafe" + + "github.com/goplus/llgo/internal/abi" +) + +type Type = abi.Type + +func I2Int(v Interface, t *Type) int64 { + if v.tab._type == t { + return int64(uintptr(v.data)) + } + panic("I2Int: type mismatch") +} + +func CheckI2Int(v Interface, t *Type) (int64, bool) { + if v.tab._type == t { + return int64(uintptr(v.data)), true + } + return 0, false +} + +func Basic(kind types.BasicKind) *Type { + return basicTypes[kind] +} + +var ( + basicTypes = [...]*Type{ + abi.Bool: basicType(abi.Bool), + abi.Int: basicType(abi.Int), + abi.Int8: basicType(abi.Int8), + abi.Int16: basicType(abi.Int16), + abi.Int32: basicType(abi.Int32), + abi.Int64: basicType(abi.Int64), + abi.Uint: basicType(abi.Uint), + abi.Uint8: basicType(abi.Uint8), + abi.Uint16: basicType(abi.Uint16), + abi.Uint32: basicType(abi.Uint32), + abi.Uint64: basicType(abi.Uint64), + abi.Uintptr: basicType(abi.Uintptr), + abi.Float32: basicType(abi.Float32), + abi.Float64: basicType(abi.Float64), + abi.Complex64: basicType(abi.Complex64), + abi.Complex128: basicType(abi.Complex128), + } +) + +var ( + sizeBasicTypes = [...]uintptr{ + abi.Bool: unsafe.Sizeof(false), + abi.Int: unsafe.Sizeof(0), + abi.Int8: 1, + abi.Int16: 2, + abi.Int32: 4, + abi.Int64: 8, + abi.Uint: unsafe.Sizeof(uint(0)), + abi.Uint8: 1, + abi.Uint16: 2, + abi.Uint32: 4, + abi.Uint64: 8, + abi.Uintptr: unsafe.Sizeof(uintptr(0)), + abi.Float32: 4, + abi.Float64: 8, + abi.Complex64: 8, + abi.Complex128: 16, + } +) + +func basicType(kind abi.Kind) *Type { + return &abi.Type{ + Size_: sizeBasicTypes[kind], + Kind_: uint8(kind), + } +} diff --git a/ssa/decl.go b/ssa/decl.go index 18760708..942a1623 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -106,6 +106,7 @@ func (g Global) Init(v Expr) { // respectively, and is nil in the generic method. type aFunction struct { Expr + pkg Package prog Program blks []BasicBlock @@ -116,9 +117,9 @@ type aFunction struct { // Function represents a function or method. type Function = *aFunction -func newFunction(fn llvm.Value, t Type, prog Program) Function { +func newFunction(fn llvm.Value, t Type, pkg Package, prog Program) Function { params, hasVArg := newParams(t, prog) - return &aFunction{Expr{fn, t}, prog, nil, params, hasVArg} + return &aFunction{Expr{fn, t}, pkg, prog, nil, params, hasVArg} } func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { diff --git a/ssa/expr.go b/ssa/expr.go index dca906b9..e8aaa3ad 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -407,8 +407,76 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { panic("todo") } +// The TypeAssert instruction tests whether interface value X has type +// AssertedType. +// +// If !CommaOk, on success it returns v, the result of the conversion +// (defined below); on failure it panics. +// +// If CommaOk: on success it returns a pair (v, true) where v is the +// result of the conversion; on failure it returns (z, false) where z +// is AssertedType's zero value. The components of the pair must be +// accessed using the Extract instruction. +// +// If Underlying: tests whether interface value X has the underlying +// type AssertedType. +// +// If AssertedType is a concrete type, TypeAssert checks whether the +// dynamic type in interface X is equal to it, and if so, the result +// of the conversion is a copy of the value in the interface. +// +// If AssertedType is an interface, TypeAssert checks whether the +// dynamic type of the interface is assignable to it, and if so, the +// result of the conversion is a copy of the interface value X. +// If AssertedType is a superinterface of X.Type(), the operation will +// fail iff the operand is nil. (Contrast with ChangeInterface, which +// performs no nil-check.) +// +// Type() reflects the actual type of the result, possibly a +// 2-types.Tuple; AssertedType is the asserted type. +// +// Depending on the TypeAssert's purpose, Pos may return: +// - the ast.CallExpr.Lparen of an explicit T(e) conversion; +// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation; +// - the ast.CaseClause.Case of a case of a type-switch statement; +// - the Ident(m).NamePos of an interface method value i.m +// (for which TypeAssert may be used to effect the nil check). +// +// Example printed form: +// +// t1 = typeassert t0.(int) +// t3 = typeassert,ok t2.(T) +func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { + if debugInstr { + log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk) + } + switch assertedTyp.kind { + case vkSigned, vkUnsigned: + pkg := b.fn.pkg + fnName := "I2Int" + if commaOk { + fnName = "CheckI2Int" + } + fn := pkg.rtFunc(fnName) + var kind types.BasicKind + switch t := assertedTyp.t.(type) { + case *types.Basic: + kind = t.Kind() + default: + panic("todo") + } + typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind))) + return b.InlineCall(fn, x, typ) + } + panic("todo") +} + // ----------------------------------------------------------------------------- +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 yields the function result if there is exactly diff --git a/ssa/package.go b/ssa/package.go index 85b7c9d2..12bc7df4 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -90,16 +90,12 @@ func Initialize(flags InitFlags) { // ----------------------------------------------------------------------------- -type Runtime interface { - Runtime() *types.Package -} - type aProgram struct { ctx llvm.Context typs typeutil.Map rt *types.Scope - rtget Runtime + rtget func() *types.Package target *Target td llvm.TargetData @@ -136,18 +132,22 @@ func NewProgram(target *Target) Program { return &aProgram{ctx: ctx, target: target, td: td} } -// SetRuntime sets the runtime package. -func (p Program) SetRuntime(runtime Runtime) { +// SetRuntime sets the runtime. +func (p Program) SetRuntime(runtime func() *types.Package) { p.rtget = runtime } func (p Program) runtime() *types.Scope { if p.rt == nil { - p.rt = p.rtget.Runtime().Scope() + p.rt = p.rtget().Scope() } return p.rt } +func (p Program) rtType(name string) *types.Named { + return p.runtime().Lookup(name).Type().(*types.Named) +} + // NewPackage creates a new package. func (p Program) NewPackage(name, pkgPath string) Package { mod := p.ctx.NewModule(pkgPath) @@ -230,11 +230,16 @@ func (p Package) NewVar(name string, typ types.Type) Global { return ret } +// VarOf returns a global variable by name. +func (p Package) VarOf(name string) Global { + return p.vars[name] +} + // NewFunc creates a new function. func (p Package) NewFunc(name string, sig *types.Signature) Function { t := p.prog.llvmSignature(sig) fn := llvm.AddFunction(p.mod, name, t.ll) - ret := newFunction(fn, t, p.prog) + ret := newFunction(fn, t, p, p.prog) p.fns[name] = ret return ret } @@ -244,9 +249,14 @@ func (p Package) FuncOf(name string) Function { return p.fns[name] } -// VarOf returns a global variable by name. -func (p Package) VarOf(name string) Global { - return p.vars[name] +func (p Package) rtFunc(fnName string) Expr { + fn := p.prog.runtime().Lookup(fnName).(*types.Func) + name := FullName(fn.Pkg(), fnName) + v, ok := p.fns[name] + if !ok { + v = p.NewFunc(name, fn.Type().(*types.Signature)) + } + return v.Expr } // ----------------------------------------------------------------------------- diff --git a/ssa/type.go b/ssa/type.go index 21e438c9..6e5030f4 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -242,9 +242,9 @@ func (p Program) toLLVMType(typ types.Type) Type { elem := p.Type(t.Elem()) return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid} case *types.Interface: - tyIface := p.runtime().Lookup("Interface").(*types.TypeName).Type().(*types.Named) - return p.toLLVMNamed(tyIface) + return p.toLLVMNamed(p.rtType("Interface")) case *types.Slice: + return p.toLLVMNamed(p.rtType("Slice")) case *types.Map: case *types.Struct: return p.toLLVMStruct(t)