diff --git a/cl/_testgo/goroutine/in.go b/cl/_testgo/goroutine/in.go new file mode 100644 index 00000000..85def841 --- /dev/null +++ b/cl/_testgo/goroutine/in.go @@ -0,0 +1,12 @@ +package main + +func main() { + done := false + go func() { + println("Hello, World!") + done = true + }() + for !done { + print(".") + } +} diff --git a/cl/_testgo/goroutine/out.ll b/cl/_testgo/goroutine/out.ll new file mode 100644 index 00000000..00c29c48 --- /dev/null +++ b/cl/_testgo/goroutine/out.ll @@ -0,0 +1,89 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [2 x i8] c".\00", align 1 +@1 = private unnamed_addr constant [14 x i8] c"Hello, World!\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 i32 @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 1) + store i1 false, ptr %2, align 1 + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %4 = getelementptr inbounds { ptr }, ptr %3, i32 0, i32 0 + store ptr %2, ptr %4, align 8 + %5 = alloca { ptr, ptr }, align 8 + %6 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 0 + store ptr @"main.main$1", ptr %6, align 8 + %7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1 + store ptr %3, ptr %7, align 8 + %8 = load { ptr, ptr }, ptr %5, align 8 + %9 = extractvalue { ptr, ptr } %8, 1 + %10 = extractvalue { ptr, ptr } %8, 0 + call void %10(ptr %9) + br label %_llgo_3 + +_llgo_1: ; preds = %_llgo_3 + %11 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 0 + store ptr @0, ptr %12, align 8 + %13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1 + store i64 1, ptr %13, align 4 + %14 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %14) + br label %_llgo_3 + +_llgo_2: ; preds = %_llgo_3 + ret i32 0 + +_llgo_3: ; preds = %_llgo_1, %_llgo_0 + %15 = load i1, ptr %2, align 1 + br i1 %15, label %_llgo_2, label %_llgo_1 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +define void @"main.main$1"(ptr %0) { +_llgo_0: + %1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0 + store ptr @1, ptr %2, align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1 + store i64 13, ptr %3, align 4 + %4 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %4) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %5 = load { ptr }, ptr %0, align 8 + %6 = extractvalue { ptr } %5, 0 + store i1 true, ptr %6, align 1 + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) diff --git a/cl/_testgo/print/in.go b/cl/_testgo/print/in.go new file mode 100644 index 00000000..bf63848a --- /dev/null +++ b/cl/_testgo/print/in.go @@ -0,0 +1,5 @@ +package main + +func main() { + println('.', byte('.')) +} diff --git a/cl/_testgo/print/out.ll b/cl/_testgo/print/out.ll new file mode 100644 index 00000000..dd4a2524 --- /dev/null +++ b/cl/_testgo/print/out.ll @@ -0,0 +1,40 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = 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 i32 @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() + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 46) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64 46) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64) diff --git a/cl/compile.go b/cl/compile.go index f5708851..05d78220 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -548,6 +548,72 @@ func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) { return } +func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon) (ret llssa.Expr) { + cv := call.Value + if mthd := call.Method; mthd != nil { + o := p.compileValue(b, cv) + fn := b.Imethod(o, mthd) + args := p.compileValues(b, call.Args, fnNormal) + ret = b.Do(act, fn, args...) + return + } + kind := p.funcKind(cv) + if kind == fnIgnore { + return + } + args := call.Args + if debugGoSSA { + log.Println(">>> Do", act, cv, args) + } + switch cv := cv.(type) { + case *ssa.Builtin: + fn := cv.Name() + if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr + arg := args[0] + ret = p.compileValue(b, arg) + // log.Println("wrapnilchk:", ret.TypeOf()) + } else { + args := p.compileValues(b, args, kind) + ret = b.BuiltinDo(act, fn, args...) + } + case *ssa.Function: + aFn, pyFn, ftype := p.compileFunction(cv) + // TODO(xsw): check ca != llssa.Call + switch ftype { + case goFunc, cFunc: + args := p.compileValues(b, args, kind) + ret = b.Do(act, aFn.Expr, args...) + case pyFunc: + args := p.compileValues(b, args, kind) + ret = b.Do(act, pyFn.Expr, args...) + case llgoPyList: + args := p.compileValues(b, args, fnHasVArg) + ret = b.PyList(args...) + case llgoCstr: + ret = cstr(b, args) + case llgoAdvance: + ret = p.advance(b, args) + case llgoIndex: + ret = p.index(b, args) + case llgoAlloca: + ret = p.alloca(b, args) + case llgoAllocaCStr: + ret = p.allocaCStr(b, args) + case llgoStringData: + ret = p.stringData(b, args) + case llgoUnreachable: // func unreachable() + b.Unreachable() + default: + log.Panicln("unknown ftype:", ftype) + } + default: + fn := p.compileValue(b, cv) + args := p.compileValues(b, args, kind) + ret = b.Do(act, fn, args...) + } + return +} + func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) { if asValue { if v, ok := p.bvals[iv]; ok { @@ -557,67 +623,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue } switch v := iv.(type) { case *ssa.Call: - cv := v.Call.Value - if mthd := v.Call.Method; mthd != nil { - o := p.compileValue(b, cv) - fn := b.Imethod(o, v.Call.Method) - args := p.compileValues(b, v.Call.Args, fnNormal) - ret = b.Call(fn, args...) - break - } - kind := p.funcKind(cv) - if kind == fnIgnore { - return - } - args := v.Call.Args - if debugGoSSA { - log.Println(">>> Call", cv, args) - } - switch cv := cv.(type) { - case *ssa.Builtin: - fn := cv.Name() - if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr - arg := args[0] - ret = p.compileValue(b, arg) - // log.Println("wrapnilchk:", ret.TypeOf()) - } else { - args := p.compileValues(b, args, kind) - ret = b.BuiltinCall(fn, args...) - } - case *ssa.Function: - aFn, pyFn, ftype := p.compileFunction(cv) - switch ftype { - case goFunc, cFunc: - args := p.compileValues(b, args, kind) - ret = b.Call(aFn.Expr, args...) - case pyFunc: - args := p.compileValues(b, args, kind) - ret = b.Call(pyFn.Expr, args...) - case llgoPyList: - args := p.compileValues(b, args, fnHasVArg) - ret = b.PyList(args...) - case llgoCstr: - ret = cstr(b, args) - case llgoAdvance: - ret = p.advance(b, args) - case llgoIndex: - ret = p.index(b, args) - case llgoAlloca: - ret = p.alloca(b, args) - case llgoAllocaCStr: - ret = p.allocaCStr(b, args) - case llgoStringData: - ret = p.stringData(b, args) - case llgoUnreachable: // func unreachable() - b.Unreachable() - default: - log.Panicln("unknown ftype:", ftype) - } - default: - fn := p.compileValue(b, cv) - args := p.compileValues(b, args, kind) - ret = b.Call(fn, args...) - } + ret = p.call(b, llssa.Call, &v.Call) case *ssa.BinOp: x := p.compileValue(b, v.X) y := p.compileValue(b, v.Y) @@ -791,6 +797,8 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { key := p.compileValue(b, v.Key) val := p.compileValue(b, v.Value) b.MapUpdate(m, key, val) + case *ssa.Go: + p.call(b, llssa.Go, &v.Call) case *ssa.Panic: arg := p.compileValue(b, v.X) b.Panic(arg) diff --git a/ssa/expr.go b/ssa/expr.go index 1a6836cd..9c3cfae0 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -892,13 +892,26 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { // t2 = println(t0, t1) // t4 = t3() func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { + return b.Do(Call, fn, args...) +} + +type DoAction int + +const ( + Call DoAction = iota + Go + Defer +) + +// Do call a function with an action. +func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { if debugInstr { var b bytes.Buffer name := fn.impl.Name() if name == "" { name = "closure" } - fmt.Fprint(&b, "Call ", fn.kind, " ", fn.raw.Type, " ", name) + fmt.Fprint(&b, "Do ", da, " ", fn.kind, " ", fn.raw.Type, " ", name) sep := ": " for _, arg := range args { fmt.Fprint(&b, sep, arg.impl) @@ -987,6 +1000,11 @@ func (b Builder) Next(iter Expr, isString bool) (ret Expr) { // `fn` indicates the function: one of the built-in functions from the // Go spec (excluding "make" and "new"). func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { + return b.BuiltinDo(Call, fn, args...) +} + +// BuiltinDo call a builtin function with an action. +func (b Builder) BuiltinDo(da DoAction, fn string, args ...Expr) (ret Expr) { switch fn { case "len": if len(args) == 1 {