summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/offlineasm
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-02-24 16:36:50 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-02-24 16:36:50 +0100
commitad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (patch)
treeb34b0daceb7c8e7fdde4b4ec43650ab7caadb0a9 /Source/JavaScriptCore/offlineasm
parent03e12282df9aa1e1fb05a8b90f1cfc2e08764cec (diff)
downloadqtwebkit-ad0d549d4cc13433f77c1ac8f0ab379c83d93f28.tar.gz
Imported WebKit commit bb52bf3c0119e8a128cd93afe5572413a8617de9 (http://svn.webkit.org/repository/webkit/trunk@108790)
Diffstat (limited to 'Source/JavaScriptCore/offlineasm')
-rw-r--r--Source/JavaScriptCore/offlineasm/armv7.rb1032
-rw-r--r--Source/JavaScriptCore/offlineasm/asm.rb176
-rw-r--r--Source/JavaScriptCore/offlineasm/ast.rb1039
-rw-r--r--Source/JavaScriptCore/offlineasm/backends.rb96
-rw-r--r--Source/JavaScriptCore/offlineasm/generate_offset_extractor.rb146
-rw-r--r--Source/JavaScriptCore/offlineasm/instructions.rb217
-rw-r--r--Source/JavaScriptCore/offlineasm/offsets.rb173
-rw-r--r--Source/JavaScriptCore/offlineasm/opt.rb134
-rw-r--r--Source/JavaScriptCore/offlineasm/parser.rb586
-rw-r--r--Source/JavaScriptCore/offlineasm/registers.rb60
-rw-r--r--Source/JavaScriptCore/offlineasm/self_hash.rb46
-rw-r--r--Source/JavaScriptCore/offlineasm/settings.rb205
-rw-r--r--Source/JavaScriptCore/offlineasm/transform.rb342
-rw-r--r--Source/JavaScriptCore/offlineasm/x86.rb681
14 files changed, 4933 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/offlineasm/armv7.rb b/Source/JavaScriptCore/offlineasm/armv7.rb
new file mode 100644
index 000000000..eb8df6869
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/armv7.rb
@@ -0,0 +1,1032 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+require "opt"
+
+class Node
+ def armV7Single
+ doubleOperand = armV7Operand
+ raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^d/
+ "s" + ($~.post_match.to_i * 2).to_s
+ end
+end
+
+class SpecialRegister < NoChildren
+ def initialize(name)
+ @name = name
+ end
+
+ def armV7Operand
+ @name
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ true
+ end
+end
+
+ARMv7_EXTRA_GPRS = [SpecialRegister.new("r9"), SpecialRegister.new("r8"), SpecialRegister.new("r3")]
+ARMv7_EXTRA_FPRS = [SpecialRegister.new("d7")]
+ARMv7_SCRATCH_FPR = SpecialRegister.new("d8")
+
+def armV7MoveImmediate(value, register)
+ # Currently we only handle the simple cases, and fall back to mov/movt for the complex ones.
+ if value >= 0 && value < 256
+ $asm.puts "movw #{register.armV7Operand}, \##{value}"
+ elsif (~value) >= 0 && (~value) < 256
+ $asm.puts "mvn #{register.armV7Operand}, \##{~value}"
+ else
+ $asm.puts "movw #{register.armV7Operand}, \##{value & 0xffff}"
+ if (value & 0xffff0000) != 0
+ $asm.puts "movt #{register.armV7Operand}, \##{value >> 16}"
+ end
+ end
+end
+
+class RegisterID
+ def armV7Operand
+ case name
+ when "t0", "a0", "r0"
+ "r0"
+ when "t1", "a1", "r1"
+ "r1"
+ when "t2", "a2"
+ "r2"
+ when "a3"
+ "r3"
+ when "t3"
+ "r4"
+ when "t4"
+ "r7"
+ when "cfr"
+ "r5"
+ when "lr"
+ "lr"
+ when "sp"
+ "sp"
+ else
+ raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
+ end
+ end
+end
+
+class FPRegisterID
+ def armV7Operand
+ case name
+ when "ft0", "fr"
+ "d0"
+ when "ft1"
+ "d1"
+ when "ft2"
+ "d2"
+ when "ft3"
+ "d3"
+ when "ft4"
+ "d4"
+ when "ft5"
+ "d5"
+ else
+ raise "Bad register #{name} for ARMv7 at #{codeOriginString}"
+ end
+ end
+end
+
+class Immediate
+ def armV7Operand
+ raise "Invalid immediate #{value} at #{codeOriginString}" if value < 0 or value > 255
+ "\##{value}"
+ end
+end
+
+class Address
+ def armV7Operand
+ raise "Bad offset at #{codeOriginString}" if offset.value < -0xff or offset.value > 0xfff
+ "[#{base.armV7Operand}, \##{offset.value}]"
+ end
+end
+
+class BaseIndex
+ def armV7Operand
+ raise "Bad offset at #{codeOriginString}" if offset.value != 0
+ "[#{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}]"
+ end
+end
+
+class AbsoluteAddress
+ def armV7Operand
+ raise "Unconverted absolute address at #{codeOriginString}"
+ end
+end
+
+#
+# Lowering of branch ops. For example:
+#
+# baddiz foo, bar, baz
+#
+# will become:
+#
+# addi foo, bar
+# bz baz
+#
+
+def armV7LowerBranchOps(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when /^b(addi|subi|ori|addp)/
+ op = $1
+ branch = "b" + $~.post_match
+
+ case op
+ when "addi", "addp"
+ op = "addis"
+ when "subi"
+ op = "subis"
+ when "ori"
+ op = "oris"
+ end
+
+ newList << Instruction.new(node.codeOrigin, op, node.operands[0..-2])
+ newList << Instruction.new(node.codeOrigin, branch, [node.operands[-1]])
+ when "bmulio"
+ tmp1 = Tmp.new(node.codeOrigin, :gpr)
+ tmp2 = Tmp.new(node.codeOrigin, :gpr)
+ newList << Instruction.new(node.codeOrigin, "smulli", [node.operands[0], node.operands[1], node.operands[1], tmp1])
+ newList << Instruction.new(node.codeOrigin, "rshifti", [node.operands[-2], Immediate.new(node.codeOrigin, 31), tmp2])
+ newList << Instruction.new(node.codeOrigin, "bineq", [tmp1, tmp2, node.operands[-1]])
+ when /^bmuli/
+ condition = $~.post_match
+ newList << Instruction.new(node.codeOrigin, "muli", node.operands[0..-2])
+ newList << Instruction.new(node.codeOrigin, "bti" + condition, [node.operands[-2], node.operands[-1]])
+ else
+ newList << node
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of shift ops. For example:
+#
+# lshifti foo, bar
+#
+# will become:
+#
+# andi foo, 31, tmp
+# lshifti tmp, bar
+#
+
+def armV7SanitizeShift(operand, list)
+ return operand if operand.immediate?
+
+ tmp = Tmp.new(operand.codeOrigin, :gpr)
+ list << Instruction.new(operand.codeOrigin, "andi", [operand, Immediate.new(operand.codeOrigin, 31), tmp])
+ tmp
+end
+
+def armV7LowerShiftOps(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when "lshifti", "rshifti", "urshifti"
+ if node.operands.size == 2
+ newList << Instruction.new(node.codeOrigin, node.opcode, [armV7SanitizeShift(node.operands[0], newList), node.operands[1]])
+ else
+ newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], armV7SanitizeShift(node.operands[1], newList), node.operands[2]])
+ raise "Wrong number of operands for shift at #{node.codeOriginString}" unless node.operands.size == 3
+ end
+ else
+ newList << node
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of malformed addresses. For example:
+#
+# loadp 10000[foo], bar
+#
+# will become:
+#
+# move 10000, tmp
+# addp foo, tmp
+# loadp 0[tmp], bar
+#
+
+class Node
+ def armV7LowerMalformedAddressesRecurse(list)
+ mapChildren {
+ | node |
+ node.armV7LowerMalformedAddressesRecurse(list)
+ }
+ end
+end
+
+class Address
+ def armV7LowerMalformedAddressesRecurse(list)
+ if offset.value < -0xff or offset.value > 0xfff
+ tmp = Tmp.new(codeOrigin, :gpr)
+ list << Instruction.new(codeOrigin, "move", [offset, tmp])
+ list << Instruction.new(codeOrigin, "addp", [base, tmp])
+ Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
+ else
+ self
+ end
+ end
+end
+
+class BaseIndex
+ def armV7LowerMalformedAddressesRecurse(list)
+ if offset.value != 0
+ tmp = Tmp.new(codeOrigin, :gpr)
+ list << Instruction.new(codeOrigin, "move", [offset, tmp])
+ list << Instruction.new(codeOrigin, "addp", [base, tmp])
+ BaseIndex.new(codeOrigin, tmp, index, scale, Immediate.new(codeOrigin, 0))
+ else
+ self
+ end
+ end
+end
+
+class AbsoluteAddress
+ def armV7LowerMalformedAddressesRecurse(list)
+ tmp = Tmp.new(codeOrigin, :gpr)
+ list << Instruction.new(codeOrigin, "move", [address, tmp])
+ Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
+ end
+end
+
+def armV7LowerMalformedAddresses(list)
+ newList = []
+ list.each {
+ | node |
+ newList << node.armV7LowerMalformedAddressesRecurse(newList)
+ }
+ newList
+end
+
+#
+# Lowering of malformed addresses in double loads and stores. For example:
+#
+# loadd [foo, bar, 8], baz
+#
+# becomes:
+#
+# leap [foo, bar, 8], tmp
+# loadd [tmp], baz
+#
+
+class Node
+ def armV7DoubleAddress(list)
+ self
+ end
+end
+
+class BaseIndex
+ def armV7DoubleAddress(list)
+ tmp = Tmp.new(codeOrigin, :gpr)
+ list << Instruction.new(codeOrigin, "leap", [self, tmp])
+ Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
+ end
+end
+
+def armV7LowerMalformedAddressesDouble(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when "loadd"
+ newList << Instruction.new(node.codeOrigin, "loadd", [node.operands[0].armV7DoubleAddress(newList), node.operands[1]])
+ when "stored"
+ newList << Instruction.new(node.codeOrigin, "stored", [node.operands[0], node.operands[1].armV7DoubleAddress(newList)])
+ else
+ newList << node
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of misplaced immediates. For example:
+#
+# storei 0, [foo]
+#
+# will become:
+#
+# move 0, tmp
+# storei tmp, [foo]
+#
+
+def armV7LowerMisplacedImmediates(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when "storei", "storep"
+ operands = node.operands
+ newOperands = []
+ operands.each {
+ | operand |
+ if operand.is_a? Immediate
+ tmp = Tmp.new(operand.codeOrigin, :gpr)
+ newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp])
+ newOperands << tmp
+ else
+ newOperands << operand
+ end
+ }
+ newList << Instruction.new(node.codeOrigin, node.opcode, newOperands)
+ else
+ newList << node
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of malformed immediates except when used in a "move" instruction.
+# For example:
+#
+# addp 642641, foo
+#
+# will become:
+#
+# move 642641, tmp
+# addp tmp, foo
+#
+
+class Node
+ def armV7LowerMalformedImmediatesRecurse(list)
+ mapChildren {
+ | node |
+ node.armV7LowerMalformedImmediatesRecurse(list)
+ }
+ end
+end
+
+class Address
+ def armV7LowerMalformedImmediatesRecurse(list)
+ self
+ end
+end
+
+class BaseIndex
+ def armV7LowerMalformedImmediatesRecurse(list)
+ self
+ end
+end
+
+class AbsoluteAddress
+ def armV7LowerMalformedImmediatesRecurse(list)
+ self
+ end
+end
+
+class Immediate
+ def armV7LowerMalformedImmediatesRecurse(list)
+ if value < 0 or value > 255
+ tmp = Tmp.new(codeOrigin, :gpr)
+ list << Instruction.new(codeOrigin, "move", [self, tmp])
+ tmp
+ else
+ self
+ end
+ end
+end
+
+def armV7LowerMalformedImmediates(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when "move"
+ newList << node
+ when "addi", "addp", "addis", "subi", "subp", "subis"
+ if node.operands[0].is_a? Immediate and
+ node.operands[0].value < 0 and
+ node.operands[0].value >= 255 and
+ node.operands.size == 2
+ if node.opcode =~ /add/
+ newOpcode = "sub" + node.opcode[-1..-1]
+ else
+ newOpcode = "add" + node.opcode[-1..-1]
+ end
+ newList << Instruction.new(node.codeOrigin, newOpcode,
+ [Immediate.new(-node.operands[0].value)] + node.operands[1..-1])
+ else
+ newList << node.armV7LowerMalformedImmediatesRecurse(newList)
+ end
+ when "muli"
+ if node.operands[0].is_a? Immediate
+ tmp = Tmp.new(codeOrigin, :gpr)
+ newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
+ newList << Instruction.new(node.codeOrigin, "muli", [tmp] + node.operands[1..-1])
+ else
+ newList << node.armV7LowerMalformedImmediatesRecurse(newList)
+ end
+ else
+ newList << node.armV7LowerMalformedImmediatesRecurse(newList)
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of misplaced addresses. For example:
+#
+# addi foo, [bar]
+#
+# will become:
+#
+# loadi [bar], tmp
+# addi foo, tmp
+# storei tmp, [bar]
+#
+# Another example:
+#
+# addi [foo], bar
+#
+# will become:
+#
+# loadi [foo], tmp
+# addi tmp, bar
+#
+
+def armV7AsRegister(preList, postList, operand, suffix, needStore)
+ return operand unless operand.address?
+
+ tmp = Tmp.new(operand.codeOrigin, if suffix == "d" then :fpr else :gpr end)
+ preList << Instruction.new(operand.codeOrigin, "load" + suffix, [operand, tmp])
+ if needStore
+ postList << Instruction.new(operand.codeOrigin, "store" + suffix, [tmp, operand])
+ end
+ tmp
+end
+
+def armV7AsRegisters(preList, postList, operands, suffix)
+ newOperands = []
+ operands.each_with_index {
+ | operand, index |
+ newOperands << armV7AsRegister(preList, postList, operand, suffix, index == operands.size - 1)
+ }
+ newOperands
+end
+
+def armV7LowerMisplacedAddresses(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ postInstructions = []
+ case node.opcode
+ when "addi", "addp", "addis", "andi", "andp", "lshifti", "muli", "negi", "noti", "ori", "oris",
+ "orp", "rshifti", "urshifti", "subi", "subp", "subis", "xori", "xorp", /^bi/, /^bp/, /^bti/,
+ /^btp/, /^ci/, /^cp/, /^ti/
+ newList << Instruction.new(node.codeOrigin,
+ node.opcode,
+ armV7AsRegisters(newList, postInstructions, node.operands, "i"))
+ when "bbeq", "bbneq", "bba", "bbaeq", "bbb", "bbbeq", "btbo", "btbz", "btbnz", "tbz", "tbnz",
+ "tbo"
+ newList << Instruction.new(node.codeOrigin,
+ node.opcode,
+ armV7AsRegisters(newList, postInstructions, node.operands, "b"))
+ when "bbgt", "bbgteq", "bblt", "bblteq", "btbs", "tbs"
+ newList << Instruction.new(node.codeOrigin,
+ node.opcode,
+ armV7AsRegisters(newList, postInstructions, node.operands, "bs"))
+ when "addd", "divd", "subd", "muld", "sqrtd", /^bd/
+ newList << Instruction.new(node.codeOrigin,
+ node.opcode,
+ armV7AsRegisters(newList, postInstructions, node.operands, "d"))
+ when "jmp", "call"
+ newList << Instruction.new(node.codeOrigin,
+ node.opcode,
+ [armV7AsRegister(newList, postInstructions, node.operands[0], "p", false)])
+ else
+ newList << node
+ end
+ newList += postInstructions
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lowering of register reuse in compare instructions. For example:
+#
+# cieq t0, t1, t0
+#
+# will become:
+#
+# mov tmp, t0
+# cieq tmp, t1, t0
+#
+
+def armV7LowerRegisterReuse(list)
+ newList = []
+ list.each {
+ | node |
+ if node.is_a? Instruction
+ case node.opcode
+ when "cieq", "cineq", "cia", "ciaeq", "cib", "cibeq", "cigt", "cigteq", "cilt", "cilteq",
+ "cpeq", "cpneq", "cpa", "cpaeq", "cpb", "cpbeq", "cpgt", "cpgteq", "cplt", "cplteq",
+ "tio", "tis", "tiz", "tinz", "tbo", "tbs", "tbz", "tbnz"
+ if node.operands.size == 2
+ if node.operands[0] == node.operands[1]
+ tmp = Tmp.new(node.codeOrigin, :gpr)
+ newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
+ newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1]])
+ else
+ newList << node
+ end
+ else
+ raise "Wrong number of arguments at #{node.codeOriginString}" unless node.operands.size == 3
+ if node.operands[0] == node.operands[2]
+ tmp = Tmp.new(node.codeOrigin, :gpr)
+ newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
+ newList << Instruction.new(node.codeOrigin, node.opcode, [tmp, node.operands[1], node.operands[2]])
+ elsif node.operands[1] == node.operands[2]
+ tmp = Tmp.new(node.codeOrigin, :gpr)
+ newList << Instruction.new(node.codeOrigin, "move", [node.operands[1], tmp])
+ newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], tmp, node.operands[2]])
+ else
+ newList << node
+ end
+ end
+ else
+ newList << node
+ end
+ else
+ newList << node
+ end
+ }
+ newList
+end
+
+#
+# Lea support.
+#
+
+class Address
+ def armV7EmitLea(destination)
+ if destination == base
+ $asm.puts "adds #{destination.armV7Operand}, \##{offset.value}"
+ else
+ $asm.puts "adds #{destination.armV7Operand}, #{base.armV7Operand}, \##{offset.value}"
+ end
+ end
+end
+
+class BaseIndex
+ def armV7EmitLea(destination)
+ raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
+ $asm.puts "add.w #{destination.armV7Operand}, #{base.armV7Operand}, #{index.armV7Operand}, lsl \##{scaleShift}"
+ end
+end
+
+# FIXME: we could support AbsoluteAddress for lea, but we don't.
+
+#
+# Actual lowering code follows.
+#
+
+class Sequence
+ def lowerARMv7
+ myList = @list
+
+ # Verify that we will only see instructions and labels.
+ myList.each {
+ | node |
+ unless node.is_a? Instruction or
+ node.is_a? Label or
+ node.is_a? LocalLabel or
+ node.is_a? Skip
+ raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
+ end
+ }
+
+ myList = armV7LowerBranchOps(myList)
+ myList = armV7LowerShiftOps(myList)
+ myList = armV7LowerMalformedAddresses(myList)
+ myList = armV7LowerMalformedAddressesDouble(myList)
+ myList = armV7LowerMisplacedImmediates(myList)
+ myList = armV7LowerMalformedImmediates(myList)
+ myList = armV7LowerMisplacedAddresses(myList)
+ myList = armV7LowerRegisterReuse(myList)
+ myList = assignRegistersToTemporaries(myList, :gpr, ARMv7_EXTRA_GPRS)
+ myList = assignRegistersToTemporaries(myList, :fpr, ARMv7_EXTRA_FPRS)
+ myList.each {
+ | node |
+ node.lower("ARMv7")
+ }
+ end
+end
+
+def armV7Operands(operands)
+ operands.map{|v| v.armV7Operand}.join(", ")
+end
+
+def armV7FlippedOperands(operands)
+ armV7Operands([operands[-1]] + operands[0..-2])
+end
+
+def emitArmV7Compact(opcode2, opcode3, operands)
+ if operands.size == 3
+ $asm.puts "#{opcode3} #{armV7FlippedOperands(operands)}"
+ else
+ raise unless operands.size == 2
+ raise unless operands[1].is_a? RegisterID
+ if operands[0].is_a? Immediate
+ $asm.puts "#{opcode3} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
+ else
+ $asm.puts "#{opcode2} #{armV7FlippedOperands(operands)}"
+ end
+ end
+end
+
+def emitArmV7(opcode, operands)
+ if operands.size == 3
+ $asm.puts "#{opcode} #{armV7FlippedOperands(operands)}"
+ else
+ raise unless operands.size == 2
+ $asm.puts "#{opcode} #{operands[1].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
+ end
+end
+
+def emitArmV7DoubleBranch(branchOpcode, operands)
+ $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
+ $asm.puts "vmrs apsr_nzcv, fpscr"
+ $asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
+end
+
+def emitArmV7Test(operands)
+ value = operands[0]
+ case operands.size
+ when 2
+ mask = Immediate.new(codeOrigin, -1)
+ when 3
+ mask = operands[1]
+ else
+ raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
+ end
+
+ if mask.is_a? Immediate and mask.value == -1
+ $asm.puts "tst #{value.armV7Operand}, #{value.armV7Operand}"
+ elsif mask.is_a? Immediate
+ $asm.puts "tst.w #{value.armV7Operand}, #{mask.armV7Operand}"
+ else
+ $asm.puts "tst #{value.armV7Operand}, #{mask.armV7Operand}"
+ end
+end
+
+def emitArmV7Compare(operands, code)
+ $asm.puts "movs #{operands[2].armV7Operand}, \#0"
+ $asm.puts "cmp #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
+ $asm.puts "it #{code}"
+ $asm.puts "mov#{code} #{operands[2].armV7Operand}, \#1"
+end
+
+def emitArmV7TestSet(operands, code)
+ $asm.puts "movs #{operands[-1].armV7Operand}, \#0"
+ emitArmV7Test(operands)
+ $asm.puts "it #{code}"
+ $asm.puts "mov#{code} #{operands[-1].armV7Operand}, \#1"
+end
+
+class Instruction
+ def lowerARMv7
+ $asm.comment codeOriginString
+ case opcode
+ when "addi", "addp", "addis"
+ if opcode == "addis"
+ suffix = "s"
+ else
+ suffix = ""
+ end
+ if operands.size == 3 and operands[0].is_a? Immediate
+ raise unless operands[1].is_a? RegisterID
+ raise unless operands[2].is_a? RegisterID
+ if operands[0].value == 0 and suffix.empty?
+ unless operands[1] == operands[2]
+ $asm.puts "mov #{operands[2].armV7Operand}, #{operands[1].armV7Operand}"
+ end
+ else
+ $asm.puts "adds #{operands[2].armV7Operand}, #{operands[1].armV7Operand}, #{operands[0].armV7Operand}"
+ end
+ elsif operands.size == 3 and operands[0].is_a? RegisterID
+ raise unless operands[1].is_a? RegisterID
+ raise unless operands[2].is_a? RegisterID
+ $asm.puts "adds #{armV7FlippedOperands(operands)}"
+ else
+ if operands[0].is_a? Immediate
+ unless Immediate.new(nil, 0) == operands[0]
+ $asm.puts "adds #{armV7FlippedOperands(operands)}"
+ end
+ else
+ $asm.puts "add#{suffix} #{armV7FlippedOperands(operands)}"
+ end
+ end
+ when "andi", "andp"
+ emitArmV7Compact("ands", "and", operands)
+ when "ori", "orp"
+ emitArmV7Compact("orrs", "orr", operands)
+ when "oris"
+ emitArmV7Compact("orrs", "orrs", operands)
+ when "xori", "xorp"
+ emitArmV7Compact("eors", "eor", operands)
+ when "lshifti"
+ emitArmV7Compact("lsls", "lsls", operands)
+ when "rshifti"
+ emitArmV7Compact("asrs", "asrs", operands)
+ when "urshifti"
+ emitArmV7Compact("lsrs", "lsrs", operands)
+ when "muli"
+ if operands.size == 2 or operands[0] == operands[2] or operands[1] == operands[2]
+ emitArmV7("muls", operands)
+ else
+ $asm.puts "mov #{operands[2].armV7Operand}, #{operands[0].armV7Operand}"
+ $asm.puts "muls #{operands[2].armV7Operand}, #{operands[2].armV7Operand}, #{operands[1].armV7Operand}"
+ end
+ when "subi", "subp", "subis"
+ emitArmV7Compact("subs", "subs", operands)
+ when "negi"
+ $asm.puts "rsbs #{operands[0].armV7Operand}, #{operands[0].armV7Operand}, \#0"
+ when "noti"
+ $asm.puts "mvns #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
+ when "loadi", "loadp"
+ $asm.puts "ldr #{armV7FlippedOperands(operands)}"
+ when "storei", "storep"
+ $asm.puts "str #{armV7Operands(operands)}"
+ when "loadb"
+ $asm.puts "ldrb #{armV7FlippedOperands(operands)}"
+ when "loadbs"
+ $asm.puts "ldrsb.w #{armV7FlippedOperands(operands)}"
+ when "storeb"
+ $asm.puts "strb #{armV7Operands(operands)}"
+ when "loadh"
+ $asm.puts "ldrh #{armV7FlippedOperands(operands)}"
+ when "loadhs"
+ $asm.puts "ldrsh.w #{armV7FlippedOperands(operands)}"
+ when "storeh"
+ $asm.puts "strh #{armV7Operands(operands)}"
+ when "loadd"
+ $asm.puts "vldr.64 #{armV7FlippedOperands(operands)}"
+ when "stored"
+ $asm.puts "vstr.64 #{armV7Operands(operands)}"
+ when "addd"
+ emitArmV7("vadd.f64", operands)
+ when "divd"
+ emitArmV7("vdiv.f64", operands)
+ when "subd"
+ emitArmV7("vsub.f64", operands)
+ when "muld"
+ emitArmV7("vmul.f64", operands)
+ when "sqrtd"
+ $asm.puts "vsqrt.f64 #{armV7FlippedOperands(operands)}"
+ when "ci2d"
+ $asm.puts "vmov #{operands[1].armV7Single}, #{operands[0].armV7Operand}"
+ $asm.puts "vcvt.f64.s32 #{operands[1].armV7Operand}, #{operands[1].armV7Single}"
+ when "bdeq"
+ emitArmV7DoubleBranch("beq", operands)
+ when "bdneq"
+ $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
+ $asm.puts "vmrs apsr_nzcv, fpscr"
+ isUnordered = LocalLabel.unique("bdneq")
+ $asm.puts "bvs #{LabelReference.new(codeOrigin, isUnordered).asmLabel}"
+ $asm.puts "bne #{operands[2].asmLabel}"
+ isUnordered.lower("ARMv7")
+ when "bdgt"
+ emitArmV7DoubleBranch("bgt", operands)
+ when "bdgteq"
+ emitArmV7DoubleBranch("bge", operands)
+ when "bdlt"
+ emitArmV7DoubleBranch("bmi", operands)
+ when "bdlteq"
+ emitArmV7DoubleBranch("bls", operands)
+ when "bdequn"
+ $asm.puts "vcmpe.f64 #{armV7Operands(operands[0..1])}"
+ $asm.puts "vmrs apsr_nzcv, fpscr"
+ $asm.puts "bvs #{operands[2].asmLabel}"
+ $asm.puts "beq #{operands[2].asmLabel}"
+ when "bdnequn"
+ emitArmV7DoubleBranch("bne", operands)
+ when "bdgtun"
+ emitArmV7DoubleBranch("bhi", operands)
+ when "bdgtequn"
+ emitArmV7DoubleBranch("bpl", operands)
+ when "bdltun"
+ emitArmV7DoubleBranch("blt", operands)
+ when "bdltequn"
+ emitArmV7DoubleBranch("ble", operands)
+ when "btd2i"
+ # FIXME: may be a good idea to just get rid of this instruction, since the interpreter
+ # currently does not use it.
+ raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
+ when "td2i"
+ $asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
+ $asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
+ when "bcd2i"
+ $asm.puts "vcvt.s32.f64 #{ARMv7_SCRATCH_FPR.armV7Single}, #{operands[0].armV7Operand}"
+ $asm.puts "vmov #{operands[1].armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
+ $asm.puts "vcvt.f64.s32 #{ARMv7_SCRATCH_FPR.armV7Operand}, #{ARMv7_SCRATCH_FPR.armV7Single}"
+ emitArmV7DoubleBranch("bne", [ARMv7_SCRATCH_FPR, operands[0], operands[2]])
+ $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
+ $asm.puts "beq #{operands[2].asmLabel}"
+ when "movdz"
+ # FIXME: either support this or remove it.
+ raise "ARMv7 does not support this opcode yet, #{codeOrigin}"
+ when "pop"
+ $asm.puts "pop #{operands[0].armV7Operand}"
+ when "push"
+ $asm.puts "push #{operands[0].armV7Operand}"
+ when "move", "sxi2p", "zxi2p"
+ if operands[0].is_a? Immediate
+ armV7MoveImmediate(operands[0].value, operands[1])
+ else
+ $asm.puts "mov #{armV7FlippedOperands(operands)}"
+ end
+ when "nop"
+ $asm.puts "nop"
+ when "bieq", "bpeq", "bbeq"
+ if Immediate.new(nil, 0) == operands[0]
+ $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
+ elsif Immediate.new(nil, 0) == operands[1]
+ $asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
+ else
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ end
+ $asm.puts "beq #{operands[2].asmLabel}"
+ when "bineq", "bpneq", "bbneq"
+ if Immediate.new(nil, 0) == operands[0]
+ $asm.puts "tst #{operands[1].armV7Operand}, #{operands[1].armV7Operand}"
+ elsif Immediate.new(nil, 0) == operands[1]
+ $asm.puts "tst #{operands[0].armV7Operand}, #{operands[0].armV7Operand}"
+ else
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ end
+ $asm.puts "bne #{operands[2].asmLabel}"
+ when "bia", "bpa", "bba"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "bhi #{operands[2].asmLabel}"
+ when "biaeq", "bpaeq", "bbaeq"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "bhs #{operands[2].asmLabel}"
+ when "bib", "bpb", "bbb"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "blo #{operands[2].asmLabel}"
+ when "bibeq", "bpbeq", "bbbeq"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "bls #{operands[2].asmLabel}"
+ when "bigt", "bpgt", "bbgt"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "bgt #{operands[2].asmLabel}"
+ when "bigteq", "bpgteq", "bbgteq"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "bge #{operands[2].asmLabel}"
+ when "bilt", "bplt", "bblt"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "blt #{operands[2].asmLabel}"
+ when "bilteq", "bplteq", "bblteq"
+ $asm.puts "cmp #{armV7Operands(operands[0..1])}"
+ $asm.puts "ble #{operands[2].asmLabel}"
+ when "btiz", "btpz", "btbz"
+ emitArmV7Test(operands)
+ $asm.puts "beq #{operands[-1].asmLabel}"
+ when "btinz", "btpnz", "btbnz"
+ emitArmV7Test(operands)
+ $asm.puts "bne #{operands[-1].asmLabel}"
+ when "btio", "btpo", "btbo"
+ emitArmV7Test(operands)
+ $asm.puts "bvs #{operands[-1].asmLabel}"
+ when "btis", "btps", "btbs"
+ emitArmV7Test(operands)
+ $asm.puts "bmi #{operands[-1].asmLabel}"
+ when "jmp"
+ if operands[0].label?
+ $asm.puts "b #{operands[0].asmLabel}"
+ else
+ $asm.puts "mov pc, #{operands[0].armV7Operand}"
+ end
+ when "call"
+ if operands[0].label?
+ $asm.puts "blx #{operands[0].asmLabel}"
+ else
+ $asm.puts "blx #{operands[0].armV7Operand}"
+ end
+ when "break"
+ $asm.puts "bkpt"
+ when "ret"
+ $asm.puts "bx lr"
+ when "cieq", "cpeq"
+ emitArmV7Compare(operands, "eq")
+ when "cineq", "cpneq"
+ emitArmV7Compare(operands, "ne")
+ when "cia", "cpa"
+ emitArmV7Compare(operands, "hi")
+ when "ciaeq", "cpaeq"
+ emitArmV7Compare(operands, "hs")
+ when "cib", "cpb"
+ emitArmV7Compare(operands, "lo")
+ when "cibeq", "cpbeq"
+ emitArmV7Compare(operands, "ls")
+ when "cigt", "cpgt"
+ emitArmV7Compare(operands, "gt")
+ when "cigteq", "cpgteq"
+ emitArmV7Compare(operands, "ge")
+ when "cilt", "cplt"
+ emitArmV7Compare(operands, "lt")
+ when "cilteq", "cplteq"
+ emitArmV7Compare(operands, "le")
+ when "tio", "tbo"
+ emitArmV7TestSet(operands, "vs")
+ when "tis", "tbs"
+ emitArmV7TestSet(operands, "mi")
+ when "tiz", "tbz"
+ emitArmV7TestSet(operands, "eq")
+ when "tinz", "tbnz"
+ emitArmV7TestSet(operands, "ne")
+ when "peek"
+ $asm.puts "ldr #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
+ when "poke"
+ $asm.puts "str #{operands[1].armV7Operand}, [sp, \##{operands[0].value * 4}]"
+ when "fii2d"
+ $asm.puts "vmov #{operands[2].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
+ when "fd2ii"
+ $asm.puts "vmov #{operands[1].armV7Operand}, #{operands[2].armV7Operand}, #{operands[0].armV7Operand}"
+ when "bo"
+ $asm.puts "bvs #{operands[0].asmLabel}"
+ when "bs"
+ $asm.puts "bmi #{operands[0].asmLabel}"
+ when "bz"
+ $asm.puts "beq #{operands[0].asmLabel}"
+ when "bnz"
+ $asm.puts "bne #{operands[0].asmLabel}"
+ when "leai", "leap"
+ operands[0].armV7EmitLea(operands[1])
+ when "smulli"
+ raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
+ $asm.puts "smull #{operands[2].armV7Operand}, #{operands[3].armV7Operand}, #{operands[0].armV7Operand}, #{operands[1].armV7Operand}"
+ else
+ raise "Unhandled opcode #{opcode} at #{codeOriginString}"
+ end
+ end
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/asm.rb b/Source/JavaScriptCore/offlineasm/asm.rb
new file mode 100644
index 000000000..a93a8c5dd
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/asm.rb
@@ -0,0 +1,176 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+$: << File.dirname(__FILE__)
+
+require "backends"
+require "digest/sha1"
+require "offsets"
+require "parser"
+require "self_hash"
+require "settings"
+require "transform"
+
+class Assembler
+ def initialize(outp)
+ @outp = outp
+ @state = :cpp
+ @commentState = :none
+ @comment = nil
+ end
+
+ def enterAsm
+ @outp.puts "asm ("
+ @state = :asm
+ end
+
+ def leaveAsm
+ putsLastComment
+ @outp.puts ");"
+ @state = :cpp
+ end
+
+ def inAsm
+ enterAsm
+ yield
+ leaveAsm
+ end
+
+ def lastComment
+ if @comment
+ result = "// #{@comment}"
+ else
+ result = ""
+ end
+ @commentState = :none
+ @comment = nil
+ result
+ end
+
+ def putsLastComment
+ comment = lastComment
+ unless comment.empty?
+ @outp.puts comment
+ end
+ end
+
+ def puts(*line)
+ raise unless @state == :asm
+ @outp.puts("\"\\t" + line.join('') + "\\n\" #{lastComment}")
+ end
+
+ def print(line)
+ raise unless @state == :asm
+ @outp.print("\"" + line + "\"")
+ end
+
+ def putsLabel(labelName)
+ raise unless @state == :asm
+ @outp.puts("OFFLINE_ASM_GLOBAL_LABEL(#{labelName}) #{lastComment}")
+ end
+
+ def putsLocalLabel(labelName)
+ raise unless @state == :asm
+ @outp.puts("LOCAL_LABEL_STRING(#{labelName}) \":\\n\" #{lastComment}")
+ end
+
+ def self.labelReference(labelName)
+ "\" SYMBOL_STRING(#{labelName}) \""
+ end
+
+ def self.localLabelReference(labelName)
+ "\" LOCAL_LABEL_STRING(#{labelName}) \""
+ end
+
+ def comment(text)
+ case @commentState
+ when :none
+ @comment = text
+ @commentState = :one
+ when :one
+ @outp.puts "// #{@comment}"
+ @outp.puts "// #{text}"
+ @comment = nil
+ @commentState = :many
+ when :many
+ @outp.puts "// #{text}"
+ else
+ raise
+ end
+ end
+end
+
+asmFile = ARGV.shift
+offsetsFile = ARGV.shift
+outputFlnm = ARGV.shift
+
+$stderr.puts "offlineasm: Parsing #{asmFile} and #{offsetsFile} and creating assembly file #{outputFlnm}."
+
+configurationList = offsetsAndConfigurationIndex(offsetsFile)
+inputData = IO::read(asmFile)
+
+inputHash =
+ "// offlineasm input hash: " + Digest::SHA1.hexdigest(inputData) +
+ " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) +
+ " " + selfHash
+
+if FileTest.exist? outputFlnm
+ File.open(outputFlnm, "r") {
+ | inp |
+ firstLine = inp.gets
+ if firstLine and firstLine.chomp == inputHash
+ $stderr.puts "offlineasm: Nothing changed."
+ exit 0
+ end
+ }
+end
+
+File.open(outputFlnm, "w") {
+ | outp |
+ $output = outp
+ $output.puts inputHash
+
+ $asm = Assembler.new($output)
+
+ ast = parse(lex(inputData))
+
+ configurationList.each {
+ | configuration |
+ offsetsList = configuration[0]
+ configIndex = configuration[1]
+ forSettings(computeSettingsCombinations(ast)[configIndex], ast) {
+ | concreteSettings, lowLevelAST, backend |
+ lowLevelAST = lowLevelAST.resolve(*buildOffsetsMap(lowLevelAST, offsetsList))
+ emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) {
+ $asm.inAsm {
+ lowLevelAST.lower(backend)
+ }
+ }
+ }
+ }
+}
+
+$stderr.puts "offlineasm: Assembly file #{outputFlnm} successfully generated."
+
diff --git a/Source/JavaScriptCore/offlineasm/ast.rb b/Source/JavaScriptCore/offlineasm/ast.rb
new file mode 100644
index 000000000..f67b0fc60
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/ast.rb
@@ -0,0 +1,1039 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# Base utility types for the AST.
+#
+
+# Valid methods for Node:
+#
+# node.children -> Returns an array of immediate children.
+#
+# node.descendents -> Returns an array of all strict descendants (children
+# and children of children, transitively).
+#
+# node.flatten -> Returns an array containing the strict descendants and
+# the node itself.
+#
+# node.filter(type) -> Returns an array containing those elements in
+# node.flatten that are of the given type (is_a? type returns true).
+#
+# node.mapChildren{|v| ...} -> Returns a new node with all children
+# replaced according to the given block.
+#
+# Examples:
+#
+# node.filter(Setting).uniq -> Returns all of the settings that the AST's
+# IfThenElse blocks depend on.
+#
+# node.filter(StructOffset).uniq -> Returns all of the structure offsets
+# that the AST depends on.
+
+class Node
+ attr_reader :codeOrigin
+
+ def initialize(codeOrigin)
+ @codeOrigin = codeOrigin
+ end
+
+ def codeOriginString
+ "line number #{@codeOrigin}"
+ end
+
+ def descendants
+ children.collect{|v| v.flatten}.flatten
+ end
+
+ def flatten
+ [self] + descendants
+ end
+
+ def filter(type)
+ flatten.select{|v| v.is_a? type}
+ end
+end
+
+class NoChildren < Node
+ def initialize(codeOrigin)
+ super(codeOrigin)
+ end
+
+ def children
+ []
+ end
+
+ def mapChildren
+ self
+ end
+end
+
+class StructOffsetKey
+ attr_reader :struct, :field
+
+ def initialize(struct, field)
+ @struct = struct
+ @field = field
+ end
+
+ def hash
+ @struct.hash + @field.hash * 3
+ end
+
+ def eql?(other)
+ @struct == other.struct and @field == other.field
+ end
+end
+
+#
+# AST nodes.
+#
+
+class StructOffset < NoChildren
+ attr_reader :struct, :field
+
+ def initialize(codeOrigin, struct, field)
+ super(codeOrigin)
+ @struct = struct
+ @field = field
+ end
+
+ @@mapping = {}
+
+ def self.forField(codeOrigin, struct, field)
+ key = StructOffsetKey.new(struct, field)
+
+ unless @@mapping[key]
+ @@mapping[key] = StructOffset.new(codeOrigin, struct, field)
+ end
+ @@mapping[key]
+ end
+
+ def dump
+ "#{struct}::#{field}"
+ end
+
+ def <=>(other)
+ if @struct != other.struct
+ return @struct <=> other.struct
+ end
+ @field <=> other.field
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class Sizeof < NoChildren
+ attr_reader :struct
+
+ def initialize(codeOrigin, struct)
+ super(codeOrigin)
+ @struct = struct
+ end
+
+ @@mapping = {}
+
+ def self.forName(codeOrigin, struct)
+ unless @@mapping[struct]
+ @@mapping[struct] = Sizeof.new(codeOrigin, struct)
+ end
+ @@mapping[struct]
+ end
+
+ def dump
+ "sizeof #{@struct}"
+ end
+
+ def <=>(other)
+ @struct <=> other.struct
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class Immediate < NoChildren
+ attr_reader :value
+
+ def initialize(codeOrigin, value)
+ super(codeOrigin)
+ @value = value
+ raise "Bad immediate value #{value.inspect} at #{codeOriginString}" unless value.is_a? Integer
+ end
+
+ def dump
+ "#{value}"
+ end
+
+ def ==(other)
+ other.is_a? Immediate and other.value == @value
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class AddImmediates < Node
+ attr_reader :left, :right
+
+ def initialize(codeOrigin, left, right)
+ super(codeOrigin)
+ @left = left
+ @right = right
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ AddImmediates.new(codeOrigin, (yield @left), (yield @right))
+ end
+
+ def dump
+ "(#{left.dump} + #{right.dump})"
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class SubImmediates < Node
+ attr_reader :left, :right
+
+ def initialize(codeOrigin, left, right)
+ super(codeOrigin)
+ @left = left
+ @right = right
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ SubImmediates.new(codeOrigin, (yield @left), (yield @right))
+ end
+
+ def dump
+ "(#{left.dump} - #{right.dump})"
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class MulImmediates < Node
+ attr_reader :left, :right
+
+ def initialize(codeOrigin, left, right)
+ super(codeOrigin)
+ @left = left
+ @right = right
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ MulImmediates.new(codeOrigin, (yield @left), (yield @right))
+ end
+
+ def dump
+ "(#{left.dump} * #{right.dump})"
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class NegImmediate < Node
+ attr_reader :child
+
+ def initialize(codeOrigin, child)
+ super(codeOrigin)
+ @child = child
+ end
+
+ def children
+ [@child]
+ end
+
+ def mapChildren
+ NegImmediate.new(codeOrigin, (yield @child))
+ end
+
+ def dump
+ "(-#{@child.dump})"
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ true
+ end
+
+ def register?
+ false
+ end
+end
+
+class RegisterID < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ @@mapping = {}
+
+ def self.forName(codeOrigin, name)
+ unless @@mapping[name]
+ @@mapping[name] = RegisterID.new(codeOrigin, name)
+ end
+ @@mapping[name]
+ end
+
+ def dump
+ name
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ true
+ end
+end
+
+class FPRegisterID < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ @@mapping = {}
+
+ def self.forName(codeOrigin, name)
+ unless @@mapping[name]
+ @@mapping[name] = FPRegisterID.new(codeOrigin, name)
+ end
+ @@mapping[name]
+ end
+
+ def dump
+ name
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ true
+ end
+end
+
+class Variable < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ @@mapping = {}
+
+ def self.forName(codeOrigin, name)
+ unless @@mapping[name]
+ @@mapping[name] = Variable.new(codeOrigin, name)
+ end
+ @@mapping[name]
+ end
+
+ def dump
+ name
+ end
+end
+
+class Address < Node
+ attr_reader :base, :offset
+
+ def initialize(codeOrigin, base, offset)
+ super(codeOrigin)
+ @base = base
+ @offset = offset
+ raise "Bad base for address #{base.inspect} at #{codeOriginString}" unless base.is_a? Variable or base.register?
+ raise "Bad offset for address #{offset.inspect} at #{codeOriginString}" unless offset.is_a? Variable or offset.immediate?
+ end
+
+ def children
+ [@base, @offset]
+ end
+
+ def mapChildren
+ Address.new(codeOrigin, (yield @base), (yield @offset))
+ end
+
+ def dump
+ "#{offset.dump}[#{base.dump}]"
+ end
+
+ def address?
+ true
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ false
+ end
+end
+
+class BaseIndex < Node
+ attr_reader :base, :index, :scale, :offset
+
+ def initialize(codeOrigin, base, index, scale, offset)
+ super(codeOrigin)
+ @base = base
+ @index = index
+ @scale = scale
+ raise unless [1, 2, 4, 8].member? @scale
+ @offset = offset
+ end
+
+ def scaleShift
+ case scale
+ when 1
+ 0
+ when 2
+ 1
+ when 4
+ 2
+ when 8
+ 3
+ else
+ raise "Bad scale at #{codeOriginString}"
+ end
+ end
+
+ def children
+ [@base, @index, @offset]
+ end
+
+ def mapChildren
+ BaseIndex.new(codeOrigin, (yield @base), (yield @index), @scale, (yield @offset))
+ end
+
+ def dump
+ "#{offset.dump}[#{base.dump}, #{index.dump}, #{scale}]"
+ end
+
+ def address?
+ true
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ false
+ end
+end
+
+class AbsoluteAddress < NoChildren
+ attr_reader :address
+
+ def initialize(codeOrigin, address)
+ super(codeOrigin)
+ @address = address
+ end
+
+ def dump
+ "#{address.dump}[]"
+ end
+
+ def address?
+ true
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ false
+ end
+end
+
+class Instruction < Node
+ attr_reader :opcode, :operands
+
+ def initialize(codeOrigin, opcode, operands)
+ super(codeOrigin)
+ @opcode = opcode
+ @operands = operands
+ end
+
+ def children
+ operands
+ end
+
+ def mapChildren(&proc)
+ Instruction.new(codeOrigin, @opcode, @operands.map(&proc))
+ end
+
+ def dump
+ "\t" + opcode.to_s + " " + operands.collect{|v| v.dump}.join(", ")
+ end
+end
+
+class Error < NoChildren
+ def initialize(codeOrigin)
+ super(codeOrigin)
+ end
+
+ def dump
+ "\terror"
+ end
+end
+
+class ConstDecl < Node
+ attr_reader :variable, :value
+
+ def initialize(codeOrigin, variable, value)
+ super(codeOrigin)
+ @variable = variable
+ @value = value
+ end
+
+ def children
+ [@variable, @value]
+ end
+
+ def mapChildren
+ ConstDecl.new(codeOrigin, (yield @variable), (yield @value))
+ end
+
+ def dump
+ "const #{@variable.dump} = #{@value.dump}"
+ end
+end
+
+$labelMapping = {}
+
+class Label < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ def self.forName(codeOrigin, name)
+ if $labelMapping[name]
+ raise "Label name collision: #{name}" unless $labelMapping[name].is_a? Label
+ else
+ $labelMapping[name] = Label.new(codeOrigin, name)
+ end
+ $labelMapping[name]
+ end
+
+ def dump
+ "#{name}:"
+ end
+end
+
+class LocalLabel < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ @@uniqueNameCounter = 0
+
+ def self.forName(codeOrigin, name)
+ if $labelMapping[name]
+ raise "Label name collision: #{name}" unless $labelMapping[name].is_a? LocalLabel
+ else
+ $labelMapping[name] = LocalLabel.new(codeOrigin, name)
+ end
+ $labelMapping[name]
+ end
+
+ def self.unique(comment)
+ newName = "_#{comment}"
+ if $labelMapping[newName]
+ while $labelMapping[newName = "_#{@@uniqueNameCounter}_#{comment}"]
+ @@uniqueNameCounter += 1
+ end
+ end
+ forName(nil, newName)
+ end
+
+ def cleanName
+ if name =~ /^\./
+ "_" + name[1..-1]
+ else
+ name
+ end
+ end
+
+ def dump
+ "#{name}:"
+ end
+end
+
+class LabelReference < Node
+ attr_reader :label
+
+ def initialize(codeOrigin, label)
+ super(codeOrigin)
+ @label = label
+ end
+
+ def children
+ [@label]
+ end
+
+ def mapChildren
+ LabelReference.new(codeOrigin, (yield @label))
+ end
+
+ def name
+ label.name
+ end
+
+ def dump
+ label.name
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ true
+ end
+end
+
+class LocalLabelReference < NoChildren
+ attr_reader :label
+
+ def initialize(codeOrigin, label)
+ super(codeOrigin)
+ @label = label
+ end
+
+ def children
+ [@label]
+ end
+
+ def mapChildren
+ LocalLabelReference.new(codeOrigin, (yield @label))
+ end
+
+ def name
+ label.name
+ end
+
+ def dump
+ label.name
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ true
+ end
+end
+
+class Sequence < Node
+ attr_reader :list
+
+ def initialize(codeOrigin, list)
+ super(codeOrigin)
+ @list = list
+ end
+
+ def children
+ list
+ end
+
+ def mapChildren(&proc)
+ Sequence.new(codeOrigin, @list.map(&proc))
+ end
+
+ def dump
+ list.collect{|v| v.dump}.join("\n")
+ end
+end
+
+class True < NoChildren
+ def initialize
+ super(nil)
+ end
+
+ @@instance = True.new
+
+ def self.instance
+ @@instance
+ end
+
+ def value
+ true
+ end
+
+ def dump
+ "true"
+ end
+end
+
+class False < NoChildren
+ def initialize
+ super(nil)
+ end
+
+ @@instance = False.new
+
+ def self.instance
+ @@instance
+ end
+
+ def value
+ false
+ end
+
+ def dump
+ "false"
+ end
+end
+
+class TrueClass
+ def asNode
+ True.instance
+ end
+end
+
+class FalseClass
+ def asNode
+ False.instance
+ end
+end
+
+class Setting < NoChildren
+ attr_reader :name
+
+ def initialize(codeOrigin, name)
+ super(codeOrigin)
+ @name = name
+ end
+
+ @@mapping = {}
+
+ def self.forName(codeOrigin, name)
+ unless @@mapping[name]
+ @@mapping[name] = Setting.new(codeOrigin, name)
+ end
+ @@mapping[name]
+ end
+
+ def dump
+ name
+ end
+end
+
+class And < Node
+ attr_reader :left, :right
+
+ def initialize(codeOrigin, left, right)
+ super(codeOrigin)
+ @left = left
+ @right = right
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ And.new(codeOrigin, (yield @left), (yield @right))
+ end
+
+ def dump
+ "(#{left.dump} and #{right.dump})"
+ end
+end
+
+class Or < Node
+ attr_reader :left, :right
+
+ def initialize(codeOrigin, left, right)
+ super(codeOrigin)
+ @left = left
+ @right = right
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ Or.new(codeOrigin, (yield @left), (yield @right))
+ end
+
+ def dump
+ "(#{left.dump} or #{right.dump})"
+ end
+end
+
+class Not < Node
+ attr_reader :child
+
+ def initialize(codeOrigin, child)
+ super(codeOrigin)
+ @child = child
+ end
+
+ def children
+ [@left, @right]
+ end
+
+ def mapChildren
+ Not.new(codeOrigin, (yield @child))
+ end
+
+ def dump
+ "(not #{child.dump})"
+ end
+end
+
+class Skip < NoChildren
+ def initialize(codeOrigin)
+ super(codeOrigin)
+ end
+
+ def dump
+ "\tskip"
+ end
+end
+
+class IfThenElse < Node
+ attr_reader :predicate, :thenCase
+ attr_accessor :elseCase
+
+ def initialize(codeOrigin, predicate, thenCase)
+ super(codeOrigin)
+ @predicate = predicate
+ @thenCase = thenCase
+ @elseCase = Skip.new(codeOrigin)
+ end
+
+ def children
+ if @elseCase
+ [@predicate, @thenCase, @elseCase]
+ else
+ [@predicate, @thenCase]
+ end
+ end
+
+ def mapChildren
+ IfThenElse.new(codeOrigin, (yield @predicate), (yield @thenCase), (yield @elseCase))
+ end
+
+ def dump
+ "if #{predicate.dump}\n" + thenCase.dump + "\nelse\n" + elseCase.dump + "\nend"
+ end
+end
+
+class Macro < Node
+ attr_reader :name, :variables, :body
+
+ def initialize(codeOrigin, name, variables, body)
+ super(codeOrigin)
+ @name = name
+ @variables = variables
+ @body = body
+ end
+
+ def children
+ @variables + [@body]
+ end
+
+ def mapChildren
+ Macro.new(codeOrigin, @name, @variables.map{|v| yield v}, (yield @body))
+ end
+
+ def dump
+ "macro #{name}(" + variables.collect{|v| v.dump}.join(", ") + ")\n" + body.dump + "\nend"
+ end
+end
+
+class MacroCall < Node
+ attr_reader :name, :operands
+
+ def initialize(codeOrigin, name, operands)
+ super(codeOrigin)
+ @name = name
+ @operands = operands
+ raise unless @operands
+ @operands.each{|v| raise unless v}
+ end
+
+ def children
+ @operands
+ end
+
+ def mapChildren(&proc)
+ MacroCall.new(codeOrigin, @name, @operands.map(&proc))
+ end
+
+ def dump
+ "\t#{name}(" + operands.collect{|v| v.dump}.join(", ") + ")"
+ end
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/backends.rb b/Source/JavaScriptCore/offlineasm/backends.rb
new file mode 100644
index 000000000..2c65b517d
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/backends.rb
@@ -0,0 +1,96 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "armv7"
+require "ast"
+require "x86"
+
+BACKENDS =
+ [
+ "X86",
+ "ARMv7"
+ ]
+
+# Keep the set of working backends separate from the set of backends that might be
+# supported. This is great because the BACKENDS list is almost like a reserved
+# words list, in that it causes settings resolution to treat those words specially.
+# Hence this lets us set aside the name of a backend we might want to support in
+# the future while not actually supporting the backend yet.
+WORKING_BACKENDS =
+ [
+ "X86",
+ "ARMv7"
+ ]
+
+BACKEND_PATTERN = Regexp.new('\\A(' + BACKENDS.join(')|(') + ')\\Z')
+
+class Node
+ def lower(name)
+ send("lower" + name)
+ end
+end
+
+# Overrides for lower() for those nodes that are backend-agnostic
+
+class Label
+ def lower(name)
+ $asm.putsLabel(self.name[1..-1])
+ end
+end
+
+class LocalLabel
+ def lower(name)
+ $asm.putsLocalLabel "_offlineasm_#{self.name[1..-1]}"
+ end
+end
+
+class LabelReference
+ def asmLabel
+ Assembler.labelReference(name[1..-1])
+ end
+end
+
+class LocalLabelReference
+ def asmLabel
+ Assembler.localLabelReference("_offlineasm_"+name[1..-1])
+ end
+end
+
+class Skip
+ def lower(name)
+ end
+end
+
+class Sequence
+ def lower(name)
+ if respond_to? "lower#{name}"
+ send("lower#{name}")
+ else
+ @list.each {
+ | node |
+ node.lower(name)
+ }
+ end
+ end
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/generate_offset_extractor.rb b/Source/JavaScriptCore/offlineasm/generate_offset_extractor.rb
new file mode 100644
index 000000000..8bdf4505d
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/generate_offset_extractor.rb
@@ -0,0 +1,146 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+$: << File.dirname(__FILE__)
+
+require "backends"
+require "digest/sha1"
+require "offsets"
+require "parser"
+require "self_hash"
+require "settings"
+require "transform"
+
+inputFlnm = ARGV.shift
+outputFlnm = ARGV.shift
+
+$stderr.puts "offlineasm: Parsing #{inputFlnm} and creating offset extractor #{outputFlnm}."
+
+def emitMagicNumber
+ OFFSET_MAGIC_NUMBERS.each {
+ | number |
+ $output.puts "#{number},"
+ }
+end
+
+inputData = IO::read(inputFlnm)
+inputHash = "// offlineasm input hash: #{Digest::SHA1.hexdigest(inputData)} #{selfHash}"
+
+if FileTest.exist? outputFlnm
+ File.open(outputFlnm, "r") {
+ | inp |
+ firstLine = inp.gets
+ if firstLine and firstLine.chomp == inputHash
+ $stderr.puts "offlineasm: Nothing changed."
+ exit 0
+ end
+ }
+end
+
+originalAST = parse(lex(inputData))
+
+#
+# Optimize the AST to make configuration extraction faster. This reduces the AST to a form
+# that only contains the things that matter for our purposes: offsets, sizes, and if
+# statements.
+#
+
+class Node
+ def offsetsPruneTo(sequence)
+ children.each {
+ | child |
+ child.offsetsPruneTo(sequence)
+ }
+ end
+
+ def offsetsPrune
+ result = Sequence.new(codeOrigin, [])
+ offsetsPruneTo(result)
+ result
+ end
+end
+
+class IfThenElse
+ def offsetsPruneTo(sequence)
+ ifThenElse = IfThenElse.new(codeOrigin, predicate, thenCase.offsetsPrune)
+ ifThenElse.elseCase = elseCase.offsetsPrune
+ sequence.list << ifThenElse
+ end
+end
+
+class StructOffset
+ def offsetsPruneTo(sequence)
+ sequence.list << self
+ end
+end
+
+class Sizeof
+ def offsetsPruneTo(sequence)
+ sequence.list << self
+ end
+end
+
+prunedAST = originalAST.offsetsPrune
+
+File.open(outputFlnm, "w") {
+ | outp |
+ $output = outp
+ outp.puts inputHash
+ length = 0
+ emitCodeInAllConfigurations(prunedAST) {
+ | settings, ast, backend, index |
+ offsetsList = ast.filter(StructOffset).uniq.sort
+ sizesList = ast.filter(Sizeof).uniq.sort
+ length += OFFSET_HEADER_MAGIC_NUMBERS.size + (OFFSET_MAGIC_NUMBERS.size + 1) * (1 + offsetsList.size + sizesList.size)
+ }
+ outp.puts "static const unsigned extractorTable[#{length}] = {"
+ emitCodeInAllConfigurations(prunedAST) {
+ | settings, ast, backend, index |
+ OFFSET_HEADER_MAGIC_NUMBERS.each {
+ | number |
+ $output.puts "#{number},"
+ }
+
+ offsetsList = ast.filter(StructOffset).uniq.sort
+ sizesList = ast.filter(Sizeof).uniq.sort
+
+ emitMagicNumber
+ outp.puts "#{index},"
+ offsetsList.each {
+ | offset |
+ emitMagicNumber
+ outp.puts "OFFLINE_ASM_OFFSETOF(#{offset.struct}, #{offset.field}),"
+ }
+ sizesList.each {
+ | offset |
+ emitMagicNumber
+ outp.puts "sizeof(#{offset.struct}),"
+ }
+ }
+ outp.puts "};"
+}
+
+$stderr.puts "offlineasm: offset extractor #{outputFlnm} successfully generated."
+
diff --git a/Source/JavaScriptCore/offlineasm/instructions.rb b/Source/JavaScriptCore/offlineasm/instructions.rb
new file mode 100644
index 000000000..497b47371
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/instructions.rb
@@ -0,0 +1,217 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+# Interesting invariant, which we take advantage of: branching instructions
+# always begin with "b", and no non-branching instructions begin with "b".
+# Terminal instructions are "jmp" and "ret".
+
+MACRO_INSTRUCTIONS =
+ [
+ "addi",
+ "andi",
+ "lshifti",
+ "muli",
+ "negi",
+ "noti",
+ "ori",
+ "rshifti",
+ "urshifti",
+ "subi",
+ "xori",
+ "loadi",
+ "loadb",
+ "loadbs",
+ "loadh",
+ "loadhs",
+ "storei",
+ "storeb",
+ "loadd",
+ "moved",
+ "stored",
+ "addd",
+ "divd",
+ "subd",
+ "muld",
+ "sqrtd",
+ "ci2d",
+ "fii2d", # usage: fii2d <gpr with least significant bits>, <gpr with most significant bits>, <fpr>
+ "fd2ii", # usage: fd2ii <fpr>, <gpr with least significant bits>, <gpr with most significant bits>
+ "bdeq",
+ "bdneq",
+ "bdgt",
+ "bdgteq",
+ "bdlt",
+ "bdlteq",
+ "bdequn",
+ "bdnequn",
+ "bdgtun",
+ "bdgtequn",
+ "bdltun",
+ "bdltequn",
+ "btd2i",
+ "td2i",
+ "bcd2i",
+ "movdz",
+ "pop",
+ "push",
+ "move",
+ "sxi2p",
+ "zxi2p",
+ "nop",
+ "bieq",
+ "bineq",
+ "bia",
+ "biaeq",
+ "bib",
+ "bibeq",
+ "bigt",
+ "bigteq",
+ "bilt",
+ "bilteq",
+ "bbeq",
+ "bbneq",
+ "bba",
+ "bbaeq",
+ "bbb",
+ "bbbeq",
+ "bbgt",
+ "bbgteq",
+ "bblt",
+ "bblteq",
+ "btio",
+ "btis",
+ "btiz",
+ "btinz",
+ "btbo",
+ "btbs",
+ "btbz",
+ "btbnz",
+ "jmp",
+ "baddio",
+ "baddis",
+ "baddiz",
+ "baddinz",
+ "bsubio",
+ "bsubis",
+ "bsubiz",
+ "bsubinz",
+ "bmulio",
+ "bmulis",
+ "bmuliz",
+ "bmulinz",
+ "borio",
+ "boris",
+ "boriz",
+ "borinz",
+ "break",
+ "call",
+ "ret",
+ "cieq",
+ "cineq",
+ "cia",
+ "ciaeq",
+ "cib",
+ "cibeq",
+ "cigt",
+ "cigteq",
+ "cilt",
+ "cilteq",
+ "tio",
+ "tis",
+ "tiz",
+ "tinz",
+ "tbo",
+ "tbs",
+ "tbz",
+ "tbnz",
+ "peek",
+ "poke",
+ "bpeq",
+ "bpneq",
+ "bpa",
+ "bpaeq",
+ "bpb",
+ "bpbeq",
+ "bpgt",
+ "bpgteq",
+ "bplt",
+ "bplteq",
+ "addp",
+ "andp",
+ "orp",
+ "subp",
+ "xorp",
+ "loadp",
+ "cpeq",
+ "cpneq",
+ "cpa",
+ "cpaeq",
+ "cpb",
+ "cpbeq",
+ "cpgt",
+ "cpgteq",
+ "cplt",
+ "cplteq",
+ "storep",
+ "btpo",
+ "btps",
+ "btpz",
+ "btpnz",
+ "baddpo",
+ "baddps",
+ "baddpz",
+ "baddpnz",
+ "bo",
+ "bs",
+ "bz",
+ "bnz",
+ "leai",
+ "leap",
+ ]
+
+X86_INSTRUCTIONS =
+ [
+ "cdqi",
+ "idivi"
+ ]
+
+ARMv7_INSTRUCTIONS =
+ [
+ "smulli",
+ "addis",
+ "subis",
+ "oris"
+ ]
+
+INSTRUCTIONS = MACRO_INSTRUCTIONS + X86_INSTRUCTIONS + ARMv7_INSTRUCTIONS
+
+INSTRUCTION_PATTERN = Regexp.new('\\A((' + INSTRUCTIONS.join(')|(') + '))\\Z')
+
+def isBranch(instruction)
+ instruction =~ /^b/
+end
+
+def hasFallThrough(instruction)
+ instruction != "ret" and instruction != "jmp"
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/offsets.rb b/Source/JavaScriptCore/offlineasm/offsets.rb
new file mode 100644
index 000000000..21e1706d2
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/offsets.rb
@@ -0,0 +1,173 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+
+OFFSET_HEADER_MAGIC_NUMBERS = [ 0x9e43fd66, 0x4379bfba ]
+OFFSET_MAGIC_NUMBERS = [ 0xec577ac7, 0x0ff5e755 ]
+
+#
+# offsetsList(ast)
+# sizesList(ast)
+#
+# Returns a list of offsets and sizes used by the AST.
+#
+
+def offsetsList(ast)
+ ast.filter(StructOffset).uniq.sort
+end
+
+def sizesList(ast)
+ ast.filter(Sizeof).uniq.sort
+end
+
+#
+# offsetsAndConfigurationIndex(ast, file) ->
+# [[offsets, index], ...]
+#
+# Parses the offsets from a file and returns a list of offsets and the
+# index of the configuration that is valid in this build target.
+#
+
+def offsetsAndConfigurationIndex(file)
+ endiannessMarkerBytes = nil
+ result = []
+
+ def readInt(endianness, bytes)
+ if endianness == :little
+ # Little endian
+ (bytes[0] << 0 |
+ bytes[1] << 8 |
+ bytes[2] << 16 |
+ bytes[3] << 24)
+ else
+ # Big endian
+ (bytes[0] << 24 |
+ bytes[1] << 16 |
+ bytes[2] << 8 |
+ bytes[3] << 0)
+ end
+ end
+
+ def prepareMagic(endianness, numbers)
+ magicBytes = []
+ numbers.each {
+ | number |
+ currentBytes = []
+ 4.times {
+ currentBytes << (number & 0xff)
+ number >>= 8
+ }
+ if endianness == :big
+ currentBytes.reverse!
+ end
+ magicBytes += currentBytes
+ }
+ magicBytes
+ end
+
+ fileBytes = []
+
+ File.open(file, "r") {
+ | inp |
+ loop {
+ byte = inp.getbyte
+ break unless byte
+ fileBytes << byte
+ }
+ }
+
+ def sliceByteArrays(byteArray, pattern)
+ result = []
+ lastSlicePoint = 0
+ (byteArray.length - pattern.length + 1).times {
+ | index |
+ foundOne = true
+ pattern.length.times {
+ | subIndex |
+ if byteArray[index + subIndex] != pattern[subIndex]
+ foundOne = false
+ break
+ end
+ }
+ if foundOne
+ result << byteArray[lastSlicePoint...index]
+ lastSlicePoint = index + pattern.length
+ end
+ }
+
+ result << byteArray[lastSlicePoint...(byteArray.length)]
+
+ result
+ end
+
+ [:little, :big].each {
+ | endianness |
+ headerMagicBytes = prepareMagic(endianness, OFFSET_HEADER_MAGIC_NUMBERS)
+ magicBytes = prepareMagic(endianness, OFFSET_MAGIC_NUMBERS)
+
+ bigArray = sliceByteArrays(fileBytes, headerMagicBytes)
+ unless bigArray.size <= 1
+ bigArray[1..-1].each {
+ | configArray |
+ array = sliceByteArrays(configArray, magicBytes)
+ index = readInt(endianness, array[1])
+ offsets = []
+ array[2..-1].each {
+ | data |
+ offsets << readInt(endianness, data)
+ }
+ result << [offsets, index]
+ }
+ end
+ }
+
+ raise unless result.length >= 1
+ raise if result.map{|v| v[1]}.uniq.size < result.map{|v| v[1]}.size
+
+ result
+end
+
+#
+# buildOffsetsMap(ast, offsetsList) -> [offsets, sizes]
+#
+# Builds a mapping between StructOffset nodes and their values.
+#
+
+def buildOffsetsMap(ast, offsetsList)
+ offsetsMap = {}
+ sizesMap = {}
+ astOffsetsList = offsetsList(ast)
+ astSizesList = sizesList(ast)
+ raise unless astOffsetsList.size + astSizesList.size == offsetsList.size
+ offsetsList(ast).each_with_index {
+ | structOffset, index |
+ offsetsMap[structOffset] = offsetsList.shift
+ }
+ sizesList(ast).each_with_index {
+ | sizeof, index |
+ sizesMap[sizeof] = offsetsList.shift
+ }
+ [offsetsMap, sizesMap]
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/opt.rb b/Source/JavaScriptCore/offlineasm/opt.rb
new file mode 100644
index 000000000..3170d3ae1
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/opt.rb
@@ -0,0 +1,134 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+
+#
+# "Optimization" passes. These are used to lower the representation for
+# backends that cannot handle some of our higher-level instructions.
+#
+
+#
+# A temporary - a variable that will be allocated to a register after we're
+# done.
+#
+
+class Node
+ def replaceTemporariesWithRegisters(kind)
+ mapChildren {
+ | node |
+ node.replaceTemporariesWithRegisters(kind)
+ }
+ end
+end
+
+class Tmp < NoChildren
+ attr_reader :firstMention, :lastMention
+ attr_reader :kind
+ attr_accessor :register
+
+ def initialize(codeOrigin, kind)
+ super(codeOrigin)
+ @kind = kind
+ end
+
+ def dump
+ "$tmp#{object_id}"
+ end
+
+ def mention!(position)
+ if not @firstMention or position < @firstMention
+ @firstMention = position
+ end
+ if not @lastMention or position > @lastMention
+ @lastMention = position
+ end
+ end
+
+ def replaceTemporariesWithRegisters(kind)
+ if @kind == kind
+ raise "Did not allocate register to temporary at #{codeOriginString}" unless @register
+ @register
+ else
+ self
+ end
+ end
+
+ def address?
+ false
+ end
+
+ def label?
+ false
+ end
+
+ def immediate?
+ false
+ end
+
+ def register?
+ true
+ end
+end
+
+# Assign registers to temporaries, by finding which temporaries interfere
+# with each other. Note that this relies on temporary live ranges not crossing
+# basic block boundaries.
+
+def assignRegistersToTemporaries(list, kind, registers)
+ list.each_with_index {
+ | node, index |
+ node.filter(Tmp).uniq.each {
+ | tmp |
+ if tmp.kind == kind
+ tmp.mention! index
+ end
+ }
+ }
+
+ freeRegisters = registers.dup
+ list.each_with_index {
+ | node, index |
+ tmpList = node.filter(Tmp).uniq
+ tmpList.each {
+ | tmp |
+ if tmp.kind == kind and tmp.firstMention == index
+ raise "Could not allocate register to temporary at #{node.codeOriginString}" if freeRegisters.empty?
+ tmp.register = freeRegisters.pop
+ end
+ }
+ tmpList.each {
+ | tmp |
+ if tmp.kind == kind and tmp.lastMention == index
+ freeRegisters.push tmp.register
+ raise "Register allocation inconsistency at #{node.codeOriginString}" if freeRegisters.size > registers.size
+ end
+ }
+ }
+
+ list.map {
+ | node |
+ node.replaceTemporariesWithRegisters(kind)
+ }
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/parser.rb b/Source/JavaScriptCore/offlineasm/parser.rb
new file mode 100644
index 000000000..f0e4b0045
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/parser.rb
@@ -0,0 +1,586 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+require "instructions"
+require "registers"
+
+class Token
+ attr_reader :codeOrigin, :string
+
+ def initialize(codeOrigin, string)
+ @codeOrigin = codeOrigin
+ @string = string
+ end
+
+ def ==(other)
+ if other.is_a? Token
+ @string == other.string
+ else
+ @string == other
+ end
+ end
+
+ def =~(other)
+ @string =~ other
+ end
+
+ def to_s
+ "#{@string.inspect} at line #{codeOrigin}"
+ end
+
+ def parseError(*comment)
+ if comment.empty?
+ raise "Parse error: #{to_s}"
+ else
+ raise "Parse error: #{to_s}: #{comment[0]}"
+ end
+ end
+end
+
+#
+# The lexer. Takes a string and returns an array of tokens.
+#
+
+def lex(str)
+ result = []
+ lineNumber = 1
+ while not str.empty?
+ case str
+ when /\A\#([^\n]*)/
+ # comment, ignore
+ when /\A\n/
+ result << Token.new(lineNumber, $&)
+ lineNumber += 1
+ when /\A[a-zA-Z]([a-zA-Z0-9_]*)/
+ result << Token.new(lineNumber, $&)
+ when /\A\.([a-zA-Z0-9_]*)/
+ result << Token.new(lineNumber, $&)
+ when /\A_([a-zA-Z0-9_]*)/
+ result << Token.new(lineNumber, $&)
+ when /\A([ \t]+)/
+ # whitespace, ignore
+ when /\A0x([0-9a-fA-F]+)/
+ result << Token.new(lineNumber, $&.hex.to_s)
+ when /\A0([0-7]+)/
+ result << Token.new(lineNumber, $&.oct.to_s)
+ when /\A([0-9]+)/
+ result << Token.new(lineNumber, $&)
+ when /\A::/
+ result << Token.new(lineNumber, $&)
+ when /\A[:,\(\)\[\]=\+\-*]/
+ result << Token.new(lineNumber, $&)
+ else
+ raise "Lexer error at line number #{lineNumber}, unexpected sequence #{str[0..20].inspect}"
+ end
+ str = $~.post_match
+ end
+ result
+end
+
+#
+# Token identification.
+#
+
+def isRegister(token)
+ token =~ REGISTER_PATTERN
+end
+
+def isInstruction(token)
+ token =~ INSTRUCTION_PATTERN
+end
+
+def isKeyword(token)
+ token =~ /\A((true)|(false)|(if)|(then)|(else)|(elsif)|(end)|(and)|(or)|(not)|(macro)|(const)|(sizeof)|(error))\Z/ or
+ token =~ REGISTER_PATTERN or
+ token =~ INSTRUCTION_PATTERN
+end
+
+def isIdentifier(token)
+ token =~ /\A[a-zA-Z]([a-zA-Z0-9_]*)\Z/ and not isKeyword(token)
+end
+
+def isLabel(token)
+ token =~ /\A_([a-zA-Z0-9_]*)\Z/
+end
+
+def isLocalLabel(token)
+ token =~ /\A\.([a-zA-Z0-9_]*)\Z/
+end
+
+def isVariable(token)
+ isIdentifier(token) or isRegister(token)
+end
+
+def isInteger(token)
+ token =~ /\A[0-9]/
+end
+
+#
+# The parser. Takes an array of tokens and returns an AST. Methods
+# other than parse(tokens) are not for public consumption.
+#
+
+class Parser
+ def initialize(tokens)
+ @tokens = tokens
+ @idx = 0
+ end
+
+ def parseError(*comment)
+ if @tokens[@idx]
+ @tokens[@idx].parseError(*comment)
+ else
+ if comment.empty?
+ raise "Parse error at end of file"
+ else
+ raise "Parse error at end of file: #{comment[0]}"
+ end
+ end
+ end
+
+ def consume(regexp)
+ if regexp
+ parseError unless @tokens[@idx] =~ regexp
+ else
+ parseError unless @idx == @tokens.length
+ end
+ @idx += 1
+ end
+
+ def skipNewLine
+ while @tokens[@idx] == "\n"
+ @idx += 1
+ end
+ end
+
+ def parsePredicateAtom
+ if @tokens[@idx] == "not"
+ @idx += 1
+ parsePredicateAtom
+ elsif @tokens[@idx] == "("
+ @idx += 1
+ skipNewLine
+ result = parsePredicate
+ parseError unless @tokens[@idx] == ")"
+ @idx += 1
+ result
+ elsif @tokens[@idx] == "true"
+ result = True.instance
+ @idx += 1
+ result
+ elsif @tokens[@idx] == "false"
+ result = False.instance
+ @idx += 1
+ result
+ elsif isIdentifier @tokens[@idx]
+ result = Setting.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ @idx += 1
+ result
+ else
+ parseError
+ end
+ end
+
+ def parsePredicateAnd
+ result = parsePredicateAtom
+ while @tokens[@idx] == "and"
+ codeOrigin = @tokens[@idx].codeOrigin
+ @idx += 1
+ skipNewLine
+ right = parsePredicateAtom
+ result = And.new(codeOrigin, result, right)
+ end
+ result
+ end
+
+ def parsePredicate
+ # some examples of precedence:
+ # not a and b -> (not a) and b
+ # a and b or c -> (a and b) or c
+ # a or b and c -> a or (b and c)
+
+ result = parsePredicateAnd
+ while @tokens[@idx] == "or"
+ codeOrigin = @tokens[@idx].codeOrigin
+ @idx += 1
+ skipNewLine
+ right = parsePredicateAnd
+ result = Or.new(codeOrigin, result, right)
+ end
+ result
+ end
+
+ def parseVariable
+ if isRegister(@tokens[@idx])
+ if @tokens[@idx] =~ FPR_PATTERN
+ result = FPRegisterID.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ else
+ result = RegisterID.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ end
+ elsif isIdentifier(@tokens[@idx])
+ result = Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ else
+ parseError
+ end
+ @idx += 1
+ result
+ end
+
+ def parseAddress(offset)
+ parseError unless @tokens[@idx] == "["
+ codeOrigin = @tokens[@idx].codeOrigin
+
+ # Three possibilities:
+ # [] -> AbsoluteAddress
+ # [a] -> Address
+ # [a,b] -> BaseIndex with scale = 1
+ # [a,b,c] -> BaseIndex
+
+ @idx += 1
+ if @tokens[@idx] == "]"
+ @idx += 1
+ return AbsoluteAddress.new(codeOrigin, offset)
+ end
+ a = parseVariable
+ if @tokens[@idx] == "]"
+ result = Address.new(codeOrigin, a, offset)
+ else
+ parseError unless @tokens[@idx] == ","
+ @idx += 1
+ b = parseVariable
+ if @tokens[@idx] == "]"
+ result = BaseIndex.new(codeOrigin, a, b, 1, offset)
+ else
+ parseError unless @tokens[@idx] == ","
+ @idx += 1
+ parseError unless ["1", "2", "4", "8"].member? @tokens[@idx].string
+ c = @tokens[@idx].string.to_i
+ @idx += 1
+ parseError unless @tokens[@idx] == "]"
+ result = BaseIndex.new(codeOrigin, a, b, c, offset)
+ end
+ end
+ @idx += 1
+ result
+ end
+
+ def parseColonColon
+ skipNewLine
+ codeOrigin = @tokens[@idx].codeOrigin
+ parseError unless isIdentifier @tokens[@idx]
+ names = [@tokens[@idx].string]
+ @idx += 1
+ while @tokens[@idx] == "::"
+ @idx += 1
+ parseError unless isIdentifier @tokens[@idx]
+ names << @tokens[@idx].string
+ @idx += 1
+ end
+ raise if names.empty?
+ [codeOrigin, names]
+ end
+
+ def parseExpressionAtom
+ skipNewLine
+ if @tokens[@idx] == "-"
+ @idx += 1
+ NegImmediate.new(@tokens[@idx - 1].codeOrigin, parseExpressionAtom)
+ elsif @tokens[@idx] == "("
+ @idx += 1
+ result = parseExpression
+ parseError unless @tokens[@idx] == ")"
+ @idx += 1
+ result
+ elsif isInteger @tokens[@idx]
+ result = Immediate.new(@tokens[@idx].codeOrigin, @tokens[@idx].string.to_i)
+ @idx += 1
+ result
+ elsif isIdentifier @tokens[@idx]
+ codeOrigin, names = parseColonColon
+ if names.size > 1
+ StructOffset.forField(codeOrigin, names[0..-2].join('::'), names[-1])
+ else
+ Variable.forName(codeOrigin, names[0])
+ end
+ elsif isRegister @tokens[@idx]
+ parseVariable
+ elsif @tokens[@idx] == "sizeof"
+ @idx += 1
+ codeOrigin, names = parseColonColon
+ Sizeof.forName(codeOrigin, names.join('::'))
+ else
+ parseError
+ end
+ end
+
+ def parseExpressionMul
+ skipNewLine
+ result = parseExpressionAtom
+ while @tokens[@idx] == "*"
+ if @tokens[@idx] == "*"
+ @idx += 1
+ result = MulImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionAtom)
+ else
+ raise
+ end
+ end
+ result
+ end
+
+ def couldBeExpression
+ @tokens[@idx] == "-" or @tokens[@idx] == "sizeof" or isInteger(@tokens[@idx]) or isVariable(@tokens[@idx]) or @tokens[@idx] == "("
+ end
+
+ def parseExpression
+ skipNewLine
+ result = parseExpressionMul
+ while @tokens[@idx] == "+" or @tokens[@idx] == "-"
+ if @tokens[@idx] == "+"
+ @idx += 1
+ result = AddImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionMul)
+ elsif @tokens[@idx] == "-"
+ @idx += 1
+ result = SubImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionMul)
+ else
+ raise
+ end
+ end
+ result
+ end
+
+ def parseOperand(comment)
+ skipNewLine
+ if couldBeExpression
+ expr = parseExpression
+ if @tokens[@idx] == "["
+ parseAddress(expr)
+ else
+ expr
+ end
+ elsif @tokens[@idx] == "["
+ parseAddress(Immediate.new(@tokens[@idx].codeOrigin, 0))
+ elsif isLabel @tokens[@idx]
+ result = LabelReference.new(@tokens[@idx].codeOrigin, Label.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
+ @idx += 1
+ result
+ elsif isLocalLabel @tokens[@idx]
+ result = LocalLabelReference.new(@tokens[@idx].codeOrigin, LocalLabel.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
+ @idx += 1
+ result
+ else
+ parseError(comment)
+ end
+ end
+
+ def parseMacroVariables
+ skipNewLine
+ consume(/\A\(\Z/)
+ variables = []
+ loop {
+ skipNewLine
+ if @tokens[@idx] == ")"
+ @idx += 1
+ break
+ elsif isIdentifier(@tokens[@idx])
+ variables << Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ @idx += 1
+ skipNewLine
+ if @tokens[@idx] == ")"
+ @idx += 1
+ break
+ elsif @tokens[@idx] == ","
+ @idx += 1
+ else
+ parseError
+ end
+ else
+ parseError
+ end
+ }
+ variables
+ end
+
+ def parseSequence(final, comment)
+ firstCodeOrigin = @tokens[@idx].codeOrigin
+ list = []
+ loop {
+ if (@idx == @tokens.length and not final) or (final and @tokens[@idx] =~ final)
+ break
+ elsif @tokens[@idx] == "\n"
+ # ignore
+ @idx += 1
+ elsif @tokens[@idx] == "const"
+ @idx += 1
+ parseError unless isVariable @tokens[@idx]
+ variable = Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+ @idx += 1
+ parseError unless @tokens[@idx] == "="
+ @idx += 1
+ value = parseOperand("while inside of const #{variable.name}")
+ list << ConstDecl.new(@tokens[@idx].codeOrigin, variable, value)
+ elsif @tokens[@idx] == "error"
+ list << Error.new(@tokens[@idx].codeOrigin)
+ @idx += 1
+ elsif @tokens[@idx] == "if"
+ codeOrigin = @tokens[@idx].codeOrigin
+ @idx += 1
+ skipNewLine
+ predicate = parsePredicate
+ consume(/\A((then)|(\n))\Z/)
+ skipNewLine
+ ifThenElse = IfThenElse.new(codeOrigin, predicate, parseSequence(/\A((else)|(end)|(elsif))\Z/, "while inside of \"if #{predicate.dump}\""))
+ list << ifThenElse
+ while @tokens[@idx] == "elsif"
+ codeOrigin = @tokens[@idx].codeOrigin
+ @idx += 1
+ skipNewLine
+ predicate = parsePredicate
+ consume(/\A((then)|(\n))\Z/)
+ skipNewLine
+ elseCase = IfThenElse.new(codeOrigin, predicate, parseSequence(/\A((else)|(end)|(elsif))\Z/, "while inside of \"if #{predicate.dump}\""))
+ ifThenElse.elseCase = elseCase
+ ifThenElse = elseCase
+ end
+ if @tokens[@idx] == "else"
+ @idx += 1
+ ifThenElse.elseCase = parseSequence(/\Aend\Z/, "while inside of else case for \"if #{predicate.dump}\"")
+ @idx += 1
+ else
+ parseError unless @tokens[@idx] == "end"
+ @idx += 1
+ end
+ elsif @tokens[@idx] == "macro"
+ codeOrigin = @tokens[@idx].codeOrigin
+ @idx += 1
+ skipNewLine
+ parseError unless isIdentifier(@tokens[@idx])
+ name = @tokens[@idx].string
+ @idx += 1
+ variables = parseMacroVariables
+ body = parseSequence(/\Aend\Z/, "while inside of macro #{name}")
+ @idx += 1
+ list << Macro.new(codeOrigin, name, variables, body)
+ elsif isInstruction @tokens[@idx]
+ codeOrigin = @tokens[@idx].codeOrigin
+ name = @tokens[@idx].string
+ @idx += 1
+ if (not final and @idx == @tokens.size) or (final and @tokens[@idx] =~ final)
+ # Zero operand instruction, and it's the last one.
+ list << Instruction.new(codeOrigin, name, [])
+ break
+ elsif @tokens[@idx] == "\n"
+ # Zero operand instruction.
+ list << Instruction.new(codeOrigin, name, [])
+ @idx += 1
+ else
+ # It's definitely an instruction, and it has at least one operand.
+ operands = []
+ endOfSequence = false
+ loop {
+ operands << parseOperand("while inside of instruction #{name}")
+ if (not final and @idx == @tokens.size) or (final and @tokens[@idx] =~ final)
+ # The end of the instruction and of the sequence.
+ endOfSequence = true
+ break
+ elsif @tokens[@idx] == ","
+ # Has another operand.
+ @idx += 1
+ elsif @tokens[@idx] == "\n"
+ # The end of the instruction.
+ @idx += 1
+ break
+ else
+ parseError("Expected a comma, newline, or #{final} after #{operands.last.dump}")
+ end
+ }
+ list << Instruction.new(codeOrigin, name, operands)
+ if endOfSequence
+ break
+ end
+ end
+ elsif isIdentifier @tokens[@idx]
+ codeOrigin = @tokens[@idx].codeOrigin
+ name = @tokens[@idx].string
+ @idx += 1
+ if @tokens[@idx] == "("
+ # Macro invocation.
+ @idx += 1
+ operands = []
+ skipNewLine
+ if @tokens[@idx] == ")"
+ @idx += 1
+ else
+ loop {
+ skipNewLine
+ if @tokens[@idx] == "macro"
+ # It's a macro lambda!
+ codeOriginInner = @tokens[@idx].codeOrigin
+ @idx += 1
+ variables = parseMacroVariables
+ body = parseSequence(/\Aend\Z/, "while inside of anonymous macro passed as argument to #{name}")
+ @idx += 1
+ operands << Macro.new(codeOriginInner, nil, variables, body)
+ else
+ operands << parseOperand("while inside of macro call to #{name}")
+ end
+ skipNewLine
+ if @tokens[@idx] == ")"
+ @idx += 1
+ break
+ elsif @tokens[@idx] == ","
+ @idx += 1
+ else
+ parseError "Unexpected #{@tokens[@idx].string.inspect} while parsing invocation of macro #{name}"
+ end
+ }
+ end
+ list << MacroCall.new(codeOrigin, name, operands)
+ else
+ parseError "Expected \"(\" after #{name}"
+ end
+ elsif isLabel @tokens[@idx] or isLocalLabel @tokens[@idx]
+ codeOrigin = @tokens[@idx].codeOrigin
+ name = @tokens[@idx].string
+ @idx += 1
+ parseError unless @tokens[@idx] == ":"
+ # It's a label.
+ if isLabel name
+ list << Label.forName(codeOrigin, name)
+ else
+ list << LocalLabel.forName(codeOrigin, name)
+ end
+ @idx += 1
+ else
+ parseError "Expecting terminal #{final} #{comment}"
+ end
+ }
+ Sequence.new(firstCodeOrigin, list)
+ end
+end
+
+def parse(tokens)
+ parser = Parser.new(tokens)
+ parser.parseSequence(nil, "")
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/registers.rb b/Source/JavaScriptCore/offlineasm/registers.rb
new file mode 100644
index 000000000..75fae4192
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/registers.rb
@@ -0,0 +1,60 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+GPRS =
+ [
+ "t0",
+ "t1",
+ "t2",
+ "t3",
+ "t4",
+ "cfr",
+ "a0",
+ "a1",
+ "r0",
+ "r1",
+ "sp",
+ "lr"
+ ]
+
+FPRS =
+ [
+ "ft0",
+ "ft1",
+ "ft2",
+ "ft3",
+ "ft4",
+ "ft5",
+ "fa0",
+ "fa1",
+ "fa2",
+ "fa3",
+ "fr"
+ ]
+
+REGISTERS = GPRS + FPRS
+
+GPR_PATTERN = Regexp.new('\\A((' + GPRS.join(')|(') + '))\\Z')
+FPR_PATTERN = Regexp.new('\\A((' + FPRS.join(')|(') + '))\\Z')
+
+REGISTER_PATTERN = Regexp.new('\\A((' + REGISTERS.join(')|(') + '))\\Z')
diff --git a/Source/JavaScriptCore/offlineasm/self_hash.rb b/Source/JavaScriptCore/offlineasm/self_hash.rb
new file mode 100644
index 000000000..a7b51e112
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/self_hash.rb
@@ -0,0 +1,46 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "digest/sha1"
+require "pathname"
+
+#
+# selfHash -> SHA1 hexdigest
+#
+# Returns a hash of the offlineasm source code. This allows dependency
+# tracking for not just changes in input, but also changes in the assembler
+# itself.
+#
+
+def selfHash
+ contents = ""
+ myPath = Pathname.new(__FILE__).dirname
+ Dir.foreach(myPath) {
+ | entry |
+ if entry =~ /\.rb$/
+ contents += IO::read(myPath + entry)
+ end
+ }
+ return Digest::SHA1.hexdigest(contents)
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/settings.rb b/Source/JavaScriptCore/offlineasm/settings.rb
new file mode 100644
index 000000000..34598181c
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/settings.rb
@@ -0,0 +1,205 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+require "backends"
+require "parser"
+require "transform"
+
+#
+# computeSettingsCombinations(ast) -> settingsCombiations
+#
+# Computes an array of settings maps, where a settings map constitutes
+# a configuration for the assembly code being generated. The map
+# contains key value pairs where keys are settings names (strings) and
+# the values are booleans (true for enabled, false for disabled).
+#
+
+def computeSettingsCombinations(ast)
+ settingsCombinations = []
+
+ def settingsCombinator(settingsCombinations, mapSoFar, remaining)
+ if remaining.empty?
+ settingsCombinations << mapSoFar
+ return
+ end
+
+ newMap = mapSoFar.dup
+ newMap[remaining[0]] = true
+ settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
+
+ newMap = mapSoFar.dup
+ newMap[remaining[0]] = false
+ settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
+ end
+
+ settingsCombinator(settingsCombinations, {}, (ast.filter(Setting).uniq.collect{|v| v.name} + ["X86", "ARMv7"]).uniq)
+
+ settingsCombinations
+end
+
+#
+# forSettings(concreteSettings, ast) {
+# | concreteSettings, lowLevelAST, backend | ... }
+#
+# Determines if the settings combination is valid, and if so, calls
+# the block with the information you need to generate code.
+#
+
+def forSettings(concreteSettings, ast)
+ # Check which architectures this combinator claims to support.
+ numClaimedBackends = 0
+ selectedBackend = nil
+ BACKENDS.each {
+ | backend |
+ isSupported = concreteSettings[backend]
+ raise unless isSupported != nil
+ numClaimedBackends += if isSupported then 1 else 0 end
+ if isSupported
+ selectedBackend = backend
+ end
+ }
+
+ return if numClaimedBackends > 1
+
+ # Resolve the AST down to a low-level form (no macros or conditionals).
+ lowLevelAST = ast.resolveSettings(concreteSettings)
+
+ yield concreteSettings, lowLevelAST, selectedBackend
+end
+
+#
+# forEachValidSettingsCombination(ast) {
+# | concreteSettings, ast, backend, index | ... }
+#
+# forEachValidSettingsCombination(ast, settingsCombinations) {
+# | concreteSettings, ast, backend, index | ... }
+#
+# Executes the given block for each valid settings combination in the
+# settings map. The ast passed into the block is resolved
+# (ast.resolve) against the settings.
+#
+# The first form will call computeSettingsCombinations(ast) for you.
+#
+
+def forEachValidSettingsCombination(ast, *optionalSettingsCombinations)
+ raise if optionalSettingsCombinations.size > 1
+
+ if optionalSettingsCombinations.empty?
+ settingsCombinations = computeSettingsCombinations(ast)
+ else
+ settingsCombinations = optionalSettingsCombiations[0]
+ end
+
+ settingsCombinations.each_with_index {
+ | concreteSettings, index |
+ forSettings(concreteSettings, ast) {
+ | concreteSettings_, lowLevelAST, backend |
+ yield concreteSettings, lowLevelAST, backend, index
+ }
+ }
+end
+
+#
+# cppSettingsTest(concreteSettings)
+#
+# Returns the C++ code used to test if we are in a configuration that
+# corresponds to the given concrete settings.
+#
+
+def cppSettingsTest(concreteSettings)
+ "#if " + concreteSettings.to_a.collect{
+ | pair |
+ (if pair[1]
+ ""
+ else
+ "!"
+ end) + "OFFLINE_ASM_" + pair[0]
+ }.join(" && ")
+end
+
+#
+# isASTErroneous(ast)
+#
+# Tests to see if the AST claims that there is an error - i.e. if the
+# user's code, after settings resolution, has Error nodes.
+#
+
+def isASTErroneous(ast)
+ not ast.filter(Error).empty?
+end
+
+#
+# assertConfiguration(concreteSettings)
+#
+# Emits a check that asserts that we're using the given configuration.
+#
+
+def assertConfiguration(concreteSettings)
+ $output.puts cppSettingsTest(concreteSettings)
+ $output.puts "#else"
+ $output.puts "#error \"Configuration mismatch.\""
+ $output.puts "#endif"
+end
+
+#
+# emitCodeInConfiguration(concreteSettings, ast, backend) {
+# | concreteSettings, ast, backend | ... }
+#
+# Emits all relevant guards to see if the configuration holds and
+# calls the block if the configuration is not erroneous.
+#
+
+def emitCodeInConfiguration(concreteSettings, ast, backend)
+ $output.puts cppSettingsTest(concreteSettings)
+
+ if isASTErroneous(ast)
+ $output.puts "#error \"Invalid configuration.\""
+ elsif not WORKING_BACKENDS.include? backend
+ $output.puts "#error \"This backend is not supported yet.\""
+ else
+ yield concreteSettings, ast, backend
+ end
+
+ $output.puts "#endif"
+end
+
+#
+# emitCodeInAllConfigurations(ast) {
+# | concreteSettings, ast, backend, index | ... }
+#
+# Emits guard codes for all valid configurations, and calls the block
+# for those configurations that are valid and not erroneous.
+#
+
+def emitCodeInAllConfigurations(ast)
+ forEachValidSettingsCombination(ast) {
+ | concreteSettings, lowLevelAST, backend, index |
+ $output.puts cppSettingsTest(concreteSettings)
+ yield concreteSettings, lowLevelAST, backend, index
+ $output.puts "#endif"
+ }
+end
+
+
+
diff --git a/Source/JavaScriptCore/offlineasm/transform.rb b/Source/JavaScriptCore/offlineasm/transform.rb
new file mode 100644
index 000000000..5f5024d9e
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/transform.rb
@@ -0,0 +1,342 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+require "ast"
+
+#
+# node.resolveSettings(settings)
+#
+# Construct a new AST that does not have any IfThenElse nodes by
+# substituting concrete boolean values for each Setting.
+#
+
+class Node
+ def resolveSettings(settings)
+ mapChildren {
+ | child |
+ child.resolveSettings(settings)
+ }
+ end
+end
+
+class True
+ def resolveSettings(settings)
+ self
+ end
+end
+
+class False
+ def resolveSettings(settings)
+ self
+ end
+end
+
+class Setting
+ def resolveSettings(settings)
+ settings[@name].asNode
+ end
+end
+
+class And
+ def resolveSettings(settings)
+ (@left.resolveSettings(settings).value and @right.resolveSettings(settings).value).asNode
+ end
+end
+
+class Or
+ def resolveSettings(settings)
+ (@left.resolveSettings(settings).value or @right.resolveSettings(settings).value).asNode
+ end
+end
+
+class Not
+ def resolveSettings(settings)
+ (not @child.resolveSettings(settings).value).asNode
+ end
+end
+
+class IfThenElse
+ def resolveSettings(settings)
+ if @predicate.resolveSettings(settings).value
+ @thenCase.resolveSettings(settings)
+ else
+ @elseCase.resolveSettings(settings)
+ end
+ end
+end
+
+class Sequence
+ def resolveSettings(settings)
+ newList = []
+ @list.each {
+ | item |
+ item = item.resolveSettings(settings)
+ if item.is_a? Sequence
+ newList += item.list
+ else
+ newList << item
+ end
+ }
+ Sequence.new(codeOrigin, newList)
+ end
+end
+
+#
+# node.demacroify(macros)
+# node.substitute(mapping)
+#
+# demacroify() constructs a new AST that does not have any Macro
+# nodes, while substitute() replaces Variable nodes with the given
+# nodes in the mapping.
+#
+
+class Node
+ def demacroify(macros)
+ mapChildren {
+ | child |
+ child.demacroify(macros)
+ }
+ end
+
+ def substitute(mapping)
+ mapChildren {
+ | child |
+ child.substitute(mapping)
+ }
+ end
+
+ def substituteLabels(mapping)
+ mapChildren {
+ | child |
+ child.substituteLabels(mapping)
+ }
+ end
+end
+
+class Macro
+ def substitute(mapping)
+ myMapping = {}
+ mapping.each_pair {
+ | key, value |
+ unless @variables.include? key
+ myMapping[key] = value
+ end
+ }
+ mapChildren {
+ | child |
+ child.substitute(myMapping)
+ }
+ end
+end
+
+class Variable
+ def substitute(mapping)
+ if mapping[self]
+ mapping[self]
+ else
+ self
+ end
+ end
+end
+
+class LocalLabel
+ def substituteLabels(mapping)
+ if mapping[self]
+ mapping[self]
+ else
+ self
+ end
+ end
+end
+
+class Sequence
+ def substitute(constants)
+ newList = []
+ myConstants = constants.dup
+ @list.each {
+ | item |
+ if item.is_a? ConstDecl
+ myConstants[item.variable] = item.value.substitute(myConstants)
+ else
+ newList << item.substitute(myConstants)
+ end
+ }
+ Sequence.new(codeOrigin, newList)
+ end
+
+ def renameLabels(comment)
+ mapping = {}
+
+ @list.each {
+ | item |
+ if item.is_a? LocalLabel
+ mapping[item] = LocalLabel.unique(if comment then comment + "_" else "" end + item.cleanName)
+ end
+ }
+
+ substituteLabels(mapping)
+ end
+
+ def demacroify(macros)
+ myMacros = macros.dup
+ @list.each {
+ | item |
+ if item.is_a? Macro
+ myMacros[item.name] = item
+ end
+ }
+ newList = []
+ @list.each {
+ | item |
+ if item.is_a? Macro
+ # Ignore.
+ elsif item.is_a? MacroCall
+ mapping = {}
+ myMyMacros = myMacros.dup
+ raise "Could not find macro #{item.name} at #{item.codeOriginString}" unless myMacros[item.name]
+ raise "Argument count mismatch for call to #{item.name} at #{item.codeOriginString}" unless item.operands.size == myMacros[item.name].variables.size
+ item.operands.size.times {
+ | idx |
+ if item.operands[idx].is_a? Variable and myMacros[item.operands[idx].name]
+ myMyMacros[myMacros[item.name].variables[idx].name] = myMacros[item.operands[idx].name]
+ mapping[myMacros[item.name].variables[idx].name] = nil
+ elsif item.operands[idx].is_a? Macro
+ myMyMacros[myMacros[item.name].variables[idx].name] = item.operands[idx]
+ mapping[myMacros[item.name].variables[idx].name] = nil
+ else
+ myMyMacros[myMacros[item.name].variables[idx]] = nil
+ mapping[myMacros[item.name].variables[idx]] = item.operands[idx]
+ end
+ }
+ newList += myMacros[item.name].body.substitute(mapping).demacroify(myMyMacros).renameLabels(item.name).list
+ else
+ newList << item.demacroify(myMacros)
+ end
+ }
+ Sequence.new(codeOrigin, newList).substitute({})
+ end
+end
+
+#
+# node.resolveOffsets(offsets, sizes)
+#
+# Construct a new AST that has offset values instead of symbolic
+# offsets.
+#
+
+class Node
+ def resolveOffsets(offsets, sizes)
+ mapChildren {
+ | child |
+ child.resolveOffsets(offsets, sizes)
+ }
+ end
+end
+
+class StructOffset
+ def resolveOffsets(offsets, sizes)
+ if offsets[self]
+ Immediate.new(codeOrigin, offsets[self])
+ else
+ self
+ end
+ end
+end
+
+class Sizeof
+ def resolveOffsets(offsets, sizes)
+ if sizes[self]
+ Immediate.new(codeOrigin, sizes[self])
+ else
+ puts "Could not find #{self.inspect} in #{sizes.keys.inspect}"
+ puts "sizes = #{sizes.inspect}"
+ self
+ end
+ end
+end
+
+#
+# node.fold
+#
+# Resolve constant references and compute arithmetic expressions.
+#
+
+class Node
+ def fold
+ mapChildren {
+ | child |
+ child.fold
+ }
+ end
+end
+
+class AddImmediates
+ def fold
+ @left = @left.fold
+ @right = @right.fold
+ return self unless @left.is_a? Immediate
+ return self unless @right.is_a? Immediate
+ Immediate.new(codeOrigin, @left.value + @right.value)
+ end
+end
+
+class SubImmediates
+ def fold
+ @left = @left.fold
+ @right = @right.fold
+ return self unless @left.is_a? Immediate
+ return self unless @right.is_a? Immediate
+ Immediate.new(codeOrigin, @left.value - @right.value)
+ end
+end
+
+class MulImmediates
+ def fold
+ @left = @left.fold
+ @right = @right.fold
+ return self unless @left.is_a? Immediate
+ return self unless @right.is_a? Immediate
+ Immediate.new(codeOrigin, @left.value * @right.value)
+ end
+end
+
+class NegImmediate
+ def fold
+ @child = @child.fold
+ return self unless @child.is_a? Immediate
+ Immediate.new(codeOrigin, -@child.value)
+ end
+end
+
+#
+# node.resolveAfterSettings(offsets, sizes)
+#
+# Compile assembly against a set of offsets.
+#
+
+class Node
+ def resolve(offsets, sizes)
+ demacroify({}).resolveOffsets(offsets, sizes).fold
+ end
+end
+
diff --git a/Source/JavaScriptCore/offlineasm/x86.rb b/Source/JavaScriptCore/offlineasm/x86.rb
new file mode 100644
index 000000000..b89f2d90c
--- /dev/null
+++ b/Source/JavaScriptCore/offlineasm/x86.rb
@@ -0,0 +1,681 @@
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+class RegisterID
+ def supports8BitOnX86
+ case name
+ when "t0", "a0", "r0", "t1", "a1", "r1", "t2", "t3"
+ true
+ when "t4", "cfr"
+ false
+ else
+ raise
+ end
+ end
+
+ def x86Operand(kind)
+ case name
+ when "t0", "a0", "r0"
+ case kind
+ when :byte
+ "%al"
+ when :half
+ "%ax"
+ when :int
+ "%eax"
+ else
+ raise
+ end
+ when "t1", "a1", "r1"
+ case kind
+ when :byte
+ "%dl"
+ when :half
+ "%dx"
+ when :int
+ "%edx"
+ else
+ raise
+ end
+ when "t2"
+ case kind
+ when :byte
+ "%cl"
+ when :half
+ "%cx"
+ when :int
+ "%ecx"
+ else
+ raise
+ end
+ when "t3"
+ case kind
+ when :byte
+ "%bl"
+ when :half
+ "%bx"
+ when :int
+ "%ebx"
+ else
+ raise
+ end
+ when "t4"
+ case kind
+ when :byte
+ "%sil"
+ when :half
+ "%si"
+ when :int
+ "%esi"
+ else
+ raise
+ end
+ when "cfr"
+ case kind
+ when :byte
+ "%dil"
+ when :half
+ "%di"
+ when :int
+ "%edi"
+ else
+ raise
+ end
+ when "sp"
+ case kind
+ when :byte
+ "%spl"
+ when :half
+ "%sp"
+ when :int
+ "%esp"
+ else
+ raise
+ end
+ else
+ raise "Bad register #{name} for X86 at #{codeOriginString}"
+ end
+ end
+ def x86CallOperand(kind)
+ "*#{x86Operand(kind)}"
+ end
+end
+
+class FPRegisterID
+ def x86Operand(kind)
+ raise unless kind == :double
+ case name
+ when "ft0", "fa0", "fr"
+ "%xmm0"
+ when "ft1", "fa1"
+ "%xmm1"
+ when "ft2", "fa2"
+ "%xmm2"
+ when "ft3", "fa3"
+ "%xmm3"
+ when "ft4"
+ "%xmm4"
+ when "ft5"
+ "%xmm5"
+ else
+ raise "Bad register #{name} for X86 at #{codeOriginString}"
+ end
+ end
+ def x86CallOperand(kind)
+ "*#{x86Operand(kind)}"
+ end
+end
+
+class Immediate
+ def x86Operand(kind)
+ "$#{value}"
+ end
+ def x86CallOperand(kind)
+ "#{value}"
+ end
+end
+
+class Address
+ def supports8BitOnX86
+ true
+ end
+
+ def x86Operand(kind)
+ "#{offset.value}(#{base.x86Operand(:int)})"
+ end
+ def x86CallOperand(kind)
+ "*#{x86Operand(kind)}"
+ end
+end
+
+class BaseIndex
+ def supports8BitOnX86
+ true
+ end
+
+ def x86Operand(kind)
+ "#{offset.value}(#{base.x86Operand(:int)}, #{index.x86Operand(:int)}, #{scale})"
+ end
+
+ def x86CallOperand(kind)
+ "*#{x86operand(kind)}"
+ end
+end
+
+class AbsoluteAddress
+ def supports8BitOnX86
+ true
+ end
+
+ def x86Operand(kind)
+ "#{address.value}"
+ end
+
+ def x86CallOperand(kind)
+ "*#{address.value}"
+ end
+end
+
+class LabelReference
+ def x86CallOperand(kind)
+ asmLabel
+ end
+end
+
+class LocalLabelReference
+ def x86CallOperand(kind)
+ asmLabel
+ end
+end
+
+class Instruction
+ def x86Operands(*kinds)
+ raise unless kinds.size == operands.size
+ result = []
+ kinds.size.times {
+ | idx |
+ result << operands[idx].x86Operand(kinds[idx])
+ }
+ result.join(", ")
+ end
+
+ def x86Suffix(kind)
+ case kind
+ when :byte
+ "b"
+ when :half
+ "w"
+ when :int
+ "l"
+ when :double
+ "sd"
+ else
+ raise
+ end
+ end
+
+ def handleX86OpWithNumOperands(opcode, kind, numOperands)
+ if numOperands == 3
+ if operands[0] == operands[2]
+ $asm.puts "#{opcode} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}"
+ elsif operands[1] == operands[2]
+ $asm.puts "#{opcode} #{operands[0].x86Operand(kind)}, #{operands[2].x86Operand(kind)}"
+ else
+ $asm.puts "mov#{x86Suffix(kind)} #{operands[0].x86Operand(kind)}, #{operands[2].x86Operand(kind)}"
+ $asm.puts "#{opcode} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}"
+ end
+ else
+ $asm.puts "#{opcode} #{operands[0].x86Operand(kind)}, #{operands[1].x86Operand(kind)}"
+ end
+ end
+
+ def handleX86Op(opcode, kind)
+ handleX86OpWithNumOperands(opcode, kind, operands.size)
+ end
+
+ def handleX86Shift(opcode, kind)
+ if operands[0].is_a? Immediate or operands[0] == RegisterID.forName(nil, "t2")
+ $asm.puts "#{opcode} #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(kind)}"
+ else
+ $asm.puts "xchgl #{operands[0].x86Operand(:int)}, %ecx"
+ $asm.puts "#{opcode} %cl, #{operands[1].x86Operand(kind)}"
+ $asm.puts "xchgl #{operands[0].x86Operand(:int)}, %ecx"
+ end
+ end
+
+ def handleX86DoubleBranch(branchOpcode, mode)
+ case mode
+ when :normal
+ $asm.puts "ucomisd #{operands[1].x86Operand(:double)}, #{operands[0].x86Operand(:double)}"
+ when :reverse
+ $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
+ else
+ raise mode.inspect
+ end
+ $asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
+ end
+
+ def handleX86IntCompare(opcodeSuffix, kind)
+ if operands[0].is_a? Immediate and operands[0].value == 0 and operands[1].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
+ $asm.puts "test#{x86Suffix(kind)} #{operands[1].x86Operand(kind)}"
+ elsif operands[1].is_a? Immediate and operands[1].value == 0 and operands[0].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
+ $asm.puts "test#{x86Suffix(kind)} #{operands[0].x86Operand(kind)}"
+ else
+ $asm.puts "cmp#{x86Suffix(kind)} #{operands[1].x86Operand(kind)}, #{operands[0].x86Operand(kind)}"
+ end
+ end
+
+ def handleX86IntBranch(branchOpcode, kind)
+ handleX86IntCompare(branchOpcode[1..-1], kind)
+ $asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
+ end
+
+ def handleX86Set(setOpcode, operand)
+ if operand.supports8BitOnX86
+ $asm.puts "#{setOpcode} #{operand.x86Operand(:byte)}"
+ $asm.puts "movzbl #{operand.x86Operand(:byte)}, #{operand.x86Operand(:int)}"
+ else
+ $asm.puts "xchgl #{operand.x86Operand(:int)}, %eax"
+ $asm.puts "#{setOpcode} %al"
+ $asm.puts "movzbl %al, %eax"
+ $asm.puts "xchgl #{operand.x86Operand(:int)}, %eax"
+ end
+ end
+
+ def handleX86IntCompareSet(setOpcode, kind)
+ handleX86IntCompare(setOpcode[3..-1], kind)
+ handleX86Set(setOpcode, operands[2])
+ end
+
+ def handleX86Test(kind)
+ value = operands[0]
+ case operands.size
+ when 2
+ mask = Immediate.new(codeOrigin, -1)
+ when 3
+ mask = operands[1]
+ else
+ raise "Expected 2 or 3 operands, but got #{operands.size} at #{codeOriginString}"
+ end
+
+ if mask.is_a? Immediate and mask.value == -1
+ if value.is_a? RegisterID
+ $asm.puts "test#{x86Suffix(kind)} #{value.x86Operand(kind)}, #{value.x86Operand(kind)}"
+ else
+ $asm.puts "cmp#{x86Suffix(kind)} $0, #{value.x86Operand(kind)}"
+ end
+ else
+ $asm.puts "test#{x86Suffix(kind)} #{mask.x86Operand(kind)}, #{value.x86Operand(kind)}"
+ end
+ end
+
+ def handleX86BranchTest(branchOpcode, kind)
+ handleX86Test(kind)
+ $asm.puts "#{branchOpcode} #{operands.last.asmLabel}"
+ end
+
+ def handleX86SetTest(setOpcode, kind)
+ handleX86Test(kind)
+ handleX86Set(setOpcode, operands.last)
+ end
+
+ def handleX86OpBranch(opcode, branchOpcode, kind)
+ handleX86OpWithNumOperands(opcode, kind, operands.size - 1)
+ case operands.size
+ when 4
+ jumpTarget = operands[3]
+ when 3
+ jumpTarget = operands[2]
+ else
+ raise self.inspect
+ end
+ $asm.puts "#{branchOpcode} #{jumpTarget.asmLabel}"
+ end
+
+ def handleX86SubBranch(branchOpcode, kind)
+ if operands.size == 4 and operands[1] == operands[2]
+ $asm.puts "negl #{operands[2].x86Operand(:int)}"
+ $asm.puts "addl #{operands[0].x86Operand(:int)}, #{operands[2].x86Operand(:int)}"
+ else
+ handleX86OpWithNumOperands("sub#{x86Suffix(kind)}", kind, operands.size - 1)
+ end
+ case operands.size
+ when 4
+ jumpTarget = operands[3]
+ when 3
+ jumpTarget = operands[2]
+ else
+ raise self.inspect
+ end
+ $asm.puts "#{branchOpcode} #{jumpTarget.asmLabel}"
+ end
+
+ def lowerX86
+ $asm.comment codeOriginString
+ case opcode
+ when "addi", "addp"
+ if operands.size == 3 and operands[0].is_a? Immediate
+ raise unless operands[1].is_a? RegisterID
+ raise unless operands[2].is_a? RegisterID
+ if operands[0].value == 0
+ unless operands[1] == operands[2]
+ $asm.puts "movl #{operands[1].x86Operand(:int)}, #{operands[2].x86Operand(:int)}"
+ end
+ else
+ $asm.puts "leal #{operands[0].value}(#{operands[1].x86Operand(:int)}), #{operands[2].x86Operand(:int)}"
+ end
+ elsif operands.size == 3 and operands[0].is_a? RegisterID
+ raise unless operands[1].is_a? RegisterID
+ raise unless operands[2].is_a? RegisterID
+ $asm.puts "leal (#{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:int)}), #{operands[2].x86Operand(:int)}"
+ else
+ unless Immediate.new(nil, 0) == operands[0]
+ $asm.puts "addl #{x86Operands(:int, :int)}"
+ end
+ end
+ when "andi", "andp"
+ handleX86Op("andl", :int)
+ when "lshifti"
+ handleX86Shift("sall", :int)
+ when "muli"
+ if operands.size == 3 and operands[0].is_a? Immediate
+ $asm.puts "imull #{x86Operands(:int, :int, :int)}"
+ else
+ # FIXME: could do some peephole in case the left operand is immediate and it's
+ # a power of two.
+ handleX86Op("imull", :int)
+ end
+ when "negi"
+ $asm.puts "negl #{x86Operands(:int)}"
+ when "noti"
+ $asm.puts "notl #{x86Operands(:int)}"
+ when "ori", "orp"
+ handleX86Op("orl", :int)
+ when "rshifti"
+ handleX86Shift("sarl", :int)
+ when "urshifti"
+ handleX86Shift("shrl", :int)
+ when "subi", "subp"
+ if operands.size == 3 and operands[1] == operands[2]
+ $asm.puts "negl #{operands[2].x86Operand(:int)}"
+ $asm.puts "addl #{operands[0].x86Operand(:int)}, #{operands[2].x86Operand(:int)}"
+ else
+ handleX86Op("subl", :int)
+ end
+ when "xori", "xorp"
+ handleX86Op("xorl", :int)
+ when "loadi", "storei", "loadp", "storep"
+ $asm.puts "movl #{x86Operands(:int, :int)}"
+ when "loadb"
+ $asm.puts "movzbl #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:int)}"
+ when "loadbs"
+ $asm.puts "movsbl #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:int)}"
+ when "loadh"
+ $asm.puts "movzwl #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:int)}"
+ when "loadhs"
+ $asm.puts "movswl #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:int)}"
+ when "storeb"
+ $asm.puts "movb #{x86Operands(:byte, :byte)}"
+ when "loadd", "moved", "stored"
+ $asm.puts "movsd #{x86Operands(:double, :double)}"
+ when "addd"
+ $asm.puts "addsd #{x86Operands(:double, :double)}"
+ when "divd"
+ $asm.puts "divsd #{x86Operands(:double, :double)}"
+ when "subd"
+ $asm.puts "subsd #{x86Operands(:double, :double)}"
+ when "muld"
+ $asm.puts "mulsd #{x86Operands(:double, :double)}"
+ when "sqrtd"
+ $asm.puts "sqrtsd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
+ when "ci2d"
+ $asm.puts "cvtsi2sd #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:double)}"
+ when "bdeq"
+ isUnordered = LocalLabel.unique("bdeq")
+ $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
+ $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}"
+ $asm.puts "je #{LabelReference.new(codeOrigin, operands[2]).asmLabel}"
+ isUnordered.lower("X86")
+ when "bdneq"
+ handleX86DoubleBranch("jne", :normal)
+ when "bdgt"
+ handleX86DoubleBranch("ja", :normal)
+ when "bdgteq"
+ handleX86DoubleBranch("jae", :normal)
+ when "bdlt"
+ handleX86DoubleBranch("ja", :reverse)
+ when "bdlteq"
+ handleX86DoubleBranch("jae", :reverse)
+ when "bdequn"
+ handleX86DoubleBranch("je", :normal)
+ when "bdnequn"
+ isUnordered = LocalLabel.unique("bdnequn")
+ isEqual = LocalLabel.unique("bdnequn")
+ $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
+ $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}"
+ $asm.puts "je #{LabelReference.new(codeOrigin, isEqual).asmLabel}"
+ isUnordered.lower("X86")
+ $asm.puts "jmp #{operands[2].asmLabel}"
+ isEqual.lower("X86")
+ when "bdgtun"
+ handleX86DoubleBranch("jb", :reverse)
+ when "bdgtequn"
+ handleX86DoubleBranch("jbe", :reverse)
+ when "bdltun"
+ handleX86DoubleBranch("jb", :normal)
+ when "bdltequn"
+ handleX86DoubleBranch("jbe", :normal)
+ when "btd2i"
+ $asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
+ $asm.puts "cmpl $0x80000000 #{operands[1].x86Operand(:int)}"
+ $asm.puts "je #{operands[2].asmLabel}"
+ when "td2i"
+ $asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
+ when "bcd2i"
+ $asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
+ $asm.puts "testl #{operands[1].x86Operand(:int)}, #{operands[1].x86Operand(:int)}"
+ $asm.puts "je #{operands[2].asmLabel}"
+ $asm.puts "cvtsi2sd #{operands[1].x86Operand(:int)}, %xmm7"
+ $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, %xmm7"
+ $asm.puts "jp #{operands[2].asmLabel}"
+ $asm.puts "jne #{operands[2].asmLabel}"
+ when "movdz"
+ $asm.puts "xorpd #{operands[0].x86Operand(:double)}, #{operands[0].x86Operand(:double)}"
+ when "pop"
+ $asm.puts "pop #{operands[0].x86Operand(:int)}"
+ when "push"
+ $asm.puts "push #{operands[0].x86Operand(:int)}"
+ when "move", "sxi2p", "zxi2p"
+ if Immediate.new(nil, 0) == operands[0] and operands[1].is_a? RegisterID
+ $asm.puts "xorl #{operands[1].x86Operand(:int)}, #{operands[1].x86Operand(:int)}"
+ elsif operands[0] != operands[1]
+ $asm.puts "movl #{x86Operands(:int, :int)}"
+ end
+ when "nop"
+ $asm.puts "nop"
+ when "bieq", "bpeq"
+ handleX86IntBranch("je", :int)
+ when "bineq", "bpneq"
+ handleX86IntBranch("jne", :int)
+ when "bia", "bpa"
+ handleX86IntBranch("ja", :int)
+ when "biaeq", "bpaeq"
+ handleX86IntBranch("jae", :int)
+ when "bib", "bpb"
+ handleX86IntBranch("jb", :int)
+ when "bibeq", "bpbeq"
+ handleX86IntBranch("jbe", :int)
+ when "bigt", "bpgt"
+ handleX86IntBranch("jg", :int)
+ when "bigteq", "bpgteq"
+ handleX86IntBranch("jge", :int)
+ when "bilt", "bplt"
+ handleX86IntBranch("jl", :int)
+ when "bilteq", "bplteq"
+ handleX86IntBranch("jle", :int)
+ when "bbeq"
+ handleX86IntBranch("je", :byte)
+ when "bbneq"
+ handleX86IntBranch("jne", :byte)
+ when "bba"
+ handleX86IntBranch("ja", :byte)
+ when "bbaeq"
+ handleX86IntBranch("jae", :byte)
+ when "bbb"
+ handleX86IntBranch("jb", :byte)
+ when "bbbeq"
+ handleX86IntBranch("jbe", :byte)
+ when "bbgt"
+ handleX86IntBranch("jg", :byte)
+ when "bbgteq"
+ handleX86IntBranch("jge", :byte)
+ when "bblt"
+ handleX86IntBranch("jl", :byte)
+ when "bblteq"
+ handleX86IntBranch("jlteq", :byte)
+ when "btio", "btpo"
+ handleX86BranchTest("jo", :int)
+ when "btis", "btps"
+ handleX86BranchTest("js", :int)
+ when "btiz", "btpz"
+ handleX86BranchTest("jz", :int)
+ when "btinz", "btpnz"
+ handleX86BranchTest("jnz", :int)
+ when "btbo"
+ handleX86BranchTest("jo", :byte)
+ when "btbs"
+ handleX86BranchTest("js", :byte)
+ when "btbz"
+ handleX86BranchTest("jz", :byte)
+ when "btbnz"
+ handleX86BranchTest("jnz", :byte)
+ when "jmp"
+ $asm.puts "jmp #{operands[0].x86CallOperand(:int)}"
+ when "baddio", "baddpo"
+ handleX86OpBranch("addl", "jo", :int)
+ when "baddis", "baddps"
+ handleX86OpBranch("addl", "js", :int)
+ when "baddiz", "baddpz"
+ handleX86OpBranch("addl", "jz", :int)
+ when "baddinz", "baddpnz"
+ handleX86OpBranch("addl", "jnz", :int)
+ when "bsubio"
+ handleX86SubBranch("jo", :int)
+ when "bsubis"
+ handleX86SubBranch("js", :int)
+ when "bsubiz"
+ handleX86SubBranch("jz", :int)
+ when "bsubinz"
+ handleX86SubBranch("jnz", :int)
+ when "bmulio"
+ handleX86OpBranch("imull", "jo", :int)
+ when "bmulis"
+ handleX86OpBranch("imull", "js", :int)
+ when "bmuliz"
+ handleX86OpBranch("imull", "jz", :int)
+ when "bmulinz"
+ handleX86OpBranch("imull", "jnz", :int)
+ when "borio"
+ handleX86OpBranch("orl", "jo", :int)
+ when "boris"
+ handleX86OpBranch("orl", "js", :int)
+ when "boriz"
+ handleX86OpBranch("orl", "jz", :int)
+ when "borinz"
+ handleX86OpBranch("orl", "jnz", :int)
+ when "break"
+ $asm.puts "int $3"
+ when "call"
+ $asm.puts "call #{operands[0].x86CallOperand(:int)}"
+ when "ret"
+ $asm.puts "ret"
+ when "cieq", "cpeq"
+ handleX86IntCompareSet("sete", :int)
+ when "cineq", "cpneq"
+ handleX86IntCompareSet("setne", :int)
+ when "cia", "cpa"
+ handleX86IntCompareSet("seta", :int)
+ when "ciaeq", "cpaeq"
+ handleX86IntCompareSet("setae", :int)
+ when "cib", "cpb"
+ handleX86IntCompareSet("setb", :int)
+ when "cibeq", "cpbeq"
+ handleX86IntCompareSet("setbe", :int)
+ when "cigt", "cpgt"
+ handleX86IntCompareSet("setg", :int)
+ when "cigteq", "cpgteq"
+ handleX86IntCompareSet("setge", :int)
+ when "cilt", "cplt"
+ handleX86IntCompareSet("setl", :int)
+ when "cilteq", "cplteq"
+ handleX86IntCompareSet("setle", :int)
+ when "tio"
+ handleX86SetTest("seto", :int)
+ when "tis"
+ handleX86SetTest("sets", :int)
+ when "tiz"
+ handleX86SetTest("setz", :int)
+ when "tinz"
+ handleX86SetTest("setnz", :int)
+ when "tbo"
+ handleX86SetTest("seto", :byte)
+ when "tbs"
+ handleX86SetTest("sets", :byte)
+ when "tbz"
+ handleX86SetTest("setz", :byte)
+ when "tbnz"
+ handleX86SetTest("setnz", :byte)
+ when "peek"
+ $asm.puts "movl #{operands[0].value * 4}(%esp), #{operands[1].x86Operand(:int)}"
+ when "poke"
+ $asm.puts "movl #{operands[0].x86Operand(:int)}, #{operands[1].value * 4}(%esp)"
+ when "cdqi"
+ $asm.puts "cdq"
+ when "idivi"
+ $asm.puts "idivl #{operands[0].x86Operand(:int)}"
+ when "fii2d"
+ $asm.puts "movd #{operands[0].x86Operand(:int)}, #{operands[2].x86Operand(:double)}"
+ $asm.puts "movd #{operands[1].x86Operand(:int)}, %xmm7"
+ $asm.puts "psllq $32, %xmm7"
+ $asm.puts "por %xmm7, #{operands[2].x86Operand(:double)}"
+ when "fd2ii"
+ $asm.puts "movd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
+ $asm.puts "movsd #{operands[0].x86Operand(:double)}, %xmm7"
+ $asm.puts "psrlq $32, %xmm7"
+ $asm.puts "movsd %xmm7, #{operands[2].x86Operand(:int)}"
+ when "bo"
+ $asm.puts "jo #{operands[0].asmLabel}"
+ when "bs"
+ $asm.puts "js #{operands[0].asmLabel}"
+ when "bz"
+ $asm.puts "jz #{operands[0].asmLabel}"
+ when "bnz"
+ $asm.puts "jnz #{operands[0].asmLabel}"
+ when "leai", "leap"
+ $asm.puts "leal #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:int)}"
+ else
+ raise "Bad opcode: #{opcode}"
+ end
+ end
+end
+