mirror of
https://github.com/golang/go
synced 2024-10-05 16:41:21 -06:00
[dev.ssa] cmd/compile/internal/ssa: implement slice opcodes
Implement OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR. reviewer: Ignore the code in OINDEX, that's from CL 14466. Change-Id: I00cc8aecd4c6f40ea5517cd660bb0ce759d91171 Reviewed-on: https://go-review.googlesource.com/14538 Reviewed-by: Todd Neal <todd@tneal.org>
This commit is contained in:
parent
c7081409bb
commit
5505e8ccc7
@ -240,6 +240,9 @@ func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimp
|
||||
// dummy node for the memory variable
|
||||
var memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}}
|
||||
|
||||
// dummy nodes for temporary variables
|
||||
var ptrvar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}}
|
||||
|
||||
// startBlock sets the current block we're generating code in to b.
|
||||
func (s *state) startBlock(b *ssa.Block) {
|
||||
if s.curBlock != nil {
|
||||
@ -1747,70 +1750,38 @@ func (s *state) expr(n *Node) *ssa.Value {
|
||||
data := s.expr(n.Right)
|
||||
return s.newValue2(ssa.OpIMake, n.Type, tab, data)
|
||||
|
||||
case OSLICE, OSLICEARR:
|
||||
v := s.expr(n.Left)
|
||||
var i, j *ssa.Value
|
||||
if n.Right.Left != nil {
|
||||
i = s.extendIndex(s.expr(n.Right.Left))
|
||||
}
|
||||
if n.Right.Right != nil {
|
||||
j = s.extendIndex(s.expr(n.Right.Right))
|
||||
}
|
||||
p, l, c := s.slice(n.Left.Type, v, i, j, nil)
|
||||
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
|
||||
case OSLICESTR:
|
||||
// Evaluate the string once.
|
||||
str := s.expr(n.Left)
|
||||
ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), str)
|
||||
len := s.newValue1(ssa.OpStringLen, Types[TINT], str)
|
||||
zero := s.constInt(Types[TINT], 0)
|
||||
|
||||
// Evaluate the slice indexes.
|
||||
var low, high *ssa.Value
|
||||
if n.Right.Left == nil {
|
||||
low = zero
|
||||
} else {
|
||||
low = s.extendIndex(s.expr(n.Right.Left))
|
||||
v := s.expr(n.Left)
|
||||
var i, j *ssa.Value
|
||||
if n.Right.Left != nil {
|
||||
i = s.extendIndex(s.expr(n.Right.Left))
|
||||
}
|
||||
if n.Right.Right == nil {
|
||||
high = len
|
||||
} else {
|
||||
high = s.extendIndex(s.expr(n.Right.Right))
|
||||
if n.Right.Right != nil {
|
||||
j = s.extendIndex(s.expr(n.Right.Right))
|
||||
}
|
||||
|
||||
// Panic if slice indices are not in bounds.
|
||||
s.sliceBoundsCheck(low, high)
|
||||
s.sliceBoundsCheck(high, len)
|
||||
|
||||
// Generate the following code assuming that indexes are in bounds.
|
||||
// The conditional is to make sure that we don't generate a string
|
||||
// that points to the next object in memory.
|
||||
// rlen = (SubPtr high low)
|
||||
// p = ptr
|
||||
// if rlen != 0 {
|
||||
// p = (AddPtr ptr low)
|
||||
// }
|
||||
// result = (StringMake p size)
|
||||
rlen := s.newValue2(ssa.OpSubPtr, Types[TINT], high, low)
|
||||
|
||||
// Use n as the "variable" for p.
|
||||
s.vars[n] = ptr
|
||||
|
||||
// Generate code to test the resulting slice length.
|
||||
var cmp *ssa.Value
|
||||
if s.config.IntSize == 8 {
|
||||
cmp = s.newValue2(ssa.OpNeq64, Types[TBOOL], rlen, zero)
|
||||
} else {
|
||||
cmp = s.newValue2(ssa.OpNeq32, Types[TBOOL], rlen, zero)
|
||||
p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
|
||||
return s.newValue2(ssa.OpStringMake, n.Type, p, l)
|
||||
case OSLICE3, OSLICE3ARR:
|
||||
v := s.expr(n.Left)
|
||||
var i *ssa.Value
|
||||
if n.Right.Left != nil {
|
||||
i = s.extendIndex(s.expr(n.Right.Left))
|
||||
}
|
||||
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.Likely = ssa.BranchLikely
|
||||
b.Control = cmp
|
||||
|
||||
// Generate code for non-zero length slice case.
|
||||
nz := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(nz)
|
||||
s.startBlock(nz)
|
||||
s.vars[n] = s.newValue2(ssa.OpAddPtr, Ptrto(Types[TUINT8]), ptr, low)
|
||||
s.endBlock()
|
||||
|
||||
// All done.
|
||||
merge := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(merge)
|
||||
nz.AddEdgeTo(merge)
|
||||
s.startBlock(merge)
|
||||
return s.newValue2(ssa.OpStringMake, Types[TSTRING], s.variable(n, Ptrto(Types[TUINT8])), rlen)
|
||||
j := s.extendIndex(s.expr(n.Right.Right.Left))
|
||||
k := s.extendIndex(s.expr(n.Right.Right.Right))
|
||||
p, l, c := s.slice(n.Left.Type, v, i, j, k)
|
||||
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
|
||||
|
||||
case OCALLFUNC, OCALLMETH:
|
||||
left := n.Left
|
||||
@ -2201,6 +2172,125 @@ func (s *state) check(cmp *ssa.Value, panicOp ssa.Op) {
|
||||
s.startBlock(bNext)
|
||||
}
|
||||
|
||||
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
|
||||
// i,j,k may be nil, in which case they are set to their default value.
|
||||
// t is a slice, ptr to array, or string type.
|
||||
func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
|
||||
var elemtype *Type
|
||||
var ptrtype *Type
|
||||
var ptr *ssa.Value
|
||||
var len *ssa.Value
|
||||
var cap *ssa.Value
|
||||
zero := s.constInt(Types[TINT], 0)
|
||||
switch {
|
||||
case t.IsSlice():
|
||||
elemtype = t.Type
|
||||
ptrtype = Ptrto(elemtype)
|
||||
ptr = s.newValue1(ssa.OpSlicePtr, ptrtype, v)
|
||||
len = s.newValue1(ssa.OpSliceLen, Types[TINT], v)
|
||||
cap = s.newValue1(ssa.OpSliceCap, Types[TINT], v)
|
||||
case t.IsString():
|
||||
elemtype = Types[TUINT8]
|
||||
ptrtype = Ptrto(elemtype)
|
||||
ptr = s.newValue1(ssa.OpStringPtr, ptrtype, v)
|
||||
len = s.newValue1(ssa.OpStringLen, Types[TINT], v)
|
||||
cap = len
|
||||
case t.IsPtr():
|
||||
if !t.Type.IsArray() {
|
||||
s.Fatalf("bad ptr to array in slice %v\n", t)
|
||||
}
|
||||
elemtype = t.Type.Type
|
||||
ptrtype = Ptrto(elemtype)
|
||||
s.nilCheck(v)
|
||||
ptr = v
|
||||
len = s.constInt(Types[TINT], t.Type.Bound)
|
||||
cap = len
|
||||
default:
|
||||
s.Fatalf("bad type in slice %v\n", t)
|
||||
}
|
||||
|
||||
// Set default values
|
||||
if i == nil {
|
||||
i = zero
|
||||
}
|
||||
if j == nil {
|
||||
j = len
|
||||
}
|
||||
if k == nil {
|
||||
k = cap
|
||||
}
|
||||
|
||||
// Panic if slice indices are not in bounds.
|
||||
s.sliceBoundsCheck(i, j)
|
||||
if j != k {
|
||||
s.sliceBoundsCheck(j, k)
|
||||
}
|
||||
if k != cap {
|
||||
s.sliceBoundsCheck(k, cap)
|
||||
}
|
||||
|
||||
// Generate the following code assuming that indexes are in bounds.
|
||||
// The conditional is to make sure that we don't generate a slice
|
||||
// that points to the next object in memory.
|
||||
// rlen = (SubPtr j i)
|
||||
// rcap = (SubPtr k i)
|
||||
// p = ptr
|
||||
// if rcap != 0 {
|
||||
// p = (AddPtr ptr (MulPtr low (ConstPtr size)))
|
||||
// }
|
||||
// result = (SliceMake p size)
|
||||
rlen := s.newValue2(ssa.OpSubPtr, Types[TINT], j, i)
|
||||
var rcap *ssa.Value
|
||||
switch {
|
||||
case t.IsString():
|
||||
// Capacity of the result is unimportant. However, we use
|
||||
// rcap to test if we've generated a zero-length slice.
|
||||
// Use length of strings for that.
|
||||
rcap = rlen
|
||||
case j == k:
|
||||
rcap = rlen
|
||||
default:
|
||||
rcap = s.newValue2(ssa.OpSubPtr, Types[TINT], k, i)
|
||||
}
|
||||
|
||||
s.vars[&ptrvar] = ptr
|
||||
|
||||
// Generate code to test the resulting slice length.
|
||||
var cmp *ssa.Value
|
||||
if s.config.IntSize == 8 {
|
||||
cmp = s.newValue2(ssa.OpNeq64, Types[TBOOL], rcap, s.constInt(Types[TINT], 0))
|
||||
} else {
|
||||
cmp = s.newValue2(ssa.OpNeq32, Types[TBOOL], rcap, s.constInt(Types[TINT], 0))
|
||||
}
|
||||
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.Likely = ssa.BranchLikely
|
||||
b.Control = cmp
|
||||
|
||||
// Generate code for non-zero length slice case.
|
||||
nz := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(nz)
|
||||
s.startBlock(nz)
|
||||
var inc *ssa.Value
|
||||
if elemtype.Width == 1 {
|
||||
inc = i
|
||||
} else {
|
||||
inc = s.newValue2(ssa.OpMulPtr, Types[TUINTPTR], i, s.constInt(Types[TINT], elemtype.Width))
|
||||
}
|
||||
s.vars[&ptrvar] = s.newValue2(ssa.OpAddPtr, ptrtype, ptr, inc)
|
||||
s.endBlock()
|
||||
|
||||
// All done.
|
||||
merge := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(merge)
|
||||
nz.AddEdgeTo(merge)
|
||||
s.startBlock(merge)
|
||||
rptr := s.variable(&ptrvar, ptrtype)
|
||||
delete(s.vars, &ptrvar)
|
||||
return rptr, rlen, rcap
|
||||
}
|
||||
|
||||
type u2fcvtTab struct {
|
||||
geq, cvt2F, and, rsh, or, add ssa.Op
|
||||
one func(*state, ssa.Type, int64) *ssa.Value
|
||||
|
@ -81,3 +81,5 @@ func TestDeferNoReturn(t *testing.T) { buildTest(t, "deferNoReturn_ssa.go") }
|
||||
|
||||
// TestClosure tests closure related behavior.
|
||||
func TestClosure(t *testing.T) { runTest(t, "closure_ssa.go") }
|
||||
|
||||
func TestArray(t *testing.T) { runTest(t, "array_ssa.go") }
|
||||
|
147
src/cmd/compile/internal/gc/testdata/array_ssa.go
vendored
Normal file
147
src/cmd/compile/internal/gc/testdata/array_ssa.go
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
package main
|
||||
|
||||
var failed = false
|
||||
|
||||
func testSliceLenCap12_ssa(a [10]int, i, j int) (int, int) {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
b := a[i:j]
|
||||
return len(b), cap(b)
|
||||
}
|
||||
|
||||
func testSliceLenCap1_ssa(a [10]int, i, j int) (int, int) {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
b := a[i:]
|
||||
return len(b), cap(b)
|
||||
}
|
||||
|
||||
func testSliceLenCap2_ssa(a [10]int, i, j int) (int, int) {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
b := a[:j]
|
||||
return len(b), cap(b)
|
||||
}
|
||||
|
||||
func testSliceLenCap() {
|
||||
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
tests := [...]struct {
|
||||
fn func(a [10]int, i, j int) (int, int)
|
||||
i, j int // slice range
|
||||
l, c int // len, cap
|
||||
}{
|
||||
// -1 means the value is not used.
|
||||
{testSliceLenCap12_ssa, 0, 0, 0, 10},
|
||||
{testSliceLenCap12_ssa, 0, 1, 1, 10},
|
||||
{testSliceLenCap12_ssa, 0, 10, 10, 10},
|
||||
{testSliceLenCap12_ssa, 10, 10, 0, 0},
|
||||
{testSliceLenCap12_ssa, 0, 5, 5, 10},
|
||||
{testSliceLenCap12_ssa, 5, 5, 0, 5},
|
||||
{testSliceLenCap12_ssa, 5, 10, 5, 5},
|
||||
{testSliceLenCap1_ssa, 0, -1, 0, 10},
|
||||
{testSliceLenCap1_ssa, 5, -1, 5, 5},
|
||||
{testSliceLenCap1_ssa, 10, -1, 0, 0},
|
||||
{testSliceLenCap2_ssa, -1, 0, 0, 10},
|
||||
{testSliceLenCap2_ssa, -1, 5, 5, 10},
|
||||
{testSliceLenCap2_ssa, -1, 10, 10, 10},
|
||||
}
|
||||
|
||||
for i, t := range tests {
|
||||
if l, c := t.fn(a, t.i, t.j); l != t.l && c != t.c {
|
||||
println("#", i, " len(a[", t.i, ":", t.j, "]), cap(a[", t.i, ":", t.j, "]) =", l, c,
|
||||
", want", t.l, t.c)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSliceGetElement_ssa(a [10]int, i, j, p int) int {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
return a[i:j][p]
|
||||
}
|
||||
|
||||
func testSliceGetElement() {
|
||||
a := [10]int{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}
|
||||
tests := [...]struct {
|
||||
i, j, p int
|
||||
want int // a[i:j][p]
|
||||
}{
|
||||
{0, 10, 2, 20},
|
||||
{0, 5, 4, 40},
|
||||
{5, 10, 3, 80},
|
||||
{1, 9, 7, 80},
|
||||
}
|
||||
|
||||
for i, t := range tests {
|
||||
if got := testSliceGetElement_ssa(a, t.i, t.j, t.p); got != t.want {
|
||||
println("#", i, " a[", t.i, ":", t.j, "][", t.p, "] = ", got, " wanted ", t.want)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSliceSetElement_ssa(a *[10]int, i, j, p, x int) {
|
||||
switch { // prevent inlining
|
||||
}
|
||||
(*a)[i:j][p] = x
|
||||
}
|
||||
|
||||
func testSliceSetElement() {
|
||||
a := [10]int{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}
|
||||
tests := [...]struct {
|
||||
i, j, p int
|
||||
want int // a[i:j][p]
|
||||
}{
|
||||
{0, 10, 2, 17},
|
||||
{0, 5, 4, 11},
|
||||
{5, 10, 3, 28},
|
||||
{1, 9, 7, 99},
|
||||
}
|
||||
|
||||
for i, t := range tests {
|
||||
testSliceSetElement_ssa(&a, t.i, t.j, t.p, t.want)
|
||||
if got := a[t.i+t.p]; got != t.want {
|
||||
println("#", i, " a[", t.i, ":", t.j, "][", t.p, "] = ", got, " wanted ", t.want)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSlicePanic1() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
println("paniced as expected")
|
||||
}
|
||||
}()
|
||||
|
||||
a := [10]int{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}
|
||||
testSliceLenCap12_ssa(a, 3, 12)
|
||||
println("expected to panic, but didn't")
|
||||
failed = true
|
||||
}
|
||||
|
||||
func testSlicePanic2() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
println("paniced as expected")
|
||||
}
|
||||
}()
|
||||
|
||||
a := [10]int{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}
|
||||
testSliceGetElement_ssa(a, 3, 7, 4)
|
||||
println("expected to panic, but didn't")
|
||||
failed = true
|
||||
}
|
||||
|
||||
func main() {
|
||||
testSliceLenCap()
|
||||
testSliceGetElement()
|
||||
testSliceSetElement()
|
||||
testSlicePanic1()
|
||||
testSlicePanic2()
|
||||
|
||||
if failed {
|
||||
panic("failed")
|
||||
}
|
||||
}
|
@ -84,6 +84,10 @@ func (t *Type) IsSlice() bool {
|
||||
return t.Etype == TARRAY && t.Bound < 0
|
||||
}
|
||||
|
||||
func (t *Type) IsArray() bool {
|
||||
return t.Etype == TARRAY && t.Bound >= 0
|
||||
}
|
||||
|
||||
func (t *Type) IsInterface() bool {
|
||||
return t.Etype == TINTER
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ type Type interface {
|
||||
IsPtr() bool
|
||||
IsString() bool
|
||||
IsSlice() bool
|
||||
IsArray() bool
|
||||
IsInterface() bool
|
||||
|
||||
IsMemory() bool // special ssa-package-only types
|
||||
@ -50,6 +51,7 @@ func (t *CompilerType) IsComplex() bool { return false }
|
||||
func (t *CompilerType) IsPtr() bool { return false }
|
||||
func (t *CompilerType) IsString() bool { return false }
|
||||
func (t *CompilerType) IsSlice() bool { return false }
|
||||
func (t *CompilerType) IsArray() bool { return false }
|
||||
func (t *CompilerType) IsInterface() bool { return false }
|
||||
func (t *CompilerType) IsMemory() bool { return t.Memory }
|
||||
func (t *CompilerType) IsFlags() bool { return t.Flags }
|
||||
|
@ -16,6 +16,7 @@ type TypeImpl struct {
|
||||
Ptr bool
|
||||
string bool
|
||||
slice bool
|
||||
array bool
|
||||
inter bool
|
||||
Elem_ Type
|
||||
|
||||
@ -32,6 +33,7 @@ func (t *TypeImpl) IsComplex() bool { return t.Complex }
|
||||
func (t *TypeImpl) IsPtr() bool { return t.Ptr }
|
||||
func (t *TypeImpl) IsString() bool { return t.string }
|
||||
func (t *TypeImpl) IsSlice() bool { return t.slice }
|
||||
func (t *TypeImpl) IsArray() bool { return t.array }
|
||||
func (t *TypeImpl) IsInterface() bool { return t.inter }
|
||||
func (t *TypeImpl) IsMemory() bool { return false }
|
||||
func (t *TypeImpl) IsFlags() bool { return false }
|
||||
|
Loading…
Reference in New Issue
Block a user