Skip to content

Commit

Permalink
[dev.ssa] cmd/compile: handle Div, Convert, GetClosurePtr etc. on ARM
Browse files Browse the repository at this point in the history
This CL adds support of Div, Mod, Convert, GetClosurePtr and 64-bit indexing
support to SSA backend for ARM.

Add tests for 64-bit indexing to cmd/compile/internal/gc/testdata/string_ssa.go.

Tests cmd/compile/internal/gc/testdata/*_ssa.go passed, except compound_ssa.go
and fp_ssa.go.

Progress on SSA for ARM. Still not complete. Essentially the only unsupported
part is floating point.

Updates #15365.

Change-Id: I269e88b67f641c25e7a813d910c96d356d236bff
Reviewed-on: https://go-review.googlesource.com/23542
Reviewed-by: David Chase <[email protected]>
  • Loading branch information
cherrymui committed Jun 5, 2016
1 parent 4636d02 commit e78d90b
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 26 deletions.
24 changes: 23 additions & 1 deletion src/cmd/compile/internal/arm/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// input args need no code
case ssa.OpSP, ssa.OpSB:
// nothing to do
case ssa.OpCopy:
case ssa.OpCopy, ssa.OpARMMOVWconvert:
if v.Type.IsMemory() {
return
}
Expand Down Expand Up @@ -148,6 +148,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
} else {
p.To.Name = obj.NAME_AUTO
}
case ssa.OpARMDIV,
ssa.OpARMDIVU,
ssa.OpARMMOD,
ssa.OpARMMODU:
// Note: for software division the assembler rewrite these
// instructions to sequence of instructions:
// - it puts numerator in R11 and denominator in g.m.divmod
// and call (say) _udiv
// - _udiv saves R0-R3 on stack and call udiv, restores R0-R3
// before return
// - udiv does the actual work
//TODO: set approperiate regmasks and call udiv directly?
// need to be careful for negative case
// Or, as soft div is already expensive, we don't care?
fallthrough
case ssa.OpARMADD,
ssa.OpARMADC,
ssa.OpARMSUB,
Expand Down Expand Up @@ -552,6 +567,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
ssa.OpARMLoweredSelect0,
ssa.OpARMLoweredSelect1:
// nothing to do
case ssa.OpARMLoweredGetClosurePtr:
// Output is hardwired to R7 (arm.REGCTXT) only,
// and R7 contains the closure pointer on
// closure entry, and this "instruction"
// is scheduled to the very beginning
// of the entry block.
// nothing to do here.
default:
v.Unimplementedf("genValue not implemented: %s", v.LongString())
}
Expand Down
39 changes: 21 additions & 18 deletions src/cmd/compile/internal/gc/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -1945,7 +1945,7 @@ func (s *state) expr(n *Node) *ssa.Value {
case n.Left.Type.IsString():
a := s.expr(n.Left)
i := s.expr(n.Right)
i = s.extendIndex(i)
i = s.extendIndex(i, Panicindex)
if !n.Bounded {
len := s.newValue1(ssa.OpStringLen, Types[TINT], a)
s.boundsCheck(i, len)
Expand Down Expand Up @@ -2034,13 +2034,13 @@ func (s *state) expr(n *Node) *ssa.Value {
var i, j, k *ssa.Value
low, high, max := n.SliceBounds()
if low != nil {
i = s.extendIndex(s.expr(low))
i = s.extendIndex(s.expr(low), panicslice)
}
if high != nil {
j = s.extendIndex(s.expr(high))
j = s.extendIndex(s.expr(high), panicslice)
}
if max != nil {
k = s.extendIndex(s.expr(max))
k = s.extendIndex(s.expr(max), panicslice)
}
p, l, c := s.slice(n.Left.Type, v, i, j, k)
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
Expand All @@ -2050,10 +2050,10 @@ func (s *state) expr(n *Node) *ssa.Value {
var i, j *ssa.Value
low, high, _ := n.SliceBounds()
if low != nil {
i = s.extendIndex(s.expr(low))
i = s.extendIndex(s.expr(low), panicslice)
}
if high != nil {
j = s.extendIndex(s.expr(high))
j = s.extendIndex(s.expr(high), panicslice)
}
p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
return s.newValue2(ssa.OpStringMake, n.Type, p, l)
Expand Down Expand Up @@ -2743,7 +2743,7 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
if n.Left.Type.IsSlice() {
a := s.expr(n.Left)
i := s.expr(n.Right)
i = s.extendIndex(i)
i = s.extendIndex(i, Panicindex)
len := s.newValue1(ssa.OpSliceLen, Types[TINT], a)
if !n.Bounded {
s.boundsCheck(i, len)
Expand All @@ -2753,7 +2753,7 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
} else { // array
a := s.addr(n.Left, bounded)
i := s.expr(n.Right)
i = s.extendIndex(i)
i = s.extendIndex(i, Panicindex)
len := s.constInt(Types[TINT], n.Left.Type.NumElem())
if !n.Bounded {
s.boundsCheck(i, len)
Expand Down Expand Up @@ -2894,12 +2894,11 @@ func (s *state) nilCheck(ptr *ssa.Value) {

// boundsCheck generates bounds checking code. Checks if 0 <= idx < len, branches to exit if not.
// Starts a new block on return.
// idx is already converted to full int width.
func (s *state) boundsCheck(idx, len *ssa.Value) {
if Debug['B'] != 0 {
return
}
// TODO: convert index to full width?
// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.

// bounds check
cmp := s.newValue2(ssa.OpIsInBounds, Types[TBOOL], idx, len)
Expand All @@ -2908,19 +2907,18 @@ func (s *state) boundsCheck(idx, len *ssa.Value) {

// sliceBoundsCheck generates slice bounds checking code. Checks if 0 <= idx <= len, branches to exit if not.
// Starts a new block on return.
// idx and len are already converted to full int width.
func (s *state) sliceBoundsCheck(idx, len *ssa.Value) {
if Debug['B'] != 0 {
return
}
// TODO: convert index to full width?
// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.

// bounds check
cmp := s.newValue2(ssa.OpIsSliceInBounds, Types[TBOOL], idx, len)
s.check(cmp, panicslice)
}

// If cmp (a bool) is true, panic using the given function.
// If cmp (a bool) is false, panic using the given function.
func (s *state) check(cmp *ssa.Value, fn *Node) {
b := s.endBlock()
b.Kind = ssa.BlockIf
Expand Down Expand Up @@ -4134,16 +4132,21 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
}

// extendIndex extends v to a full int width.
func (s *state) extendIndex(v *ssa.Value) *ssa.Value {
// panic using the given function if v does not fit in an int (only on 32-bit archs).
func (s *state) extendIndex(v *ssa.Value, panicfn *Node) *ssa.Value {
size := v.Type.Size()
if size == s.config.IntSize {
return v
}
if size > s.config.IntSize {
// TODO: truncate 64-bit indexes on 32-bit pointer archs. We'd need to test
// the high word and branch to out-of-bounds failure if it is not 0.
s.Unimplementedf("64->32 index truncation not implemented")
return v
// truncate 64-bit indexes on 32-bit pointer archs. Test the
// high word and branch to out-of-bounds failure if it is not 0.
if Debug['B'] == 0 {
hi := s.newValue1(ssa.OpInt64Hi, Types[TUINT32], v)
cmp := s.newValue2(ssa.OpEq32, Types[TBOOL], hi, s.constInt32(Types[TUINT32], 0))
s.check(cmp, panicfn)
}
return s.newValue1(ssa.OpTrunc64to32, Types[TINT], v)
}

// Extend value to the required size
Expand Down
64 changes: 64 additions & 0 deletions src/cmd/compile/internal/gc/testdata/string_ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,67 @@ func testSmallIndexType() {
}
}

//go:noinline
func testInt64Index_ssa(s string, i int64) byte {
return s[i]
}

//go:noinline
func testInt64Slice_ssa(s string, i, j int64) string {
return s[i:j]
}

func testInt64Index() {
tests := []struct {
i int64
j int64
b byte
s string
}{
{0, 5, 'B', "Below"},
{5, 10, 'E', "Exact"},
{10, 15, 'A', "Above"},
}

str := "BelowExactAbove"
for i, t := range tests {
if got := testInt64Index_ssa(str, t.i); got != t.b {
println("#", i, "got ", got, ", wanted", t.b)
failed = true
}
if got := testInt64Slice_ssa(str, t.i, t.j); got != t.s {
println("#", i, "got ", got, ", wanted", t.s)
failed = true
}
}
}

func testInt64IndexPanic() {
defer func() {
if r := recover(); r != nil {
println("paniced as expected")
}
}()

str := "foobar"
println("got ", testInt64Index_ssa(str, 1<<32+1))
println("expected to panic, but didn't")
failed = true
}

func testInt64SlicePanic() {
defer func() {
if r := recover(); r != nil {
println("paniced as expected")
}
}()

str := "foobar"
println("got ", testInt64Slice_ssa(str, 1<<32, 1<<32+1))
println("expected to panic, but didn't")
failed = true
}

//go:noinline
func testStringElem_ssa(s string, i int) byte {
return s[i]
Expand Down Expand Up @@ -153,6 +214,9 @@ func main() {
testSmallIndexType()
testStringElem()
testStringElemConst()
testInt64Index()
testInt64IndexPanic()
testInt64SlicePanic()

if failed {
panic("failed")
Expand Down
17 changes: 17 additions & 0 deletions src/cmd/compile/internal/ssa/gen/ARM.rules
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@

(Mul32uhilo x y) -> (MULLU x y)

(Div32 x y) -> (DIV x y)
(Div32u x y) -> (DIVU x y)
(Div16 x y) -> (DIV (SignExt16to32 x) (SignExt16to32 y))
(Div16u x y) -> (DIVU (ZeroExt16to32 x) (ZeroExt16to32 y))
(Div8 x y) -> (DIV (SignExt8to32 x) (SignExt8to32 y))
(Div8u x y) -> (DIVU (ZeroExt8to32 x) (ZeroExt8to32 y))

(Mod32 x y) -> (MOD x y)
(Mod32u x y) -> (MODU x y)
(Mod16 x y) -> (MOD (SignExt16to32 x) (SignExt16to32 y))
(Mod16u x y) -> (MODU (ZeroExt16to32 x) (ZeroExt16to32 y))
(Mod8 x y) -> (MOD (SignExt8to32 x) (SignExt8to32 y))
(Mod8u x y) -> (MODU (ZeroExt8to32 x) (ZeroExt8to32 y))

(And32 x y) -> (AND x y)
(And16 x y) -> (AND x y)
(And8 x y) -> (AND x y)
Expand Down Expand Up @@ -278,6 +292,9 @@
(Select0 <t> x) && !t.IsFlags() -> (LoweredSelect0 x)
(Select1 x) -> (LoweredSelect1 x)

(GetClosurePtr) -> (LoweredGetClosurePtr)
(Convert x mem) -> (MOVWconvert x mem)

// Absorb pseudo-ops into blocks.
(If (Equal cc) yes no) -> (EQ cc yes no)
(If (NotEqual cc) yes no) -> (NE cc yes no)
Expand Down
18 changes: 17 additions & 1 deletion src/cmd/compile/internal/ssa/gen/ARMOps.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func init() {
gpsp = gp | buildReg("SP")
gpspsb = gpsp | buildReg("SB")
flags = buildReg("FLAGS")
callerSave = gp
callerSave = gp | flags
)
// Common regInfo
var (
Expand Down Expand Up @@ -101,6 +101,10 @@ func init() {
{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true}, // arg0 * arg1
{name: "HMUL", argLength: 2, reg: gp21, asm: "MULL", commutative: true}, // (arg0 * arg1) >> 32, signed
{name: "HMULU", argLength: 2, reg: gp21, asm: "MULLU", commutative: true}, // (arg0 * arg1) >> 32, unsigned
{name: "DIV", argLength: 2, reg: gp21cf, asm: "DIV"}, // arg0 / arg1, signed, soft div clobbers flags
{name: "DIVU", argLength: 2, reg: gp21cf, asm: "DIVU"}, // arg0 / arg1, unsighed
{name: "MOD", argLength: 2, reg: gp21cf, asm: "MOD"}, // arg0 % arg1, signed
{name: "MODU", argLength: 2, reg: gp21cf, asm: "MODU"}, // arg0 % arg1, unsigned

{name: "ADDS", argLength: 2, reg: gp21cf, asm: "ADD", commutative: true}, // arg0 + arg1, set carry flag
{name: "ADC", argLength: 3, reg: gp2flags1, asm: "ADC", commutative: true}, // arg0 + arg1 + carry, arg2=flags
Expand Down Expand Up @@ -251,6 +255,18 @@ func init() {
clobbers: buildReg("R1 R2 FLAGS"),
},
},

// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
// and sorts it to the very beginning of the block to prevent other
// use of R7 (arm.REGCTXT, the closure pointer)
{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R7")}}},

// MOVWconvert converts between pointers and integers.
// We have a special op for this so as to not confuse GC
// (particularly stack maps). It takes a memory arg so it
// gets correctly ordered with respect to GC safepoints.
// arg0=ptr/int arg1=mem, output=int/ptr
{name: "MOVWconvert", argLength: 2, reg: gp11, asm: "MOVW"},
}

blocks := []blockData{
Expand Down
Loading

0 comments on commit e78d90b

Please sign in to comment.