llgo/ssa: {datstruct, interface}.go
This commit is contained in:
478
ssa/expr.go
478
ssa/expr.go
@@ -24,7 +24,6 @@ import (
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llgo/internal/abi"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
@@ -423,6 +422,16 @@ func checkExpr(v Expr, t types.Type, b Builder) Expr {
|
||||
return v
|
||||
}
|
||||
|
||||
func needsNegativeCheck(x Expr) bool {
|
||||
if x.kind == vkSigned {
|
||||
if rv := x.impl.IsAConstantInt(); !rv.IsNil() && rv.SExtValue() >= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func llvmParamsEx(data Expr, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
|
||||
if data.IsNil() {
|
||||
return llvmParams(0, vals, params, b)
|
||||
@@ -619,323 +628,6 @@ func (b Builder) MakeClosure(fn Expr, bindings []Expr) Expr {
|
||||
return b.aggregateValue(prog.Closure(tfn), fn.impl, data)
|
||||
}
|
||||
|
||||
// The FieldAddr instruction yields the address of Field of *struct X.
|
||||
//
|
||||
// The field is identified by its index within the field list of the
|
||||
// struct type of X.
|
||||
//
|
||||
// Dynamically, this instruction panics if X evaluates to a nil
|
||||
// pointer.
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Pointer.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = &t0.name [#1]
|
||||
func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||
}
|
||||
prog := b.Prog
|
||||
tstruc := prog.Elem(x.Type)
|
||||
telem := prog.Field(tstruc, idx)
|
||||
pt := prog.Pointer(telem)
|
||||
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
||||
}
|
||||
|
||||
// The Field instruction yields the value of Field of struct X.
|
||||
func (b Builder) Field(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Field %v, %d\n", x.impl, idx)
|
||||
}
|
||||
return b.getField(x, idx)
|
||||
}
|
||||
|
||||
func (b Builder) getField(x Expr, idx int) Expr {
|
||||
tfld := b.Prog.Field(x.Type, idx)
|
||||
fld := llvm.CreateExtractValue(b.impl, x.impl, idx)
|
||||
return Expr{fld, tfld}
|
||||
}
|
||||
|
||||
// StringData returns the data pointer of a string.
|
||||
func (b Builder) StringData(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("StringData %v\n", x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
|
||||
return Expr{ptr, prog.CStr()}
|
||||
}
|
||||
|
||||
// StringLen returns the length of a string.
|
||||
func (b Builder) StringLen(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("StringLen %v\n", x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
|
||||
return Expr{ptr, prog.Int()}
|
||||
}
|
||||
|
||||
// SliceData returns the data pointer of a slice.
|
||||
func (b Builder) SliceData(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceData %v\n", x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
|
||||
return Expr{ptr, prog.CStr()}
|
||||
}
|
||||
|
||||
// SliceLen returns the length of a slice.
|
||||
func (b Builder) SliceLen(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceLen %v\n", x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
|
||||
return Expr{ptr, prog.Int()}
|
||||
}
|
||||
|
||||
// SliceCap returns the length of a slice cap.
|
||||
func (b Builder) SliceCap(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceCap %v\n", x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 2)
|
||||
return Expr{ptr, prog.Int()}
|
||||
}
|
||||
|
||||
// The IndexAddr instruction yields the address of the element at
|
||||
// index `idx` of collection `x`. `idx` is an integer expression.
|
||||
//
|
||||
// The elements of maps and strings are not addressable; use Lookup (map),
|
||||
// Index (string), or MapUpdate instead.
|
||||
//
|
||||
// Dynamically, this instruction panics if `x` evaluates to a nil *array
|
||||
// pointer.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = &t0[t1]
|
||||
func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
|
||||
}
|
||||
idx = b.checkIndex(idx)
|
||||
prog := b.Prog
|
||||
telem := prog.Index(x.Type)
|
||||
pt := prog.Pointer(telem)
|
||||
switch x.raw.Type.Underlying().(type) {
|
||||
case *types.Slice:
|
||||
ptr := b.SliceData(x)
|
||||
indices := []llvm.Value{idx.impl}
|
||||
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
|
||||
}
|
||||
// case *types.Pointer:
|
||||
indices := []llvm.Value{idx.impl}
|
||||
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
|
||||
}
|
||||
|
||||
func needsNegativeCheck(x Expr) bool {
|
||||
if x.kind == vkSigned {
|
||||
if rv := x.impl.IsAConstantInt(); !rv.IsNil() && rv.SExtValue() >= 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check index >= 0 and size to uint
|
||||
func (b Builder) checkIndex(idx Expr) Expr {
|
||||
if needsNegativeCheck(idx) {
|
||||
check := Expr{b.impl.CreateICmp(llvm.IntSLT, idx.impl, llvm.ConstInt(idx.ll, 0, false), ""), b.Prog.Bool()}
|
||||
b.InlineCall(b.Func.Pkg.rtFunc("AssertIndexRange"), check)
|
||||
}
|
||||
typ := b.Prog.Uint()
|
||||
if b.Prog.SizeOf(idx.Type) < b.Prog.SizeOf(typ) {
|
||||
idx.Type = typ
|
||||
idx.impl = castUintptr(b, idx.impl, typ)
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
// The Index instruction yields element Index of collection X, an array,
|
||||
// string or type parameter containing an array, a string, a pointer to an,
|
||||
// array or a slice.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = t0[t1]
|
||||
func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Index %v, %v\n", x.impl, idx.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
var telem Type
|
||||
var ptr Expr
|
||||
switch t := x.raw.Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if t.Kind() != types.String {
|
||||
panic(fmt.Errorf("invalid operation: cannot index %v", t))
|
||||
}
|
||||
telem = prog.rawType(types.Typ[types.Byte])
|
||||
ptr = b.StringData(x)
|
||||
case *types.Array:
|
||||
telem = prog.Index(x.Type)
|
||||
if addr != nil {
|
||||
ptr = addr(x)
|
||||
} else {
|
||||
size := b.SizeOf(telem, t.Len())
|
||||
ptr = b.Alloca(size)
|
||||
b.Store(ptr, x)
|
||||
}
|
||||
}
|
||||
idx = b.checkIndex(idx)
|
||||
pt := prog.Pointer(telem)
|
||||
indices := []llvm.Value{idx.impl}
|
||||
buf := Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
|
||||
return b.Load(buf)
|
||||
}
|
||||
|
||||
// The Lookup instruction yields element Index of collection map X.
|
||||
// Index is the appropriate key type.
|
||||
//
|
||||
// If CommaOk, the result is a 2-tuple of the value above and a
|
||||
// boolean indicating the result of a map membership test for the key.
|
||||
// The components of the tuple are accessed using Extract.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = t0[t1]
|
||||
// t5 = t3[t4],ok
|
||||
func (b Builder) Lookup(x, key Expr, commaOk bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Lookup %v, %v, %v\n", x.impl, key.impl, commaOk)
|
||||
}
|
||||
// TODO(xsw)
|
||||
// panic("todo")
|
||||
return
|
||||
}
|
||||
|
||||
// The Slice instruction yields a slice of an existing string, slice
|
||||
// or *array X between optional integer bounds Low and High.
|
||||
//
|
||||
// Dynamically, this instruction panics if X evaluates to a nil *array
|
||||
// pointer.
|
||||
//
|
||||
// Type() returns string if the type of X was string, otherwise a
|
||||
// *types.Slice with the same element type as X.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = slice t0[1:]
|
||||
func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
pkg := b.Func.Pkg
|
||||
var nCap Expr
|
||||
var nEltSize Expr
|
||||
var base Expr
|
||||
if low.IsNil() {
|
||||
low = prog.IntVal(0, prog.Int())
|
||||
}
|
||||
switch t := x.raw.Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if t.Kind() != types.String {
|
||||
panic(fmt.Errorf("invalid operation: cannot slice %v", t))
|
||||
}
|
||||
if high.IsNil() {
|
||||
high = b.StringLen(x)
|
||||
}
|
||||
ret.Type = x.Type
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl
|
||||
return
|
||||
case *types.Slice:
|
||||
nEltSize = b.SizeOf(prog.Index(x.Type))
|
||||
nCap = b.SliceCap(x)
|
||||
if high.IsNil() {
|
||||
high = b.SliceCap(x)
|
||||
}
|
||||
ret.Type = x.Type
|
||||
base = b.SliceData(x)
|
||||
case *types.Pointer:
|
||||
telem := t.Elem()
|
||||
switch te := telem.Underlying().(type) {
|
||||
case *types.Array:
|
||||
elem := prog.rawType(te.Elem())
|
||||
ret.Type = prog.Slice(elem)
|
||||
nEltSize = b.SizeOf(elem)
|
||||
nCap = prog.IntVal(uint64(te.Len()), prog.Int())
|
||||
if high.IsNil() {
|
||||
high = nCap
|
||||
}
|
||||
base = x
|
||||
}
|
||||
}
|
||||
if max.IsNil() {
|
||||
max = nCap
|
||||
}
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The MakeMap instruction creates a new hash-table-based map object
|
||||
// and yields a value of kind map.
|
||||
//
|
||||
// t is a (possibly named) *types.Map.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make map[string]int t0
|
||||
// t1 = make StringIntMap t0
|
||||
func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl)
|
||||
}
|
||||
pkg := b.Func.Pkg
|
||||
ret.Type = t
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl
|
||||
// TODO(xsw): nReserve
|
||||
return
|
||||
}
|
||||
|
||||
// The MakeSlice instruction yields a slice of length Len backed by a
|
||||
// newly allocated array of length Cap.
|
||||
//
|
||||
// Both Len and Cap must be non-nil Values of integer type.
|
||||
//
|
||||
// (Alloc(types.Array) followed by Slice will not suffice because
|
||||
// Alloc can only create arrays of constant length.)
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Slice.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make []string 1:int t0
|
||||
// t1 = make StringSlice 1:int t0
|
||||
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
|
||||
}
|
||||
pkg := b.Func.Pkg
|
||||
if cap.IsNil() {
|
||||
cap = len
|
||||
}
|
||||
elemSize := b.SizeOf(b.Prog.Index(t))
|
||||
size := b.BinOp(token.MUL, cap, elemSize)
|
||||
ptr := b.InlineCall(pkg.rtFunc("AllocZ"), size)
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), ptr, len, cap).impl
|
||||
ret.Type = t
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Alloc instruction reserves space for a variable of the given type,
|
||||
@@ -1164,156 +856,6 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
|
||||
return llvm.CreateIntToPtr(b, x, t)
|
||||
}
|
||||
|
||||
// MakeInterface constructs an instance of an interface type from a
|
||||
// value of a concrete type.
|
||||
//
|
||||
// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
|
||||
// of X, and Program.MethodValue(m) to find the implementation of a method.
|
||||
//
|
||||
// To construct the zero value of an interface type T, use:
|
||||
//
|
||||
// NewConst(constant.MakeNil(), T, pos)
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make interface{} <- int (42:int)
|
||||
// t2 = make Stringer <- t0
|
||||
func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
|
||||
raw := tinter.raw.Type
|
||||
if debugInstr {
|
||||
log.Printf("MakeInterface %v, %v\n", raw, x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
pkg := b.Func.Pkg
|
||||
switch tx := x.raw.Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
kind := tx.Kind()
|
||||
switch {
|
||||
case kind >= types.Bool && kind <= types.Uintptr:
|
||||
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
|
||||
tptr := prog.Uintptr()
|
||||
vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr}
|
||||
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
|
||||
case kind == types.Float32:
|
||||
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
|
||||
tptr := prog.Uintptr()
|
||||
i32 := b.impl.CreateBitCast(x.impl, prog.tyInt32(), "")
|
||||
vptr := Expr{llvm.CreateIntCast(b.impl, i32, tptr.ll), tptr}
|
||||
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
|
||||
case kind == types.Float64:
|
||||
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
|
||||
tptr := prog.Uintptr()
|
||||
vptr := Expr{b.impl.CreateBitCast(x.impl, tptr.ll, ""), tptr}
|
||||
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
|
||||
case kind == types.String:
|
||||
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter}
|
||||
}
|
||||
}
|
||||
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.raw.Type, commaOk)
|
||||
}
|
||||
switch assertedTyp.kind {
|
||||
case vkSigned, vkUnsigned, vkFloat, vkBool:
|
||||
pkg := b.Func.Pkg
|
||||
fnName := "I2Int"
|
||||
if commaOk {
|
||||
fnName = "CheckI2Int"
|
||||
}
|
||||
fn := pkg.rtFunc(fnName)
|
||||
var kind types.BasicKind
|
||||
switch t := assertedTyp.raw.Type.(type) {
|
||||
case *types.Basic:
|
||||
kind = t.Kind()
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
|
||||
ret = b.InlineCall(fn, x, typ)
|
||||
if kind != types.Uintptr {
|
||||
conv := func(v llvm.Value) llvm.Value {
|
||||
switch kind {
|
||||
case types.Float32:
|
||||
v = castInt(b, v, b.Prog.Int32())
|
||||
v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
|
||||
case types.Float64:
|
||||
v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
|
||||
default:
|
||||
v = castInt(b, v, assertedTyp)
|
||||
}
|
||||
return v
|
||||
}
|
||||
if !commaOk {
|
||||
ret.Type = assertedTyp
|
||||
ret.impl = conv(ret.impl)
|
||||
} else {
|
||||
ret.Type = b.Prog.toTuple(
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", assertedTyp.RawType()),
|
||||
ret.Type.RawType().(*types.Tuple).At(1),
|
||||
),
|
||||
)
|
||||
val0 := conv(b.impl.CreateExtractValue(ret.impl, 0, ""))
|
||||
val1 := b.impl.CreateExtractValue(ret.impl, 1, "")
|
||||
ret.impl = llvm.ConstStruct([]llvm.Value{val0, val1}, false)
|
||||
}
|
||||
}
|
||||
return
|
||||
case vkString:
|
||||
pkg := b.Func.Pkg
|
||||
fnName := "I2String"
|
||||
if commaOk {
|
||||
fnName = "CheckI2String"
|
||||
}
|
||||
fn := pkg.rtFunc(fnName)
|
||||
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(abi.String)))
|
||||
return b.InlineCall(fn, x, typ)
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// TODO(xsw): make inline call
|
||||
|
||||
Reference in New Issue
Block a user