Class: C::Node
- Inherits:
-
Object
- Object
- C::Node
- Defined in:
- lib/castaddon/node_bones.rb,
lib/castaddon/node_common.rb,
lib/castaddon/node_adarwin.rb,
lib/castaddon/transformations.rb
Overview
This class provides an extension to the CAST node class, which is a parent class for all other CAST classes. The extension consists of three different types of methods:
-
Methods starting with
transform_, handling the major code transformations. -
Methods to obtain information on variables, such as their direction and whether they are defined or not.
-
Helper methods, among others those that indicate whether a node is of a certain class.
Instance Method Summary collapse
-
#add? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Add’ class.
-
#addassign? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘AddAssign’ class.
-
#alu? ⇒ Boolean
This method returns ‘true’ if the node is performing an ALU operation.
-
#and? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘And’ class.
-
#array? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Array’ class.
-
#assign? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Assign’ class.
-
#assignment_expression? ⇒ Boolean
This method returns ‘true’ if the node’s class inherits from the ‘AssignmentExpression’ class.
-
#binary_expression? ⇒ Boolean
This method returns ‘true’ if the node’s class inherits from the ‘BinaryExpression’ class.
-
#block? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Block’ class.
-
#call? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Call’ class.
-
#declaration? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declaration’ class.
-
#declarator? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declarator’ class.
-
#direction(variable_name) ⇒ Object
This method finds the direction of a variable based on the node information.
-
#for_statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘For’ class.
-
#function_declaration? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declarator’ class with its ‘indirect_type’ equal to ‘Function’ .
-
#function_definition? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘FunctionDef’ class.
-
#get_accesses(accesses = [], loop_data = [], if_statements = []) ⇒ Object
This method retrieves all array references in the current node.
-
#get_all_loops ⇒ Object
This method retrieves all loops from a loop nest.
-
#get_array_name ⇒ Object
This method retrieves the name of the array reference.
-
#get_complexity ⇒ Object
This method returns the complexity of a piece of code in terms of the amount of ALU operations (multiplications, additions, etc.).
-
#get_conditions(results = []) ⇒ Object
This method retrieves the bounds for an if-statement.
-
#get_direct_loops(loop_data = []) ⇒ Object
This method retrieves all directly following loops from a node, i.e.
-
#get_functions ⇒ Object
Method to obtain a list of all functions in the code.
-
#get_index_nodes ⇒ Object
This method retrieves all nodes from the current node that are index node.
-
#get_indices ⇒ Object
This method retrieves all indices of index nodes from the current node.
-
#get_single_loop ⇒ Object
This method retrieves a single loop from the current node and collects its data: 1) the loop variable, 2) the lower-bound, 3) the upper-bound, and 4) the loop step.
-
#get_value ⇒ Object
This method retrieves the value from the current node.
-
#has_conditional_statements? ⇒ Boolean
This method checks whether the given code has any conditional statements (if-statements).
-
#if_statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘If’ class.
-
#index? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Index’ class.
-
#intliteral? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘IntLiteral’ class.
-
#lengths(result = []) ⇒ Object
This is a helper method which calls itself recursively, depending on the dimensions of the variable.
-
#less? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Less’ class.
-
#less_or_equal? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘LessOrEqual’ class.
-
#more? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘More’ class.
-
#more_or_equal? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘MoreOrEqual’ class.
-
#node_exists?(target) ⇒ Boolean
This method searches for a target node and checks whether it exists.
-
#or? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Or’ class.
-
#parameter? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Parameter’ class.
-
#pointer? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Pointer’ class.
-
#postdec? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostDec’ class.
-
#postinc? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostInc’ class.
-
#predec? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PreDec’ class.
-
#preinc? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PreInc’ class.
-
#preprocess(conditional = true) ⇒ Object
Pre-process method.
-
#remove_index ⇒ Object
This method is a small helper function to remove index nodes from a node.
-
#remove_loop(from, to) ⇒ Object
This method walks through the node and finds the first for-loop.
-
#remove_once(target) ⇒ Object
This method is a small helper function to remove a node once.
- #rename_variables(suffix, excludes) ⇒ Object
-
#replace_variable(variable_name, replacement) ⇒ Object
This method searches for a variable name in the node and replaces it with the method’s argument, which is given as a string.
-
#search_and_replace_function_call(target, replacements) ⇒ Object
This method searches for a target function call and replaces it with another.
-
#search_and_replace_function_definition(old_name, new_name) ⇒ Object
This method searches for a target function name and replaces it with another name.
-
#search_and_replace_node(target, replacements) ⇒ Object
This method searches for a target node and replaces it with a replacement node.
-
#size(variable_name) ⇒ Object
This method returns the sizes of a variable as defined at the initialization of the array.
-
#statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘ExpressionStatement’ class.
-
#string? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘StringLiteral’ class.
-
#strip_brackets ⇒ Object
This method is a small helper function which simply strips any outer brackets from a node.
-
#subtract? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Subtract’ class.
-
#transform_flatten(array) ⇒ Object
This method transforms multi-dimensional arrays into 1D arrays.
-
#transform_merge_threads(amount, excludes) ⇒ Object
Method to merge the computations of multiple threads.
-
#transform_reduction(input_variable, output_variable, id) ⇒ Object
This method provides the transformations necessary to perform reduction type of operations.
- #transform_shuffle(arrays) ⇒ Object
-
#transform_substitution(array, inout) ⇒ Object
Method to transform array accesses into register accesses.
-
#transform_use_local_memory(arrays) ⇒ Object
Method to enable the use of local memory for a list of array variables which is given as an argument to the method.
-
#undefined_variables ⇒ Object
This method returns a list of undefined variables in the node.
-
#unit_increment? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostInc’, ‘PreInc’ class or if it is of the ‘Assign’ class and adds with a value of 1.
-
#variable? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Variable’ class.
-
#variable_type(variable_name) ⇒ Object
This method returns the type of a variable (e.g. int, float).
Instance Method Details
#add? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Add’ class. Otherwise, it returns ‘false’.
181 182 183 |
# File 'lib/castaddon/node_common.rb', line 181 def add? return (self.class == C::Add) end |
#addassign? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘AddAssign’ class. Otherwise, it returns ‘false’.
193 194 195 |
# File 'lib/castaddon/node_common.rb', line 193 def addassign? return (self.class == C::AddAssign) end |
#alu? ⇒ Boolean
This method returns ‘true’ if the node is performing an ALU operation. Otherwise, it returns ‘false’.
268 269 270 |
# File 'lib/castaddon/node_common.rb', line 268 def alu? return add? || subtract? || addassign? || postinc? || postdec? || preinc? || predec? || binary_expression? end |
#and? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘And’ class. Otherwise, it returns ‘false’.
145 146 147 |
# File 'lib/castaddon/node_common.rb', line 145 def and? return (self.class == C::And) end |
#array? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Array’ class. Otherwise, it returns ‘false’.
72 73 74 |
# File 'lib/castaddon/node_common.rb', line 72 def array? return (self.class == C::Array) end |
#assign? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Assign’ class. Otherwise, it returns ‘false’.
229 230 231 |
# File 'lib/castaddon/node_common.rb', line 229 def assign? return (self.class == C::Assign) end |
#assignment_expression? ⇒ Boolean
This method returns ‘true’ if the node’s class inherits from the ‘AssignmentExpression’ class. Otherwise, it returns ‘false’.
255 256 257 |
# File 'lib/castaddon/node_common.rb', line 255 def assignment_expression? return (self.class.superclass == C::AssignmentExpression) end |
#binary_expression? ⇒ Boolean
This method returns ‘true’ if the node’s class inherits from the ‘BinaryExpression’ class. Otherwise, it returns ‘false’.
248 249 250 |
# File 'lib/castaddon/node_common.rb', line 248 def binary_expression? return (self.class.superclass == C::BinaryExpression) end |
#block? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Block’ class. Otherwise, it returns ‘false’.
127 128 129 |
# File 'lib/castaddon/node_common.rb', line 127 def block? return (self.class == C::Block) end |
#call? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Call’ class. Otherwise, it returns ‘false’.
108 109 110 |
# File 'lib/castaddon/node_common.rb', line 108 def call? return (self.class == C::Call) end |
#declaration? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declaration’ class. Otherwise, it returns ‘false’.
96 97 98 |
# File 'lib/castaddon/node_common.rb', line 96 def declaration? return (self.class == C::Declaration) end |
#declarator? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declarator’ class. Otherwise, it returns ‘false’.
90 91 92 |
# File 'lib/castaddon/node_common.rb', line 90 def declarator? return (self.class == C::Declarator) end |
#direction(variable_name) ⇒ Object
This method finds the direction of a variable based on the node information. The direction of a variable can be either:
-
in:- The variable is accessed read-only. -
out:- The variable is accessed write-only.
The method takes the name of a variable and walks through the node to search for expressions (assignments and binary expressions). For each expression it takes the first and second part of the expression and stores it in a list. Afterwards, the expressions in the list are analysed for occurrences of the variable.
The method raises an error if the variable does not appear at all: it is neither input nor output.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/castaddon/node_bones.rb', line 146 def direction(variable_name) result = {:in => false, :out => false } expressions = {:in => [], :out => []} output_nodes = [] self.preorder do |node| # First find out if the current node actually contains the target variable somewhere name_match = false node.preorder do |match_node| name_match = true if (match_node.variable?) && (match_node.name == variable_name) end # If there is a match and the current node is of an assignment/binary/declarator type, we can start processing if (name_match) && (node.assignment_expression? || node.binary_expression? || node.declarator?) # First find out if this node can be considered an input (see sum/acc/temp register variable problem - chunk/example1 vs chunk/example5) possible_input = true node.preorder do |test_node| output_nodes.each do |output_node| possible_input = false if test_node =~ output_node end end # Store the node's data in a list (input/output lists are separated) if node.assignment_expression? output_nodes << node.lval expressions[:out] << node.lval.remove_index expressions[:in] << node.rval if possible_input if !node.assign? expressions[:in] << node.lval if possible_input end elsif node.binary_expression? expressions[:in] << node.expr1 if possible_input expressions[:in] << node.expr2.remove_index if possible_input elsif node.declarator? && node.init expressions[:in] << node.init if possible_input end end end # Set the result according to the list of nodes expressions.each do |key,expression_list| expression_list.each do |expression| expression.preorder do |node| if (node.variable?) && (node.name == variable_name) result[key] = true end end end end # Return the result return Bones::INOUT if result[:in] && result[:out] return Bones::INPUT if result[:in] return Bones::OUTPUT if result[:out] raise_error('Variable "'+variable_name+'" is neither input nor output') end |
#for_statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘For’ class. Otherwise, it returns ‘false’.
133 134 135 |
# File 'lib/castaddon/node_common.rb', line 133 def for_statement? return (self.class == C::For) end |
#function_declaration? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Declarator’ class with its ‘indirect_type’ equal to ‘Function’ . Otherwise, it returns ‘false’.
121 122 123 |
# File 'lib/castaddon/node_common.rb', line 121 def function_declaration? return (self.class == C::Declarator && self.indirect_type.class == C::Function) end |
#function_definition? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘FunctionDef’ class. Otherwise, it returns ‘false’.
114 115 116 |
# File 'lib/castaddon/node_common.rb', line 114 def function_definition? return (self.class == C::FunctionDef) end |
#get_accesses(accesses = [], loop_data = [], if_statements = []) ⇒ Object
This method retrieves all array references in the current node. It retrieves information on loops and on if-statements as well. This method is destructive on the current node. It is furthermore called recursively.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/castaddon/node_adarwin.rb', line 51 def get_accesses(accesses = [],loop_data = [],if_statements = []) # Iterate over all the nodes self.preorder do |node| # A loop has been found. Proceed as follows: 1) store the loop data, 2) # call this method recursively on the loop body, and 3) remove the loop # body from the node. if node.for_statement? && node.stmt next_loop_data = loop_data.clone next_loop_data.push(node.get_single_loop) node.stmt.clone.get_accesses(accesses,next_loop_data,if_statements) node.remove_node(node.stmt) # An if-statement has been found. Proceed as follows: 1) store the (one or # more) if-statement conditions, 2) call this method recursively on the # if-statement body, and 3) remove the if-statement body from the node. elsif node.if_statement? next_if_statements = if_statements.clone node.cond.get_conditions().each do |condition| next_if_statements.push(condition) end node.then.clone.get_accesses(accesses,loop_data,next_if_statements) node.remove_node(node.then) # We haven't found an if-statement or loop in the current node, so it # implies that we can search for an array reference. # TODO: Array references as part of conditions or bounds of loops are not # found in this way. else # Collect all the writes we have seen so far. This is used to check for # references that are 'register' references because they have been # written before. writes = accesses.map{ |e| e[:access] if e[:type] == 'write' }.flatten # Collect the potential references to_search = [] if node.assignment_expression? to_search << [node.lval,'write',true] to_search << [node.lval,'read',false] if !node.assign? to_search << [node.rval,'read',false] elsif node.binary_expression? to_search << [node.expr1,'read',false] to_search << [node.expr2,'read',false] elsif node.declarator? && node.init to_search << [node.init,'read',false] end # Process the potential references into 'accesses' hashes to_search.each do |item| if item[2] || (writes & item[0].get_index_nodes().flatten).empty? item[0].get_index_nodes().each do |access| accesses << { :access => access, :name => access.get_array_name(), :indices => access.get_indices(), :type => item[1], :loop_data => loop_data, :if_statements => if_statements } end end end end end # Return the array references as hashes return accesses.uniq end |
#get_all_loops ⇒ Object
This method retrieves all loops from a loop nest. The method is based on the get_single_loop method to extract the actual loop information.
202 203 204 205 206 207 208 |
# File 'lib/castaddon/node_adarwin.rb', line 202 def get_all_loops() loops = [] self.preorder do |node| loops << node.get_single_loop() if node.for_statement? end return loops end |
#get_array_name ⇒ Object
This method retrieves the name of the array reference.
231 232 233 234 235 |
# File 'lib/castaddon/node_adarwin.rb', line 231 def get_array_name() self.preorder do |node| return node.expr.to_s if node.index? && !node.expr.index? end end |
#get_complexity ⇒ Object
This method returns the complexity of a piece of code in terms of the amount of ALU operations (multiplications, additions, etc.).
49 50 51 52 53 54 55 |
# File 'lib/castaddon/node_bones.rb', line 49 def get_complexity count = 0 self.preorder do |node| count += 1 if node.alu? end return count end |
#get_conditions(results = []) ⇒ Object
This method retrieves the bounds for an if-statement. The method is called recursively if there are multiple conditions. TODO: What about ‘||’ (or) conditions? They are currently handles as ‘&&’. TODO: Are these all the possibilities (&&,||,>=,>,<=,<) for conditions?
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/castaddon/node_adarwin.rb', line 126 def get_conditions(results=[]) # Recursive call for 'And' (&&) and 'or' (||) compound conditions if and? || or? expr1.get_conditions(results) expr2.get_conditions(results) # Greater than or equal (>=) elsif more_or_equal? results << [simplify("#{expr1}")+'='+simplify("(#{expr2})"),''] # Greater than (>) elsif more? results << [simplify("#{expr1}")+'='+simplify("(#{expr2})+1"),''] # Less than or equal (<=) elsif less_or_equal? results << ['',simplify("#{expr1}")+'='+simplify("(#{expr2})")] # Less than (<) elsif less? results << ['',simplify("#{expr1}")+'='+simplify("(#{expr2})-1")] # Unsupported conditions else raise_error("Unsupported if-condition: #{self.to_s}") end end |
#get_direct_loops(loop_data = []) ⇒ Object
This method retrieves all directly following loops from a node, i.e. the loops belonging to a perfectly nested loop. It is a recursive method: it retrieves a first loop and calls the method again on the body of the loop. It collects all the data in the loop_data array.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/castaddon/node_adarwin.rb', line 12 def get_direct_loops(loop_data = []) # Retrieve the next loop new_loop = get_single_loop() # Don't continue if the loop is independent of the writes in the code. This # is part of the selection process of what loops to be considered inner or # outer loops. if loop_data.length > 0 written_indices = self.clone.get_accesses().map do |a| (a[:type] == "write") ? a[:indices].map{ |i| i.to_s } : [] end if !written_indices.flatten.uniq.include?(new_loop[:var]) return loop_data end end # Push the new loop into the array loop_data.push(new_loop) # Check whether the current is actually a loop. # TODO: Is this check really needed or is this just a safety net? if self.for_statement? && self.stmt body = self.stmt.stmts # Check whether or not there is another loop directly following and make # sure that the body is not empty. if body.length == 1 && body.first.for_statement? body.first.get_direct_loops(loop_data) end end # Return all the loop data return loop_data end |
#get_functions ⇒ Object
Method to obtain a list of all functions in the code. If no functions can be found, an empty array is returned. For every function found, the function itself is pushed to the list. This method also makes sure that there is at least one function with the name ‘main’. If this is not the case, an error is raised.
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/castaddon/node_bones.rb', line 33 def get_functions includes_main = false function_list = [] self.preorder do |node| if node.function_definition? function_list.push(node) includes_main = true if (node.name == 'main' || node.name == Bones::VARIABLE_PREFIX+'main') end end raise_error('No "main"-function detected in the input file') if !includes_main return function_list end |
#get_index_nodes ⇒ Object
This method retrieves all nodes from the current node that are index node. Such nodes represent array references, e.g. in A, [i+3] is the index node.
213 214 215 216 217 218 219 |
# File 'lib/castaddon/node_adarwin.rb', line 213 def get_index_nodes() nodes = [] self.preorder do |node| nodes << node if node.index? && !node.parent.index? end return nodes end |
#get_indices ⇒ Object
This method retrieves all indices of index nodes from the current node.
222 223 224 225 226 227 228 |
# File 'lib/castaddon/node_adarwin.rb', line 222 def get_indices() indices = [] self.postorder do |node| indices << node.index if node.index? end return indices end |
#get_single_loop ⇒ Object
This method retrieves a single loop from the current node and collects its data: 1) the loop variable, 2) the lower-bound, 3) the upper-bound, and 4) the loop step. FIXME: For decrementing loops, should the min/max be swapped?
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/castaddon/node_adarwin.rb', line 159 def get_single_loop() loop_datum = { :var => '', :min => '', :max => '', :step => ''} if self.for_statement? # Get the loop start condition and the loop variable. # TODO: Add support for other types of initialisations, e.g. a declaration if self.init.assign? loop_datum[:var] = self.init.lval.name loop_datum[:min] = self.init.rval.get_value.to_s elsif self.init.declaration? loop_datum[:var] = self.init.declarators.first.name loop_datum[:min] = self.init.declarators.first.init.to_s else raise_error("Unsupported loop initialization: #{self.init}") end # Get the loop's upper-bound condition. # TODO: Add support for the unsupported cases. var_is_on_left = (self.cond.expr1.get_value == loop_datum[:var]) loop_datum[:max] = case when self.cond.less? then (var_is_on_left) ? simplify("#{self.cond.expr2.get_value}-1") : "unsupported" when self.cond.more? then (var_is_on_left) ? "unsupported" : simplify("#{self.cond.expr1.get_value}-1") when self.cond.less_or_equal? then (var_is_on_left) ? "#{self.cond.expr2.get_value}" : "unsupported" when self.cond.more_or_equal? then (var_is_on_left) ? "unsupported" : "#{self.cond.expr1.get_value}" end raise_error("Unsupported loop condition: #{self.cond}") if loop_datum[:max] == "unsupported" # Get the loop iterator. # TODO: Investigate whether we can handle non-basic cases iterator = self.iter.to_s loop_datum[:step] = case iterator when "#{loop_datum[:var]}++" then '1' when "++#{loop_datum[:var]}" then '1' when "#{loop_datum[:var]}--" then '-1' when "--#{loop_datum[:var]}" then '-1' else simplify(self.iter.rval.to_s.gsub(loop_datum[:var],'0')) end end return loop_datum end |
#get_value ⇒ Object
This method retrieves the value from the current node. The value can be an integer (in case of a constant) or a string (in case of a variable).
239 240 241 242 243 |
# File 'lib/castaddon/node_adarwin.rb', line 239 def get_value() return self.val if self.intliteral? return self.name if self.variable? return self.to_s end |
#has_conditional_statements? ⇒ Boolean
This method checks whether the given code has any conditional statements (if-statements)
306 307 308 309 310 311 312 313 |
# File 'lib/castaddon/node_bones.rb', line 306 def has_conditional_statements? self.preorder do |node| if node.if_statement? return true end end return false end |
#if_statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘If’ class. Otherwise, it returns ‘false’.
139 140 141 |
# File 'lib/castaddon/node_common.rb', line 139 def if_statement? return (self.class == C::If) end |
#index? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Index’ class. Otherwise, it returns ‘false’.
102 103 104 |
# File 'lib/castaddon/node_common.rb', line 102 def index? return (self.class == C::Index) end |
#intliteral? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘IntLiteral’ class. Otherwise, it returns ‘false’.
223 224 225 |
# File 'lib/castaddon/node_common.rb', line 223 def intliteral? return (self.class == C::IntLiteral) end |
#lengths(result = []) ⇒ Object
This is a helper method which calls itself recursively, depending on the dimensions of the variable. It stores the resulting array sizes in an array ‘result’.
112 113 114 115 116 |
# File 'lib/castaddon/node_bones.rb', line 112 def lengths(result = []) found = '('+self.length.to_s+')' result.push(found) return (self.type && self.type.array?) ? self.type.lengths(result) : result end |
#less? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Less’ class. Otherwise, it returns ‘false’.
157 158 159 |
# File 'lib/castaddon/node_common.rb', line 157 def less? return (self.class == C::Less) end |
#less_or_equal? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘LessOrEqual’ class. Otherwise, it returns ‘false’.
169 170 171 |
# File 'lib/castaddon/node_common.rb', line 169 def less_or_equal? return (self.class == C::LessOrEqual) end |
#more? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘More’ class. Otherwise, it returns ‘false’.
163 164 165 |
# File 'lib/castaddon/node_common.rb', line 163 def more? return (self.class == C::More) end |
#more_or_equal? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘MoreOrEqual’ class. Otherwise, it returns ‘false’.
175 176 177 |
# File 'lib/castaddon/node_common.rb', line 175 def more_or_equal? return (self.class == C::MoreOrEqual) end |
#node_exists?(target) ⇒ Boolean
This method searches for a target node and checks whether it exists. The input to the method is the target node. The method walks through the node and checks whether:
-
The node’s class is the same as the target class (+node.class == target.class+)
-
The node has a parent (+node.parent != nil+)
-
The node is equal to the target node (+node.match?(target)+)
If all checks are successful, the method will return the value ‘true’ immediately. If the target node cannot be found, the method returns ‘false’.
242 243 244 245 246 247 248 249 |
# File 'lib/castaddon/node_bones.rb', line 242 def node_exists?(target) self.preorder do |node| if (node.class == target.class) && (node.parent != nil) && (node.match?(target)) return true end end return false end |
#or? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Or’ class. Otherwise, it returns ‘false’.
151 152 153 |
# File 'lib/castaddon/node_common.rb', line 151 def or? return (self.class == C::Or) end |
#parameter? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Parameter’ class. Otherwise, it returns ‘false’.
84 85 86 |
# File 'lib/castaddon/node_common.rb', line 84 def parameter? return (self.class == C::Parameter) end |
#pointer? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Pointer’ class. Otherwise, it returns ‘false’.
78 79 80 |
# File 'lib/castaddon/node_common.rb', line 78 def pointer? return (self.class == C::Pointer) end |
#postdec? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostDec’ class. Otherwise, it returns ‘false’.
211 212 213 |
# File 'lib/castaddon/node_common.rb', line 211 def postdec? return (self.class == C::PostDec) end |
#postinc? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostInc’ class. Otherwise, it returns ‘false’.
199 200 201 |
# File 'lib/castaddon/node_common.rb', line 199 def postinc? return (self.class == C::PostInc) end |
#predec? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PreDec’ class. Otherwise, it returns ‘false’.
217 218 219 |
# File 'lib/castaddon/node_common.rb', line 217 def predec? return (self.class == C::PreDec) end |
#preinc? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PreInc’ class. Otherwise, it returns ‘false’.
205 206 207 |
# File 'lib/castaddon/node_common.rb', line 205 def preinc? return (self.class == C::PreInc) end |
#preprocess(conditional = true) ⇒ Object
Pre-process method. It currently pre-processes a piece of code (typically the kernel code) to replace particular code structures with others, which can be handled (better) by Bones. For now, the pre-process method performs the following transformations:
-
Replaces all incrementors (i++) outside for loops with an assignment (i=i+1).
-
Replaces all decrementors (i–) outside for loops with an assignment (i=i-1).
17 18 19 20 21 22 23 24 25 |
# File 'lib/castaddon/node_bones.rb', line 17 def preprocess(conditional=true) self.preorder do |node| if node.postinc? || node.preinc? node.replace_with(C::AssignmentExpression.parse(node.expr.to_s+' = '+node.expr.to_s+' + 1')) unless conditional && node.parent.for_statement? elsif node.postdec? || node.predec? node.replace_with(C::AssignmentExpression.parse(node.expr.to_s+' = '+node.expr.to_s+' - 1')) unless conditional && node.parent.for_statement? end end end |
#remove_index ⇒ Object
This method is a small helper function to remove index nodes from a node. It first clones to original node in order to not overwrite it, then walks the node and removes index nodes. Finally, it returns a new node.
296 297 298 299 300 301 302 |
# File 'lib/castaddon/node_bones.rb', line 296 def remove_index node_clone = self.clone node_clone.preorder do |node| node.index.detach if node.index? end return node_clone end |
#remove_loop(from, to) ⇒ Object
This method walks through the node and finds the first for-loop. If it is found, it returns the contents of the for-loop and the name of the loop variable. Obtaining the loop variable is conditional because it can either be an assignment (‘k=0’) or a variable definition (‘int k=0’).
The method raises an error when no for-loop can be found. It also raises an error if the loop is not in canonical form.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/castaddon/node_bones.rb', line 213 def remove_loop(from,to) self.preorder do |node| if node.for_statement? from_statement = (node.init.assign?) ? node.init.rval : node.init.declarators[0].init from_loop = (from_statement.variable?) ? from_statement.name : from_statement.to_s 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) to_loop = to_loop.gsub(/\s/,'') to_loop = '('+to_loop+')-1' if node.cond.less? to_loop = simplify(to_loop) from_loop = simplify(from_loop) puts Bones::WARNING+'The loop iterator starts at: "'+from_loop+'" (expected "'+from+'")' if from_loop != from puts Bones::WARNING+'The loop iterator ends at: "'+to_loop+'" (expected "'+to+'")' if to_loop != to raise_error('The loop increment must be 1') if !(node.iter.unit_increment?) name = (node.init.assign?) ? node.init.lval.name : node.init.declarators.first.name return node.stmt, name end end raise_error('Unexpected number of for-loops') end |
#remove_once(target) ⇒ Object
This method is a small helper function to remove a node once.
49 50 51 52 53 54 55 56 57 |
# File 'lib/castaddon/node_common.rb', line 49 def remove_once(target) self.postorder do |node| if node == target node.detach return self end end return self end |
#rename_variables(suffix, excludes) ⇒ Object
141 142 143 144 145 146 147 |
# File 'lib/castaddon/transformations.rb', line 141 def rename_variables(suffix,excludes) self.preorder do |node| if (node.variable? || node.declarator?) && !(excludes.include?(node.name)) && (!node.parent.call?) node.name = node.name+suffix end end end |
#replace_variable(variable_name, replacement) ⇒ Object
This method searches for a variable name in the node and replaces it with the method’s argument, which is given as a string. The node itself is modified. The method checks whether:
-
The node is a variable (
node.variable?) -
The variable has the correct name (+node.name == variable_name+)
-
The variable is not in a function call (+!node.parent.call?+)
13 14 15 16 17 |
# File 'lib/castaddon/node_common.rb', line 13 def replace_variable(variable_name,replacement) self.preorder do |node| node.name = replacement if (node.variable?) && (node.name == variable_name) && (!node.parent.call?) end end |
#search_and_replace_function_call(target, replacements) ⇒ Object
This method searches for a target function call and replaces it with another. Both the target and the replacement function call are given as arguments to the method. The method walks through the node and checks whether:
-
The node’s class is the same as the target class (+node.class == target.class+)
-
The node has a parent which is a function call (
node.parent.call?) -
The node is equal to the target node (+node.match?(target)+)
If all checks are successful, the node will be replaced with the replacement node. The method will continue searching for other occurrences of the function call.
The method returns itself.
263 264 265 266 267 268 269 270 |
# File 'lib/castaddon/node_bones.rb', line 263 def search_and_replace_function_call(target,replacements) self.preorder do |node| if (node.class == target.class) && (node.parent.call?) && (node.match?(target)) node.replace_with(replacements) end end return self end |
#search_and_replace_function_definition(old_name, new_name) ⇒ Object
This method searches for a target function name and replaces it with another name. Both the target and the replacement name are given as arguments to the method. The method walks through the node and checks whether:
-
The node’s class is a function definition or declaration
-
The node’s name is equal to the target node’s name
If the checks are successful, the node’s name will be replaced The method will continue searching for other occurrences of functions with the same name.
The method returns itself.
283 284 285 286 287 288 289 290 |
# File 'lib/castaddon/node_bones.rb', line 283 def search_and_replace_function_definition(old_name,new_name) self.preorder do |node| if (node.function_definition? || node.function_declaration?) && (node.name == old_name) node.name = new_name end end return self end |
#search_and_replace_node(target, replacements) ⇒ Object
This method searches for a target node and replaces it with a replacement node. Both the target node and the replacement node are given as arguments to the method. The method walks through the node and checks whether:
-
The node’s class is the same as the target class (+node.class == target.class+)
-
The node has a parent (+node.parent != nil+)
-
The node is equal to the target node (+node.match?(target)+)
If all checks are successful, the node will be replaced with the replacement node and the method will return immediately.
The method returns itself if the target node cannot be found.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/castaddon/node_common.rb', line 31 def search_and_replace_node(target,replacements) if (self.class == target.class) && (self.match?(target)) return replacements end self.preorder do |node| if (node.class == target.class) && (node.match?(target)) if (node.parent != nil) node.replace_with(replacements) else return replacements end return self end end return self end |
#size(variable_name) ⇒ Object
This method returns the sizes of a variable as defined at the initialization of the array. There are multiple possibilities:
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/castaddon/node_bones.rb', line 81 def size(variable_name) self.preorder do |node| if node.declarator? && node.name == variable_name if node.indirect_type if node.indirect_type.array? return node.indirect_type.lengths elsif node.indirect_type.pointer? node.preorder do |inner_node| if inner_node.call? && inner_node.expr.name == 'malloc' if !node.indirect_type.type # This is a check to ensure single-pointer only string = '('+inner_node.args.to_s+'/sizeof('+node.type.to_s.gsub('*','').strip+'))' string.gsub!(/sizeof\(int\)\/sizeof\(int\)/,'1') string.gsub!(/sizeof\(unsigned int\)\/sizeof\(unsigned int\)/,'1') string.gsub!(/sizeof\(char\)\/sizeof\(char\)/,'1') string.gsub!(/sizeof\(unsigned char\)\/sizeof\(unsigned char\)/,'1') string.gsub!(/sizeof\(double\)\/sizeof\(double\)/,'1') string.gsub!(/sizeof\(float\)\/sizeof\(float\)/,'1') return [string] end end end end end end end return [] end |
#statement? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘ExpressionStatement’ class. Otherwise, it returns ‘false’.
274 275 276 |
# File 'lib/castaddon/node_common.rb', line 274 def statement? return (self.class == C::ExpressionStatement) || (self.class == C::Declaration) end |
#string? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘StringLiteral’ class. Otherwise, it returns ‘false’.
241 242 243 |
# File 'lib/castaddon/node_common.rb', line 241 def string? return (self.class == C::StringLiteral) end |
#strip_brackets ⇒ Object
This method is a small helper function which simply strips any outer brackets from a node. If no outer brackets are found, then nothing happens and the node itself is returned.
62 63 64 |
# File 'lib/castaddon/node_common.rb', line 62 def strip_brackets return (self.block?) ? self.stmts : self end |
#subtract? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Subtract’ class. Otherwise, it returns ‘false’.
187 188 189 |
# File 'lib/castaddon/node_common.rb', line 187 def subtract? return (self.class == C::Subtract) end |
#transform_flatten(array) ⇒ Object
This method transforms multi-dimensional arrays into 1D arrays. The target array variable list is given as argument. The method walks through the node. First, it checks whether:
-
The node represents an array access (
node.index?) -
The node has a parent node (
node.parent)
Then, the method loops over all array variables. It then checks two more things:
-
Whether the given name is the same as the name found in the array access node (+node.variable_name == array.name+)
-
Whether the dimensions of the given array are the same as the dimensions of the node (+node.dimension == array.dimension+)
Then, the method is ready to perform the flattening. It first gets the index for the first dimension and then iterates over all remaining dimensions. For those dimensions, the index is multiplied by the size of the previous dimension.
The method performs the transformation on the node itself. Any old data is thus lost.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/castaddon/transformations.rb', line 57 def transform_flatten(array) self.preorder do |node| if (node.index?) && (node.parent) if (node.variable_name == array.name) && (node.dimension == array.dimensions) # Compute the new index results = array.species.dimensions.each_with_index.map { |d,n| '('+node.index_at_dimension(n).to_s+')'+array.factors[n] } replacement = array.name+'['+results.join(' + ')+']' # Replace the node node.replace_with(C::Index.parse(replacement)) end end end end |
#transform_merge_threads(amount, excludes) ⇒ Object
Method to merge the computations of multiple threads.
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/castaddon/transformations.rb', line 130 def transform_merge_threads(amount,excludes) self.preorder do |node| if node.statement? replacement = C::NodeArray.new amount.times do |i| replacement.push(node.clone.rename_variables('_m'+i.to_s,excludes)) end node.replace_with(replacement) end end end |
#transform_reduction(input_variable, output_variable, id) ⇒ Object
This method provides the transformations necessary to perform reduction type of operations. The transformations involved in this function are on variable names and index locations. The argument id specifies which transformation to be performed.
Accepted inputs at this point: 2, 3 and 4 (CUDA/OPENCL) Also accepted input: 8 (CUDA), 9 (OPENCL) (to create an atomic version of the code) TODO: Complete the atomic support, e.g. add support for multiplications and ternary operators
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/castaddon/transformations.rb', line 158 def transform_reduction(input_variable,output_variable,id) # Pre-process assign-add type constructions if self.stmts[0].expr.addassign? 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)) end # Create atomic code if id == 8 || id == 9 function_name = (id == 8) ? 'atomicAdd' : 'atomic_add' self.preorder do |node| if node.assign? if node.lval.index? && node.lval.variable_name == output_variable.name if node.rval.add? if node.rval.expr1.variable_name == output_variable.name node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr1.to_s+','+node.rval.expr2.to_s+')')) elsif node.rval.expr2.variable_name == output_variable.name node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr2.to_s+','+node.rval.expr1.to_s+')')) end elsif node.rval.subtract? if node.rval.expr1.variable_name == output_variable.name node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr1.to_s+',-'+node.rval.expr2.to_s+')')) elsif node.rval.expr2.variable_name == output_variable.name node.replace_with(C::Call.parse(function_name+'(&'+node.rval.expr2.to_s+',-'+node.rval.expr1.to_s+')')) end else raise_error('Unsupported atomic reduction operator: '+node.rval.type.inspect) end end end end return self else # Split the statement into an operation, the input, and the output results = [] operation = self.stmts[0].expr.rval.class [self.stmts[0].expr.rval.expr1.detach,self.stmts[0].expr.rval.expr2.detach].each do |nodes| nodes.preorder do |node| if (node.index?) results[0] = nodes if node.variable_name == input_variable.name results[1] = nodes if node.variable_name == output_variable.name end end end # Process the input part results[0].preorder do |node| if (node.index?) && (node.variable_name == input_variable.name) temporary = C::Variable.parse(Bones::VARIABLE_PREFIX+'temporary') results[0] = C::Index.parse(Bones::LOCAL_MEMORY+'['+Bones::VARIABLE_PREFIX+'offset_id]') if id == 3 results[0] = temporary if id == 5 if id == 2 || id == 4 if node.parent node.replace_with(temporary) else results[0] = temporary end end end end # Process the output part results[1] = C::Variable.parse(Bones::PRIVATE_MEMORY) if id == 2 || id == 5 results[1] = C::Index.parse(Bones::LOCAL_MEMORY+'['+Bones::LOCAL_ID+']') if id == 3 results[1] = '0' if id == 4 # Merge the results together with the operation return C::Expression.parse(results[1].to_s+'+'+results[0].to_s) if id == 3 || id == 5 case operation.to_s when 'C::Add' then return C::Expression.parse(results[1].to_s+'+'+results[0].to_s) when 'C::Subtract' then return C::Expression.parse(results[1].to_s+'-'+results[0].to_s) else raise_error('Unsupported reduction operation '+operation.to_s+'.') end end end |
#transform_shuffle(arrays) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/castaddon/transformations.rb', line 113 def transform_shuffle(arrays) arrays.each do |array| # Change the variable names self.stmts.preorder do |node| if (node.index?) && (node.parent) if node.variable_name == array.name && node.expr.index? replacement = node.variable_name.to_s+'['+node.index.to_s+']['+node.expr.index.to_s+']' node.replace_with(C::Index.parse(replacement)) end end end end end |
#transform_substitution(array, inout) ⇒ Object
Method to transform array accesses into register accesses. This is only valid for the local loop body and could have been done by the actual compiler in a number of cases.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/castaddon/transformations.rb', line 76 def transform_substitution(array,inout) replacement = 'register_'+array.name original_name = '' # Change the variable names self.stmts.preorder do |node| if (node.index?) && (node.parent) # First time replacement if original_name == '' if node.variable_name == array.name node.replace_with(C::Variable.parse(replacement)) original_name = node.to_s end # Second, third, etc. replacement else if original_name == node.to_s node.replace_with(C::Variable.parse(replacement)) end end end end # Add prologue and epilogue code if original_name != '' if inout self.stmts[0].insert_prev(C::Declaration.parse(array.type_name+' '+replacement+'='+original_name+';')) else self.stmts[0].insert_prev(C::Declaration.parse(array.type_name+' '+replacement+';')) end self.stmts[self.stmts.length-1].insert_next(C::ExpressionStatement.parse(original_name+' = '+replacement+';')) end end |
#transform_use_local_memory(arrays) ⇒ Object
Method to enable the use of local memory for a list of array variables which is given as an argument to the method. The method walks through the node. First, it checks whether:
-
The node represents an array access (
node.index?) -
The node has a parent node (
node.parent)
Then, the method loops over all array variables. It checks one more thing: whether the array variable’s name is the same as the name found in the array access node.
If all conditions are met, the method performs two replacements:
-
The variable name is changed to correspond to local memory
-
The index names are changed to correspond to local indices
The method performs the transformation on the node itself. Any old data is thus lost.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/castaddon/transformations.rb', line 24 def transform_use_local_memory(arrays) self.preorder do |node| if (node.index?) && (node.parent) arrays.each do |array| if node.variable_name == array.name node.variable_name = Bones::LOCAL_MEMORY+'_'+array.name array.species.dimensions.each_with_index do |dimension,num_dimension| node.replace_variable(Bones::GLOBAL_ID+'_'+num_dimension.to_s,Bones::LOCAL_ID+'_'+num_dimension.to_s) end end end end end end |
#undefined_variables ⇒ Object
This method returns a list of undefined variables in the node. It walks the node tree until it finds a node that full-fills the following:
-
The node is a variable (
node.variable?) -
The variable is not in a function call (+!node.parent.call?+)
-
The variable is not defined in the code (+!self.variable_type(node.name)+)
124 125 126 127 128 129 130 |
# File 'lib/castaddon/node_bones.rb', line 124 def undefined_variables variables = [] self.preorder do |node| variables.push(node.name) if (node.variable?) && (!node.parent.call?) && (!self.variable_type(node.name)) end return variables.uniq end |
#unit_increment? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘PostInc’, ‘PreInc’ class or if it is of the ‘Assign’ class and adds with a value of 1. Otherwise, it returns ‘false’.
262 263 264 |
# File 'lib/castaddon/node_common.rb', line 262 def unit_increment? 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) end |
#variable? ⇒ Boolean
This method returns ‘true’ if the node is of the ‘Variable’ class. Otherwise, it returns ‘false’.
68 |
# File 'lib/castaddon/node_common.rb', line 68 def variable? ; (self.class == C::Variable) end |
#variable_type(variable_name) ⇒ Object
This method returns the type of a variable (e.g. int, float). The method requires the name of a variable as an argument. It first tries to find a declaration for the variable in by walking through the node. If it cannot find it, it will search for a parameter definition from a function call. If that cannot be found either, the method will return ‘nil’, meaning that the variable is not defined at all in the current node.
65 66 67 68 69 70 71 72 |
# File 'lib/castaddon/node_bones.rb', line 65 def variable_type(variable_name) self.preorder do |node| if node.declarator? || node.parameter? return node.type if node.name == variable_name end end return nil end |