]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/arm/ssa.go
internal/buildcfg: move build configuration out of cmd/internal/objabi
[gostls13.git] / src / cmd / compile / internal / arm / ssa.go
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package arm
6
7 import (
8         "fmt"
9         "internal/buildcfg"
10         "math"
11         "math/bits"
12
13         "cmd/compile/internal/base"
14         "cmd/compile/internal/ir"
15         "cmd/compile/internal/logopt"
16         "cmd/compile/internal/ssa"
17         "cmd/compile/internal/ssagen"
18         "cmd/compile/internal/types"
19         "cmd/internal/obj"
20         "cmd/internal/obj/arm"
21 )
22
23 // loadByType returns the load instruction of the given type.
24 func loadByType(t *types.Type) obj.As {
25         if t.IsFloat() {
26                 switch t.Size() {
27                 case 4:
28                         return arm.AMOVF
29                 case 8:
30                         return arm.AMOVD
31                 }
32         } else {
33                 switch t.Size() {
34                 case 1:
35                         if t.IsSigned() {
36                                 return arm.AMOVB
37                         } else {
38                                 return arm.AMOVBU
39                         }
40                 case 2:
41                         if t.IsSigned() {
42                                 return arm.AMOVH
43                         } else {
44                                 return arm.AMOVHU
45                         }
46                 case 4:
47                         return arm.AMOVW
48                 }
49         }
50         panic("bad load type")
51 }
52
53 // storeByType returns the store instruction of the given type.
54 func storeByType(t *types.Type) obj.As {
55         if t.IsFloat() {
56                 switch t.Size() {
57                 case 4:
58                         return arm.AMOVF
59                 case 8:
60                         return arm.AMOVD
61                 }
62         } else {
63                 switch t.Size() {
64                 case 1:
65                         return arm.AMOVB
66                 case 2:
67                         return arm.AMOVH
68                 case 4:
69                         return arm.AMOVW
70                 }
71         }
72         panic("bad store type")
73 }
74
75 // shift type is used as Offset in obj.TYPE_SHIFT operands to encode shifted register operands
76 type shift int64
77
78 // copied from ../../../internal/obj/util.go:/TYPE_SHIFT
79 func (v shift) String() string {
80         op := "<<>>->@>"[((v>>5)&3)<<1:]
81         if v&(1<<4) != 0 {
82                 // register shift
83                 return fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
84         } else {
85                 // constant shift
86                 return fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
87         }
88 }
89
90 // makeshift encodes a register shifted by a constant
91 func makeshift(reg int16, typ int64, s int64) shift {
92         return shift(int64(reg&0xf) | typ | (s&31)<<7)
93 }
94
95 // genshift generates a Prog for r = r0 op (r1 shifted by n)
96 func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
97         p := s.Prog(as)
98         p.From.Type = obj.TYPE_SHIFT
99         p.From.Offset = int64(makeshift(r1, typ, n))
100         p.Reg = r0
101         if r != 0 {
102                 p.To.Type = obj.TYPE_REG
103                 p.To.Reg = r
104         }
105         return p
106 }
107
108 // makeregshift encodes a register shifted by a register
109 func makeregshift(r1 int16, typ int64, r2 int16) shift {
110         return shift(int64(r1&0xf) | typ | int64(r2&0xf)<<8 | 1<<4)
111 }
112
113 // genregshift generates a Prog for r = r0 op (r1 shifted by r2)
114 func genregshift(s *ssagen.State, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
115         p := s.Prog(as)
116         p.From.Type = obj.TYPE_SHIFT
117         p.From.Offset = int64(makeregshift(r1, typ, r2))
118         p.Reg = r0
119         if r != 0 {
120                 p.To.Type = obj.TYPE_REG
121                 p.To.Reg = r
122         }
123         return p
124 }
125
126 // find a (lsb, width) pair for BFC
127 // lsb must be in [0, 31], width must be in [1, 32 - lsb]
128 // return (0xffffffff, 0) if v is not a binary like 0...01...10...0
129 func getBFC(v uint32) (uint32, uint32) {
130         var m, l uint32
131         // BFC is not applicable with zero
132         if v == 0 {
133                 return 0xffffffff, 0
134         }
135         // find the lowest set bit, for example l=2 for 0x3ffffffc
136         l = uint32(bits.TrailingZeros32(v))
137         // m-1 represents the highest set bit index, for example m=30 for 0x3ffffffc
138         m = 32 - uint32(bits.LeadingZeros32(v))
139         // check if v is a binary like 0...01...10...0
140         if (1<<m)-(1<<l) == v {
141                 // it must be m > l for non-zero v
142                 return l, m - l
143         }
144         // invalid
145         return 0xffffffff, 0
146 }
147
148 func ssaGenValue(s *ssagen.State, v *ssa.Value) {
149         switch v.Op {
150         case ssa.OpCopy, ssa.OpARMMOVWreg:
151                 if v.Type.IsMemory() {
152                         return
153                 }
154                 x := v.Args[0].Reg()
155                 y := v.Reg()
156                 if x == y {
157                         return
158                 }
159                 as := arm.AMOVW
160                 if v.Type.IsFloat() {
161                         switch v.Type.Size() {
162                         case 4:
163                                 as = arm.AMOVF
164                         case 8:
165                                 as = arm.AMOVD
166                         default:
167                                 panic("bad float size")
168                         }
169                 }
170                 p := s.Prog(as)
171                 p.From.Type = obj.TYPE_REG
172                 p.From.Reg = x
173                 p.To.Type = obj.TYPE_REG
174                 p.To.Reg = y
175         case ssa.OpARMMOVWnop:
176                 // nothing to do
177         case ssa.OpLoadReg:
178                 if v.Type.IsFlags() {
179                         v.Fatalf("load flags not implemented: %v", v.LongString())
180                         return
181                 }
182                 p := s.Prog(loadByType(v.Type))
183                 ssagen.AddrAuto(&p.From, v.Args[0])
184                 p.To.Type = obj.TYPE_REG
185                 p.To.Reg = v.Reg()
186         case ssa.OpStoreReg:
187                 if v.Type.IsFlags() {
188                         v.Fatalf("store flags not implemented: %v", v.LongString())
189                         return
190                 }
191                 p := s.Prog(storeByType(v.Type))
192                 p.From.Type = obj.TYPE_REG
193                 p.From.Reg = v.Args[0].Reg()
194                 ssagen.AddrAuto(&p.To, v)
195         case ssa.OpARMADD,
196                 ssa.OpARMADC,
197                 ssa.OpARMSUB,
198                 ssa.OpARMSBC,
199                 ssa.OpARMRSB,
200                 ssa.OpARMAND,
201                 ssa.OpARMOR,
202                 ssa.OpARMXOR,
203                 ssa.OpARMBIC,
204                 ssa.OpARMMUL,
205                 ssa.OpARMADDF,
206                 ssa.OpARMADDD,
207                 ssa.OpARMSUBF,
208                 ssa.OpARMSUBD,
209                 ssa.OpARMSLL,
210                 ssa.OpARMSRL,
211                 ssa.OpARMSRA,
212                 ssa.OpARMMULF,
213                 ssa.OpARMMULD,
214                 ssa.OpARMNMULF,
215                 ssa.OpARMNMULD,
216                 ssa.OpARMDIVF,
217                 ssa.OpARMDIVD:
218                 r := v.Reg()
219                 r1 := v.Args[0].Reg()
220                 r2 := v.Args[1].Reg()
221                 p := s.Prog(v.Op.Asm())
222                 p.From.Type = obj.TYPE_REG
223                 p.From.Reg = r2
224                 p.Reg = r1
225                 p.To.Type = obj.TYPE_REG
226                 p.To.Reg = r
227         case ssa.OpARMSRR:
228                 genregshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR)
229         case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD, ssa.OpARMFMULAD:
230                 r := v.Reg()
231                 r0 := v.Args[0].Reg()
232                 r1 := v.Args[1].Reg()
233                 r2 := v.Args[2].Reg()
234                 if r != r0 {
235                         v.Fatalf("result and addend are not in the same register: %v", v.LongString())
236                 }
237                 p := s.Prog(v.Op.Asm())
238                 p.From.Type = obj.TYPE_REG
239                 p.From.Reg = r2
240                 p.Reg = r1
241                 p.To.Type = obj.TYPE_REG
242                 p.To.Reg = r
243         case ssa.OpARMADDS,
244                 ssa.OpARMSUBS:
245                 r := v.Reg0()
246                 r1 := v.Args[0].Reg()
247                 r2 := v.Args[1].Reg()
248                 p := s.Prog(v.Op.Asm())
249                 p.Scond = arm.C_SBIT
250                 p.From.Type = obj.TYPE_REG
251                 p.From.Reg = r2
252                 p.Reg = r1
253                 p.To.Type = obj.TYPE_REG
254                 p.To.Reg = r
255         case ssa.OpARMSRAcond:
256                 // ARM shift instructions uses only the low-order byte of the shift amount
257                 // generate conditional instructions to deal with large shifts
258                 // flag is already set
259                 // SRA.HS       $31, Rarg0, Rdst // shift 31 bits to get the sign bit
260                 // SRA.LO       Rarg1, Rarg0, Rdst
261                 r := v.Reg()
262                 r1 := v.Args[0].Reg()
263                 r2 := v.Args[1].Reg()
264                 p := s.Prog(arm.ASRA)
265                 p.Scond = arm.C_SCOND_HS
266                 p.From.Type = obj.TYPE_CONST
267                 p.From.Offset = 31
268                 p.Reg = r1
269                 p.To.Type = obj.TYPE_REG
270                 p.To.Reg = r
271                 p = s.Prog(arm.ASRA)
272                 p.Scond = arm.C_SCOND_LO
273                 p.From.Type = obj.TYPE_REG
274                 p.From.Reg = r2
275                 p.Reg = r1
276                 p.To.Type = obj.TYPE_REG
277                 p.To.Reg = r
278         case ssa.OpARMBFX, ssa.OpARMBFXU:
279                 p := s.Prog(v.Op.Asm())
280                 p.From.Type = obj.TYPE_CONST
281                 p.From.Offset = v.AuxInt >> 8
282                 p.SetFrom3Const(v.AuxInt & 0xff)
283                 p.Reg = v.Args[0].Reg()
284                 p.To.Type = obj.TYPE_REG
285                 p.To.Reg = v.Reg()
286         case ssa.OpARMANDconst, ssa.OpARMBICconst:
287                 // try to optimize ANDconst and BICconst to BFC, which saves bytes and ticks
288                 // BFC is only available on ARMv7, and its result and source are in the same register
289                 if buildcfg.GOARM == 7 && v.Reg() == v.Args[0].Reg() {
290                         var val uint32
291                         if v.Op == ssa.OpARMANDconst {
292                                 val = ^uint32(v.AuxInt)
293                         } else { // BICconst
294                                 val = uint32(v.AuxInt)
295                         }
296                         lsb, width := getBFC(val)
297                         // omit BFC for ARM's imm12
298                         if 8 < width && width < 24 {
299                                 p := s.Prog(arm.ABFC)
300                                 p.From.Type = obj.TYPE_CONST
301                                 p.From.Offset = int64(width)
302                                 p.SetFrom3Const(int64(lsb))
303                                 p.To.Type = obj.TYPE_REG
304                                 p.To.Reg = v.Reg()
305                                 break
306                         }
307                 }
308                 // fall back to ordinary form
309                 fallthrough
310         case ssa.OpARMADDconst,
311                 ssa.OpARMADCconst,
312                 ssa.OpARMSUBconst,
313                 ssa.OpARMSBCconst,
314                 ssa.OpARMRSBconst,
315                 ssa.OpARMRSCconst,
316                 ssa.OpARMORconst,
317                 ssa.OpARMXORconst,
318                 ssa.OpARMSLLconst,
319                 ssa.OpARMSRLconst,
320                 ssa.OpARMSRAconst:
321                 p := s.Prog(v.Op.Asm())
322                 p.From.Type = obj.TYPE_CONST
323                 p.From.Offset = v.AuxInt
324                 p.Reg = v.Args[0].Reg()
325                 p.To.Type = obj.TYPE_REG
326                 p.To.Reg = v.Reg()
327         case ssa.OpARMADDSconst,
328                 ssa.OpARMSUBSconst,
329                 ssa.OpARMRSBSconst:
330                 p := s.Prog(v.Op.Asm())
331                 p.Scond = arm.C_SBIT
332                 p.From.Type = obj.TYPE_CONST
333                 p.From.Offset = v.AuxInt
334                 p.Reg = v.Args[0].Reg()
335                 p.To.Type = obj.TYPE_REG
336                 p.To.Reg = v.Reg0()
337         case ssa.OpARMSRRconst:
338                 genshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
339         case ssa.OpARMADDshiftLL,
340                 ssa.OpARMADCshiftLL,
341                 ssa.OpARMSUBshiftLL,
342                 ssa.OpARMSBCshiftLL,
343                 ssa.OpARMRSBshiftLL,
344                 ssa.OpARMRSCshiftLL,
345                 ssa.OpARMANDshiftLL,
346                 ssa.OpARMORshiftLL,
347                 ssa.OpARMXORshiftLL,
348                 ssa.OpARMBICshiftLL:
349                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
350         case ssa.OpARMADDSshiftLL,
351                 ssa.OpARMSUBSshiftLL,
352                 ssa.OpARMRSBSshiftLL:
353                 p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
354                 p.Scond = arm.C_SBIT
355         case ssa.OpARMADDshiftRL,
356                 ssa.OpARMADCshiftRL,
357                 ssa.OpARMSUBshiftRL,
358                 ssa.OpARMSBCshiftRL,
359                 ssa.OpARMRSBshiftRL,
360                 ssa.OpARMRSCshiftRL,
361                 ssa.OpARMANDshiftRL,
362                 ssa.OpARMORshiftRL,
363                 ssa.OpARMXORshiftRL,
364                 ssa.OpARMBICshiftRL:
365                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
366         case ssa.OpARMADDSshiftRL,
367                 ssa.OpARMSUBSshiftRL,
368                 ssa.OpARMRSBSshiftRL:
369                 p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
370                 p.Scond = arm.C_SBIT
371         case ssa.OpARMADDshiftRA,
372                 ssa.OpARMADCshiftRA,
373                 ssa.OpARMSUBshiftRA,
374                 ssa.OpARMSBCshiftRA,
375                 ssa.OpARMRSBshiftRA,
376                 ssa.OpARMRSCshiftRA,
377                 ssa.OpARMANDshiftRA,
378                 ssa.OpARMORshiftRA,
379                 ssa.OpARMXORshiftRA,
380                 ssa.OpARMBICshiftRA:
381                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
382         case ssa.OpARMADDSshiftRA,
383                 ssa.OpARMSUBSshiftRA,
384                 ssa.OpARMRSBSshiftRA:
385                 p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
386                 p.Scond = arm.C_SBIT
387         case ssa.OpARMXORshiftRR:
388                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
389         case ssa.OpARMMVNshiftLL:
390                 genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
391         case ssa.OpARMMVNshiftRL:
392                 genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
393         case ssa.OpARMMVNshiftRA:
394                 genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
395         case ssa.OpARMMVNshiftLLreg:
396                 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL)
397         case ssa.OpARMMVNshiftRLreg:
398                 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR)
399         case ssa.OpARMMVNshiftRAreg:
400                 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR)
401         case ssa.OpARMADDshiftLLreg,
402                 ssa.OpARMADCshiftLLreg,
403                 ssa.OpARMSUBshiftLLreg,
404                 ssa.OpARMSBCshiftLLreg,
405                 ssa.OpARMRSBshiftLLreg,
406                 ssa.OpARMRSCshiftLLreg,
407                 ssa.OpARMANDshiftLLreg,
408                 ssa.OpARMORshiftLLreg,
409                 ssa.OpARMXORshiftLLreg,
410                 ssa.OpARMBICshiftLLreg:
411                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LL)
412         case ssa.OpARMADDSshiftLLreg,
413                 ssa.OpARMSUBSshiftLLreg,
414                 ssa.OpARMRSBSshiftLLreg:
415                 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LL)
416                 p.Scond = arm.C_SBIT
417         case ssa.OpARMADDshiftRLreg,
418                 ssa.OpARMADCshiftRLreg,
419                 ssa.OpARMSUBshiftRLreg,
420                 ssa.OpARMSBCshiftRLreg,
421                 ssa.OpARMRSBshiftRLreg,
422                 ssa.OpARMRSCshiftRLreg,
423                 ssa.OpARMANDshiftRLreg,
424                 ssa.OpARMORshiftRLreg,
425                 ssa.OpARMXORshiftRLreg,
426                 ssa.OpARMBICshiftRLreg:
427                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LR)
428         case ssa.OpARMADDSshiftRLreg,
429                 ssa.OpARMSUBSshiftRLreg,
430                 ssa.OpARMRSBSshiftRLreg:
431                 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LR)
432                 p.Scond = arm.C_SBIT
433         case ssa.OpARMADDshiftRAreg,
434                 ssa.OpARMADCshiftRAreg,
435                 ssa.OpARMSUBshiftRAreg,
436                 ssa.OpARMSBCshiftRAreg,
437                 ssa.OpARMRSBshiftRAreg,
438                 ssa.OpARMRSCshiftRAreg,
439                 ssa.OpARMANDshiftRAreg,
440                 ssa.OpARMORshiftRAreg,
441                 ssa.OpARMXORshiftRAreg,
442                 ssa.OpARMBICshiftRAreg:
443                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_AR)
444         case ssa.OpARMADDSshiftRAreg,
445                 ssa.OpARMSUBSshiftRAreg,
446                 ssa.OpARMRSBSshiftRAreg:
447                 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_AR)
448                 p.Scond = arm.C_SBIT
449         case ssa.OpARMHMUL,
450                 ssa.OpARMHMULU:
451                 // 32-bit high multiplication
452                 p := s.Prog(v.Op.Asm())
453                 p.From.Type = obj.TYPE_REG
454                 p.From.Reg = v.Args[0].Reg()
455                 p.Reg = v.Args[1].Reg()
456                 p.To.Type = obj.TYPE_REGREG
457                 p.To.Reg = v.Reg()
458                 p.To.Offset = arm.REGTMP // throw away low 32-bit into tmp register
459         case ssa.OpARMMULLU:
460                 // 32-bit multiplication, results 64-bit, high 32-bit in out0, low 32-bit in out1
461                 p := s.Prog(v.Op.Asm())
462                 p.From.Type = obj.TYPE_REG
463                 p.From.Reg = v.Args[0].Reg()
464                 p.Reg = v.Args[1].Reg()
465                 p.To.Type = obj.TYPE_REGREG
466                 p.To.Reg = v.Reg0()           // high 32-bit
467                 p.To.Offset = int64(v.Reg1()) // low 32-bit
468         case ssa.OpARMMULA, ssa.OpARMMULS:
469                 p := s.Prog(v.Op.Asm())
470                 p.From.Type = obj.TYPE_REG
471                 p.From.Reg = v.Args[0].Reg()
472                 p.Reg = v.Args[1].Reg()
473                 p.To.Type = obj.TYPE_REGREG2
474                 p.To.Reg = v.Reg()                   // result
475                 p.To.Offset = int64(v.Args[2].Reg()) // addend
476         case ssa.OpARMMOVWconst:
477                 p := s.Prog(v.Op.Asm())
478                 p.From.Type = obj.TYPE_CONST
479                 p.From.Offset = v.AuxInt
480                 p.To.Type = obj.TYPE_REG
481                 p.To.Reg = v.Reg()
482         case ssa.OpARMMOVFconst,
483                 ssa.OpARMMOVDconst:
484                 p := s.Prog(v.Op.Asm())
485                 p.From.Type = obj.TYPE_FCONST
486                 p.From.Val = math.Float64frombits(uint64(v.AuxInt))
487                 p.To.Type = obj.TYPE_REG
488                 p.To.Reg = v.Reg()
489         case ssa.OpARMCMP,
490                 ssa.OpARMCMN,
491                 ssa.OpARMTST,
492                 ssa.OpARMTEQ,
493                 ssa.OpARMCMPF,
494                 ssa.OpARMCMPD:
495                 p := s.Prog(v.Op.Asm())
496                 p.From.Type = obj.TYPE_REG
497                 // Special layout in ARM assembly
498                 // Comparing to x86, the operands of ARM's CMP are reversed.
499                 p.From.Reg = v.Args[1].Reg()
500                 p.Reg = v.Args[0].Reg()
501         case ssa.OpARMCMPconst,
502                 ssa.OpARMCMNconst,
503                 ssa.OpARMTSTconst,
504                 ssa.OpARMTEQconst:
505                 // Special layout in ARM assembly
506                 p := s.Prog(v.Op.Asm())
507                 p.From.Type = obj.TYPE_CONST
508                 p.From.Offset = v.AuxInt
509                 p.Reg = v.Args[0].Reg()
510         case ssa.OpARMCMPF0,
511                 ssa.OpARMCMPD0:
512                 p := s.Prog(v.Op.Asm())
513                 p.From.Type = obj.TYPE_REG
514                 p.From.Reg = v.Args[0].Reg()
515         case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL:
516                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
517         case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL:
518                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
519         case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA:
520                 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
521         case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg:
522                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL)
523         case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg:
524                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LR)
525         case ssa.OpARMCMPshiftRAreg, ssa.OpARMCMNshiftRAreg, ssa.OpARMTSTshiftRAreg, ssa.OpARMTEQshiftRAreg:
526                 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_AR)
527         case ssa.OpARMMOVWaddr:
528                 p := s.Prog(arm.AMOVW)
529                 p.From.Type = obj.TYPE_ADDR
530                 p.From.Reg = v.Args[0].Reg()
531                 p.To.Type = obj.TYPE_REG
532                 p.To.Reg = v.Reg()
533
534                 var wantreg string
535                 // MOVW $sym+off(base), R
536                 // the assembler expands it as the following:
537                 // - base is SP: add constant offset to SP (R13)
538                 //               when constant is large, tmp register (R11) may be used
539                 // - base is SB: load external address from constant pool (use relocation)
540                 switch v.Aux.(type) {
541                 default:
542                         v.Fatalf("aux is of unknown type %T", v.Aux)
543                 case *obj.LSym:
544                         wantreg = "SB"
545                         ssagen.AddAux(&p.From, v)
546                 case *ir.Name:
547                         wantreg = "SP"
548                         ssagen.AddAux(&p.From, v)
549                 case nil:
550                         // No sym, just MOVW $off(SP), R
551                         wantreg = "SP"
552                         p.From.Offset = v.AuxInt
553                 }
554                 if reg := v.Args[0].RegName(); reg != wantreg {
555                         v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
556                 }
557
558         case ssa.OpARMMOVBload,
559                 ssa.OpARMMOVBUload,
560                 ssa.OpARMMOVHload,
561                 ssa.OpARMMOVHUload,
562                 ssa.OpARMMOVWload,
563                 ssa.OpARMMOVFload,
564                 ssa.OpARMMOVDload:
565                 p := s.Prog(v.Op.Asm())
566                 p.From.Type = obj.TYPE_MEM
567                 p.From.Reg = v.Args[0].Reg()
568                 ssagen.AddAux(&p.From, v)
569                 p.To.Type = obj.TYPE_REG
570                 p.To.Reg = v.Reg()
571         case ssa.OpARMMOVBstore,
572                 ssa.OpARMMOVHstore,
573                 ssa.OpARMMOVWstore,
574                 ssa.OpARMMOVFstore,
575                 ssa.OpARMMOVDstore:
576                 p := s.Prog(v.Op.Asm())
577                 p.From.Type = obj.TYPE_REG
578                 p.From.Reg = v.Args[1].Reg()
579                 p.To.Type = obj.TYPE_MEM
580                 p.To.Reg = v.Args[0].Reg()
581                 ssagen.AddAux(&p.To, v)
582         case ssa.OpARMMOVWloadidx, ssa.OpARMMOVBUloadidx, ssa.OpARMMOVBloadidx, ssa.OpARMMOVHUloadidx, ssa.OpARMMOVHloadidx:
583                 // this is just shift 0 bits
584                 fallthrough
585         case ssa.OpARMMOVWloadshiftLL:
586                 p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
587                 p.From.Reg = v.Args[0].Reg()
588         case ssa.OpARMMOVWloadshiftRL:
589                 p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
590                 p.From.Reg = v.Args[0].Reg()
591         case ssa.OpARMMOVWloadshiftRA:
592                 p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
593                 p.From.Reg = v.Args[0].Reg()
594         case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx:
595                 // this is just shift 0 bits
596                 fallthrough
597         case ssa.OpARMMOVWstoreshiftLL:
598                 p := s.Prog(v.Op.Asm())
599                 p.From.Type = obj.TYPE_REG
600                 p.From.Reg = v.Args[2].Reg()
601                 p.To.Type = obj.TYPE_SHIFT
602                 p.To.Reg = v.Args[0].Reg()
603                 p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
604         case ssa.OpARMMOVWstoreshiftRL:
605                 p := s.Prog(v.Op.Asm())
606                 p.From.Type = obj.TYPE_REG
607                 p.From.Reg = v.Args[2].Reg()
608                 p.To.Type = obj.TYPE_SHIFT
609                 p.To.Reg = v.Args[0].Reg()
610                 p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
611         case ssa.OpARMMOVWstoreshiftRA:
612                 p := s.Prog(v.Op.Asm())
613                 p.From.Type = obj.TYPE_REG
614                 p.From.Reg = v.Args[2].Reg()
615                 p.To.Type = obj.TYPE_SHIFT
616                 p.To.Reg = v.Args[0].Reg()
617                 p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
618         case ssa.OpARMMOVBreg,
619                 ssa.OpARMMOVBUreg,
620                 ssa.OpARMMOVHreg,
621                 ssa.OpARMMOVHUreg:
622                 a := v.Args[0]
623                 for a.Op == ssa.OpCopy || a.Op == ssa.OpARMMOVWreg || a.Op == ssa.OpARMMOVWnop {
624                         a = a.Args[0]
625                 }
626                 if a.Op == ssa.OpLoadReg {
627                         t := a.Type
628                         switch {
629                         case v.Op == ssa.OpARMMOVBreg && t.Size() == 1 && t.IsSigned(),
630                                 v.Op == ssa.OpARMMOVBUreg && t.Size() == 1 && !t.IsSigned(),
631                                 v.Op == ssa.OpARMMOVHreg && t.Size() == 2 && t.IsSigned(),
632                                 v.Op == ssa.OpARMMOVHUreg && t.Size() == 2 && !t.IsSigned():
633                                 // arg is a proper-typed load, already zero/sign-extended, don't extend again
634                                 if v.Reg() == v.Args[0].Reg() {
635                                         return
636                                 }
637                                 p := s.Prog(arm.AMOVW)
638                                 p.From.Type = obj.TYPE_REG
639                                 p.From.Reg = v.Args[0].Reg()
640                                 p.To.Type = obj.TYPE_REG
641                                 p.To.Reg = v.Reg()
642                                 return
643                         default:
644                         }
645                 }
646                 if buildcfg.GOARM >= 6 {
647                         // generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7
648                         genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0)
649                         return
650                 }
651                 fallthrough
652         case ssa.OpARMMVN,
653                 ssa.OpARMCLZ,
654                 ssa.OpARMREV,
655                 ssa.OpARMREV16,
656                 ssa.OpARMRBIT,
657                 ssa.OpARMSQRTF,
658                 ssa.OpARMSQRTD,
659                 ssa.OpARMNEGF,
660                 ssa.OpARMNEGD,
661                 ssa.OpARMABSD,
662                 ssa.OpARMMOVWF,
663                 ssa.OpARMMOVWD,
664                 ssa.OpARMMOVFW,
665                 ssa.OpARMMOVDW,
666                 ssa.OpARMMOVFD,
667                 ssa.OpARMMOVDF:
668                 p := s.Prog(v.Op.Asm())
669                 p.From.Type = obj.TYPE_REG
670                 p.From.Reg = v.Args[0].Reg()
671                 p.To.Type = obj.TYPE_REG
672                 p.To.Reg = v.Reg()
673         case ssa.OpARMMOVWUF,
674                 ssa.OpARMMOVWUD,
675                 ssa.OpARMMOVFWU,
676                 ssa.OpARMMOVDWU:
677                 p := s.Prog(v.Op.Asm())
678                 p.Scond = arm.C_UBIT
679                 p.From.Type = obj.TYPE_REG
680                 p.From.Reg = v.Args[0].Reg()
681                 p.To.Type = obj.TYPE_REG
682                 p.To.Reg = v.Reg()
683         case ssa.OpARMCMOVWHSconst:
684                 p := s.Prog(arm.AMOVW)
685                 p.Scond = arm.C_SCOND_HS
686                 p.From.Type = obj.TYPE_CONST
687                 p.From.Offset = v.AuxInt
688                 p.To.Type = obj.TYPE_REG
689                 p.To.Reg = v.Reg()
690         case ssa.OpARMCMOVWLSconst:
691                 p := s.Prog(arm.AMOVW)
692                 p.Scond = arm.C_SCOND_LS
693                 p.From.Type = obj.TYPE_CONST
694                 p.From.Offset = v.AuxInt
695                 p.To.Type = obj.TYPE_REG
696                 p.To.Reg = v.Reg()
697         case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
698                 s.Call(v)
699         case ssa.OpARMCALLudiv:
700                 p := s.Prog(obj.ACALL)
701                 p.To.Type = obj.TYPE_MEM
702                 p.To.Name = obj.NAME_EXTERN
703                 p.To.Sym = ir.Syms.Udiv
704         case ssa.OpARMLoweredWB:
705                 p := s.Prog(obj.ACALL)
706                 p.To.Type = obj.TYPE_MEM
707                 p.To.Name = obj.NAME_EXTERN
708                 p.To.Sym = v.Aux.(*obj.LSym)
709         case ssa.OpARMLoweredPanicBoundsA, ssa.OpARMLoweredPanicBoundsB, ssa.OpARMLoweredPanicBoundsC:
710                 p := s.Prog(obj.ACALL)
711                 p.To.Type = obj.TYPE_MEM
712                 p.To.Name = obj.NAME_EXTERN
713                 p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
714                 s.UseArgs(8) // space used in callee args area by assembly stubs
715         case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC:
716                 p := s.Prog(obj.ACALL)
717                 p.To.Type = obj.TYPE_MEM
718                 p.To.Name = obj.NAME_EXTERN
719                 p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
720                 s.UseArgs(12) // space used in callee args area by assembly stubs
721         case ssa.OpARMDUFFZERO:
722                 p := s.Prog(obj.ADUFFZERO)
723                 p.To.Type = obj.TYPE_MEM
724                 p.To.Name = obj.NAME_EXTERN
725                 p.To.Sym = ir.Syms.Duffzero
726                 p.To.Offset = v.AuxInt
727         case ssa.OpARMDUFFCOPY:
728                 p := s.Prog(obj.ADUFFCOPY)
729                 p.To.Type = obj.TYPE_MEM
730                 p.To.Name = obj.NAME_EXTERN
731                 p.To.Sym = ir.Syms.Duffcopy
732                 p.To.Offset = v.AuxInt
733         case ssa.OpARMLoweredNilCheck:
734                 // Issue a load which will fault if arg is nil.
735                 p := s.Prog(arm.AMOVB)
736                 p.From.Type = obj.TYPE_MEM
737                 p.From.Reg = v.Args[0].Reg()
738                 ssagen.AddAux(&p.From, v)
739                 p.To.Type = obj.TYPE_REG
740                 p.To.Reg = arm.REGTMP
741                 if logopt.Enabled() {
742                         logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
743                 }
744                 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
745                         base.WarnfAt(v.Pos, "generated nil check")
746                 }
747         case ssa.OpARMLoweredZero:
748                 // MOVW.P       Rarg2, 4(R1)
749                 // CMP  Rarg1, R1
750                 // BLE  -2(PC)
751                 // arg1 is the address of the last element to zero
752                 // arg2 is known to be zero
753                 // auxint is alignment
754                 var sz int64
755                 var mov obj.As
756                 switch {
757                 case v.AuxInt%4 == 0:
758                         sz = 4
759                         mov = arm.AMOVW
760                 case v.AuxInt%2 == 0:
761                         sz = 2
762                         mov = arm.AMOVH
763                 default:
764                         sz = 1
765                         mov = arm.AMOVB
766                 }
767                 p := s.Prog(mov)
768                 p.Scond = arm.C_PBIT
769                 p.From.Type = obj.TYPE_REG
770                 p.From.Reg = v.Args[2].Reg()
771                 p.To.Type = obj.TYPE_MEM
772                 p.To.Reg = arm.REG_R1
773                 p.To.Offset = sz
774                 p2 := s.Prog(arm.ACMP)
775                 p2.From.Type = obj.TYPE_REG
776                 p2.From.Reg = v.Args[1].Reg()
777                 p2.Reg = arm.REG_R1
778                 p3 := s.Prog(arm.ABLE)
779                 p3.To.Type = obj.TYPE_BRANCH
780                 p3.To.SetTarget(p)
781         case ssa.OpARMLoweredMove:
782                 // MOVW.P       4(R1), Rtmp
783                 // MOVW.P       Rtmp, 4(R2)
784                 // CMP  Rarg2, R1
785                 // BLE  -3(PC)
786                 // arg2 is the address of the last element of src
787                 // auxint is alignment
788                 var sz int64
789                 var mov obj.As
790                 switch {
791                 case v.AuxInt%4 == 0:
792                         sz = 4
793                         mov = arm.AMOVW
794                 case v.AuxInt%2 == 0:
795                         sz = 2
796                         mov = arm.AMOVH
797                 default:
798                         sz = 1
799                         mov = arm.AMOVB
800                 }
801                 p := s.Prog(mov)
802                 p.Scond = arm.C_PBIT
803                 p.From.Type = obj.TYPE_MEM
804                 p.From.Reg = arm.REG_R1
805                 p.From.Offset = sz
806                 p.To.Type = obj.TYPE_REG
807                 p.To.Reg = arm.REGTMP
808                 p2 := s.Prog(mov)
809                 p2.Scond = arm.C_PBIT
810                 p2.From.Type = obj.TYPE_REG
811                 p2.From.Reg = arm.REGTMP
812                 p2.To.Type = obj.TYPE_MEM
813                 p2.To.Reg = arm.REG_R2
814                 p2.To.Offset = sz
815                 p3 := s.Prog(arm.ACMP)
816                 p3.From.Type = obj.TYPE_REG
817                 p3.From.Reg = v.Args[2].Reg()
818                 p3.Reg = arm.REG_R1
819                 p4 := s.Prog(arm.ABLE)
820                 p4.To.Type = obj.TYPE_BRANCH
821                 p4.To.SetTarget(p)
822         case ssa.OpARMEqual,
823                 ssa.OpARMNotEqual,
824                 ssa.OpARMLessThan,
825                 ssa.OpARMLessEqual,
826                 ssa.OpARMGreaterThan,
827                 ssa.OpARMGreaterEqual,
828                 ssa.OpARMLessThanU,
829                 ssa.OpARMLessEqualU,
830                 ssa.OpARMGreaterThanU,
831                 ssa.OpARMGreaterEqualU:
832                 // generate boolean values
833                 // use conditional move
834                 p := s.Prog(arm.AMOVW)
835                 p.From.Type = obj.TYPE_CONST
836                 p.From.Offset = 0
837                 p.To.Type = obj.TYPE_REG
838                 p.To.Reg = v.Reg()
839                 p = s.Prog(arm.AMOVW)
840                 p.Scond = condBits[v.Op]
841                 p.From.Type = obj.TYPE_CONST
842                 p.From.Offset = 1
843                 p.To.Type = obj.TYPE_REG
844                 p.To.Reg = v.Reg()
845         case ssa.OpARMLoweredGetClosurePtr:
846                 // Closure pointer is R7 (arm.REGCTXT).
847                 ssagen.CheckLoweredGetClosurePtr(v)
848         case ssa.OpARMLoweredGetCallerSP:
849                 // caller's SP is FixedFrameSize below the address of the first arg
850                 p := s.Prog(arm.AMOVW)
851                 p.From.Type = obj.TYPE_ADDR
852                 p.From.Offset = -base.Ctxt.FixedFrameSize()
853                 p.From.Name = obj.NAME_PARAM
854                 p.To.Type = obj.TYPE_REG
855                 p.To.Reg = v.Reg()
856         case ssa.OpARMLoweredGetCallerPC:
857                 p := s.Prog(obj.AGETCALLERPC)
858                 p.To.Type = obj.TYPE_REG
859                 p.To.Reg = v.Reg()
860         case ssa.OpARMFlagConstant:
861                 v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
862         case ssa.OpARMInvertFlags:
863                 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
864         case ssa.OpClobber, ssa.OpClobberReg:
865                 // TODO: implement for clobberdead experiment. Nop is ok for now.
866         default:
867                 v.Fatalf("genValue not implemented: %s", v.LongString())
868         }
869 }
870
871 var condBits = map[ssa.Op]uint8{
872         ssa.OpARMEqual:         arm.C_SCOND_EQ,
873         ssa.OpARMNotEqual:      arm.C_SCOND_NE,
874         ssa.OpARMLessThan:      arm.C_SCOND_LT,
875         ssa.OpARMLessThanU:     arm.C_SCOND_LO,
876         ssa.OpARMLessEqual:     arm.C_SCOND_LE,
877         ssa.OpARMLessEqualU:    arm.C_SCOND_LS,
878         ssa.OpARMGreaterThan:   arm.C_SCOND_GT,
879         ssa.OpARMGreaterThanU:  arm.C_SCOND_HI,
880         ssa.OpARMGreaterEqual:  arm.C_SCOND_GE,
881         ssa.OpARMGreaterEqualU: arm.C_SCOND_HS,
882 }
883
884 var blockJump = map[ssa.BlockKind]struct {
885         asm, invasm obj.As
886 }{
887         ssa.BlockARMEQ:     {arm.ABEQ, arm.ABNE},
888         ssa.BlockARMNE:     {arm.ABNE, arm.ABEQ},
889         ssa.BlockARMLT:     {arm.ABLT, arm.ABGE},
890         ssa.BlockARMGE:     {arm.ABGE, arm.ABLT},
891         ssa.BlockARMLE:     {arm.ABLE, arm.ABGT},
892         ssa.BlockARMGT:     {arm.ABGT, arm.ABLE},
893         ssa.BlockARMULT:    {arm.ABLO, arm.ABHS},
894         ssa.BlockARMUGE:    {arm.ABHS, arm.ABLO},
895         ssa.BlockARMUGT:    {arm.ABHI, arm.ABLS},
896         ssa.BlockARMULE:    {arm.ABLS, arm.ABHI},
897         ssa.BlockARMLTnoov: {arm.ABMI, arm.ABPL},
898         ssa.BlockARMGEnoov: {arm.ABPL, arm.ABMI},
899 }
900
901 // To model a 'LEnoov' ('<=' without overflow checking) branching
902 var leJumps = [2][2]ssagen.IndexJump{
903         {{Jump: arm.ABEQ, Index: 0}, {Jump: arm.ABPL, Index: 1}}, // next == b.Succs[0]
904         {{Jump: arm.ABMI, Index: 0}, {Jump: arm.ABEQ, Index: 0}}, // next == b.Succs[1]
905 }
906
907 // To model a 'GTnoov' ('>' without overflow checking) branching
908 var gtJumps = [2][2]ssagen.IndexJump{
909         {{Jump: arm.ABMI, Index: 1}, {Jump: arm.ABEQ, Index: 1}}, // next == b.Succs[0]
910         {{Jump: arm.ABEQ, Index: 1}, {Jump: arm.ABPL, Index: 0}}, // next == b.Succs[1]
911 }
912
913 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
914         switch b.Kind {
915         case ssa.BlockPlain:
916                 if b.Succs[0].Block() != next {
917                         p := s.Prog(obj.AJMP)
918                         p.To.Type = obj.TYPE_BRANCH
919                         s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
920                 }
921
922         case ssa.BlockDefer:
923                 // defer returns in R0:
924                 // 0 if we should continue executing
925                 // 1 if we should jump to deferreturn call
926                 p := s.Prog(arm.ACMP)
927                 p.From.Type = obj.TYPE_CONST
928                 p.From.Offset = 0
929                 p.Reg = arm.REG_R0
930                 p = s.Prog(arm.ABNE)
931                 p.To.Type = obj.TYPE_BRANCH
932                 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
933                 if b.Succs[0].Block() != next {
934                         p := s.Prog(obj.AJMP)
935                         p.To.Type = obj.TYPE_BRANCH
936                         s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
937                 }
938
939         case ssa.BlockExit:
940
941         case ssa.BlockRet:
942                 s.Prog(obj.ARET)
943
944         case ssa.BlockRetJmp:
945                 p := s.Prog(obj.ARET)
946                 p.To.Type = obj.TYPE_MEM
947                 p.To.Name = obj.NAME_EXTERN
948                 p.To.Sym = b.Aux.(*obj.LSym)
949
950         case ssa.BlockARMEQ, ssa.BlockARMNE,
951                 ssa.BlockARMLT, ssa.BlockARMGE,
952                 ssa.BlockARMLE, ssa.BlockARMGT,
953                 ssa.BlockARMULT, ssa.BlockARMUGT,
954                 ssa.BlockARMULE, ssa.BlockARMUGE,
955                 ssa.BlockARMLTnoov, ssa.BlockARMGEnoov:
956                 jmp := blockJump[b.Kind]
957                 switch next {
958                 case b.Succs[0].Block():
959                         s.Br(jmp.invasm, b.Succs[1].Block())
960                 case b.Succs[1].Block():
961                         s.Br(jmp.asm, b.Succs[0].Block())
962                 default:
963                         if b.Likely != ssa.BranchUnlikely {
964                                 s.Br(jmp.asm, b.Succs[0].Block())
965                                 s.Br(obj.AJMP, b.Succs[1].Block())
966                         } else {
967                                 s.Br(jmp.invasm, b.Succs[1].Block())
968                                 s.Br(obj.AJMP, b.Succs[0].Block())
969                         }
970                 }
971
972         case ssa.BlockARMLEnoov:
973                 s.CombJump(b, next, &leJumps)
974
975         case ssa.BlockARMGTnoov:
976                 s.CombJump(b, next, &gtJumps)
977
978         default:
979                 b.Fatalf("branch not implemented: %s", b.LongString())
980         }
981 }