llgo/ssa: rtType, rtFunc

This commit is contained in:
xushiwei
2024-04-27 17:39:25 +08:00
parent f1761c0c9c
commit 6a02c3ac4c
14 changed files with 259 additions and 19 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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
}
// -----------------------------------------------------------------------------

View File

@@ -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)