[Qt]REGRESSION(r92254): It made 2 tests timeout
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / armv7.rb
1 # Copyright (C) 2011 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 #    notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 #    notice, this list of conditions and the following disclaimer in the
10 #    documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22 # THE POSSIBILITY OF SUCH DAMAGE.
23
24 require "ast"
25 require "opt"
26
27 class Node
28     def armV7Single
29         doubleOperand = armV7Operand
30         raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^d/
31         "s" + ($~.post_match.to_i * 2).to_s
32     end
33 end
34
35 class SpecialRegister < NoChildren
36     def initialize(name)
37         @name = name
38     end
39     
40     def armV7Operand
41         @name
42     end
43     
44     def address?
45         false
46     end
47     
48     def label?
49         false
50     end
51     
52     def immediate?
53         false
54     end
55     
56     def register?
57         true
58     end
59 end
60
61 ARMv7_EXTRA_GPRS = [SpecialRegister.new("r9"), SpecialRegister.new("r8"), SpecialRegister.new("r3")]
62 ARMv7_EXTRA_FPRS = [SpecialRegister.new("d7")]
63 ARMv7_SCRATCH_FPR = SpecialRegister.new("d8")
64
65 def armV7MoveImmediate(value, register)
66     # Currently we only handle the simple cases, and fall back to mov/movt for the complex ones.
67     if value >= 0 && value < 256
68         $asm.puts "movw #{register.armV7Operand}, \##{value}"
69     elsif (~value) >= 0 && (~value) < 256
70         $asm.puts "mvn #{register.armV7Operand}, \##{~value}"
71     else
72         $asm.puts "movw #{register.armV7Operand}, \##{value & 0xffff}"
73         if (value & 0xffff0000) != 0
74             $asm.puts "movt #{register.armV7Operand}, \##{value >> 16}"
75         end
76     end
77 end
78
79 class RegisterID
80     def armV7Operand
81         case name
82         when "t0", "a0", "r0"
83             "r0"
84         when "t1", "a1", "r1"
85             "r1"
86         when "t2", "a2"
87             "r2"
88         when "a3"
89             "r3"
90         when "t3"
91             "r4"
92         when "t4"
93             "r7"
94         when "cfr"
95             "r5"
96         when "lr"
97             "lr"
98         when "sp"
99             "sp"
100         else
101             raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
102         end
103     end
104 end
105
106 class FPRegisterID
107     def armV7Operand
108         case name
109         when "ft0", "fr"
110             "d0"
111         when "ft1"
112             "d1"
113         when "ft2"
114             "d2"
115         when "ft3"
116             "d3"
117         when "ft4"
118             "d4"
119         when "ft5"
120             "d5"
121         else
122             raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
123         end
124     end
125 end
126
127 class Immediate
128     def armV7Operand
129         raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 255
130         "\##{value}"
131     end
132 end
133
134 class Address
135     def armV7Operand
136         raise "Bad offset at #{codeOriginString}" if offset.value < -0xff or offset.value > 0xfff
137         "[#{base.armV7Operand}, \##{offset.value}]"
138     end
139 end
140
141 class BaseIndex
142     def armV7Operand
143         raise "Bad offset at #{codeOriginString}" if offset.value != 0
144         "[#{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}]"
145     end
146 end
147
148 class AbsoluteAddress
149     def armV7Operand
150         raise "Unconverted absolute address at #{codeOriginString}"
151     end
152 end
153
154 #
155 # Lowering of branch ops. For example:
156 #
157 # baddiz foo, bar, baz
158 #
159 # will become:
160 #
161 # addi foo, bar
162 # bz baz
163 #
164
165 def armV7LowerBranchOps(list)
166     newList = []
167     list.each {
168         | node |
169         if node.is_a? Instruction
170             case node.opcode
171             when /^b(addi|subi|ori|addp)/
172                 op = $1
173                 branch = "b" + $~.post_match
174                 
175                 case op
176                 when "addi", "addp"
177                     op = "addis"
178                 when "subi"
179                     op = "subis"
180                 when "ori"
181                     op = "oris"
182                 end
183                 
184                 newList << Instruction.new(node.codeOrigin, op, node.operands[0..-2])
185                 newList << Instruction.new(node.codeOrigin, branch, [node.operands[-1]])
186             when "bmulio"
187                 tmp1 = Tmp.new(node.codeOrigin, :gpr)
188                 tmp2 = Tmp.new(node.codeOrigin, :gpr)
189                 newList << Instruction.new(node.codeOrigin, "smulli", [node.operands[0], node.operands[1], node.operands[1], tmp1])
190                 newList << Instruction.new(node.codeOrigin, "rshifti", [node.operands[-2], Immediate.new(node.codeOrigin, 31), tmp2])
191                 newList << Instruction.new(node.codeOrigin, "bineq", [tmp1, tmp2, node.operands[-1]])
192             when /^bmuli/
193                 condition = $~.post_match
194                 newList << Instruction.new(node.codeOrigin, "muli", node.operands[0..-2])
195                 newList << Instruction.new(node.codeOrigin, "bti" + condition, [node.operands[-2], node.operands[-1]])
196             else
197                 newList << node
198             end
199         else
200             newList << node
201         end
202     }
203     newList
204 end
205
206 #
207 # Lowering of shift ops. For example:
208 #
209 # lshifti foo, bar
210 #
211 # will become:
212 #
213 # andi foo, 31, tmp
214 # lshifti tmp, bar
215 #
216
217 def armV7SanitizeShift(operand, list)
218     return operand if operand.immediate?
219     
220     tmp = Tmp.new(operand.codeOrigin, :gpr)
221     list << Instruction.new(operand.codeOrigin, "andi", [operand, Immediate.new(operand.codeOrigin, 31), tmp])
222     tmp
223 end
224
225 def armV7LowerShiftOps(list)
226     newList = []
227     list.each {
228         | node |
229         if node.is_a? Instruction
230             case node.opcode
231             when "lshifti", "rshifti", "urshifti"
232                 if node.operands.size == 2
233                     newList << Instruction.new(node.codeOrigin, node.opcode, [armV7SanitizeShift(node.operands[0], newList), node.operands[1]])
234                 else
235                     newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], armV7SanitizeShift(node.operands[1], newList), node.operands[2]])
236                     raise "Wrong number of operands for shift at #{node.codeOriginString}" unless node.operands.size == 3
237                 end
238             else
239                 newList << node
240             end
241         else
242             newList << node
243         end
244     }
245     newList
246 end
247
248 #
249 # Lowering of malformed addresses. For example:
250 #
251 # loadp 10000[foo], bar
252 #
253 # will become:
254 #
255 # move 10000, tmp
256 # addp foo, tmp
257 # loadp 0[tmp], bar
258 #
259
260 class Node
261     def armV7LowerMalformedAddressesRecurse(list)
262         mapChildren {
263             | node |
264             node.armV7LowerMalformedAddressesRecurse(list)
265         }
266     end
267 end
268
269 class Address
270     def armV7LowerMalformedAddressesRecurse(list)
271         if offset.value < -0xff or offset.value > 0xfff
272             tmp = Tmp.new(codeOrigin, :gpr)
273             list << Instruction.new(codeOrigin, "move", [offset, tmp])
274             list << Instruction.new(codeOrigin, "addp", [base, tmp])
275             Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
276         else
277             self
278         end
279     end
280 end
281
282 class BaseIndex
283     def armV7LowerMalformedAddressesRecurse(list)
284         if offset.value != 0
285             tmp = Tmp.new(codeOrigin, :gpr)
286             list << Instruction.new(codeOrigin, "move", [offset, tmp])
287             list << Instruction.new(codeOrigin, "addp", [base, tmp])
288             BaseIndex.new(codeOrigin, tmp, index, scale, Immediate.new(codeOrigin, 0))
289         else
290             self
291         end
292     end
293 end
294
295 class AbsoluteAddress
296     def armV7LowerMalformedAddressesRecurse(list)
297         tmp = Tmp.new(codeOrigin, :gpr)
298         list << Instruction.new(codeOrigin, "move", [address, tmp])
299         Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
300     end
301 end
302
303 def armV7LowerMalformedAddresses(list)
304     newList = []
305     list.each {
306         | node |
307         newList << node.armV7LowerMalformedAddressesRecurse(newList)
308     }
309     newList
310 end
311
312 #
313 # Lowering of malformed addresses in double loads and stores. For example:
314 #
315 # loadd [foo, bar, 8], baz
316 #
317 # becomes:
318 #
319 # leap [foo, bar, 8], tmp
320 # loadd [tmp], baz
321 #
322
323 class Node
324     def armV7DoubleAddress(list)
325         self
326     end
327 end
328
329 class BaseIndex
330     def armV7DoubleAddress(list)
331         tmp = Tmp.new(codeOrigin, :gpr)
332         list << Instruction.new(codeOrigin, "leap", [self, tmp])
333         Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
334     end
335 end
336
337 def armV7LowerMalformedAddressesDouble(list)
338     newList = []
339     list.each {
340         | node |
341         if node.is_a? Instruction
342             case node.opcode
343             when "loadd"
344                 newList << Instruction.new(node.codeOrigin, "loadd", [node.operands[0].armV7DoubleAddress(newList), node.operands[1]])
345             when "stored"
346                 newList << Instruction.new(node.codeOrigin, "stored", [node.operands[0], node.operands[1].armV7DoubleAddress(newList)])
347             else
348                 newList << node
349             end
350         else
351             newList << node
352         end
353     }
354     newList
355 end
356
357 #
358 # Lowering of misplaced immediates. For example:
359 #
360 # storei 0, [foo]
361 #
362 # will become:
363 #
364 # move 0, tmp
365 # storei tmp, [foo]
366 #
367
368 def armV7LowerMisplacedImmediates(list)
369     newList = []
370     list.each {
371         | node |
372         if node.is_a? Instruction
373             case node.opcode
374             when "storei", "storep"
375                 operands = node.operands
376                 newOperands = []
377                 operands.each {
378                     | operand |
379                     if operand.is_a? Immediate
380                         tmp = Tmp.new(operand.codeOrigin, :gpr)
381                         newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp])
382                         newOperands << tmp
383                     else
384                         newOperands << operand
385                     end
386                 }
387                 newList << Instruction.new(node.codeOrigin, node.opcode, newOperands)
388             else
389                 newList << node
390             end
391         else
392             newList << node
393         end
394     }
395     newList
396 end
397
398 #
399 # Lowering of malformed immediates except when used in a "move" instruction.
400 # For example:
401 #
402 # addp 642641, foo
403 #
404 # will become:
405 #
406 # move 642641, tmp
407 # addp tmp, foo
408 #
409
410 class Node
411     def armV7LowerMalformedImmediatesRecurse(list)
412         mapChildren {
413             | node |
414             node.armV7LowerMalformedImmediatesRecurse(list)
415         }
416     end
417 end
418
419 class Address
420     def armV7LowerMalformedImmediatesRecurse(list)
421         self
422     end
423 end
424
425 class BaseIndex
426     def armV7LowerMalformedImmediatesRecurse(list)
427         self
428     end
429 end
430
431 class AbsoluteAddress
432     def armV7LowerMalformedImmediatesRecurse(list)
433         self
434     end
435 end
436
437 class Immediate
438     def armV7LowerMalformedImmediatesRecurse(list)
439         if value < 0 or value > 255
440             tmp = Tmp.new(codeOrigin, :gpr)
441             list << Instruction.new(codeOrigin, "move", [self, tmp])
442             tmp
443         else
444             self
445         end
446     end
447 end
448
449 def armV7LowerMalformedImmediates(list)
450     newList = []
451     list.each {
452         | node |
453         if node.is_a? Instruction
454             case node.opcode
455             when "move"
456                 newList << node
457             when "addi", "addp", "addis", "subi", "subp", "subis"
458                 if node.operands[0].is_a? Immediate and
459                         node.operands[0].value < 0 and
460                         node.operands[0].value >= 255 and
461                         node.operands.size == 2
462                     if node.opcode =~ /add/
463                         newOpcode = "sub" + node.opcode[-1..-1]
464                     else
465                         newOpcode = "add" + node.opcode[-1..-1]
466                     end
467                     newList << Instruction.new(node.codeOrigin, newOpcode,
468                                                [Immediate.new(-node.operands[0].value)] + node.operands[1..-1])
469                 else
470                     newList << node.armV7LowerMalformedImmediatesRecurse(newList)
471                 end
472             when "muli"
473                 if node.operands[0].is_a? Immediate
474                     tmp = Tmp.new(codeOrigin, :gpr)
475                     newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
476                     newList << Instruction.new(node.codeOrigin, "muli", [tmp] + node.operands[1..-1])
477                 else
478                     newList << node.armV7LowerMalformedImmediatesRecurse(newList)
479                 end
480             else
481                 newList << node.armV7LowerMalformedImmediatesRecurse(newList)
482             end
483         else
484             newList << node
485         end
486     }
487     newList
488 end
489
490 #
491 # Lowering of misplaced addresses. For example:
492 #
493 # addi foo, [bar]
494 #
495 # will become:
496 #
497 # loadi [bar], tmp
498 # addi foo, tmp
499 # storei tmp, [bar]
500 #
501 # Another example:
502 #
503 # addi [foo], bar
504 #
505 # will become:
506 #
507 # loadi [foo], tmp
508 # addi tmp, bar
509 #
510
511 def armV7AsRegister(preList, postList, operand, suffix, needStore)
512     return operand unless operand.address?
513     
514     tmp = Tmp.new(operand.codeOrigin, if suffix == "d" then :fpr else :gpr end)
515     preList << Instruction.new(operand.codeOrigin, "load" + suffix, [operand, tmp])
516     if needStore
517         postList << Instruction.new(operand.codeOrigin, "store" + suffix, [tmp, operand])
518     end
519     tmp
520 end
521
522 def armV7AsRegisters(preList, postList, operands, suffix)
523     newOperands = []
524     operands.each_with_index {
525         | operand, index |
526         newOperands << armV7AsRegister(preList, postList, operand, suffix, index == operands.size - 1)
527     }
528     newOperands
529 end
530
531 def armV7LowerMisplacedAddresses(list)
532     newList = []
533     list.each {
534         | node |
535         if node.is_a? Instruction
536             postInstructions = []
537             case node.opcode
538             when "addi", "addp", "addis", "andi", "andp", "lshifti", "muli", "negi", "noti", "ori", "oris",
539                 "orp", "rshifti", "urshifti", "subi", "subp", "subis", "xori", "xorp", /^bi/, /^bp/, /^bti/,
540                 /^btp/, /^ci/, /^cp/, /^ti/
541                 newList << Instruction.new(node.codeOrigin,
542                                            node.opcode,
543                                            armV7AsRegisters(newList, postInstructions, node.operands, "i"))
544             when "bbeq", "bbneq", "bba", "bbaeq", "bbb", "bbbeq", "btbo", "btbz", "btbnz", "tbz", "tbnz",
545                 "tbo"
546                 newList << Instruction.new(node.codeOrigin,
547                                            node.opcode,
548                                            armV7AsRegisters(newList, postInstructions, node.operands, "b"))
549             when "bbgt", "bbgteq", "bblt", "bblteq", "btbs", "tbs"
550                 newList << Instruction.new(node.codeOrigin,
551                                            node.opcode,
552                                            armV7AsRegisters(newList, postInstructions, node.operands, "bs"))
553             when "addd", "divd", "subd", "muld", "sqrtd", /^bd/
554                 newList << Instruction.new(node.codeOrigin,
555                                            node.opcode,
556                                            armV7AsRegisters(newList, postInstructions, node.operands, "d"))
557             when "jmp", "call"
558                 newList << Instruction.new(node.codeOrigin,
559                                            node.opcode,
560                                            [armV7AsRegister(newList, postInstructions, node.operands[0], "p", false)])
561             else
562                 newList << node
563             end
564             newList += postInstructions
565         else
566             newList << node
567         end
568     }
569     newList
570 end
571
572 #
573 # Lowering of register reuse in compare instructions. For example:
574 #
575 # cieq t0, t1, t0
576 #
577 # will become:
578 #
579 # mov tmp, t0
580 # cieq tmp, t1, t0
581 #
582
583 def armV7LowerRegisterReuse(list)
584     newList = []
585     list.each {
586         | node |
587         if node.is_a? Instruction
588             case node.opcode
589             when "cieq", "cineq", "cia", "ciaeq", "cib", "cibeq", "cigt", "cigteq", "cilt", "cilteq",
590                 "cpeq", "cpneq", "cpa", "cpaeq", "cpb", "cpbeq", "cpgt", "cpgteq", "cplt", "cplteq",
591                 "tio", "tis", "tiz", "tinz", "tbo", "tbs", "tbz", "tbnz"
592                 if node.operands.size == 2
593                     if node.operands[0] == node.operands[1]
594                         tmp = Tmp.new(node.codeOrigin, :gpr)
595                         newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
596                         newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1]])
597                     else
598                         newList << node
599                     end
600                 else
601                     raise "Wrong number of arguments at #{node.codeOriginString}" unless node.operands.size == 3
602                     if node.operands[0] == node.operands[2]
603                         tmp = Tmp.new(node.codeOrigin, :gpr)
604                         newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
605                         newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1], node.operands[2]])
606                     elsif node.operands[1] == node.operands[2]
607                         tmp = Tmp.new(node.codeOrigin, :gpr)
608                         newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp])
609                         newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], tmp, node.operands[2]])
610                     else
611                         newList << node
612                     end
613                 end
614             else
615                 newList << node
616             end
617         else
618             newList << node
619         end
620     }
621     newList
622 end
623
624 #
625 # Lea support.
626 #
627
628 class Address
629     def armV7EmitLea(destination)
630         if destination == base
631             $asm.puts "adds #{destination.armV7Operand}, \##{offset.value}"
632         else
633             $asm.puts "adds #{destination.armV7Operand}, #{base.armV7Operand}, \##{offset.value}"
634         end
635     end
636 end
637
638 class BaseIndex
639     def armV7EmitLea(destination)
640         raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
641         $asm.puts "add.w #{destination.armV7Operand}, #{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}"
642     end
643 end
644
645 # FIXME: we could support AbsoluteAddress for lea, but we don't.
646
647 #
648 # Actual lowering code follows.
649 #
650
651 class Sequence
652     def lowerARMv7
653         myList = @list
654         
655         # Verify that we will only see instructions and labels.
656         myList.each {
657             | node |
658             unless node.is_a? Instruction or
659                     node.is_a? Label or
660                     node.is_a? LocalLabel or
661                     node.is_a? Skip
662                 raise "Unexpected #{node.inspect} at #{node.codeOrigin}" 
663             end
664         }
665         
666         myList = armV7LowerBranchOps(myList)
667         myList = armV7LowerShiftOps(myList)
668         myList = armV7LowerMalformedAddresses(myList)
669         myList = armV7LowerMalformedAddressesDouble(myList)
670         myList = armV7LowerMisplacedImmediates(myList)
671         myList = armV7LowerMalformedImmediates(myList)
672         myList = armV7LowerMisplacedAddresses(myList)
673         myList = armV7LowerRegisterReuse(myList)
674         myList = assignRegistersToTemporaries(myList, :gpr, ARMv7_EXTRA_GPRS)
675         myList = assignRegistersToTemporaries(myList, :fpr, ARMv7_EXTRA_FPRS)
676         myList.each {
677             | node |
678             node.lower("ARMv7")
679         }
680     end
681 end
682
683 def armV7Operands(operands)
684     operands.map{|v| v.armV7Operand}.join(", ")
685 end
686
687 def armV7FlippedOperands(operands)
688     armV7Operands([operands[-1]] + operands[0..-2])
689 end
690
691 def emitArmV7Compact(opcode2, opcode3, operands)
692     if operands.size == 3
693         $asm.puts "#{opcode3} #{armV7FlippedOperands(operands)}"
694     else
695         raise unless operands.size == 2
696         raise unless operands[1].is_a? RegisterID
697         if operands[0].is_a? Immediate
698             $asm.puts "#{opcode3} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
699         else
700             $asm.puts "#{opcode2} #{armV7FlippedOperands(operands)}"
701         end
702     end
703 end
704
705 def emitArmV7(opcode, operands)
706     if operands.size == 3
707         $asm.puts "#{opcode} #{armV7FlippedOperands(operands)}"
708     else
709         raise unless operands.size == 2
710         $asm.puts "#{opcode} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
711     end
712 end
713
714 def emitArmV7DoubleBranch(branchOpcode, operands)
715     $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
716     $asm.puts "vmrs apsr_nzcv, fpscr"
717     $asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
718 end
719
720 def emitArmV7Test(operands)
721     value = operands[0]
722     case operands.size
723     when 2
724         mask = Immediate.new(codeOrigin, -1)
725     when 3
726         mask = operands[1]
727     else
728         raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
729     end
730     
731     if mask.is_a? Immediate and mask.value == -1
732         $asm.puts "tst #{value.armV7Operand}, #{value.armV7Operand}"
733     elsif mask.is_a? Immediate
734         $asm.puts "tst.w #{value.armV7Operand}, #{mask.armV7Operand}"
735     else
736         $asm.puts "tst #{value.armV7Operand}, #{mask.armV7Operand}"
737     end
738 end
739
740 def emitArmV7Compare(operands, code)
741     $asm.puts "movs #{operands[2].armV7Operand}, \#0"
742     $asm.puts "cmp #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
743     $asm.puts "it #{code}"
744     $asm.puts "mov#{code} #{operands[2].armV7Operand}, \#1"
745 end
746
747 def emitArmV7TestSet(operands, code)
748     $asm.puts "movs #{operands[-1].armV7Operand}, \#0"
749     emitArmV7Test(operands)
750     $asm.puts "it #{code}"
751     $asm.puts "mov#{code} #{operands[-1].armV7Operand}, \#1"
752 end
753
754 class Instruction
755     def lowerARMv7
756         $asm.comment codeOriginString
757         case opcode
758         when "addi", "addp", "addis"
759             if opcode == "addis"
760                 suffix = "s"
761             else
762                 suffix = ""
763             end
764             if operands.size == 3 and operands[0].is_a? Immediate
765                 raise unless operands[1].is_a? RegisterID
766                 raise unless operands[2].is_a? RegisterID
767                 if operands[0].value == 0 and suffix.empty?
768                     unless operands[1] == operands[2]
769                         $asm.puts "mov #{operands[2].armV7Operand}, #{operands[1].armV7Operand}"
770                     end
771                 else
772                     $asm.puts "adds #{operands[2].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
773                 end
774             elsif operands.size == 3 and operands[0].is_a? RegisterID
775                 raise unless operands[1].is_a? RegisterID
776                 raise unless operands[2].is_a? RegisterID
777                 $asm.puts "adds #{armV7FlippedOperands(operands)}"
778             else
779                 if operands[0].is_a? Immediate
780                     unless Immediate.new(nil, 0) == operands[0]
781                         $asm.puts "adds #{armV7FlippedOperands(operands)}"
782                     end
783                 else
784                     $asm.puts "add#{suffix} #{armV7FlippedOperands(operands)}"
785                 end
786             end
787         when "andi", "andp"
788             emitArmV7Compact("ands", "and", operands)
789         when "ori", "orp"
790             emitArmV7Compact("orrs", "orr", operands)
791         when "oris"
792             emitArmV7Compact("orrs", "orrs", operands)
793         when "xori", "xorp"
794             emitArmV7Compact("eors", "eor", operands)
795         when "lshifti"
796             emitArmV7Compact("lsls", "lsls", operands)
797         when "rshifti"
798             emitArmV7Compact("asrs", "asrs", operands)
799         when "urshifti"
800             emitArmV7Compact("lsrs", "lsrs", operands)
801         when "muli"
802             if operands.size == 2 or operands[0] == operands[2] or operands[1] == operands[2]
803                 emitArmV7("muls", operands)
804             else
805                 $asm.puts "mov #{operands[2].armV7Operand}, #{operands[0].armV7Operand}"
806                 $asm.puts "muls #{operands[2].armV7Operand}, #{operands[2].armV7Operand}, #{operands[1].armV7Operand}"
807             end
808         when "subi", "subp", "subis"
809             emitArmV7Compact("subs", "subs", operands)
810         when "negi"
811             $asm.puts "rsbs #{operands[0].armV7Operand}, #{operands[0].armV7Operand}, \#0"
812         when "noti"
813             $asm.puts "mvns #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
814         when "loadi", "loadp"
815             $asm.puts "ldr #{armV7FlippedOperands(operands)}"
816         when "storei", "storep"
817             $asm.puts "str #{armV7Operands(operands)}"
818         when "loadb"
819             $asm.puts "ldrb #{armV7FlippedOperands(operands)}"
820         when "loadbs"
821             $asm.puts "ldrsb.w #{armV7FlippedOperands(operands)}"
822         when "storeb"
823             $asm.puts "strb #{armV7Operands(operands)}"
824         when "loadh"
825             $asm.puts "ldrh #{armV7FlippedOperands(operands)}"
826         when "loadhs"
827             $asm.puts "ldrsh.w #{armV7FlippedOperands(operands)}"
828         when "storeh"
829             $asm.puts "strh #{armV7Operands(operands)}"
830         when "loadd"
831             $asm.puts "vldr.64 #{armV7FlippedOperands(operands)}"
832         when "stored"
833             $asm.puts "vstr.64 #{armV7Operands(operands)}"
834         when "addd"
835             emitArmV7("vadd.f64", operands)
836         when "divd"
837             emitArmV7("vdiv.f64", operands)
838         when "subd"
839             emitArmV7("vsub.f64", operands)
840         when "muld"
841             emitArmV7("vmul.f64", operands)
842         when "sqrtd"
843             $asm.puts "vsqrt.f64 #{armV7FlippedOperands(operands)}"
844         when "ci2d"
845             $asm.puts "vmov #{operands[1].armV7Single}, #{operands[0].armV7Operand}"
846             $asm.puts "vcvt.f64.s32 #{operands[1].armV7Operand}, #{operands[1].armV7Single}"
847         when "bdeq"
848             emitArmV7DoubleBranch("beq", operands)
849         when "bdneq"
850             $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
851             $asm.puts "vmrs apsr_nzcv, fpscr"
852             isUnordered = LocalLabel.unique("bdneq")
853             $asm.puts "bvs #{LabelReference.new(codeOrigin, isUnordered).asmLabel}"
854             $asm.puts "bne #{operands[2].asmLabel}"
855             isUnordered.lower("ARMv7")
856         when "bdgt"
857             emitArmV7DoubleBranch("bgt", operands)
858         when "bdgteq"
859             emitArmV7DoubleBranch("bge", operands)
860         when "bdlt"
861             emitArmV7DoubleBranch("bmi", operands)
862         when "bdlteq"
863             emitArmV7DoubleBranch("bls", operands)
864         when "bdequn"
865             $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
866             $asm.puts "vmrs apsr_nzcv, fpscr"
867             $asm.puts "bvs #{operands[2].asmLabel}"
868             $asm.puts "beq #{operands[2].asmLabel}"
869         when "bdnequn"
870             emitArmV7DoubleBranch("bne", operands)
871         when "bdgtun"
872             emitArmV7DoubleBranch("bhi", operands)
873         when "bdgtequn"
874             emitArmV7DoubleBranch("bpl", operands)
875         when "bdltun"
876             emitArmV7DoubleBranch("blt", operands)
877         when "bdltequn"
878             emitArmV7DoubleBranch("ble", operands)
879         when "btd2i"
880             # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
881             # currently does not use it.
882             raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
883         when "td2i"
884             $asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
885             $asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
886         when "bcd2i"
887             $asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
888             $asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
889             $asm.puts "vcvt.f64.s32 #{ARMv7_SCRATCH_FPR.armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
890             emitArmV7DoubleBranch("bne", [ARMv7_SCRATCH_FPR, operands[0], operands[2]])
891             $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
892             $asm.puts "beq #{operands[2].asmLabel}"
893         when "movdz"
894             # FIXME: either support this or remove it.
895             raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
896         when "pop"
897             $asm.puts "pop #{operands[0].armV7Operand}"
898         when "push"
899             $asm.puts "push #{operands[0].armV7Operand}"
900         when "move", "sxi2p", "zxi2p"
901             if operands[0].is_a? Immediate
902                 armV7MoveImmediate(operands[0].value, operands[1])
903             else
904                 $asm.puts "mov #{armV7FlippedOperands(operands)}"
905             end
906         when "nop"
907             $asm.puts "nop"
908         when "bieq", "bpeq", "bbeq"
909             if Immediate.new(nil, 0) == operands[0]
910                 $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
911             elsif Immediate.new(nil, 0) == operands[1]
912                 $asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
913             else
914                 $asm.puts "cmp #{armV7Operands(operands[0..1])}"
915             end
916             $asm.puts "beq #{operands[2].asmLabel}"
917         when "bineq", "bpneq", "bbneq"
918             if Immediate.new(nil, 0) == operands[0]
919                 $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
920             elsif Immediate.new(nil, 0) == operands[1]
921                 $asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
922             else
923                 $asm.puts "cmp #{armV7Operands(operands[0..1])}"
924             end
925             $asm.puts "bne #{operands[2].asmLabel}"
926         when "bia", "bpa", "bba"
927             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
928             $asm.puts "bhi #{operands[2].asmLabel}"
929         when "biaeq", "bpaeq", "bbaeq"
930             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
931             $asm.puts "bhs #{operands[2].asmLabel}"
932         when "bib", "bpb", "bbb"
933             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
934             $asm.puts "blo #{operands[2].asmLabel}"
935         when "bibeq", "bpbeq", "bbbeq"
936             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
937             $asm.puts "bls #{operands[2].asmLabel}"
938         when "bigt", "bpgt", "bbgt"
939             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
940             $asm.puts "bgt #{operands[2].asmLabel}"
941         when "bigteq", "bpgteq", "bbgteq"
942             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
943             $asm.puts "bge #{operands[2].asmLabel}"
944         when "bilt", "bplt", "bblt"
945             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
946             $asm.puts "blt #{operands[2].asmLabel}"
947         when "bilteq", "bplteq", "bblteq"
948             $asm.puts "cmp #{armV7Operands(operands[0..1])}"
949             $asm.puts "ble #{operands[2].asmLabel}"
950         when "btiz", "btpz", "btbz"
951             emitArmV7Test(operands)
952             $asm.puts "beq #{operands[-1].asmLabel}"
953         when "btinz", "btpnz", "btbnz"
954             emitArmV7Test(operands)
955             $asm.puts "bne #{operands[-1].asmLabel}"
956         when "btio", "btpo", "btbo"
957             emitArmV7Test(operands)
958             $asm.puts "bvs #{operands[-1].asmLabel}"
959         when "btis", "btps", "btbs"
960             emitArmV7Test(operands)
961             $asm.puts "bmi #{operands[-1].asmLabel}"
962         when "jmp"
963             if operands[0].label?
964                 $asm.puts "b #{operands[0].asmLabel}"
965             else
966                 $asm.puts "mov pc, #{operands[0].armV7Operand}"
967             end
968         when "call"
969             if operands[0].label?
970                 $asm.puts "blx #{operands[0].asmLabel}"
971             else
972                 $asm.puts "blx #{operands[0].armV7Operand}"
973             end
974         when "break"
975             $asm.puts "bkpt"
976         when "ret"
977             $asm.puts "bx lr"
978         when "cieq", "cpeq"
979             emitArmV7Compare(operands, "eq")
980         when "cineq", "cpneq"
981             emitArmV7Compare(operands, "ne")
982         when "cia", "cpa"
983             emitArmV7Compare(operands, "hi")
984         when "ciaeq", "cpaeq"
985             emitArmV7Compare(operands, "hs")
986         when "cib", "cpb"
987             emitArmV7Compare(operands, "lo")
988         when "cibeq", "cpbeq"
989             emitArmV7Compare(operands, "ls")
990         when "cigt", "cpgt"
991             emitArmV7Compare(operands, "gt")
992         when "cigteq", "cpgteq"
993             emitArmV7Compare(operands, "ge")
994         when "cilt", "cplt"
995             emitArmV7Compare(operands, "lt")
996         when "cilteq", "cplteq"
997             emitArmV7Compare(operands, "le")
998         when "tio", "tbo"
999             emitArmV7TestSet(operands, "vs")
1000         when "tis", "tbs"
1001             emitArmV7TestSet(operands, "mi")
1002         when "tiz", "tbz"
1003             emitArmV7TestSet(operands, "eq")
1004         when "tinz", "tbnz"
1005             emitArmV7TestSet(operands, "ne")
1006         when "peek"
1007             $asm.puts "ldr #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
1008         when "poke"
1009             $asm.puts "str #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
1010         when "fii2d"
1011             $asm.puts "vmov #{operands[2].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
1012         when "fd2ii"
1013             $asm.puts "vmov #{operands[1].armV7Operand}, #{operands[2].armV7Operand}, #{operands[0].armV7Operand}"
1014         when "bo"
1015             $asm.puts "bvs #{operands[0].asmLabel}"
1016         when "bs"
1017             $asm.puts "bmi #{operands[0].asmLabel}"
1018         when "bz"
1019             $asm.puts "beq #{operands[0].asmLabel}"
1020         when "bnz"
1021             $asm.puts "bne #{operands[0].asmLabel}"
1022         when "leai", "leap"
1023             operands[0].armV7EmitLea(operands[1])
1024         when "smulli"
1025             raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
1026             $asm.puts "smull #{operands[2].armV7Operand}, #{operands[3].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
1027         else
1028             raise "Unhandled opcode #{opcode} at #{codeOriginString}"
1029         end
1030     end
1031 end
1032