settings.rb 6.4 KB

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