]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - offlineasm/sh4.rb
JavaScriptCore-7600.1.4.15.12.tar.gz
[apple/javascriptcore.git] / offlineasm / sh4.rb
... / ...
CommitLineData
1# Copyright (C) 2013 Apple Inc. All rights reserved.
2# Copyright (C) 2013 Cisco Systems, Inc. All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7# 1. Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9# 2. Redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution.
12#
13# THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY
14# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR ITS
17# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25require 'risc'
26
27class Node
28 def sh4SingleHi
29 doubleOperand = sh4Operand
30 raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/
31 "fr" + ($~.post_match.to_i).to_s
32 end
33 def sh4SingleLo
34 doubleOperand = sh4Operand
35 raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/
36 "fr" + ($~.post_match.to_i + 1).to_s
37 end
38end
39
40class SpecialRegister < NoChildren
41 def sh4Operand
42 @name
43 end
44
45 def dump
46 @name
47 end
48
49 def register?
50 true
51 end
52end
53
54SH4_TMP_GPRS = [ SpecialRegister.new("r3"), SpecialRegister.new("r11"), SpecialRegister.new("r13") ]
55SH4_TMP_FPRS = [ SpecialRegister.new("dr10") ]
56
57class RegisterID
58 def sh4Operand
59 case name
60 when "t0"
61 "r0"
62 when "t1"
63 "r1"
64 when "t2"
65 "r2"
66 when "t3"
67 "r10"
68 when "t4", "a0"
69 "r4"
70 when "t5", "a1"
71 "r5"
72 when "t6", "a2"
73 "r6"
74 when "t7", "a3"
75 "r7"
76 when "t8"
77 "r8"
78 when "t9"
79 "r9"
80 when "cfr"
81 "r14"
82 when "sp"
83 "r15"
84 when "lr"
85 "pr"
86 else
87 raise "Bad register #{name} for SH4 at #{codeOriginString}"
88 end
89 end
90end
91
92class FPRegisterID
93 def sh4Operand
94 case name
95 when "ft0", "fr"
96 "dr0"
97 when "ft1"
98 "dr2"
99 when "ft2"
100 "dr4"
101 when "ft3"
102 "dr6"
103 when "ft4"
104 "dr8"
105 when "fa0"
106 "dr12"
107 else
108 raise "Bad register #{name} for SH4 at #{codeOriginString}"
109 end
110 end
111end
112
113class Immediate
114 def sh4Operand
115 raise "Invalid immediate #{value} at #{codeOriginString}" if value < -128 or value > 127
116 "##{value}"
117 end
118end
119
120class Address
121 def sh4Operand
122 raise "Bad offset #{offset.value} at #{codeOriginString}" if offset.value < 0 or offset.value > 60
123 if offset.value == 0
124 "@#{base.sh4Operand}"
125 else
126 "@(#{offset.value}, #{base.sh4Operand})"
127 end
128 end
129
130 def sh4OperandPostInc
131 raise "Bad offset #{offset.value} for post inc at #{codeOriginString}" unless offset.value == 0
132 "@#{base.sh4Operand}+"
133 end
134
135 def sh4OperandPreDec
136 raise "Bad offset #{offset.value} for pre dec at #{codeOriginString}" unless offset.value == 0
137 "@-#{base.sh4Operand}"
138 end
139end
140
141class BaseIndex
142 def sh4Operand
143 raise "Unconverted base index at #{codeOriginString}"
144 end
145end
146
147class AbsoluteAddress
148 def sh4Operand
149 raise "Unconverted absolute address at #{codeOriginString}"
150 end
151end
152
153class LabelReference
154 def sh4Operand
155 value
156 end
157end
158
159class SubImmediates < Node
160 def sh4Operand
161 "#{@left.sh4Operand} - #{@right.sh4Operand}"
162 end
163end
164
165class ConstPool < Node
166 attr_reader :size
167 attr_reader :entries
168
169 def initialize(codeOrigin, entries, size)
170 super(codeOrigin)
171 raise "Invalid size #{size} for ConstPool" unless size == 16 or size == 32
172 @size = size
173 @entries = entries
174 end
175
176 def dump
177 "#{size}: #{entries}"
178 end
179
180 def address?
181 false
182 end
183
184 def label?
185 false
186 end
187
188 def immediate?
189 false
190 end
191
192 def register?
193 false
194 end
195
196 def lowerSH4
197 if size == 16
198 $asm.puts ".balign 2"
199 else
200 $asm.puts ".balign 4"
201 end
202 entries.map {
203 |e|
204 e.label.lower("SH4")
205 if e.size == 16
206 $asm.puts ".word #{e.value}"
207 else
208 $asm.puts ".long #{e.value}"
209 end
210 }
211 end
212end
213
214class ConstPoolEntry < Node
215 attr_reader :size
216 attr_reader :value
217 attr_reader :label
218 attr_reader :labelref
219
220 def initialize(codeOrigin, value, size)
221 super(codeOrigin)
222 raise "Invalid size #{size} for ConstPoolEntry" unless size == 16 or size == 32
223 @size = size
224 @value = value
225 @label = LocalLabel.unique("constpool#{size}")
226 @labelref = LocalLabelReference.new(codeOrigin, label)
227 end
228
229 def dump
230 "#{value} (#{size} @ #{label})"
231 end
232
233 def ==(other)
234 other.is_a? ConstPoolEntry and other.value == @value
235 end
236
237 def address?
238 false
239 end
240
241 def label?
242 false
243 end
244
245 def immediate?
246 false
247 end
248
249 def register?
250 false
251 end
252end
253
254
255#
256# Lowering of shift ops for SH4. For example:
257#
258# rshifti foo, bar
259#
260# becomes:
261#
262# negi foo, tmp
263# shad tmp, bar
264#
265
266def sh4LowerShiftOps(list)
267 newList = []
268 list.each {
269 | node |
270 if node.is_a? Instruction
271 case node.opcode
272 when "ulshifti", "ulshiftp", "urshifti", "urshiftp", "lshifti", "lshiftp", "rshifti", "rshiftp"
273 if node.opcode[0, 1] == "u"
274 type = "l"
275 direction = node.opcode[1, 1]
276 else
277 type = "a"
278 direction = node.opcode[0, 1]
279 end
280 if node.operands[0].is_a? Immediate
281 maskedImm = Immediate.new(node.operands[0].codeOrigin, node.operands[0].value & 31)
282 if maskedImm.value == 0
283 # There is nothing to do here.
284 elsif maskedImm.value == 1 or (type == "l" and [2, 8, 16].include? maskedImm.value)
285 newList << Instruction.new(node.codeOrigin, "sh#{type}#{direction}x", [maskedImm, node.operands[1]])
286 else
287 tmp = Tmp.new(node.codeOrigin, :gpr)
288 if direction == "l"
289 newList << Instruction.new(node.codeOrigin, "move", [maskedImm, tmp])
290 else
291 newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, -1 * maskedImm.value), tmp])
292 end
293 newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
294 end
295 else
296 tmp = Tmp.new(node.codeOrigin, :gpr)
297 newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, 31), tmp])
298 newList << Instruction.new(node.codeOrigin, "andi", [node.operands[0], tmp])
299 if direction == "r"
300 newList << Instruction.new(node.codeOrigin, "negi", [tmp, tmp])
301 end
302 newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
303 end
304 else
305 newList << node
306 end
307 else
308 newList << node
309 end
310 }
311 newList
312end
313
314
315#
316# Lowering of simple branch ops for SH4. For example:
317#
318# baddis foo, bar, baz
319#
320# will become:
321#
322# addi foo, bar
323# bs bar, baz
324#
325
326def sh4LowerSimpleBranchOps(list)
327 newList = []
328 list.each {
329 | node |
330 if node.is_a? Instruction
331 annotation = node.annotation
332 case node.opcode
333 when /^b(addi|subi|ori|addp)/
334 op = $1
335 bc = $~.post_match
336
337 case op
338 when "addi", "addp"
339 op = "addi"
340 when "subi", "subp"
341 op = "subi"
342 when "ori", "orp"
343 op = "ori"
344 end
345
346 if bc == "s"
347 raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
348 if node.operands[1].is_a? RegisterID or node.operands[1].is_a? SpecialRegister
349 newList << Instruction.new(node.codeOrigin, op, node.operands[0..1])
350 newList << Instruction.new(node.codeOrigin, "bs", node.operands[1..2])
351 else
352 tmpVal = Tmp.new(node.codeOrigin, :gpr)
353 tmpPtr = Tmp.new(node.codeOrigin, :gpr)
354 addr = Address.new(node.codeOrigin, tmpPtr, Immediate.new(node.codeOrigin, 0))
355 newList << Instruction.new(node.codeOrigin, "leap", [node.operands[1], tmpPtr])
356 newList << Instruction.new(node.codeOrigin, "loadi", [addr, tmpVal])
357 newList << Instruction.new(node.codeOrigin, op, [node.operands[0], tmpVal])
358 newList << Instruction.new(node.codeOrigin, "storei", [tmpVal, addr])
359 newList << Instruction.new(node.codeOrigin, "bs", [tmpVal, node.operands[2]])
360 end
361 elsif bc == "nz"
362 raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
363 newList << Instruction.new(node.codeOrigin, op, node.operands[0..1])
364 newList << Instruction.new(node.codeOrigin, "btinz", node.operands[1..2])
365 else
366 newList << node
367 end
368 when "bmulio", "bmulpo"
369 raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
370 tmp1 = Tmp.new(node.codeOrigin, :gpr)
371 tmp2 = Tmp.new(node.codeOrigin, :gpr)
372 newList << Instruction.new(node.codeOrigin, node.opcode, [tmp1, tmp2].concat(node.operands))
373 else
374 newList << node
375 end
376 else
377 newList << node
378 end
379 }
380 newList
381end
382
383
384#
385# Lowering of double accesses for SH4. For example:
386#
387# loadd [foo, bar, 8], baz
388#
389# becomes:
390#
391# leap [foo, bar, 8], tmp
392# loaddReversedAndIncrementAddress [tmp], baz
393#
394
395def sh4LowerDoubleAccesses(list)
396 newList = []
397 list.each {
398 | node |
399 if node.is_a? Instruction
400 case node.opcode
401 when "loadd"
402 tmp = Tmp.new(codeOrigin, :gpr)
403 addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
404 newList << Instruction.new(codeOrigin, "leap", [node.operands[0], tmp])
405 newList << Instruction.new(node.codeOrigin, "loaddReversedAndIncrementAddress", [addr, node.operands[1]], node.annotation)
406 when "stored"
407 tmp = Tmp.new(codeOrigin, :gpr)
408 addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
409 newList << Instruction.new(codeOrigin, "leap", [node.operands[1].withOffset(8), tmp])
410 newList << Instruction.new(node.codeOrigin, "storedReversedAndDecrementAddress", [node.operands[0], addr], node.annotation)
411 else
412 newList << node
413 end
414 else
415 newList << node
416 end
417 }
418 newList
419end
420
421
422#
423# Lowering of double specials for SH4.
424#
425
426def sh4LowerDoubleSpecials(list)
427 newList = []
428 list.each {
429 | node |
430 if node.is_a? Instruction
431 case node.opcode
432 when "bdltun", "bdgtun"
433 # Handle specific floating point unordered opcodes.
434 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], node.operands[2]])
435 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], node.operands[2]])
436 newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands)
437 when "bdnequn", "bdgtequn", "bdltequn"
438 newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands)
439 when "bdneq", "bdgteq", "bdlteq"
440 # Handle specific floating point ordered opcodes.
441 outlabel = LocalLabel.unique("out_#{node.opcode}")
442 outref = LocalLabelReference.new(codeOrigin, outlabel)
443 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], outref])
444 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], outref])
445 newList << Instruction.new(codeOrigin, node.opcode, node.operands)
446 newList << outlabel
447 else
448 newList << node
449 end
450 else
451 newList << node
452 end
453 }
454 newList
455end
456
457
458#
459# Lowering of misplaced labels for SH4.
460#
461
462def sh4LowerMisplacedLabels(list)
463 newList = []
464 list.each {
465 | node |
466 if node.is_a? Instruction
467 operands = node.operands
468 newOperands = []
469 operands.each {
470 | operand |
471 if operand.is_a? LabelReference and node.opcode != "mova"
472 tmp = Tmp.new(operand.codeOrigin, :gpr)
473 newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp])
474 newOperands << tmp
475 else
476 newOperands << operand
477 end
478 }
479 newList << Instruction.new(node.codeOrigin, node.opcode, newOperands, node.annotation)
480 else
481 newList << node
482 end
483 }
484 newList
485end
486
487
488#
489# Lowering of misplaced special registers for SH4. For example:
490#
491# storep pr, foo
492#
493# becomes:
494#
495# stspr tmp
496# storep tmp, foo
497#
498
499def sh4LowerMisplacedSpecialRegisters(list)
500 newList = []
501 list.each {
502 | node |
503 if node.is_a? Instruction
504 case node.opcode
505 when "move"
506 if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr"
507 newList << Instruction.new(codeOrigin, "stspr", [node.operands[1]])
508 elsif node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr"
509 newList << Instruction.new(codeOrigin, "ldspr", [node.operands[0]])
510 else
511 newList << node
512 end
513 when "loadi", "loadis", "loadp"
514 if node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr"
515 tmp = Tmp.new(codeOrigin, :gpr)
516 newList << Instruction.new(codeOrigin, node.opcode, [node.operands[0], tmp])
517 newList << Instruction.new(codeOrigin, "ldspr", [tmp])
518 else
519 newList << node
520 end
521 when "storei", "storep"
522 if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr"
523 tmp = Tmp.new(codeOrigin, :gpr)
524 newList << Instruction.new(codeOrigin, "stspr", [tmp])
525 newList << Instruction.new(codeOrigin, node.opcode, [tmp, node.operands[1]])
526 else
527 newList << node
528 end
529 else
530 newList << node
531 end
532 else
533 newList << node
534 end
535 }
536 newList
537end
538
539
540#
541# Group immediate values outside -128..127 range into constant pools for SH4.
542# These constant pools will be placed behind non-return opcodes jmp and ret, for example:
543#
544# move 1024, foo
545# ...
546# ret
547#
548# becomes:
549#
550# move [label], foo
551# ...
552# ret
553# label: 1024
554#
555
556def sh4LowerConstPool(list)
557 newList = []
558 currentPool16 = []
559 currentPool32 = []
560 list.each {
561 | node |
562 if node.is_a? Instruction
563 case node.opcode
564 when "jmp", "ret", "flushcp"
565 if node.opcode == "flushcp"
566 outlabel = LocalLabel.unique("flushcp")
567 newList << Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, outlabel)])
568 else
569 newList << node
570 end
571 if not currentPool16.empty?
572 newList << ConstPool.new(codeOrigin, currentPool16, 16)
573 currentPool16 = []
574 end
575 if not currentPool32.empty?
576 newList << ConstPool.new(codeOrigin, currentPool32, 32)
577 currentPool32 = []
578 end
579 if node.opcode == "flushcp"
580 newList << outlabel
581 end
582 when "move"
583 if node.operands[0].is_a? Immediate and not (-128..127).include? node.operands[0].value
584 poolEntry = nil
585 if (-32768..32767).include? node.operands[0].value
586 currentPool16.each { |e|
587 if e.value == node.operands[0].value
588 poolEntry = e
589 end
590 }
591 if !poolEntry
592 poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 16)
593 currentPool16 << poolEntry
594 end
595 else
596 currentPool32.each { |e|
597 if e.value == node.operands[0].value
598 poolEntry = e
599 end
600 }
601 if !poolEntry
602 poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 32)
603 currentPool32 << poolEntry
604 end
605 end
606 newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
607 elsif node.operands[0].is_a? LabelReference
608 poolEntry = nil
609 currentPool32.each { |e|
610 if e.value == node.operands[0].asmLabel
611 poolEntry = e
612 end
613 }
614 if !poolEntry
615 poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].asmLabel, 32)
616 currentPool32 << poolEntry
617 end
618 newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
619 elsif node.operands[0].is_a? SubImmediates
620 poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].sh4Operand, 32)
621 currentPool32 << poolEntry
622 newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
623 else
624 newList << node
625 end
626 else
627 newList << node
628 end
629 else
630 newList << node
631 end
632 }
633 if not currentPool16.empty?
634 newList << ConstPool.new(codeOrigin, currentPool16, 16)
635 end
636 if not currentPool32.empty?
637 newList << ConstPool.new(codeOrigin, currentPool32, 32)
638 end
639 newList
640end
641
642
643#
644# Lowering of argument setup for SH4.
645# This phase avoids argument register trampling. For example, if a0 == t4:
646#
647# setargs t1, t4
648#
649# becomes:
650#
651# move t4, a1
652# move t1, a0
653#
654
655def sh4LowerArgumentSetup(list)
656 a0 = RegisterID.forName(codeOrigin, "a0")
657 a1 = RegisterID.forName(codeOrigin, "a1")
658 a2 = RegisterID.forName(codeOrigin, "a2")
659 a3 = RegisterID.forName(codeOrigin, "a3")
660 newList = []
661 list.each {
662 | node |
663 if node.is_a? Instruction
664 case node.opcode
665 when "setargs"
666 if node.operands.size == 2
667 if node.operands[1].sh4Operand != a0.sh4Operand
668 newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
669 newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
670 elsif node.operands[0].sh4Operand != a1.sh4Operand
671 newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
672 newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
673 else
674 # As (operands[0] == a1) and (operands[1] == a0), we just need to swap a0 and a1.
675 newList << Instruction.new(codeOrigin, "xori", [a0, a1])
676 newList << Instruction.new(codeOrigin, "xori", [a1, a0])
677 newList << Instruction.new(codeOrigin, "xori", [a0, a1])
678 end
679 elsif node.operands.size == 4
680 # FIXME: We just raise an error if something is likely to go wrong for now.
681 # It would be better to implement a recovering algorithm.
682 if (node.operands[0].sh4Operand == a1.sh4Operand) or
683 (node.operands[0].sh4Operand == a2.sh4Operand) or
684 (node.operands[0].sh4Operand == a3.sh4Operand) or
685 (node.operands[1].sh4Operand == a0.sh4Operand) or
686 (node.operands[1].sh4Operand == a2.sh4Operand) or
687 (node.operands[1].sh4Operand == a3.sh4Operand) or
688 (node.operands[2].sh4Operand == a0.sh4Operand) or
689 (node.operands[2].sh4Operand == a1.sh4Operand) or
690 (node.operands[2].sh4Operand == a3.sh4Operand) or
691 (node.operands[3].sh4Operand == a0.sh4Operand) or
692 (node.operands[3].sh4Operand == a1.sh4Operand) or
693 (node.operands[3].sh4Operand == a2.sh4Operand)
694 raise "Potential argument register trampling detected."
695 end
696
697 newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
698 newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
699 newList << Instruction.new(codeOrigin, "move", [node.operands[2], a2])
700 newList << Instruction.new(codeOrigin, "move", [node.operands[3], a3])
701 else
702 raise "Invalid operands number (#{node.operands.size}) for setargs"
703 end
704 else
705 newList << node
706 end
707 else
708 newList << node
709 end
710 }
711 newList
712end
713
714
715class Sequence
716 def getModifiedListSH4
717 result = @list
718
719 # Verify that we will only see instructions and labels.
720 result.each {
721 | node |
722 unless node.is_a? Instruction or
723 node.is_a? Label or
724 node.is_a? LocalLabel or
725 node.is_a? Skip
726 raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
727 end
728 }
729
730 result = sh4LowerShiftOps(result)
731 result = sh4LowerSimpleBranchOps(result)
732 result = riscLowerMalformedAddresses(result) {
733 | node, address |
734 if address.is_a? Address
735 case node.opcode
736 when "btbz", "btbnz", "cbeq", "bbeq", "bbneq", "bbb", "loadb", "storeb"
737 (0..15).include? address.offset.value and
738 ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
739 (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
740 when "loadh"
741 (0..30).include? address.offset.value and
742 ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
743 (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
744 else
745 (0..60).include? address.offset.value
746 end
747 else
748 false
749 end
750 }
751 result = sh4LowerDoubleAccesses(result)
752 result = sh4LowerDoubleSpecials(result)
753 result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep", "muli", "mulp", "andi", "ori", "xori",
754 "cbeq", "cieq", "cpeq", "cineq", "cpneq", "cib", "baddio", "bsubio", "bmulio", "baddis",
755 "bbeq", "bbneq", "bbb", "bieq", "bpeq", "bineq", "bpneq", "bia", "bpa", "biaeq", "bpaeq", "bib", "bpb",
756 "bigteq", "bpgteq", "bilt", "bplt", "bigt", "bpgt", "bilteq", "bplteq", "btiz", "btpz", "btinz", "btpnz", "btbz", "btbnz"])
757 result = riscLowerMalformedImmediates(result, -128..127)
758 result = riscLowerMisplacedAddresses(result)
759 result = sh4LowerMisplacedLabels(result)
760 result = sh4LowerMisplacedSpecialRegisters(result)
761
762 result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_GPRS)
763 result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_FPRS)
764
765 result = sh4LowerConstPool(result)
766 result = sh4LowerArgumentSetup(result)
767
768 return result
769 end
770end
771
772def sh4Operands(operands)
773 operands.map{|v| v.sh4Operand}.join(", ")
774end
775
776def emitSH4Branch(sh4opcode, operand)
777 raise "Invalid operand #{operand}" unless operand.is_a? RegisterID or operand.is_a? SpecialRegister
778 $asm.puts "#{sh4opcode} @#{operand.sh4Operand}"
779 $asm.puts "nop"
780end
781
782def emitSH4ShiftImm(val, operand, direction)
783 tmp = val
784 while tmp > 0
785 if tmp >= 16
786 $asm.puts "shl#{direction}16 #{operand.sh4Operand}"
787 tmp -= 16
788 elsif tmp >= 8
789 $asm.puts "shl#{direction}8 #{operand.sh4Operand}"
790 tmp -= 8
791 elsif tmp >= 2
792 $asm.puts "shl#{direction}2 #{operand.sh4Operand}"
793 tmp -= 2
794 else
795 $asm.puts "shl#{direction} #{operand.sh4Operand}"
796 tmp -= 1
797 end
798 end
799end
800
801def emitSH4BranchIfT(dest, neg)
802 outlabel = LocalLabel.unique("branchIfT")
803 sh4opcode = neg ? "bt" : "bf"
804 $asm.puts "#{sh4opcode} #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
805 if dest.is_a? LocalLabelReference
806 $asm.puts "bra #{dest.asmLabel}"
807 $asm.puts "nop"
808 else
809 emitSH4Branch("jmp", dest)
810 end
811 outlabel.lower("SH4")
812end
813
814def emitSH4IntCompare(cmpOpcode, operands)
815 $asm.puts "cmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
816end
817
818def emitSH4CondBranch(cmpOpcode, neg, operands)
819 emitSH4IntCompare(cmpOpcode, operands)
820 emitSH4BranchIfT(operands[2], neg)
821end
822
823def emitSH4CompareSet(cmpOpcode, neg, operands)
824 emitSH4IntCompare(cmpOpcode, operands)
825 if !neg
826 $asm.puts "movt #{operands[2].sh4Operand}"
827 else
828 outlabel = LocalLabel.unique("compareSet")
829 $asm.puts "mov #0, #{operands[2].sh4Operand}"
830 $asm.puts "bt #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
831 $asm.puts "mov #1, #{operands[2].sh4Operand}"
832 outlabel.lower("SH4")
833 end
834end
835
836def emitSH4BranchIfNaN(operands)
837 raise "Invalid operands number (#{operands.size})" unless operands.size == 2
838 $asm.puts "fcmp/eq #{sh4Operands([operands[0], operands[0]])}"
839 $asm.puts "bf #{operands[1].asmLabel}"
840end
841
842def emitSH4DoubleCondBranch(cmpOpcode, neg, operands)
843 if cmpOpcode == "lt"
844 $asm.puts "fcmp/gt #{sh4Operands([operands[0], operands[1]])}"
845 else
846 $asm.puts "fcmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
847 end
848 emitSH4BranchIfT(operands[2], neg)
849end
850
851class Instruction
852 def lowerSH4
853 $asm.comment codeOriginString
854 case opcode
855 when "addi", "addp"
856 if operands.size == 3
857 if operands[0].sh4Operand == operands[2].sh4Operand
858 $asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
859 elsif operands[1].sh4Operand == operands[2].sh4Operand
860 $asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
861 else
862 $asm.puts "mov #{sh4Operands([operands[0], operands[2]])}"
863 $asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
864 end
865 else
866 $asm.puts "add #{sh4Operands(operands)}"
867 end
868 when "subi", "subp"
869 if operands.size == 3
870 if operands[1].is_a? Immediate
871 $asm.puts "mov #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[1].value), operands[2]])}"
872 $asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
873 elsif operands[1].sh4Operand == operands[2].sh4Operand
874 $asm.puts "neg #{sh4Operands([operands[2], operands[2]])}"
875 $asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
876 else
877 $asm.puts "mov #{sh4Operands([operands[0], operands[2]])}"
878 $asm.puts "sub #{sh4Operands([operands[1], operands[2]])}"
879 end
880 else
881 if operands[0].is_a? Immediate
882 $asm.puts "add #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[0].value), operands[1]])}"
883 else
884 $asm.puts "sub #{sh4Operands(operands)}"
885 end
886 end
887 when "muli", "mulp"
888 $asm.puts "mul.l #{sh4Operands(operands[0..1])}"
889 $asm.puts "sts macl, #{operands[-1].sh4Operand}"
890 when "negi", "negp"
891 if operands.size == 2
892 $asm.puts "neg #{sh4Operands(operands)}"
893 else
894 $asm.puts "neg #{sh4Operands([operands[0], operands[0]])}"
895 end
896 when "andi", "andp", "ori", "orp", "xori", "xorp"
897 raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 2
898 sh4opcode = opcode[0..-2]
899 $asm.puts "#{sh4opcode} #{sh4Operands(operands)}"
900 when "shllx", "shlrx"
901 raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate
902 if operands[0].value == 1
903 $asm.puts "shl#{opcode[3, 1]} #{operands[1].sh4Operand}"
904 else
905 $asm.puts "shl#{opcode[3, 1]}#{operands[0].value} #{operands[1].sh4Operand}"
906 end
907 when "shalx", "sharx"
908 raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate and operands[0].value == 1
909 $asm.puts "sha#{opcode[3, 1]} #{operands[1].sh4Operand}"
910 when "shld", "shad"
911 $asm.puts "#{opcode} #{sh4Operands(operands)}"
912 when "loaddReversedAndIncrementAddress"
913 # As we are little endian, we don't use "fmov @Rm, DRn" here.
914 $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleLo}"
915 $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleHi}"
916 when "storedReversedAndDecrementAddress"
917 # As we are little endian, we don't use "fmov DRm, @Rn" here.
918 $asm.puts "fmov.s #{operands[0].sh4SingleHi}, #{operands[1].sh4OperandPreDec}"
919 $asm.puts "fmov.s #{operands[0].sh4SingleLo}, #{operands[1].sh4OperandPreDec}"
920 when "ci2d"
921 $asm.puts "lds #{operands[0].sh4Operand}, fpul"
922 $asm.puts "float fpul, #{operands[1].sh4Operand}"
923 when "fii2d"
924 $asm.puts "lds #{operands[0].sh4Operand}, fpul"
925 $asm.puts "fsts fpul, #{operands[2].sh4SingleLo}"
926 $asm.puts "lds #{operands[1].sh4Operand}, fpul"
927 $asm.puts "fsts fpul, #{operands[2].sh4SingleHi}"
928 when "fd2ii"
929 $asm.puts "flds #{operands[0].sh4SingleLo}, fpul"
930 $asm.puts "sts fpul, #{operands[1].sh4Operand}"
931 $asm.puts "flds #{operands[0].sh4SingleHi}, fpul"
932 $asm.puts "sts fpul, #{operands[2].sh4Operand}"
933 when "addd", "subd", "muld", "divd"
934 sh4opcode = opcode[0..-2]
935 $asm.puts "f#{sh4opcode} #{sh4Operands(operands)}"
936 when "bcd2i"
937 $asm.puts "ftrc #{operands[0].sh4Operand}, fpul"
938 $asm.puts "sts fpul, #{operands[1].sh4Operand}"
939 $asm.puts "float fpul, #{SH4_TMP_FPRS[0].sh4Operand}"
940 $asm.puts "fcmp/eq #{sh4Operands([operands[0], SH4_TMP_FPRS[0]])}"
941 $asm.puts "bf #{operands[2].asmLabel}"
942 $asm.puts "tst #{sh4Operands([operands[1], operands[1]])}"
943 $asm.puts "bt #{operands[2].asmLabel}"
944 when "bdnan"
945 emitSH4BranchIfNaN(operands)
946 when "bdneq"
947 emitSH4DoubleCondBranch("eq", true, operands)
948 when "bdgteq"
949 emitSH4DoubleCondBranch("lt", true, operands)
950 when "bdlt"
951 emitSH4DoubleCondBranch("lt", false, operands)
952 when "bdlteq"
953 emitSH4DoubleCondBranch("gt", true, operands)
954 when "bdgt"
955 emitSH4DoubleCondBranch("gt", false, operands)
956 when "baddio", "baddpo", "bsubio", "bsubpo"
957 raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 3
958 $asm.puts "#{opcode[1, 3]}v #{sh4Operands([operands[0], operands[1]])}"
959 $asm.puts "bt #{operands[2].asmLabel}"
960 when "bmulio", "bmulpo"
961 raise "Invalid operands number (#{operands.size})" unless operands.size == 5
962 $asm.puts "dmuls.l #{sh4Operands([operands[2], operands[3]])}"
963 $asm.puts "sts macl, #{operands[3].sh4Operand}"
964 $asm.puts "cmp/pz #{operands[3].sh4Operand}"
965 $asm.puts "movt #{operands[1].sh4Operand}"
966 $asm.puts "add #-1, #{operands[1].sh4Operand}"
967 $asm.puts "sts mach, #{operands[0].sh4Operand}"
968 $asm.puts "cmp/eq #{sh4Operands([operands[0], operands[1]])}"
969 $asm.puts "bf #{operands[4].asmLabel}"
970 when "btiz", "btpz", "btbz", "btinz", "btpnz", "btbnz"
971 if operands.size == 3
972 $asm.puts "tst #{sh4Operands([operands[0], operands[1]])}"
973 else
974 if operands[0].sh4Operand == "r0"
975 $asm.puts "cmp/eq #0, r0"
976 else
977 $asm.puts "tst #{sh4Operands([operands[0], operands[0]])}"
978 end
979 end
980 emitSH4BranchIfT(operands[-1], (opcode[-2, 2] == "nz"))
981 when "cieq", "cpeq", "cbeq"
982 emitSH4CompareSet("eq", false, operands)
983 when "cineq", "cpneq", "cbneq"
984 emitSH4CompareSet("eq", true, operands)
985 when "cib", "cpb", "cbb"
986 emitSH4CompareSet("hs", true, operands)
987 when "bieq", "bpeq", "bbeq"
988 emitSH4CondBranch("eq", false, operands)
989 when "bineq", "bpneq", "bbneq"
990 emitSH4CondBranch("eq", true, operands)
991 when "bib", "bpb", "bbb"
992 emitSH4CondBranch("hs", true, operands)
993 when "bia", "bpa", "bba"
994 emitSH4CondBranch("hi", false, operands)
995 when "bibeq", "bpbeq"
996 emitSH4CondBranch("hi", true, operands)
997 when "biaeq", "bpaeq"
998 emitSH4CondBranch("hs", false, operands)
999 when "bigteq", "bpgteq", "bbgteq"
1000 emitSH4CondBranch("ge", false, operands)
1001 when "bilt", "bplt", "bblt"
1002 emitSH4CondBranch("ge", true, operands)
1003 when "bigt", "bpgt", "bbgt"
1004 emitSH4CondBranch("gt", false, operands)
1005 when "bilteq", "bplteq", "bblteq"
1006 emitSH4CondBranch("gt", true, operands)
1007 when "bs"
1008 $asm.puts "cmp/pz #{operands[0].sh4Operand}"
1009 $asm.puts "bf #{operands[1].asmLabel}"
1010 when "call"
1011 if operands[0].is_a? LocalLabelReference
1012 $asm.puts "bsr #{operands[0].asmLabel}"
1013 $asm.puts "nop"
1014 elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
1015 emitSH4Branch("jsr", operands[0])
1016 else
1017 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
1018 end
1019 when "jmp"
1020 if operands[0].is_a? LocalLabelReference
1021 $asm.puts "bra #{operands[0].asmLabel}"
1022 $asm.puts "nop"
1023 elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
1024 emitSH4Branch("jmp", operands[0])
1025 else
1026 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
1027 end
1028 when "ret"
1029 $asm.puts "rts"
1030 $asm.puts "nop"
1031 when "loadb"
1032 $asm.puts "mov.b #{sh4Operands(operands)}"
1033 $asm.puts "extu.b #{sh4Operands([operands[1], operands[1]])}"
1034 when "storeb"
1035 $asm.puts "mov.b #{sh4Operands(operands)}"
1036 when "loadh"
1037 $asm.puts "mov.w #{sh4Operands(operands)}"
1038 $asm.puts "extu.w #{sh4Operands([operands[1], operands[1]])}"
1039 when "loadi", "loadis", "loadp", "storei", "storep"
1040 $asm.puts "mov.l #{sh4Operands(operands)}"
1041 when "alignformova"
1042 $asm.puts ".balign 4" # As balign directive is in a code section, fill value is 'nop' instruction.
1043 when "mova"
1044 $asm.puts "mova #{sh4Operands(operands)}"
1045 when "move"
1046 if operands[0].is_a? ConstPoolEntry
1047 if operands[0].size == 16
1048 $asm.puts "mov.w #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}"
1049 else
1050 $asm.puts "mov.l #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}"
1051 end
1052 elsif operands[0].sh4Operand != operands[1].sh4Operand
1053 $asm.puts "mov #{sh4Operands(operands)}"
1054 end
1055 when "leap"
1056 if operands[0].is_a? BaseIndex
1057 biop = operands[0]
1058 $asm.puts "mov #{sh4Operands([biop.index, operands[1]])}"
1059 if biop.scaleShift > 0
1060 emitSH4ShiftImm(biop.scaleShift, operands[1], "l")
1061 end
1062 $asm.puts "add #{sh4Operands([biop.base, operands[1]])}"
1063 if biop.offset.value != 0
1064 $asm.puts "add #{sh4Operands([biop.offset, operands[1]])}"
1065 end
1066 elsif operands[0].is_a? Address
1067 if operands[0].base != operands[1]
1068 $asm.puts "mov #{sh4Operands([operands[0].base, operands[1]])}"
1069 end
1070 if operands[0].offset.value != 0
1071 $asm.puts "add #{sh4Operands([operands[0].offset, operands[1]])}"
1072 end
1073 else
1074 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
1075 end
1076 when "ldspr"
1077 $asm.puts "lds #{sh4Operands(operands)}, pr"
1078 when "stspr"
1079 $asm.puts "sts pr, #{sh4Operands(operands)}"
1080 when "memfence"
1081 $asm.puts "synco"
1082 when "pop"
1083 if operands[0].sh4Operand == "pr"
1084 $asm.puts "lds.l @r15+, #{sh4Operands(operands)}"
1085 else
1086 $asm.puts "mov.l @r15+, #{sh4Operands(operands)}"
1087 end
1088 when "push"
1089 if operands[0].sh4Operand == "pr"
1090 $asm.puts "sts.l #{sh4Operands(operands)}, @-r15"
1091 else
1092 $asm.puts "mov.l #{sh4Operands(operands)}, @-r15"
1093 end
1094 when "popCalleeSaves"
1095 $asm.puts "mov.l @r15+, r8"
1096 $asm.puts "mov.l @r15+, r9"
1097 $asm.puts "mov.l @r15+, r10"
1098 $asm.puts "mov.l @r15+, r11"
1099 $asm.puts "mov.l @r15+, r13"
1100 when "pushCalleeSaves"
1101 $asm.puts "mov.l r13, @-r15"
1102 $asm.puts "mov.l r11, @-r15"
1103 $asm.puts "mov.l r10, @-r15"
1104 $asm.puts "mov.l r9, @-r15"
1105 $asm.puts "mov.l r8, @-r15"
1106 when "break"
1107 # This special opcode always generates an illegal instruction exception.
1108 $asm.puts ".word 0xfffd"
1109 else
1110 lowerDefault
1111 end
1112 end
1113end
1114