source: webkit/trunk/Source/JavaScriptCore/offlineasm/asm.rb

Last change on this file was 291142, checked in by Elliott Williams, 3 years ago

[XCBuild] Emit a discovered dependency file from offlineasm
https://bugs.webkit.org/show_bug.cgi?id=237329

Reviewed by Alexey Proskuryakov.

Xcode needs to know what files offlineasm uses and produces in order to schedule it
correctly in incremental builds. Rather than use generated xcfilelists like WebKit does
elsewhere in the project, emit a depfile from offlineasm based on the parse tree's source
files.

Discovered dependency files ("depfiles") are Makefile-formatted files which list the inputs
used to produce an output. They are emitting during the build to a temporary directory, and
ensure that subsequent incremental builds will re-run offlineasm when any of the included
sources change. This is the same mechanism clang uses to track header dependencies.

Unfortunately, the legacy build system will refuse to execute a script phase or rule that
emits a depfile. To work around this, convert the offlineasm pipeline to be based on build
rules, to be used by XCBuild only. The idea is that LowLevelInterpreter.asm is listed as a
source build file in JSCLLIntSettingsExtractor, JSCLLIntOffsetsExtractor, and
JavaScriptCore. Each target uses a build rule to generate its respective header from
LowLevelInterpreter.asm. Xcode schedules these rule executions before any clang tasks.

The legacy build system avoids executing the rules via EXCLUDED_SOURCE_FILE_NAMES, and
instead uses the existing build phases, which have "(Legacy)" in their names and are now
no-ops under XCBuild.

Aside from working around the legacy build system's limitations, using build rules is
probably a superior way to express what we're doing, as it gives Xcode the opportunity to
compile other objects in parallel, and could be easily extended to compile multiple discrete
asm files should the need arise.

  • Configurations/ToolExecutable.xcconfig: Build rules are XCBuild-only.
  • JavaScriptCore.xcodeproj/project.pbxproj: Add build rules, rename legacy scripts.
  • offlineasm/asm.rb: Add --depfile flag.
  • offlineasm/generate_offset_extractor.rb: Add --depfile flag.
  • offlineasm/generate_settings_extractor.rb: Add --depfile flag.
File size: 13.7 KB
Line 
1#!/usr/bin/env ruby
2
3# Copyright (C) 2011-2021 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 'optparse'
33require "parser"
34require "self_hash"
35require "settings"
36require "shellwords"
37require "transform"
38
39class Assembler
40 def initialize(outp)
41 @outp = outp
42 @state = :cpp
43 resetAsm
44 end
45
46 def resetAsm
47 @comment = nil
48 @internalComment = nil
49 @annotation = nil
50 @codeOrigin = nil
51 @numLocalLabels = 0
52 @numGlobalLabels = 0
53 @deferredActions = []
54 @deferredNextLabelActions = []
55 @count = 0
56 @debugAnnotationStr = nil
57 @lastDebugAnnotationStr = nil
58
59 @newlineSpacerState = :none
60 @lastlabel = ""
61 end
62
63 def enterAsm
64 @outp.puts ""
65 putStr "OFFLINE_ASM_BEGIN" if !$emitWinAsm
66
67 if !$emitWinAsm
68 putStr "OFFLINE_ASM_GLOBAL_LABEL(llintPCRangeStart)"
69 else
70 putsProc("llintPCRangeStart", "")
71 putsProcEndIfNeeded
72 end
73 @state = :asm
74 SourceFile.outputDotFileList(@outp) if $enableDebugAnnotations
75 end
76
77 def leaveAsm
78 putsProcEndIfNeeded if $emitWinAsm
79 if !$emitWinAsm
80 putStr "OFFLINE_ASM_GLOBAL_LABEL(llintPCRangeEnd)"
81 else
82 putsProc("llintPCRangeEnd", "")
83 putsProcEndIfNeeded
84 end
85 putsLastComment
86 (@deferredNextLabelActions + @deferredActions).each {
87 | action |
88 action.call()
89 }
90 putStr "OFFLINE_ASM_END" if !$emitWinAsm
91 @state = :cpp
92 end
93
94 def deferAction(&proc)
95 @deferredActions << proc
96 end
97
98 def deferNextLabelAction(&proc)
99 @deferredNextLabelActions << proc
100 end
101
102 def newUID
103 @count += 1
104 @count
105 end
106
107 def inAsm
108 resetAsm
109 enterAsm
110 yield
111 leaveAsm
112 end
113
114 # Concatenates all the various components of the comment to dump.
115 def lastComment
116 separator = " "
117 result = ""
118 result = "#{@comment}" if @comment
119 if @annotation and $enableInstrAnnotations
120 result += separator if result != ""
121 result += "#{@annotation}"
122 end
123 if @internalComment
124 result += separator if result != ""
125 result += "#{@internalComment}"
126 end
127 if $enableCodeOriginComments and @codeOrigin and @codeOrigin != @lastCodeOrigin
128 @lastCodeOrigin = @codeOrigin
129 result += separator if result != ""
130 result += "#{@codeOrigin}"
131 end
132 if result != ""
133 result = $commentPrefix + " " + result
134 end
135
136 # Reset all the components that we've just sent to be dumped.
137 @comment = nil
138 @annotation = nil
139 @codeOrigin = nil
140 @internalComment = nil
141 result
142 end
143
144 # Puts a C Statement in the output stream.
145 def putc(*line)
146 raise unless @state == :asm
147 @outp.puts(formatDump(" " + line.join(''), lastComment))
148 end
149
150 def formatDump(dumpStr, comment, commentColumns=$preferredCommentStartColumn)
151 result = ""
152 if comment.length > 0
153 result = "%-#{commentColumns}s %s" % [dumpStr, comment]
154 else
155 result = dumpStr
156 end
157 if $enableDebugAnnotations
158 if @debugAnnotationStr and @debugAnnotationStr != @lastDebugAnnotationStr
159 result = "%-#{$preferredDebugAnnotationColumns}s%s" % [@debugAnnotationStr, result]
160 else
161 result = "%-#{$preferredDebugAnnotationColumns}s%s" % ["", result]
162 end
163 @lastDebugAnnotationStr = @debugAnnotationStr
164 @debugAnnotationStr = nil
165 end
166 result
167 end
168
169 # private method for internal use only.
170 def putAnnotation(text)
171 raise unless @state == :asm
172 if $enableInstrAnnotations
173 @outp.puts text
174 @annotation = nil
175 end
176 end
177
178 def putLocalAnnotation()
179 putAnnotation " // #{@annotation}" if @annotation
180 end
181
182 def putGlobalAnnotation()
183 putsNewlineSpacerIfAppropriate(:annotation)
184 putAnnotation "// #{@annotation}" if @annotation
185 end
186
187 def putsLastComment
188 comment = lastComment
189 unless comment.empty?
190 @outp.puts comment
191 end
192 end
193
194 def putStr(str)
195 if $enableDebugAnnotations
196 @outp.puts "%-#{$preferredDebugAnnotationColumns}s%s" % ["", str]
197 else
198 @outp.puts str
199 end
200 end
201
202 def puts(*line)
203 raise unless @state == :asm
204 if !$emitWinAsm
205 @outp.puts(formatDump(" \"" + line.join('') + " \\n\"", lastComment))
206 else
207 @outp.puts(formatDump(" " + line.join(''), lastComment))
208 end
209 end
210
211 def print(line)
212 raise unless @state == :asm
213 @outp.print("\"" + line + "\"")
214 end
215
216 def putsNewlineSpacerIfAppropriate(state)
217 if @newlineSpacerState != state
218 @outp.puts("\n")
219 @newlineSpacerState = state
220 end
221 end
222
223 def putsProc(label, comment)
224 raise unless $emitWinAsm
225 @outp.puts(formatDump("#{label} PROC PUBLIC", comment))
226 @lastlabel = label
227 end
228
229 def putsProcEndIfNeeded
230 raise unless $emitWinAsm
231 if @lastlabel != ""
232 @outp.puts("#{@lastlabel} ENDP")
233 end
234 @lastlabel = ""
235 end
236
237 def putsLabel(labelName, isGlobal)
238 raise unless @state == :asm
239 @deferredNextLabelActions.each {
240 | action |
241 action.call()
242 }
243 @deferredNextLabelActions = []
244 @numGlobalLabels += 1
245 putsProcEndIfNeeded if $emitWinAsm and isGlobal
246 putsNewlineSpacerIfAppropriate(:global)
247 @internalComment = $enableLabelCountComments ? "Global Label #{@numGlobalLabels}" : nil
248 if isGlobal
249 if !$emitWinAsm
250 @outp.puts(formatDump("OFFLINE_ASM_GLOBAL_LABEL(#{labelName})", lastComment))
251 else
252 putsProc(labelName, lastComment)
253 end
254 elsif /\Allint_op_/.match(labelName)
255 if !$emitWinAsm
256 @outp.puts(formatDump("OFFLINE_ASM_OPCODE_LABEL(op_#{$~.post_match})", lastComment))
257 else
258 label = "llint_" + "op_#{$~.post_match}"
259 @outp.puts(formatDump(" _#{label}:", lastComment))
260 end
261 else
262 if !$emitWinAsm
263 @outp.puts(formatDump("OFFLINE_ASM_GLUE_LABEL(#{labelName})", lastComment))
264 else
265 @outp.puts(formatDump(" _#{labelName}:", lastComment))
266 end
267 end
268 if $emitELFDebugDirectives
269 deferNextLabelAction {
270 putStr(" \".size #{labelName} , . - #{labelName} \\n\"")
271 putStr(" \".type #{labelName} , function \\n\"")
272 }
273 end
274 @newlineSpacerState = :none # After a global label, we can use another spacer.
275 end
276
277 def putsLocalLabel(labelName)
278 raise unless @state == :asm
279 @numLocalLabels += 1
280 @outp.puts("\n")
281 @internalComment = $enableLabelCountComments ? "Local Label #{@numLocalLabels}" : nil
282 if !$emitWinAsm
283 @outp.puts(formatDump(" OFFLINE_ASM_LOCAL_LABEL(#{labelName})", lastComment))
284 else
285 @outp.puts(formatDump(" #{labelName}:", lastComment))
286 end
287 end
288
289 def self.externLabelReference(labelName)
290 if !$emitWinAsm
291 "\" LOCAL_REFERENCE(#{labelName}) \""
292 else
293 "#{labelName}"
294 end
295 end
296
297 def self.labelReference(labelName)
298 if !$emitWinAsm
299 "\" LOCAL_LABEL_STRING(#{labelName}) \""
300 else
301 "_#{labelName}"
302 end
303 end
304
305 def self.localLabelReference(labelName)
306 if !$emitWinAsm
307 "\" LOCAL_LABEL_STRING(#{labelName}) \""
308 else
309 "#{labelName}"
310 end
311 end
312
313 def self.cLabelReference(labelName)
314 if /\Allint_op_/.match(labelName)
315 "op_#{$~.post_match}" # strip opcodes of their llint_ prefix.
316 else
317 "#{labelName}"
318 end
319 end
320
321 def self.cLocalLabelReference(labelName)
322 "#{labelName}"
323 end
324
325 def codeOrigin(text)
326 @codeOrigin = text
327 end
328
329 def comment(text)
330 @comment = text
331 end
332
333 def annotation(text)
334 @annotation = text
335 end
336
337 def debugAnnotation(text)
338 @debugAnnotationStr = text
339 end
340end
341
342IncludeFile.processIncludeOptions()
343
344asmFile = ARGV.shift
345offsetsFile = ARGV.shift
346outputFlnm = ARGV.shift
347variants = ARGV.shift.split(/[,\s]+/)
348
349$options = {}
350OptionParser.new do |opts|
351 opts.banner = "Usage: asm.rb asmFile offsetsFile outputFileName [--assembler=<ASM>] [--webkit-additions-path=<path>] [--binary-format=<format>] [--depfile=<depfile>]"
352 # This option is currently only used to specify the masm assembler
353 opts.on("--assembler=[ASM]", "Specify an assembler to use.") do |assembler|
354 $options[:assembler] = assembler
355 end
356 opts.on("--webkit-additions-path=PATH", "WebKitAdditions path.") do |path|
357 $options[:webkit_additions_path] = path
358 end
359 opts.on("--binary-format=FORMAT", "Specify the binary format used by the target system.") do |format|
360 $options[:binary_format] = format
361 end
362 opts.on("--depfile=DEPFILE", "Path to write Makefile-style discovered dependencies to.") do |path|
363 $options[:depfile] = path
364 end
365end.parse!
366
367begin
368 configurationList = offsetsAndConfigurationIndexForVariants(offsetsFile, variants)
369rescue MissingMagicValuesException
370 $stderr.puts "offlineasm: No magic values found. Skipping assembly file generation."
371 exit 1
372end
373
374# The MS compiler doesn't accept DWARF2 debug annotations.
375if isMSVC
376 $enableDebugAnnotations = false
377end
378
379$emitWinAsm = isMSVC ? outputFlnm.index(".asm") != nil : false
380$commentPrefix = $emitWinAsm ? ";" : "//"
381
382# We want this in all ELF systems we support, except for C_LOOP (we'll disable it later on if we are building cloop)
383$emitELFDebugDirectives = $options.has_key?(:binary_format) && $options[:binary_format] == "ELF"
384
385inputHash =
386 $commentPrefix + " offlineasm input hash: " + parseHash(asmFile, $options) +
387 " " + Digest::SHA1.hexdigest(configurationList.map{|v| (v[0] + [v[1]]).join(' ')}.join(' ')) +
388 " " + selfHash +
389 " " + Digest::SHA1.hexdigest($options.has_key?(:assembler) ? $options[:assembler] : "")
390
391if FileTest.exist?(outputFlnm) and (not $options[:depfile] or FileTest.exist?($options[:depfile]))
392 lastLine = nil
393 File.open(outputFlnm, "r") {
394 | file |
395 file.each_line {
396 | line |
397 line = line.chomp
398 unless line.empty?
399 lastLine = line
400 end
401 }
402 }
403 if lastLine and lastLine == inputHash
404 # Nothing changed.
405 exit 0
406 end
407end
408
409File.open(outputFlnm, "w") {
410 | outp |
411 $output = outp
412
413 $asm = Assembler.new($output)
414
415 sources = Set.new
416 ast = parse(asmFile, $options, sources)
417 settingsCombinations = computeSettingsCombinations(ast)
418
419 if $options[:depfile]
420 depfile = File.open($options[:depfile], "w")
421 depfile.print(Shellwords.escape(outputFlnm), ": ")
422 depfile.puts(Shellwords.join(sources.sort))
423 end
424
425 configurationList.each {
426 | configuration |
427 offsetsList = configuration[0]
428 configIndex = configuration[1]
429 forSettings(settingsCombinations[configIndex], ast) {
430 | concreteSettings, lowLevelAST, backend |
431
432 # There could be multiple backends we are generating for, but the C_LOOP is
433 # always by itself so this check to turn off $enableDebugAnnotations won't
434 # affect the generation for any other backend.
435 if backend == "C_LOOP" || backend == "C_LOOP_WIN"
436 $enableDebugAnnotations = false
437 $preferredCommentStartColumn = 60
438 $emitELFDebugDirectives = false
439 end
440
441 lowLevelAST = lowLevelAST.demacroify({})
442 lowLevelAST = lowLevelAST.resolve(buildOffsetsMap(lowLevelAST, offsetsList))
443 lowLevelAST.validate
444 emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) {
445 $currentSettings = concreteSettings
446 $asm.inAsm {
447 lowLevelAST.lower(backend)
448 }
449 }
450 }
451 }
452
453 $output.fsync
454 $output.puts inputHash
455}
Note: See TracBrowser for help on using the repository browser.