bones-compiler 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. data/CHANGELOG +117 -0
  2. data/LICENSE +9 -0
  3. data/README.rdoc +126 -0
  4. data/Rakefile +107 -0
  5. data/VERSION +1 -0
  6. data/bin/bones +20 -0
  7. data/examples/applications/ffos.c +552 -0
  8. data/examples/benchmarks/2mm.c +70 -0
  9. data/examples/benchmarks/3mm.c +81 -0
  10. data/examples/benchmarks/adi.c +81 -0
  11. data/examples/benchmarks/atax.c +65 -0
  12. data/examples/benchmarks/bicg.c +67 -0
  13. data/examples/benchmarks/cholesky.c +64 -0
  14. data/examples/benchmarks/common.h +168 -0
  15. data/examples/benchmarks/correlation.c +97 -0
  16. data/examples/benchmarks/covariance.c +77 -0
  17. data/examples/benchmarks/doitgen.c +63 -0
  18. data/examples/benchmarks/durbin.c +76 -0
  19. data/examples/benchmarks/dynprog.c +67 -0
  20. data/examples/benchmarks/fdtd-2d-apml.c +114 -0
  21. data/examples/benchmarks/fdtd-2d.c +74 -0
  22. data/examples/benchmarks/floyd-warshall.c +50 -0
  23. data/examples/benchmarks/gemm.c +69 -0
  24. data/examples/benchmarks/gemver.c +89 -0
  25. data/examples/benchmarks/gesummv.c +64 -0
  26. data/examples/benchmarks/gramschmidt.c +84 -0
  27. data/examples/benchmarks/jacobi-1d-imper.c +55 -0
  28. data/examples/benchmarks/jacobi-2d-imper.c +61 -0
  29. data/examples/benchmarks/lu.c +57 -0
  30. data/examples/benchmarks/ludcmp.c +91 -0
  31. data/examples/benchmarks/mvt.c +65 -0
  32. data/examples/benchmarks/overview.txt +38 -0
  33. data/examples/benchmarks/reg_detect.c +82 -0
  34. data/examples/benchmarks/saxpy.c +45 -0
  35. data/examples/benchmarks/seidel-2d.c +51 -0
  36. data/examples/benchmarks/symm.c +74 -0
  37. data/examples/benchmarks/syr2k.c +65 -0
  38. data/examples/benchmarks/syrk.c +62 -0
  39. data/examples/benchmarks/trisolv.c +57 -0
  40. data/examples/benchmarks/trmm.c +57 -0
  41. data/examples/chunk/example1.c +54 -0
  42. data/examples/chunk/example2.c +44 -0
  43. data/examples/chunk/example3.c +59 -0
  44. data/examples/chunk/example4.c +55 -0
  45. data/examples/chunk/example5.c +52 -0
  46. data/examples/element/example1.c +46 -0
  47. data/examples/element/example10.c +50 -0
  48. data/examples/element/example11.c +47 -0
  49. data/examples/element/example12.c +56 -0
  50. data/examples/element/example2.c +46 -0
  51. data/examples/element/example3.c +58 -0
  52. data/examples/element/example4.c +49 -0
  53. data/examples/element/example5.c +56 -0
  54. data/examples/element/example6.c +46 -0
  55. data/examples/element/example7.c +54 -0
  56. data/examples/element/example8.c +45 -0
  57. data/examples/element/example9.c +48 -0
  58. data/examples/neighbourhood/example1.c +54 -0
  59. data/examples/neighbourhood/example2.c +55 -0
  60. data/examples/neighbourhood/example3.c +82 -0
  61. data/examples/neighbourhood/example4.c +52 -0
  62. data/examples/shared/example1.c +45 -0
  63. data/examples/shared/example2.c +51 -0
  64. data/examples/shared/example3.c +55 -0
  65. data/examples/shared/example4.c +52 -0
  66. data/examples/shared/example5.c +48 -0
  67. data/lib/bones.rb +266 -0
  68. data/lib/bones/algorithm.rb +541 -0
  69. data/lib/bones/engine.rb +386 -0
  70. data/lib/bones/preprocessor.rb +161 -0
  71. data/lib/bones/species.rb +196 -0
  72. data/lib/bones/structure.rb +94 -0
  73. data/lib/bones/variable.rb +169 -0
  74. data/lib/bones/variablelist.rb +72 -0
  75. data/lib/castaddon.rb +27 -0
  76. data/lib/castaddon/index.rb +40 -0
  77. data/lib/castaddon/node.rb +753 -0
  78. data/lib/castaddon/type.rb +37 -0
  79. data/skeletons/CPU-C/common/epilogue.c +0 -0
  80. data/skeletons/CPU-C/common/globals.c +17 -0
  81. data/skeletons/CPU-C/common/globals_kernel.c +1 -0
  82. data/skeletons/CPU-C/common/header.c +0 -0
  83. data/skeletons/CPU-C/common/mem_copy_D2H.c +0 -0
  84. data/skeletons/CPU-C/common/mem_copy_H2D.c +0 -0
  85. data/skeletons/CPU-C/common/mem_epilogue.c +0 -0
  86. data/skeletons/CPU-C/common/mem_prologue.c +3 -0
  87. data/skeletons/CPU-C/common/prologue.c +0 -0
  88. data/skeletons/CPU-C/common/timer_1_start.c +0 -0
  89. data/skeletons/CPU-C/common/timer_1_stop.c +0 -0
  90. data/skeletons/CPU-C/common/timer_2_start.c +20 -0
  91. data/skeletons/CPU-C/common/timer_2_stop.c +8 -0
  92. data/skeletons/CPU-C/kernel/default.host.c +3 -0
  93. data/skeletons/CPU-C/kernel/default.kernel.c +15 -0
  94. data/skeletons/CPU-C/skeletons.txt +24 -0
  95. data/skeletons/CPU-OPENCL-AMD/common/epilogue.c +6 -0
  96. data/skeletons/CPU-OPENCL-AMD/common/globals.c +155 -0
  97. data/skeletons/CPU-OPENCL-AMD/common/globals_kernel.c +4 -0
  98. data/skeletons/CPU-OPENCL-AMD/common/header.c +0 -0
  99. data/skeletons/CPU-OPENCL-AMD/common/mem_copy_D2H.c +8 -0
  100. data/skeletons/CPU-OPENCL-AMD/common/mem_copy_H2D.c +4 -0
  101. data/skeletons/CPU-OPENCL-AMD/common/mem_epilogue.c +3 -0
  102. data/skeletons/CPU-OPENCL-AMD/common/mem_prologue.c +6 -0
  103. data/skeletons/CPU-OPENCL-AMD/common/prologue.c +24 -0
  104. data/skeletons/CPU-OPENCL-AMD/common/timer_1_start.c +5 -0
  105. data/skeletons/CPU-OPENCL-AMD/common/timer_1_stop.c +9 -0
  106. data/skeletons/CPU-OPENCL-AMD/common/timer_2_start.c +16 -0
  107. data/skeletons/CPU-OPENCL-AMD/common/timer_2_stop.c +11 -0
  108. data/skeletons/CPU-OPENCL-AMD/kernel/D-element-to-1-shared.host.c +67 -0
  109. data/skeletons/CPU-OPENCL-AMD/kernel/D-element-to-1-shared.kernel.cl +72 -0
  110. data/skeletons/CPU-OPENCL-AMD/kernel/default.host.c +14 -0
  111. data/skeletons/CPU-OPENCL-AMD/kernel/default.kernel.cl +13 -0
  112. data/skeletons/CPU-OPENCL-AMD/skeletons.txt +26 -0
  113. data/skeletons/CPU-OPENCL-INTEL/common/epilogue.c +3 -0
  114. data/skeletons/CPU-OPENCL-INTEL/common/globals.c +154 -0
  115. data/skeletons/CPU-OPENCL-INTEL/common/globals_kernel.c +4 -0
  116. data/skeletons/CPU-OPENCL-INTEL/common/header.c +31 -0
  117. data/skeletons/CPU-OPENCL-INTEL/common/mem_copy_D2H.c +5 -0
  118. data/skeletons/CPU-OPENCL-INTEL/common/mem_copy_H2D.c +3 -0
  119. data/skeletons/CPU-OPENCL-INTEL/common/mem_epilogue.c +3 -0
  120. data/skeletons/CPU-OPENCL-INTEL/common/mem_prologue.c +4 -0
  121. data/skeletons/CPU-OPENCL-INTEL/common/prologue.c +24 -0
  122. data/skeletons/CPU-OPENCL-INTEL/common/timer_1_start.c +5 -0
  123. data/skeletons/CPU-OPENCL-INTEL/common/timer_1_stop.c +9 -0
  124. data/skeletons/CPU-OPENCL-INTEL/common/timer_2_start.c +16 -0
  125. data/skeletons/CPU-OPENCL-INTEL/common/timer_2_stop.c +11 -0
  126. data/skeletons/CPU-OPENCL-INTEL/kernel/D-element-to-1-shared.host.c +67 -0
  127. data/skeletons/CPU-OPENCL-INTEL/kernel/D-element-to-1-shared.kernel.cl +72 -0
  128. data/skeletons/CPU-OPENCL-INTEL/kernel/default.host.c +14 -0
  129. data/skeletons/CPU-OPENCL-INTEL/kernel/default.kernel.cl +13 -0
  130. data/skeletons/CPU-OPENCL-INTEL/skeletons.txt +26 -0
  131. data/skeletons/CPU-OPENMP/common/epilogue.c +0 -0
  132. data/skeletons/CPU-OPENMP/common/globals.c +37 -0
  133. data/skeletons/CPU-OPENMP/common/globals_kernel.c +6 -0
  134. data/skeletons/CPU-OPENMP/common/header.c +0 -0
  135. data/skeletons/CPU-OPENMP/common/mem_copy_D2H.c +0 -0
  136. data/skeletons/CPU-OPENMP/common/mem_copy_H2D.c +0 -0
  137. data/skeletons/CPU-OPENMP/common/mem_epilogue.c +0 -0
  138. data/skeletons/CPU-OPENMP/common/mem_prologue.c +3 -0
  139. data/skeletons/CPU-OPENMP/common/prologue.c +0 -0
  140. data/skeletons/CPU-OPENMP/common/timer_1_start.c +12 -0
  141. data/skeletons/CPU-OPENMP/common/timer_1_stop.c +0 -0
  142. data/skeletons/CPU-OPENMP/common/timer_2_start.c +18 -0
  143. data/skeletons/CPU-OPENMP/common/timer_2_stop.c +8 -0
  144. data/skeletons/CPU-OPENMP/kernel/D-element-to-1-shared.host.c +27 -0
  145. data/skeletons/CPU-OPENMP/kernel/D-element-to-1-shared.kernel.c +46 -0
  146. data/skeletons/CPU-OPENMP/kernel/default.host.c +11 -0
  147. data/skeletons/CPU-OPENMP/kernel/default.kernel.c +18 -0
  148. data/skeletons/CPU-OPENMP/skeletons.txt +26 -0
  149. data/skeletons/GPU-CUDA/common/epilogue.c +0 -0
  150. data/skeletons/GPU-CUDA/common/globals.c +31 -0
  151. data/skeletons/GPU-CUDA/common/globals_kernel.c +4 -0
  152. data/skeletons/GPU-CUDA/common/header.c +0 -0
  153. data/skeletons/GPU-CUDA/common/mem_copy_D2H.c +3 -0
  154. data/skeletons/GPU-CUDA/common/mem_copy_H2D.c +3 -0
  155. data/skeletons/GPU-CUDA/common/mem_epilogue.c +3 -0
  156. data/skeletons/GPU-CUDA/common/mem_prologue.c +5 -0
  157. data/skeletons/GPU-CUDA/common/prologue.c +6 -0
  158. data/skeletons/GPU-CUDA/common/timer_1_start.c +6 -0
  159. data/skeletons/GPU-CUDA/common/timer_1_stop.c +10 -0
  160. data/skeletons/GPU-CUDA/common/timer_2_start.c +6 -0
  161. data/skeletons/GPU-CUDA/common/timer_2_stop.c +10 -0
  162. data/skeletons/GPU-CUDA/kernel/2xN-N-chunk-1-N-to-D-element.host.c +3 -0
  163. data/skeletons/GPU-CUDA/kernel/2xN-N-chunk-1-N-to-D-element.kernel.cu +105 -0
  164. data/skeletons/GPU-CUDA/kernel/D-element-to-1-shared.host.c +3 -0
  165. data/skeletons/GPU-CUDA/kernel/D-element-to-1-shared.kernel.cu +119 -0
  166. data/skeletons/GPU-CUDA/kernel/D-element-to-N-shared.host.c +3 -0
  167. data/skeletons/GPU-CUDA/kernel/D-element-to-N-shared.kernel.cu +166 -0
  168. data/skeletons/GPU-CUDA/kernel/N-N-chunk-1-N-to-D-element.host.c +3 -0
  169. data/skeletons/GPU-CUDA/kernel/N-N-chunk-1-N-to-D-element.kernel.cu +69 -0
  170. data/skeletons/GPU-CUDA/kernel/N-neighbourhood-N-to-N-element.host.c +3 -0
  171. data/skeletons/GPU-CUDA/kernel/N-neighbourhood-N-to-N-element.kernel.cu +42 -0
  172. data/skeletons/GPU-CUDA/kernel/default.host.c +3 -0
  173. data/skeletons/GPU-CUDA/kernel/default.kernel.cu +28 -0
  174. data/skeletons/GPU-CUDA/skeletons.txt +30 -0
  175. data/skeletons/GPU-OPENCL-AMD/common/epilogue.c +3 -0
  176. data/skeletons/GPU-OPENCL-AMD/common/globals.c +155 -0
  177. data/skeletons/GPU-OPENCL-AMD/common/globals_kernel.c +4 -0
  178. data/skeletons/GPU-OPENCL-AMD/common/header.c +0 -0
  179. data/skeletons/GPU-OPENCL-AMD/common/mem_copy_D2H.c +4 -0
  180. data/skeletons/GPU-OPENCL-AMD/common/mem_copy_H2D.c +4 -0
  181. data/skeletons/GPU-OPENCL-AMD/common/mem_epilogue.c +3 -0
  182. data/skeletons/GPU-OPENCL-AMD/common/mem_prologue.c +3 -0
  183. data/skeletons/GPU-OPENCL-AMD/common/prologue.c +24 -0
  184. data/skeletons/GPU-OPENCL-AMD/common/timer_1_start.c +5 -0
  185. data/skeletons/GPU-OPENCL-AMD/common/timer_1_stop.c +9 -0
  186. data/skeletons/GPU-OPENCL-AMD/common/timer_2_start.c +4 -0
  187. data/skeletons/GPU-OPENCL-AMD/common/timer_2_stop.c +11 -0
  188. data/skeletons/GPU-OPENCL-AMD/kernel/D-element-to-1-shared.host.c +67 -0
  189. data/skeletons/GPU-OPENCL-AMD/kernel/D-element-to-1-shared.kernel.cl +72 -0
  190. data/skeletons/GPU-OPENCL-AMD/kernel/default.host.c +14 -0
  191. data/skeletons/GPU-OPENCL-AMD/kernel/default.kernel.cl +13 -0
  192. data/skeletons/GPU-OPENCL-AMD/skeletons.txt +26 -0
  193. data/skeletons/verification/header.c +2 -0
  194. data/skeletons/verification/timer_start.c +4 -0
  195. data/skeletons/verification/timer_stop.c +6 -0
  196. data/skeletons/verification/verify_results.c +23 -0
  197. data/test/bones/test_algorithm.rb +40 -0
  198. data/test/bones/test_common.rb +54 -0
  199. data/test/bones/test_preprocessor.rb +46 -0
  200. data/test/bones/test_species.rb +21 -0
  201. data/test/bones/test_variable.rb +84 -0
  202. data/test/test_helper.rb +106 -0
  203. metadata +303 -0
@@ -0,0 +1,72 @@
1
+
2
+ module Bones
3
+ # This class is based on the standard Array class. It is
4
+ # meant to contain a list of elements of the Variable class.
5
+ # In that sense, using the Array class will suffice. However,
6
+ # this class extends the list with a small number of addi-
7
+ # tional methods. These methods involve selecting a subset
8
+ # of the list or sorting the list.
9
+ class Variablelist < Array
10
+ attr_accessor :representative
11
+
12
+ # This method returns a subset of the list, based on the
13
+ # argument +direction+ given. It either returns a list of
14
+ # input variables or a list of output variables.
15
+ def select(direction)
16
+ array = Variablelist.new()
17
+ self.each do |element|
18
+ array.push(element) if ((direction == INPUT) && (element.input?)) || ((direction == OUTPUT) && (element.output?))
19
+ end
20
+ return array
21
+ end
22
+
23
+ # Method to set a representative variable for this variable-
24
+ # list. It is set based on the variable's species-name, e.g.
25
+ # 'in0' or 'out2'.
26
+ def set_representative(ids)
27
+ @representative = select(ids.to_s.scan(/\D+/).join)[ids.to_s.scan(/\d+/).join.to_i]
28
+ end
29
+
30
+ # This method is a short-hand version to select a list of
31
+ # input variables. It calls the +select+ method internally.
32
+ def inputs
33
+ select(INPUT)
34
+ end
35
+
36
+ # This method is a short-hand version to select a list of
37
+ # output variables. It calls the +select+ method internally.
38
+ def outputs
39
+ select(OUTPUT)
40
+ end
41
+
42
+ # This method is a short-hand version to select a list of
43
+ # input only variables. It calls the +select+ method
44
+ # internally.
45
+ def inputs_only
46
+ self-select(OUTPUT)
47
+ end
48
+
49
+ # This method is a short-hand version to select a list of
50
+ # input only variables. It calls the +select+ method
51
+ # internally.
52
+ def outputs_only
53
+ self-select(INPUT)
54
+ end
55
+
56
+ # This method sorts the list of variables based on its
57
+ # species' pattern (e.g. element or chunk). An alphabet
58
+ # is based as an argument to this method to specify the
59
+ # prefered order. This alphabet must be an array of strings.
60
+ def sort_by(alphabet)
61
+ clone = self.clone
62
+ self.clear
63
+ alphabet.each do |letter|
64
+ clone.each do |array|
65
+ self.push(array) if array.species.pattern == letter
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
data/lib/castaddon.rb ADDED
@@ -0,0 +1,27 @@
1
+ # Include the CAST gem, which provides a C99-parser. The gem
2
+ # is based on an intermediate representation in the form of
3
+ # an abstract syntax tree (AST). The gem provides C to AST
4
+ # and AST to C.
5
+ module C
6
+ end
7
+ require 'rubygems'
8
+ require 'cast'
9
+
10
+ # Include the extentions to the CAST gem provided by the author
11
+ # of Bones. These extentions provide a significant amount of
12
+ # functionality for Bones itself.
13
+ require 'castaddon/node.rb'
14
+ require 'castaddon/type.rb'
15
+ require 'castaddon/index.rb'
16
+
17
+ # Modify the NodeArray and NodeChain lists to output correct
18
+ # code when printed to a file.
19
+ class C::NodeList
20
+ # Modify the 'to_s' method to output correct code when printed
21
+ # to a file. Originally, it would separate instances of the list
22
+ # with a ','. Instead, a newline command is added.
23
+ def to_s
24
+ self.join("\n")
25
+ end
26
+ end
27
+
@@ -0,0 +1,40 @@
1
+ # This class provides an extention to the CAST index class.
2
+ # The class contains a number of functions applicable to array
3
+ # accesses of the form 'array[x][y]' or 'vector[i]'.
4
+ #
5
+ # The provided methods are helpers to extend the CAST functionality
6
+ # and to clean-up the Bones classes.
7
+ class C::Index
8
+
9
+ # This method is a recursive method which *gets* the name of
10
+ # a variable from the index definition. Depending on the
11
+ # number of dimensions, it will go deeper into the structure
12
+ # and eventually return the name.
13
+ def variable_name
14
+ return (self.expr.variable?) ? self.expr.name : self.expr.variable_name
15
+ end
16
+
17
+ # This method is a recursive method which *sets* the name of
18
+ # a variable from the index definition. Depending on the
19
+ # number of dimensions, it will go deeper into the structure
20
+ # and eventually set the name.
21
+ def variable_name=(name)
22
+ (self.expr.variable?) ? self.expr.name = name : self.expr.variable_name=(name)
23
+ end
24
+
25
+ # This method returns the dimension of an index expression.
26
+ # It starts at dimension 1, but if it can find a new dimension
27
+ # it will increment the count and call itself again.
28
+ def dimension(count=1)
29
+ return (self.expr.index?) ? self.expr.dimension(count+1) : count
30
+ end
31
+
32
+ # This method returns the index itself at a given dimension.
33
+ # It uses recursion to iterate through the dimensions, but
34
+ # will eventually return a new index node.
35
+ def index_at_dimension(dimension)
36
+ return (dimension == 0) ? self.index : self.expr.index_at_dimension(dimension-1)
37
+ end
38
+
39
+ end
40
+
@@ -0,0 +1,753 @@
1
+ # This class provides an extension to the CAST node class, which
2
+ # is a parent class for all other CAST classes. The extension
3
+ # consists of three different types of methods:
4
+ # * Methods starting with +transform_+, handling the major code transformations.
5
+ # * Methods to obtain information on variables, such as their direction and whether they are defined or not.
6
+ # * Helper methods, among others those that indicate whether a node is of a certain class.
7
+ class C::Node
8
+
9
+ # Pre-process method. It currently pre-processes a piece of
10
+ # code (typically the kernel code) to replace particular
11
+ # code structures with others, which can be handled (better)
12
+ # by Bones. For now, the pre-process method performs the
13
+ # following transformations:
14
+ # * Replaces all incrementors (i++) outside for loops with an assignment (i=i+1).
15
+ # * Replaces all decrementors (i--) outside for loops with an assignment (i=i-1).
16
+ def preprocess
17
+ self.preorder do |node|
18
+ if node.postinc? || node.preinc?
19
+ node.replace_with(C::AssignmentExpression.parse(node.expr.to_s+' = '+node.expr.to_s+' + 1')) unless node.parent.for_statement?
20
+ elsif node.postdec? || node.predec?
21
+ node.replace_with(C::AssignmentExpression.parse(node.expr.to_s+' = '+node.expr.to_s+' - 1')) unless node.parent.for_statement?
22
+ end
23
+ end
24
+ end
25
+
26
+ # Method to obtain a list of all functions in the code. If
27
+ # no functions can be found, an empty array is returned. For
28
+ # every function found, the function itself is pushed to the
29
+ # list. This method also makes sure that there is at least one
30
+ # function with the name 'main'. If this is not the case, an
31
+ # error is raised.
32
+ def get_functions
33
+ includes_main = false
34
+ function_list = []
35
+ self.preorder do |node|
36
+ if node.function_definition?
37
+ function_list.push(node)
38
+ includes_main = true if (node.name == 'main' || node.name == Bones::VARIABLE_PREFIX+'main')
39
+ end
40
+ end
41
+ raise_error('No "main"-function detected in the input file') if !includes_main
42
+ return function_list
43
+ end
44
+
45
+ # Method to enable the use of local memory for a list of
46
+ # array variables which is given as an argument to the method.
47
+ # The method walks through the node. First, it checks whether:
48
+ # * The node represents an array access (+node.index?+)
49
+ # * The node has a parent node (+node.parent+)
50
+ # Then, the method loops over all array variables. It checks
51
+ # one more thing: whether the array variable's name is the
52
+ # same as the name found in the array access node.
53
+ #
54
+ # If all conditions are met, the method performs two replacements:
55
+ # * The variable name is changed to correspond to local memory
56
+ # * The index names are changed to correspond to local indices
57
+ #
58
+ # The method performs the transformation on the node itself.
59
+ # Any old data is thus lost.
60
+ def transform_use_local_memory(arrays)
61
+ self.preorder do |node|
62
+ if (node.index?) && (node.parent)
63
+ arrays.each do |array|
64
+ if node.variable_name == array.name
65
+ node.variable_name = Bones::LOCAL_MEMORY+'_'+array.name
66
+ array.species.dimensions.each_with_index do |dimension,num_dimension|
67
+ node.replace_variable(Bones::GLOBAL_ID+'_'+num_dimension.to_s,Bones::LOCAL_ID+'_'+num_dimension.to_s)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # This method transforms multi-dimensional arrays into 1D
76
+ # arrays. The target array variable list is given as argument.
77
+ # The method walks through the node. First, it checks whether:
78
+ # * The node represents an array access (+node.index?+)
79
+ # * The node has a parent node (+node.parent+)
80
+ # Then, the method loops over all array variables. It then
81
+ # checks two more things:
82
+ # * Whether the given name is the same as the name found in the array access node (+node.variable_name == array.name+)
83
+ # * Whether the dimensions of the given array are the same as the dimensions of the node (+node.dimension == array.dimension+)
84
+ #
85
+ # Then, the method is ready to perform the flattening. It
86
+ # first gets the index for the first dimension and then
87
+ # iterates over all remaining dimensions. For those dimensions,
88
+ # the index is multiplied by the size of the previous
89
+ # dimension.
90
+ #
91
+ # The method performs the transformation on the node itself.
92
+ # Any old data is thus lost.
93
+ def transform_flatten(array)
94
+ self.preorder do |node|
95
+ if (node.index?) && (node.parent)
96
+ if (node.variable_name == array.name) && (node.dimension == array.dimensions)
97
+
98
+ # Compute the new index
99
+ results = array.species.dimensions.each_with_index.map { |d,n| '('+node.index_at_dimension(n).to_s+')'+array.factors[n] }
100
+ replacement = array.name+'['+results.join(' + ')+']'
101
+
102
+ # Replace the node
103
+ node.replace_with(C::Index.parse(replacement))
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # Method to transform array accesses into register accesses.
110
+ # This is only valid for the local loop body and could have
111
+ # been done by the actual compiler in a number of cases.
112
+ def transform_substitution(array,inout)
113
+ replacement = 'register_'+array.name
114
+ original_name = ''
115
+
116
+ # Change the variable names
117
+ self.stmts.preorder do |node|
118
+ if (node.index?) && (node.parent)
119
+
120
+ # First time replacement
121
+ if original_name == ''
122
+ if node.variable_name == array.name
123
+ node.replace_with(C::Variable.parse(replacement))
124
+ original_name = node.to_s
125
+ end
126
+
127
+ # Second, third, etc. replacement
128
+ else
129
+ if original_name == node.to_s
130
+ node.replace_with(C::Variable.parse(replacement))
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ # Add prologue and epilogue code
137
+ if original_name != ''
138
+ if inout
139
+ self.stmts[0].insert_prev(C::Declaration.parse(array.type_name+' '+replacement+'='+original_name+';'))
140
+ else
141
+ self.stmts[0].insert_prev(C::Declaration.parse(array.type_name+' '+replacement+';'))
142
+ end
143
+ self.stmts[self.stmts.length-1].insert_next(C::ExpressionStatement.parse(original_name+' = '+replacement+';'))
144
+ end
145
+ end
146
+
147
+ # Method to shuffle a 2D array access (e.g. transform from
148
+ # A[i][j] into A[j][i]).
149
+ def transform_shuffle(arrays)
150
+ arrays.each do |array|
151
+
152
+ # Change the variable names
153
+ self.stmts.preorder do |node|
154
+ if (node.index?) && (node.parent)
155
+ if node.variable_name == array.name && node.expr.index?
156
+ replacement = node.variable_name.to_s+'['+node.index.to_s+']['+node.expr.index.to_s+']'
157
+ node.replace_with(C::Index.parse(replacement))
158
+ end
159
+ end
160
+ end
161
+
162
+ end
163
+ end
164
+
165
+ # Method to merge the computations of multiple threads.
166
+ def transform_merge_threads(amount,excludes)
167
+ self.preorder do |node|
168
+ if node.statement?
169
+ replacement = C::NodeArray.new
170
+ amount.times do |i|
171
+ replacement.push(node.clone.rename_variables('_m'+i.to_s,excludes))
172
+ end
173
+ node.replace_with(replacement)
174
+ end
175
+ end
176
+ end
177
+ def rename_variables(suffix,excludes)
178
+ self.preorder do |node|
179
+ if (node.variable? || node.declarator?) && !(excludes.include?(node.name)) && (!node.parent.call?)
180
+ node.name = node.name+suffix
181
+ end
182
+ end
183
+ end
184
+
185
+ # This method provides the transformations necessary to
186
+ # perform reduction type of operations. The transformations
187
+ # involved in this function are on variable names and index
188
+ # locations. The argument +id+ specifies which transformation
189
+ # to be performed.
190
+ #
191
+ # Accepted inputs at this point: 2, 3 and 4 (CUDA/OPENCL)
192
+ # Also accepted input: 8 (CUDA), 9 (OPENCL) (to create an atomic version of the code)
193
+ # TODO: Complete the atomic support, e.g. add support for multiplications and ternary operators
194
+ def transform_reduction(input_variable,output_variable,id)
195
+
196
+ # Pre-process assign-add type constructions
197
+ if self.stmts[0].expr.addassign?
198
+ self.stmts[0].expr.replace_with(C::Assign.parse(self.stmts[0].expr.lval.to_s+'='+self.stmts[0].expr.lval.to_s+'+'+self.stmts[0].expr.rval.to_s))
199
+ end
200
+
201
+ # Create atomic code
202
+ if id == 8 || id == 9
203
+ function_name = (id == 8) ? 'atomicAdd' : 'atomic_add'
204
+ self.preorder do |node|
205
+ if node.assign?
206
+ if node.lval.index? && node.lval.variable_name == output_variable.name
207
+ if node.rval.add?
208
+ if node.rval.expr1.variable_name == output_variable.name
209
+ node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr1.to_s+','+node.rval.expr2.to_s+')'))
210
+ elsif node.rval.expr2.variable_name == output_variable.name
211
+ node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr2.to_s+','+node.rval.expr1.to_s+')'))
212
+ end
213
+ elsif node.rval.subtract?
214
+ if node.rval.expr1.variable_name == output_variable.name
215
+ node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr1.to_s+',-'+node.rval.expr2.to_s+')'))
216
+ elsif node.rval.expr2.variable_name == output_variable.name
217
+ node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr2.to_s+',-'+node.rval.expr1.to_s+')'))
218
+ end
219
+ else
220
+ raise_error('Unsupported atomic reduction operator: '+node.rval.type.inspect)
221
+ end
222
+ end
223
+ end
224
+ end
225
+ return self
226
+ else
227
+
228
+ # Split the statement into an operation, the input, and the output
229
+ results = []
230
+ operation = self.stmts[0].expr.rval.class
231
+ [self.stmts[0].expr.rval.expr1.detach,self.stmts[0].expr.rval.expr2.detach].each do |nodes|
232
+ nodes.preorder do |node|
233
+ if (node.index?)
234
+ results[0] = nodes if node.variable_name == input_variable.name
235
+ results[1] = nodes if node.variable_name == output_variable.name
236
+ end
237
+ end
238
+ end
239
+
240
+ # Process the input part
241
+ results[0].preorder do |node|
242
+ if (node.index?) && (node.variable_name == input_variable.name)
243
+ temporary = C::Variable.parse(Bones::VARIABLE_PREFIX+'temporary')
244
+ results[0] = C::Index.parse(Bones::LOCAL_MEMORY+'['+Bones::VARIABLE_PREFIX+'offset_id]') if id == 3
245
+ results[0] = temporary if id == 5
246
+ if id == 2 || id == 4
247
+ if node.parent
248
+ node.replace_with(temporary)
249
+ else
250
+ results[0] = temporary
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ # Process the output part
257
+ results[1] = C::Variable.parse(Bones::PRIVATE_MEMORY) if id == 2 || id == 5
258
+ results[1] = C::Index.parse(Bones::LOCAL_MEMORY+'['+Bones::LOCAL_ID+']') if id == 3
259
+ results[1] = '0' if id == 4
260
+
261
+ # Merge the results together with the operation
262
+ return C::Expression.parse(results[1].to_s+'+'+results[0].to_s) if id == 3 || id == 5
263
+ case operation.to_s
264
+ when 'C::Add' then return C::Expression.parse(results[1].to_s+'+'+results[0].to_s)
265
+ when 'C::Subtract' then return C::Expression.parse(results[1].to_s+'-'+results[0].to_s)
266
+ else raise_error('Unsupported reduction operation '+operation.to_s+'.')
267
+ end
268
+ end
269
+ end
270
+
271
+ # This method returns the complexity of a piece of code in
272
+ # terms of the amount of ALU operations (multiplications,
273
+ # additions, etc.).
274
+ def get_complexity
275
+ count = 0
276
+ self.preorder do |node|
277
+ count += 1 if node.alu?
278
+ end
279
+ return count
280
+ end
281
+
282
+ # This method returns the type of a variable (e.g. int, float).
283
+ # The method requires the name of a variable as an argument.
284
+ # It first tries to find a declaration for the variable in
285
+ # by walking through the node. If it cannot find it, it will
286
+ # search for a parameter definition from a function call. If
287
+ # that cannot be found either, the method will return 'nil',
288
+ # meaning that the variable is not defined at all in the
289
+ # current node.
290
+ def variable_type(variable_name)
291
+ self.preorder do |node|
292
+ if node.declarator? || node.parameter?
293
+ return node.type if node.name == variable_name
294
+ end
295
+ end
296
+ return nil
297
+ end
298
+
299
+ # This method returns the sizes of a variable as defined
300
+ # at the initialization of the array. There are multiple
301
+ # possibilities:
302
+ # * Static arrays (e.g. int array[12])
303
+ # * Static arrays with defines (e.g. int input[N1][N2][N3])
304
+ # * Variable length arrays (e.g. float temp[n][m])
305
+ # * Dynamically allocated arrays (e.g. int *a = (int *)malloc(size*4))
306
+ def size(variable_name)
307
+ self.preorder do |node|
308
+ if node.declarator? && node.name == variable_name
309
+ if node.indirect_type
310
+ if node.indirect_type.array?
311
+ return node.indirect_type.lengths
312
+ elsif node.indirect_type.pointer?
313
+ node.preorder do |inner_node|
314
+ if inner_node.call? && inner_node.expr.name == 'malloc'
315
+ if !node.indirect_type.type # This is a check to ensure single-pointer only
316
+ string = '('+inner_node.args.to_s+'/sizeof('+node.type.to_s.gsub('*','').strip+'))'
317
+ string.gsub!(/sizeof\(int\)\/sizeof\(int\)/,'1')
318
+ string.gsub!(/sizeof\(unsigned int\)\/sizeof\(unsigned int\)/,'1')
319
+ string.gsub!(/sizeof\(char\)\/sizeof\(char\)/,'1')
320
+ string.gsub!(/sizeof\(unsigned char\)\/sizeof\(unsigned char\)/,'1')
321
+ string.gsub!(/sizeof\(double\)\/sizeof\(double\)/,'1')
322
+ string.gsub!(/sizeof\(float\)\/sizeof\(float\)/,'1')
323
+ return [string]
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
331
+ return []
332
+ end
333
+
334
+ # This is a helper method which calls itself recursively,
335
+ # depending on the dimensions of the variable. It stores
336
+ # the resulting array sizes in an array 'result'.
337
+ def lengths(result = [])
338
+ found = '('+self.length.to_s+')'
339
+ result.push(found)
340
+ return (self.type && self.type.array?) ? self.type.lengths(result) : result
341
+ end
342
+
343
+ # This method returns a list of undefined variables in the
344
+ # node. It walks the node tree until it finds a node that
345
+ # full-fills the following:
346
+ # * The node is a variable (+node.variable?+)
347
+ # * The variable is not in a function call (+!node.parent.call?+)
348
+ # * The variable is not defined in the code (+!self.variable_type(node.name)+)
349
+ def undefined_variables
350
+ variables = []
351
+ self.preorder do |node|
352
+ variables.push(node.name) if (node.variable?) && (!node.parent.call?) && (!self.variable_type(node.name))
353
+ end
354
+ return variables.uniq
355
+ end
356
+
357
+ # This method finds the direction of a variable based on the
358
+ # node information. The direction of a variable can be either:
359
+ # * +in:+ - The variable is accessed read-only.
360
+ # * +out:+ - The variable is accessed write-only.
361
+ #
362
+ # The method takes the name of a variable and walks through
363
+ # the node to search for expressions (assignments and binary
364
+ # expressions). For each expression it takes the first and
365
+ # second part of the expression and stores it in a list.
366
+ # Afterwards, the expressions in the list are analysed for
367
+ # occurrences of the variable.
368
+ #
369
+ # The method raises an error if the variable does not appear
370
+ # at all: it is neither input nor output.
371
+ def direction(variable_name)
372
+ result = {:in => false, :out => false }
373
+ expressions = {:in => [], :out => []}
374
+ output_nodes = []
375
+ self.preorder do |node|
376
+
377
+ # First find out if the current node actually contains the target variable somewhere
378
+ name_match = false
379
+ node.preorder do |match_node|
380
+ name_match = true if (match_node.variable?) && (match_node.name == variable_name)
381
+ end
382
+
383
+ # If there is a match and the current node is of an assignment/binary/declarator type, we can start processing
384
+ if (name_match) && (node.assignment_expression? || node.binary_expression? || node.declarator?)
385
+
386
+ # First find out if this node can be considered an input (see sum/acc/temp register variable problem - chunk/example1 vs chunk/example5)
387
+ possible_input = true
388
+ node.preorder do |test_node|
389
+ output_nodes.each do |output_node|
390
+ possible_input = false if test_node =~ output_node
391
+ end
392
+ end
393
+
394
+ # Store the node's data in a list (input/output lists are separated)
395
+ if node.assignment_expression?
396
+ output_nodes << node.lval
397
+ expressions[:out] << node.lval.remove_index
398
+ expressions[:in] << node.rval if possible_input
399
+ if !node.assign?
400
+ expressions[:in] << node.lval if possible_input
401
+ end
402
+ elsif node.binary_expression?
403
+ expressions[:in] << node.expr1 if possible_input
404
+ expressions[:in] << node.expr2.remove_index if possible_input
405
+ elsif node.declarator? && node.init
406
+ expressions[:in] << node.init if possible_input
407
+ end
408
+ end
409
+ end
410
+
411
+ # Set the result according to the list of nodes
412
+ expressions.each do |key,expression_list|
413
+ expression_list.each do |expression|
414
+ expression.preorder do |node|
415
+ if (node.variable?) && (node.name == variable_name)
416
+ result[key] = true
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ # Return the result
423
+ return Bones::INOUT if result[:in] && result[:out]
424
+ return Bones::INPUT if result[:in]
425
+ return Bones::OUTPUT if result[:out]
426
+ raise_error('Variable "'+variable_name+'" is neither input nor output')
427
+ end
428
+
429
+ # This method walks through the node and finds the first
430
+ # for-loop. If it is found, it returns the contents of the
431
+ # for-loop and the name of the loop variable. Obtaining the
432
+ # loop variable is conditional because it can either be an
433
+ # assignment ('k=0') or a variable definition ('int k=0').
434
+ #
435
+ # The method raises an error when no for-loop can be found.
436
+ # It also raises an error if the loop is not in canonical
437
+ # form.
438
+ def remove_loop(from,to)
439
+ bones_common = Bones::Common.new()
440
+ self.preorder do |node|
441
+ if node.for_statement?
442
+ from_statement = (node.init.assign?) ? node.init.rval : node.init.declarators[0].init
443
+ from_loop = (from_statement.variable?) ? from_statement.name : from_statement.to_s
444
+ to_loop = (node.cond.expr2.variable?) ? node.cond.expr2.name : ((node.cond.expr2.intliteral?) ? node.cond.expr2.val.to_s : node.cond.expr2.to_s)
445
+ to_loop = to_loop.gsub(/\s/,'')
446
+ to_loop = '('+to_loop+')-1' if node.cond.less?
447
+ to_loop = bones_common.simplify(to_loop)
448
+ from_loop = bones_common.simplify(from_loop)
449
+ puts Bones::WARNING+'The loop iterator starts at: "'+from_loop+'" (expected "'+from+'")' if from_loop != from
450
+ puts Bones::WARNING+'The loop iterator ends at: "'+to_loop+'" (expected "'+to+'")' if to_loop != to
451
+ raise_error('The loop increment must be 1') if !(node.iter.unit_increment?)
452
+ name = (node.init.assign?) ? node.init.lval.name : node.init.declarators.first.name
453
+ return node.stmt, name
454
+ end
455
+ end
456
+ raise_error('Unexpected number of for-loops')
457
+ end
458
+
459
+ # This method searches for a variable name in the node and
460
+ # replaces it with the method's argument, which is given as
461
+ # a string. The node itself is modified. The method checks
462
+ # whether:
463
+ # * The node is a variable (+node.variable?+)
464
+ # * The variable has the correct name (+node.name == variable_name+)
465
+ # * The variable is not in a function call (+!node.parent.call?+)
466
+ def replace_variable(variable_name,replacement)
467
+ self.preorder do |node|
468
+ node.name = replacement if (node.variable?) && (node.name == variable_name) && (!node.parent.call?)
469
+ end
470
+ end
471
+
472
+ # This method searches for a target node and replaces it with
473
+ # a replacement node. Both the target node and the replacement
474
+ # node are given as arguments to the method. The method walks
475
+ # through the node and checks whether:
476
+ # * The node's class is the same as the target class (+node.class == target.class+)
477
+ # * The node has a parent (+node.parent != nil+)
478
+ # * The node is equal to the target node (+node.match?(target)+)
479
+ # If all checks are successful, the node will be replaced with
480
+ # the replacement node and the method will return immediately.
481
+ #
482
+ # The method returns itself if the target node cannot be
483
+ # found.
484
+ def seach_and_replace_node(target,replacements)
485
+ self.preorder do |node|
486
+ if (node.class == target.class) && (node.parent != nil) && (node.match?(target))
487
+ node.replace_with(replacements)
488
+ return self
489
+ end
490
+ end
491
+ return self
492
+ end
493
+
494
+ # This method searches for a target node and checks whether it
495
+ # exists. The input to the method is the target node. The method
496
+ # walks through the node and checks whether:
497
+ # * The node's class is the same as the target class (+node.class == target.class+)
498
+ # * The node has a parent (+node.parent != nil+)
499
+ # * The node is equal to the target node (+node.match?(target)+)
500
+ # If all checks are successful, the method will return the value
501
+ # 'true' immediately. If the target node cannot be found, the
502
+ # method returns 'false'.
503
+ def node_exists?(target)
504
+ self.preorder do |node|
505
+ if (node.class == target.class) && (node.parent != nil) && (node.match?(target))
506
+ return true
507
+ end
508
+ end
509
+ return false
510
+ end
511
+
512
+ # This method searches for a target function call and replaces
513
+ # it with another. Both the target and the replacement function
514
+ # call are given as arguments to the method. The method walks
515
+ # through the node and checks whether:
516
+ # * The node's class is the same as the target class (+node.class == target.class+)
517
+ # * The node has a parent which is a function call (+node.parent.call?+)
518
+ # * The node is equal to the target node (+node.match?(target)+)
519
+ # If all checks are successful, the node will be replaced with
520
+ # the replacement node. The method will continue searching for
521
+ # other occurrences of the function call.
522
+ #
523
+ # The method returns itself.
524
+ def seach_and_replace_function_call(target,replacements)
525
+ self.preorder do |node|
526
+ if (node.class == target.class) && (node.parent.call?) && (node.match?(target))
527
+ node.replace_with(replacements)
528
+ end
529
+ end
530
+ return self
531
+ end
532
+
533
+ # This method searches for a target function name and replaces
534
+ # it with another name. Both the target and the replacement
535
+ # name are given as arguments to the method. The method walks
536
+ # through the node and checks whether:
537
+ # * The node's class is a function definition or declaration
538
+ # * The node's name is equal to the target node's name
539
+ # If the checks are successful, the node's name will be replaced
540
+ # The method will continue searching for other occurrences of
541
+ # functions with the same name.
542
+ #
543
+ # The method returns itself.
544
+ def seach_and_replace_function_definition(old_name,new_name)
545
+ self.preorder do |node|
546
+ if (node.function_definition? || node.function_declaration?) && (node.name == old_name)
547
+ node.name = new_name
548
+ end
549
+ end
550
+ return self
551
+ end
552
+
553
+ # This method is a small helper function to remove index
554
+ # nodes from a node. It first clones to original node in
555
+ # order to not overwrite it, then walks the node and removes
556
+ # index nodes. Finally, it returns a new node.
557
+ def remove_index
558
+ node_clone = self.clone
559
+ node_clone.preorder do |node|
560
+ node.index.detach if node.index?
561
+ end
562
+ return node_clone
563
+ end
564
+
565
+ # This method is a small helper function which simply strips
566
+ # any outer brackets from a node. If no outer brackets are
567
+ # found, then nothing happens and the node itself is returned.
568
+ def strip_brackets
569
+ return (self.block?) ? self.stmts : self
570
+ end
571
+
572
+ # This method returns 'true' if the node is of the 'Variable'
573
+ # class. Otherwise, it returns 'false'.
574
+ def variable? ; (self.class == C::Variable) end
575
+
576
+ # This method returns 'true' if the node is of the 'Array'
577
+ # class. Otherwise, it returns 'false'.
578
+ def array?
579
+ return (self.class == C::Array)
580
+ end
581
+
582
+ # This method returns 'true' if the node is of the 'Pointer'
583
+ # class. Otherwise, it returns 'false'.
584
+ def pointer?
585
+ return (self.class == C::Pointer)
586
+ end
587
+
588
+ # This method returns 'true' if the node is of the 'Parameter'
589
+ # class. Otherwise, it returns 'false'.
590
+ def parameter?
591
+ return (self.class == C::Parameter)
592
+ end
593
+
594
+ # This method returns 'true' if the node is of the 'Declarator'
595
+ # class. Otherwise, it returns 'false'.
596
+ def declarator?
597
+ return (self.class == C::Declarator)
598
+ end
599
+
600
+ # This method returns 'true' if the node is of the 'Declaration'
601
+ # class. Otherwise, it returns 'false'.
602
+ def declaration?
603
+ return (self.class == C::Declaration)
604
+ end
605
+
606
+ # This method returns 'true' if the node is of the 'Index'
607
+ # class. Otherwise, it returns 'false'.
608
+ def index?
609
+ return (self.class == C::Index)
610
+ end
611
+
612
+ # This method returns 'true' if the node is of the 'Call'
613
+ # class. Otherwise, it returns 'false'.
614
+ def call?
615
+ return (self.class == C::Call)
616
+ end
617
+
618
+ # This method returns 'true' if the node is of the 'FunctionDef'
619
+ # class. Otherwise, it returns 'false'.
620
+ def function_definition?
621
+ return (self.class == C::FunctionDef)
622
+ end
623
+
624
+ # This method returns 'true' if the node is of the 'Declarator'
625
+ # class with its 'indirect_type' equal to 'Function' . Otherwise,
626
+ # it returns 'false'.
627
+ def function_declaration?
628
+ return (self.class == C::Declarator && self.indirect_type.class == C::Function)
629
+ end
630
+
631
+ # This method returns 'true' if the node is of the 'Block'
632
+ # class. Otherwise, it returns 'false'.
633
+ def block?
634
+ return (self.class == C::Block)
635
+ end
636
+
637
+ # This method returns 'true' if the node is of the 'For'
638
+ # class. Otherwise, it returns 'false'.
639
+ def for_statement?
640
+ return (self.class == C::For)
641
+ end
642
+
643
+ # This method returns 'true' if the node is of the 'Less'
644
+ # class. Otherwise, it returns 'false'.
645
+ def less?
646
+ return (self.class == C::Less)
647
+ end
648
+
649
+ # This method returns 'true' if the node is of the 'Add'
650
+ # class. Otherwise, it returns 'false'.
651
+ def add?
652
+ return (self.class == C::Add)
653
+ end
654
+
655
+ # This method returns 'true' if the node is of the 'Subtract'
656
+ # class. Otherwise, it returns 'false'.
657
+ def subtract?
658
+ return (self.class == C::Subtract)
659
+ end
660
+
661
+ # This method returns 'true' if the node is of the 'AddAssign'
662
+ # class. Otherwise, it returns 'false'.
663
+ def addassign?
664
+ return (self.class == C::AddAssign)
665
+ end
666
+
667
+ # This method returns 'true' if the node is of the 'PostInc'
668
+ # class. Otherwise, it returns 'false'.
669
+ def postinc?
670
+ return (self.class == C::PostInc)
671
+ end
672
+
673
+ # This method returns 'true' if the node is of the 'PreInc'
674
+ # class. Otherwise, it returns 'false'.
675
+ def preinc?
676
+ return (self.class == C::PreInc)
677
+ end
678
+
679
+ # This method returns 'true' if the node is of the 'PostDec'
680
+ # class. Otherwise, it returns 'false'.
681
+ def postdec?
682
+ return (self.class == C::PostDec)
683
+ end
684
+
685
+ # This method returns 'true' if the node is of the 'PreDec'
686
+ # class. Otherwise, it returns 'false'.
687
+ def predec?
688
+ return (self.class == C::PreDec)
689
+ end
690
+
691
+ # This method returns 'true' if the node is of the 'IntLiteral'
692
+ # class. Otherwise, it returns 'false'.
693
+ def intliteral?
694
+ return (self.class == C::IntLiteral)
695
+ end
696
+
697
+ # This method returns 'true' if the node is of the 'Assign'
698
+ # class. Otherwise, it returns 'false'.
699
+ def assign?
700
+ return (self.class == C::Assign)
701
+ end
702
+
703
+ # This method returns 'true' if the node is of the 'Call'
704
+ # class. Otherwise, it returns 'false'.
705
+ def call?
706
+ return (self.class == C::Call)
707
+ end
708
+
709
+ # This method returns 'true' if the node's class inherits
710
+ # from the 'BinaryExpression' class. Otherwise, it returns
711
+ # 'false'.
712
+ def binary_expression?
713
+ return (self.class.superclass == C::BinaryExpression)
714
+ end
715
+
716
+ # This method returns 'true' if the node's class inherits
717
+ # from the 'AssignmentExpression' class. Otherwise, it returns
718
+ # 'false'.
719
+ def assignment_expression?
720
+ return (self.class.superclass == C::AssignmentExpression)
721
+ end
722
+
723
+ # This method returns 'true' if the node is of the 'PostInc', 'PreInc'
724
+ # class or if it is of the 'Assign' class and adds with a value of 1.
725
+ # Otherwise, it returns 'false'.
726
+ def unit_increment?
727
+ return (self.class == C::PostInc) || (self.class == C::PreInc) || (self.class == C::Assign && self.rval.class == C::Add && self.rval.expr2.class == C::IntLiteral && self.rval.expr2.val == 1)
728
+ end
729
+
730
+ # This method returns 'true' if the node is performing an ALU
731
+ # operation. Otherwise, it returns 'false'.
732
+ def alu?
733
+ return add? || subtract? || addassign? || postinc? || postdec? || preinc? || predec? || binary_expression?
734
+ end
735
+
736
+ # This method returns 'true' if the node is of the 'ExpressionStatement'
737
+ # class. Otherwise, it returns 'false'.
738
+ def statement?
739
+ return (self.class == C::ExpressionStatement) || (self.class == C::Declaration)
740
+ end
741
+
742
+ # From this point on are the private methods.
743
+ private
744
+
745
+ # Override the existing 'indent' method to set the indent size
746
+ # manually.
747
+ def indent s, levels=1
748
+ space = Bones::INDENT
749
+ s.gsub(/^/, space)
750
+ end
751
+
752
+ end
753
+