1#!/usr/bin/env ruby 2 3# Copyright (C) 2011 Apple Inc. All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 14# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24# THE POSSIBILITY OF SUCH DAMAGE. 25 26$: << File.dirname(__FILE__) 27 28require "config" 29require "backends" 30require "digest/sha1" 31require "offsets" 32require "parser" 33require "self_hash" 34require "settings" 35require "transform" 36 37class Assembler 38 def initialize(outp) 39 @outp = outp 40 @state = :cpp 41 @commentState = :none 42 @comment = nil 43 @internalComment = nil 44 @annotation = nil 45 @codeOrigin = nil 46 @numLocalLabels = 0 47 @numGlobalLabels = 0 48 49 @newlineSpacerState = :none 50 end 51 52 def enterAsm 53 @outp.puts "OFFLINE_ASM_BEGIN" 54 @state = :asm 55 end 56 57 def leaveAsm 58 putsLastComment 59 @outp.puts "OFFLINE_ASM_END" 60 @state = :cpp 61 end 62 63 def inAsm 64 enterAsm 65 yield 66 leaveAsm 67 end 68 69 # Concatenates all the various components of the comment to dump. 70 def lastComment 71 separator = " " 72 result = "" 73 result = "#{@comment}" if @comment 74 if @annotation and $enableInstrAnnotations 75 result += separator if result != "" 76 result += "#{@annotation}" 77 end 78 if @internalComment 79 result += separator if result != "" 80 result += "#{@internalComment}" 81 end 82 if @codeOrigin and $enableCodeOriginComments 83 result += separator if result != "" 84 result += "#{@codeOrigin}" 85 end 86 if result != "" 87 result = "// " + result 88 end 89 90 # Reset all the components that we've just sent to be dumped. 91 @commentState = :none 92 @comment = nil 93 @annotation = nil 94 @codeOrigin = nil 95 @internalComment = nil 96 result 97 end 98 99 # Puts a C Statement in the output stream. 100 def putc(*line) 101 raise unless @state == :asm 102 @outp.puts(formatDump(" " + line.join(''), lastComment)) 103 end 104 105 def formatDump(dumpStr, comment, commentColumns=$preferredCommentStartColumn) 106 if comment.length > 0 107 "%-#{commentColumns}s %s" % [dumpStr, comment] 108 else 109 dumpStr 110 end 111 end 112 113 # private method for internal use only. 114 def putAnnotation(text) 115 raise unless @state == :asm 116 if $enableInstrAnnotations 117 @outp.puts text 118 @annotation = nil 119 end 120 end 121 122 def putLocalAnnotation() 123 putAnnotation " // #{@annotation}" if @annotation 124 end 125 126 def putGlobalAnnotation() 127 putsNewlineSpacerIfAppropriate(:annotation) 128 putAnnotation "// #{@annotation}" if @annotation 129 end 130 131 def putsLastComment 132 comment = lastComment 133 unless comment.empty? 134 @outp.puts comment 135 end 136 end 137 138 def puts(*line) 139 raise unless @state == :asm 140 @outp.puts(formatDump(" \"\\t" + line.join('') + "\\n\"", lastComment)) 141 end 142 143 def print(line) 144 raise unless @state == :asm 145 @outp.print("\"" + line + "\"") 146 end 147 148 def putsNewlineSpacerIfAppropriate(state) 149 if @newlineSpacerState != state 150 @outp.puts("\n") 151 @newlineSpacerState = state 152 end 153 end 154 155 def putsLabel(labelName) 156 raise unless @state == :asm 157 @numGlobalLabels += 1 158 putsNewlineSpacerIfAppropriate(:global) 159 @internalComment = $enableLabelCountComments ? "Global Label #{@numGlobalLabels}" : nil 160 if /\Allint_op_/.match(labelName) 161 @outp.puts(formatDump("OFFLINE_ASM_OPCODE_LABEL(op_#{$~.post_match})", lastComment)) 162 else 163 @outp.puts(formatDump("OFFLINE_ASM_GLUE_LABEL(#{labelName})", lastComment)) 164 end 165 @newlineSpacerState = :none # After a global label, we can use another spacer. 166 end 167 168 def putsLocalLabel(labelName) 169 raise unless @state == :asm 170 @numLocalLabels += 1 171 @outp.puts("\n") 172 @internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil 173 @outp.puts(formatDump(" OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment)) 174 end 175 176 def self.labelReference(labelName) 177 "\" LOCAL_REFERENCE(#{labelName}) \"" 178 end 179 180 def self.localLabelReference(labelName) 181 "\" LOCAL_LABEL_STRING(#{labelName}) \"" 182 end 183 184 def self.cLabelReference(labelName) 185 if /\Allint_op_/.match(labelName) 186 "op_#{$~.post_match}" # strip opcodes of their llint_ prefix. 187 else 188 "#{labelName}" 189 end 190 end 191 192 def self.cLocalLabelReference(labelName) 193 "#{labelName}" 194 end 195 196 def codeOrigin(text) 197 case @commentState 198 when :none 199 @codeOrigin = text 200 @commentState = :one 201 when :one 202 if $enableCodeOriginComments 203 @outp.puts " // #{@codeOrigin}" 204 @outp.puts " // #{text}" 205 end 206 @codeOrigin = nil 207 @commentState = :many 208 when :many 209 @outp.puts "// #{text}" if $enableCodeOriginComments 210 else 211 raise 212 end 213 end 214 215 def comment(text) 216 @comment = text 217 end 218 def annotation(text) 219 @annotation = text 220 end 221end 222 223asmFile = ARGV.shift 224offsetsFile = ARGV.shift 225outputFlnm = ARGV.shift 226 227$stderr.puts "offlineasm: Parsing #{asmFile} and #{offsetsFile} and creating assembly file #{outputFlnm}." 228 229begin 230 configurationList = offsetsAndConfigurationIndex(offsetsFile) 231rescue MissingMagicValuesException 232 $stderr.puts "offlineasm: No magic values found. Skipping assembly file generation." 233 exit 0 234end 235 236inputHash = 237 "// offlineasm input hash: " + parseHash(asmFile) + 238 " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) + 239 " " + selfHash 240 241if FileTest.exist? outputFlnm 242 File.open(outputFlnm, "r") { 243 | inp | 244 firstLine = inp.gets 245 if firstLine and firstLine.chomp == inputHash 246 $stderr.puts "offlineasm: Nothing changed." 247 exit 0 248 end 249 } 250end 251 252File.open(outputFlnm, "w") { 253 | outp | 254 $output = outp 255 $output.puts inputHash 256 257 $asm = Assembler.new($output) 258 259 ast = parse(asmFile) 260 261 configurationList.each { 262 | configuration | 263 offsetsList = configuration[0] 264 configIndex = configuration[1] 265 forSettings(computeSettingsCombinations(ast)[configIndex], ast) { 266 | concreteSettings, lowLevelAST, backend | 267 lowLevelAST = lowLevelAST.resolve(*buildOffsetsMap(lowLevelAST, offsetsList)) 268 lowLevelAST.validate 269 emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) { 270 $asm.inAsm { 271 lowLevelAST.lower(backend) 272 } 273 } 274 } 275 } 276} 277 278$stderr.puts "offlineasm: Assembly file #{outputFlnm} successfully generated." 279 280