diff --git a/cl/_testdata/printval/in.go b/cl/_testdata/printval/in.go new file mode 100644 index 00000000..93d49b80 --- /dev/null +++ b/cl/_testdata/printval/in.go @@ -0,0 +1,12 @@ +package main + +import _ "unsafe" + +//go:linkname printf printf +func printf(format *int8, __llgo_va_list ...any) + +var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0} + +func main() { + printf(&format[0], 100) +} diff --git a/cl/_testdata/printval/out.ll b/cl/_testdata/printval/out.ll new file mode 100644 index 00000000..62e449a3 --- /dev/null +++ b/cl/_testdata/printval/out.ll @@ -0,0 +1,37 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@main.format = global ptr null + +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 +} + +declare void @printf(ptr, ...) + +define void @main() { +_llgo_0: + call void @main.init() + call void (ptr, ...) @printf(ptr @main.format, i64 100) + ret void +} diff --git a/cl/compile.go b/cl/compile.go index 1b9501c8..dbd32cd0 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -19,6 +19,7 @@ package cl import ( "fmt" "go/ast" + "go/constant" "go/token" "go/types" "log" @@ -93,9 +94,10 @@ type context struct { fset *token.FileSet goTyps *types.Package goPkg *ssa.Package - link map[string]string // pkgPath.nameInPkg => linkname - loaded map[*types.Package]none // loaded packages - bvals map[ssa.Value]llssa.Expr // block values + link map[string]string // pkgPath.nameInPkg => linkname + loaded map[*types.Package]none // loaded packages + bvals map[ssa.Value]llssa.Expr // block values + vargs map[*ssa.Alloc][]llssa.Expr // varargs inits []func() } @@ -160,6 +162,43 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo return ret } +func isAny(t types.Type) bool { + if t, ok := t.(*types.Interface); ok { + return t.Empty() + } + return false +} + +func intVal(v ssa.Value) int64 { + if c, ok := v.(*ssa.Const); ok { + if iv, exact := constant.Int64Val(c.Value); exact { + return iv + } + } + panic("intVal: ssa.Value is not a const int") +} + +func (p *context) isVArgs(vx ssa.Value) (ret []llssa.Expr, ok bool) { + if va, vok := vx.(*ssa.Alloc); vok { + ret, ok = p.vargs[va] // varargs: this is a varargs index + } + return +} + +func (p *context) checkVArgs(v *ssa.Alloc, t types.Type) bool { + if v.Comment == "varargs" { // this is a varargs allocation + if t, ok := t.(*types.Pointer); ok { + if arr, ok := t.Elem().(*types.Array); ok { + if isAny(arr.Elem()) { + p.vargs[v] = make([]llssa.Expr, arr.Len()) + return true + } + } + } + } + return false +} + func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret llssa.Expr) { if v, ok := p.bvals[iv]; ok { return v @@ -185,12 +224,31 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l x := p.compileValue(b, v.X) ret = b.UnOp(v.Op, x) case *ssa.IndexAddr: - x := p.compileValue(b, v.X) + vx := v.X + if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index + return + } + x := p.compileValue(b, vx) idx := p.compileValue(b, v.Index) ret = b.IndexAddr(x, idx) + case *ssa.Slice: + vx := v.X + if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice + return + } + panic("todo") case *ssa.Alloc: t := v.Type() + if p.checkVArgs(v, t) { // varargs: this is a varargs allocation + return + } ret = b.Alloc(p.prog.Type(t), v.Heap) + case *ssa.MakeInterface: + t := v.Type() + if isAny(t) { // varargs: don't need to convert an expr to any + return + } + panic("todo") default: panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) } @@ -205,7 +263,19 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { } switch v := instr.(type) { case *ssa.Store: - ptr := p.compileValue(b, v.Addr) + va := v.Addr + if va, ok := va.(*ssa.IndexAddr); ok { + if args, ok := p.isVArgs(va.X); ok { // varargs: this is a varargs store + idx := intVal(va.Index) + val := v.Val + if vi, ok := val.(*ssa.MakeInterface); ok { + val = vi.X + } + args[idx] = p.compileValue(b, val) + return + } + } + ptr := p.compileValue(b, va) val := p.compileValue(b, v.Val) b.Store(ptr, val) case *ssa.Jump: @@ -262,12 +332,16 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) []llssa.Expr { _ = b switch v := v.(type) { + case *ssa.Slice: // varargs: this is a varargs slice + if args, ok := p.isVArgs(v.X); ok { + return append(ret, args...) + } case *ssa.Const: if v.Value == nil { return ret } } - panic("todo") + panic(fmt.Sprintf("compileVArg: unknown value - %T\n", v)) } func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) []llssa.Expr { @@ -316,6 +390,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll goPkg: pkg, link: make(map[string]string), loaded: make(map[*types.Package]none), + vargs: make(map[*ssa.Alloc][]llssa.Expr), } ctx.initFiles(pkgPath, files) for _, m := range members { diff --git a/cl/compile_test.go b/cl/compile_test.go index c18dfa2b..6e250588 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -22,15 +22,15 @@ import ( "github.com/goplus/llgo/cl/cltest" ) -func TestFromTestdata(t *testing.T) { - cltest.FromDir(t, "", "./_testdata") -} - func testCompile(t *testing.T, src, expected string) { t.Helper() cltest.TestCompileEx(t, src, "foo.go", expected) } +func TestFromTestdata(t *testing.T) { + cltest.FromDir(t, "", "./_testdata") +} + func TestVar(t *testing.T) { testCompile(t, `package foo