blob: d70ca63a918d7bd6686b3156d667c7e193d08f21 [file] [log] [blame]
#!/usr/bin/env ruby
# Copyright (C) 2015-2017 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, :bank, :width
def initialize(role, bank, width)
@role = role
@bank = bank
@width = width
end
def self.widthCode(width)
if width == "Ptr"
"POINTER_WIDTH"
else
"Width#{width}"
end
end
def widthCode
Arg.widthCode(width)
end
def self.roleCode(role)
case role
when "U"
"Use"
when "D"
"Def"
when "ZD"
"ZDef"
when "UD"
"UseDef"
when "UZD"
"UseZDef"
when "UA"
"UseAddr"
when "S"
"Scratch"
when "ED"
"EarlyDef"
when "EZD"
"EarlyZDef"
when "LU"
"LateUse"
else
raise
end
end
def roleCode
Arg.roleCode(role)
end
def to_s
"#{role}:#{bank}:#{width}"
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)|(ED)|(EZD)|(LU))\Z/
end
def isGF(token)
token =~ /\A((G)|(F))\Z/
end
def isKind(token)
token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(SimpleAddr)|(Addr)|(ExtendedOffsetAddr)|(Index)|(RelCond)|(ResCond)|(DoubleCond)|(StatusCond))\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 consumeBank
result = token.string
parseError("Expected bank (G or F)") unless isGF(result)
advance
result
end
def consumeKind
result = token.string
parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, Tmp, SimpleAddr, Addr, ExtendedOffsetAddr, Index, RelCond, ResCond, DoubleCond, or StatusCond)") 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(":")
bank = consumeBank
consume(":")
width = consumeWidth
signature << Arg.new(role, bank, 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].bank != "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.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::#{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
maxNumOperands = 0
$opcodes.values.each {
| opcode |
next if opcode.custom
opcode.overloads.each {
| overload |
maxNumOperands = overload.signature.length if overload.signature.length > maxNumOperands
}
}
formTableWidth = (maxNumOperands + 1) * maxNumOperands / 2
writeH("OpcodeUtils") {
| outp |
outp.puts "#include \"AirCustom.h\""
outp.puts "#include \"AirInst.h\""
outp.puts "#include \"AirFormTable.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 "ALWAYS_INLINE void Inst::forEachArg(const Functor& functor)"
outp.puts "{"
outp.puts "switch (kind.opcode) {"
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{opcode.name}:"
end
}
outp.puts "forEachArgCustom(scopedLambdaRef<EachArgCallback>(functor));"
outp.puts "return;"
outp.puts "default:"
outp.puts "forEachArgSimple(functor);"
outp.puts "return;"
outp.puts "}"
outp.puts "}"
outp.puts "template<typename Func>"
outp.puts "ALWAYS_INLINE void Inst::forEachArgSimple(const Func& func)"
outp.puts "{"
outp.puts " size_t numOperands = args.size();"
outp.puts " size_t formOffset = (numOperands - 1) * numOperands / 2;"
outp.puts " const uint8_t* formBase = g_formTable + kind.opcode * #{formTableWidth} + formOffset;"
outp.puts " for (size_t i = 0; i < numOperands; ++i) {"
outp.puts " uint8_t form = formBase[i];"
outp.puts " ASSERT(!(form & (1 << formInvalidShift)));"
outp.puts " func(args[i], decodeFormRole(form), decodeFormBank(form), decodeFormWidth(form));"
outp.puts " }"
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::#{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::#{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::#{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::#{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 "const uint8_t g_formTable[#{$opcodes.size * formTableWidth}] = {"
$opcodes.values.each {
| opcode |
overloads = [nil] * (maxNumOperands + 1)
unless opcode.custom
opcode.overloads.each {
| overload |
overloads[overload.signature.length] = overload
}
end
(0..maxNumOperands).each {
| numOperands |
overload = overloads[numOperands]
if overload
outp.puts "// #{opcode.name} #{overload.signature.join(', ')}"
numOperands.times {
| index |
arg = overload.signature[index]
outp.print "ENCODE_INST_FORM(Arg::#{arg.roleCode}, #{arg.bank}P, #{arg.widthCode}), "
}
else
outp.puts "// Invalid: #{opcode.name} with numOperands = #{numOperands}"
numOperands.times {
outp.print "INVALID_INST_FORM, "
}
end
outp.puts
}
}
outp.puts "};"
outp.puts "void Inst::forEachArgCustom(ScopedLambda<EachArgCallback> lambda)"
outp.puts "{"
outp.puts "switch (kind.opcode) {"
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{opcode.name}:"
outp.puts "#{opcode.name}Custom::forEachArg(*this, lambda);"
outp.puts "break;"
end
}
outp.puts "default:"
outp.puts "dataLog(\"Bad call to forEachArgCustom, not custom opcode: \", kind, \"\\n\");"
outp.puts "RELEASE_ASSERT_NOT_REACHED();"
outp.puts "}"
outp.puts "}"
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.bank}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 "SimpleAddr"
outp.puts "if (!args[#{index}].ptr().isGP())"
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 "ExtendedOffsetAddr"
if arg.role == "UA"
outp.puts "if (args[#{index}].isStack() && args[#{index}].stackSlot()->isSpill())"
outp.puts "OPGEN_RETURN(false);"
end
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"
when "StatusCond"
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::#{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::admitsExtendedOffsetAddr(unsigned argIndex)"
outp.puts "{"
outp.puts "switch (kind.opcode) {"
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{opcode.name}:"
outp.puts "OPGEN_RETURN(#{opcode.name}Custom::admitsExtendedOffsetAddr(*this, argIndex));"
outp.puts "break;"
end
}
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::#{opcode.name}:"
foundTrue = true
end
}
if foundTrue
outp.puts "return true;"
end
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{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.effects)"
outp.puts "return true;"
outp.puts "switch (kind.opcode) {"
foundTrue = false
$opcodes.values.each {
| opcode |
if opcode.attributes[:effects]
outp.puts "case Opcode::#{opcode.name}:"
foundTrue = true
end
}
if foundTrue
outp.puts "return true;"
end
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{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.effects)"
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::#{opcode.name}:"
foundTrue = true
end
}
if foundTrue
outp.puts "return true;"
end
$opcodes.values.each {
| opcode |
if opcode.custom
outp.puts "case Opcode::#{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].bank == "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 "SimpleAddr", "Addr", "ExtendedOffsetAddr"
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()"
when "StatusCond"
outp.print "args[#{index}].asStatusCondition()"
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"
}