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 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 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 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 210end 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 220def 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 } 227end 228 229 230 231