]> git.saurik.com Git - apple/javascriptcore.git/blame - offlineasm/arm.rb
JavaScriptCore-1218.33.tar.gz
[apple/javascriptcore.git] / offlineasm / arm.rb
CommitLineData
93a37866
A
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", "storeq"])
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