1 # Copyright (C) 2011 Apple Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
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.
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.
31 # computeSettingsCombinations(ast) -> settingsCombiations
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).
39 def computeSettingsCombinations(ast
)
40 settingsCombinations
= []
42 def settingsCombinator(settingsCombinations
, mapSoFar
, remaining
)
44 settingsCombinations
<< mapSoFar
49 newMap
[remaining
[0]] = true
50 settingsCombinator(settingsCombinations
, newMap
, remaining
[1..-1])
53 newMap
[remaining
[0]] = false
54 settingsCombinator(settingsCombinations
, newMap
, remaining
[1..-1])
57 nonBackendSettings
= ast
.filter(Setting
).uniq
.collect
{ |v
| v
.name
}
58 nonBackendSettings
.delete_if
{
66 allBackendsFalse
[backend
] = false
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).
75 map
= allBackendsFalse
.clone
77 settingsCombinator(settingsCombinations
, map
, nonBackendSettings
)
84 # forSettings(concreteSettings, ast) {
85 # | concreteSettings, lowLevelAST, backend | ... }
87 # Determines if the settings combination is valid, and if so, calls
88 # the block with the information you need to generate code.
91 def forSettings(concreteSettings
, ast
)
92 # Check which architectures this combinator claims to support.
93 numClaimedBackends
= 0
97 if concreteSettings
[backend
]
98 raise if selectedBackend
99 selectedBackend
= backend
103 return unless isValidBackend
? selectedBackend
105 # Resolve the AST down to a low-level form (no macros or conditionals).
106 lowLevelAST
= ast
.resolveSettings(concreteSettings
)
108 yield concreteSettings
, lowLevelAST
, selectedBackend
112 # forEachValidSettingsCombination(ast) {
113 # | concreteSettings, ast, backend, index | ... }
115 # forEachValidSettingsCombination(ast, settingsCombinations) {
116 # | concreteSettings, ast, backend, index | ... }
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.
122 # The first form will call computeSettingsCombinations(ast) for you.
125 def forEachValidSettingsCombination(ast
, *optionalSettingsCombinations
)
126 raise if optionalSettingsCombinations
.size
> 1
128 if optionalSettingsCombinations
.empty
?
129 settingsCombinations
= computeSettingsCombinations(ast
)
131 settingsCombinations
= optionalSettingsCombiations
[0]
134 settingsCombinations
.each_with_index
{
135 | concreteSettings
, index
|
136 forSettings(concreteSettings
, ast
) {
137 | concreteSettings_
, lowLevelAST
, backend
|
138 yield concreteSettings
, lowLevelAST
, backend
, index
144 # cppSettingsTest(concreteSettings)
146 # Returns the C++ code used to test if we are in a configuration that
147 # corresponds to the given concrete settings.
150 def cppSettingsTest(concreteSettings
)
151 "#if " + concreteSettings
.to_a
.collect
{
157 end) + "OFFLINE_ASM_" + pair
[0]
162 # isASTErroneous(ast)
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.
168 def isASTErroneous(ast
)
169 not ast
.filter(Error
).empty
?
173 # assertConfiguration(concreteSettings)
175 # Emits a check that asserts that we're using the given configuration.
178 def assertConfiguration(concreteSettings
)
179 $output.puts
cppSettingsTest(concreteSettings
)
181 $output.puts
"#error \"Configuration mismatch.\""
182 $output.puts
"#endif"
186 # emitCodeInConfiguration(concreteSettings, ast, backend) {
187 # | concreteSettings, ast, backend | ... }
189 # Emits all relevant guards to see if the configuration holds and
190 # calls the block if the configuration is not erroneous.
193 def emitCodeInConfiguration(concreteSettings
, ast
, backend
)
194 Label
.resetReferenced
197 $output.puts
cppSettingsTest(concreteSettings
)
199 if backend
== "X86_WIN"
200 $output.puts
".MODEL FLAT, C"
202 $output.puts
"INCLUDE #{File.basename($output.path)}.sym"
203 $output.puts
"_TEXT SEGMENT"
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.\""
211 yield concreteSettings
, ast
, backend
215 $output.puts
"#endif"
217 $output.puts
"_TEXT ENDS"
220 # Write symbols needed by MASM
221 File
.open("#{File.basename($output.path)}.sym", "w") {
223 Label
.forReferencedExtern
{
225 outp
.puts
"EXTERN #{name[1..-1]} : near"
232 # emitCodeInAllConfigurations(ast) {
233 # | concreteSettings, ast, backend, index | ... }
235 # Emits guard codes for all valid configurations, and calls the block
236 # for those configurations that are valid and not erroneous.
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"