0647c82bae8f53c22fd115ae27a31073fba53749
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / settings.rb
1 # Copyright (C) 2011 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 #    notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 #    notice, this list of conditions and the following disclaimer in the
10 #    documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22 # THE POSSIBILITY OF SUCH DAMAGE.
23
24 require "config"
25 require "ast"
26 require "backends"
27 require "parser"
28 require "transform"
29
30 #
31 # computeSettingsCombinations(ast) -> settingsCombiations
32 #
33 # Computes an array of settings maps, where a settings map constitutes
34 # a configuration for the assembly code being generated. The map
35 # contains key value pairs where keys are settings names (strings) and
36 # the values are booleans (true for enabled, false for disabled).
37 #
38
39 def computeSettingsCombinations(ast)
40     settingsCombinations = []
41     
42     def settingsCombinator(settingsCombinations, mapSoFar, remaining)
43         if remaining.empty?
44             settingsCombinations << mapSoFar
45             return
46         end
47         
48         newMap = mapSoFar.dup
49         newMap[remaining[0]] = true
50         settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
51         
52         newMap = mapSoFar.dup
53         newMap[remaining[0]] = false
54         settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
55     end
56     
57     nonBackendSettings = ast.filter(Setting).uniq.collect{ |v| v.name }
58     nonBackendSettings.delete_if {
59         | setting |
60         isBackend? setting
61     }
62     
63     allBackendsFalse = {}
64     BACKENDS.each {
65         | backend |
66         allBackendsFalse[backend] = false
67     }
68     
69     # This will create entries for invalid backends. That's fine. It's necessary
70     # because it ensures that generate_offsets_extractor (which knows about valid
71     # backends) has settings indices that are compatible with what asm will see
72     # (asm doesn't know about valid backends).
73     BACKENDS.each {
74         | backend |
75         map = allBackendsFalse.clone
76         map[backend] = true
77         settingsCombinator(settingsCombinations, map, nonBackendSettings)
78     }
79     
80     settingsCombinations
81 end
82
83 #
84 # forSettings(concreteSettings, ast) {
85 #     | concreteSettings, lowLevelAST, backend | ... }
86 #
87 # Determines if the settings combination is valid, and if so, calls
88 # the block with the information you need to generate code.
89 #
90
91 def forSettings(concreteSettings, ast)
92     # Check which architectures this combinator claims to support.
93     selectedBackend = nil
94     BACKENDS.each {
95         | backend |
96         if concreteSettings[backend]
97             raise if selectedBackend
98             selectedBackend = backend
99         end
100     }
101     
102     return unless isValidBackend? selectedBackend
103     
104     # Resolve the AST down to a low-level form (no macros or conditionals).
105     lowLevelAST = ast.resolveSettings(concreteSettings)
106     
107     yield concreteSettings, lowLevelAST, selectedBackend
108 end
109
110 #
111 # forEachValidSettingsCombination(ast) {
112 #     | concreteSettings, ast, backend, index | ... }
113 #
114 # forEachValidSettingsCombination(ast, settingsCombinations) {
115 #     | concreteSettings, ast, backend, index | ... }
116 #
117 # Executes the given block for each valid settings combination in the
118 # settings map. The ast passed into the block is resolved
119 # (ast.resolve) against the settings.
120 #
121 # The first form will call computeSettingsCombinations(ast) for you.
122 #
123
124 def forEachValidSettingsCombination(ast, *optionalSettingsCombinations)
125     raise if optionalSettingsCombinations.size > 1
126     
127     if optionalSettingsCombinations.empty?
128         settingsCombinations = computeSettingsCombinations(ast)
129     else
130         settingsCombinations = optionalSettingsCombiations[0]
131     end
132     
133     settingsCombinations.each_with_index {
134         | concreteSettings, index |
135         forSettings(concreteSettings, ast) {
136             | concreteSettings_, lowLevelAST, backend |
137             yield concreteSettings, lowLevelAST, backend, index
138         }
139     }
140 end
141
142 #
143 # cppSettingsTest(concreteSettings)
144 #
145 # Returns the C++ code used to test if we are in a configuration that
146 # corresponds to the given concrete settings.
147 #
148
149 def cppSettingsTest(concreteSettings)
150     "#if " + concreteSettings.to_a.collect{
151         | pair |
152         (if pair[1]
153              ""
154          else
155              "!"
156          end) + "OFFLINE_ASM_" + pair[0]
157     }.join(" && ")
158 end
159
160 #
161 # isASTErroneous(ast)
162 #
163 # Tests to see if the AST claims that there is an error - i.e. if the
164 # user's code, after settings resolution, has Error nodes.
165 #
166
167 def isASTErroneous(ast)
168     not ast.filter(Error).empty?
169 end
170
171 #
172 # assertConfiguration(concreteSettings)
173 #
174 # Emits a check that asserts that we're using the given configuration.
175 #
176
177 def assertConfiguration(concreteSettings)
178     $output.puts cppSettingsTest(concreteSettings)
179     $output.puts "#else"
180     $output.puts "#error \"Configuration mismatch.\""
181     $output.puts "#endif"
182 end
183
184 #
185 # emitCodeInConfiguration(concreteSettings, ast, backend) {
186 #     | concreteSettings, ast, backend | ... }
187 #
188 # Emits all relevant guards to see if the configuration holds and
189 # calls the block if the configuration is not erroneous.
190 #
191
192 def emitCodeInConfiguration(concreteSettings, ast, backend)
193     Label.resetReferenced
194
195     if !$emitWinAsm
196         $output.puts cppSettingsTest(concreteSettings)
197     else
198         if backend == "X86_WIN"
199             $output.puts ".MODEL FLAT, C"
200         end
201         $output.puts "INCLUDE #{File.basename($output.path)}.sym"
202         $output.puts "_TEXT SEGMENT"
203     end
204     
205     if isASTErroneous(ast)
206         $output.puts "#error \"Invalid configuration.\""
207     elsif not WORKING_BACKENDS.include? backend
208         $output.puts "#error \"This backend is not supported yet.\""
209     else
210         yield concreteSettings, ast, backend
211     end
212     
213     if !$emitWinAsm
214         $output.puts "#endif"
215     else
216         $output.puts "_TEXT ENDS"
217         $output.puts "END"
218
219         # Write symbols needed by MASM
220         File.open("#{File.basename($output.path)}.sym", "w") {
221             | outp |
222             Label.forReferencedExtern {
223                 | name |
224                 outp.puts "EXTERN #{name[1..-1]} : near"
225             }
226         }
227     end
228 end
229
230 #
231 # emitCodeInAllConfigurations(ast) {
232 #     | concreteSettings, ast, backend, index | ... }
233 #
234 # Emits guard codes for all valid configurations, and calls the block
235 # for those configurations that are valid and not erroneous.
236 #
237
238 def emitCodeInAllConfigurations(ast)
239     forEachValidSettingsCombination(ast) {
240         | concreteSettings, lowLevelAST, backend, index |
241         $output.puts cppSettingsTest(concreteSettings)
242         yield concreteSettings, lowLevelAST, backend, index
243         $output.puts "#endif"
244     }
245 end
246
247
248