1# Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
2# Copyright (C) 2013 University of Szeged. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23# THE POSSIBILITY OF SUCH DAMAGE.
24
25require "config"
26require "ast"
27require "opt"
28require "risc"
29
30def isARMv7
31    case $activeBackend
32    when "ARMv7"
33        true
34    when "ARMv7_TRADITIONAL", "ARM"
35        false
36    else
37        raise "bad value for $activeBackend: #{$activeBackend}"
38    end
39end
40
41def isARMv7Traditional
42    case $activeBackend
43    when "ARMv7_TRADITIONAL"
44        true
45    when "ARMv7", "ARM"
46        false
47    else
48        raise "bad value for $activeBackend: #{$activeBackend}"
49    end
50end
51
52class Node
53    def armSingle
54        doubleOperand = armOperand
55        raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^d/
56        "s" + ($~.post_match.to_i * 2).to_s
57    end
58end
59
60class SpecialRegister
61    def armOperand
62        @name
63    end
64end
65
66ARM_EXTRA_GPRS = [SpecialRegister.new("r9"), SpecialRegister.new("r8"), SpecialRegister.new("r3")]
67ARM_EXTRA_FPRS = [SpecialRegister.new("d7")]
68ARM_SCRATCH_FPR = SpecialRegister.new("d6")
69
70def armMoveImmediate(value, register)
71    # Currently we only handle the simple cases, and fall back to mov/movt for the complex ones.
72    if value >= 0 && value < 256
73        $asm.puts "mov #{register.armOperand}, \##{value}"
74    elsif (~value) >= 0 && (~value) < 256
75        $asm.puts "mvn #{register.armOperand}, \##{~value}"
76    elsif isARMv7 or isARMv7Traditional
77        $asm.puts "movw #{register.armOperand}, \##{value & 0xffff}"
78        if (value & 0xffff0000) != 0
79            $asm.puts "movt #{register.armOperand}, \##{(value >> 16) & 0xffff}"
80        end
81    else
82        $asm.puts "ldr #{register.armOperand}, =#{value}"
83    end
84end
85
86class RegisterID
87    def armOperand
88        case name
89        when "t0", "a0", "r0"
90            "r0"
91        when "t1", "a1", "r1"
92            "r1"
93        when "t2", "a2"
94            "r2"
95        when "a3"
96            "r3"
97        when "t3"
98            "r4"
99        when "t4"
100            "r10"
101        when "cfr"
102            "r5"
103        when "lr"
104            "lr"
105        when "sp"
106            "sp"
107        else
108            raise "Bad register #{name} for ARM at #{codeOriginString}"
109        end
110    end
111end
112
113class FPRegisterID
114    def armOperand
115        case name
116        when "ft0", "fr"
117            "d0"
118        when "ft1"
119            "d1"
120        when "ft2"
121            "d2"
122        when "ft3"
123            "d3"
124        when "ft4"
125            "d4"
126        when "ft5"
127            "d5"
128        else
129            raise "Bad register #{name} for ARM at #{codeOriginString}"
130        end
131    end
132end
133
134class Immediate
135    def armOperand
136        raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 255
137        "\##{value}"
138    end
139end
140
141class Address
142    def armOperand
143        raise "Bad offset at #{codeOriginString}" if offset.value < -0xff or offset.value > 0xfff
144        "[#{base.armOperand}, \##{offset.value}]"
145    end
146end
147
148class BaseIndex
149    def armOperand
150        raise "Bad offset at #{codeOriginString}" if offset.value != 0
151        "[#{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}]"
152    end
153end
154
155class AbsoluteAddress
156    def armOperand
157        raise "Unconverted absolute address at #{codeOriginString}"
158    end
159end
160
161#
162# Lea support.
163#
164
165class Address
166    def armEmitLea(destination)
167        if destination == base
168            $asm.puts "adds #{destination.armOperand}, \##{offset.value}"
169        else
170            $asm.puts "adds #{destination.armOperand}, #{base.armOperand}, \##{offset.value}"
171        end
172    end
173end
174
175class BaseIndex
176    def armEmitLea(destination)
177        raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
178        $asm.puts "add #{destination.armOperand}, #{base.armOperand}, #{index.armOperand}, lsl \##{scaleShift}"
179    end
180end
181
182# FIXME: we could support AbsoluteAddress for lea, but we don't.
183
184#
185# Actual lowering code follows.
186#
187
188class Sequence
189    def getModifiedListARM
190        raise unless $activeBackend == "ARM"
191        getModifiedListARMCommon
192    end
193
194    def getModifiedListARMv7
195        raise unless $activeBackend == "ARMv7"
196        getModifiedListARMCommon
197    end
198
199    def getModifiedListARMv7_TRADITIONAL
200        raise unless $activeBackend == "ARMv7_TRADITIONAL"
201        getModifiedListARMCommon
202    end
203
204    def getModifiedListARMCommon
205        result = @list
206        result = riscLowerSimpleBranchOps(result)
207        result = riscLowerHardBranchOps(result)
208        result = riscLowerShiftOps(result)
209        result = riscLowerMalformedAddresses(result) {
210            | node, address |
211            if address.is_a? BaseIndex
212                address.offset.value == 0
213            elsif address.is_a? Address
214                (-0xff..0xfff).include? address.offset.value
215            else
216                false
217            end
218        }
219        result = riscLowerMalformedAddressesDouble(result)
220        result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep"])
221        result = riscLowerMalformedImmediates(result, 0..0xff)
222        result = riscLowerMisplacedAddresses(result)
223        result = riscLowerRegisterReuse(result)
224        result = assignRegistersToTemporaries(result, :gpr, ARM_EXTRA_GPRS)
225        result = assignRegistersToTemporaries(result, :fpr, ARM_EXTRA_FPRS)
226        return result
227    end
228end
229
230def armOperands(operands)
231    operands.map{|v| v.armOperand}.join(", ")
232end
233
234def armFlippedOperands(operands)
235    armOperands([operands[-1]] + operands[0..-2])
236end
237
238def emitArmCompact(opcode2, opcode3, operands)
239    if operands.size == 3
240        $asm.puts "#{opcode3} #{armFlippedOperands(operands)}"
241    else
242        raise unless operands.size == 2
243        raise unless operands[1].register?
244        if operands[0].immediate?
245            $asm.puts "#{opcode3} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
246        else
247            $asm.puts "#{opcode2} #{armFlippedOperands(operands)}"
248        end
249    end
250end
251
252def emitArm(opcode, operands)
253    if operands.size == 3
254        $asm.puts "#{opcode} #{armFlippedOperands(operands)}"
255    else
256        raise unless operands.size == 2
257        $asm.puts "#{opcode} #{operands[1].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
258    end
259end
260
261def emitArmDoubleBranch(branchOpcode, operands)
262    $asm.puts "vcmpe.f64 #{armOperands(operands[0..1])}"
263    $asm.puts "vmrs apsr_nzcv, fpscr"
264    $asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
265end
266
267def emitArmTest(operands)
268    value = operands[0]
269    case operands.size
270    when 2
271        mask = Immediate.new(codeOrigin, -1)
272    when 3
273        mask = operands[1]
274    else
275        raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
276    end
277    
278    if mask.immediate? and mask.value == -1
279        $asm.puts "tst #{value.armOperand}, #{value.armOperand}"
280    else
281        $asm.puts "tst #{value.armOperand}, #{mask.armOperand}"
282    end
283end
284
285def emitArmCompare(operands, code)
286    $asm.puts "movs #{operands[2].armOperand}, \#0"
287    $asm.puts "cmp #{operands[0].armOperand}, #{operands[1].armOperand}"
288    $asm.puts "it #{code}"
289    $asm.puts "mov#{code} #{operands[2].armOperand}, \#1"
290end
291
292def emitArmTestSet(operands, code)
293    $asm.puts "movs #{operands[-1].armOperand}, \#0"
294    emitArmTest(operands)
295    $asm.puts "it #{code}"
296    $asm.puts "mov#{code} #{operands[-1].armOperand}, \#1"
297end
298
299class Instruction
300    def lowerARM
301        raise unless $activeBackend == "ARM"
302        lowerARMCommon
303    end
304
305    def lowerARMv7
306        raise unless $activeBackend == "ARMv7"
307        lowerARMCommon
308    end
309
310    def lowerARMv7_TRADITIONAL
311        raise unless $activeBackend == "ARMv7_TRADITIONAL"
312        lowerARMCommon
313    end
314
315    def lowerARMCommon
316        $asm.codeOrigin codeOriginString if $enableCodeOriginComments
317        $asm.annotation annotation if $enableInstrAnnotations
318
319        case opcode
320        when "addi", "addp", "addis", "addps"
321            if opcode == "addis" or opcode == "addps"
322                suffix = "s"
323            else
324                suffix = ""
325            end
326            if operands.size == 3 and operands[0].immediate?
327                raise unless operands[1].register?
328                raise unless operands[2].register?
329                if operands[0].value == 0 and suffix.empty?
330                    unless operands[1] == operands[2]
331                        $asm.puts "mov #{operands[2].armOperand}, #{operands[1].armOperand}"
332                    end
333                else
334                    $asm.puts "adds #{operands[2].armOperand}, #{operands[1].armOperand}, #{operands[0].armOperand}"
335                end
336            elsif operands.size == 3 and operands[0].immediate?
337                raise unless operands[1].register?
338                raise unless operands[2].register?
339                $asm.puts "adds #{armFlippedOperands(operands)}"
340            else
341                if operands[0].immediate?
342                    unless Immediate.new(nil, 0) == operands[0]
343                        $asm.puts "adds #{armFlippedOperands(operands)}"
344                    end
345                else
346                    $asm.puts "add#{suffix} #{armFlippedOperands(operands)}"
347                end
348            end
349        when "andi", "andp"
350            emitArmCompact("ands", "and", operands)
351        when "ori", "orp"
352            emitArmCompact("orrs", "orr", operands)
353        when "oris"
354            emitArmCompact("orrs", "orrs", operands)
355        when "xori", "xorp"
356            emitArmCompact("eors", "eor", operands)
357        when "lshifti", "lshiftp"
358            emitArmCompact("lsls", "lsls", operands)
359        when "rshifti", "rshiftp"
360            emitArmCompact("asrs", "asrs", operands)
361        when "urshifti", "urshiftp"
362            emitArmCompact("lsrs", "lsrs", operands)
363        when "muli", "mulp"
364            emitArm("mul", operands)
365        when "subi", "subp", "subis"
366            emitArmCompact("subs", "subs", operands)
367        when "negi", "negp"
368            $asm.puts "rsbs #{operands[0].armOperand}, #{operands[0].armOperand}, \#0"
369        when "noti"
370            $asm.puts "mvns #{operands[0].armOperand}, #{operands[0].armOperand}"
371        when "loadi", "loadis", "loadp"
372            $asm.puts "ldr #{armFlippedOperands(operands)}"
373        when "storei", "storep"
374            $asm.puts "str #{armOperands(operands)}"
375        when "loadb"
376            $asm.puts "ldrb #{armFlippedOperands(operands)}"
377        when "loadbs"
378            $asm.puts "ldrsb.w #{armFlippedOperands(operands)}"
379        when "storeb"
380            $asm.puts "strb #{armOperands(operands)}"
381        when "loadh"
382            $asm.puts "ldrh #{armFlippedOperands(operands)}"
383        when "loadhs"
384            $asm.puts "ldrsh.w #{armFlippedOperands(operands)}"
385        when "storeh"
386            $asm.puts "strh #{armOperands(operands)}"
387        when "loadd"
388            $asm.puts "vldr.64 #{armFlippedOperands(operands)}"
389        when "stored"
390            $asm.puts "vstr.64 #{armOperands(operands)}"
391        when "addd"
392            emitArm("vadd.f64", operands)
393        when "divd"
394            emitArm("vdiv.f64", operands)
395        when "subd"
396            emitArm("vsub.f64", operands)
397        when "muld"
398            emitArm("vmul.f64", operands)
399        when "sqrtd"
400            $asm.puts "vsqrt.f64 #{armFlippedOperands(operands)}"
401        when "ci2d"
402            $asm.puts "vmov #{operands[1].armSingle}, #{operands[0].armOperand}"
403            $asm.puts "vcvt.f64.s32 #{operands[1].armOperand}, #{operands[1].armSingle}"
404        when "bdeq"
405            emitArmDoubleBranch("beq", operands)
406        when "bdneq"
407            $asm.puts "vcmpe.f64 #{armOperands(operands[0..1])}"
408            $asm.puts "vmrs apsr_nzcv, fpscr"
409            isUnordered = LocalLabel.unique("bdneq")
410            $asm.puts "bvs #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
411            $asm.puts "bne #{operands[2].asmLabel}"
412            isUnordered.lower("ARM")
413        when "bdgt"
414            emitArmDoubleBranch("bgt", operands)
415        when "bdgteq"
416            emitArmDoubleBranch("bge", operands)
417        when "bdlt"
418            emitArmDoubleBranch("bmi", operands)
419        when "bdlteq"
420            emitArmDoubleBranch("bls", operands)
421        when "bdequn"
422            $asm.puts "vcmpe.f64 #{armOperands(operands[0..1])}"
423            $asm.puts "vmrs apsr_nzcv, fpscr"
424            $asm.puts "bvs #{operands[2].asmLabel}"
425            $asm.puts "beq #{operands[2].asmLabel}"
426        when "bdnequn"
427            emitArmDoubleBranch("bne", operands)
428        when "bdgtun"
429            emitArmDoubleBranch("bhi", operands)
430        when "bdgtequn"
431            emitArmDoubleBranch("bpl", operands)
432        when "bdltun"
433            emitArmDoubleBranch("blt", operands)
434        when "bdltequn"
435            emitArmDoubleBranch("ble", operands)
436        when "btd2i"
437            # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
438            # currently does not use it.
439            raise "ARM does not support this opcode yet, #{codeOrigin}"
440        when "td2i"
441            $asm.puts "vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
442            $asm.puts "vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
443        when "bcd2i"
444            $asm.puts "vcvt.s32.f64 #{ARM_SCRATCH_FPR.armSingle}, #{operands[0].armOperand}"
445            $asm.puts "vmov #{operands[1].armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
446            $asm.puts "vcvt.f64.s32 #{ARM_SCRATCH_FPR.armOperand}, #{ARM_SCRATCH_FPR.armSingle}"
447            emitArmDoubleBranch("bne", [ARM_SCRATCH_FPR, operands[0], operands[2]])
448            $asm.puts "tst #{operands[1].armOperand}, #{operands[1].armOperand}"
449            $asm.puts "beq #{operands[2].asmLabel}"
450        when "movdz"
451            # FIXME: either support this or remove it.
452            raise "ARM does not support this opcode yet, #{codeOrigin}"
453        when "pop"
454            $asm.puts "pop #{operands[0].armOperand}"
455        when "push"
456            $asm.puts "push #{operands[0].armOperand}"
457        when "move"
458            if operands[0].immediate?
459                armMoveImmediate(operands[0].value, operands[1])
460            else
461                $asm.puts "mov #{armFlippedOperands(operands)}"
462            end
463        when "nop"
464            $asm.puts "nop"
465        when "bieq", "bpeq", "bbeq"
466            if Immediate.new(nil, 0) == operands[0]
467                $asm.puts "tst #{operands[1].armOperand}, #{operands[1].armOperand}"
468            elsif Immediate.new(nil, 0) == operands[1]
469                $asm.puts "tst #{operands[0].armOperand}, #{operands[0].armOperand}"
470            else
471                $asm.puts "cmp #{armOperands(operands[0..1])}"
472            end
473            $asm.puts "beq #{operands[2].asmLabel}"
474        when "bineq", "bpneq", "bbneq"
475            if Immediate.new(nil, 0) == operands[0]
476                $asm.puts "tst #{operands[1].armOperand}, #{operands[1].armOperand}"
477            elsif Immediate.new(nil, 0) == operands[1]
478                $asm.puts "tst #{operands[0].armOperand}, #{operands[0].armOperand}"
479            else
480                $asm.puts "cmp #{armOperands(operands[0..1])}"
481            end
482            $asm.puts "bne #{operands[2].asmLabel}"
483        when "bia", "bpa", "bba"
484            $asm.puts "cmp #{armOperands(operands[0..1])}"
485            $asm.puts "bhi #{operands[2].asmLabel}"
486        when "biaeq", "bpaeq", "bbaeq"
487            $asm.puts "cmp #{armOperands(operands[0..1])}"
488            $asm.puts "bhs #{operands[2].asmLabel}"
489        when "bib", "bpb", "bbb"
490            $asm.puts "cmp #{armOperands(operands[0..1])}"
491            $asm.puts "blo #{operands[2].asmLabel}"
492        when "bibeq", "bpbeq", "bbbeq"
493            $asm.puts "cmp #{armOperands(operands[0..1])}"
494            $asm.puts "bls #{operands[2].asmLabel}"
495        when "bigt", "bpgt", "bbgt"
496            $asm.puts "cmp #{armOperands(operands[0..1])}"
497            $asm.puts "bgt #{operands[2].asmLabel}"
498        when "bigteq", "bpgteq", "bbgteq"
499            $asm.puts "cmp #{armOperands(operands[0..1])}"
500            $asm.puts "bge #{operands[2].asmLabel}"
501        when "bilt", "bplt", "bblt"
502            $asm.puts "cmp #{armOperands(operands[0..1])}"
503            $asm.puts "blt #{operands[2].asmLabel}"
504        when "bilteq", "bplteq", "bblteq"
505            $asm.puts "cmp #{armOperands(operands[0..1])}"
506            $asm.puts "ble #{operands[2].asmLabel}"
507        when "btiz", "btpz", "btbz"
508            emitArmTest(operands)
509            $asm.puts "beq #{operands[-1].asmLabel}"
510        when "btinz", "btpnz", "btbnz"
511            emitArmTest(operands)
512            $asm.puts "bne #{operands[-1].asmLabel}"
513        when "btis", "btps", "btbs"
514            emitArmTest(operands)
515            $asm.puts "bmi #{operands[-1].asmLabel}"
516        when "jmp"
517            if operands[0].label?
518                $asm.puts "b #{operands[0].asmLabel}"
519            else
520                $asm.puts "mov pc, #{operands[0].armOperand}"
521            end
522            if not isARMv7 and not isARMv7Traditional
523                $asm.puts ".ltorg"
524            end
525        when "call"
526            if operands[0].label?
527                $asm.puts "blx #{operands[0].asmLabel}"
528            else
529                $asm.puts "blx #{operands[0].armOperand}"
530            end
531        when "break"
532            $asm.puts "bkpt #0"
533        when "ret"
534            $asm.puts "bx lr"
535        when "cieq", "cpeq", "cbeq"
536            emitArmCompare(operands, "eq")
537        when "cineq", "cpneq", "cbneq"
538            emitArmCompare(operands, "ne")
539        when "cia", "cpa", "cba"
540            emitArmCompare(operands, "hi")
541        when "ciaeq", "cpaeq", "cbaeq"
542            emitArmCompare(operands, "hs")
543        when "cib", "cpb", "cbb"
544            emitArmCompare(operands, "lo")
545        when "cibeq", "cpbeq", "cbbeq"
546            emitArmCompare(operands, "ls")
547        when "cigt", "cpgt", "cbgt"
548            emitArmCompare(operands, "gt")
549        when "cigteq", "cpgteq", "cbgteq"
550            emitArmCompare(operands, "ge")
551        when "cilt", "cplt", "cblt"
552            emitArmCompare(operands, "lt")
553        when "cilteq", "cplteq", "cblteq"
554            emitArmCompare(operands, "le")
555        when "tis", "tbs", "tps"
556            emitArmTestSet(operands, "mi")
557        when "tiz", "tbz", "tpz"
558            emitArmTestSet(operands, "eq")
559        when "tinz", "tbnz", "tpnz"
560            emitArmTestSet(operands, "ne")
561        when "peek"
562            $asm.puts "ldr #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
563        when "poke"
564            $asm.puts "str #{operands[1].armOperand}, [sp, \##{operands[0].value * 4}]"
565        when "fii2d"
566            $asm.puts "vmov #{operands[2].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
567        when "fd2ii"
568            $asm.puts "vmov #{operands[1].armOperand}, #{operands[2].armOperand}, #{operands[0].armOperand}"
569        when "bo"
570            $asm.puts "bvs #{operands[0].asmLabel}"
571        when "bs"
572            $asm.puts "bmi #{operands[0].asmLabel}"
573        when "bz"
574            $asm.puts "beq #{operands[0].asmLabel}"
575        when "bnz"
576            $asm.puts "bne #{operands[0].asmLabel}"
577        when "leai", "leap"
578            operands[0].armEmitLea(operands[1])
579        when "smulli"
580            raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
581            $asm.puts "smull #{operands[2].armOperand}, #{operands[3].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
582        else
583            lowerDefault
584        end
585    end
586end
587
588