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
24require "config"
25require "ast"
26require "backends"
27require "parser"
28require "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
39def 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
60end
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
70def 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
90end
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
106def 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    }
122end
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
131def 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(" && ")
140end
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
149def isASTErroneous(ast)
150    not ast.filter(Error).empty?
151end
152
153#
154# assertConfiguration(concreteSettings)
155#
156# Emits a check that asserts that we're using the given configuration.
157#
158
159def assertConfiguration(concreteSettings)
160    $output.puts cppSettingsTest(concreteSettings)
161    $output.puts "#else"
162    $output.puts "#error \"Configuration mismatch.\""
163    $output.puts "#endif"
164end
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
174def emitCodeInConfiguration(concreteSettings, ast, backend)
175    $output.puts cppSettingsTest(concreteSettings)
176    
177    if isASTErroneous(ast)
178        $output.puts "#error \"Invalid configuration.\""
179    elsif not WORKING_BACKENDS.include? backend
180        $output.puts "#error \"This backend is not supported yet.\""
181    else
182        yield concreteSettings, ast, backend
183    end
184    
185    $output.puts "#endif"
186end
187
188#
189# emitCodeInAllConfigurations(ast) {
190#     | concreteSettings, ast, backend, index | ... }
191#
192# Emits guard codes for all valid configurations, and calls the block
193# for those configurations that are valid and not erroneous.
194#
195
196def emitCodeInAllConfigurations(ast)
197    forEachValidSettingsCombination(ast) {
198        | concreteSettings, lowLevelAST, backend, index |
199        $output.puts cppSettingsTest(concreteSettings)
200        yield concreteSettings, lowLevelAST, backend, index
201        $output.puts "#endif"
202    }
203end
204
205
206
207