|  | # Copyright (C) 2011 Apple Inc. All rights reserved. | 
|  | # | 
|  | # Redistribution and use in source and binary forms, with or without | 
|  | # modification, are permitted provided that the following conditions | 
|  | # are met: | 
|  | # 1. Redistributions of source code must retain the above copyright | 
|  | #    notice, this list of conditions and the following disclaimer. | 
|  | # 2. Redistributions in binary form must reproduce the above copyright | 
|  | #    notice, this list of conditions and the following disclaimer in the | 
|  | #    documentation and/or other materials provided with the distribution. | 
|  | # | 
|  | # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 
|  | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | 
|  | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 
|  | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|  | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
|  | # THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | require "config" | 
|  | require "ast" | 
|  | require "backends" | 
|  | require "parser" | 
|  | require "transform" | 
|  |  | 
|  | # | 
|  | # computeSettingsCombinations(ast) -> settingsCombiations | 
|  | # | 
|  | # Computes an array of settings maps, where a settings map constitutes | 
|  | # a configuration for the assembly code being generated. The map | 
|  | # contains key value pairs where keys are settings names (strings) and | 
|  | # the values are booleans (true for enabled, false for disabled). | 
|  | # | 
|  |  | 
|  | def computeSettingsCombinations(ast) | 
|  | settingsCombinations = [] | 
|  |  | 
|  | def settingsCombinator(settingsCombinations, mapSoFar, remaining) | 
|  | if remaining.empty? | 
|  | settingsCombinations << mapSoFar | 
|  | return | 
|  | end | 
|  |  | 
|  | newMap = mapSoFar.dup | 
|  | newMap[remaining[0]] = true | 
|  | settingsCombinator(settingsCombinations, newMap, remaining[1..-1]) | 
|  |  | 
|  | newMap = mapSoFar.dup | 
|  | newMap[remaining[0]] = false | 
|  | settingsCombinator(settingsCombinations, newMap, remaining[1..-1]) | 
|  | end | 
|  |  | 
|  | nonBackendSettings = ast.filter(Setting).uniq.collect{ |v| v.name } | 
|  | nonBackendSettings.delete_if { | 
|  | | setting | | 
|  | isBackend? setting | 
|  | } | 
|  |  | 
|  | allBackendsFalse = {} | 
|  | BACKENDS.each { | 
|  | | backend | | 
|  | allBackendsFalse[backend] = false | 
|  | } | 
|  |  | 
|  | # This will create entries for invalid backends. That's fine. It's necessary | 
|  | # because it ensures that generate_offsets_extractor (which knows about valid | 
|  | # backends) has settings indices that are compatible with what asm will see | 
|  | # (asm doesn't know about valid backends). | 
|  | BACKENDS.each { | 
|  | | backend | | 
|  | map = allBackendsFalse.clone | 
|  | map[backend] = true | 
|  | settingsCombinator(settingsCombinations, map, nonBackendSettings) | 
|  | } | 
|  |  | 
|  | settingsCombinations | 
|  | end | 
|  |  | 
|  | # | 
|  | # forSettings(concreteSettings, ast) { | 
|  | #     | concreteSettings, lowLevelAST, backend | ... } | 
|  | # | 
|  | # Determines if the settings combination is valid, and if so, calls | 
|  | # the block with the information you need to generate code. | 
|  | # | 
|  |  | 
|  | def forSettings(concreteSettings, ast) | 
|  | # Check which architectures this combinator claims to support. | 
|  | selectedBackend = nil | 
|  | BACKENDS.each { | 
|  | | backend | | 
|  | if concreteSettings[backend] | 
|  | raise if selectedBackend | 
|  | selectedBackend = backend | 
|  | end | 
|  | } | 
|  |  | 
|  | return unless isValidBackend? selectedBackend | 
|  |  | 
|  | # Resolve the AST down to a low-level form (no macros or conditionals). | 
|  | lowLevelAST = ast.resolveSettings(concreteSettings) | 
|  |  | 
|  | yield concreteSettings, lowLevelAST, selectedBackend | 
|  | end | 
|  |  | 
|  | # | 
|  | # forEachValidSettingsCombination(ast) { | 
|  | #     | concreteSettings, ast, backend, index | ... } | 
|  | # | 
|  | # forEachValidSettingsCombination(ast, settingsCombinations) { | 
|  | #     | concreteSettings, ast, backend, index | ... } | 
|  | # | 
|  | # Executes the given block for each valid settings combination in the | 
|  | # settings map. The ast passed into the block is resolved | 
|  | # (ast.resolve) against the settings. | 
|  | # | 
|  | # The first form will call computeSettingsCombinations(ast) for you. | 
|  | # | 
|  |  | 
|  | def forEachValidSettingsCombination(ast, *optionalSettingsCombinations) | 
|  | raise if optionalSettingsCombinations.size > 1 | 
|  |  | 
|  | if optionalSettingsCombinations.empty? | 
|  | settingsCombinations = computeSettingsCombinations(ast) | 
|  | else | 
|  | settingsCombinations = optionalSettingsCombiations[0] | 
|  | end | 
|  |  | 
|  | settingsCombinations.each_with_index { | 
|  | | concreteSettings, index | | 
|  | forSettings(concreteSettings, ast) { | 
|  | | concreteSettings_, lowLevelAST, backend | | 
|  | yield concreteSettings, lowLevelAST, backend, index | 
|  | } | 
|  | } | 
|  | end | 
|  |  | 
|  | # | 
|  | # cppSettingsTest(concreteSettings) | 
|  | # | 
|  | # Returns the C++ code used to test if we are in a configuration that | 
|  | # corresponds to the given concrete settings. | 
|  | # | 
|  |  | 
|  | def cppSettingsTest(concreteSettings) | 
|  | "#if " + concreteSettings.to_a.collect{ | 
|  | | pair | | 
|  | (if pair[1] | 
|  | "" | 
|  | else | 
|  | "!" | 
|  | end) + "OFFLINE_ASM_" + pair[0] | 
|  | }.join(" && ") | 
|  | end | 
|  |  | 
|  | # | 
|  | # isASTErroneous(ast) | 
|  | # | 
|  | # Tests to see if the AST claims that there is an error - i.e. if the | 
|  | # user's code, after settings resolution, has Error nodes. | 
|  | # | 
|  |  | 
|  | def isASTErroneous(ast) | 
|  | not ast.demacroify({}).filter(Error).empty? | 
|  | end | 
|  |  | 
|  | # | 
|  | # assertConfiguration(concreteSettings) | 
|  | # | 
|  | # Emits a check that asserts that we're using the given configuration. | 
|  | # | 
|  |  | 
|  | def assertConfiguration(concreteSettings) | 
|  | $output.puts cppSettingsTest(concreteSettings) | 
|  | $output.puts "#else" | 
|  | $output.puts "#error \"Configuration mismatch.\"" | 
|  | $output.puts "#endif" | 
|  | end | 
|  |  | 
|  | # | 
|  | # emitCodeInConfiguration(concreteSettings, ast, backend) { | 
|  | #     | concreteSettings, ast, backend | ... } | 
|  | # | 
|  | # Emits all relevant guards to see if the configuration holds and | 
|  | # calls the block if the configuration is not erroneous. | 
|  | # | 
|  |  | 
|  | def emitCodeInConfiguration(concreteSettings, ast, backend) | 
|  | Label.resetReferenced | 
|  |  | 
|  | if !$emitWinAsm | 
|  | $output.puts cppSettingsTest(concreteSettings) | 
|  | else | 
|  | if backend == "X86_WIN" | 
|  | $output.puts ".MODEL FLAT, C" | 
|  | end | 
|  | $output.puts "INCLUDE #{File.basename($output.path)}.sym" | 
|  | $output.puts "_TEXT SEGMENT" | 
|  | end | 
|  |  | 
|  | if isASTErroneous(ast) | 
|  | $output.puts "#error \"Invalid configuration. Error at: #{ast.filter(Error)[0].codeOrigin}\"" | 
|  | elsif not WORKING_BACKENDS.include? backend | 
|  | $output.puts "#error \"This backend is not supported yet.\"" | 
|  | else | 
|  | yield concreteSettings, ast, backend | 
|  | end | 
|  |  | 
|  | if !$emitWinAsm | 
|  | $output.puts "#endif" | 
|  | else | 
|  | $output.puts "_TEXT ENDS" | 
|  | $output.puts "END" | 
|  |  | 
|  | # Write symbols needed by MASM | 
|  | File.open("#{File.basename($output.path)}.sym", "w") { | 
|  | | outp | | 
|  | Label.forReferencedExtern { | 
|  | | name | | 
|  | outp.puts "EXTERN #{name[1..-1]} : near" | 
|  | } | 
|  | } | 
|  | end | 
|  | end | 
|  |  | 
|  | # | 
|  | # emitCodeInAllConfigurations(ast) { | 
|  | #     | concreteSettings, ast, backend, index | ... } | 
|  | # | 
|  | # Emits guard codes for all valid configurations, and calls the block | 
|  | # for those configurations that are valid and not erroneous. | 
|  | # | 
|  |  | 
|  | def emitCodeInAllConfigurations(ast) | 
|  | forEachValidSettingsCombination(ast) { | 
|  | | concreteSettings, lowLevelAST, backend, index | | 
|  | $output.puts cppSettingsTest(concreteSettings) | 
|  | yield concreteSettings, lowLevelAST, backend, index | 
|  | $output.puts "#endif" | 
|  | } | 
|  | end | 
|  |  | 
|  |  | 
|  |  |