diff --git a/cl/_testcgo/alloca/in.go b/cl/_testcgo/alloca/in.go index 8d601b52..5550ac64 100644 --- a/cl/_testcgo/alloca/in.go +++ b/cl/_testcgo/alloca/in.go @@ -7,8 +7,8 @@ import ( ) func main() { - s := c.String("Hi\n") + s := c.Str("Hi\n") s2 := c.Alloca(4) c.Memcpy(s2, unsafe.Pointer(s), 4) - c.Printf(c.String("%s"), s2) + c.Printf(c.Str("%s"), s2) } diff --git a/cl/_testcgo/any/in.go b/cl/_testcgo/any/in.go index d6734fbd..4d9300ed 100644 --- a/cl/_testcgo/any/in.go +++ b/cl/_testcgo/any/in.go @@ -9,5 +9,5 @@ func incVal(a any) int { } func main() { - c.Printf(c.String("Hello %d\n"), incVal(100)) + c.Printf(c.Str("Hello %d\n"), incVal(100)) } diff --git a/cl/_testcgo/sum/in.go b/cl/_testcgo/sum/in.go new file mode 100644 index 00000000..435310bd --- /dev/null +++ b/cl/_testcgo/sum/in.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/goplus/llgo/internal/runtime/c" +) + +func sum(args ...int) (ret int) { + for _, v := range args { + ret += v + } + return +} + +func main() { + c.Printf(c.Str("Hello %d\n"), sum(1, 2, 3, 4)) +} diff --git a/cl/_testcgo/sum/out.ll b/cl/_testcgo/sum/out.ll new file mode 100644 index 00000000..7a8cda47 --- /dev/null +++ b/cl/_testcgo/sum/out.ll @@ -0,0 +1,68 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } + +@"main.init$guard" = global ptr null +@0 = private unnamed_addr constant [10 x i8] c"Hello %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() { +_llgo_0: + call void @main.init() + %0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16) + %1 = getelementptr inbounds i64, ptr %0, i64 0 + store i64 1, ptr %1, align 4 + %2 = getelementptr inbounds i64, ptr %0, i64 1 + store i64 2, ptr %2, align 4 + %3 = getelementptr inbounds i64, ptr %0, i64 2 + store i64 3, ptr %3, align 4 + %4 = getelementptr inbounds i64, ptr %0, i64 3 + store i64 4, ptr %4, align 4 + %5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(i64 4, i64 4) + %6 = call i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %5) + %7 = call i32 (ptr, ...) @printf(ptr @0, i64 %6) + ret void +} + +define i64 @main.sum(%"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 [ 0, %_llgo_0 ], [ %8, %_llgo_2 ] + %3 = phi i64 [ -1, %_llgo_0 ], [ %4, %_llgo_2 ] + %4 = add i64 %3, 1 + %5 = icmp slt i64 %4, %1 + br i1 %5, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %6 = getelementptr inbounds i64, %"github.com/goplus/llgo/internal/runtime.Slice" %0, i64 %4 + %7 = load i64, %"github.com/goplus/llgo/internal/runtime.Slice" %6, align 4 + %8 = add i64 %2, %7 + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + ret i64 %2 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64) + +declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64) + +declare i32 @printf(ptr, ...) + +declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice") diff --git a/cl/_testcgo/unreachable/in.go b/cl/_testcgo/unreachable/in.go index c0c0a46b..3f62b8a8 100644 --- a/cl/_testcgo/unreachable/in.go +++ b/cl/_testcgo/unreachable/in.go @@ -10,5 +10,5 @@ func foo() { func main() { foo() - c.Printf(c.String("Hello\n")) + c.Printf(c.Str("Hello\n")) } diff --git a/cl/compile.go b/cl/compile.go index a4f67c8b..b0a954be 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -142,6 +142,7 @@ type context struct { bvals map[ssa.Value]llssa.Expr // block values vargs map[*ssa.Alloc][]llssa.Expr // varargs inits []func() + phis []func() } func (p *context) compileType(pkg llssa.Package, t *ssa.Type) { @@ -203,6 +204,7 @@ func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa defer func() { p.fn = nil }() + p.phis = nil nblk := len(f.Blocks) if nblk == 0 { // external function return @@ -219,6 +221,9 @@ func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa for i, block := range f.Blocks { p.compileBlock(b, block, i == 0 && name == "main") } + for _, phi := range p.phis { + phi() + } }) } @@ -275,7 +280,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.CString(sv) + return b.CStr(sv) } } } @@ -349,6 +354,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue case *ssa.UnOp: x := p.compileValue(b, v.X) ret = b.UnOp(v.Op, x) + case *ssa.Phi: + phi := b.Phi(p.prog.Type(v.Type())) + ret = phi.Expr + p.phis = append(p.phis, func() { + vals := p.compileValues(b, v.Edges, 0) + preds := v.Block().Preds + bblks := make([]llssa.BasicBlock, len(preds)) + for i, pred := range preds { + bblks[i] = p.fn.Block(pred.Index) + } + phi.AddIncoming(vals, bblks) + }) case *ssa.ChangeType: t := v.Type() x := p.compileValue(b, v.X) @@ -373,7 +390,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice return } - panic("todo") + var low, high, max llssa.Expr + x := p.compileValue(b, vx) + if v.Low != nil { + low = p.compileValue(b, v.Low) + } + if v.High != nil { + high = p.compileValue(b, v.High) + } + if v.Max != nil { + max = p.compileValue(b, v.Max) + } + ret = b.Slice(x, low, high, max) case *ssa.Alloc: t := v.Type().(*types.Pointer) if p.checkVArgs(v, t) { // varargs: this is a varargs allocation diff --git a/go.mod b/go.mod index 4c601893..f680f52c 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.20240429101120-83f0ad1fd7b3 + github.com/goplus/llvm v0.7.3-0.20240430032503-06c2c6248df7 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 0de84975..3cc2877d 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/goplus/llvm v0.7.3-0.20240429063826-4d6268bd1670 h1:M5fGMLl9vj1W4lSKr 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/llvm v0.7.3-0.20240430032503-06c2c6248df7 h1:mRJ4mVkklwfhFRI5lmC9ZrEptTkW2FLSEWiZg/rqM7k= +github.com/goplus/llvm v0.7.3-0.20240430032503-06c2c6248df7/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= diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index 7c11ea9a..2bbccdd3 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -23,8 +23,8 @@ const ( LLGoPackage = "decl" ) -//go:linkname String llgo.cstr -func String(string) *int8 +//go:linkname Str llgo.cstr +func Str(string) *int8 //go:linkname Alloca llgo.alloca func Alloca(size uintptr) unsafe.Pointer diff --git a/internal/runtime/llgo_autogen.ll b/internal/runtime/llgo_autogen.ll index f5d5d5eb..0708430f 100644 --- a/internal/runtime/llgo_autogen.ll +++ b/internal/runtime/llgo_autogen.ll @@ -172,6 +172,19 @@ _llgo_0: ret %"github.com/goplus/llgo/internal/runtime.iface" %12 } +define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 0 + %5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 1 + %6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 2 + store ptr %0, ptr %4, align 8 + store i64 %1, ptr %5, align 4 + store i64 %2, ptr %6, align 4 + %7 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, align 8 + ret %"github.com/goplus/llgo/internal/runtime.Slice" %7 +} + define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() { _llgo_0: %0 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 @@ -185,6 +198,15 @@ _llgo_0: ret %"github.com/goplus/llgo/internal/runtime.Slice" %4 } +define i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) { +_llgo_0: + %1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8 + store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8 + %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1 + %3 = load i64, ptr %2, align 4 + ret i64 %3 +} + define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) { _llgo_0: %1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16) diff --git a/internal/runtime/z_slice.go b/internal/runtime/z_slice.go index 215d327a..ff4b309e 100644 --- a/internal/runtime/z_slice.go +++ b/internal/runtime/z_slice.go @@ -24,9 +24,9 @@ import ( // Slice is the runtime representation of a slice. type Slice struct { - array unsafe.Pointer - len int - cap int + data unsafe.Pointer + len int + cap int } // NilSlice returns a nil slice. @@ -34,4 +34,14 @@ func NilSlice() Slice { return Slice{nil, 0, 0} } +// NewSlice creates a new slice. +func NewSlice(data unsafe.Pointer, len, cap int) Slice { + return Slice{data, len, cap} +} + +// SliceLen returns the length of a slice. +func SliceLen(s Slice) int { + return s.len +} + // ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 7e3a310f..c9f17005 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -35,6 +35,11 @@ type Expr struct { Type } +// IsNil checks if the expression is nil or not. +func (v Expr) IsNil() bool { + return v.Type == nil +} + /* // TypeOf returns the type of the expression. func (v Expr) TypeOf() types.Type { @@ -67,16 +72,6 @@ func (p delayExprTy) String() string { // ----------------------------------------------------------------------------- -func llvmValues(vals []Expr) []llvm.Value { - ret := make([]llvm.Value, len(vals)) - for i, v := range vals { - ret[i] = v.impl - } - return ret -} - -// ----------------------------------------------------------------------------- - // Null returns a null constant expression. func (p Program) Null(t Type) Expr { return Expr{llvm.ConstNull(t.ll), t} @@ -142,9 +137,15 @@ func (b Builder) Const(v constant.Value, typ Type) Expr { case kind == types.Bool: return prog.BoolVal(constant.BoolVal(v)) case kind >= types.Int && kind <= types.Uintptr: - if v, exact := constant.Uint64Val(v); exact { - return prog.IntVal(v, typ) + if v, exact := constant.Int64Val(v); exact { + return prog.IntVal(uint64(v), typ) } + panic("todo") + /* + if v, exact := constant.Uint64Val(v); exact { + return prog.IntVal(v, typ) + } + */ case kind == types.Float32 || kind == types.Float64: if v, exact := constant.Float64Val(v); exact { return prog.FloatVal(v, typ) @@ -153,12 +154,12 @@ func (b Builder) Const(v constant.Value, typ Type) Expr { return prog.StringVal(constant.StringVal(v)) } } - panic("todo") + panic(fmt.Sprintf("unsupported Const: %v, %v", v, typ.t)) } -// 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()} +// CStr returns a c-style string constant expression. +func (b Builder) CStr(v string) Expr { + return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()} } // ----------------------------------------------------------------------------- @@ -321,6 +322,43 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr { panic("todo") } +// ----------------------------------------------------------------------------- + +func llvmValues(vals []Expr) []llvm.Value { + ret := make([]llvm.Value, len(vals)) + for i, v := range vals { + ret[i] = v.impl + } + return ret +} + +func llvmBlocks(bblks []BasicBlock) []llvm.BasicBlock { + ret := make([]llvm.BasicBlock, len(bblks)) + for i, v := range bblks { + ret[i] = v.impl + } + return ret +} + +// Phi represents a phi node. +type Phi struct { + Expr +} + +// AddIncoming adds incoming values to a phi node. +func (p Phi) AddIncoming(vals []Expr, bblks []BasicBlock) { + v := llvmValues(vals) + b := llvmBlocks(bblks) + p.impl.AddIncoming(v, b) +} + +// Phi returns a phi node. +func (b Builder) Phi(t Type) Phi { + return Phi{Expr{llvm.CreatePHI(b.impl, t.ll), t}} +} + +// ----------------------------------------------------------------------------- + // Load returns the value at the pointer ptr. func (b Builder) Load(ptr Expr) Expr { if debugInstr { @@ -392,6 +430,45 @@ func (b Builder) IndexAddr(x, idx Expr) Expr { return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt} } +// The Slice instruction yields a slice of an existing string, slice +// or *array X between optional integer bounds Low and High. +// +// Dynamically, this instruction panics if X evaluates to a nil *array +// pointer. +// +// Type() returns string if the type of X was string, otherwise a +// *types.Slice with the same element type as X. +// +// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice +// operation, the ast.CompositeLit.Lbrace if created by a literal, or +// NoPos if not explicit in the source (e.g. a variadic argument slice). +// +// Example printed form: +// +// t1 = slice t0[1:] +func (b Builder) Slice(x, low, high, max Expr) (ret Expr) { + if debugInstr { + log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl) + } + prog := b.Prog + pkg := b.fn.pkg + switch t := x.t.Underlying().(type) { + case *types.Pointer: + telem := t.Elem() + switch te := telem.Underlying().(type) { + case *types.Array: + ret.Type = prog.Type(types.NewSlice(te.Elem())) + if low.IsNil() && high.IsNil() && max.IsNil() { + n := prog.Val(int(te.Len())) + return b.InlineCall(pkg.rtFunc("NewSlice"), n, n) + } + } + } + panic("todo") +} + +// ----------------------------------------------------------------------------- + // The Alloc instruction reserves space for a variable of the given type, // zero-initializes it, and yields its address. // @@ -453,6 +530,8 @@ func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) { } */ +// ----------------------------------------------------------------------------- + // The ChangeType instruction applies to X a value-preserving type // change to Type(). // @@ -713,6 +792,16 @@ func (b Builder) Call(fn Expr, args ...Expr) (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) { + switch fn { + case "len": + if len(args) == 1 { + arg := args[0] + switch arg.t.Underlying().(type) { + case *types.Slice: + return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg) + } + } + } panic("todo") } diff --git a/ssa/package.go b/ssa/package.go index c2961ef7..b6134196 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -225,7 +225,7 @@ func (p Program) Bool() Type { return p.boolTy } -func (p Program) CString() Type { +func (p Program) CStr() Type { if p.cstrTy == nil { // *int8 p.cstrTy = p.Type(types.NewPointer(types.Typ[types.Int8])) } diff --git a/ssa/type.go b/ssa/type.go index 6b02513a..aef4c354 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -104,6 +104,10 @@ type aType struct { type Type = *aType +func (p Program) Slice(typ Type) Type { + return p.Type(types.NewSlice(typ.t)) +} + func (p Program) Pointer(typ Type) Type { return p.Type(types.NewPointer(typ.t)) }