From 4eb2ddaf15b0ca094177532e7a70a71c1fc6c9a6 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 13:59:06 +0800 Subject: [PATCH 1/9] cl: support llgo.cstr --- cl/_testdata/cstr/in.go | 13 ++++++++++ cl/_testdata/cstr/out.ll | 26 ++++++++++++++++++++ cl/builtin_test.go | 18 ++++++++++++++ cl/compile.go | 40 +++++++++++++++++++++++++----- cl/import.go | 53 +++++++++++++++++++++++++++++++--------- internal/runtime/c/c.go | 6 ++--- ssa/expr.go | 20 +++++++-------- ssa/stmt_builder.go | 2 +- 8 files changed, 147 insertions(+), 31 deletions(-) create mode 100644 cl/_testdata/cstr/in.go create mode 100644 cl/_testdata/cstr/out.ll diff --git a/cl/_testdata/cstr/in.go b/cl/_testdata/cstr/in.go new file mode 100644 index 00000000..a8285b4e --- /dev/null +++ b/cl/_testdata/cstr/in.go @@ -0,0 +1,13 @@ +package main + +import _ "unsafe" + +//go:linkname cstr llgo.cstr +func cstr(string) *int8 + +//go:linkname printf C.printf +func printf(format *int8, __llgo_va_list ...any) + +func main() { + printf(cstr("Hello, world\n")) +} diff --git a/cl/_testdata/cstr/out.ll b/cl/_testdata/cstr/out.ll new file mode 100644 index 00000000..d5bfe25a --- /dev/null +++ b/cl/_testdata/cstr/out.ll @@ -0,0 +1,26 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = 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 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + call void (ptr, ...) @printf([14 x i8] c"Hello, world\0A\00") + ret void +} + +declare void @printf(ptr, ...) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index c947bdd5..bf83a119 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -25,6 +25,24 @@ import ( "golang.org/x/tools/go/ssa" ) +func TestCStrNoArgs(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("cstr: no error?") + } + }() + cstr(nil, nil) +} + +func TestCStrNonconst(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("cstr: no error?") + } + }() + cstr(nil, []ssa.Value{&ssa.Parameter{}}) +} + func TestPkgNoInit(t *testing.T) { pkg := types.NewPackage("foo", "foo") ctx := &context{ diff --git a/cl/compile.go b/cl/compile.go index ed182b14..20ba4cdd 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -189,8 +189,9 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) { sig := f.Signature - name, ok := p.funcName(pkgTypes, f, true) - if !ok { // ignored + name, ftype := p.funcName(pkgTypes, f, true) + switch ftype { + case ignoredFunc, llgoInstr: // llgo extended instructions return } if debugInstr { @@ -269,6 +270,18 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool { return false } +func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { + if len(args) == 1 { + if c, ok := args[0].(*ssa.Const); ok { + if v := c.Value; v.Kind() == constant.String { + sv := constant.StringVal(v) + return b.Prog.CStringVal(sv) + } + } + } + panic("cstr(): invalid arguments") +} + func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) { if asValue { if v, ok := p.bvals[iv]; ok { @@ -287,8 +300,9 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue if debugGoSSA { log.Println(">>> Call", cv, call.Args) } - if builtin, ok := cv.(*ssa.Builtin); ok { - fn := builtin.Name() + switch cv := cv.(type) { + case *ssa.Builtin: + fn := cv.Name() if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr arg := call.Args[0] ret = p.compileValue(b, arg) @@ -297,7 +311,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue args := p.compileValues(b, call.Args, kind) ret = b.BuiltinCall(fn, args...) } - } else { + case *ssa.Function: + fn, ftype := p.funcOf(cv) + switch ftype { + case goFunc, cFunc: + args := p.compileValues(b, call.Args, kind) + ret = b.Call(fn.Expr, args...) + case llgoCstr: + ret = cstr(b, call.Args) + default: + panic("todo") + } + default: fn := p.compileValue(b, cv) args := p.compileValues(b, call.Args, kind) ret = b.Call(fn, args...) @@ -421,7 +446,10 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } } case *ssa.Function: - fn := p.funcOf(v) + fn, ftype := p.funcOf(v) + if ftype >= llgoInstrBase { + panic("can't use llgo instruction as a value") + } return fn.Expr case *ssa.Global: g := p.varOf(v) diff --git a/cl/import.go b/cl/import.go index 249402a6..7cf32f7e 100644 --- a/cl/import.go +++ b/cl/import.go @@ -134,9 +134,9 @@ func (p *context) initLinkname(pkgPath, line string) { text := strings.TrimSpace(line[len(linkname):]) if idx := strings.IndexByte(text, ' '); idx > 0 { link := strings.TrimLeft(text[idx+1:], " ") - if strings.Contains(link, ".") { // eg. C.printf, C.strlen + if strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr name := pkgPath + "." + text[:idx] - p.link[name] = link[2:] + p.link[name] = link } else { panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") } @@ -171,25 +171,56 @@ func checkCgo(fnName string) bool { (fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check")) } -func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, bool) { +const ( + ignoredFunc = iota + goFunc + cFunc + llgoInstr = -1 + + llgoInstrBase = 0x80 + llgoCstr = llgoInstrBase + 1 + llgoAlloca = llgoInstrBase + 2 + llgoUnreachable = llgoInstrBase + 3 +) + +func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, int) { name := funcName(pkg, fn) if ignore && ignoreName(name) || checkCgo(fn.Name()) { - return name, false + return name, ignoredFunc } if v, ok := p.link[name]; ok { - return v, true + if strings.HasPrefix(v, "C.") { + return v[2:], cFunc + } + if strings.HasPrefix(v, "llgo.") { + return v[5:], llgoInstr + } + return v, goFunc } - return name, true + return name, goFunc } -func (p *context) funcOf(fn *ssa.Function) llssa.Function { +// funcOf returns a function by name and set ftype = goFunc, cFunc, etc. +// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc. +func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { pkgTypes := p.ensureLoaded(fn.Pkg.Pkg) pkg := p.pkg - name, _ := p.funcName(pkgTypes, fn, false) - if ret := pkg.FuncOf(name); ret != nil { - return ret + name, ftype := p.funcName(pkgTypes, fn, false) + if ftype == llgoInstr { + switch name { + case "cstr": + ftype = llgoCstr + case "alloca": + ftype = llgoAlloca + case "unreachable": + ftype = llgoUnreachable + default: + panic("unknown llgo instruction: " + name) + } + } else if ret = pkg.FuncOf(name); ret == nil { + ret = pkg.NewFunc(name, fn.Signature) } - return pkg.NewFunc(name, fn.Signature) + return } func (p *context) varOf(v *ssa.Global) llssa.Global { diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index 451d80b0..d3914551 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -22,13 +22,13 @@ const ( LLGoPackage = "decl" ) -//go:linkname String llgo.CString +//go:linkname String llgo.cstr func String(string) *int8 -//go:linkname Alloca llgo.Alloca +//go:linkname Alloca llgo.alloca func Alloca(size uintptr) unsafe.Pointer -//go:linkname Unreachable llgo.Unreachable +//go:linkname Unreachable llgo.unreachable func Unreachable() //go:linkname Malloc C.malloc diff --git a/ssa/expr.go b/ssa/expr.go index 4ea32c11..777f0da8 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -132,7 +132,7 @@ func (p Program) Val(v interface{}) Expr { // Const returns a constant expression. func (b Builder) Const(v constant.Value, typ Type) Expr { - prog := b.prog + prog := b.Prog if v == nil { return prog.Null(typ) } @@ -277,7 +277,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { } return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} case isPredOp(op): // op: == != < <= < >= - tret := b.prog.Bool() + tret := b.Prog.Bool() kind := x.kind switch kind { case vkSigned: @@ -318,7 +318,7 @@ func (b Builder) Load(ptr Expr) Expr { if debugInstr { log.Printf("Load %v\n", ptr.impl) } - telem := b.prog.Elem(ptr.Type) + telem := b.Prog.Elem(ptr.Type) return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem} } @@ -354,7 +354,7 @@ func (b Builder) FieldAddr(x Expr, idx int) Expr { if debugInstr { log.Printf("FieldAddr %v, %d\n", x.impl, idx) } - prog := b.prog + prog := b.Prog tstruc := prog.Elem(x.Type) telem := prog.Field(tstruc, idx) pt := prog.Pointer(telem) @@ -377,7 +377,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr { if debugInstr { log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl) } - prog := b.prog + prog := b.Prog telem := prog.Index(x.Type) pt := prog.Pointer(telem) indices := []llvm.Value{idx.impl} @@ -407,7 +407,7 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) { if debugInstr { log.Printf("Alloc %v, %v\n", t, heap) } - prog := b.prog + prog := b.Prog telem := t.Elem() if heap { pkg := b.fn.pkg @@ -452,7 +452,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { switch typ.(type) { default: ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast") - ret.Type = b.prog.Type(typ) + ret.Type = b.Prog.Type(typ) return } } @@ -488,7 +488,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { // t1 = convert []byte <- string (t0) func (b Builder) Convert(t Type, x Expr) (ret Expr) { typ := t.t - ret.Type = b.prog.Type(typ) + ret.Type = b.Prog.Type(typ) switch und := typ.Underlying().(type) { case *types.Basic: kind := und.Kind() @@ -624,7 +624,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { default: panic("todo") } - typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind))) + typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind))) return b.InlineCall(fn, x, typ) } panic("todo") @@ -659,7 +659,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { } switch t := fn.t.(type) { case *types.Signature: - ret.Type = b.prog.retType(t) + ret.Type = b.Prog.retType(t) default: panic("todo") } diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index a8af71d4..bed0d537 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -50,7 +50,7 @@ func (p BasicBlock) Index() int { type aBuilder struct { impl llvm.Builder fn Function - prog Program + Prog Program } // Builder represents a builder for creating instructions in a function. From 1610894a8089e7ed1351917ec0600389e6ea77e8 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 14:34:26 +0800 Subject: [PATCH 2/9] llgo/ssa: b.CString --- cl/_testdata/cstr/out.ll | 3 ++- cl/compile.go | 2 +- ssa/expr.go | 11 +++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cl/_testdata/cstr/out.ll b/cl/_testdata/cstr/out.ll index d5bfe25a..f7b5d255 100644 --- a/cl/_testdata/cstr/out.ll +++ b/cl/_testdata/cstr/out.ll @@ -2,6 +2,7 @@ source_filename = "main" @"main.init$guard" = global ptr null +@0 = private unnamed_addr constant [14 x i8] c"Hello, world\0A\00", align 1 define void @main.init() { _llgo_0: @@ -19,7 +20,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 define void @main() { _llgo_0: call void @main.init() - call void (ptr, ...) @printf([14 x i8] c"Hello, world\0A\00") + call void (ptr, ...) @printf(ptr @0) ret void } diff --git a/cl/compile.go b/cl/compile.go index 20ba4cdd..1c10d564 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -275,7 +275,7 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { if c, ok := args[0].(*ssa.Const); ok { if v := c.Value; v.Kind() == constant.String { sv := constant.StringVal(v) - return b.Prog.CStringVal(sv) + return b.CString(sv) } } } diff --git a/ssa/expr.go b/ssa/expr.go index 777f0da8..e38961eb 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -82,12 +82,6 @@ func (p Program) Null(t Type) Expr { return Expr{llvm.ConstNull(t.ll), t} } -// CStringVal returns a c-style string constant expression. -func (p Program) CStringVal(v string) Expr { - t := p.CString() - return Expr{llvm.ConstString(v, true), t} -} - // StringVal returns string constant expression. func (p Program) StringVal(v string) Expr { t := p.String() @@ -153,6 +147,11 @@ func (b Builder) Const(v constant.Value, typ Type) Expr { panic("todo") } +// CString returns a c-style string constant expression. +func (b Builder) CString(v string) Expr { + return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CString()} +} + // ----------------------------------------------------------------------------- const ( From 7a347d4563701f0a4ede09724ddcf47686286be7 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 14:39:37 +0800 Subject: [PATCH 3/9] update llvm --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index be8b2c10..cdd12ae6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/aykevl/go-wasm v0.0.1 github.com/goplus/gogen v1.15.2 - github.com/goplus/llvm v0.7.2 + github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670 github.com/goplus/mod v0.13.10 github.com/qiniu/x v1.13.10 golang.org/x/tools v0.20.0 diff --git a/go.sum b/go.sum index 2db946f6..0677cfe9 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/goplus/llvm v0.7.1 h1:B12Fr/wc3pAsq5PLuac9u9IuKpLRuCufdVAeGDP/MRw= github.com/goplus/llvm v0.7.1/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.7.2 h1:NL3LlwAmYVCGA6yV40AjOvMDKl2dbCqoYPtugmLQK+E= github.com/goplus/llvm v0.7.2/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= +github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670 h1:M5fGMLl9vj1W4lSKrOkhzVpoyyMOXtfAU5MsukbhUnY= +github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE= github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= From 2b82af519ced47727606a1106f42a7c88cb5c126 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 17:58:10 +0800 Subject: [PATCH 4/9] llgo/ssa: Alloca, ArrayAlloca --- cl/_testcgo/alloca/in.go | 14 ++++++++++ cl/_testcgo/alloca/out.ll | 36 ++++++++++++++++++++++++++ cl/{_testdata => _testcgo}/cstr/in.go | 0 cl/{_testdata => _testcgo}/cstr/out.ll | 0 cl/compile.go | 10 +++++++ internal/runtime/c/c.go | 3 ++- ssa/expr.go | 22 ++++++++++++++++ 7 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 cl/_testcgo/alloca/in.go create mode 100644 cl/_testcgo/alloca/out.ll rename cl/{_testdata => _testcgo}/cstr/in.go (100%) rename cl/{_testdata => _testcgo}/cstr/out.ll (100%) diff --git a/cl/_testcgo/alloca/in.go b/cl/_testcgo/alloca/in.go new file mode 100644 index 00000000..83452977 --- /dev/null +++ b/cl/_testcgo/alloca/in.go @@ -0,0 +1,14 @@ +package main + +import ( + "unsafe" + + "github.com/goplus/llgo/internal/runtime/c" +) + +func main() { + s := c.String("Hi\n") + s2 := c.Alloca(4) + c.Memcpy(s2, unsafe.Pointer(s), 4) + c.Printf(c.String("%s"), s) +} diff --git a/cl/_testcgo/alloca/out.ll b/cl/_testcgo/alloca/out.ll new file mode 100644 index 00000000..44cd672d --- /dev/null +++ b/cl/_testcgo/alloca/out.ll @@ -0,0 +1,36 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"main.init$guard" = 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 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + %0 = call ptr @"github.com/goplus/llgo/internal/runtime/c.String"([4 x i8] c"Hi\0A\00") + %1 = alloca i8, i64 4, align 1 + %2 = call ptr @memcpy(ptr %1, ptr %0, i64 4) + %3 = call ptr @"github.com/goplus/llgo/internal/runtime/c.String"([3 x i8] c"%s\00") + %4 = call i32 (ptr, ...) @printf(ptr %3, ptr %0) + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime/c.String"(%"github.com/goplus/llgo/internal/runtime.String") + +declare ptr @memcpy(ptr, ptr, i64) + +declare i32 @printf(ptr, ...) diff --git a/cl/_testdata/cstr/in.go b/cl/_testcgo/cstr/in.go similarity index 100% rename from cl/_testdata/cstr/in.go rename to cl/_testcgo/cstr/in.go diff --git a/cl/_testdata/cstr/out.ll b/cl/_testcgo/cstr/out.ll similarity index 100% rename from cl/_testdata/cstr/out.ll rename to cl/_testcgo/cstr/out.ll diff --git a/cl/compile.go b/cl/compile.go index 1c10d564..4782e8b1 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -282,6 +282,14 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { panic("cstr(): invalid arguments") } +func (p *context) alloca(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { + if len(args) == 1 { + n := p.compileValue(b, args[0]) + return b.Alloca(n) + } + panic("alloca(size uintptr): invalid arguments") +} + func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) { if asValue { if v, ok := p.bvals[iv]; ok { @@ -319,6 +327,8 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue ret = b.Call(fn.Expr, args...) case llgoCstr: ret = cstr(b, call.Args) + case llgoAlloca: + ret = p.alloca(b, call.Args) default: panic("todo") } diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index d3914551..7c11ea9a 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -16,6 +16,7 @@ package c +import "C" import "unsafe" const ( @@ -38,4 +39,4 @@ func Malloc(size uintptr) unsafe.Pointer func Memcpy(dst, src unsafe.Pointer, n uintptr) unsafe.Pointer //go:linkname Printf C.printf -func Printf(format *int8, __llgo_va_list ...any) +func Printf(format *int8, __llgo_va_list ...any) C.int diff --git a/ssa/expr.go b/ssa/expr.go index e38961eb..817bb55c 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -420,6 +420,28 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) { return } +// Alloca allocates space for n bytes. +func (b Builder) Alloca(n Expr) (ret Expr) { + if debugInstr { + log.Printf("Alloca %v\n", n.impl) + } + prog := b.Prog + telem := prog.tyInt8() + ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl) + ret.Type = &aType{prog.tyVoidPtr(), types.Typ[types.UnsafePointer], vkPtr} + return +} + +// ArrayAlloca reserves space for an array of n elements of type telem. +func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) { + if debugInstr { + log.Printf("ArrayAlloca %v, %v\n", telem.t, n.impl) + } + ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl) + ret.Type = b.Prog.Pointer(telem) + return +} + // The ChangeType instruction applies to X a value-preserving type // change to Type(). // From ae5efdf16c6676b509278a864a5d3d4c89a45c6d Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 18:01:03 +0800 Subject: [PATCH 5/9] disable ArrayAlloca --- ssa/expr.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssa/expr.go b/ssa/expr.go index 817bb55c..822e7172 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -432,6 +432,7 @@ func (b Builder) Alloca(n Expr) (ret Expr) { return } +/* // ArrayAlloca reserves space for an array of n elements of type telem. func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) { if debugInstr { @@ -441,6 +442,7 @@ func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) { ret.Type = b.Prog.Pointer(telem) return } +*/ // The ChangeType instruction applies to X a value-preserving type // change to Type(). From 8c1b8ad9455cb3704dddaad29fe76c79e3acc236 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 18:12:15 +0800 Subject: [PATCH 6/9] update llvm --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index cdd12ae6..4c601893 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/aykevl/go-wasm v0.0.1 github.com/goplus/gogen v1.15.2 - github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670 + github.com/goplus/llvm v0.7.3-0.20240429101120-83f0ad1fd7b3 github.com/goplus/mod v0.13.10 github.com/qiniu/x v1.13.10 golang.org/x/tools v0.20.0 diff --git a/go.sum b/go.sum index 0677cfe9..0de84975 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/goplus/llvm v0.7.2 h1:NL3LlwAmYVCGA6yV40AjOvMDKl2dbCqoYPtugmLQK+E= github.com/goplus/llvm v0.7.2/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670 h1:M5fGMLl9vj1W4lSKrOkhzVpoyyMOXtfAU5MsukbhUnY= github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= +github.com/goplus/llvm v0.7.3-0.20240429101120-83f0ad1fd7b3 h1:FWne6LdbQ0p+lGBpkdmCYzcJDDTH85ygkkWKy2Pd3EQ= +github.com/goplus/llvm v0.7.3-0.20240429101120-83f0ad1fd7b3/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE= github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= From e1d1d6a2d98a3b7e386cd8885424452459a70632 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 18:33:02 +0800 Subject: [PATCH 7/9] llgo/ssa: unreachable --- cl/_testcgo/unreachable/in.go | 14 +++++++++++++ cl/_testcgo/unreachable/out.ll | 38 ++++++++++++++++++++++++++++++++++ cl/compile.go | 2 ++ ssa/stmt_builder.go | 5 +++++ 4 files changed, 59 insertions(+) create mode 100644 cl/_testcgo/unreachable/in.go create mode 100644 cl/_testcgo/unreachable/out.ll diff --git a/cl/_testcgo/unreachable/in.go b/cl/_testcgo/unreachable/in.go new file mode 100644 index 00000000..c0c0a46b --- /dev/null +++ b/cl/_testcgo/unreachable/in.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/goplus/llgo/internal/runtime/c" +) + +func foo() { + c.Unreachable() +} + +func main() { + foo() + c.Printf(c.String("Hello\n")) +} diff --git a/cl/_testcgo/unreachable/out.ll b/cl/_testcgo/unreachable/out.ll new file mode 100644 index 00000000..52ff58db --- /dev/null +++ b/cl/_testcgo/unreachable/out.ll @@ -0,0 +1,38 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"main.init$guard" = global ptr null + +define void @main.foo() { +_llgo_0: + unreachable + ret void +} + +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() { +_llgo_0: + call void @main.init() + call void @main.foo() + %0 = call ptr @"github.com/goplus/llgo/internal/runtime/c.String"([7 x i8] c"Hello\0A\00") + %1 = call i32 (ptr, ...) @printf(ptr %0) + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime/c.String"(%"github.com/goplus/llgo/internal/runtime.String") + +declare i32 @printf(ptr, ...) diff --git a/cl/compile.go b/cl/compile.go index 4782e8b1..5764fa9e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -329,6 +329,8 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue ret = cstr(b, call.Args) case llgoAlloca: ret = p.alloca(b, call.Args) + case llgoUnreachable: + b.Unreachable() default: panic("todo") } diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index bed0d537..73d2eb09 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -76,6 +76,11 @@ func (b Builder) Panic(v Expr) { b.impl.CreateUnreachable() // TODO(xsw): pass v } +// Unreachable emits an unreachable instruction. +func (b Builder) Unreachable() { + b.impl.CreateUnreachable() +} + // Return emits a return instruction. func (b Builder) Return(results ...Expr) { if debugInstr { From 664c3fcce39f43ecb059dc843db0c54c9ee452b7 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 18:46:13 +0800 Subject: [PATCH 8/9] compileInstrOrValue --- cl/compile.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index 5764fa9e..8421a2ad 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -335,9 +335,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue panic("todo") } default: - fn := p.compileValue(b, cv) - args := p.compileValues(b, call.Args, kind) - ret = b.Call(fn, args...) + panic("todo") + /* + fn := p.compileValue(b, cv) + args := p.compileValues(b, call.Args, kind) + ret = b.Call(fn, args...) + */ } case *ssa.BinOp: x := p.compileValue(b, v.X) @@ -458,11 +461,14 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } } case *ssa.Function: - fn, ftype := p.funcOf(v) - if ftype >= llgoInstrBase { - panic("can't use llgo instruction as a value") - } - return fn.Expr + panic("unreachable") + /* + fn, ftype := p.funcOf(v) + if ftype >= llgoInstrBase { + panic("can't use llgo instruction as a value") + } + return fn.Expr + */ case *ssa.Global: g := p.varOf(v) return g.Expr From a6d31ad8b827c1ffb7d232e0c874b83725d70bff Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Apr 2024 18:55:09 +0800 Subject: [PATCH 9/9] TestErrAlloca --- cl/builtin_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index bf83a119..a3fa722a 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -25,6 +25,16 @@ import ( "golang.org/x/tools/go/ssa" ) +func TestErrAlloca(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("alloca: no error?") + } + }() + var ctz context + ctz.alloca(nil, nil) +} + func TestCStrNoArgs(t *testing.T) { defer func() { if r := recover(); r == nil {