]> git.saurik.com Git - apple/javascriptcore.git/blame - offlineasm/cloop.rb
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / offlineasm / cloop.rb
CommitLineData
93a37866
A
1# Copyright (C) 2012 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
24require "config"
25require "ast"
26require "opt"
27
28# The CLoop llint backend is initially based on the ARMv7 backend, and
29# then further enhanced with a few instructions from the x86 backend to
30# support building for X64 targets. Hence, the shape of the generated
31# code and the usage convention of registers will look a lot like the
32# ARMv7 backend's.
33
34def cloopMapType(type)
35 case type
36 when :int; ".i"
37 when :uint; ".u"
38 when :int32; ".i32"
39 when :uint32; ".u32"
40 when :int64; ".i64"
41 when :uint64; ".u64"
42 when :int8; ".i8"
43 when :uint8; ".u8"
44 when :int8Ptr; ".i8p"
45 when :voidPtr; ".vp"
46 when :nativeFunc; ".nativeFunc"
47 when :double; ".d"
48 when :castToDouble; ".castToDouble"
49 when :castToInt64; ".castToInt64"
50 when :opcode; ".opcode"
51 else;
52 raise "Unsupported type"
53 end
54end
55
56
57class SpecialRegister < NoChildren
58 def clDump
59 @name
60 end
61 def clValue(type=:int)
62 @name + cloopMapType(type)
63 end
64end
65
66C_LOOP_SCRATCH_FPR = SpecialRegister.new("d6")
67
68class RegisterID
69 def clDump
70 case name
71 when "t0"
72 "t0"
73 when "t1"
74 "t1"
75 when "t2"
76 "t2"
77 when "t3"
78 "t3"
79 when "t4"
80 "rPC"
81 when "t6"
82 "rBasePC"
83 when "csr1"
84 "tagTypeNumber"
85 when "csr2"
86 "tagMask"
87 when "cfr"
88 "cfr"
89 when "lr"
90 "rRetVPC"
91 when "sp"
92 "sp"
93 else
94 raise "Bad register #{name} for C_LOOP at #{codeOriginString}"
95 end
96 end
97 def clValue(type=:int)
98 clDump + cloopMapType(type)
99 end
100end
101
102class FPRegisterID
103 def clDump
104 case name
105 when "ft0", "fr"
106 "d0"
107 when "ft1"
108 "d1"
109 when "ft2"
110 "d2"
111 when "ft3"
112 "d3"
113 when "ft4"
114 "d4"
115 when "ft5"
116 "d5"
117 else
118 raise "Bad register #{name} for C_LOOP at #{codeOriginString}"
119 end
120 end
121 def clValue(type=:int)
122 clDump + cloopMapType(type)
123 end
124end
125
126class Immediate
127 def clDump
128 "#{value}"
129 end
130 def clValue(type=:int)
131 # There is a case of a very large unsigned number (0x8000000000000000)
132 # which we wish to encode. Unfortunately, the C/C++ compiler
133 # complains if we express that number as a positive decimal integer.
134 # Hence, for positive values, we just convert the number into hex form
135 # to keep the compiler happy.
136 #
137 # However, for negative values, the to_s(16) hex conversion method does
138 # not strip the "-" sign resulting in a meaningless "0x-..." valueStr.
139 # To workaround this, we simply don't encode negative numbers as hex.
140
141 valueStr = (value < 0) ? "#{value}" : "0x#{value.to_s(16)}"
142
143 case type
144 when :int8; "int8_t(#{valueStr})"
145 when :int32; "int32_t(#{valueStr})"
146 when :int64; "int64_t(#{valueStr})"
147 when :int; "intptr_t(#{valueStr})"
148 when :uint8; "uint8_t(#{valueStr})"
149 when :uint32; "uint32_t(#{valueStr})"
150 when :uint64; "uint64_t(#{valueStr})"
151 when :uint; "uintptr_t(#{valueStr})"
152 else
153 raise "Not implemented immediate of type: #{type}"
154 end
155 end
156end
157
158class Address
159 def clDump
160 "[#{base.clDump}, #{offset.value}]"
161 end
162 def clValue(type=:int)
163 case type
164 when :int8; int8MemRef
165 when :int32; int32MemRef
166 when :int64; int64MemRef
167 when :int; intMemRef
168 when :uint8; uint8MemRef
169 when :uint32; uint32MemRef
170 when :uint64; uint64MemRef
171 when :uint; uintMemRef
172 when :opcode; opcodeMemRef
173 when :nativeFunc; nativeFuncMemRef
174 else
175 raise "Unexpected Address type: #{type}"
176 end
177 end
178 def pointerExpr
179 if base.is_a? RegisterID and base.name == "sp"
180 offsetValue = "#{offset.value}"
181 "(ASSERT(#{offsetValue} == offsetof(JITStackFrame, vm)), &sp->vm)"
182 elsif offset.value == 0
183 "#{base.clValue(:int8Ptr)}"
184 elsif offset.value > 0
185 "#{base.clValue(:int8Ptr)} + #{offset.value}"
186 else
187 "#{base.clValue(:int8Ptr)} - #{-offset.value}"
188 end
189 end
190 def int8MemRef
191 "*CAST<int8_t*>(#{pointerExpr})"
192 end
193 def int16MemRef
194 "*CAST<int16_t*>(#{pointerExpr})"
195 end
196 def int32MemRef
197 "*CAST<int32_t*>(#{pointerExpr})"
198 end
199 def int64MemRef
200 "*CAST<int64_t*>(#{pointerExpr})"
201 end
202 def intMemRef
203 "*CAST<intptr_t*>(#{pointerExpr})"
204 end
205 def uint8MemRef
206 "*CAST<uint8_t*>(#{pointerExpr})"
207 end
208 def uint16MemRef
209 "*CAST<uint16_t*>(#{pointerExpr})"
210 end
211 def uint32MemRef
212 "*CAST<uint32_t*>(#{pointerExpr})"
213 end
214 def uint64MemRef
215 "*CAST<uint64_t*>(#{pointerExpr})"
216 end
217 def uintMemRef
218 "*CAST<uintptr_t*>(#{pointerExpr})"
219 end
220 def nativeFuncMemRef
221 "*CAST<NativeFunction*>(#{pointerExpr})"
222 end
223 def opcodeMemRef
224 "*CAST<Opcode*>(#{pointerExpr})"
225 end
226 def dblMemRef
227 "*CAST<double*>(#{pointerExpr})"
228 end
229end
230
231class BaseIndex
232 def clDump
233 "[#{base.clDump}, #{offset.clDump}, #{index.clDump} << #{scaleShift}]"
234 end
235 def clValue(type=:int)
236 case type
237 when :int8; int8MemRef
238 when :int32; int32MemRef
239 when :int64; int64MemRef
240 when :int; intMemRef
241 when :uint8; uint8MemRef
242 when :uint32; uint32MemRef
243 when :uint64; uint64MemRef
244 when :uint; uintMemRef
245 when :opcode; opcodeMemRef
246 else
247 raise "Unexpected BaseIndex type: #{type}"
248 end
249 end
250 def pointerExpr
251 if base.is_a? RegisterID and base.name == "sp"
252 offsetValue = "(#{index.clValue} << #{scaleShift}) + #{offset.clValue})"
253 "(ASSERT(#{offsetValue} == offsetof(JITStackFrame, vm)), &sp->vm)"
254 else
255 "#{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift}) + #{offset.clValue}"
256 end
257 end
258 def int8MemRef
259 "*CAST<int8_t*>(#{pointerExpr})"
260 end
261 def int16MemRef
262 "*CAST<int16_t*>(#{pointerExpr})"
263 end
264 def int32MemRef
265 "*CAST<int32_t*>(#{pointerExpr})"
266 end
267 def int64MemRef
268 "*CAST<int64_t*>(#{pointerExpr})"
269 end
270 def intMemRef
271 "*CAST<intptr_t*>(#{pointerExpr})"
272 end
273 def uint8MemRef
274 "*CAST<uint8_t*>(#{pointerExpr})"
275 end
276 def uint16MemRef
277 "*CAST<uint16_t*>(#{pointerExpr})"
278 end
279 def uint32MemRef
280 "*CAST<uint32_t*>(#{pointerExpr})"
281 end
282 def uint64MemRef
283 "*CAST<uint64_t*>(#{pointerExpr})"
284 end
285 def uintMemRef
286 "*CAST<uintptr_t*>(#{pointerExpr})"
287 end
288 def opcodeMemRef
289 "*CAST<Opcode*>(#{pointerExpr})"
290 end
291 def dblMemRef
292 "*CAST<double*>(#{pointerExpr})"
293 end
294end
295
296class AbsoluteAddress
297 def clDump
298 "#{codeOriginString}"
299 end
300 def clValue
301 clDump
302 end
303end
304
305
306#
307# Lea support.
308#
309
310class Address
311 def cloopEmitLea(destination, type)
312 if destination == base
313 $asm.putc "#{destination.clValue(:int8Ptr)} += #{offset.clValue(type)};"
314 else
315 $asm.putc "#{destination.clValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + #{offset.clValue(type)};"
316 end
317 end
318end
319
320class BaseIndex
321 def cloopEmitLea(destination, type)
322 raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
323 $asm.putc "#{destination.clValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift});"
324 end
325end
326
327#
328# Actual lowering code follows.
329#
330
331class Sequence
332 def getModifiedListC_LOOP
333 myList = @list
334
335 # Verify that we will only see instructions and labels.
336 myList.each {
337 | node |
338 unless node.is_a? Instruction or
339 node.is_a? Label or
340 node.is_a? LocalLabel or
341 node.is_a? Skip
342 raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
343 end
344 }
345
346 return myList
347 end
348end
349
350def clOperands(operands)
351 operands.map{|v| v.clDump}.join(", ")
352end
353
354
355def cloopEmitOperation(operands, type, operator)
356 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || \
357 type == :int64 || type == :uint64 || type == :double
358 if operands.size == 3
359 $asm.putc "#{operands[2].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};"
360 if operands[2].is_a? RegisterID and (type == :int32 or type == :uint32)
361 $asm.putc "#{operands[2].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port.
362 end
363 else
364 raise unless operands.size == 2
365 raise unless not operands[1].is_a? Immediate
366 $asm.putc "#{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};"
367 if operands[1].is_a? RegisterID and (type == :int32 or type == :uint32)
368 $asm.putc "#{operands[1].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port.
369 end
370 end
371end
372
373def cloopEmitShiftOperation(operands, type, operator)
374 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || type == :int64 || type == :uint64
375 if operands.size == 3
376 $asm.putc "#{operands[2].clValue(type)} = #{operands[1].clValue(type)} #{operator} (#{operands[0].clValue(:int)} & 0x1f);"
377 if operands[2].is_a? RegisterID and (type == :int32 or type == :uint32)
378 $asm.putc "#{operands[2].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port.
379 end
380 else
381 raise unless operands.size == 2
382 raise unless not operands[1].is_a? Immediate
383 $asm.putc "#{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} (#{operands[0].clValue(:int)} & 0x1f);"
384 if operands[1].is_a? RegisterID and (type == :int32 or type == :uint32)
385 $asm.putc "#{operands[1].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port.
386 end
387 end
388end
389
390def cloopEmitUnaryOperation(operands, type, operator)
391 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || type == :int64 || type == :uint64
392 raise unless operands.size == 1
393 raise unless not operands[0].is_a? Immediate
394 $asm.putc "#{operands[0].clValue(type)} = #{operator}#{operands[0].clValue(type)};"
395 if operands[0].is_a? RegisterID and (type == :int32 or type == :uint32)
396 $asm.putc "#{operands[0].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port.
397 end
398end
399
400def cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, condition)
401 $asm.putc "if (std::isnan(#{operands[0].clValue(:double)}) || isnan(#{operands[1].clValue(:double)})"
402 $asm.putc " || (#{operands[0].clValue(:double)} #{condition} #{operands[1].clValue(:double)}))"
403 $asm.putc " goto #{operands[2].cLabel};"
404end
405
406
407def cloopEmitCompareAndSet(operands, type, comparator)
408 # The result is a boolean. Hence, it doesn't need to be based on the type
409 # of the arguments being compared.
410 $asm.putc "#{operands[2].clValue} = (#{operands[0].clValue(type)} #{comparator} #{op2 = operands[1].clValue(type)});"
411end
412
413
414def cloopEmitCompareAndBranch(operands, type, comparator)
415 $asm.putc "if (#{operands[0].clValue(type)} #{comparator} #{operands[1].clValue(type)})"
416 $asm.putc " goto #{operands[2].cLabel};"
417end
418
419
420# conditionTest should contain a string that provides a comparator and a RHS
421# value e.g. "< 0".
422def cloopGenerateConditionExpression(operands, type, conditionTest)
423 op1 = operands[0].clValue(type)
424
425 # The operands must consist of 2 or 3 values.
426 case operands.size
427 when 2 # Just test op1 against the conditionTest.
428 lhs = op1
429 when 3 # Mask op1 with op2 before testing against the conditionTest.
430 lhs = "(#{op1} & #{operands[1].clValue(type)})"
431 else
432 raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
433 end
434
435 "#{lhs} #{conditionTest}"
436end
437
438# conditionTest should contain a string that provides a comparator and a RHS
439# value e.g. "< 0".
440def cloopEmitTestAndBranchIf(operands, type, conditionTest, branchTarget)
441 conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest)
442 $asm.putc "if (#{conditionExpr})"
443 $asm.putc " goto #{branchTarget};"
444end
445
446def cloopEmitTestSet(operands, type, conditionTest)
447 # The result is a boolean condition. Hence, the result type is always an
448 # int. The passed in type is only used for the values being tested in
449 # the condition test.
450 conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest)
451 $asm.putc "#{operands[-1].clValue} = (#{conditionExpr});"
452end
453
454def cloopEmitOpAndBranch(operands, operator, type, conditionTest)
455 case type
456 when :int; tempType = "intptr_t"
457 when :int32; tempType = "int32_t"
458 when :int64; tempType = "int64_t"
459 else
460 raise "Unimplemented type"
461 end
462
463 op1 = operands[0].clValue(type)
464 op2 = operands[1].clValue(type)
465
466 $asm.putc "{"
467 $asm.putc " #{tempType} temp = #{op2} #{operator} #{op1};"
468 $asm.putc " #{op2} = temp;"
469 $asm.putc " if (temp #{conditionTest})"
470 $asm.putc " goto #{operands[2].cLabel};"
471 $asm.putc "}"
472end
473
474def cloopAddOverflowTest(operands, type)
475 case type
476 when :int32
477 tempType = "int32_t"
478 signBit = "SIGN_BIT32"
479 else
480 raise "Unimplemented type"
481 end
482
483 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};"
484 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};"
485 $asm.putc " // sign(b) sign(a) | Overflows if:"
486 $asm.putc " // 0 0 | sign(b+a) = 1 (pos + pos != neg)"
487 $asm.putc " // 0 1 | never"
488 $asm.putc " // 1 0 | never"
489 $asm.putc " // 1 1 | sign(b+a) = 0 (neg + neg != pos)"
490 "((#{signBit}(b) == #{signBit}(a)) && (#{signBit}(b+a) != #{signBit}(a)))"
491end
492
493def cloopSubOverflowTest(operands, type)
494 case type
495 when :int32
496 tempType = "int32_t"
497 signBit = "SIGN_BIT32"
498 else
499 raise "Unimplemented type"
500 end
501
502 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};"
503 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};"
504 $asm.putc " // sign(b) sign(a) | Overflows if:"
505 $asm.putc " // 0 0 | never"
506 $asm.putc " // 0 1 | sign(b-a) = 1 (pos - neg != pos)"
507 $asm.putc " // 1 0 | sign(b-a) = 0 (neg - pos != pos)"
508 $asm.putc " // 1 1 | never"
509 "((#{signBit}(b) != #{signBit}(a)) && (#{signBit}(b-a) == #{signBit}(a)))"
510end
511
512def cloopMulOverflowTest(operands, type)
513 case type
514 when :int32
515 tempType = "uint32_t"
516 else
517 raise "Unimplemented type"
518 end
519 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};"
520 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};"
521 "((b | a) >> 15)"
522end
523
524def cloopEmitOpAndBranchIfOverflow(operands, operator, type)
525 $asm.putc "{"
526
527 # Emit the overflow test based on the operands and the type:
528 case operator
529 when "+"; overflowTest = cloopAddOverflowTest(operands, type)
530 when "-"; overflowTest = cloopSubOverflowTest(operands, type)
531 when "*"; overflowTest = cloopMulOverflowTest(operands, type)
532 else
533 raise "Unimplemented opeartor"
534 end
535
536 $asm.putc " bool didOverflow = #{overflowTest};"
537 $asm.putc " #{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};"
538 $asm.putc " if (didOverflow)"
539 $asm.putc " goto #{operands[2].cLabel};"
540 $asm.putc "}"
541end
542
543# operands: callTarget, currentFrame, currentPC
544def cloopEmitCallSlowPath(operands)
545 $asm.putc "{"
546 $asm.putc " ExecState* exec = CAST<ExecState*>(#{operands[1].clValue(:voidPtr)});"
547 $asm.putc " Instruction* pc = CAST<Instruction*>(#{operands[2].clValue(:voidPtr)});"
548 $asm.putc " SlowPathReturnType result = #{operands[0].cLabel}(exec, pc);"
549 $asm.putc " LLInt::decodeResult(result, t0.instruction, t1.execState);"
550 $asm.putc "}"
551end
552
553class Instruction
554 def lowerC_LOOP
555 $asm.codeOrigin codeOriginString if $enableCodeOriginComments
556 $asm.annotation annotation if $enableInstrAnnotations && (opcode != "cloopDo")
557
558 case opcode
559 when "addi"
560 cloopEmitOperation(operands, :int32, "+")
561 when "addq"
562 cloopEmitOperation(operands, :int64, "+")
563 when "addp"
564 cloopEmitOperation(operands, :int, "+")
565
566 when "andi"
567 cloopEmitOperation(operands, :int32, "&")
568 when "andq"
569 cloopEmitOperation(operands, :int64, "&")
570 when "andp"
571 cloopEmitOperation(operands, :int, "&")
572
573 when "ori"
574 cloopEmitOperation(operands, :int32, "|")
575 when "orq"
576 cloopEmitOperation(operands, :int64, "|")
577 when "orp"
578 cloopEmitOperation(operands, :int, "|")
579
580 when "xori"
581 cloopEmitOperation(operands, :int32, "^")
582 when "xorq"
583 cloopEmitOperation(operands, :int64, "^")
584 when "xorp"
585 cloopEmitOperation(operands, :int, "^")
586
587 when "lshifti"
588 cloopEmitShiftOperation(operands, :int32, "<<")
589 when "lshiftq"
590 cloopEmitShiftOperation(operands, :int64, "<<")
591 when "lshiftp"
592 cloopEmitShiftOperation(operands, :int, "<<")
593
594 when "rshifti"
595 cloopEmitShiftOperation(operands, :int32, ">>")
596 when "rshiftq"
597 cloopEmitShiftOperation(operands, :int64, ">>")
598 when "rshiftp"
599 cloopEmitShiftOperation(operands, :int, ">>")
600
601 when "urshifti"
602 cloopEmitShiftOperation(operands, :uint32, ">>")
603 when "urshiftq"
604 cloopEmitShiftOperation(operands, :uint64, ">>")
605 when "urshiftp"
606 cloopEmitShiftOperation(operands, :uint, ">>")
607
608 when "muli"
609 cloopEmitOperation(operands, :int32, "*")
610 when "mulq"
611 cloopEmitOperation(operands, :int64, "*")
612 when "mulp"
613 cloopEmitOperation(operands, :int, "*")
614
615 when "subi"
616 cloopEmitOperation(operands, :int32, "-")
617 when "subq"
618 cloopEmitOperation(operands, :int64, "-")
619 when "subp"
620 cloopEmitOperation(operands, :int, "-")
621
622 when "negi"
623 cloopEmitUnaryOperation(operands, :int32, "-")
624 when "negq"
625 cloopEmitUnaryOperation(operands, :int64, "-")
626 when "negp"
627 cloopEmitUnaryOperation(operands, :int, "-")
628
629 when "noti"
630 cloopEmitUnaryOperation(operands, :int32, "!")
631
632 when "loadi"
633 $asm.putc "#{operands[1].clValue(:uint)} = #{operands[0].uint32MemRef};"
634 # There's no need to call clearHighWord() here because the above will
635 # automatically take care of 0 extension.
636 when "loadis"
637 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int32MemRef};"
638 when "loadq"
639 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].int64MemRef};"
640 when "loadp"
641 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].intMemRef};"
642 when "storei"
643 $asm.putc "#{operands[1].int32MemRef} = #{operands[0].clValue(:int32)};"
644 when "storeq"
645 $asm.putc "#{operands[1].int64MemRef} = #{operands[0].clValue(:int64)};"
646 when "storep"
647 $asm.putc "#{operands[1].intMemRef} = #{operands[0].clValue(:int)};"
648 when "loadb"
649 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].uint8MemRef};"
650 when "loadbs"
651 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int8MemRef};"
652 when "storeb"
653 $asm.putc "#{operands[1].uint8MemRef} = #{operands[0].clValue(:int8)};"
654 when "loadh"
655 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].uint16MemRef};"
656 when "loadhs"
657 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int16MemRef};"
658 when "storeh"
659 $asm.putc "*#{operands[1].uint16MemRef} = #{operands[0].clValue(:int16)};"
660 when "loadd"
661 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].dblMemRef};"
662 when "stored"
663 $asm.putc "#{operands[1].dblMemRef} = #{operands[0].clValue(:double)};"
664
665 when "addd"
666 cloopEmitOperation(operands, :double, "+")
667 when "divd"
668 cloopEmitOperation(operands, :double, "/")
669 when "subd"
670 cloopEmitOperation(operands, :double, "-")
671 when "muld"
672 cloopEmitOperation(operands, :double, "*")
673
674 # Convert an int value to its double equivalent, and store it in a double register.
675 when "ci2d"
676 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].clValue(:int32)};"
677
678 when "bdeq"
679 cloopEmitCompareAndBranch(operands, :double, "==")
680 when "bdneq"
681 cloopEmitCompareAndBranch(operands, :double, "!=")
682 when "bdgt"
683 cloopEmitCompareAndBranch(operands, :double, ">");
684 when "bdgteq"
685 cloopEmitCompareAndBranch(operands, :double, ">=");
686 when "bdlt"
687 cloopEmitCompareAndBranch(operands, :double, "<");
688 when "bdlteq"
689 cloopEmitCompareAndBranch(operands, :double, "<=");
690
691 when "bdequn"
692 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "==")
693 when "bdnequn"
694 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "!=")
695 when "bdgtun"
696 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">")
697 when "bdgtequn"
698 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">=")
699 when "bdltun"
700 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<")
701 when "bdltequn"
702 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<=")
703
704 when "td2i"
705 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].clValue(:double)};"
706 $asm.putc "#{operands[1].clDump}.clearHighWord();"
707
708 when "bcd2i" # operands: srcDbl dstInt slowPath
709 $asm.putc "{"
710 $asm.putc " double d = #{operands[0].clValue(:double)};"
711 $asm.putc " const int32_t asInt32 = int32_t(d);"
712 $asm.putc " if (asInt32 != d || (!asInt32 && std::signbit(d))) // true for -0.0"
713 $asm.putc " goto #{operands[2].cLabel};"
714 $asm.putc " #{operands[1].clValue} = asInt32;"
715 $asm.putc " #{operands[1].clDump}.clearHighWord();"
716 $asm.putc "}"
717
718 when "move"
719 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].clValue(:int)};"
720 when "sxi2q"
721 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].clValue(:int32)};"
722 when "zxi2q"
723 $asm.putc "#{operands[1].clValue(:uint64)} = #{operands[0].clValue(:uint32)};"
724 when "nop"
725 $asm.putc "// nop"
726 when "bbeq"
727 cloopEmitCompareAndBranch(operands, :int8, "==")
728 when "bieq"
729 cloopEmitCompareAndBranch(operands, :int32, "==")
730 when "bqeq"
731 cloopEmitCompareAndBranch(operands, :int64, "==")
732 when "bpeq"
733 cloopEmitCompareAndBranch(operands, :int, "==")
734
735 when "bbneq"
736 cloopEmitCompareAndBranch(operands, :int8, "!=")
737 when "bineq"
738 cloopEmitCompareAndBranch(operands, :int32, "!=")
739 when "bqneq"
740 cloopEmitCompareAndBranch(operands, :int64, "!=")
741 when "bpneq"
742 cloopEmitCompareAndBranch(operands, :int, "!=")
743
744 when "bba"
745 cloopEmitCompareAndBranch(operands, :uint8, ">")
746 when "bia"
747 cloopEmitCompareAndBranch(operands, :uint32, ">")
748 when "bqa"
749 cloopEmitCompareAndBranch(operands, :uint64, ">")
750 when "bpa"
751 cloopEmitCompareAndBranch(operands, :uint, ">")
752
753 when "bbaeq"
754 cloopEmitCompareAndBranch(operands, :uint8, ">=")
755 when "biaeq"
756 cloopEmitCompareAndBranch(operands, :uint32, ">=")
757 when "bqaeq"
758 cloopEmitCompareAndBranch(operands, :uint64, ">=")
759 when "bpaeq"
760 cloopEmitCompareAndBranch(operands, :uint, ">=")
761
762 when "bbb"
763 cloopEmitCompareAndBranch(operands, :uint8, "<")
764 when "bib"
765 cloopEmitCompareAndBranch(operands, :uint32, "<")
766 when "bqb"
767 cloopEmitCompareAndBranch(operands, :uint64, "<")
768 when "bpb"
769 cloopEmitCompareAndBranch(operands, :uint, "<")
770
771 when "bbbeq"
772 cloopEmitCompareAndBranch(operands, :uint8, "<=")
773 when "bibeq"
774 cloopEmitCompareAndBranch(operands, :uint32, "<=")
775 when "bqbeq"
776 cloopEmitCompareAndBranch(operands, :uint64, "<=")
777 when "bpbeq"
778 cloopEmitCompareAndBranch(operands, :uint, "<=")
779
780 when "bbgt"
781 cloopEmitCompareAndBranch(operands, :int8, ">")
782 when "bigt"
783 cloopEmitCompareAndBranch(operands, :int32, ">")
784 when "bqgt"
785 cloopEmitCompareAndBranch(operands, :int64, ">")
786 when "bpgt"
787 cloopEmitCompareAndBranch(operands, :int, ">")
788
789 when "bbgteq"
790 cloopEmitCompareAndBranch(operands, :int8, ">=")
791 when "bigteq"
792 cloopEmitCompareAndBranch(operands, :int32, ">=")
793 when "bqgteq"
794 cloopEmitCompareAndBranch(operands, :int64, ">=")
795 when "bpgteq"
796 cloopEmitCompareAndBranch(operands, :int, ">=")
797
798 when "bblt"
799 cloopEmitCompareAndBranch(operands, :int8, "<")
800 when "bilt"
801 cloopEmitCompareAndBranch(operands, :int32, "<")
802 when "bqlt"
803 cloopEmitCompareAndBranch(operands, :int64, "<")
804 when "bplt"
805 cloopEmitCompareAndBranch(operands, :int, "<")
806
807 when "bblteq"
808 cloopEmitCompareAndBranch(operands, :int8, "<=")
809 when "bilteq"
810 cloopEmitCompareAndBranch(operands, :int32, "<=")
811 when "bqlteq"
812 cloopEmitCompareAndBranch(operands, :int64, "<=")
813 when "bplteq"
814 cloopEmitCompareAndBranch(operands, :int, "<=")
815
816 when "btbz"
817 cloopEmitTestAndBranchIf(operands, :int8, "== 0", operands[-1].cLabel)
818 when "btiz"
819 cloopEmitTestAndBranchIf(operands, :int32, "== 0", operands[-1].cLabel)
820 when "btqz"
821 cloopEmitTestAndBranchIf(operands, :int64, "== 0", operands[-1].cLabel)
822 when "btpz"
823 cloopEmitTestAndBranchIf(operands, :int, "== 0", operands[-1].cLabel)
824
825 when "btbnz"
826 cloopEmitTestAndBranchIf(operands, :int8, "!= 0", operands[-1].cLabel)
827 when "btinz"
828 cloopEmitTestAndBranchIf(operands, :int32, "!= 0", operands[-1].cLabel)
829 when "btqnz"
830 cloopEmitTestAndBranchIf(operands, :int64, "!= 0", operands[-1].cLabel)
831 when "btpnz"
832 cloopEmitTestAndBranchIf(operands, :int, "!= 0", operands[-1].cLabel)
833
834 when "btbs"
835 cloopEmitTestAndBranchIf(operands, :int8, "< 0", operands[-1].cLabel)
836 when "btis"
837 cloopEmitTestAndBranchIf(operands, :int32, "< 0", operands[-1].cLabel)
838 when "btqs"
839 cloopEmitTestAndBranchIf(operands, :int64, "< 0", operands[-1].cLabel)
840 when "btps"
841 cloopEmitTestAndBranchIf(operands, :int, "< 0", operands[-1].cLabel)
842
843 # For jmp, we do not want to assume that we have COMPUTED_GOTO support.
844 # Fortunately, the only times we should ever encounter indirect jmps is
845 # when the jmp target is a CLoop opcode (by design).
846 #
847 # Hence, we check if the jmp target is a known label reference. If so,
848 # we can emit a goto directly. If it is not a known target, then we set
849 # the target in the opcode, and dispatch to it via whatever dispatch
850 # mechanism is in used.
851 when "jmp"
852 if operands[0].is_a? LocalLabelReference or operands[0].is_a? LabelReference
853 # Handles jumps local or global labels.
854 $asm.putc "goto #{operands[0].cLabel};"
855 else
856 # Handles jumps to some computed target.
857 # NOTE: must be an opcode handler or a llint glue helper.
858 $asm.putc "opcode = #{operands[0].clValue(:opcode)};"
859 $asm.putc "DISPATCH_OPCODE();"
860 end
861
862 when "call"
863 $asm.putc "CRASH(); // generic call instruction not supported by design!"
864 when "break"
865 $asm.putc "CRASH(); // break instruction not implemented."
866 when "ret"
867 $asm.putc "goto doReturnHelper;"
868
869 when "cbeq"
870 cloopEmitCompareAndSet(operands, :uint8, "==")
871 when "cieq"
872 cloopEmitCompareAndSet(operands, :uint32, "==")
873 when "cqeq"
874 cloopEmitCompareAndSet(operands, :uint64, "==")
875 when "cpeq"
876 cloopEmitCompareAndSet(operands, :uint, "==")
877
878 when "cbneq"
879 cloopEmitCompareAndSet(operands, :uint8, "!=")
880 when "cineq"
881 cloopEmitCompareAndSet(operands, :uint32, "!=")
882 when "cqneq"
883 cloopEmitCompareAndSet(operands, :uint64, "!=")
884 when "cpneq"
885 cloopEmitCompareAndSet(operands, :uint, "!=")
886
887 when "cba"
888 cloopEmitCompareAndSet(operands, :uint8, ">")
889 when "cia"
890 cloopEmitCompareAndSet(operands, :uint32, ">")
891 when "cqa"
892 cloopEmitCompareAndSet(operands, :uint64, ">")
893 when "cpa"
894 cloopEmitCompareAndSet(operands, :uint, ">")
895
896 when "cbaeq"
897 cloopEmitCompareAndSet(operands, :uint8, ">=")
898 when "ciaeq"
899 cloopEmitCompareAndSet(operands, :uint32, ">=")
900 when "cqaeq"
901 cloopEmitCompareAndSet(operands, :uint64, ">=")
902 when "cpaeq"
903 cloopEmitCompareAndSet(operands, :uint, ">=")
904
905 when "cbb"
906 cloopEmitCompareAndSet(operands, :uint8, "<")
907 when "cib"
908 cloopEmitCompareAndSet(operands, :uint32, "<")
909 when "cqb"
910 cloopEmitCompareAndSet(operands, :uint64, "<")
911 when "cpb"
912 cloopEmitCompareAndSet(operands, :uint, "<")
913
914 when "cbbeq"
915 cloopEmitCompareAndSet(operands, :uint8, "<=")
916 when "cibeq"
917 cloopEmitCompareAndSet(operands, :uint32, "<=")
918 when "cqbeq"
919 cloopEmitCompareAndSet(operands, :uint64, "<=")
920 when "cpbeq"
921 cloopEmitCompareAndSet(operands, :uint, "<=")
922
923 when "cbgt"
924 cloopEmitCompareAndSet(operands, :int8, ">")
925 when "cigt"
926 cloopEmitCompareAndSet(operands, :int32, ">")
927 when "cqgt"
928 cloopEmitCompareAndSet(operands, :int64, ">")
929 when "cpgt"
930 cloopEmitCompareAndSet(operands, :int, ">")
931
932 when "cbgteq"
933 cloopEmitCompareAndSet(operands, :int8, ">=")
934 when "cigteq"
935 cloopEmitCompareAndSet(operands, :int32, ">=")
936 when "cqgteq"
937 cloopEmitCompareAndSet(operands, :int64, ">=")
938 when "cpgteq"
939 cloopEmitCompareAndSet(operands, :int, ">=")
940
941 when "cblt"
942 cloopEmitCompareAndSet(operands, :int8, "<")
943 when "cilt"
944 cloopEmitCompareAndSet(operands, :int32, "<")
945 when "cqlt"
946 cloopEmitCompareAndSet(operands, :int64, "<")
947 when "cplt"
948 cloopEmitCompareAndSet(operands, :int, "<")
949
950 when "cblteq"
951 cloopEmitCompareAndSet(operands, :int8, "<=")
952 when "cilteq"
953 cloopEmitCompareAndSet(operands, :int32, "<=")
954 when "cqlteq"
955 cloopEmitCompareAndSet(operands, :int64, "<=")
956 when "cplteq"
957 cloopEmitCompareAndSet(operands, :int, "<=")
958
959 when "tbs"
960 cloopEmitTestSet(operands, :int8, "< 0")
961 when "tis"
962 cloopEmitTestSet(operands, :int32, "< 0")
963 when "tqs"
964 cloopEmitTestSet(operands, :int64, "< 0")
965 when "tps"
966 cloopEmitTestSet(operands, :int, "< 0")
967
968 when "tbz"
969 cloopEmitTestSet(operands, :int8, "== 0")
970 when "tiz"
971 cloopEmitTestSet(operands, :int32, "== 0")
972 when "tqz"
973 cloopEmitTestSet(operands, :int64, "== 0")
974 when "tpz"
975 cloopEmitTestSet(operands, :int, "== 0")
976
977 when "tbnz"
978 cloopEmitTestSet(operands, :int8, "!= 0")
979 when "tinz"
980 cloopEmitTestSet(operands, :int32, "!= 0")
981 when "tqnz"
982 cloopEmitTestSet(operands, :int64, "!= 0")
983 when "tpnz"
984 cloopEmitTestSet(operands, :int, "!= 0")
985
986 # 64-bit instruction: cdqi (based on X64)
987 # Sign extends the lower 32 bits of t0, but put the sign extension into
988 # the lower 32 bits of t1. Leave the upper 32 bits of t0 and t1 unchanged.
989 when "cdqi"
990 $asm.putc "{"
991 $asm.putc " int64_t temp = t0.i32; // sign extend the low 32bit"
992 $asm.putc " t0.i32 = temp; // low word"
993 $asm.putc " t0.clearHighWord();"
994 $asm.putc " t1.i32 = uint64_t(temp) >> 32; // high word"
995 $asm.putc " t1.clearHighWord();"
996 $asm.putc "}"
997
998 # 64-bit instruction: idivi op1 (based on X64)
999 # Divide a 64-bit integer numerator by the specified denominator.
1000 # The numerator is specified in t0 and t1 as follows:
1001 # 1. low 32 bits of the numerator is in the low 32 bits of t0.
1002 # 2. high 32 bits of the numerator is in the low 32 bits of t1.
1003 #
1004 # The resultant quotient is a signed 32-bit int, and is to be stored
1005 # in the lower 32 bits of t0.
1006 # The resultant remainder is a signed 32-bit int, and is to be stored
1007 # in the lower 32 bits of t1.
1008 when "idivi"
1009 # Divide t1,t0 (EDX,EAX) by the specified arg, and store the remainder in t1,
1010 # and quotient in t0:
1011 $asm.putc "{"
1012 $asm.putc " int64_t dividend = (int64_t(t1.u32) << 32) | t0.u32;"
1013 $asm.putc " int64_t divisor = #{operands[0].clValue(:int)};"
1014 $asm.putc " t1.i32 = dividend % divisor; // remainder"
1015 $asm.putc " t1.clearHighWord();"
1016 $asm.putc " t0.i32 = dividend / divisor; // quotient"
1017 $asm.putc " t0.clearHighWord();"
1018 $asm.putc "}"
1019
1020 # 32-bit instruction: fii2d int32LoOp int32HiOp dblOp (based on ARMv7)
1021 # Decode 2 32-bit ints (low and high) into a 64-bit double.
1022 when "fii2d"
1023 $asm.putc "#{operands[2].clValue(:double)} = Ints2Double(#{operands[0].clValue(:uint32)}, #{operands[1].clValue(:uint32)});"
1024
1025 # 32-bit instruction: f2dii dblOp int32LoOp int32HiOp (based on ARMv7)
1026 # Encode a 64-bit double into 2 32-bit ints (low and high).
1027 when "fd2ii"
1028 $asm.putc "Double2Ints(#{operands[0].clValue(:double)}, #{operands[1].clValue(:uint32)}, #{operands[2].clValue(:uint32)});"
1029
1030 # 64-bit instruction: fq2d int64Op dblOp (based on X64)
1031 # Copy a bit-encoded double in a 64-bit int register to a double register.
1032 when "fq2d"
1033 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].clValue(:castToDouble)};"
1034
1035 # 64-bit instruction: fd2q dblOp int64Op (based on X64 instruction set)
1036 # Copy a double as a bit-encoded double into a 64-bit int register.
1037 when "fd2q"
1038 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].clValue(:castToInt64)};"
1039
1040 when "leai"
1041 operands[0].cloopEmitLea(operands[1], :int32)
1042 when "leap"
1043 operands[0].cloopEmitLea(operands[1], :int)
1044
1045 when "baddio"
1046 cloopEmitOpAndBranchIfOverflow(operands, "+", :int32)
1047 when "bsubio"
1048 cloopEmitOpAndBranchIfOverflow(operands, "-", :int32)
1049 when "bmulio"
1050 cloopEmitOpAndBranchIfOverflow(operands, "*", :int32)
1051
1052 when "baddis"
1053 cloopEmitOpAndBranch(operands, "+", :int32, "< 0")
1054 when "baddiz"
1055 cloopEmitOpAndBranch(operands, "+", :int32, "== 0")
1056 when "baddinz"
1057 cloopEmitOpAndBranch(operands, "+", :int32, "!= 0")
1058
1059 when "baddqs"
1060 cloopEmitOpAndBranch(operands, "+", :int64, "< 0")
1061 when "baddqz"
1062 cloopEmitOpAndBranch(operands, "+", :int64, "== 0")
1063 when "baddqnz"
1064 cloopEmitOpAndBranch(operands, "+", :int64, "!= 0")
1065
1066 when "baddps"
1067 cloopEmitOpAndBranch(operands, "+", :int, "< 0")
1068 when "baddpz"
1069 cloopEmitOpAndBranch(operands, "+", :int, "== 0")
1070 when "baddpnz"
1071 cloopEmitOpAndBranch(operands, "+", :int, "!= 0")
1072
1073 when "bsubis"
1074 cloopEmitOpAndBranch(operands, "-", :int32, "< 0")
1075 when "bsubiz"
1076 cloopEmitOpAndBranch(operands, "-", :int32, "== 0")
1077 when "bsubinz"
1078 cloopEmitOpAndBranch(operands, "-", :int32, "!= 0")
1079
1080 when "borris"
1081 cloopEmitOpAndBranch(operands, "|", :int32, "< 0")
1082 when "borriz"
1083 cloopEmitOpAndBranch(operands, "|", :int32, "== 0")
1084 when "borrinz"
1085 cloopEmitOpAndBranch(operands, "|", :int32, "!= 0")
1086
1087 # A convenience and compact call to crash because we don't want to use
1088 # the generic llint crash mechanism which relies on the availability
1089 # of the call instruction (which cannot be implemented in a generic
1090 # way, and can be abused if we made it just work for this special case).
1091 # Using a special cloopCrash instruction is cleaner.
1092 when "cloopCrash"
1093 $asm.putc "CRASH();"
1094
1095 # We can't rely on the llint JS call mechanism which actually makes
1096 # use of the call instruction. Instead, we just implement JS calls
1097 # as an opcode dispatch.
1098 when "cloopCallJSFunction"
1099 $asm.putc "opcode = #{operands[0].clValue(:opcode)};"
1100 $asm.putc "DISPATCH_OPCODE();"
1101
1102 # We can't do generic function calls with an arbitrary set of args, but
1103 # fortunately we don't have to here. All native function calls always
1104 # have a fixed prototype of 1 args: the passed ExecState.
1105 when "cloopCallNative"
1106 $asm.putc "nativeFunc = #{operands[0].clValue(:nativeFunc)};"
1107 $asm.putc "functionReturnValue = JSValue::decode(nativeFunc(t0.execState));"
1108 $asm.putc "#if USE(JSVALUE32_64)"
1109 $asm.putc " t1.i = functionReturnValue.tag();"
1110 $asm.putc " t0.i = functionReturnValue.payload();"
1111 $asm.putc "#else // USE_JSVALUE64)"
1112 $asm.putc " t0.encodedJSValue = JSValue::encode(functionReturnValue);"
1113 $asm.putc "#endif // USE_JSVALUE64)"
1114
1115 # We can't do generic function calls with an arbitrary set of args, but
1116 # fortunately we don't have to here. All slow path function calls always
1117 # have a fixed prototype too. See cloopEmitCallSlowPath() for details.
1118 when "cloopCallSlowPath"
1119 cloopEmitCallSlowPath(operands)
1120
1121 # For debugging only. This is used to insert instrumentation into the
1122 # generated LLIntAssembly.h during llint development only. Do not use
1123 # for production code.
1124 when "cloopDo"
1125 $asm.putc "#{annotation}"
1126
1127 else
1128 lowerDefault
1129 end
1130 end
1131end