]>
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 | ||
24 | require "ast" | |
25 | require "backends" | |
26 | require "parser" | |
27 | require "transform" | |
28 | ||
29 | # | |
30 | # computeSettingsCombinations(ast) -> settingsCombiations | |
31 | # | |
32 | # Computes an array of settings maps, where a settings map constitutes | |
33 | # a configuration for the assembly code being generated. The map | |
34 | # contains key value pairs where keys are settings names (strings) and | |
35 | # the values are booleans (true for enabled, false for disabled). | |
36 | # | |
37 | ||
38 | def computeSettingsCombinations(ast) | |
39 | settingsCombinations = [] | |
40 | ||
41 | def settingsCombinator(settingsCombinations, mapSoFar, remaining) | |
42 | if remaining.empty? | |
43 | settingsCombinations << mapSoFar | |
44 | return | |
45 | end | |
46 | ||
47 | newMap = mapSoFar.dup | |
48 | newMap[remaining[0]] = true | |
49 | settingsCombinator(settingsCombinations, newMap, remaining[1..-1]) | |
50 | ||
51 | newMap = mapSoFar.dup | |
52 | newMap[remaining[0]] = false | |
53 | settingsCombinator(settingsCombinations, newMap, remaining[1..-1]) | |
54 | end | |
55 | ||
56 | settingsCombinator(settingsCombinations, {}, (ast.filter(Setting).uniq.collect{|v| v.name} + BACKENDS).uniq) | |
57 | ||
58 | settingsCombinations | |
59 | end | |
60 | ||
61 | # | |
62 | # forSettings(concreteSettings, ast) { | |
63 | # | concreteSettings, lowLevelAST, backend | ... } | |
64 | # | |
65 | # Determines if the settings combination is valid, and if so, calls | |
66 | # the block with the information you need to generate code. | |
67 | # | |
68 | ||
69 | def forSettings(concreteSettings, ast) | |
70 | # Check which architectures this combinator claims to support. | |
71 | numClaimedBackends = 0 | |
72 | selectedBackend = nil | |
73 | BACKENDS.each { | |
74 | | backend | | |
75 | isSupported = concreteSettings[backend] | |
76 | raise unless isSupported != nil | |
77 | numClaimedBackends += if isSupported then 1 else 0 end | |
78 | if isSupported | |
79 | selectedBackend = backend | |
80 | end | |
81 | } | |
82 | ||
83 | return if numClaimedBackends > 1 | |
84 | ||
85 | # Resolve the AST down to a low-level form (no macros or conditionals). | |
86 | lowLevelAST = ast.resolveSettings(concreteSettings) | |
87 | ||
88 | yield concreteSettings, lowLevelAST, selectedBackend | |
89 | end | |
90 | ||
91 | # | |
92 | # forEachValidSettingsCombination(ast) { | |
93 | # | concreteSettings, ast, backend, index | ... } | |
94 | # | |
95 | # forEachValidSettingsCombination(ast, settingsCombinations) { | |
96 | # | concreteSettings, ast, backend, index | ... } | |
97 | # | |
98 | # Executes the given block for each valid settings combination in the | |
99 | # settings map. The ast passed into the block is resolved | |
100 | # (ast.resolve) against the settings. | |
101 | # | |
102 | # The first form will call computeSettingsCombinations(ast) for you. | |
103 | # | |
104 | ||
105 | def forEachValidSettingsCombination(ast, *optionalSettingsCombinations) | |
106 | raise if optionalSettingsCombinations.size > 1 | |
107 | ||
108 | if optionalSettingsCombinations.empty? | |
109 | settingsCombinations = computeSettingsCombinations(ast) | |
110 | else | |
111 | settingsCombinations = optionalSettingsCombiations[0] | |
112 | end | |
113 | ||
114 | settingsCombinations.each_with_index { | |
115 | | concreteSettings, index | | |
116 | forSettings(concreteSettings, ast) { | |
117 | | concreteSettings_, lowLevelAST, backend | | |
118 | yield concreteSettings, lowLevelAST, backend, index | |
119 | } | |
120 | } | |
121 | end | |
122 | ||
123 | # | |
124 | # cppSettingsTest(concreteSettings) | |
125 | # | |
126 | # Returns the C++ code used to test if we are in a configuration that | |
127 | # corresponds to the given concrete settings. | |
128 | # | |
129 | ||
130 | def cppSettingsTest(concreteSettings) | |
131 | "#if " + concreteSettings.to_a.collect{ | |
132 | | pair | | |
133 | (if pair[1] | |
134 | "" | |
135 | else | |
136 | "!" | |
137 | end) + "OFFLINE_ASM_" + pair[0] | |
138 | }.join(" && ") | |
139 | end | |
140 | ||
141 | # | |
142 | # isASTErroneous(ast) | |
143 | # | |
144 | # Tests to see if the AST claims that there is an error - i.e. if the | |
145 | # user's code, after settings resolution, has Error nodes. | |
146 | # | |
147 | ||
148 | def isASTErroneous(ast) | |
149 | not ast.filter(Error).empty? | |
150 | end | |
151 | ||
152 | # | |
153 | # assertConfiguration(concreteSettings) | |
154 | # | |
155 | # Emits a check that asserts that we're using the given configuration. | |
156 | # | |
157 | ||
158 | def assertConfiguration(concreteSettings) | |
159 | $output.puts cppSettingsTest(concreteSettings) | |
160 | $output.puts "#else" | |
161 | $output.puts "#error \"Configuration mismatch.\"" | |
162 | $output.puts "#endif" | |
163 | end | |
164 | ||
165 | # | |
166 | # emitCodeInConfiguration(concreteSettings, ast, backend) { | |
167 | # | concreteSettings, ast, backend | ... } | |
168 | # | |
169 | # Emits all relevant guards to see if the configuration holds and | |
170 | # calls the block if the configuration is not erroneous. | |
171 | # | |
172 | ||
173 | def emitCodeInConfiguration(concreteSettings, ast, backend) | |
174 | $output.puts cppSettingsTest(concreteSettings) | |
175 | ||
176 | if isASTErroneous(ast) | |
177 | $output.puts "#error \"Invalid configuration.\"" | |
178 | elsif not WORKING_BACKENDS.include? backend | |
179 | $output.puts "#error \"This backend is not supported yet.\"" | |
180 | else | |
181 | yield concreteSettings, ast, backend | |
182 | end | |
183 | ||
184 | $output.puts "#endif" | |
185 | end | |
186 | ||
187 | # | |
188 | # emitCodeInAllConfigurations(ast) { | |
189 | # | concreteSettings, ast, backend, index | ... } | |
190 | # | |
191 | # Emits guard codes for all valid configurations, and calls the block | |
192 | # for those configurations that are valid and not erroneous. | |
193 | # | |
194 | ||
195 | def emitCodeInAllConfigurations(ast) | |
196 | forEachValidSettingsCombination(ast) { | |
197 | | concreteSettings, lowLevelAST, backend, index | | |
198 | $output.puts cppSettingsTest(concreteSettings) | |
199 | yield concreteSettings, lowLevelAST, backend, index | |
200 | $output.puts "#endif" | |
201 | } | |
202 | end | |
203 | ||
204 | ||
205 |