diff --git a/_demo/chan/chan.go b/_demo/chan/chan.go new file mode 100644 index 00000000..46a21d68 --- /dev/null +++ b/_demo/chan/chan.go @@ -0,0 +1,11 @@ +package main + +func main() { + ch := make(chan int, 10) + println(ch, len(ch), cap(ch)) + go func() { + ch <- 100 + }() + n, ok := <-ch + println(n, ok) +} diff --git a/cl/_testgo/chan/in.go b/cl/_testgo/chan/in.go new file mode 100644 index 00000000..4748fabf --- /dev/null +++ b/cl/_testgo/chan/in.go @@ -0,0 +1,19 @@ +package main + +func main() { + ch := make(chan int, 10) + var v any = ch + println(ch, len(ch), cap(ch), v) + go func() { + ch <- 100 + }() + n := <-ch + println(n) + + ch2 := make(chan int, 10) + go func() { + close(ch2) + }() + n2, ok := <-ch2 + println(n2, ok) +} diff --git a/cl/_testgo/chan/out.ll b/cl/_testgo/chan/out.ll new file mode 100644 index 00000000..1c8a0e79 --- /dev/null +++ b/cl/_testgo/chan/out.ll @@ -0,0 +1 @@ +; \ No newline at end of file diff --git a/cl/compile.go b/cl/compile.go index 23383f5d..2feccb78 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -459,7 +459,11 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue ret = b.BinOp(v.Op, x, y) case *ssa.UnOp: x := p.compileValue(b, v.X) - ret = b.UnOp(v.Op, x) + if v.Op == token.ARROW { + ret = b.Recv(x, v.CommaOk) + } else { + ret = b.UnOp(v.Op, x) + } case *ssa.ChangeType: t := v.Type() x := p.compileValue(b, v.X) @@ -579,6 +583,10 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue case *ssa.Field: x := p.compileValue(b, v.X) ret = b.Field(x, v.Field) + case *ssa.MakeChan: + t := v.Type() + size := p.compileValue(b, v.Size) + ret = b.MakeChan(p.prog.Type(t, llssa.InGo), size) default: panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) } @@ -651,6 +659,10 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { case *ssa.Panic: arg := p.compileValue(b, v.X) b.Panic(arg) + case *ssa.Send: + ch := p.compileValue(b, v.Chan) + x := p.compileValue(b, v.X) + b.Send(ch, x) default: panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr)) } diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index ec19a872..fdb09c99 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -192,4 +192,20 @@ func ArrayOf(length uintptr, elem *Type) *Type { return &ret.Type } +func ChanOf(dir int, strChan string, elem *Type) *Type { + ret := &abi.ChanType{ + Type: Type{ + Size_: 8, + Hash: uint32(abi.Chan), + Align_: pointerAlign, + FieldAlign_: pointerAlign, + Kind_: uint8(abi.Chan), + Str_: strChan + " " + elem.String(), + }, + Elem: elem, + Dir: abi.ChanDir(dir), + } + return &ret.Type +} + // ----------------------------------------------------------------------------- diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 90186b57..38a5db75 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -65,6 +65,18 @@ func UnderlyingKind(t types.Type) abi.Kind { panic("todo") } +func ChanDir(dir types.ChanDir) (abi.ChanDir, string) { + switch dir { + case types.SendRecv: + return abi.BothDir, "chan" + case types.SendOnly: + return abi.SendDir, "chan->" + case types.RecvOnly: + return abi.RecvDir, "<-chan" + } + panic("invlid chan dir") +} + // ----------------------------------------------------------------------------- type DataKind int @@ -165,6 +177,18 @@ func (b *Builder) TypeName(t types.Type) (ret string, pub bool) { key, pub1 := b.TypeName(t.Key()) elem, pub2 := b.TypeName(t.Elem()) return fmt.Sprintf("map[%s]%s", key, elem), pub1 && pub2 + case *types.Chan: + elem, pub := b.TypeName(t.Elem()) + var s string + switch t.Dir() { + case types.SendRecv: + s = "chan" + case types.SendOnly: + s = "chan<-" + case types.RecvOnly: + s = "<-chan" + } + return fmt.Sprintf("%s %s", s, elem), pub } log.Panicf("todo: %T\n", t) return diff --git a/ssa/abitype.go b/ssa/abitype.go index 940a779f..f45c25e3 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -68,6 +68,8 @@ func (b Builder) abiTypeOf(t types.Type) func() Expr { return b.abiSliceOf(t) case *types.Array: return b.abiArrayOf(t) + case *types.Chan: + return b.abiChanOf(t) } panic("todo") } @@ -274,6 +276,14 @@ func (b Builder) abiArrayOf(t *types.Array) func() Expr { } } +func (b Builder) abiChanOf(t *types.Chan) func() Expr { + elem := b.abiTypeOf(t.Elem()) + return func() Expr { + dir, s := abi.ChanDir(t.Dir()) + return b.Call(b.Pkg.rtFunc("ChanOf"), b.Prog.IntVal(uint64(dir), b.Prog.Int()), b.Str(s), elem()) + } +} + // func StructField(name string, typ *abi.Type, off uintptr, tag string, embedded bool) // func Struct(pkgPath string, size uintptr, fields []abi.StructField) func (b Builder) abiStructOf(t *types.Struct) func() Expr { diff --git a/ssa/datastruct.go b/ssa/datastruct.go index 9ce447fa..2dd0a7f8 100644 --- a/ssa/datastruct.go +++ b/ssa/datastruct.go @@ -540,4 +540,75 @@ func (b Builder) Next(iter Expr, isString bool) (ret Expr) { panic("todo") } +// The MakeChan instruction creates a new channel object and yields a +// value of kind chan. +// +// Type() returns a (possibly named) *types.Chan. +// +// Pos() returns the ast.CallExpr.Lparen for the make(chan) that +// created it. +// +// Example printed form: +// +// t0 = make chan int 0 +// t0 = make IntChan 0 +// +// type MakeChan struct { +// register +// Size Value // int; size of buffer; zero => synchronous. +// } +func (b Builder) MakeChan(t Type, size Expr) (ret Expr) { + if debugInstr { + log.Printf("MakeChan %v, %v\n", t.RawType(), size.impl) + } + prog := b.Prog + eltSize := prog.IntVal(prog.SizeOf(prog.Elem(t)), prog.Int()) + ret.Type = t + ret.impl = b.InlineCall(b.Pkg.rtFunc("NewChan"), eltSize, size).impl + return +} + +// The Send instruction sends X on channel Chan. +// +// Pos() returns the ast.SendStmt.Arrow, if explicit in the source. +// +// Example printed form: +// +// send t0 <- t1 +func (b Builder) Send(ch Expr, x Expr) (ret Expr) { + if debugInstr { + log.Printf("Send %v, %v\n", ch.impl, x.impl) + } + prog := b.Prog + eltSize := prog.IntVal(prog.SizeOf(prog.Elem(ch.Type)), prog.Int()) + ret = b.InlineCall(b.Pkg.rtFunc("ChanSend"), ch, b.toPtr(x), eltSize) + return +} + +func (b Builder) toPtr(x Expr) Expr { + typ := x.Type + vtyp := b.Prog.VoidPtr() + vptr := b.Alloc(typ, false) + b.Store(vptr, x) + return Expr{vptr.impl, vtyp} +} + +func (b Builder) Recv(ch Expr, commaOk bool) (ret Expr) { + if debugInstr { + log.Printf("Recv %v, %v\n", ch.impl, commaOk) + } + prog := b.Prog + eltSize := prog.IntVal(prog.SizeOf(prog.Elem(ch.Type)), prog.Int()) + etyp := prog.Elem(ch.Type) + ptr := b.Alloc(etyp, false) + ok := b.InlineCall(b.Pkg.rtFunc("ChanRecv"), ch, ptr, eltSize) + if commaOk { + val := b.Load(ptr) + t := prog.Struct(etyp, prog.Bool()) + return b.aggregateValue(t, val.impl, ok.impl) + } else { + return b.Load(ptr) + } +} + // ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 227b920b..eb566020 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1001,6 +1001,8 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { return b.SliceLen(arg) case vkString: return b.StringLen(arg) + case vkChan: + return b.InlineCall(b.Pkg.rtFunc("ChanLen"), arg) } } case "cap": @@ -1009,6 +1011,8 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { switch arg.kind { case vkSlice: return b.SliceCap(arg) + case vkChan: + return b.InlineCall(b.Pkg.rtFunc("ChanCap"), arg) } } case "append": @@ -1047,6 +1051,14 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { } } } + case "close": + if len(args) == 1 { + arg := args[0] + switch arg.kind { + case vkChan: + return b.InlineCall(b.Pkg.rtFunc("ChanClose"), arg) + } + } case "recover": return b.Recover() case "print", "println": @@ -1114,6 +1126,9 @@ func (b Builder) PrintEx(ln bool, args ...Expr) (ret Expr) { case vkComplex: fn = "PrintComplex" typ = prog.Complex128() + case vkChan: + fn = "PrintPointer" + typ = prog.VoidPtr() default: panic(fmt.Errorf("illegal types for operand: print %v", arg.RawType())) } diff --git a/ssa/package.go b/ssa/package.go index fc5a404a..b7e8af96 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -133,6 +133,7 @@ type aProgram struct { rtIfaceTy llvm.Type rtSliceTy llvm.Type rtMapTy llvm.Type + rtChanTy llvm.Type anyTy Type voidTy Type @@ -287,6 +288,13 @@ func (p Program) rtString() llvm.Type { return p.rtStringTy } +func (p Program) rtChan() llvm.Type { + if p.rtChanTy.IsNil() { + p.rtChanTy = p.rtType("Chan").ll + } + return p.rtChanTy +} + func (p Program) tyComplex64() llvm.Type { if p.c64Type.IsNil() { ctx := p.ctx diff --git a/ssa/type.go b/ssa/type.go index 16a98dd6..4dcf2592 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -57,6 +57,7 @@ const ( vkEface vkIface vkStruct + vkChan ) // ----------------------------------------------------------------------------- @@ -371,6 +372,7 @@ func (p Program) toType(raw types.Type) Type { elem := p.rawType(t.Elem()) return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkArray} case *types.Chan: + return &aType{llvm.PointerType(p.rtChan(), 0), typ, vkChan} } panic(fmt.Sprintf("toLLVMType: todo - %T\n", raw)) }