diff --git a/ssa/expr.go b/ssa/expr.go index 5a25085b..9b69c4af 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -93,6 +93,40 @@ func phisExpr(t Type, phis []llvm.Value) Expr { // ----------------------------------------------------------------------------- +func (p Program) Zero(t Type) Expr { + var ret llvm.Value + switch u := t.raw.Type.Underlying().(type) { + case *types.Basic: + kind := u.Kind() + switch { + case kind >= types.Bool && kind <= types.Uintptr: + ret = llvm.ConstInt(p.rawType(u).ll, 0, false) + case kind == types.String: + ret = p.Zero(p.rtType("String")).impl + case kind == types.UnsafePointer: + ret = llvm.ConstPointerNull(p.tyVoidPtr()) + case kind <= types.Float64: + ret = llvm.ConstFloat(p.Float64().ll, 0) + case kind == types.Float32: + ret = llvm.ConstFloat(p.Float32().ll, 0) + default: + panic("todo") + } + case *types.Pointer: + return Expr{llvm.ConstNull(t.ll), t} + case *types.Struct: + n := u.NumFields() + flds := make([]llvm.Value, n) + for i := 0; i < n; i++ { + flds[i] = p.Zero(p.rawType(u.Field(i).Type())).impl + } + ret = llvm.ConstStruct(flds, false) + default: + log.Panicln("todo:", u) + } + return Expr{ret, t} +} + // Null returns a null constant expression. func (p Program) Null(t Type) Expr { return Expr{llvm.ConstNull(t.ll), t} @@ -125,12 +159,6 @@ func (p Program) FloatVal(v float64, t Type) Expr { return Expr{ret, t} } -func (p Program) ByteVal(v byte) Expr { - t := p.Byte() - ret := llvm.ConstInt(t.ll, uint64(v), false) - return Expr{ret, t} -} - // Val returns a constant expression. func (p Program) Val(v interface{}) Expr { switch v := v.(type) { @@ -941,21 +969,6 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { return } -// The Extract instruction yields component Index of Tuple. -// -// This is used to access the results of instructions with multiple -// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and -// IndexExpr(Map). -// -// Example printed form: -// -// t1 = extract t0 #1 -func (b Builder) Extract(x Expr, index int) (ret Expr) { - ret.Type = b.Prog.toType(x.Type.raw.Type.(*types.Tuple).At(index).Type()) - ret.impl = llvm.CreateExtractValue(b.impl, x.impl, index) - return -} - // A Builtin represents a specific use of a built-in function, e.g. len. // // Builtins are immutable values. Builtins do not have addresses. @@ -1009,7 +1022,7 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { ret.Type = prog.Void() for i, arg := range args { if ln && i > 0 { - b.InlineCall(b.Pkg.rtFunc("PrintByte"), prog.ByteVal(' ')) + b.InlineCall(b.Pkg.rtFunc("PrintByte"), prog.IntVal(' ', prog.Byte())) } var fn string typ := arg.Type @@ -1049,7 +1062,7 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { b.InlineCall(b.Pkg.rtFunc(fn), arg) } if ln { - b.InlineCall(b.Pkg.rtFunc("PrintByte"), prog.ByteVal('\n')) + b.InlineCall(b.Pkg.rtFunc("PrintByte"), prog.IntVal('\n', prog.Byte())) } return case "copy": diff --git a/ssa/interface.go b/ssa/interface.go index e497cae9..5aebb33e 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -161,11 +161,13 @@ func (b Builder) makeIntfAlloc(tinter Type, rawIntf *types.Interface, typ Type, func (b Builder) makeIntfByPtr(tinter Type, rawIntf *types.Interface, typ Type, vptr Expr) (ret Expr) { if rawIntf.Empty() { - return Expr{b.unsafeEface(b.abiType(typ.raw.Type).impl, vptr.impl), tinter} + tabi := b.abiType(typ.raw.Type) + return Expr{b.unsafeEface(tabi.impl, vptr.impl), tinter} } panic("todo") } +// TODO(xsw): remove MakeAnyIntptr, MakeAnyString func (b Builder) makeIntfByIntptr(tinter Type, rawIntf *types.Interface, typ Type, x llvm.Value) (ret Expr) { if rawIntf.Empty() { tptr := b.Prog.Uintptr() @@ -176,45 +178,7 @@ func (b Builder) makeIntfByIntptr(tinter Type, rawIntf *types.Interface, typ Typ 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) @@ -277,10 +241,89 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { fnName = "CheckI2String" } return b.InlineCall(pkg.rtFunc(fnName), x) - case vkStruct: } 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} + */ + panic("todo") + } + 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") +} // ----------------------------------------------------------------------------- @@ -293,4 +336,12 @@ func (b Builder) InterfaceData(x Expr) Expr { return Expr{ptr, b.Prog.VoidPtr()} } +func (b Builder) faceAbiType(x Expr) Expr { + if x.kind == vkIface { + panic("todo") + } + typ := llvm.CreateExtractValue(b.impl, x.impl, 0) + return Expr{typ, b.Prog.AbiTypePtr()} +} + // ----------------------------------------------------------------------------- diff --git a/ssa/package.go b/ssa/package.go index 879e38ee..f7a967e9 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -137,6 +137,7 @@ type aProgram struct { intTy Type uintTy Type f64Ty Type + f32Ty Type byteTy Type i32Ty Type u32Ty Type @@ -293,6 +294,16 @@ func (p Program) NewPackage(name, pkgPath string) Package { return ret } +// Tuple returns a tuple type. +func (p Program) Tuple(typs ...Type) Type { + n := len(typs) + els := make([]*types.Var, n) + for i, t := range typs { + els[i] = types.NewParam(token.NoPos, nil, "", t.raw.Type) + } + return p.rawType(types.NewTuple(els...)) +} + // Eface returns the empty interface type. func (p Program) Eface() Type { if p.efaceTy == nil { @@ -418,6 +429,14 @@ func (p Program) Float64() Type { return p.f64Ty } +// Float32 returns float32 type. +func (p Program) Float32() Type { + if p.f32Ty == nil { + p.f32Ty = p.rawType(types.Typ[types.Float32]) + } + return p.f32Ty +} + // Byte returns byte type. func (p Program) Byte() Type { if p.byteTy == nil { diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 2bbaf771..4ef42c9a 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -160,6 +160,22 @@ func (b Builder) Return(results ...Expr) { } } +// The Extract instruction yields component Index of Tuple. +// +// This is used to access the results of instructions with multiple +// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and +// IndexExpr(Map). +// +// Example printed form: +// +// t1 = extract t0 #1 +func (b Builder) Extract(x Expr, i int) (ret Expr) { + if debugInstr { + log.Printf("Extract %v, %d\n", x.impl, i) + } + return b.getField(x, i) +} + // Jump emits a jump instruction. func (b Builder) Jump(jmpb BasicBlock) { if b.Func != jmpb.fn { diff --git a/ssa/type.go b/ssa/type.go index 391e95da..64314d6c 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -141,9 +141,14 @@ func (p Program) Index(typ Type) Type { } func (p Program) Field(typ Type, i int) Type { - tunder := typ.raw.Type.Underlying() - tfld := tunder.(*types.Struct).Field(i).Type() - return p.rawType(tfld) + var fld *types.Var + switch t := typ.raw.Type.(type) { + case *types.Tuple: + fld = t.At(i) + default: + fld = t.Underlying().(*types.Struct).Field(i) + } + return p.rawType(fld.Type()) } func (p Program) rawType(raw types.Type) Type { @@ -218,9 +223,11 @@ func (p Program) tyInt64() llvm.Type { return p.int64Type } +/* func (p Program) toTuple(typ *types.Tuple) Type { return &aType{p.toLLVMTuple(typ), rawType{typ}, vkTuple} } +*/ func (p Program) toType(raw types.Type) Type { typ := rawType{raw} @@ -385,7 +392,7 @@ func (p Program) toNamed(raw *types.Named) Type { switch t := raw.Underlying().(type) { case *types.Struct: name := NameOf(raw) - return &aType{p.toLLVMNamedStruct(name, t), rawType{raw}, vkInvalid} + return &aType{p.toLLVMNamedStruct(name, t), rawType{raw}, vkStruct} default: return p.rawType(t) }