]>
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 | ||
57 | settingsCombinator(settingsCombinations, {}, (ast.filter(Setting).uniq.collect{|v| v.name} + BACKENDS).uniq) | |
58 | ||
59 | settingsCombinations | |
60 | end | |
61 | ||
62 | # | |
63 | # forSettings(concreteSettings, ast) { | |
64 | # | concreteSettings, lowLevelAST, backend | ... } | |
65 | # | |
66 | # Determines if the settings combination is valid, and if so, calls | |
67 | # the block with the information you need to generate code. | |
68 | # | |
69 | ||
70 | def forSettings(concreteSettings, ast) | |
71 | # Check which architectures this combinator claims to support. | |
72 | numClaimedBackends = 0 | |
73 | selectedBackend = nil | |
74 | BACKENDS.each { | |
75 | | backend | | |
76 | isSupported = concreteSettings[backend] | |
77 | raise unless isSupported != nil | |
78 | numClaimedBackends += if isSupported then 1 else 0 end | |
79 | if isSupported | |
80 | selectedBackend = backend | |
81 | end | |
82 | } | |
83 | ||
84 | return if numClaimedBackends > 1 | |
85 | ||
86 | # Resolve the AST down to a low-level form (no macros or conditionals). | |
87 | lowLevelAST = ast.resolveSettings(concreteSettings) | |
88 | ||
89 | yield concreteSettings, lowLevelAST, selectedBackend | |
90 | end | |
91 | ||
92 | # | |
93 | # forEachValidSettingsCombination(ast) { | |
94 | # | concreteSettings, ast, backend, index | ... } | |
95 | # | |
96 | # forEachValidSettingsCombination(ast, settingsCombinations) { | |
97 | # | concreteSettings, ast, backend, index | ... } | |
98 | # | |
99 | # Executes the given block for each valid settings combination in the | |
100 | # settings map. The ast passed into the block is resolved | |
101 | # (ast.resolve) against the settings. | |
102 | # | |
103 | # The first form will call computeSettingsCombinations(ast) for you. | |
104 | # | |
105 | ||
106 | def forEachValidSettingsCombination(ast, *optionalSettingsCombinations) | |
107 | raise if optionalSettingsCombinations.size > 1 | |
108 | ||
109 | if optionalSettingsCombinations.empty? | |
110 | settingsCombinations = computeSettingsCombinations(ast) | |
111 | else | |
112 | settingsCombinations = optionalSettingsCombiations[0] | |
113 | end | |
114 | ||
115 | settingsCombinations.each_with_index { | |
116 | | concreteSettings, index | | |
117 | forSettings(concreteSettings, ast) { | |
118 | | concreteSettings_, lowLevelAST, backend | | |
119 | yield concreteSettings, lowLevelAST, backend, index | |
120 | } | |
121 | } | |
122 | end | |
123 | ||
124 | # | |
125 | # cppSettingsTest(concreteSettings) | |
126 | # | |
127 | # Returns the C++ code used to test if we are in a configuration that | |
128 | # corresponds to the given concrete settings. | |
129 | # | |
130 | ||
131 | def cppSettingsTest(concreteSettings) | |
132 | "#if " + concreteSettings.to_a.collect{ | |
133 | | pair | | |
134 | (if pair[1] | |
135 | "" | |
136 | else | |
137 | "!" | |
138 | end) + "OFFLINE_ASM_" + pair[0] | |
139 | }.join(" && ") | |
140 | end | |
141 | ||
142 | # | |
143 | # isASTErroneous(ast) | |
144 | # | |
145 | # Tests to see if the AST claims that there is an error - i.e. if the | |
146 | # user's code, after settings resolution, has Error nodes. | |
147 | # | |
148 | ||
149 | def isASTErroneous(ast) | |
150 | not ast.filter(Error).empty? | |
151 | end | |
152 | ||
153 | # | |
154 | # assertConfiguration(concreteSettings) | |
155 | # | |
156 | # Emits a check that asserts that we're using the given configuration. | |
157 | # | |
158 | ||
159 | def assertConfiguration(concreteSettings) | |
160 | $output.puts cppSettingsTest(concreteSettings) | |
161 | $output.puts "#else" | |
162 | $output.puts "#error \"Configuration mismatch.\"" | |
163 | $output.puts "#endif" | |
164 | end | |
165 | ||
166 | # | |
167 | # emitCodeInConfiguration(concreteSettings, ast, backend) { | |
168 | # | concreteSettings, ast, backend | ... } | |
169 | # | |
170 | # Emits all relevant guards to see if the configuration holds and | |
171 | # calls the block if the configuration is not erroneous. | |
172 | # | |
173 | ||
174 | def emitCodeInConfiguration(concreteSettings, ast, backend) | |
81345200 A |
175 | Label.resetReferenced |
176 | ||
177 | if !$emitWinAsm | |
178 | $output.puts cppSettingsTest(concreteSettings) | |
179 | else | |
180 | if backend == "X86_WIN" | |
181 | $output.puts ".MODEL FLAT, C" | |
182 | end | |
183 | $output.puts "INCLUDE #{File.basename($output.path)}.sym" | |
184 | $output.puts "_TEXT SEGMENT" | |
185 | end | |
6fe7ccc8 A |
186 | |
187 | if isASTErroneous(ast) | |
188 | $output.puts "#error \"Invalid configuration.\"" | |
189 | elsif not WORKING_BACKENDS.include? backend | |
190 | $output.puts "#error \"This backend is not supported yet.\"" | |
191 | else | |
192 | yield concreteSettings, ast, backend | |
193 | end | |
194 | ||
81345200 A |
195 | if !$emitWinAsm |
196 | $output.puts "#endif" | |
197 | else | |
198 | $output.puts "_TEXT ENDS" | |
199 | $output.puts "END" | |
200 | ||
201 | # Write symbols needed by MASM | |
202 | File.open("#{File.basename($output.path)}.sym", "w") { | |
203 | | outp | | |
204 | Label.forReferencedExtern { | |
205 | | name | | |
206 | outp.puts "EXTERN #{name[1..-1]} : near" | |
207 | } | |
208 | } | |
209 | end | |
6fe7ccc8 A |
210 | end |
211 | ||
212 | # | |
213 | # emitCodeInAllConfigurations(ast) { | |
214 | # | concreteSettings, ast, backend, index | ... } | |
215 | # | |
216 | # Emits guard codes for all valid configurations, and calls the block | |
217 | # for those configurations that are valid and not erroneous. | |
218 | # | |
219 | ||
220 | def emitCodeInAllConfigurations(ast) | |
221 | forEachValidSettingsCombination(ast) { | |
222 | | concreteSettings, lowLevelAST, backend, index | | |
223 | $output.puts cppSettingsTest(concreteSettings) | |
224 | yield concreteSettings, lowLevelAST, backend, index | |
225 | $output.puts "#endif" | |
226 | } | |
227 | end | |
228 | ||
229 | ||
230 |