summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/b3/air/opcode_generator.rb
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/b3/air/opcode_generator.rb
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/b3/air/opcode_generator.rb')
-rw-r--r--Source/JavaScriptCore/b3/air/opcode_generator.rb1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/b3/air/opcode_generator.rb b/Source/JavaScriptCore/b3/air/opcode_generator.rb
new file mode 100644
index 000000000..d14240515
--- /dev/null
+++ b/Source/JavaScriptCore/b3/air/opcode_generator.rb
@@ -0,0 +1,1228 @@
+#!/usr/bin/env ruby
+
+# Copyright (C) 2015-2016 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 "pathname"
+
+class Opcode
+ attr_reader :name, :custom, :overloads
+ attr_reader :attributes
+
+ def initialize(name, custom)
+ @name = name
+ @custom = custom
+ @attributes = {}
+ unless custom
+ @overloads = []
+ end
+ end
+
+ def masmName
+ name[0].downcase + name[1..-1]
+ end
+end
+
+class Arg
+ attr_reader :role, :type, :width
+
+ def initialize(role, type, width)
+ @role = role
+ @type = type
+ @width = width
+ end
+
+ def widthCode
+ if width == "Ptr"
+ "Arg::pointerWidth()"
+ else
+ "Arg::Width#{width}"
+ end
+ end
+end
+
+class Overload
+ attr_reader :signature, :forms
+
+ def initialize(signature, forms)
+ @signature = signature
+ @forms = forms
+ end
+end
+
+class Kind
+ attr_reader :name
+ attr_accessor :custom
+
+ def initialize(name)
+ @name = name
+ @custom = false
+ end
+
+ def ==(other)
+ if other.is_a? String
+ @name == other
+ else
+ @name == other.name and @custom == other.custom
+ end
+ end
+
+ def Kind.argKinds(kind)
+ if kind == "Addr"
+ ["Addr", "Stack", "CallArg"]
+ else
+ [kind]
+ end
+ end
+
+ def argKinds
+ Kind.argKinds(kind)
+ end
+end
+
+class Form
+ attr_reader :kinds, :altName, :archs
+
+ def initialize(kinds, altName, archs)
+ @kinds = kinds
+ @altName = altName
+ @archs = archs
+ end
+end
+
+class Origin
+ attr_reader :fileName, :lineNumber
+
+ def initialize(fileName, lineNumber)
+ @fileName = fileName
+ @lineNumber = lineNumber
+ end
+
+ def to_s
+ "#{fileName}:#{lineNumber}"
+ end
+end
+
+class Token
+ attr_reader :origin, :string
+
+ def initialize(origin, string)
+ @origin = origin
+ @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 #{origin}"
+ end
+
+ def parseError(*comment)
+ if comment.empty?
+ raise "Parse error: #{to_s}"
+ else
+ raise "Parse error: #{to_s}: #{comment[0]}"
+ end
+ end
+end
+
+def lex(str, fileName)
+ fileName = Pathname.new(fileName)
+ result = []
+ lineNumber = 1
+ while not str.empty?
+ case str
+ when /\A\#([^\n]*)/
+ # comment, ignore
+ when /\A\n/
+ # newline, ignore
+ lineNumber += 1
+ when /\A([a-zA-Z0-9_]+)/
+ result << Token.new(Origin.new(fileName, lineNumber), $&)
+ when /\A([ \t\r]+)/
+ # whitespace, ignore
+ when /\A[,:*\/]/
+ result << Token.new(Origin.new(fileName, lineNumber), $&)
+ else
+ raise "Lexer error at #{Origin.new(fileName, lineNumber).to_s}, unexpected sequence #{str[0..20].inspect}"
+ end
+ str = $~.post_match
+ end
+ result
+end
+
+def isRole(token)
+ token =~ /\A((U)|(D)|(UD)|(ZD)|(UZD)|(UA)|(S))\Z/
+end
+
+def isGF(token)
+ token =~ /\A((G)|(F))\Z/
+end
+
+def isKind(token)
+ token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(Addr)|(Index)|(RelCond)|(ResCond)|(DoubleCond))\Z/
+end
+
+def isArch(token)
+ token =~ /\A((x86)|(x86_32)|(x86_64)|(arm)|(armv7)|(arm64)|(32)|(64))\Z/
+end
+
+def isWidth(token)
+ token =~ /\A((8)|(16)|(32)|(64)|(Ptr))\Z/
+end
+
+def isKeyword(token)
+ isRole(token) or isGF(token) or isKind(token) or isArch(token) or isWidth(token) or
+ token == "custom" or token == "as"
+end
+
+def isIdentifier(token)
+ token =~ /\A([a-zA-Z0-9_]+)\Z/ and not isKeyword(token)
+end
+
+class Parser
+ def initialize(data, fileName)
+ @tokens = lex(data, fileName)
+ @idx = 0
+ end
+
+ def token
+ @tokens[@idx]
+ end
+
+ def advance
+ @idx += 1
+ end
+
+ def parseError(*comment)
+ if token
+ token.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(string)
+ parseError("Expected #{string}") unless token == string
+ advance
+ end
+
+ def consumeIdentifier
+ result = token.string
+ parseError("Expected identifier") unless isIdentifier(result)
+ advance
+ result
+ end
+
+ def consumeRole
+ result = token.string
+ parseError("Expected role (U, D, UD, ZD, UZD, UA, or S)") unless isRole(result)
+ advance
+ result
+ end
+
+ def consumeType
+ result = token.string
+ parseError("Expected type (G or F)") unless isGF(result)
+ advance
+ result
+ end
+
+ def consumeKind
+ result = token.string
+ parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, Tmp, Addr, Index, RelCond, ResCond, or DoubleCond)") unless isKind(result)
+ advance
+ result
+ end
+
+ def consumeWidth
+ result = token.string
+ parseError("Expected width (8, 16, 32, or 64)") unless isWidth(result)
+ advance
+ result
+ end
+
+ def parseArchs
+ return nil unless isArch(token)
+
+ result = []
+ while isArch(token)
+ case token.string
+ when "x86"
+ result << "X86"
+ result << "X86_64"
+ when "x86_32"
+ result << "X86"
+ when "x86_64"
+ result << "X86_64"
+ when "arm"
+ result << "ARMv7"
+ result << "ARM64"
+ when "armv7"
+ result << "ARMv7"
+ when "arm64"
+ result << "ARM64"
+ when "32"
+ result << "X86"
+ result << "ARMv7"
+ when "64"
+ result << "X86_64"
+ result << "ARM64"
+ else
+ raise token.string
+ end
+ advance
+ end
+
+ consume(":")
+ @lastArchs = result
+ end
+
+ def consumeArchs
+ result = @lastArchs
+ @lastArchs = nil
+ result
+ end
+
+ def parseAndConsumeArchs
+ parseArchs
+ consumeArchs
+ end
+
+ def intersectArchs(left, right)
+ return left unless right
+ return right unless left
+
+ left.select {
+ | value |
+ right.find {
+ | otherValue |
+ value == otherValue
+ }
+ }
+ end
+
+ def parse
+ result = {}
+
+ loop {
+ break if @idx >= @tokens.length
+
+ if token == "custom"
+ consume("custom")
+ opcodeName = consumeIdentifier
+
+ parseError("Cannot overload a custom opcode") if result[opcodeName]
+
+ result[opcodeName] = Opcode.new(opcodeName, true)
+ else
+ opcodeArchs = parseAndConsumeArchs
+
+ opcodeName = consumeIdentifier
+
+ if result[opcodeName]
+ opcode = result[opcodeName]
+ parseError("Cannot overload a custom opcode") if opcode.custom
+ else
+ opcode = Opcode.new(opcodeName, false)
+ result[opcodeName] = opcode
+ end
+
+ signature = []
+ forms = []
+
+ if isRole(token)
+ loop {
+ role = consumeRole
+ consume(":")
+ type = consumeType
+ consume(":")
+ width = consumeWidth
+
+ signature << Arg.new(role, type, width)
+
+ break unless token == ","
+ consume(",")
+ }
+ end
+
+ while token == "/"
+ consume("/")
+ case token.string
+ when "branch"
+ opcode.attributes[:branch] = true
+ opcode.attributes[:terminal] = true
+ when "terminal"
+ opcode.attributes[:terminal] = true
+ when "effects"
+ opcode.attributes[:effects] = true
+ when "return"
+ opcode.attributes[:return] = true
+ opcode.attributes[:terminal] = true
+ else
+ parseError("Bad / directive")
+ end
+ advance
+ end
+
+ parseArchs
+ if isKind(token)
+ loop {
+ kinds = []
+ altName = nil
+ formArchs = consumeArchs
+ loop {
+ kinds << Kind.new(consumeKind)
+
+ if token == "*"
+ parseError("Can only apply * to Tmp") unless kinds[-1].name == "Tmp"
+ kinds[-1].custom = true
+ consume("*")
+ end
+
+ break unless token == ","
+ consume(",")
+ }
+
+ if token == "as"
+ consume("as")
+ altName = consumeIdentifier
+ end
+
+ parseError("Form has wrong number of arguments for overload") unless kinds.length == signature.length
+ kinds.each_with_index {
+ | kind, index |
+ if kind.name == "Imm" or kind.name == "BigImm" or kind.name == "BitImm" or kind.name == "BitImm64"
+ if signature[index].role != "U"
+ parseError("Form has an immediate for a non-use argument")
+ end
+ if signature[index].type != "G"
+ parseError("Form has an immediate for a non-general-purpose argument")
+ end
+ end
+ }
+ forms << Form.new(kinds, altName, intersectArchs(opcodeArchs, formArchs))
+
+ parseArchs
+ break unless isKind(token)
+ }
+ end
+
+ if signature.length == 0
+ raise unless forms.length == 0
+ forms << Form.new([], nil, opcodeArchs)
+ end
+
+ opcode.overloads << Overload.new(signature, forms)
+ end
+ }
+
+ result
+ end
+end
+
+$fileName = ARGV[0]
+
+parser = Parser.new(IO::read($fileName), $fileName)
+$opcodes = parser.parse
+
+def writeH(filename)
+ File.open("Air#{filename}.h", "w") {
+ | outp |
+
+ outp.puts "// Generated by opcode_generator.rb from #{$fileName} -- do not edit!"
+
+ outp.puts "#ifndef Air#{filename}_h"
+ outp.puts "#define Air#{filename}_h"
+
+ yield outp
+
+ outp.puts "#endif // Air#{filename}_h"
+ }
+end
+
+writeH("Opcode") {
+ | outp |
+ outp.puts "namespace JSC { namespace B3 { namespace Air {"
+ outp.puts "enum Opcode : int16_t {"
+ $opcodes.keys.sort.each {
+ | opcode |
+ outp.puts " #{opcode},"
+ }
+ outp.puts "};"
+
+ outp.puts "static const unsigned numOpcodes = #{$opcodes.keys.size};"
+ outp.puts "} } } // namespace JSC::B3::Air"
+
+ outp.puts "namespace WTF {"
+ outp.puts "class PrintStream;"
+ outp.puts "JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::B3::Air::Opcode);"
+ outp.puts "} // namespace WTF"
+}
+
+# From here on, we don't try to emit properly indented code, since we're using a recursive pattern
+# matcher.
+
+def matchForms(outp, speed, forms, columnIndex, columnGetter, filter, callback)
+ return if forms.length == 0
+
+ if filter[forms]
+ return
+ end
+
+ if columnIndex >= forms[0].kinds.length
+ raise "Did not reduce to one form: #{forms.inspect}" unless forms.length == 1
+ callback[forms[0]]
+ outp.puts "break;"
+ return
+ end
+
+ groups = {}
+ forms.each {
+ | form |
+ kind = form.kinds[columnIndex].name
+ if groups[kind]
+ groups[kind] << form
+ else
+ groups[kind] = [form]
+ end
+ }
+
+ if speed == :fast and groups.length == 1
+ matchForms(outp, speed, forms, columnIndex + 1, columnGetter, filter, callback)
+ return
+ end
+
+ outp.puts "switch (#{columnGetter[columnIndex]}) {"
+ groups.each_pair {
+ | key, value |
+ outp.puts "#if USE(JSVALUE64)" if key == "BigImm" or key == "BitImm64"
+ Kind.argKinds(key).each {
+ | argKind |
+ outp.puts "case Arg::#{argKind}:"
+ }
+ matchForms(outp, speed, value, columnIndex + 1, columnGetter, filter, callback)
+ outp.puts "break;"
+ outp.puts "#endif // USE(JSVALUE64)" if key == "BigImm" or key == "BitImm64"
+ }
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+end
+
+def matchInstOverload(outp, speed, inst)
+ outp.puts "switch (#{inst}->kind.opcode) {"
+ $opcodes.values.each {
+ | opcode |
+ outp.puts "case #{opcode.name}:"
+ if opcode.custom
+ yield opcode, nil
+ else
+ needOverloadSwitch = ((opcode.overloads.size != 1) or speed == :safe)
+ outp.puts "switch (#{inst}->args.size()) {" if needOverloadSwitch
+ opcode.overloads.each {
+ | overload |
+ outp.puts "case #{overload.signature.length}:" if needOverloadSwitch
+ yield opcode, overload
+ outp.puts "break;" if needOverloadSwitch
+ }
+ if needOverloadSwitch
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+ end
+ end
+ outp.puts "break;"
+ }
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+end
+
+def matchInstOverloadForm(outp, speed, inst)
+ matchInstOverload(outp, speed, inst) {
+ | opcode, overload |
+ if opcode.custom
+ yield opcode, nil, nil
+ else
+ columnGetter = proc {
+ | columnIndex |
+ "#{inst}->args[#{columnIndex}].kind()"
+ }
+ filter = proc { false }
+ callback = proc {
+ | form |
+ yield opcode, overload, form
+ }
+ matchForms(outp, speed, overload.forms, 0, columnGetter, filter, callback)
+ end
+ }
+end
+
+def beginArchs(outp, archs)
+ return unless archs
+ if archs.empty?
+ outp.puts "#if 0"
+ return
+ end
+ outp.puts("#if " + archs.map {
+ | arch |
+ "CPU(#{arch})"
+ }.join(" || "))
+end
+
+def endArchs(outp, archs)
+ return unless archs
+ outp.puts "#endif"
+end
+
+writeH("OpcodeUtils") {
+ | outp |
+ outp.puts "#include \"AirCustom.h\""
+ outp.puts "#include \"AirInst.h\""
+ outp.puts "namespace JSC { namespace B3 { namespace Air {"
+
+ outp.puts "inline bool opgenHiddenTruth() { return true; }"
+ outp.puts "template<typename T>"
+ outp.puts "inline T* opgenHiddenPtrIdentity(T* pointer) { return pointer; }"
+ outp.puts "#define OPGEN_RETURN(value) do {\\"
+ outp.puts " if (opgenHiddenTruth())\\"
+ outp.puts " return value;\\"
+ outp.puts "} while (false)"
+
+ outp.puts "template<typename Functor>"
+ outp.puts "void Inst::forEachArg(const Functor& functor)"
+ outp.puts "{"
+ matchInstOverload(outp, :fast, "this") {
+ | opcode, overload |
+ if opcode.custom
+ outp.puts "#{opcode.name}Custom::forEachArg(*this, functor);"
+ else
+ overload.signature.each_with_index {
+ | arg, index |
+
+ role = nil
+ case arg.role
+ when "U"
+ role = "Use"
+ when "D"
+ role = "Def"
+ when "ZD"
+ role = "ZDef"
+ when "UD"
+ role = "UseDef"
+ when "UZD"
+ role = "UseZDef"
+ when "UA"
+ role = "UseAddr"
+ when "S"
+ role = "Scratch"
+ else
+ raise
+ end
+
+ outp.puts "functor(args[#{index}], Arg::#{role}, Arg::#{arg.type}P, #{arg.widthCode});"
+ }
+ end
+ }
+ outp.puts "}"
+
+ outp.puts "template<typename... Arguments>"
+ outp.puts "ALWAYS_INLINE bool isValidForm(Opcode opcode, Arguments... arguments)"
+ outp.puts "{"
+ outp.puts "Arg::Kind kinds[sizeof...(Arguments)] = { arguments... };"
+ outp.puts "switch (opcode) {"
+ $opcodes.values.each {
+ | opcode |
+ outp.puts "case #{opcode.name}:"
+ if opcode.custom
+ outp.puts "OPGEN_RETURN(#{opcode.name}Custom::isValidFormStatic(arguments...));"
+ else
+ outp.puts "switch (sizeof...(Arguments)) {"
+ opcode.overloads.each {
+ | overload |
+ outp.puts "case #{overload.signature.length}:"
+ columnGetter = proc { | columnIndex | "opgenHiddenPtrIdentity(kinds)[#{columnIndex}]" }
+ filter = proc { false }
+ callback = proc {
+ | form |
+ # This conservatively says that Stack is not a valid form for UseAddr,
+ # because it's only valid if it's not a spill slot. This is consistent with
+ # isValidForm() being conservative and it also happens to be practical since
+ # we don't really use isValidForm for deciding when Stack is safe.
+ overload.signature.length.times {
+ | index |
+ if overload.signature[index].role == "UA"
+ outp.puts "if (opgenHiddenPtrIdentity(kinds)[#{index}] == Arg::Stack)"
+ outp.puts " return false;"
+ end
+ }
+
+ notCustom = (not form.kinds.detect { | kind | kind.custom })
+ if notCustom
+ beginArchs(outp, form.archs)
+ outp.puts "OPGEN_RETURN(true);"
+ endArchs(outp, form.archs)
+ end
+ }
+ matchForms(outp, :safe, overload.forms, 0, columnGetter, filter, callback)
+ outp.puts "break;"
+ }
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+ end
+ outp.puts "break;"
+ }
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+ outp.puts "return false; "
+ outp.puts "}"
+
+ outp.puts "inline bool isDefinitelyTerminal(Opcode opcode)"
+ outp.puts "{"
+ outp.puts "switch (opcode) {"
+ didFindTerminals = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:terminal]
+ outp.puts "case #{opcode.name}:"
+ didFindTerminals = true
+ end
+ }
+ if didFindTerminals
+ outp.puts "return true;"
+ end
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "inline bool isReturn(Opcode opcode)"
+ outp.puts "{"
+ outp.puts "switch (opcode) {"
+ didFindReturns = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:return]
+ outp.puts "case #{opcode.name}:"
+ didFindReturns = true
+ end
+ }
+ if didFindReturns
+ outp.puts "return true;"
+ end
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "} } } // namespace JSC::B3::Air"
+}
+
+writeH("OpcodeGenerated") {
+ | outp |
+ outp.puts "#include \"AirInstInlines.h\""
+ outp.puts "#include \"wtf/PrintStream.h\""
+ outp.puts "namespace WTF {"
+ outp.puts "using namespace JSC::B3::Air;"
+ outp.puts "void printInternal(PrintStream& out, Opcode opcode)"
+ outp.puts "{"
+ outp.puts " switch (opcode) {"
+ $opcodes.keys.each {
+ | opcode |
+ outp.puts " case #{opcode}:"
+ outp.puts " out.print(\"#{opcode}\");"
+ outp.puts " return;"
+ }
+ outp.puts " }"
+ outp.puts " RELEASE_ASSERT_NOT_REACHED();"
+ outp.puts "}"
+ outp.puts "} // namespace WTF"
+ outp.puts "namespace JSC { namespace B3 { namespace Air {"
+ outp.puts "bool Inst::isValidForm()"
+ outp.puts "{"
+ matchInstOverloadForm(outp, :safe, "this") {
+ | opcode, overload, form |
+ if opcode.custom
+ outp.puts "OPGEN_RETURN(#{opcode.name}Custom::isValidForm(*this));"
+ else
+ beginArchs(outp, form.archs)
+ needsMoreValidation = false
+ overload.signature.length.times {
+ | index |
+ arg = overload.signature[index]
+ kind = form.kinds[index]
+ needsMoreValidation |= kind.custom
+
+ # Some kinds of Args reqire additional validation.
+ case kind.name
+ when "Tmp"
+ outp.puts "if (!args[#{index}].tmp().is#{arg.type}P())"
+ outp.puts "OPGEN_RETURN(false);"
+ when "Imm"
+ outp.puts "if (!Arg::isValidImmForm(args[#{index}].value()))"
+ outp.puts "OPGEN_RETURN(false);"
+ when "BitImm"
+ outp.puts "if (!Arg::isValidBitImmForm(args[#{index}].value()))"
+ outp.puts "OPGEN_RETURN(false);"
+ when "BitImm64"
+ outp.puts "if (!Arg::isValidBitImm64Form(args[#{index}].value()))"
+ outp.puts "OPGEN_RETURN(false);"
+ when "Addr"
+ if arg.role == "UA"
+ outp.puts "if (args[#{index}].isStack() && args[#{index}].stackSlot()->isSpill())"
+ outp.puts "OPGEN_RETURN(false);"
+ end
+
+ outp.puts "if (!Arg::isValidAddrForm(args[#{index}].offset()))"
+ outp.puts "OPGEN_RETURN(false);"
+ when "Index"
+ outp.puts "if (!Arg::isValidIndexForm(args[#{index}].scale(), args[#{index}].offset(), #{arg.widthCode}))"
+ outp.puts "OPGEN_RETURN(false);"
+ when "BigImm"
+ when "RelCond"
+ when "ResCond"
+ when "DoubleCond"
+ else
+ raise "Unexpected kind: #{kind.name}"
+ end
+ }
+ if needsMoreValidation
+ outp.puts "if (!is#{opcode.name}Valid(*this))"
+ outp.puts "OPGEN_RETURN(false);"
+ end
+ outp.puts "OPGEN_RETURN(true);"
+ endArchs(outp, form.archs)
+ end
+ }
+ outp.puts "return false;"
+ outp.puts "}"
+
+ outp.puts "bool Inst::admitsStack(unsigned argIndex)"
+ outp.puts "{"
+ outp.puts "switch (kind.opcode) {"
+ $opcodes.values.each {
+ | opcode |
+ outp.puts "case #{opcode.name}:"
+
+ if opcode.custom
+ outp.puts "OPGEN_RETURN(#{opcode.name}Custom::admitsStack(*this, argIndex));"
+ else
+ # Switch on the argIndex.
+ outp.puts "switch (argIndex) {"
+
+ numArgs = opcode.overloads.map {
+ | overload |
+ overload.signature.length
+ }.max
+
+ numArgs.times {
+ | argIndex |
+ outp.puts "case #{argIndex}:"
+
+ # Check if all of the forms of all of the overloads either do, or don't, admit an address
+ # at this index. We expect this to be a very common case.
+ numYes = 0
+ numNo = 0
+ opcode.overloads.each {
+ | overload |
+ useAddr = (overload.signature[argIndex] and
+ overload.signature[argIndex].role == "UA")
+ overload.forms.each {
+ | form |
+ if form.kinds[argIndex] == "Addr" and not useAddr
+ numYes += 1
+ else
+ numNo += 1
+ end
+ }
+ }
+
+ # Note that we deliberately test numYes first because if we end up with no forms, we want
+ # to say that Address is inadmissible.
+ if numYes == 0
+ outp.puts "OPGEN_RETURN(false);"
+ elsif numNo == 0
+ outp.puts "OPGEN_RETURN(true);"
+ else
+ # Now do the full test.
+
+ needOverloadSwitch = (opcode.overloads.size != 1)
+
+ outp.puts "switch (args.size()) {" if needOverloadSwitch
+ opcode.overloads.each {
+ | overload |
+
+ useAddr = (overload.signature[argIndex] and
+ overload.signature[argIndex].role == "UA")
+
+ # Again, check if all of them do what we want.
+ numYes = 0
+ numNo = 0
+ overload.forms.each {
+ | form |
+ if form.kinds[argIndex] == "Addr" and not useAddr
+ numYes += 1
+ else
+ numNo += 1
+ end
+ }
+
+ if numYes == 0
+ # Don't emit anything, just drop to default.
+ elsif numNo == 0
+ outp.puts "case #{overload.signature.length}:" if needOverloadSwitch
+ outp.puts "OPGEN_RETURN(true);"
+ outp.puts "break;" if needOverloadSwitch
+ else
+ outp.puts "case #{overload.signature.length}:" if needOverloadSwitch
+
+ # This is how we test the hypothesis that changing this argument to an
+ # address yields a valid form.
+ columnGetter = proc {
+ | columnIndex |
+ if columnIndex == argIndex
+ "Arg::Addr"
+ else
+ "args[#{columnIndex}].kind()"
+ end
+ }
+ filter = proc {
+ | forms |
+ numYes = 0
+
+ forms.each {
+ | form |
+ if form.kinds[argIndex] == "Addr"
+ numYes += 1
+ end
+ }
+
+ if numYes == 0
+ # Drop down, emit no code, since we cannot match.
+ true
+ else
+ # Keep going.
+ false
+ end
+ }
+ callback = proc {
+ | form |
+ beginArchs(outp, form.archs)
+ outp.puts "OPGEN_RETURN(true);"
+ endArchs(outp, form.archs)
+ }
+ matchForms(outp, :safe, overload.forms, 0, columnGetter, filter, callback)
+
+ outp.puts "break;" if needOverloadSwitch
+ end
+ }
+ if needOverloadSwitch
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+ end
+ end
+
+ outp.puts "break;"
+ }
+
+ outp.puts "default:"
+ outp.puts "break;"
+ outp.puts "}"
+ end
+
+ outp.puts "break;"
+ }
+ outp.puts "default:";
+ outp.puts "break;"
+ outp.puts "}"
+ outp.puts "return false;"
+ outp.puts "}"
+
+ outp.puts "bool Inst::isTerminal()"
+ outp.puts "{"
+ outp.puts "switch (kind.opcode) {"
+ foundTrue = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:terminal]
+ outp.puts "case #{opcode.name}:"
+ foundTrue = true
+ end
+ }
+ if foundTrue
+ outp.puts "return true;"
+ end
+ $opcodes.values.each {
+ | opcode |
+ if opcode.custom
+ outp.puts "case #{opcode.name}:"
+ outp.puts "return #{opcode.name}Custom::isTerminal(*this);"
+ end
+ }
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "bool Inst::hasNonArgNonControlEffects()"
+ outp.puts "{"
+ outp.puts "if (kind.traps)"
+ outp.puts "return true;"
+ outp.puts "switch (kind.opcode) {"
+ foundTrue = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:effects]
+ outp.puts "case #{opcode.name}:"
+ foundTrue = true
+ end
+ }
+ if foundTrue
+ outp.puts "return true;"
+ end
+ $opcodes.values.each {
+ | opcode |
+ if opcode.custom
+ outp.puts "case #{opcode.name}:"
+ outp.puts "return #{opcode.name}Custom::hasNonArgNonControlEffects(*this);"
+ end
+ }
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "bool Inst::hasNonArgEffects()"
+ outp.puts "{"
+ outp.puts "if (kind.traps)"
+ outp.puts "return true;"
+ outp.puts "switch (kind.opcode) {"
+ foundTrue = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:terminal] or opcode.attributes[:effects]
+ outp.puts "case #{opcode.name}:"
+ foundTrue = true
+ end
+ }
+ if foundTrue
+ outp.puts "return true;"
+ end
+ $opcodes.values.each {
+ | opcode |
+ if opcode.custom
+ outp.puts "case #{opcode.name}:"
+ outp.puts "return #{opcode.name}Custom::hasNonArgEffects(*this);"
+ end
+ }
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "CCallHelpers::Jump Inst::generate(CCallHelpers& jit, GenerationContext& context)"
+ outp.puts "{"
+ outp.puts "UNUSED_PARAM(jit);"
+ outp.puts "UNUSED_PARAM(context);"
+ outp.puts "CCallHelpers::Jump result;"
+ matchInstOverloadForm(outp, :fast, "this") {
+ | opcode, overload, form |
+ if opcode.custom
+ outp.puts "OPGEN_RETURN(#{opcode.name}Custom::generate(*this, jit, context));"
+ else
+ beginArchs(outp, form.archs)
+ if form.altName
+ methodName = form.altName
+ else
+ methodName = opcode.masmName
+ end
+ if opcode.attributes[:branch]
+ outp.print "result = "
+ end
+ outp.print "jit.#{methodName}("
+
+ form.kinds.each_with_index {
+ | kind, index |
+ if index != 0
+ outp.print ", "
+ end
+ case kind.name
+ when "Tmp"
+ if overload.signature[index].type == "G"
+ outp.print "args[#{index}].gpr()"
+ else
+ outp.print "args[#{index}].fpr()"
+ end
+ when "Imm", "BitImm"
+ outp.print "args[#{index}].asTrustedImm32()"
+ when "BigImm", "BitImm64"
+ outp.print "args[#{index}].asTrustedImm64()"
+ when "Addr"
+ outp.print "args[#{index}].asAddress()"
+ when "Index"
+ outp.print "args[#{index}].asBaseIndex()"
+ when "RelCond"
+ outp.print "args[#{index}].asRelationalCondition()"
+ when "ResCond"
+ outp.print "args[#{index}].asResultCondition()"
+ when "DoubleCond"
+ outp.print "args[#{index}].asDoubleCondition()"
+ end
+ }
+
+ outp.puts ");"
+ outp.puts "OPGEN_RETURN(result);"
+ endArchs(outp, form.archs)
+ end
+ }
+ outp.puts "RELEASE_ASSERT_NOT_REACHED();"
+ outp.puts "return result;"
+ outp.puts "}"
+
+ outp.puts "} } } // namespace JSC::B3::Air"
+}
+
+# This is a hack for JSAir. It's a joke.
+File.open("JSAir_opcode.js", "w") {
+ | outp |
+ outp.puts "\"use strict\";"
+ outp.puts "// Generated by opcode_generator.rb from #{$fileName} -- do not edit!"
+
+ $opcodes.values.each {
+ | opcode |
+ outp.puts "const #{opcode.name} = Symbol(#{opcode.name.inspect});"
+ }
+
+ outp.puts "function Inst_forEachArg(inst, func)"
+ outp.puts "{"
+ outp.puts "let replacement;"
+ outp.puts "switch (inst.opcode) {"
+ $opcodes.values.each {
+ | opcode |
+ outp.puts "case #{opcode.name}:"
+ if opcode.custom
+ outp.puts "#{opcode.name}Custom.forEachArg(inst, func);"
+ else
+ needOverloadSwitch = opcode.overloads.size != 1
+ outp.puts "switch (inst.args.length) {" if needOverloadSwitch
+ opcode.overloads.each {
+ | overload |
+ outp.puts "case #{overload.signature.length}:" if needOverloadSwitch
+ overload.signature.each_with_index {
+ | arg, index |
+ role = nil
+ case arg.role
+ when "U"
+ role = "Use"
+ when "D"
+ role = "Def"
+ when "ZD"
+ role = "ZDef"
+ when "UD"
+ role = "UseDef"
+ when "UZD"
+ role = "UseZDef"
+ when "UA"
+ role = "UseAddr"
+ when "S"
+ role = "Scratch"
+ else
+ raise
+ end
+
+ outp.puts "inst.visitArg(#{index}, func, Arg.#{role}, #{arg.type}P, #{arg.width});"
+ }
+ outp.puts "break;"
+ }
+ if needOverloadSwitch
+ outp.puts "default:"
+ outp.puts "throw new Error(\"Bad overload\");"
+ outp.puts "break;"
+ outp.puts "}"
+ end
+ end
+ outp.puts "break;"
+ }
+ outp.puts "default:"
+ outp.puts "throw \"Bad opcode\";"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "function Inst_hasNonArgEffects(inst)"
+ outp.puts "{"
+ outp.puts "switch (inst.opcode) {"
+ foundTrue = false
+ $opcodes.values.each {
+ | opcode |
+ if opcode.attributes[:terminal] or opcode.attributes[:effects]
+ outp.puts "case #{opcode.name}:"
+ foundTrue = true
+ end
+ }
+ if foundTrue
+ outp.puts "return true;"
+ end
+ $opcodes.values.each {
+ | opcode |
+ if opcode.custom
+ outp.puts "case #{opcode.name}:"
+ outp.puts "return #{opcode.name}Custom.hasNonArgNonControlEffects(inst);"
+ end
+ }
+ outp.puts "default:"
+ outp.puts "return false;"
+ outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "function opcodeCode(opcode)"
+ outp.puts "{"
+ outp.puts "switch (opcode) {"
+ $opcodes.keys.sort.each_with_index {
+ | opcode, index |
+ outp.puts "case #{opcode}:"
+ outp.puts "return #{index}"
+ }
+ outp.puts "default:"
+ outp.puts "throw new Error(\"bad opcode\");"
+ outp.puts "}"
+ outp.puts "}"
+}
+