return s.newValue1(op, Types[TINT], s.expr(n.Left))
case n.Left.Type.IsString(): // string; not reachable for OCAP
return s.newValue1(ssa.OpStringLen, Types[TINT], s.expr(n.Left))
- case n.Left.Type.IsMap():
- return s.lenMap(n, s.expr(n.Left))
- case n.Left.Type.IsChan():
- if n.Op == OCAP {
- s.Unimplementedf("unhandled cap(chan)")
- } else {
- s.Unimplementedf("unhandled len(chan)")
- }
- return nil
+ case n.Left.Type.IsMap(), n.Left.Type.IsChan():
+ return s.referenceTypeBuiltin(n, s.expr(n.Left))
default: // array
return s.constInt(Types[TINT], n.Left.Type.Bound)
}
return s.variable(n, n.Type)
}
-func (s *state) lenMap(n *Node, x *ssa.Value) *ssa.Value {
+// referenceTypeBuiltin generates code for the len/cap builtins for maps and channels.
+func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
+ if !n.Left.Type.IsMap() && !n.Left.Type.IsChan() {
+ s.Fatalf("node must be a map or a channel")
+ }
// if n == nil {
// return 0
// } else {
+ // // len
// return *((*int)n)
+ // // cap
+ // return *(((*int)n)+1)
// }
lenType := n.Type
nilValue := s.newValue0(ssa.OpConstNil, Types[TUINTPTR])
bElse := s.f.NewBlock(ssa.BlockPlain)
bAfter := s.f.NewBlock(ssa.BlockPlain)
- // length of a nil map is zero
+ // length/capacity of a nil map/chan is zero
addEdge(b, bThen)
s.startBlock(bThen)
s.vars[n] = s.zeroVal(lenType)
s.endBlock()
addEdge(bThen, bAfter)
- // the length is stored in the first word
addEdge(b, bElse)
s.startBlock(bElse)
- s.vars[n] = s.newValue2(ssa.OpLoad, lenType, x, s.mem())
+ if n.Op == OLEN {
+ // length is stored in the first word for map/chan
+ s.vars[n] = s.newValue2(ssa.OpLoad, lenType, x, s.mem())
+ } else if n.Op == OCAP {
+ // capacity is stored in the second word for chan
+ sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Width, x)
+ s.vars[n] = s.newValue2(ssa.OpLoad, lenType, sw, s.mem())
+ } else {
+ s.Fatalf("op must be OLEN or OCAP")
+ }
s.endBlock()
addEdge(bElse, bAfter)
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// chan_ssa.go tests chan operations.
+package main
+
+import "fmt"
+
+var failed = false
+
+func lenChan_ssa(v chan int) int {
+ switch { // prevent inlining
+
+ }
+ return len(v)
+}
+func capChan_ssa(v chan int) int {
+ switch { // prevent inlining
+
+ }
+ return cap(v)
+}
+
+func testLenChan() {
+
+ v := make(chan int, 10)
+ v <- 1
+ v <- 1
+ v <- 1
+
+ if want, got := 3, lenChan_ssa(v); got != want {
+ fmt.Printf("expected len(chan) = %d, got %d", want, got)
+ failed = true
+ }
+}
+
+func testLenNilChan() {
+
+ var v chan int
+ if want, got := 0, lenChan_ssa(v); got != want {
+ fmt.Printf("expected len(nil) = %d, got %d", want, got)
+ failed = true
+ }
+}
+
+func testCapChan() {
+
+ v := make(chan int, 25)
+
+ if want, got := 25, capChan_ssa(v); got != want {
+ fmt.Printf("expected cap(chan) = %d, got %d", want, got)
+ failed = true
+ }
+}
+
+func testCapNilChan() {
+
+ var v chan int
+ if want, got := 0, capChan_ssa(v); got != want {
+ fmt.Printf("expected cap(nil) = %d, got %d", want, got)
+ failed = true
+ }
+}
+
+func main() {
+ testLenChan()
+ testLenNilChan()
+
+ testCapChan()
+ testCapNilChan()
+
+ if failed {
+ panic("failed")
+ }
+}