diff --git a/cl/_testcgo/any/out.ll b/cl/_testcgo/any/out.ll index e69de29b..132c18d4 100644 --- a/cl/_testcgo/any/out.ll +++ b/cl/_testcgo/any/out.ll @@ -0,0 +1,55 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } + +@main.format = global ptr null +@"main.init$guard" = global ptr null + +define i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %2 = call i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) + %3 = add i64 %2, 1 + ret i64 %3 +} + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + store i8 72, ptr @main.format, align 1 + store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1 + store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1 + store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1 + store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1 + store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1 + store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1 + store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + %0 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(i64 100) + %1 = call i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) + call void (ptr, ...) @printf(ptr @main.format, i64 %1) + ret void +} + +declare void @printf(ptr, ...) + +declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64) + +declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, i64) diff --git a/cl/compile.go b/cl/compile.go index 58aa435a..4f602c2c 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -324,11 +324,12 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l } ret = b.Alloc(p.prog.Type(t), v.Heap) case *ssa.MakeInterface: + const ( + delayExpr = true // varargs: don't need to convert an expr to any + ) t := v.Type() - if isAny(t) { // varargs: don't need to convert an expr to any - return - } - panic("todo") + x := p.compileValue(b, v.X) + ret = b.MakeInterface(t, x, delayExpr) case *ssa.TypeAssert: x := p.compileValue(b, v.X) ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk) @@ -417,6 +418,9 @@ func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) [] switch v := v.(type) { case *ssa.Slice: // varargs: this is a varargs slice if args, ok := p.isVArgs(v.X); ok { + for i, arg := range args { + args[i] = arg.Do(true) + } return append(ret, args...) } case *ssa.Const: @@ -431,7 +435,7 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) n := len(vals) - hasVArg ret := make([]llssa.Expr, n) for i := 0; i < n; i++ { - ret[i] = p.compileValue(b, vals[i]) + ret[i] = p.compileValue(b, vals[i]).Do(false) } if hasVArg > 0 { ret = p.compileVArg(ret, b, vals[n]) diff --git a/cl/compile_test.go b/cl/compile_test.go index 249d8274..671f8cba 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -28,7 +28,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestcgo(t *testing.T) { - cltest.FromDir(t, "any", "./_testcgo", true) + cltest.FromDir(t, "", "./_testcgo", true) } func TestFromTestdata(t *testing.T) { diff --git a/internal/runtime/z_iface.go b/internal/runtime/z_iface.go index b3ae4d05..e2a07fea 100644 --- a/internal/runtime/z_iface.go +++ b/internal/runtime/z_iface.go @@ -30,9 +30,37 @@ var ( TyAny = &InterfaceType{} ) +func MakeAnyInt(typ *Type, data uintptr) Interface { + return Interface{ + tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}, + data: unsafe.Pointer(data), + } +} + func MakeAny(typ *Type, data unsafe.Pointer) Interface { return Interface{ tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}, data: data, } } + +func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface { + return Interface{ + tab: &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}}, + data: data, + } +} + +func I2Int(v Interface, t *Type) uintptr { + if v.tab._type == t { + return uintptr(v.data) + } + panic("I2Int: type mismatch") +} + +func CheckI2Int(v Interface, t *Type) (uintptr, bool) { + if v.tab._type == t { + return uintptr(v.data), true + } + return 0, false +} diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index 41b4f53d..f0ad7723 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -25,20 +25,6 @@ import ( 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] } diff --git a/internal/unsafeheader/unsafeheader.go b/internal/unsafeheader/unsafeheader.go deleted file mode 100644 index 6d092c62..00000000 --- a/internal/unsafeheader/unsafeheader.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 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 unsafeheader contains header declarations for the Go runtime's slice -// and string implementations. -// -// This package allows packages that cannot import "reflect" to use types that -// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. -package unsafeheader - -import ( - "unsafe" -) - -// Slice is the runtime representation of a slice. -// It cannot be used safely or portably and its representation may -// change in a later release. -// -// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the -// data it references will not be garbage collected. -type Slice struct { - Data unsafe.Pointer - Len int - Cap int -} - -// String is the runtime representation of a string. -// It cannot be used safely or portably and its representation may -// change in a later release. -// -// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the -// data it references will not be garbage collected. -type String struct { - Data unsafe.Pointer - Len int -} diff --git a/ssa/expr.go b/ssa/expr.go index e8aaa3ad..932ae445 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -41,6 +41,37 @@ func (v Expr) TypeOf() types.Type { } */ +// Do evaluates the delay expression and returns the result. +func (v Expr) Do(isVArg bool) Expr { + if vt := v.Type; vt.kind == vkDelayExpr { + delay := vt.t.(*delayExprTy) + if f := delay.f; f != nil { + delay.f = nil + delay.val = f(isVArg) + } + return delay.val + } + return v +} + +// DelayExpr returns a delay expression. +func DelayExpr(f func(isVArg bool) Expr) Expr { + return Expr{Type: &aType{t: &delayExprTy{f: f}, kind: vkDelayExpr}} +} + +type delayExprTy struct { + f func(isVArg bool) Expr + val Expr +} + +func (p *delayExprTy) Underlying() types.Type { + panic("don't call") +} + +func (p *delayExprTy) String() string { + return "delayExpr" +} + // ----------------------------------------------------------------------------- func llvmValues(vals []Expr) []llvm.Value { @@ -407,6 +438,47 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { panic("todo") } +// 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) +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. +// +// Example printed form: +// +// t1 = make interface{} <- int (42:int) +// t2 = make Stringer <- t0 +func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Expr) { + if debugInstr { + log.Printf("MakeInterface %v, %v\n", inter, x.impl) + } + t := inter.Underlying().(*types.Interface) + isAny := t.Empty() + fnDo := func(isVArg bool) Expr { + if isVArg { // don't need make interface + return x + } + pkg := b.fn.pkg + switch x.kind { + case vkSigned, vkUnsigned, vkFloat: + fn := pkg.rtFunc("MakeAnyInt") + return b.InlineCall(fn, x) + } + panic("todo") + } + if mayDelay && isAny { + return DelayExpr(fnDo) + } + return fnDo(false) +} + // The TypeAssert instruction tests whether interface value X has type // AssertedType. // @@ -451,7 +523,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk) } switch assertedTyp.kind { - case vkSigned, vkUnsigned: + case vkSigned, vkUnsigned, vkFloat: pkg := b.fn.pkg fnName := "I2Int" if commaOk { diff --git a/ssa/type.go b/ssa/type.go index 90978212..ffec59b9 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -41,6 +41,7 @@ const ( vkBool vkFunc vkTuple + vkDelayExpr = -1 ) // -----------------------------------------------------------------------------