2016-03-21 22:57:26 -07:00
|
|
|
// Copyright 2016 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.
|
|
|
|
|
|
|
|
|
|
package arm
|
|
|
|
|
|
|
|
|
|
import (
|
2016-06-17 10:34:06 -04:00
|
|
|
"fmt"
|
2016-05-31 11:27:16 -04:00
|
|
|
"math"
|
|
|
|
|
|
2016-03-21 22:57:26 -07:00
|
|
|
"cmd/compile/internal/gc"
|
|
|
|
|
"cmd/compile/internal/ssa"
|
|
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"cmd/internal/obj/arm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var ssaRegToReg = []int16{
|
|
|
|
|
arm.REG_R0,
|
|
|
|
|
arm.REG_R1,
|
|
|
|
|
arm.REG_R2,
|
|
|
|
|
arm.REG_R3,
|
2016-05-06 10:13:31 -07:00
|
|
|
arm.REG_R4,
|
|
|
|
|
arm.REG_R5,
|
|
|
|
|
arm.REG_R6,
|
|
|
|
|
arm.REG_R7,
|
|
|
|
|
arm.REG_R8,
|
|
|
|
|
arm.REG_R9,
|
2016-05-31 14:01:34 -04:00
|
|
|
arm.REGG, // aka R10
|
2016-05-06 10:13:31 -07:00
|
|
|
arm.REG_R11,
|
|
|
|
|
arm.REG_R12,
|
2016-03-21 22:57:26 -07:00
|
|
|
arm.REGSP, // aka R13
|
2016-05-06 10:13:31 -07:00
|
|
|
arm.REG_R14,
|
|
|
|
|
arm.REG_R15,
|
|
|
|
|
|
2016-05-31 11:27:16 -04:00
|
|
|
arm.REG_F0,
|
|
|
|
|
arm.REG_F1,
|
|
|
|
|
arm.REG_F2,
|
|
|
|
|
arm.REG_F3,
|
|
|
|
|
arm.REG_F4,
|
|
|
|
|
arm.REG_F5,
|
|
|
|
|
arm.REG_F6,
|
|
|
|
|
arm.REG_F7,
|
|
|
|
|
arm.REG_F8,
|
|
|
|
|
arm.REG_F9,
|
|
|
|
|
arm.REG_F10,
|
|
|
|
|
arm.REG_F11,
|
|
|
|
|
arm.REG_F12,
|
|
|
|
|
arm.REG_F13,
|
|
|
|
|
arm.REG_F14,
|
|
|
|
|
arm.REG_F15,
|
|
|
|
|
|
2016-05-06 10:13:31 -07:00
|
|
|
arm.REG_CPSR, // flag
|
|
|
|
|
0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case.
|
2016-03-21 22:57:26 -07:00
|
|
|
}
|
|
|
|
|
|
2016-06-24 15:06:17 -04:00
|
|
|
// Smallest possible faulting page at address zero,
|
|
|
|
|
// see ../../../../runtime/internal/sys/arch_arm.go
|
|
|
|
|
const minZeroPage = 4096
|
|
|
|
|
|
2016-05-15 00:12:56 -04:00
|
|
|
// loadByType returns the load instruction of the given type.
|
|
|
|
|
func loadByType(t ssa.Type) obj.As {
|
|
|
|
|
if t.IsFloat() {
|
2016-05-31 11:27:16 -04:00
|
|
|
switch t.Size() {
|
|
|
|
|
case 4:
|
|
|
|
|
return arm.AMOVF
|
|
|
|
|
case 8:
|
|
|
|
|
return arm.AMOVD
|
|
|
|
|
}
|
2016-05-15 00:12:56 -04:00
|
|
|
} else {
|
|
|
|
|
switch t.Size() {
|
|
|
|
|
case 1:
|
|
|
|
|
if t.IsSigned() {
|
|
|
|
|
return arm.AMOVB
|
|
|
|
|
} else {
|
|
|
|
|
return arm.AMOVBU
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
if t.IsSigned() {
|
|
|
|
|
return arm.AMOVH
|
|
|
|
|
} else {
|
|
|
|
|
return arm.AMOVHU
|
|
|
|
|
}
|
|
|
|
|
case 4:
|
|
|
|
|
return arm.AMOVW
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
panic("bad load type")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storeByType returns the store instruction of the given type.
|
|
|
|
|
func storeByType(t ssa.Type) obj.As {
|
|
|
|
|
if t.IsFloat() {
|
2016-05-31 11:27:16 -04:00
|
|
|
switch t.Size() {
|
|
|
|
|
case 4:
|
|
|
|
|
return arm.AMOVF
|
|
|
|
|
case 8:
|
|
|
|
|
return arm.AMOVD
|
|
|
|
|
}
|
2016-05-15 00:12:56 -04:00
|
|
|
} else {
|
|
|
|
|
switch t.Size() {
|
|
|
|
|
case 1:
|
|
|
|
|
return arm.AMOVB
|
|
|
|
|
case 2:
|
|
|
|
|
return arm.AMOVH
|
|
|
|
|
case 4:
|
|
|
|
|
return arm.AMOVW
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
panic("bad store type")
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-17 10:34:06 -04:00
|
|
|
// shift type is used as Offset in obj.TYPE_SHIFT operands to encode shifted register operands
|
|
|
|
|
type shift int64
|
|
|
|
|
|
|
|
|
|
// copied from ../../../internal/obj/util.go:/TYPE_SHIFT
|
|
|
|
|
func (v shift) String() string {
|
|
|
|
|
op := "<<>>->@>"[((v>>5)&3)<<1:]
|
|
|
|
|
if v&(1<<4) != 0 {
|
|
|
|
|
// register shift
|
|
|
|
|
return fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
|
|
|
|
|
} else {
|
|
|
|
|
// constant shift
|
|
|
|
|
return fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeshift encodes a register shifted by a constant
|
|
|
|
|
func makeshift(reg int16, typ int64, s int64) shift {
|
|
|
|
|
return shift(int64(reg&0xf) | typ | (s&31)<<7)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// genshift generates a Prog for r = r0 op (r1 shifted by s)
|
|
|
|
|
func genshift(as obj.As, r0, r1, r int16, typ int64, s int64) *obj.Prog {
|
|
|
|
|
p := gc.Prog(as)
|
|
|
|
|
p.From.Type = obj.TYPE_SHIFT
|
|
|
|
|
p.From.Offset = int64(makeshift(r1, typ, s))
|
|
|
|
|
p.Reg = r0
|
|
|
|
|
if r != 0 {
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
|
|
|
|
}
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeregshift encodes a register shifted by a register
|
|
|
|
|
func makeregshift(r1 int16, typ int64, r2 int16) shift {
|
|
|
|
|
return shift(int64(r1&0xf) | typ | int64(r2&0xf)<<8 | 1<<4)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// genregshift generates a Prog for r = r0 op (r1 shifted by r2)
|
|
|
|
|
func genregshift(as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
|
|
|
|
|
p := gc.Prog(as)
|
|
|
|
|
p.From.Type = obj.TYPE_SHIFT
|
|
|
|
|
p.From.Offset = int64(makeregshift(r1, typ, r2))
|
|
|
|
|
p.Reg = r0
|
|
|
|
|
if r != 0 {
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
|
|
|
|
}
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-21 22:57:26 -07:00
|
|
|
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
|
|
|
|
s.SetLineno(v.Line)
|
|
|
|
|
switch v.Op {
|
|
|
|
|
case ssa.OpInitMem:
|
|
|
|
|
// memory arg needs no code
|
|
|
|
|
case ssa.OpArg:
|
|
|
|
|
// input args need no code
|
2016-05-31 14:01:34 -04:00
|
|
|
case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
|
2016-03-21 22:57:26 -07:00
|
|
|
// nothing to do
|
2016-06-13 16:49:09 -04:00
|
|
|
case ssa.OpCopy, ssa.OpARMMOVWconvert, ssa.OpARMMOVWreg:
|
2016-05-13 15:31:14 -04:00
|
|
|
if v.Type.IsMemory() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
x := gc.SSARegNum(v.Args[0])
|
|
|
|
|
y := gc.SSARegNum(v)
|
|
|
|
|
if x == y {
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-05-31 11:27:16 -04:00
|
|
|
as := arm.AMOVW
|
|
|
|
|
if v.Type.IsFloat() {
|
|
|
|
|
switch v.Type.Size() {
|
|
|
|
|
case 4:
|
|
|
|
|
as = arm.AMOVF
|
|
|
|
|
case 8:
|
|
|
|
|
as = arm.AMOVD
|
|
|
|
|
default:
|
|
|
|
|
panic("bad float size")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(as)
|
2016-05-13 15:31:14 -04:00
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = x
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = y
|
2016-07-15 14:07:15 -04:00
|
|
|
case ssa.OpARMMOVWnop:
|
|
|
|
|
if gc.SSARegNum(v) != gc.SSARegNum(v.Args[0]) {
|
|
|
|
|
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
|
|
|
|
}
|
|
|
|
|
// nothing to do
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.OpLoadReg:
|
2016-05-15 00:12:56 -04:00
|
|
|
if v.Type.IsFlags() {
|
|
|
|
|
v.Unimplementedf("load flags not implemented: %v", v.LongString())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(loadByType(v.Type))
|
2016-03-21 22:57:26 -07:00
|
|
|
n, off := gc.AutoVar(v.Args[0])
|
|
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Node = n
|
|
|
|
|
p.From.Sym = gc.Linksym(n.Sym)
|
|
|
|
|
p.From.Offset = off
|
|
|
|
|
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
|
|
|
|
p.From.Name = obj.NAME_PARAM
|
|
|
|
|
p.From.Offset += n.Xoffset
|
|
|
|
|
} else {
|
|
|
|
|
p.From.Name = obj.NAME_AUTO
|
|
|
|
|
}
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpPhi:
|
2016-06-15 15:26:47 -07:00
|
|
|
gc.CheckLoweredPhi(v)
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.OpStoreReg:
|
2016-05-15 00:12:56 -04:00
|
|
|
if v.Type.IsFlags() {
|
|
|
|
|
v.Unimplementedf("store flags not implemented: %v", v.LongString())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(storeByType(v.Type))
|
2016-03-21 22:57:26 -07:00
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
n, off := gc.AutoVar(v)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Node = n
|
|
|
|
|
p.To.Sym = gc.Linksym(n.Sym)
|
|
|
|
|
p.To.Offset = off
|
|
|
|
|
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
|
|
|
|
p.To.Name = obj.NAME_PARAM
|
|
|
|
|
p.To.Offset += n.Xoffset
|
|
|
|
|
} else {
|
|
|
|
|
p.To.Name = obj.NAME_AUTO
|
|
|
|
|
}
|
2016-05-25 09:49:28 -04:00
|
|
|
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
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMADD,
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
ssa.OpARMADC,
|
2016-05-06 10:13:31 -07:00
|
|
|
ssa.OpARMSUB,
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
ssa.OpARMSBC,
|
2016-05-06 10:13:31 -07:00
|
|
|
ssa.OpARMRSB,
|
|
|
|
|
ssa.OpARMAND,
|
|
|
|
|
ssa.OpARMOR,
|
|
|
|
|
ssa.OpARMXOR,
|
2016-05-13 15:22:56 -04:00
|
|
|
ssa.OpARMBIC,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMMUL,
|
|
|
|
|
ssa.OpARMADDF,
|
|
|
|
|
ssa.OpARMADDD,
|
|
|
|
|
ssa.OpARMSUBF,
|
|
|
|
|
ssa.OpARMSUBD,
|
|
|
|
|
ssa.OpARMMULF,
|
|
|
|
|
ssa.OpARMMULD,
|
|
|
|
|
ssa.OpARMDIVF,
|
|
|
|
|
ssa.OpARMDIVD:
|
2016-03-21 22:57:26 -07:00
|
|
|
r := gc.SSARegNum(v)
|
|
|
|
|
r1 := gc.SSARegNum(v.Args[0])
|
|
|
|
|
r2 := gc.SSARegNum(v.Args[1])
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
2016-05-06 10:13:31 -07:00
|
|
|
p.From.Reg = r2
|
|
|
|
|
p.Reg = r1
|
2016-03-21 22:57:26 -07:00
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
case ssa.OpARMADDS,
|
|
|
|
|
ssa.OpARMSUBS:
|
2016-08-23 16:49:28 -07:00
|
|
|
r := gc.SSARegNum0(v)
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
r1 := gc.SSARegNum(v.Args[0])
|
|
|
|
|
r2 := gc.SSARegNum(v.Args[1])
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = r2
|
|
|
|
|
p.Reg = r1
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
2016-05-13 15:22:56 -04:00
|
|
|
case ssa.OpARMSLL,
|
2016-06-17 10:34:06 -04:00
|
|
|
ssa.OpARMSRL,
|
|
|
|
|
ssa.OpARMSRA:
|
2016-05-13 15:22:56 -04:00
|
|
|
r := gc.SSARegNum(v)
|
|
|
|
|
r1 := gc.SSARegNum(v.Args[0])
|
|
|
|
|
r2 := gc.SSARegNum(v.Args[1])
|
2016-06-17 10:34:06 -04:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
2016-05-13 15:22:56 -04:00
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = r2
|
|
|
|
|
p.Reg = r1
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
2016-06-17 10:34:06 -04:00
|
|
|
case ssa.OpARMSRAcond:
|
2016-05-13 15:22:56 -04:00
|
|
|
// ARM shift instructions uses only the low-order byte of the shift amount
|
|
|
|
|
// generate conditional instructions to deal with large shifts
|
2016-06-17 10:34:06 -04:00
|
|
|
// flag is already set
|
2016-05-13 15:22:56 -04:00
|
|
|
// SRA.HS $31, Rarg0, Rdst // shift 31 bits to get the sign bit
|
|
|
|
|
// SRA.LO Rarg1, Rarg0, Rdst
|
|
|
|
|
r := gc.SSARegNum(v)
|
|
|
|
|
r1 := gc.SSARegNum(v.Args[0])
|
|
|
|
|
r2 := gc.SSARegNum(v.Args[1])
|
2016-06-17 10:34:06 -04:00
|
|
|
p := gc.Prog(arm.ASRA)
|
2016-05-13 15:22:56 -04:00
|
|
|
p.Scond = arm.C_SCOND_HS
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = 31
|
|
|
|
|
p.Reg = r1
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
|
|
|
|
p = gc.Prog(arm.ASRA)
|
|
|
|
|
p.Scond = arm.C_SCOND_LO
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = r2
|
|
|
|
|
p.Reg = r1
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = r
|
2016-06-06 22:36:45 -04:00
|
|
|
case ssa.OpARMADDconst,
|
2016-06-13 16:49:09 -04:00
|
|
|
ssa.OpARMADCconst,
|
2016-06-06 22:36:45 -04:00
|
|
|
ssa.OpARMSUBconst,
|
2016-06-13 16:49:09 -04:00
|
|
|
ssa.OpARMSBCconst,
|
2016-05-06 10:13:31 -07:00
|
|
|
ssa.OpARMRSBconst,
|
2016-06-13 16:49:09 -04:00
|
|
|
ssa.OpARMRSCconst,
|
2016-05-06 10:13:31 -07:00
|
|
|
ssa.OpARMANDconst,
|
|
|
|
|
ssa.OpARMORconst,
|
|
|
|
|
ssa.OpARMXORconst,
|
2016-05-13 15:22:56 -04:00
|
|
|
ssa.OpARMBICconst,
|
|
|
|
|
ssa.OpARMSLLconst,
|
|
|
|
|
ssa.OpARMSRLconst,
|
|
|
|
|
ssa.OpARMSRAconst:
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = v.AuxInt
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-06-13 16:49:09 -04:00
|
|
|
case ssa.OpARMADDSconst,
|
|
|
|
|
ssa.OpARMSUBSconst,
|
|
|
|
|
ssa.OpARMRSBSconst:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = v.AuxInt
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
2016-08-23 16:49:28 -07:00
|
|
|
p.To.Reg = gc.SSARegNum0(v)
|
2016-05-25 23:17:42 -04:00
|
|
|
case ssa.OpARMSRRconst:
|
2016-06-17 10:34:06 -04:00
|
|
|
genshift(arm.AMOVW, 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v), arm.SHIFT_RR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMADDshiftLL,
|
|
|
|
|
ssa.OpARMADCshiftLL,
|
|
|
|
|
ssa.OpARMSUBshiftLL,
|
|
|
|
|
ssa.OpARMSBCshiftLL,
|
|
|
|
|
ssa.OpARMRSBshiftLL,
|
|
|
|
|
ssa.OpARMRSCshiftLL,
|
|
|
|
|
ssa.OpARMANDshiftLL,
|
|
|
|
|
ssa.OpARMORshiftLL,
|
|
|
|
|
ssa.OpARMXORshiftLL,
|
|
|
|
|
ssa.OpARMBICshiftLL:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LL, v.AuxInt)
|
|
|
|
|
case ssa.OpARMADDSshiftLL,
|
|
|
|
|
ssa.OpARMSUBSshiftLL,
|
|
|
|
|
ssa.OpARMRSBSshiftLL:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum0(v), arm.SHIFT_LL, v.AuxInt)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
case ssa.OpARMADDshiftRL,
|
|
|
|
|
ssa.OpARMADCshiftRL,
|
|
|
|
|
ssa.OpARMSUBshiftRL,
|
|
|
|
|
ssa.OpARMSBCshiftRL,
|
|
|
|
|
ssa.OpARMRSBshiftRL,
|
|
|
|
|
ssa.OpARMRSCshiftRL,
|
|
|
|
|
ssa.OpARMANDshiftRL,
|
|
|
|
|
ssa.OpARMORshiftRL,
|
|
|
|
|
ssa.OpARMXORshiftRL,
|
|
|
|
|
ssa.OpARMBICshiftRL:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMADDSshiftRL,
|
|
|
|
|
ssa.OpARMSUBSshiftRL,
|
|
|
|
|
ssa.OpARMRSBSshiftRL:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum0(v), arm.SHIFT_LR, v.AuxInt)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
case ssa.OpARMADDshiftRA,
|
|
|
|
|
ssa.OpARMADCshiftRA,
|
|
|
|
|
ssa.OpARMSUBshiftRA,
|
|
|
|
|
ssa.OpARMSBCshiftRA,
|
|
|
|
|
ssa.OpARMRSBshiftRA,
|
|
|
|
|
ssa.OpARMRSCshiftRA,
|
|
|
|
|
ssa.OpARMANDshiftRA,
|
|
|
|
|
ssa.OpARMORshiftRA,
|
|
|
|
|
ssa.OpARMXORshiftRA,
|
|
|
|
|
ssa.OpARMBICshiftRA:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_AR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMADDSshiftRA,
|
|
|
|
|
ssa.OpARMSUBSshiftRA,
|
|
|
|
|
ssa.OpARMRSBSshiftRA:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum0(v), arm.SHIFT_AR, v.AuxInt)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
2016-08-30 09:12:22 -04:00
|
|
|
case ssa.OpARMXORshiftRR:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_RR, v.AuxInt)
|
2016-06-17 10:34:06 -04:00
|
|
|
case ssa.OpARMMVNshiftLL:
|
|
|
|
|
genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v), arm.SHIFT_LL, v.AuxInt)
|
|
|
|
|
case ssa.OpARMMVNshiftRL:
|
|
|
|
|
genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v), arm.SHIFT_LR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMMVNshiftRA:
|
|
|
|
|
genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v), arm.SHIFT_AR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMMVNshiftLLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LL)
|
|
|
|
|
case ssa.OpARMMVNshiftRLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LR)
|
|
|
|
|
case ssa.OpARMMVNshiftRAreg:
|
|
|
|
|
genregshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_AR)
|
|
|
|
|
case ssa.OpARMADDshiftLLreg,
|
|
|
|
|
ssa.OpARMADCshiftLLreg,
|
|
|
|
|
ssa.OpARMSUBshiftLLreg,
|
|
|
|
|
ssa.OpARMSBCshiftLLreg,
|
|
|
|
|
ssa.OpARMRSBshiftLLreg,
|
|
|
|
|
ssa.OpARMRSCshiftLLreg,
|
|
|
|
|
ssa.OpARMANDshiftLLreg,
|
|
|
|
|
ssa.OpARMORshiftLLreg,
|
|
|
|
|
ssa.OpARMXORshiftLLreg,
|
|
|
|
|
ssa.OpARMBICshiftLLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum(v), arm.SHIFT_LL)
|
|
|
|
|
case ssa.OpARMADDSshiftLLreg,
|
|
|
|
|
ssa.OpARMSUBSshiftLLreg,
|
|
|
|
|
ssa.OpARMRSBSshiftLLreg:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum0(v), arm.SHIFT_LL)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
case ssa.OpARMADDshiftRLreg,
|
|
|
|
|
ssa.OpARMADCshiftRLreg,
|
|
|
|
|
ssa.OpARMSUBshiftRLreg,
|
|
|
|
|
ssa.OpARMSBCshiftRLreg,
|
|
|
|
|
ssa.OpARMRSBshiftRLreg,
|
|
|
|
|
ssa.OpARMRSCshiftRLreg,
|
|
|
|
|
ssa.OpARMANDshiftRLreg,
|
|
|
|
|
ssa.OpARMORshiftRLreg,
|
|
|
|
|
ssa.OpARMXORshiftRLreg,
|
|
|
|
|
ssa.OpARMBICshiftRLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum(v), arm.SHIFT_LR)
|
|
|
|
|
case ssa.OpARMADDSshiftRLreg,
|
|
|
|
|
ssa.OpARMSUBSshiftRLreg,
|
|
|
|
|
ssa.OpARMRSBSshiftRLreg:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum0(v), arm.SHIFT_LR)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
|
|
|
|
case ssa.OpARMADDshiftRAreg,
|
|
|
|
|
ssa.OpARMADCshiftRAreg,
|
|
|
|
|
ssa.OpARMSUBshiftRAreg,
|
|
|
|
|
ssa.OpARMSBCshiftRAreg,
|
|
|
|
|
ssa.OpARMRSBshiftRAreg,
|
|
|
|
|
ssa.OpARMRSCshiftRAreg,
|
|
|
|
|
ssa.OpARMANDshiftRAreg,
|
|
|
|
|
ssa.OpARMORshiftRAreg,
|
|
|
|
|
ssa.OpARMXORshiftRAreg,
|
|
|
|
|
ssa.OpARMBICshiftRAreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum(v), arm.SHIFT_AR)
|
|
|
|
|
case ssa.OpARMADDSshiftRAreg,
|
|
|
|
|
ssa.OpARMSUBSshiftRAreg,
|
|
|
|
|
ssa.OpARMRSBSshiftRAreg:
|
2016-08-23 16:49:28 -07:00
|
|
|
p := genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), gc.SSARegNum0(v), arm.SHIFT_AR)
|
2016-06-17 10:34:06 -04:00
|
|
|
p.Scond = arm.C_SBIT
|
2016-05-13 15:22:56 -04:00
|
|
|
case ssa.OpARMHMUL,
|
|
|
|
|
ssa.OpARMHMULU:
|
|
|
|
|
// 32-bit high multiplication
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p.To.Type = obj.TYPE_REGREG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
p.To.Offset = arm.REGTMP // throw away low 32-bit into tmp register
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
case ssa.OpARMMULLU:
|
2016-07-13 16:15:54 -07:00
|
|
|
// 32-bit multiplication, results 64-bit, high 32-bit in out0, low 32-bit in out1
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p.To.Type = obj.TYPE_REGREG
|
2016-07-13 16:15:54 -07:00
|
|
|
p.To.Reg = gc.SSARegNum0(v) // high 32-bit
|
|
|
|
|
p.To.Offset = int64(gc.SSARegNum1(v)) // low 32-bit
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
case ssa.OpARMMULA:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p.To.Type = obj.TYPE_REGREG2
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v) // result
|
|
|
|
|
p.To.Offset = int64(gc.SSARegNum(v.Args[2])) // addend
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.OpARMMOVWconst:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
2016-03-29 16:39:53 -07:00
|
|
|
p.From.Offset = v.AuxInt
|
2016-03-21 22:57:26 -07:00
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-05-31 11:27:16 -04:00
|
|
|
case ssa.OpARMMOVFconst,
|
|
|
|
|
ssa.OpARMMOVDconst:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_FCONST
|
|
|
|
|
p.From.Val = math.Float64frombits(uint64(v.AuxInt))
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMCMP,
|
|
|
|
|
ssa.OpARMCMN,
|
|
|
|
|
ssa.OpARMTST,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMTEQ,
|
|
|
|
|
ssa.OpARMCMPF,
|
|
|
|
|
ssa.OpARMCMPD:
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
2016-04-18 12:21:51 -04:00
|
|
|
// Special layout in ARM assembly
|
|
|
|
|
// Comparing to x86, the operands of ARM's CMP are reversed.
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[0])
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMCMPconst,
|
|
|
|
|
ssa.OpARMCMNconst,
|
|
|
|
|
ssa.OpARMTSTconst,
|
|
|
|
|
ssa.OpARMTEQconst:
|
|
|
|
|
// Special layout in ARM assembly
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = v.AuxInt
|
|
|
|
|
p.Reg = gc.SSARegNum(v.Args[0])
|
2016-07-06 10:04:45 -04:00
|
|
|
case ssa.OpARMCMPF0,
|
|
|
|
|
ssa.OpARMCMPD0:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
2016-06-17 10:34:06 -04:00
|
|
|
case ssa.OpARMCMPshiftLL:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), 0, arm.SHIFT_LL, v.AuxInt)
|
|
|
|
|
case ssa.OpARMCMPshiftRL:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), 0, arm.SHIFT_LR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMCMPshiftRA:
|
|
|
|
|
genshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), 0, arm.SHIFT_AR, v.AuxInt)
|
|
|
|
|
case ssa.OpARMCMPshiftLLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), 0, arm.SHIFT_LL)
|
|
|
|
|
case ssa.OpARMCMPshiftRLreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), 0, arm.SHIFT_LR)
|
|
|
|
|
case ssa.OpARMCMPshiftRAreg:
|
|
|
|
|
genregshift(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[2]), 0, arm.SHIFT_AR)
|
2016-06-06 22:36:45 -04:00
|
|
|
case ssa.OpARMMOVWaddr:
|
2016-06-15 15:56:52 -07:00
|
|
|
p := gc.Prog(arm.AMOVW)
|
|
|
|
|
p.From.Type = obj.TYPE_ADDR
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
|
|
|
|
|
var wantreg string
|
2016-06-06 22:36:45 -04:00
|
|
|
// MOVW $sym+off(base), R
|
|
|
|
|
// the assembler expands it as the following:
|
|
|
|
|
// - base is SP: add constant offset to SP (R13)
|
|
|
|
|
// when constant is large, tmp register (R11) may be used
|
|
|
|
|
// - base is SB: load external address from constant pool (use relocation)
|
|
|
|
|
switch v.Aux.(type) {
|
|
|
|
|
default:
|
|
|
|
|
v.Fatalf("aux is of unknown type %T", v.Aux)
|
|
|
|
|
case *ssa.ExternSymbol:
|
2016-06-15 15:56:52 -07:00
|
|
|
wantreg = "SB"
|
|
|
|
|
gc.AddAux(&p.From, v)
|
2016-06-15 15:17:45 -07:00
|
|
|
case *ssa.ArgSymbol, *ssa.AutoSymbol:
|
2016-06-15 15:56:52 -07:00
|
|
|
wantreg = "SP"
|
|
|
|
|
gc.AddAux(&p.From, v)
|
|
|
|
|
case nil:
|
|
|
|
|
// No sym, just MOVW $off(SP), R
|
|
|
|
|
wantreg = "SP"
|
|
|
|
|
p.From.Reg = arm.REGSP
|
|
|
|
|
p.From.Offset = v.AuxInt
|
2016-06-06 22:36:45 -04:00
|
|
|
}
|
2016-06-15 15:56:52 -07:00
|
|
|
if reg := gc.SSAReg(v.Args[0]); reg.Name() != wantreg {
|
|
|
|
|
v.Fatalf("bad reg %s for symbol type %T, want %s", reg.Name(), v.Aux, wantreg)
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMMOVBload,
|
|
|
|
|
ssa.OpARMMOVBUload,
|
|
|
|
|
ssa.OpARMMOVHload,
|
|
|
|
|
ssa.OpARMMOVHUload,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMMOVWload,
|
|
|
|
|
ssa.OpARMMOVFload,
|
|
|
|
|
ssa.OpARMMOVDload:
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
gc.AddAux(&p.From, v)
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMMOVBstore,
|
|
|
|
|
ssa.OpARMMOVHstore,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMMOVWstore,
|
|
|
|
|
ssa.OpARMMOVFstore,
|
|
|
|
|
ssa.OpARMMOVDstore:
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
gc.AddAux(&p.To, v)
|
2016-06-17 10:34:06 -04:00
|
|
|
case ssa.OpARMMOVWloadidx:
|
|
|
|
|
// this is just shift 0 bits
|
|
|
|
|
fallthrough
|
|
|
|
|
case ssa.OpARMMOVWloadshiftLL:
|
|
|
|
|
p := genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LL, v.AuxInt)
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
case ssa.OpARMMOVWloadshiftRL:
|
|
|
|
|
p := genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_LR, v.AuxInt)
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
case ssa.OpARMMOVWloadshiftRA:
|
|
|
|
|
p := genshift(v.Op.Asm(), 0, gc.SSARegNum(v.Args[1]), gc.SSARegNum(v), arm.SHIFT_AR, v.AuxInt)
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
case ssa.OpARMMOVWstoreidx:
|
|
|
|
|
// this is just shift 0 bits
|
|
|
|
|
fallthrough
|
|
|
|
|
case ssa.OpARMMOVWstoreshiftLL:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[2])
|
|
|
|
|
p.To.Type = obj.TYPE_SHIFT
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Offset = int64(makeshift(gc.SSARegNum(v.Args[1]), arm.SHIFT_LL, v.AuxInt))
|
|
|
|
|
case ssa.OpARMMOVWstoreshiftRL:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[2])
|
|
|
|
|
p.To.Type = obj.TYPE_SHIFT
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Offset = int64(makeshift(gc.SSARegNum(v.Args[1]), arm.SHIFT_LR, v.AuxInt))
|
|
|
|
|
case ssa.OpARMMOVWstoreshiftRA:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[2])
|
|
|
|
|
p.To.Type = obj.TYPE_SHIFT
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Offset = int64(makeshift(gc.SSARegNum(v.Args[1]), arm.SHIFT_AR, v.AuxInt))
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMMOVBreg,
|
|
|
|
|
ssa.OpARMMOVBUreg,
|
|
|
|
|
ssa.OpARMMOVHreg,
|
2016-06-17 10:34:06 -04:00
|
|
|
ssa.OpARMMOVHUreg:
|
|
|
|
|
a := v.Args[0]
|
2016-07-15 14:07:15 -04:00
|
|
|
for a.Op == ssa.OpCopy || a.Op == ssa.OpARMMOVWreg || a.Op == ssa.OpARMMOVWnop {
|
2016-06-17 10:34:06 -04:00
|
|
|
a = a.Args[0]
|
|
|
|
|
}
|
|
|
|
|
if a.Op == ssa.OpLoadReg {
|
|
|
|
|
t := a.Type
|
|
|
|
|
switch {
|
|
|
|
|
case v.Op == ssa.OpARMMOVBreg && t.Size() == 1 && t.IsSigned(),
|
|
|
|
|
v.Op == ssa.OpARMMOVBUreg && t.Size() == 1 && !t.IsSigned(),
|
|
|
|
|
v.Op == ssa.OpARMMOVHreg && t.Size() == 2 && t.IsSigned(),
|
|
|
|
|
v.Op == ssa.OpARMMOVHUreg && t.Size() == 2 && !t.IsSigned():
|
|
|
|
|
// arg is a proper-typed load, already zero/sign-extended, don't extend again
|
|
|
|
|
if gc.SSARegNum(v) == gc.SSARegNum(v.Args[0]) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(arm.AMOVW)
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fallthrough
|
|
|
|
|
case ssa.OpARMMVN,
|
2016-08-30 09:12:22 -04:00
|
|
|
ssa.OpARMCLZ,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMSQRTD,
|
2016-06-29 15:20:48 -04:00
|
|
|
ssa.OpARMNEGF,
|
|
|
|
|
ssa.OpARMNEGD,
|
2016-05-31 11:27:16 -04:00
|
|
|
ssa.OpARMMOVWF,
|
|
|
|
|
ssa.OpARMMOVWD,
|
|
|
|
|
ssa.OpARMMOVFW,
|
|
|
|
|
ssa.OpARMMOVDW,
|
|
|
|
|
ssa.OpARMMOVFD,
|
|
|
|
|
ssa.OpARMMOVDF:
|
|
|
|
|
p := gc.Prog(v.Op.Asm())
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
case ssa.OpARMMOVWUF,
|
|
|
|
|
ssa.OpARMMOVWUD,
|
|
|
|
|
ssa.OpARMMOVFWU,
|
|
|
|
|
ssa.OpARMMOVDWU:
|
2016-05-06 10:13:31 -07:00
|
|
|
p := gc.Prog(v.Op.Asm())
|
2016-05-31 11:27:16 -04:00
|
|
|
p.Scond = arm.C_UBIT
|
2016-05-06 10:13:31 -07:00
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-06-17 10:34:06 -04:00
|
|
|
case ssa.OpARMCMOVWHSconst:
|
|
|
|
|
p := gc.Prog(arm.AMOVW)
|
|
|
|
|
p.Scond = arm.C_SCOND_HS
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = v.AuxInt
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
case ssa.OpARMCMOVWLSconst:
|
|
|
|
|
p := gc.Prog(arm.AMOVW)
|
|
|
|
|
p.Scond = arm.C_SCOND_LS
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = v.AuxInt
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.OpARMCALLstatic:
|
2016-05-15 00:12:56 -04:00
|
|
|
if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
|
|
|
|
|
// Deferred calls will appear to be returning to
|
|
|
|
|
// the CALL deferreturn(SB) that we are about to emit.
|
|
|
|
|
// However, the stack trace code will show the line
|
|
|
|
|
// of the instruction byte before the return PC.
|
|
|
|
|
// To avoid that being an unrelated instruction,
|
|
|
|
|
// insert an actual hardware NOP that will have the right line number.
|
|
|
|
|
// This is different from obj.ANOP, which is a virtual no-op
|
|
|
|
|
// that doesn't make it into the instruction stream.
|
|
|
|
|
ginsnop()
|
|
|
|
|
}
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(obj.ACALL)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
|
|
|
|
|
if gc.Maxarg < v.AuxInt {
|
|
|
|
|
gc.Maxarg = v.AuxInt
|
|
|
|
|
}
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMCALLclosure:
|
|
|
|
|
p := gc.Prog(obj.ACALL)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Offset = 0
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
if gc.Maxarg < v.AuxInt {
|
|
|
|
|
gc.Maxarg = v.AuxInt
|
|
|
|
|
}
|
|
|
|
|
case ssa.OpARMCALLdefer:
|
|
|
|
|
p := gc.Prog(obj.ACALL)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
|
|
|
|
|
if gc.Maxarg < v.AuxInt {
|
|
|
|
|
gc.Maxarg = v.AuxInt
|
|
|
|
|
}
|
|
|
|
|
case ssa.OpARMCALLgo:
|
|
|
|
|
p := gc.Prog(obj.ACALL)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(gc.Newproc.Sym)
|
|
|
|
|
if gc.Maxarg < v.AuxInt {
|
|
|
|
|
gc.Maxarg = v.AuxInt
|
|
|
|
|
}
|
|
|
|
|
case ssa.OpARMCALLinter:
|
|
|
|
|
p := gc.Prog(obj.ACALL)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Offset = 0
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
if gc.Maxarg < v.AuxInt {
|
|
|
|
|
gc.Maxarg = v.AuxInt
|
|
|
|
|
}
|
2016-05-13 15:31:14 -04:00
|
|
|
case ssa.OpARMDUFFZERO:
|
|
|
|
|
p := gc.Prog(obj.ADUFFZERO)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
|
|
|
|
|
p.To.Offset = v.AuxInt
|
|
|
|
|
case ssa.OpARMDUFFCOPY:
|
|
|
|
|
p := gc.Prog(obj.ADUFFCOPY)
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
|
|
|
|
|
p.To.Offset = v.AuxInt
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMLoweredNilCheck:
|
2016-06-24 15:06:17 -04:00
|
|
|
// Optimization - if the subsequent block has a load or store
|
|
|
|
|
// at the same address, we don't need to issue this instruction.
|
|
|
|
|
mem := v.Args[1]
|
|
|
|
|
for _, w := range v.Block.Succs[0].Block().Values {
|
|
|
|
|
if w.Op == ssa.OpPhi {
|
|
|
|
|
if w.Type.IsMemory() {
|
|
|
|
|
mem = w
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() {
|
|
|
|
|
// w doesn't use a store - can't be a memory op.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if w.Args[len(w.Args)-1] != mem {
|
|
|
|
|
v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w)
|
|
|
|
|
}
|
|
|
|
|
switch w.Op {
|
|
|
|
|
case ssa.OpARMMOVBload, ssa.OpARMMOVBUload, ssa.OpARMMOVHload, ssa.OpARMMOVHUload,
|
|
|
|
|
ssa.OpARMMOVWload, ssa.OpARMMOVFload, ssa.OpARMMOVDload,
|
|
|
|
|
ssa.OpARMMOVBstore, ssa.OpARMMOVHstore, ssa.OpARMMOVWstore,
|
|
|
|
|
ssa.OpARMMOVFstore, ssa.OpARMMOVDstore:
|
|
|
|
|
// arg0 is ptr, auxint is offset
|
|
|
|
|
if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage {
|
|
|
|
|
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
|
|
|
|
gc.Warnl(v.Line, "removed nil check")
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-07-27 12:33:08 -04:00
|
|
|
case ssa.OpARMDUFFZERO, ssa.OpARMLoweredZero:
|
2016-06-24 15:06:17 -04:00
|
|
|
// arg0 is ptr
|
|
|
|
|
if w.Args[0] == v.Args[0] {
|
|
|
|
|
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
|
|
|
|
gc.Warnl(v.Line, "removed nil check")
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-07-27 12:33:08 -04:00
|
|
|
case ssa.OpARMDUFFCOPY, ssa.OpARMLoweredMove:
|
2016-06-24 15:06:17 -04:00
|
|
|
// arg0 is dst ptr, arg1 is src ptr
|
|
|
|
|
if w.Args[0] == v.Args[0] || w.Args[1] == v.Args[0] {
|
|
|
|
|
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
|
|
|
|
gc.Warnl(v.Line, "removed nil check")
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
if w.Type.IsMemory() {
|
|
|
|
|
if w.Op == ssa.OpVarDef || w.Op == ssa.OpVarKill || w.Op == ssa.OpVarLive {
|
|
|
|
|
// these ops are OK
|
|
|
|
|
mem = w
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// We can't delay the nil check past the next store.
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-06 10:13:31 -07:00
|
|
|
// Issue a load which will fault if arg is nil.
|
|
|
|
|
p := gc.Prog(arm.AMOVB)
|
|
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
|
|
|
|
gc.AddAux(&p.From, v)
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = arm.REGTMP
|
|
|
|
|
if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
|
|
|
|
|
gc.Warnl(v.Line, "generated nil check")
|
|
|
|
|
}
|
2016-07-27 12:33:08 -04:00
|
|
|
case ssa.OpARMLoweredZero:
|
2016-05-13 15:31:14 -04:00
|
|
|
// MOVW.P Rarg2, 4(R1)
|
|
|
|
|
// CMP Rarg1, R1
|
2016-07-27 12:33:08 -04:00
|
|
|
// BLE -2(PC)
|
|
|
|
|
// arg1 is the address of the last element to zero
|
2016-05-13 15:31:14 -04:00
|
|
|
// arg2 is known to be zero
|
2016-07-27 12:33:08 -04:00
|
|
|
// auxint is alignment
|
|
|
|
|
var sz int64
|
|
|
|
|
var mov obj.As
|
|
|
|
|
switch {
|
|
|
|
|
case v.AuxInt%4 == 0:
|
|
|
|
|
sz = 4
|
|
|
|
|
mov = arm.AMOVW
|
|
|
|
|
case v.AuxInt%2 == 0:
|
|
|
|
|
sz = 2
|
|
|
|
|
mov = arm.AMOVH
|
|
|
|
|
default:
|
2016-06-27 16:54:57 -04:00
|
|
|
sz = 1
|
|
|
|
|
mov = arm.AMOVB
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(mov)
|
2016-05-13 15:31:14 -04:00
|
|
|
p.Scond = arm.C_PBIT
|
|
|
|
|
p.From.Type = obj.TYPE_REG
|
|
|
|
|
p.From.Reg = gc.SSARegNum(v.Args[2])
|
|
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Reg = arm.REG_R1
|
2016-06-27 16:54:57 -04:00
|
|
|
p.To.Offset = sz
|
2016-05-13 15:31:14 -04:00
|
|
|
p2 := gc.Prog(arm.ACMP)
|
|
|
|
|
p2.From.Type = obj.TYPE_REG
|
|
|
|
|
p2.From.Reg = gc.SSARegNum(v.Args[1])
|
|
|
|
|
p2.Reg = arm.REG_R1
|
2016-07-27 12:33:08 -04:00
|
|
|
p3 := gc.Prog(arm.ABLE)
|
2016-05-13 15:31:14 -04:00
|
|
|
p3.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
gc.Patch(p3, p)
|
2016-07-27 12:33:08 -04:00
|
|
|
case ssa.OpARMLoweredMove:
|
2016-05-13 15:31:14 -04:00
|
|
|
// MOVW.P 4(R1), Rtmp
|
|
|
|
|
// MOVW.P Rtmp, 4(R2)
|
|
|
|
|
// CMP Rarg2, R1
|
2016-07-27 12:33:08 -04:00
|
|
|
// BLE -3(PC)
|
|
|
|
|
// arg2 is the address of the last element of src
|
|
|
|
|
// auxint is alignment
|
|
|
|
|
var sz int64
|
|
|
|
|
var mov obj.As
|
|
|
|
|
switch {
|
|
|
|
|
case v.AuxInt%4 == 0:
|
|
|
|
|
sz = 4
|
|
|
|
|
mov = arm.AMOVW
|
|
|
|
|
case v.AuxInt%2 == 0:
|
|
|
|
|
sz = 2
|
|
|
|
|
mov = arm.AMOVH
|
|
|
|
|
default:
|
2016-06-27 16:54:57 -04:00
|
|
|
sz = 1
|
|
|
|
|
mov = arm.AMOVB
|
|
|
|
|
}
|
|
|
|
|
p := gc.Prog(mov)
|
2016-05-13 15:31:14 -04:00
|
|
|
p.Scond = arm.C_PBIT
|
|
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Reg = arm.REG_R1
|
2016-06-27 16:54:57 -04:00
|
|
|
p.From.Offset = sz
|
2016-05-13 15:31:14 -04:00
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = arm.REGTMP
|
2016-06-27 16:54:57 -04:00
|
|
|
p2 := gc.Prog(mov)
|
2016-05-13 15:31:14 -04:00
|
|
|
p2.Scond = arm.C_PBIT
|
|
|
|
|
p2.From.Type = obj.TYPE_REG
|
|
|
|
|
p2.From.Reg = arm.REGTMP
|
|
|
|
|
p2.To.Type = obj.TYPE_MEM
|
|
|
|
|
p2.To.Reg = arm.REG_R2
|
2016-06-27 16:54:57 -04:00
|
|
|
p2.To.Offset = sz
|
2016-05-13 15:31:14 -04:00
|
|
|
p3 := gc.Prog(arm.ACMP)
|
|
|
|
|
p3.From.Type = obj.TYPE_REG
|
|
|
|
|
p3.From.Reg = gc.SSARegNum(v.Args[2])
|
|
|
|
|
p3.Reg = arm.REG_R1
|
2016-07-27 12:33:08 -04:00
|
|
|
p4 := gc.Prog(arm.ABLE)
|
2016-05-13 15:31:14 -04:00
|
|
|
p4.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
gc.Patch(p4, p)
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.OpVarDef:
|
|
|
|
|
gc.Gvardef(v.Aux.(*gc.Node))
|
|
|
|
|
case ssa.OpVarKill:
|
|
|
|
|
gc.Gvarkill(v.Aux.(*gc.Node))
|
|
|
|
|
case ssa.OpVarLive:
|
|
|
|
|
gc.Gvarlive(v.Aux.(*gc.Node))
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
case ssa.OpKeepAlive:
|
2016-09-03 18:48:30 -07:00
|
|
|
gc.KeepAlive(v)
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.OpARMEqual,
|
|
|
|
|
ssa.OpARMNotEqual,
|
|
|
|
|
ssa.OpARMLessThan,
|
|
|
|
|
ssa.OpARMLessEqual,
|
|
|
|
|
ssa.OpARMGreaterThan,
|
|
|
|
|
ssa.OpARMGreaterEqual,
|
|
|
|
|
ssa.OpARMLessThanU,
|
|
|
|
|
ssa.OpARMLessEqualU,
|
|
|
|
|
ssa.OpARMGreaterThanU,
|
|
|
|
|
ssa.OpARMGreaterEqualU:
|
2016-05-13 11:25:07 -04:00
|
|
|
// generate boolean values
|
|
|
|
|
// use conditional move
|
|
|
|
|
p := gc.Prog(arm.AMOVW)
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = 0
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
|
|
|
|
p = gc.Prog(arm.AMOVW)
|
|
|
|
|
p.Scond = condBits[v.Op]
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = 1
|
|
|
|
|
p.To.Type = obj.TYPE_REG
|
|
|
|
|
p.To.Reg = gc.SSARegNum(v)
|
2016-07-13 16:15:54 -07:00
|
|
|
case ssa.OpSelect0, ssa.OpSelect1:
|
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
|
|
|
// nothing to do
|
2016-05-25 09:49:28 -04:00
|
|
|
case ssa.OpARMLoweredGetClosurePtr:
|
2016-07-03 13:40:03 -07:00
|
|
|
// Closure pointer is R7 (arm.REGCTXT).
|
|
|
|
|
gc.CheckLoweredGetClosurePtr(v)
|
2016-06-13 16:49:09 -04:00
|
|
|
case ssa.OpARMFlagEQ,
|
|
|
|
|
ssa.OpARMFlagLT_ULT,
|
|
|
|
|
ssa.OpARMFlagLT_UGT,
|
|
|
|
|
ssa.OpARMFlagGT_ULT,
|
|
|
|
|
ssa.OpARMFlagGT_UGT:
|
|
|
|
|
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
|
|
|
|
case ssa.OpARMInvertFlags:
|
|
|
|
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
2016-03-21 22:57:26 -07:00
|
|
|
default:
|
|
|
|
|
v.Unimplementedf("genValue not implemented: %s", v.LongString())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-13 11:25:07 -04:00
|
|
|
var condBits = map[ssa.Op]uint8{
|
|
|
|
|
ssa.OpARMEqual: arm.C_SCOND_EQ,
|
|
|
|
|
ssa.OpARMNotEqual: arm.C_SCOND_NE,
|
|
|
|
|
ssa.OpARMLessThan: arm.C_SCOND_LT,
|
|
|
|
|
ssa.OpARMLessThanU: arm.C_SCOND_LO,
|
|
|
|
|
ssa.OpARMLessEqual: arm.C_SCOND_LE,
|
|
|
|
|
ssa.OpARMLessEqualU: arm.C_SCOND_LS,
|
|
|
|
|
ssa.OpARMGreaterThan: arm.C_SCOND_GT,
|
|
|
|
|
ssa.OpARMGreaterThanU: arm.C_SCOND_HI,
|
|
|
|
|
ssa.OpARMGreaterEqual: arm.C_SCOND_GE,
|
|
|
|
|
ssa.OpARMGreaterEqualU: arm.C_SCOND_HS,
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-06 10:13:31 -07:00
|
|
|
var blockJump = map[ssa.BlockKind]struct {
|
|
|
|
|
asm, invasm obj.As
|
|
|
|
|
}{
|
|
|
|
|
ssa.BlockARMEQ: {arm.ABEQ, arm.ABNE},
|
|
|
|
|
ssa.BlockARMNE: {arm.ABNE, arm.ABEQ},
|
|
|
|
|
ssa.BlockARMLT: {arm.ABLT, arm.ABGE},
|
|
|
|
|
ssa.BlockARMGE: {arm.ABGE, arm.ABLT},
|
|
|
|
|
ssa.BlockARMLE: {arm.ABLE, arm.ABGT},
|
|
|
|
|
ssa.BlockARMGT: {arm.ABGT, arm.ABLE},
|
2016-05-13 11:25:07 -04:00
|
|
|
ssa.BlockARMULT: {arm.ABLO, arm.ABHS},
|
|
|
|
|
ssa.BlockARMUGE: {arm.ABHS, arm.ABLO},
|
2016-05-06 10:13:31 -07:00
|
|
|
ssa.BlockARMUGT: {arm.ABHI, arm.ABLS},
|
|
|
|
|
ssa.BlockARMULE: {arm.ABLS, arm.ABHI},
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-21 22:57:26 -07:00
|
|
|
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
|
|
|
|
|
s.SetLineno(b.Line)
|
|
|
|
|
|
|
|
|
|
switch b.Kind {
|
2016-09-09 13:11:07 -07:00
|
|
|
case ssa.BlockPlain, ssa.BlockCheck:
|
2016-04-28 16:52:47 -07:00
|
|
|
if b.Succs[0].Block() != next {
|
2016-03-21 22:57:26 -07:00
|
|
|
p := gc.Prog(obj.AJMP)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
2016-04-28 16:52:47 -07:00
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
2016-03-21 22:57:26 -07:00
|
|
|
}
|
2016-05-06 10:13:31 -07:00
|
|
|
|
2016-05-15 00:12:56 -04:00
|
|
|
case ssa.BlockDefer:
|
|
|
|
|
// defer returns in R0:
|
|
|
|
|
// 0 if we should continue executing
|
|
|
|
|
// 1 if we should jump to deferreturn call
|
|
|
|
|
p := gc.Prog(arm.ACMP)
|
|
|
|
|
p.From.Type = obj.TYPE_CONST
|
|
|
|
|
p.From.Offset = 0
|
|
|
|
|
p.Reg = arm.REG_R0
|
|
|
|
|
p = gc.Prog(arm.ABNE)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
|
|
|
|
|
if b.Succs[0].Block() != next {
|
|
|
|
|
p := gc.Prog(obj.AJMP)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-13 15:22:56 -04:00
|
|
|
case ssa.BlockExit:
|
|
|
|
|
gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
|
|
|
|
|
|
2016-03-21 22:57:26 -07:00
|
|
|
case ssa.BlockRet:
|
|
|
|
|
gc.Prog(obj.ARET)
|
2016-05-06 10:13:31 -07:00
|
|
|
|
2016-05-15 00:12:56 -04:00
|
|
|
case ssa.BlockRetJmp:
|
2016-06-03 18:03:29 -04:00
|
|
|
p := gc.Prog(obj.ARET)
|
2016-05-15 00:12:56 -04:00
|
|
|
p.To.Type = obj.TYPE_MEM
|
|
|
|
|
p.To.Name = obj.NAME_EXTERN
|
|
|
|
|
p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
|
|
|
|
|
|
2016-05-06 10:13:31 -07:00
|
|
|
case ssa.BlockARMEQ, ssa.BlockARMNE,
|
|
|
|
|
ssa.BlockARMLT, ssa.BlockARMGE,
|
|
|
|
|
ssa.BlockARMLE, ssa.BlockARMGT,
|
|
|
|
|
ssa.BlockARMULT, ssa.BlockARMUGT,
|
|
|
|
|
ssa.BlockARMULE, ssa.BlockARMUGE:
|
|
|
|
|
jmp := blockJump[b.Kind]
|
|
|
|
|
var p *obj.Prog
|
|
|
|
|
switch next {
|
|
|
|
|
case b.Succs[0].Block():
|
|
|
|
|
p = gc.Prog(jmp.invasm)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
|
|
|
|
|
case b.Succs[1].Block():
|
|
|
|
|
p = gc.Prog(jmp.asm)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
|
|
|
|
default:
|
|
|
|
|
p = gc.Prog(jmp.asm)
|
|
|
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
|
|
|
|
q := gc.Prog(obj.AJMP)
|
|
|
|
|
q.To.Type = obj.TYPE_BRANCH
|
|
|
|
|
s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
b.Unimplementedf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
|
2016-03-21 22:57:26 -07:00
|
|
|
}
|
|
|
|
|
}
|