Enable gigacage on iOS
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / asm.rb
1 #!/usr/bin/env ruby
2
3 # Copyright (C) 2011, 2016 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
28 require "config"
29 require "backends"
30 require "digest/sha1"
31 require "offsets"
32 require 'optparse'
33 require "parser"
34 require "self_hash"
35 require "settings"
36 require "transform"
37
38 class Assembler
39     def initialize(outp)
40         @outp = outp
41         @state = :cpp
42         @commentState = :none
43         @comment = nil
44         @internalComment = nil
45         @annotation = nil
46         @codeOrigin = nil
47         @numLocalLabels = 0
48         @numGlobalLabels = 0
49         @deferredActions = []
50         @count = 0
51
52         @newlineSpacerState = :none
53         @lastlabel = ""
54     end
55
56     def enterAsm
57         @outp.puts "OFFLINE_ASM_BEGIN" if !$emitWinAsm
58
59         if !$emitWinAsm
60             @outp.puts "OFFLINE_ASM_GLOBAL_LABEL(llintPCRangeStart)"
61         else
62             putsProc("llintPCRangeStart", "")
63             putsProcEndIfNeeded
64         end
65         @state = :asm
66         SourceFile.outputDotFileList(@outp) if $enableDebugAnnotations
67     end
68     
69     def leaveAsm
70         putsProcEndIfNeeded if $emitWinAsm
71         if !$emitWinAsm
72             @outp.puts "OFFLINE_ASM_GLOBAL_LABEL(llintPCRangeEnd)"
73         else
74             putsProc("llintPCRangeEnd", "")
75             putsProcEndIfNeeded
76         end
77         putsLastComment
78         @deferredActions.each {
79             | action |
80             action.call()
81         }
82         @outp.puts "OFFLINE_ASM_END" if !$emitWinAsm
83         @state = :cpp
84     end
85     
86     def deferAction(&proc)
87         @deferredActions << proc
88     end
89     
90     def newUID
91         @count += 1
92         @count
93     end
94     
95     def inAsm
96         enterAsm
97         yield
98         leaveAsm
99     end
100     
101     # Concatenates all the various components of the comment to dump.
102     def lastComment
103         separator = " "
104         result = ""
105         result = "#{@comment}" if @comment
106         if @annotation and $enableInstrAnnotations
107             result += separator if result != ""
108             result += "#{@annotation}"
109         end
110         if @internalComment
111             result += separator if result != ""
112             result += "#{@internalComment}"
113         end
114         if @codeOrigin and $enableCodeOriginComments
115             result += separator if result != ""
116             result += "#{@codeOrigin}"
117         end
118         if result != ""
119             result = $commentPrefix + " " + result
120         end
121
122         # Reset all the components that we've just sent to be dumped.
123         @commentState = :none
124         @comment = nil
125         @annotation = nil
126         @codeOrigin = nil
127         @internalComment = nil
128         result
129     end
130     
131     # Puts a C Statement in the output stream.
132     def putc(*line)
133         raise unless @state == :asm
134         @outp.puts(formatDump("    " + line.join(''), lastComment))
135     end
136     
137     def formatDump(dumpStr, comment, commentColumns=$preferredCommentStartColumn)
138         if comment.length > 0
139             "%-#{commentColumns}s %s" % [dumpStr, comment]
140         else
141             dumpStr
142         end
143     end
144
145     # private method for internal use only.
146     def putAnnotation(text)
147         raise unless @state == :asm
148         if $enableInstrAnnotations
149             @outp.puts text
150             @annotation = nil
151         end
152     end
153
154     def putLocalAnnotation()
155         putAnnotation "    // #{@annotation}" if @annotation
156     end
157
158     def putGlobalAnnotation()
159         putsNewlineSpacerIfAppropriate(:annotation)
160         putAnnotation "// #{@annotation}" if @annotation
161     end
162
163     def putsLastComment
164         comment = lastComment
165         unless comment.empty?
166             @outp.puts comment
167         end
168     end
169     
170     def puts(*line)
171         raise unless @state == :asm
172         if !$emitWinAsm
173             @outp.puts(formatDump("    \"\\t" + line.join('') + "\\n\"", lastComment))
174         else
175             @outp.puts(formatDump("    " + line.join(''), lastComment))
176         end
177     end
178     
179     def print(line)
180         raise unless @state == :asm
181         @outp.print("\"" + line + "\"")
182     end
183     
184     def putsNewlineSpacerIfAppropriate(state)
185         if @newlineSpacerState != state
186             @outp.puts("\n")
187             @newlineSpacerState = state
188         end
189     end
190
191     def putsProc(label, comment)
192         raise unless $emitWinAsm
193         @outp.puts(formatDump("#{label} PROC PUBLIC", comment))
194         @lastlabel = label
195     end
196
197     def putsProcEndIfNeeded
198         raise unless $emitWinAsm
199         if @lastlabel != ""
200             @outp.puts("#{@lastlabel} ENDP")
201         end
202         @lastlabel = ""
203     end
204
205     def putsLabel(labelName, isGlobal)
206         raise unless @state == :asm
207         @numGlobalLabels += 1
208         putsProcEndIfNeeded if $emitWinAsm and isGlobal
209         putsNewlineSpacerIfAppropriate(:global)
210         @internalComment = $enableLabelCountComments ? "Global Label #{@numGlobalLabels}" : nil
211         if isGlobal
212             if !$emitWinAsm
213                 @outp.puts(formatDump("OFFLINE_ASM_GLOBAL_LABEL(#{labelName})", lastComment))
214             else
215                 putsProc(labelName, lastComment)
216             end            
217         elsif /\Allint_op_/.match(labelName)
218             if !$emitWinAsm
219                 @outp.puts(formatDump("OFFLINE_ASM_OPCODE_LABEL(op_#{$~.post_match})", lastComment))
220             else
221                 label = "llint_" + "op_#{$~.post_match}"
222                 @outp.puts(formatDump("  _#{label}:", lastComment))
223             end            
224         else
225             if !$emitWinAsm
226                 @outp.puts(formatDump("OFFLINE_ASM_GLUE_LABEL(#{labelName})", lastComment))
227             else
228                 @outp.puts(formatDump("  _#{labelName}:", lastComment))
229             end
230         end
231         @newlineSpacerState = :none # After a global label, we can use another spacer.
232     end
233     
234     def putsLocalLabel(labelName)
235         raise unless @state == :asm
236         @numLocalLabels += 1
237         @outp.puts("\n")
238         @internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil
239         if !$emitWinAsm
240             @outp.puts(formatDump("  OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment))
241         else
242             @outp.puts(formatDump("  #{labelName}:", lastComment))
243         end
244     end
245
246     def self.externLabelReference(labelName)
247         if !$emitWinAsm
248             "\" LOCAL_REFERENCE(#{labelName}) \""
249         else
250             "#{labelName}"
251         end
252     end
253
254     def self.labelReference(labelName)
255         if !$emitWinAsm
256             "\" LOCAL_LABEL_STRING(#{labelName}) \""
257         else
258             "_#{labelName}"
259         end
260     end
261     
262     def self.localLabelReference(labelName)
263         if !$emitWinAsm
264             "\" LOCAL_LABEL_STRING(#{labelName}) \""
265         else
266             "#{labelName}"
267         end
268     end
269     
270     def self.cLabelReference(labelName)
271         if /\Allint_op_/.match(labelName)
272             "op_#{$~.post_match}"  # strip opcodes of their llint_ prefix.
273         else
274             "#{labelName}"
275         end
276     end
277     
278     def self.cLocalLabelReference(labelName)
279         "#{labelName}"
280     end
281     
282     def codeOrigin(text)
283         case @commentState
284         when :none
285             @codeOrigin = text
286             @commentState = :one
287         when :one
288             if $enableCodeOriginComments
289                 @outp.puts "    " + $commentPrefix + " #{@codeOrigin}"
290                 @outp.puts "    " + $commentPrefix + " #{text}"
291             end
292             @codeOrigin = nil
293             @commentState = :many
294         when :many
295             @outp.puts $commentPrefix + " #{text}" if $enableCodeOriginComments
296         else
297             raise
298         end
299     end
300
301     def comment(text)
302         @comment = text
303     end
304
305     def annotation(text)
306         @annotation = text
307     end
308
309     def debugAnnotation(text)
310         @outp.puts text
311     end
312 end
313
314 IncludeFile.processIncludeOptions()
315
316 asmFile = ARGV.shift
317 offsetsFile = ARGV.shift
318 outputFlnm = ARGV.shift
319
320 $options = {}
321 OptionParser.new do |opts|
322     opts.banner = "Usage: asm.rb asmFile offsetsFile outputFileName [--assembler=<ASM>]"
323     # This option is currently only used to specify the masm assembler
324     opts.on("--assembler=[ASM]", "Specify an assembler to use.") do |assembler|
325         $options[:assembler] = assembler
326     end
327 end.parse!
328
329 begin
330     configurationList = offsetsAndConfigurationIndex(offsetsFile)
331 rescue MissingMagicValuesException
332     $stderr.puts "offlineasm: No magic values found. Skipping assembly file generation."
333     exit 1
334 end
335
336 # The MS compiler doesn't accept DWARF2 debug annotations.
337 if isMSVC
338     $enableDebugAnnotations = false
339 end
340
341 $emitWinAsm = isMSVC ? outputFlnm.index(".asm") != nil : false
342 $commentPrefix = $emitWinAsm ? ";" : "//"
343
344 inputHash =
345     $commentPrefix + " offlineasm input hash: " + parseHash(asmFile) +
346     " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) +
347     " " + selfHash +
348     " " + Digest::SHA1.hexdigest($options.has_key?(:assembler) ? $options[:assembler] : "")
349
350 if FileTest.exist? outputFlnm
351     File.open(outputFlnm, "r") {
352         | inp |
353         firstLine = inp.gets
354         if firstLine and firstLine.chomp == inputHash
355             $stderr.puts "offlineasm: Nothing changed."
356             exit 0
357         end
358     }
359 end
360
361 File.open(outputFlnm, "w") {
362     | outp |
363     $output = outp
364     $output.puts inputHash
365
366     $asm = Assembler.new($output)
367     
368     ast = parse(asmFile)
369
370     configurationList.each {
371         | configuration |
372         offsetsList = configuration[0]
373         configIndex = configuration[1]
374         forSettings(computeSettingsCombinations(ast)[configIndex], ast) {
375             | concreteSettings, lowLevelAST, backend |
376
377             # There could be multiple backends we are generating for, but the C_LOOP is
378             # always by itself so this check to turn off $enableDebugAnnotations won't
379             # affect the generation for any other backend.
380             if backend == "C_LOOP"
381                 $enableDebugAnnotations = false
382             end
383
384             lowLevelAST = lowLevelAST.resolve(buildOffsetsMap(lowLevelAST, offsetsList))
385             lowLevelAST.validate
386             emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) {
387                 $asm.inAsm {
388                     lowLevelAST.lower(backend)
389                 }
390             }
391         }
392     }
393 }