opcode_generator.rb [plain text]
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\ when /\A\n/
lineNumber += 1
when /\A([a-zA-Z0-9_]+)/
result << Token.new(Origin.new(fileName, lineNumber), $&)
when /\A([ \t\r]+)/
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 "void printInternal(PrintStream&, JSC::B3::Air::Opcode);"
outp.puts "} // namespace WTF"
}
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}->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 |
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 isTerminal(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
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 (opcode) {"
$opcodes.values.each {
| opcode |
outp.puts "case #{opcode.name}:"
if opcode.custom
outp.puts "OPGEN_RETURN(#{opcode.name}Custom::admitsStack(*this, argIndex));"
else
outp.puts "switch (argIndex) {"
numArgs = opcode.overloads.map {
| overload |
overload.signature.length
}.max
numArgs.times {
| argIndex |
outp.puts "case #{argIndex}:"
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
}
}
if numYes == 0
outp.puts "OPGEN_RETURN(false);"
elsif numNo == 0
outp.puts "OPGEN_RETURN(true);"
else
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")
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
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
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
true
else
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::hasNonArgNonControlEffects()"
outp.puts "{"
outp.puts "switch (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 "switch (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(*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"
}
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 "}"
}