$: << File.dirname(__FILE__)
require "config"
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
@internalComment = nil
@annotation = nil
@codeOrigin = nil
@numLocalLabels = 0
@numGlobalLabels = 0
@newlineSpacerState = :none
end
def enterAsm
@outp.puts "OFFLINE_ASM_BEGIN"
@state = :asm
end
def leaveAsm
putsLastComment
@outp.puts "OFFLINE_ASM_END"
@state = :cpp
end
def inAsm
enterAsm
yield
leaveAsm
end
def lastComment
separator = " "
result = ""
result = "#{@comment}" if @comment
if @annotation and $enableInstrAnnotations
result += separator if result != ""
result += "#{@annotation}"
end
if @internalComment
result += separator if result != ""
result += "#{@internalComment}"
end
if @codeOrigin and $enableCodeOriginComments
result += separator if result != ""
result += "#{@codeOrigin}"
end
if result != ""
result = "// " + result
end
@commentState = :none
@comment = nil
@annotation = nil
@codeOrigin = nil
@internalComment = nil
result
end
def putc(*line)
raise unless @state == :asm
@outp.puts(formatDump(" " + line.join(''), lastComment))
end
def formatDump(dumpStr, comment, commentColumns=$preferredCommentStartColumn)
if comment.length > 0
"%-#{commentColumns}s %s" % [dumpStr, comment]
else
dumpStr
end
end
def putAnnotation(text)
raise unless @state == :asm
if $enableInstrAnnotations
@outp.puts text
@annotation = nil
end
end
def putLocalAnnotation()
putAnnotation " // #{@annotation}" if @annotation
end
def putGlobalAnnotation()
putsNewlineSpacerIfAppropriate(:annotation)
putAnnotation "// #{@annotation}" if @annotation
end
def putsLastComment
comment = lastComment
unless comment.empty?
@outp.puts comment
end
end
def puts(*line)
raise unless @state == :asm
@outp.puts(formatDump(" \"\\t" + line.join('') + "\\n\"", lastComment))
end
def print(line)
raise unless @state == :asm
@outp.print("\"" + line + "\"")
end
def putsNewlineSpacerIfAppropriate(state)
if @newlineSpacerState != state
@outp.puts("\n")
@newlineSpacerState = state
end
end
def putsLabel(labelName)
raise unless @state == :asm
@numGlobalLabels += 1
putsNewlineSpacerIfAppropriate(:global)
@internalComment = $enableLabelCountComments ? "Global Label #{@numGlobalLabels}" : nil
if /\Allint_op_/.match(labelName)
@outp.puts(formatDump("OFFLINE_ASM_OPCODE_LABEL(op_#{$~.post_match})", lastComment))
else
@outp.puts(formatDump("OFFLINE_ASM_GLUE_LABEL(#{labelName})", lastComment))
end
@newlineSpacerState = :none end
def putsLocalLabel(labelName)
raise unless @state == :asm
@numLocalLabels += 1
@outp.puts("\n")
@internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil
@outp.puts(formatDump(" OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment))
end
def self.labelReference(labelName)
"\" LOCAL_REFERENCE(#{labelName}) \""
end
def self.localLabelReference(labelName)
"\" LOCAL_LABEL_STRING(#{labelName}) \""
end
def self.cLabelReference(labelName)
if /\Allint_op_/.match(labelName)
"op_#{$~.post_match}" else
"#{labelName}"
end
end
def self.cLocalLabelReference(labelName)
"#{labelName}"
end
def codeOrigin(text)
case @commentState
when :none
@codeOrigin = text
@commentState = :one
when :one
if $enableCodeOriginComments
@outp.puts " // #{@codeOrigin}"
@outp.puts " // #{text}"
end
@codeOrigin = nil
@commentState = :many
when :many
@outp.puts "// #{text}" if $enableCodeOriginComments
else
raise
end
end
def comment(text)
@comment = text
end
def annotation(text)
@annotation = text
end
end
asmFile = ARGV.shift
offsetsFile = ARGV.shift
outputFlnm = ARGV.shift
$stderr.puts "offlineasm: Parsing #{asmFile} and #{offsetsFile} and creating assembly file #{outputFlnm}."
begin
configurationList = offsetsAndConfigurationIndex(offsetsFile)
rescue MissingMagicValuesException
$stderr.puts "offlineasm: No magic values found. Skipping assembly file generation."
exit 0
end
inputHash =
"// offlineasm input hash: " + parseHash(asmFile) +
" " + 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(asmFile)
configurationList.each {
| configuration |
offsetsList = configuration[0]
configIndex = configuration[1]
forSettings(computeSettingsCombinations(ast)[configIndex], ast) {
| concreteSettings, lowLevelAST, backend |
lowLevelAST = lowLevelAST.resolve(*buildOffsetsMap(lowLevelAST, offsetsList))
lowLevelAST.validate
emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) {
$asm.inAsm {
lowLevelAST.lower(backend)
}
}
}
}
}
$stderr.puts "offlineasm: Assembly file #{outputFlnm} successfully generated."