llgo/ssa: rtType, rtFunc
This commit is contained in:
16
cl/_testcgo/any/in.go
Normal file
16
cl/_testcgo/any/in.go
Normal file
@@ -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))
|
||||||
|
}
|
||||||
0
cl/_testcgo/any/out.ll
Normal file
0
cl/_testcgo/any/out.ll
Normal file
@@ -329,6 +329,9 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
panic("todo")
|
panic("todo")
|
||||||
|
case *ssa.TypeAssert:
|
||||||
|
x := p.compileValue(b, v.X)
|
||||||
|
ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
|
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func testCompile(t *testing.T, src, expected string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTestcgo(t *testing.T) {
|
func TestFromTestcgo(t *testing.T) {
|
||||||
cltest.FromDir(t, "", "./_testcgo", true)
|
cltest.FromDir(t, "any", "./_testcgo", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromTestdata(t *testing.T) {
|
func TestFromTestdata(t *testing.T) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package build
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"go/types"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -112,6 +113,11 @@ func Do(args []string, conf *Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prog := llssa.NewProgram(nil)
|
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
|
mode := conf.Mode
|
||||||
if mode == ModeBuild && len(initial) == 1 {
|
if mode == ModeBuild && len(initial) == 1 {
|
||||||
mode = ModeInstall
|
mode = ModeInstall
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package llgen
|
package llgen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/goplus/llgo/cl"
|
"github.com/goplus/llgo/cl"
|
||||||
@@ -48,6 +49,12 @@ func GenFromFile(inFile string) string {
|
|||||||
ssaPkg.Build()
|
ssaPkg.Build()
|
||||||
|
|
||||||
prog := llssa.NewProgram(nil)
|
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)
|
ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
|
||||||
check(err)
|
check(err)
|
||||||
|
|
||||||
|
|||||||
15
internal/runtime/slice.go
Normal file
15
internal/runtime/slice.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -26,8 +26,6 @@ type Interface = iface
|
|||||||
|
|
||||||
type InterfaceType = abi.InterfaceType
|
type InterfaceType = abi.InterfaceType
|
||||||
|
|
||||||
type Type = abi.Type
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TyAny = &InterfaceType{}
|
TyAny = &InterfaceType{}
|
||||||
)
|
)
|
||||||
|
|||||||
23
internal/runtime/z_slice.go
Normal file
23
internal/runtime/z_slice.go
Normal file
@@ -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}
|
||||||
|
}
|
||||||
93
internal/runtime/z_type.go
Normal file
93
internal/runtime/z_type.go
Normal file
@@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,7 @@ func (g Global) Init(v Expr) {
|
|||||||
// respectively, and is nil in the generic method.
|
// respectively, and is nil in the generic method.
|
||||||
type aFunction struct {
|
type aFunction struct {
|
||||||
Expr
|
Expr
|
||||||
|
pkg Package
|
||||||
prog Program
|
prog Program
|
||||||
blks []BasicBlock
|
blks []BasicBlock
|
||||||
|
|
||||||
@@ -116,9 +117,9 @@ type aFunction struct {
|
|||||||
// Function represents a function or method.
|
// Function represents a function or method.
|
||||||
type Function = *aFunction
|
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)
|
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) {
|
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
|
||||||
|
|||||||
68
ssa/expr.go
68
ssa/expr.go
@@ -407,8 +407,76 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
|||||||
panic("todo")
|
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 represents a function or method call.
|
||||||
//
|
//
|
||||||
// The Call instruction yields the function result if there is exactly
|
// The Call instruction yields the function result if there is exactly
|
||||||
|
|||||||
@@ -90,16 +90,12 @@ func Initialize(flags InitFlags) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Runtime interface {
|
|
||||||
Runtime() *types.Package
|
|
||||||
}
|
|
||||||
|
|
||||||
type aProgram struct {
|
type aProgram struct {
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
typs typeutil.Map
|
typs typeutil.Map
|
||||||
|
|
||||||
rt *types.Scope
|
rt *types.Scope
|
||||||
rtget Runtime
|
rtget func() *types.Package
|
||||||
|
|
||||||
target *Target
|
target *Target
|
||||||
td llvm.TargetData
|
td llvm.TargetData
|
||||||
@@ -136,18 +132,22 @@ func NewProgram(target *Target) Program {
|
|||||||
return &aProgram{ctx: ctx, target: target, td: td}
|
return &aProgram{ctx: ctx, target: target, td: td}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRuntime sets the runtime package.
|
// SetRuntime sets the runtime.
|
||||||
func (p Program) SetRuntime(runtime Runtime) {
|
func (p Program) SetRuntime(runtime func() *types.Package) {
|
||||||
p.rtget = runtime
|
p.rtget = runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Program) runtime() *types.Scope {
|
func (p Program) runtime() *types.Scope {
|
||||||
if p.rt == nil {
|
if p.rt == nil {
|
||||||
p.rt = p.rtget.Runtime().Scope()
|
p.rt = p.rtget().Scope()
|
||||||
}
|
}
|
||||||
return p.rt
|
return p.rt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Program) rtType(name string) *types.Named {
|
||||||
|
return p.runtime().Lookup(name).Type().(*types.Named)
|
||||||
|
}
|
||||||
|
|
||||||
// NewPackage creates a new package.
|
// NewPackage creates a new package.
|
||||||
func (p Program) NewPackage(name, pkgPath string) Package {
|
func (p Program) NewPackage(name, pkgPath string) Package {
|
||||||
mod := p.ctx.NewModule(pkgPath)
|
mod := p.ctx.NewModule(pkgPath)
|
||||||
@@ -230,11 +230,16 @@ func (p Package) NewVar(name string, typ types.Type) Global {
|
|||||||
return ret
|
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.
|
// NewFunc creates a new function.
|
||||||
func (p Package) NewFunc(name string, sig *types.Signature) Function {
|
func (p Package) NewFunc(name string, sig *types.Signature) Function {
|
||||||
t := p.prog.llvmSignature(sig)
|
t := p.prog.llvmSignature(sig)
|
||||||
fn := llvm.AddFunction(p.mod, name, t.ll)
|
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
|
p.fns[name] = ret
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -244,9 +249,14 @@ func (p Package) FuncOf(name string) Function {
|
|||||||
return p.fns[name]
|
return p.fns[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// VarOf returns a global variable by name.
|
func (p Package) rtFunc(fnName string) Expr {
|
||||||
func (p Package) VarOf(name string) Global {
|
fn := p.prog.runtime().Lookup(fnName).(*types.Func)
|
||||||
return p.vars[name]
|
name := FullName(fn.Pkg(), fnName)
|
||||||
|
v, ok := p.fns[name]
|
||||||
|
if !ok {
|
||||||
|
v = p.NewFunc(name, fn.Type().(*types.Signature))
|
||||||
|
}
|
||||||
|
return v.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -242,9 +242,9 @@ func (p Program) toLLVMType(typ types.Type) Type {
|
|||||||
elem := p.Type(t.Elem())
|
elem := p.Type(t.Elem())
|
||||||
return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid}
|
return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid}
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
tyIface := p.runtime().Lookup("Interface").(*types.TypeName).Type().(*types.Named)
|
return p.toLLVMNamed(p.rtType("Interface"))
|
||||||
return p.toLLVMNamed(tyIface)
|
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
|
return p.toLLVMNamed(p.rtType("Slice"))
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
return p.toLLVMStruct(t)
|
return p.toLLVMStruct(t)
|
||||||
|
|||||||
Reference in New Issue
Block a user