]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
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 | ||
93a37866 | 28 | require "config" |
6fe7ccc8 A |
29 | require "backends" |
30 | require "digest/sha1" | |
31 | require "offsets" | |
32 | require "parser" | |
33 | require "self_hash" | |
34 | require "settings" | |
35 | require "transform" | |
36 | ||
37 | class Assembler | |
38 | def initialize(outp) | |
39 | @outp = outp | |
40 | @state = :cpp | |
41 | @commentState = :none | |
42 | @comment = nil | |
93a37866 A |
43 | @internalComment = nil |
44 | @annotation = nil | |
45 | @codeOrigin = nil | |
46 | @numLocalLabels = 0 | |
47 | @numGlobalLabels = 0 | |
48 | ||
49 | @newlineSpacerState = :none | |
81345200 | 50 | @lastlabel = "" |
6fe7ccc8 | 51 | end |
81345200 | 52 | |
6fe7ccc8 | 53 | def enterAsm |
81345200 | 54 | @outp.puts "OFFLINE_ASM_BEGIN" if !$emitWinAsm |
6fe7ccc8 A |
55 | @state = :asm |
56 | end | |
57 | ||
58 | def leaveAsm | |
81345200 | 59 | putsProcEndIfNeeded if $emitWinAsm |
6fe7ccc8 | 60 | putsLastComment |
81345200 | 61 | @outp.puts "OFFLINE_ASM_END" if !$emitWinAsm |
6fe7ccc8 A |
62 | @state = :cpp |
63 | end | |
64 | ||
65 | def inAsm | |
66 | enterAsm | |
67 | yield | |
68 | leaveAsm | |
69 | end | |
70 | ||
93a37866 | 71 | # Concatenates all the various components of the comment to dump. |
6fe7ccc8 | 72 | def lastComment |
93a37866 A |
73 | separator = " " |
74 | result = "" | |
75 | result = "#{@comment}" if @comment | |
76 | if @annotation and $enableInstrAnnotations | |
77 | result += separator if result != "" | |
78 | result += "#{@annotation}" | |
79 | end | |
80 | if @internalComment | |
81 | result += separator if result != "" | |
82 | result += "#{@internalComment}" | |
83 | end | |
84 | if @codeOrigin and $enableCodeOriginComments | |
85 | result += separator if result != "" | |
86 | result += "#{@codeOrigin}" | |
87 | end | |
88 | if result != "" | |
81345200 | 89 | result = $commentPrefix + " " + result |
6fe7ccc8 | 90 | end |
93a37866 A |
91 | |
92 | # Reset all the components that we've just sent to be dumped. | |
6fe7ccc8 A |
93 | @commentState = :none |
94 | @comment = nil | |
93a37866 A |
95 | @annotation = nil |
96 | @codeOrigin = nil | |
97 | @internalComment = nil | |
6fe7ccc8 A |
98 | result |
99 | end | |
100 | ||
93a37866 A |
101 | # Puts a C Statement in the output stream. |
102 | def putc(*line) | |
103 | raise unless @state == :asm | |
104 | @outp.puts(formatDump(" " + line.join(''), lastComment)) | |
105 | end | |
106 | ||
107 | def formatDump(dumpStr, comment, commentColumns=$preferredCommentStartColumn) | |
108 | if comment.length > 0 | |
109 | "%-#{commentColumns}s %s" % [dumpStr, comment] | |
110 | else | |
111 | dumpStr | |
112 | end | |
113 | end | |
114 | ||
115 | # private method for internal use only. | |
116 | def putAnnotation(text) | |
117 | raise unless @state == :asm | |
118 | if $enableInstrAnnotations | |
119 | @outp.puts text | |
120 | @annotation = nil | |
121 | end | |
122 | end | |
123 | ||
124 | def putLocalAnnotation() | |
125 | putAnnotation " // #{@annotation}" if @annotation | |
126 | end | |
127 | ||
128 | def putGlobalAnnotation() | |
129 | putsNewlineSpacerIfAppropriate(:annotation) | |
130 | putAnnotation "// #{@annotation}" if @annotation | |
131 | end | |
132 | ||
6fe7ccc8 A |
133 | def putsLastComment |
134 | comment = lastComment | |
135 | unless comment.empty? | |
136 | @outp.puts comment | |
137 | end | |
138 | end | |
139 | ||
140 | def puts(*line) | |
141 | raise unless @state == :asm | |
81345200 A |
142 | if !$emitWinAsm |
143 | @outp.puts(formatDump(" \"\\t" + line.join('') + "\\n\"", lastComment)) | |
144 | else | |
145 | @outp.puts(formatDump(" " + line.join(''), lastComment)) | |
146 | end | |
6fe7ccc8 A |
147 | end |
148 | ||
149 | def print(line) | |
150 | raise unless @state == :asm | |
151 | @outp.print("\"" + line + "\"") | |
152 | end | |
153 | ||
93a37866 A |
154 | def putsNewlineSpacerIfAppropriate(state) |
155 | if @newlineSpacerState != state | |
156 | @outp.puts("\n") | |
157 | @newlineSpacerState = state | |
158 | end | |
159 | end | |
160 | ||
81345200 A |
161 | def putsProc(label, comment) |
162 | raise unless $emitWinAsm | |
163 | @outp.puts(formatDump("#{label} PROC PUBLIC", comment)) | |
164 | @lastlabel = label | |
165 | end | |
166 | ||
167 | def putsProcEndIfNeeded | |
168 | raise unless $emitWinAsm | |
169 | if @lastlabel != "" | |
170 | @outp.puts("#{@lastlabel} ENDP") | |
171 | end | |
172 | @lastlabel = "" | |
173 | end | |
174 | ||
175 | def putsLabel(labelName, isGlobal) | |
6fe7ccc8 | 176 | raise unless @state == :asm |
93a37866 | 177 | @numGlobalLabels += 1 |
81345200 | 178 | putsProcEndIfNeeded if $emitWinAsm and isGlobal |
93a37866 A |
179 | putsNewlineSpacerIfAppropriate(:global) |
180 | @internalComment = $enableLabelCountComments ? "Global Label #{@numGlobalLabels}" : nil | |
81345200 A |
181 | if isGlobal |
182 | if !$emitWinAsm | |
183 | @outp.puts(formatDump("OFFLINE_ASM_GLOBAL_LABEL(#{labelName})", lastComment)) | |
184 | else | |
185 | putsProc(labelName, lastComment) | |
186 | end | |
187 | elsif /\Allint_op_/.match(labelName) | |
188 | if !$emitWinAsm | |
189 | @outp.puts(formatDump("OFFLINE_ASM_OPCODE_LABEL(op_#{$~.post_match})", lastComment)) | |
190 | else | |
191 | label = "llint_" + "op_#{$~.post_match}" | |
192 | @outp.puts(formatDump(" _#{label}:", lastComment)) | |
193 | end | |
93a37866 | 194 | else |
81345200 A |
195 | if !$emitWinAsm |
196 | @outp.puts(formatDump("OFFLINE_ASM_GLUE_LABEL(#{labelName})", lastComment)) | |
197 | else | |
198 | @outp.puts(formatDump(" _#{labelName}:", lastComment)) | |
199 | end | |
93a37866 A |
200 | end |
201 | @newlineSpacerState = :none # After a global label, we can use another spacer. | |
6fe7ccc8 A |
202 | end |
203 | ||
204 | def putsLocalLabel(labelName) | |
205 | raise unless @state == :asm | |
93a37866 A |
206 | @numLocalLabels += 1 |
207 | @outp.puts("\n") | |
208 | @internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil | |
81345200 A |
209 | if !$emitWinAsm |
210 | @outp.puts(formatDump(" OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment)) | |
211 | else | |
212 | @outp.puts(formatDump(" #{labelName}:", lastComment)) | |
213 | end | |
6fe7ccc8 | 214 | end |
81345200 A |
215 | |
216 | def self.externLabelReference(labelName) | |
217 | if !$emitWinAsm | |
218 | "\" LOCAL_REFERENCE(#{labelName}) \"" | |
219 | else | |
220 | "#{labelName}" | |
221 | end | |
222 | end | |
223 | ||
6fe7ccc8 | 224 | def self.labelReference(labelName) |
81345200 A |
225 | if !$emitWinAsm |
226 | "\" LOCAL_LABEL_STRING(#{labelName}) \"" | |
227 | else | |
228 | "_#{labelName}" | |
229 | end | |
6fe7ccc8 A |
230 | end |
231 | ||
232 | def self.localLabelReference(labelName) | |
81345200 A |
233 | if !$emitWinAsm |
234 | "\" LOCAL_LABEL_STRING(#{labelName}) \"" | |
235 | else | |
236 | "#{labelName}" | |
237 | end | |
6fe7ccc8 A |
238 | end |
239 | ||
93a37866 A |
240 | def self.cLabelReference(labelName) |
241 | if /\Allint_op_/.match(labelName) | |
242 | "op_#{$~.post_match}" # strip opcodes of their llint_ prefix. | |
243 | else | |
244 | "#{labelName}" | |
245 | end | |
246 | end | |
247 | ||
248 | def self.cLocalLabelReference(labelName) | |
249 | "#{labelName}" | |
250 | end | |
251 | ||
252 | def codeOrigin(text) | |
6fe7ccc8 A |
253 | case @commentState |
254 | when :none | |
93a37866 | 255 | @codeOrigin = text |
6fe7ccc8 A |
256 | @commentState = :one |
257 | when :one | |
93a37866 | 258 | if $enableCodeOriginComments |
81345200 A |
259 | @outp.puts " " + $commentPrefix + " #{@codeOrigin}" |
260 | @outp.puts " " + $commentPrefix + " #{text}" | |
93a37866 A |
261 | end |
262 | @codeOrigin = nil | |
6fe7ccc8 A |
263 | @commentState = :many |
264 | when :many | |
81345200 | 265 | @outp.puts $commentPrefix + " #{text}" if $enableCodeOriginComments |
6fe7ccc8 A |
266 | else |
267 | raise | |
268 | end | |
269 | end | |
93a37866 A |
270 | |
271 | def comment(text) | |
272 | @comment = text | |
273 | end | |
274 | def annotation(text) | |
275 | @annotation = text | |
276 | end | |
6fe7ccc8 A |
277 | end |
278 | ||
81345200 A |
279 | IncludeFile.processIncludeOptions() |
280 | ||
6fe7ccc8 A |
281 | asmFile = ARGV.shift |
282 | offsetsFile = ARGV.shift | |
283 | outputFlnm = ARGV.shift | |
284 | ||
285 | $stderr.puts "offlineasm: Parsing #{asmFile} and #{offsetsFile} and creating assembly file #{outputFlnm}." | |
286 | ||
287 | begin | |
288 | configurationList = offsetsAndConfigurationIndex(offsetsFile) | |
289 | rescue MissingMagicValuesException | |
93a37866 | 290 | $stderr.puts "offlineasm: No magic values found. Skipping assembly file generation." |
6fe7ccc8 A |
291 | exit 0 |
292 | end | |
293 | ||
81345200 A |
294 | $emitWinAsm = isMSVC ? outputFlnm.index(".asm") != nil : false |
295 | $commentPrefix = $emitWinAsm ? ";" : "//" | |
296 | ||
6fe7ccc8 | 297 | inputHash = |
81345200 | 298 | $commentPrefix + " offlineasm input hash: " + parseHash(asmFile) + |
6fe7ccc8 A |
299 | " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) + |
300 | " " + selfHash | |
301 | ||
302 | if FileTest.exist? outputFlnm | |
303 | File.open(outputFlnm, "r") { | |
304 | | inp | | |
305 | firstLine = inp.gets | |
306 | if firstLine and firstLine.chomp == inputHash | |
307 | $stderr.puts "offlineasm: Nothing changed." | |
308 | exit 0 | |
309 | end | |
310 | } | |
311 | end | |
312 | ||
313 | File.open(outputFlnm, "w") { | |
314 | | outp | | |
315 | $output = outp | |
316 | $output.puts inputHash | |
81345200 | 317 | |
6fe7ccc8 A |
318 | $asm = Assembler.new($output) |
319 | ||
320 | ast = parse(asmFile) | |
81345200 | 321 | |
6fe7ccc8 A |
322 | configurationList.each { |
323 | | configuration | | |
324 | offsetsList = configuration[0] | |
325 | configIndex = configuration[1] | |
326 | forSettings(computeSettingsCombinations(ast)[configIndex], ast) { | |
327 | | concreteSettings, lowLevelAST, backend | | |
328 | lowLevelAST = lowLevelAST.resolve(*buildOffsetsMap(lowLevelAST, offsetsList)) | |
329 | lowLevelAST.validate | |
330 | emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) { | |
331 | $asm.inAsm { | |
332 | lowLevelAST.lower(backend) | |
333 | } | |
334 | } | |
335 | } | |
336 | } | |
337 | } | |
338 | ||
339 | $stderr.puts "offlineasm: Assembly file #{outputFlnm} successfully generated." | |
340 |