class Adarwin::Engine
This is the main ‘engine’ for the A-darwin algorithmic species extraction tool. It contains methods to parse the command-line arguments, to run the pre-processor, to insert the annotations, and to pretty print the final output. TODO: Add a syntax check by a normal compiler first (e.g. gcc)
Public Class Methods
Initializes the engine and processes the command line arguments. This method uses the ‘trollop’ gem to parse the arguments and to create a nicely formatted help menu. This method additionally initializes a result- hash and reads the contents of the source file from disk.
Command-line usage:¶ ↑
adarwin --application <input> [OPTIONS]
Options:¶ ↑
--application, -a <s>: Input application file --no-memory-annotations, -m: Disable the printing of memory annotations --mem-remove-spurious, -s: Memcopy optimisation: remove spurious copies --mem-copyin-to-front, -f: Memcopy optimisation: move copyins to front --mem-copyout-to-back, -b: Memcopy optimisation: move copyouts to back --mem-to-outer-loop, -l: Memcopy optimisation: move copies to outer loops --only-alg-number, -o <i>: Only generate code for the x-th species (99 -> all) (default: 99) --version, -v: Print version and exit --help, -h: Show this message
# File lib/adarwin/engine.rb 30 def initialize 31 @result = {:original_code => [], 32 :species_code => []} 33 34 # Parse the command line options using the 'trollop' gem. 35 @options = Trollop::options do 36 version 'A-darwin, part of Bones version '+File.read(ADARWIN_DIR+'/VERSION').strip+' (c) 2013 Cedric Nugteren, Eindhoven University of Technology' 37 banner NL+'A-darwin is an algorithmic species extraction tool. ' + 38 'For more information, see the README.rdoc file or visit the Bones/A-darwin website at http://parse.ele.tue.nl/bones/.' + NL + NL + 39 'Usage:' + NL + 40 ' adarwin --application <input> [OPTIONS]' + NL + 41 'using the following flags:' 42 opt :application, 'Input application file', :short => 'a', :type => String 43 opt :no_memory_annotations, 'Disable the printing of memory annotations', :short => 'm', :default => false 44 opt :mem_remove_spurious, 'Memcopy optimisation: remove spurious copies', :short => 'r', :default => false 45 opt :mem_copyin_to_front, 'Memcopy optimisation: move copyins to front', :short => 'f', :default => false 46 opt :mem_copyout_to_back, 'Memcopy optimisation: move copyouts to back', :short => 'b', :default => false 47 opt :mem_to_outer_loop, 'Memcopy optimisation: move copies to outer loops', :short => 'l', :default => false 48 opt :fusion, 'Type of kernel fusion to perform (0 -> disable)', :short => 'k', :type => Integer, :default => 0 49 opt :print_arc, 'Print array reference characterisations (ARC) instead of species', :short => 'c', :default => false 50 opt :silent, 'Become silent (no message printing)', :short => 's', :default => false 51 opt :only_alg_number, 'Only generate code for the x-th species (99 -> all)', :short => 'o', :type => Integer, :default => 99 52 end 53 Trollop::die 'no input file supplied (use: --application)' if !@options[:application_given] 54 Trollop::die 'input file "'+@options[:application]+'" does not exist' if !File.exists?(@options[:application]) 55 @options[:name] = @options[:application].split('/').last.split('.').first 56 @options[:no_memory_annotations] = true if @options[:print_arc] 57 58 # Obtain the source code from file 59 @source = File.open(@options[:application],'r'){|f| f.read} 60 @basename = File.basename(@options[:application],'.c') 61 end
Public Instance Methods
Method to obtain the children of a nest
# File lib/adarwin/engine.rb 216 def get_children(parent) 217 children = [] 218 @nests.map do |nest| 219 if parent.depth+1 == nest.depth 220 if parent.level == nest.level.reverse.drop(1).reverse 221 children << nest 222 end 223 end 224 end 225 return children 226 end
Iterate over the loop nests and insert the memory copy annotations into the original AST.
# File lib/adarwin/engine.rb 274 def insert_copies(scop_ast) 275 @nests.each do |nest| 276 if nest.has_copyins? 277 nest.code.insert_prev(C::StringLiteral.parse(nest.print_copyins)) 278 end 279 if nest.has_copyouts? 280 nest.code.insert_next(C::StringLiteral.parse(nest.print_copyouts)) 281 end 282 end 283 end
This method iterates over the loop nests and inserts the species into the original AST. It also inserts the synchronisation barries when needed, and only if the user is interested in the memory copy annotations.
# File lib/adarwin/engine.rb 231 def insert_species(scop_ast) 232 233 # Find out where the synchronisation statements are needed 234 sync_needed = [] 235 @nests.each do |nest| 236 sync_needed << nest.copyins.map{ |c| c.get_sync_id } 237 sync_needed << nest.copyouts.map{ |c| c.get_sync_id } 238 end 239 sync_needed = sync_needed.flatten.uniq 240 241 # Insert the annotations into the code 242 sync = 0 243 @nests.each do |nest| 244 sync = 2*nest.id 245 246 # Insert the pre-kernel synchronisation barrier 247 if sync_needed.include?(sync) && !@options[:no_memory_annotations] 248 nest.code.insert_prev(C::StringLiteral.parse(PRAGMA_DELIMITER_START+PRAGMA_SPECIES+' sync '+(sync).to_s+PRAGMA_DELIMITER_END)) 249 end 250 251 # Insert the pre-kernel species (start of species) 252 if nest.has_species? 253 to_print = (@options[:print_arc]) ? nest.print_arc_start : nest.print_species_start 254 nest.code.insert_prev(C::StringLiteral.parse(to_print)) 255 end 256 257 # Insert the post-kernel synchronisation barrier 258 if sync_needed.include?(sync+1) && !@options[:no_memory_annotations] 259 node = (nest.code.next && nest.code.next.string? && nest.code.next.val =~ /pragma species copyout/) ? nest.code.next : nest.code 260 node.insert_next(C::StringLiteral.parse(PRAGMA_DELIMITER_START+PRAGMA_SPECIES+' sync '+(sync+1).to_s+PRAGMA_DELIMITER_END)) 261 end 262 263 # Insert the post-kernel species (end of species) 264 if nest.has_species? 265 to_print = (@options[:print_arc]) ? nest.print_arc_end : nest.print_species_end 266 location = nest.code 267 location.insert_next(C::StringLiteral.parse(to_print)) 268 end 269 end 270 end
This method populates the Nest
datastructure (recursively). It is the main method to process the loop nests and fine the species information. It is called recursively.
# File lib/adarwin/engine.rb 171 def populate_nests(ast,level=[]) 172 173 # Only proceed if it is a loop 174 if ast.block? 175 176 # Create the new loop nests for the current depth level 177 ast.stmts.each_with_index do |nest,index| 178 new_level = level.clone.push(index) 179 180 # Push the loop nest, but only if it is not disabled by options 181 if @options[:only_alg_number].to_i == 99 || @options[:only_alg_number].to_i == (@id+1) 182 183 # Only continue if the nest is an actual loop nest 184 if nest.for_statement? 185 @nests.push(Nest.new(new_level,nest,@id,@basename,!@options[:silent])) 186 @id += 1 187 end 188 end 189 end 190 191 # Proceed to the next depth level. 192 # TODO: Make it an option to only investigate the outer most level(s). 193 ast.stmts.each_with_index do |nest,index| 194 new_level = level.clone.push(index) 195 if nest.stmt # && new_level == 0 196 populate_nests(nest.stmt,new_level) 197 end 198 end 199 end 200 end
Method to process a file and to output target code. This method calls all the other methods, it is the main engine.
Tasks:¶ ↑
-
Run the preprocessor to obtain algorithm information.
-
Use the ‘CAST’ gem to parse the source into an AST.
-
Call the code generator to perform the real work and produce output.
# File lib/adarwin/engine.rb 70 def process 71 72 # Run the preprocessor 73 preprocessor = Adarwin::Preprocessor.new(@source) 74 preprocessor.process 75 @result[:header_code] = preprocessor.header_code 76 77 # Set-up the CAST gem to include certain types 78 # FIXME: What about other (user-defined?) types? 79 parser = C::Parser.new 80 parser.type_names << 'FILE' 81 parser.type_names << 'size_t' 82 83 # Parse the original source code into AST form (using CAST) 84 original_ast = parser.parse(preprocessor.parsed_code) 85 86 # Process every SCoP, one by one 87 @id = 0 88 @result[:species_code] = preprocessor.target_code 89 preprocessor.scop_code.each do |scop_code| 90 process_scop(scop_code) 91 end 92 end
# File lib/adarwin/engine.rb 94 def process_scop(scop_code) 95 # Create an AST of the SCoP (using CAST) and save a backup 96 scop_ast = C::Block.parse('{'+scop_code+'}') 97 original_scop_ast = scop_ast.clone 98 99 # Process the scop to identify the loop nests of interest and to find the 100 # corresponding species. This is the method performing most of the work. 101 @nests = [] 102 populate_nests(scop_ast) 103 104 # return if no loop nests are found in the code 105 return unless @nests.length > 0 106 107 # Remove inner-loop (nested) species. This removes all species that are 108 # found within another species. For completeness, this might be desired in 109 # some cases. 110 # TODO: Make this an option 111 @nests.each do |nest| 112 if nest.has_species? 113 remove_inner_species(get_children(nest)) 114 end 115 end 116 @nests.delete_if{ |n| n.removed } 117 118 # Iterate over the nests/statements to optimize the copies. Currently, 119 # this will only look at loop nests with a depth of 1. Re-call the memory 120 # copy optimisations method every time a change is made. 121 # TODO: Investigate what the depth should be. 122 basenests = @nests.select{ |n| n.depth == 1 } 123 recursive_copy_optimisations(basenests,@options) 124 125 # Kernel fusion is enabled (1,2,3,4) or disabled (0) 126 if @options[:fusion] > 0 127 # Test if fusion is legal and perform the actual transformation 128 kernel_fusion(@nests, @options[:fusion]) 129 end 130 131 # Delete the to-be-removed code (because of fusion) 132 @nests.each do |nest| 133 if nest.removed 134 scop_ast.remove_once(nest.code) 135 end 136 end 137 @nests.delete_if{ |n| n.removed } 138 139 # Insert the species and memory copy annotations into the original code. 140 # Don't do this if the user specified that he is not interested in the 141 # memory copy annotations. 142 insert_copies(scop_ast) unless @options[:no_memory_annotations] 143 insert_species(scop_ast) 144 145 # Create the modified SCoP and remove the quotes from the pragma's 146 # FIXME: This is a hack for now, this has conflicts with strings in code 147 modified_scop = INDENT+SCOP_START+NL+scop_ast.to_s+NL+INDENT+SCOP_END+NL 148 modified_scop = modified_scop.gsub(PRAGMA_DELIMITER_START,'') 149 modified_scop = modified_scop.gsub(PRAGMA_DELIMITER_END,'') 150 151 # Print the result SCoP 152 puts modified_scop if !@options[:silent] 153 154 # Store the result 155 @result[:species_code].gsub!(scop_code,modified_scop) 156 end
This method removes all species in the current loop nest (called recursively). It assumes these species should be removed.
# File lib/adarwin/engine.rb 204 def remove_inner_species(nests) 205 nests.each do |nest| 206 nest.copyins = [] 207 nest.copyouts = [] 208 nest.species = '' 209 nest.removed = true 210 children = get_children(nest) 211 remove_inner_species(children) if children 212 end 213 end
This method writes the output code to a file.
# File lib/adarwin/engine.rb 159 def write_output 160 161 # Populate the species file 162 # TODO: The filename is fixed, make this an optional argument 163 File.open(File.join(@options[:application].rpartition('.').first+'_species'+'.c'),'w') do |target| 164 target.puts @result[:species_code] 165 end 166 end