diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 3e9fb082..14775932 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -24,6 +24,57 @@ import ( "hash" ) +// ----------------------------------------------------------------------------- + +type Kind int + +const ( + Invalid Kind = iota + Indirect // allocate memory for the value + Pointer // store a pointer value directly in the interface value + Integer // store a integer value directly in the interface value + BitCast // store other value (need bitcast) directly in the interface value +) + +func KindOf(raw types.Type, is32Bits bool) (ret Kind) { + switch t := raw.Underlying().(type) { + case *types.Basic: + kind := t.Kind() + switch { + case types.Bool <= kind && kind <= types.Uintptr: + if is32Bits && (kind == types.Int64 || kind == types.Uint64) { + return Indirect + } + return Integer + case kind == types.Float32: + return BitCast + case kind == types.Float64 || kind == types.Complex64: + if is32Bits { + return Indirect + } + return BitCast + case kind == types.UnsafePointer: + return Pointer + } + case *types.Pointer, *types.Signature, *types.Map, *types.Chan: + return Pointer + case *types.Struct: + if t.NumFields() == 1 { + return KindOf(t.Field(0).Type(), is32Bits) + } + case *types.Interface, *types.Slice: + case *types.Array: + if t.Len() == 1 { + return KindOf(t.Elem(), is32Bits) + } + default: + panic("unkown type") + } + return Indirect +} + +// ----------------------------------------------------------------------------- + // Builder is a helper for constructing ABI types. type Builder struct { h hash.Hash @@ -95,3 +146,5 @@ func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { ret = h.Sum(b.buf[:0]) return } + +// ----------------------------------------------------------------------------- diff --git a/ssa/interface.go b/ssa/interface.go index 7b9c49dc..51a7900b 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -178,6 +178,90 @@ func (b Builder) makeIntfByIntptr(tinter Type, rawIntf *types.Interface, typ Typ panic("todo") } +func (b Builder) valFromData(t Type, data Expr) Expr { + kind := abi.KindOf(t.raw.Type, b.Prog.is32Bits) + switch kind { + case abi.Indirect: + impl := b.impl + tll := t.ll + tptr := llvm.PointerType(tll, 0) + ptr := llvm.CreatePointerCast(impl, data.impl, tptr) + return Expr{llvm.CreateLoad(impl, tll, ptr), t} + case abi.Pointer: + return Expr{data.impl, t} + case abi.Integer: + x := castUintptr(b, data.impl, b.Prog.Uintptr()) + return Expr{castInt(b, x, t), t} + case abi.BitCast: + x := castUintptr(b, data.impl, b.Prog.Uintptr()) + return Expr{llvm.CreateBitCast(b.impl, x, t.ll), t} + } + 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) Expr { + if debugInstr { + log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk) + } + tx := b.faceAbiType(x) + tabi := b.abiType(assertedTyp.raw.Type) + eq := b.BinOp(token.EQL, tx, tabi) + if commaOk { + prog := b.Prog + t := prog.Tuple(assertedTyp, prog.Bool()) + val := b.valFromData(assertedTyp, b.InterfaceData(x)) + zero := prog.Zero(assertedTyp) + valTrue := aggregateValue(b.impl, t.ll, val.impl, prog.BoolVal(true).impl) + valFalse := aggregateValue(b.impl, t.ll, zero.impl, prog.BoolVal(false).impl) + return Expr{llvm.CreateSelect(b.impl, eq.impl, valTrue, valFalse), t} + } + blks := b.Func.MakeBlocks(2) + b.If(eq, blks[0], blks[1]) + b.SetBlock(blks[1]) + b.Panic(b.Str("type assertion failed")) + b.SetBlock(blks[0]) + return b.valFromData(assertedTyp, b.InterfaceData(x)) +} + /* func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { if debugInstr { @@ -246,82 +330,6 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { } */ -// 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) Expr { - if debugInstr { - log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk) - } - tx := b.faceAbiType(x) - tabi := b.abiType(assertedTyp.raw.Type) - eq := b.BinOp(token.EQL, tx, tabi) - if commaOk { - prog := b.Prog - t := prog.Tuple(assertedTyp, prog.Bool()) - val := b.valFromData(assertedTyp, b.InterfaceData(x)) - zero := prog.Zero(assertedTyp) - valTrue := aggregateValue(b.impl, t.ll, val.impl, prog.BoolVal(true).impl) - valFalse := aggregateValue(b.impl, t.ll, zero.impl, prog.BoolVal(false).impl) - return Expr{llvm.CreateSelect(b.impl, eq.impl, valTrue, valFalse), t} - } - blks := b.Func.MakeBlocks(2) - b.If(eq, blks[0], blks[1]) - b.SetBlock(blks[1]) - b.Panic(b.Str("type assertion failed")) - b.SetBlock(blks[0]) - return b.valFromData(assertedTyp, b.InterfaceData(x)) -} - -func (b Builder) valFromData(t Type, data Expr) Expr { - switch u := t.raw.Type.Underlying().(type) { - case *types.Basic: - kind := u.Kind() - switch { - case kind >= types.Bool && kind <= types.Uintptr: - panic("todo") - } - } - _ = data - panic("todo") -} - // ----------------------------------------------------------------------------- // InterfaceData returns the data pointer of an interface.