Merge pull request #134 from xushiwei/q

cl: checkVArgs bugfix
This commit is contained in:
xushiwei
2024-05-09 06:57:27 +08:00
committed by GitHub
7 changed files with 172 additions and 69 deletions

13
cl/_testdata/vargs/in.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "github.com/goplus/llgo/internal/runtime/c"
func test(a ...any) {
for _, v := range a {
c.Printf(c.Str("%d\n"), v.(int))
}
}
func main() {
test(1, 2, 3)
}

89
cl/_testdata/vargs/out.ll Normal file
View File

@@ -0,0 +1,89 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
@"main.init$guard" = global ptr null
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
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
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main(i32 %0, ptr %1) {
_llgo_0:
store i32 %0, ptr @__llgo_argc, align 4
store ptr %1, ptr @__llgo_argv, align 8
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 0
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%5 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %4, i64 1)
store %"github.com/goplus/llgo/internal/runtime.iface" %5, ptr %3, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 1
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%8 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %7, i64 2)
store %"github.com/goplus/llgo/internal/runtime.iface" %8, ptr %6, align 8
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 2
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%11 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %10, i64 3)
store %"github.com/goplus/llgo/internal/runtime.iface" %11, ptr %9, align 8
%12 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %2, i64 16, i64 3, i64 0, i64 3, i64 3)
call void @main.test(%"github.com/goplus/llgo/internal/runtime.Slice" %12)
ret void
}
define void @main.test(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%2 = phi i64 [ -1, %_llgo_0 ], [ %3, %_llgo_2 ]
%3 = add i64 %2, 1
%4 = icmp slt i64 %3, %1
br i1 %4, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %5, i64 %3
%7 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, align 8
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%9 = call i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %7, ptr %8)
%10 = call i32 (ptr, ...) @printf(ptr @0, i64 %9)
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
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)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
declare i32 @printf(ptr, ...)

View File

@@ -100,6 +100,16 @@ func TestErrAlloca(t *testing.T) {
ctx.alloca(nil, nil) ctx.alloca(nil, nil)
} }
func TestErrAllocaCStr(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("allocaCStr: no error?")
}
}()
var ctx context
ctx.allocaCStr(nil, nil)
}
func TestCStrNoArgs(t *testing.T) { func TestCStrNoArgs(t *testing.T) {
defer func() { defer func() {
if r := recover(); r == nil { if r := recover(); r == nil {

View File

@@ -62,13 +62,15 @@ const (
) )
func (p *context) funcKind(vfn ssa.Value) int { func (p *context) funcKind(vfn ssa.Value) int {
if fn, ok := vfn.(*ssa.Function); ok && fn.Signature.Recv() == nil { if fn, ok := vfn.(*ssa.Function); ok {
params := fn.Signature.Params() params := fn.Signature.Params()
n := params.Len() n := params.Len()
if n == 0 { if n == 0 {
if fn.Signature.Recv() == nil {
if fn.Name() == "init" && p.pkgNoInit(fn.Pkg.Pkg) { if fn.Name() == "init" && p.pkgNoInit(fn.Pkg.Pkg) {
return fnIgnore return fnIgnore
} }
}
} else { } else {
last := params.At(n - 1) last := params.At(n - 1)
if last.Name() == llssa.NameValist { if last.Name() == llssa.NameValist {
@@ -356,9 +358,9 @@ func (p *context) isVArgs(vx ssa.Value) (ret []llssa.Expr, ok bool) {
} }
func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool { func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
if v.Comment == "varargs" { // this is a varargs allocation if v.Comment == "varargs" { // this maybe a varargs allocation
if arr, ok := t.Elem().(*types.Array); ok { if arr, ok := t.Elem().(*types.Array); ok {
if isAny(arr.Elem()) { if isAny(arr.Elem()) && isVargs(p, v) {
p.vargs[v] = make([]llssa.Expr, arr.Len()) p.vargs[v] = make([]llssa.Expr, arr.Len())
return true return true
} }
@@ -367,6 +369,20 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
return false return false
} }
func isVargs(ctx *context, v *ssa.Alloc) bool {
refs := *v.Referrers()
n := len(refs)
lastref := refs[n-1]
if i, ok := lastref.(*ssa.Slice); ok {
if refs = *i.Referrers(); len(refs) == 1 {
if call, ok := refs[0].(*ssa.Call); ok {
return ctx.funcKind(call.Call.Value) == fnHasVArg
}
}
}
return false
}
// func cstr(string) *int8 // func cstr(string) *int8
func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 { if len(args) == 1 {
@@ -468,42 +484,42 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
} }
switch v := iv.(type) { switch v := iv.(type) {
case *ssa.Call: case *ssa.Call:
call := v.Call cv := v.Call.Value
cv := call.Value
kind := p.funcKind(cv) kind := p.funcKind(cv)
if kind == fnIgnore { if kind == fnIgnore {
return return
} }
args := v.Call.Args
if debugGoSSA { if debugGoSSA {
log.Println(">>> Call", cv, call.Args) log.Println(">>> Call", cv, args)
} }
switch cv := cv.(type) { switch cv := cv.(type) {
case *ssa.Builtin: case *ssa.Builtin:
fn := cv.Name() fn := cv.Name()
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
arg := call.Args[0] arg := args[0]
ret = p.compileValue(b, arg) ret = p.compileValue(b, arg)
// log.Println("wrapnilchk:", ret.TypeOf()) // log.Println("wrapnilchk:", ret.TypeOf())
} else { } else {
args := p.compileValues(b, call.Args, kind) args := p.compileValues(b, args, kind)
ret = b.BuiltinCall(fn, args...) ret = b.BuiltinCall(fn, args...)
} }
case *ssa.Function: case *ssa.Function:
fn, ftype := p.compileFunction(cv) fn, ftype := p.compileFunction(cv)
switch ftype { switch ftype {
case goFunc, cFunc: case goFunc, cFunc:
args := p.compileValues(b, call.Args, kind) args := p.compileValues(b, args, kind)
ret = b.Call(fn.Expr, args...) ret = b.Call(fn.Expr, args...)
case llgoCstr: case llgoCstr:
ret = cstr(b, call.Args) ret = cstr(b, args)
case llgoAdvance: case llgoAdvance:
ret = p.advance(b, call.Args) ret = p.advance(b, args)
case llgoIndex: case llgoIndex:
ret = p.index(b, call.Args) ret = p.index(b, args)
case llgoAlloca: case llgoAlloca:
ret = p.alloca(b, call.Args) ret = p.alloca(b, args)
case llgoAllocaCStr: case llgoAllocaCStr:
ret = p.allocaCStr(b, call.Args) ret = p.allocaCStr(b, args)
case llgoUnreachable: // func unreachable() case llgoUnreachable: // func unreachable()
b.Unreachable() b.Unreachable()
default: default:
@@ -511,7 +527,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
} }
default: default:
fn := p.compileValue(b, cv) fn := p.compileValue(b, cv)
args := p.compileValues(b, call.Args, kind) args := p.compileValues(b, args, kind)
ret = b.Call(fn, args...) ret = b.Call(fn, args...)
} }
case *ssa.BinOp: case *ssa.BinOp:
@@ -534,7 +550,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = b.FieldAddr(x, v.Field) ret = b.FieldAddr(x, v.Field)
case *ssa.Alloc: case *ssa.Alloc:
t := v.Type().(*types.Pointer) t := v.Type().(*types.Pointer)
if p.checkVArgs(v, t) { // varargs: this is a varargs allocation if p.checkVArgs(v, t) { // varargs: this maybe a varargs allocation
return return
} }
elem := p.prog.Type(t.Elem(), llssa.InGo) elem := p.prog.Type(t.Elem(), llssa.InGo)
@@ -580,12 +596,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
} }
ret = b.Slice(x, low, high, max) ret = b.Slice(x, low, high, max)
case *ssa.MakeInterface: case *ssa.MakeInterface:
const ( if refs := *v.Referrers(); len(refs) == 1 {
delayExpr = true // varargs: don't need to convert an expr to any if ref, ok := refs[0].(*ssa.Store); ok {
) if va, ok := ref.Addr.(*ssa.IndexAddr); ok {
if _, ok = p.isVArgs(va.X); ok { // varargs: this is a varargs store
return
}
}
}
}
t := p.prog.Type(v.Type(), llssa.InGo) t := p.prog.Type(v.Type(), llssa.InGo)
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.MakeInterface(t, x, delayExpr) ret = b.MakeInterface(t, x)
case *ssa.MakeSlice: case *ssa.MakeSlice:
var nCap llssa.Expr var nCap llssa.Expr
t := p.prog.Type(v.Type(), llssa.InGo) t := p.prog.Type(v.Type(), llssa.InGo)

View File

@@ -44,8 +44,6 @@ func (v Expr) IsNil() bool {
// Do evaluates the delay expression and returns the result. // Do evaluates the delay expression and returns the result.
func (v Expr) Do(b Builder) Expr { func (v Expr) Do(b Builder) Expr {
switch vt := v.Type; vt.kind { switch vt := v.Type; vt.kind {
case vkDelayExpr:
return vt.raw.Type.(delayExprTy)()
case vkPhisExpr: case vkPhisExpr:
e := vt.raw.Type.(*phisExprTy) e := vt.raw.Type.(*phisExprTy)
return b.aggregateValue(e.Type, e.phis...) return b.aggregateValue(e.Type, e.phis...)
@@ -55,23 +53,6 @@ func (v Expr) Do(b Builder) Expr {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// DelayExpr returns a delay expression.
func DelayExpr(f func() Expr) Expr {
return Expr{Type: &aType{raw: rawType{delayExprTy(f)}, kind: vkDelayExpr}}
}
type delayExprTy func() Expr
func (p delayExprTy) Underlying() types.Type {
panic("don't call")
}
func (p delayExprTy) String() string {
return "delayExpr"
}
// -----------------------------------------------------------------------------
type phisExprTy struct { type phisExprTy struct {
phis []llvm.Value phis []llvm.Value
Type Type
@@ -1059,14 +1040,11 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
// //
// t1 = make interface{} <- int (42:int) // t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0 // t2 = make Stringer <- t0
func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) { func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
raw := tinter.raw.Type raw := tinter.raw.Type
if debugInstr { if debugInstr {
log.Printf("MakeInterface %v, %v\n", raw, x.impl) log.Printf("MakeInterface %v, %v\n", raw, x.impl)
} }
tiund := raw.Underlying().(*types.Interface)
isAny := tiund.Empty()
fnDo := func() Expr {
prog := b.Prog prog := b.Prog
pkg := b.Func.Pkg pkg := b.Func.Pkg
switch tx := x.raw.Type.Underlying().(type) { switch tx := x.raw.Type.Underlying().(type) {
@@ -1083,11 +1061,6 @@ func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) {
} }
} }
panic("todo") panic("todo")
}
if mayDelay && isAny {
return DelayExpr(fnDo)
}
return fnDo()
} }
// The TypeAssert instruction tests whether interface value X has type // The TypeAssert instruction tests whether interface value X has type

View File

@@ -73,9 +73,7 @@ func TestCvtType(t *testing.T) {
} }
func TestUserdefExpr(t *testing.T) { func TestUserdefExpr(t *testing.T) {
a := delayExprTy(nil)
b := &phisExprTy{} b := &phisExprTy{}
_ = a.String()
_ = b.String() _ = b.String()
test := func(a types.Type) { test := func(a types.Type) {
defer func() { defer func() {
@@ -85,7 +83,6 @@ func TestUserdefExpr(t *testing.T) {
}() }()
a.Underlying() a.Underlying()
} }
test(a)
test(b) test(b)
} }

View File

@@ -44,8 +44,7 @@ const (
vkFuncPtr vkFuncPtr
vkClosure vkClosure
vkTuple vkTuple
vkDelayExpr = -1 vkPhisExpr = -1
vkPhisExpr = -2
) )
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------