]>
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 | |
6fe7ccc8 A |
50 | end |
51 | ||
52 | def enterAsm | |
93a37866 | 53 | @outp.puts "OFFLINE_ASM_BEGIN" |
6fe7ccc8 A |
54 | @state = :asm |
55 | end | |
56 | ||
57 | def leaveAsm | |
58 | putsLastComment | |
93a37866 | 59 | @outp.puts "OFFLINE_ASM_END" |
6fe7ccc8 A |
60 | @state = :cpp |
61 | end | |
62 | ||
63 | def inAsm | |
64 | enterAsm | |
65 | yield | |
66 | leaveAsm | |
67 | end | |
68 | ||
93a37866 | 69 | # Concatenates all the various components of the comment to dump. |
6fe7ccc8 | 70 | def lastComment |
93a37866 A |
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 | |
6fe7ccc8 | 88 | end |
93a37866 A |
89 | |
90 | # Reset all the components that we've just sent to be dumped. | |
6fe7ccc8 A |
91 | @commentState = :none |
92 | @comment = nil | |
93a37866 A |
93 | @annotation = nil |
94 | @codeOrigin = nil | |
95 | @internalComment = nil | |
6fe7ccc8 A |
96 | result |
97 | end | |
98 | ||
93a37866 A |
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 | ||
6fe7ccc8 A |
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 | |
93a37866 | 140 | @outp.puts(formatDump(" \"\\t" + line.join('') + "\\n\"", lastComment)) |
6fe7ccc8 A |
141 | end |
142 | ||
143 | def print(line) | |
144 | raise unless @state == :asm | |
145 | @outp.print("\"" + line + "\"") | |
146 | end | |
147 | ||
93a37866 A |
148 | def putsNewlineSpacerIfAppropriate(state) |
149 | if @newlineSpacerState != state | |
150 | @outp.puts("\n") | |
151 | @newlineSpacerState = state | |
152 | end | |
153 | end | |
154 | ||
6fe7ccc8 A |
155 | def putsLabel(labelName) |
156 | raise unless @state == :asm | |
93a37866 A |
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. | |
6fe7ccc8 A |
166 | end |
167 | ||
168 | def putsLocalLabel(labelName) | |
169 | raise unless @state == :asm | |
93a37866 A |
170 | @numLocalLabels += 1 |
171 | @outp.puts("\n") | |
172 | @internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil | |
173 | @outp.puts(formatDump(" OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment)) | |
6fe7ccc8 A |
174 | end |
175 | ||
176 | def self.labelReference(labelName) | |
93a37866 | 177 | "\" LOCAL_REFERENCE(#{labelName}) \"" |
6fe7ccc8 A |
178 | end |
179 | ||
180 | def self.localLabelReference(labelName) | |
181 | "\" LOCAL_LABEL_STRING(#{labelName}) \"" | |
182 | end | |
183 | ||
93a37866 A |
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) | |
6fe7ccc8 A |
197 | case @commentState |
198 | when :none | |
93a37866 | 199 | @codeOrigin = text |
6fe7ccc8 A |
200 | @commentState = :one |
201 | when :one | |
93a37866 A |
202 | if $enableCodeOriginComments |
203 | @outp.puts " // #{@codeOrigin}" | |
204 | @outp.puts " // #{text}" | |
205 | end | |
206 | @codeOrigin = nil | |
6fe7ccc8 A |
207 | @commentState = :many |
208 | when :many | |
93a37866 | 209 | @outp.puts "// #{text}" if $enableCodeOriginComments |
6fe7ccc8 A |
210 | else |
211 | raise | |
212 | end | |
213 | end | |
93a37866 A |
214 | |
215 | def comment(text) | |
216 | @comment = text | |
217 | end | |
218 | def annotation(text) | |
219 | @annotation = text | |
220 | end | |
6fe7ccc8 A |
221 | end |
222 | ||
223 | asmFile = ARGV.shift | |
224 | offsetsFile = ARGV.shift | |
225 | outputFlnm = ARGV.shift | |
226 | ||
227 | $stderr.puts "offlineasm: Parsing #{asmFile} and #{offsetsFile} and creating assembly file #{outputFlnm}." | |
228 | ||
229 | begin | |
230 | configurationList = offsetsAndConfigurationIndex(offsetsFile) | |
231 | rescue MissingMagicValuesException | |
93a37866 | 232 | $stderr.puts "offlineasm: No magic values found. Skipping assembly file generation." |
6fe7ccc8 A |
233 | exit 0 |
234 | end | |
235 | ||
236 | inputHash = | |
237 | "// offlineasm input hash: " + parseHash(asmFile) + | |
238 | " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) + | |
239 | " " + selfHash | |
240 | ||
241 | if 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 | } | |
250 | end | |
251 | ||
252 | File.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 |