]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
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 | ||
93a37866 | 24 | require "config" |
6fe7ccc8 A |
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 | ||
ed1e77d3 A |
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 | } | |
6fe7ccc8 A |
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 | numClaimedBackends = 0 | |
94 | selectedBackend = nil | |
95 | BACKENDS.each { | |
96 | | backend | | |
ed1e77d3 A |
97 | if concreteSettings[backend] |
98 | raise if selectedBackend | |
6fe7ccc8 A |
99 | selectedBackend = backend |
100 | end | |
101 | } | |
102 | ||
ed1e77d3 | 103 | return unless isValidBackend? selectedBackend |
6fe7ccc8 A |
104 | |
105 | # Resolve the AST down to a low-level form (no macros or conditionals). | |
106 | lowLevelAST = ast.resolveSettings(concreteSettings) | |
107 | ||
108 | yield concreteSettings, lowLevelAST, selectedBackend | |
109 | end | |
110 | ||
111 | # | |
112 | # forEachValidSettingsCombination(ast) { | |
113 | # | concreteSettings, ast, backend, index | ... } | |
114 | # | |
115 | # forEachValidSettingsCombination(ast, settingsCombinations) { | |
116 | # | concreteSettings, ast, backend, index | ... } | |
117 | # | |
118 | # Executes the given block for each valid settings combination in the | |
119 | # settings map. The ast passed into the block is resolved | |
120 | # (ast.resolve) against the settings. | |
121 | # | |
122 | # The first form will call computeSettingsCombinations(ast) for you. | |
123 | # | |
124 | ||
125 | def forEachValidSettingsCombination(ast, *optionalSettingsCombinations) | |
126 | raise if optionalSettingsCombinations.size > 1 | |
127 | ||
128 | if optionalSettingsCombinations.empty? | |
129 | settingsCombinations = computeSettingsCombinations(ast) | |
130 | else | |
131 | settingsCombinations = optionalSettingsCombiations[0] | |
132 | end | |
133 | ||
134 | settingsCombinations.each_with_index { | |
135 | | concreteSettings, index | | |
136 | forSettings(concreteSettings, ast) { | |
137 | | concreteSettings_, lowLevelAST, backend | | |
138 | yield concreteSettings, lowLevelAST, backend, index | |
139 | } | |
140 | } | |
141 | end | |
142 | ||
143 | # | |
144 | # cppSettingsTest(concreteSettings) | |
145 | # | |
146 | # Returns the C++ code used to test if we are in a configuration that | |
147 | # corresponds to the given concrete settings. | |
148 | # | |
149 | ||
150 | def cppSettingsTest(concreteSettings) | |
151 | "#if " + concreteSettings.to_a.collect{ | |
152 | | pair | | |
153 | (if pair[1] | |
154 | "" | |
155 | else | |
156 | "!" | |
157 | end) + "OFFLINE_ASM_" + pair[0] | |
158 | }.join(" && ") | |
159 | end | |
160 | ||
161 | # | |
162 | # isASTErroneous(ast) | |
163 | # | |
164 | # Tests to see if the AST claims that there is an error - i.e. if the | |
165 | # user's code, after settings resolution, has Error nodes. | |
166 | # | |
167 | ||
168 | def isASTErroneous(ast) | |
169 | not ast.filter(Error).empty? | |
170 | end | |
171 | ||
172 | # | |
173 | # assertConfiguration(concreteSettings) | |
174 | # | |
175 | # Emits a check that asserts that we're using the given configuration. | |
176 | # | |
177 | ||
178 | def assertConfiguration(concreteSettings) | |
179 | $output.puts cppSettingsTest(concreteSettings) | |
180 | $output.puts "#else" | |
181 | $output.puts "#error \"Configuration mismatch.\"" | |
182 | $output.puts "#endif" | |
183 | end | |
184 | ||
185 | # | |
186 | # emitCodeInConfiguration(concreteSettings, ast, backend) { | |
187 | # | concreteSettings, ast, backend | ... } | |
188 | # | |
189 | # Emits all relevant guards to see if the configuration holds and | |
190 | # calls the block if the configuration is not erroneous. | |
191 | # | |
192 | ||
193 | def emitCodeInConfiguration(concreteSettings, ast, backend) | |
81345200 A |
194 | Label.resetReferenced |
195 | ||
196 | if !$emitWinAsm | |
197 | $output.puts cppSettingsTest(concreteSettings) | |
198 | else | |
199 | if backend == "X86_WIN" | |
200 | $output.puts ".MODEL FLAT, C" | |
201 | end | |
202 | $output.puts "INCLUDE #{File.basename($output.path)}.sym" | |
203 | $output.puts "_TEXT SEGMENT" | |
204 | end | |
6fe7ccc8 A |
205 | |
206 | if isASTErroneous(ast) | |
207 | $output.puts "#error \"Invalid configuration.\"" | |
208 | elsif not WORKING_BACKENDS.include? backend | |
209 | $output.puts "#error \"This backend is not supported yet.\"" | |
210 | else | |
211 | yield concreteSettings, ast, backend | |
212 | end | |
213 | ||
81345200 A |
214 | if !$emitWinAsm |
215 | $output.puts "#endif" | |
216 | else | |
217 | $output.puts "_TEXT ENDS" | |
218 | $output.puts "END" | |
219 | ||
220 | # Write symbols needed by MASM | |
221 | File.open("#{File.basename($output.path)}.sym", "w") { | |
222 | | outp | | |
223 | Label.forReferencedExtern { | |
224 | | name | | |
225 | outp.puts "EXTERN #{name[1..-1]} : near" | |
226 | } | |
227 | } | |
228 | end | |
6fe7ccc8 A |
229 | end |
230 | ||
231 | # | |
232 | # emitCodeInAllConfigurations(ast) { | |
233 | # | concreteSettings, ast, backend, index | ... } | |
234 | # | |
235 | # Emits guard codes for all valid configurations, and calls the block | |
236 | # for those configurations that are valid and not erroneous. | |
237 | # | |
238 | ||
239 | def emitCodeInAllConfigurations(ast) | |
240 | forEachValidSettingsCombination(ast) { | |
241 | | concreteSettings, lowLevelAST, backend, index | | |
242 | $output.puts cppSettingsTest(concreteSettings) | |
243 | yield concreteSettings, lowLevelAST, backend, index | |
244 | $output.puts "#endif" | |
245 | } | |
246 | end | |
247 | ||
248 | ||
249 |