Class: Adarwin::Engine
Overview
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)
Instance Method Summary collapse
-
#get_children(parent) ⇒ Object
Method to obtain the children of a nest.
-
#initialize ⇒ Engine
constructor
Initializes the engine and processes the command line arguments.
-
#insert_copies(scop_ast) ⇒ Object
Iterate over the loop nests and insert the memory copy annotations into the original AST.
-
#insert_species(scop_ast) ⇒ Object
This method iterates over the loop nests and inserts the species into the original AST.
-
#populate_nests(ast, level = []) ⇒ Object
This method populates the Nest datastructure (recursively).
-
#process ⇒ Object
Method to process a file and to output target code.
-
#remove_inner_species(nests) ⇒ Object
This method removes all species in the current loop nest (called recursively).
-
#write_output ⇒ Object
This method writes the output code to a file.
Constructor Details
#initialize ⇒ Engine
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--to-front, -f: Memcopy optimisation: move 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
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/adarwin/engine.rb', line 30 def initialize @result = {:original_code => [], :species_code => []} # Parse the command line options using the 'trollop' gem. = Trollop:: do version 'A-darwin, part of Bones version '+File.read(ADARWIN_DIR+'/VERSION').strip+' (c) 2013 Cedric Nugteren, Eindhoven University of Technology' NL+'A-darwin is an algorithmic species extraction tool. ' + 'For more information, see the README.rdoc file or visit the Bones/A-darwin website at http://parse.ele.tue.nl/bones/.' + NL + NL + 'Usage:' + NL + ' adarwin --application <input> [OPTIONS]' + NL + 'using the following flags:' opt :application, 'Input application file', :short => 'a', :type => String opt :no_memory_annotations, 'Disable the printing of memory annotations', :short => 'm', :default => false opt :mem_remove_spurious, 'Memcopy optimisation: remove spurious copies', :short => 'r', :default => false opt :mem_copyin_to_front, 'Memcopy optimisation: move copyins to front', :short => 'f', :default => false opt :mem_copyout_to_back, 'Memcopy optimisation: move copyouts to back', :short => 'b', :default => false opt :mem_to_outer_loop, 'Memcopy optimisation: move copies to outer loops', :short => 'l', :default => false opt :fusion, 'Type of kernel fusion to perform (0 -> disable)', :short => 'k', :type => Integer, :default => 0 opt :print_arc, 'Print array reference characterisations (ARC) instead of species', :short => 'c', :default => false opt :silent, 'Become silent (no message printing)', :short => 's', :default => false opt :only_alg_number, 'Only generate code for the x-th species (99 -> all)', :short => 'o', :type => Integer, :default => 99 end Trollop::die 'no input file supplied (use: --application)' if ![:application_given] Trollop::die 'input file "'+[:application]+'" does not exist' if !File.exists?([:application]) [:name] = [:application].split('/').last.split('.').first [:no_memory_annotations] = true if [:print_arc] # Obtain the source code from file @source = File.open([:application],'r'){|f| f.read} @basename = File.basename([:application],'.c') end |
Instance Method Details
#get_children(parent) ⇒ Object
Method to obtain the children of a nest
205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/adarwin/engine.rb', line 205 def get_children(parent) children = [] @nests.map do |nest| if parent.depth+1 == nest.depth if parent.level == nest.level.reverse.drop(1).reverse children << nest end end end return children end |
#insert_copies(scop_ast) ⇒ Object
Iterate over the loop nests and insert the memory copy annotations into the original AST.
263 264 265 266 267 268 269 270 271 272 |
# File 'lib/adarwin/engine.rb', line 263 def insert_copies(scop_ast) @nests.each do |nest| if nest. nest.code.insert_prev(C::StringLiteral.parse(nest.)) end if nest.has_copyouts? nest.code.insert_next(C::StringLiteral.parse(nest.print_copyouts)) end end end |
#insert_species(scop_ast) ⇒ Object
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.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/adarwin/engine.rb', line 220 def insert_species(scop_ast) # Find out where the synchronisation statements are needed sync_needed = [] @nests.each do |nest| sync_needed << nest..map{ |c| c.get_sync_id } sync_needed << nest.copyouts.map{ |c| c.get_sync_id } end sync_needed = sync_needed.flatten.uniq # Insert the annotations into the code sync = 0 @nests.each do |nest| sync = 2*nest.id # Insert the pre-kernel synchronisation barrier if sync_needed.include?(sync) && ![:no_memory_annotations] nest.code.insert_prev(C::StringLiteral.parse(PRAGMA_DELIMITER_START+PRAGMA_SPECIES+' sync '+(sync).to_s+PRAGMA_DELIMITER_END)) end # Insert the pre-kernel species (start of species) if nest.has_species? to_print = ([:print_arc]) ? nest.print_arc_start : nest.print_species_start nest.code.insert_prev(C::StringLiteral.parse(to_print)) end # Insert the post-kernel synchronisation barrier if sync_needed.include?(sync+1) && ![:no_memory_annotations] node = (nest.code.next && nest.code.next.string? && nest.code.next.val =~ /pragma species copyout/) ? nest.code.next : nest.code node.insert_next(C::StringLiteral.parse(PRAGMA_DELIMITER_START+PRAGMA_SPECIES+' sync '+(sync+1).to_s+PRAGMA_DELIMITER_END)) end # Insert the post-kernel species (end of species) if nest.has_species? to_print = ([:print_arc]) ? nest.print_arc_end : nest.print_species_end location = nest.code location.insert_next(C::StringLiteral.parse(to_print)) end end end |
#populate_nests(ast, level = []) ⇒ Object
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.
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 |
# File 'lib/adarwin/engine.rb', line 160 def populate_nests(ast,level=[]) # Only proceed if it is a loop if ast.block? # Create the new loop nests for the current depth level ast.stmts.each_with_index do |nest,index| new_level = level.clone.push(index) # Push the loop nest, but only if it is not disabled by options if [:only_alg_number].to_i == 99 || [:only_alg_number].to_i == (@id+1) # Only continue if the nest is an actual loop nest if nest.for_statement? @nests.push(Nest.new(new_level,nest,@id,@basename,![:silent])) end end @id += 1 end # Proceed to the next depth level. # TODO: Make it an option to only investigate the outer most level(s). ast.stmts.each_with_index do |nest,index| new_level = level.clone.push(index) if nest.stmt # && new_level == 0 populate_nests(nest.stmt,new_level) end end end end |
#process ⇒ Object
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.
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/adarwin/engine.rb', line 70 def process # Run the preprocessor preprocessor = Adarwin::Preprocessor.new(@source) preprocessor.process @result[:header_code] = preprocessor.header_code # Set-up the CAST gem to include certain types # FIXME: What about other (user-defined?) types? parser = C::Parser.new parser.type_names << 'FILE' parser.type_names << 'size_t' # Parse the original source code into AST form (using CAST) original_ast = parser.parse(preprocessor.parsed_code) # Create an AST of the SCoP (using CAST) and save a backup scop_ast = C::Block.parse('{'+preprocessor.scop_code+'}') original_scop_ast = scop_ast.clone # Process the scop to identify the loop nests of interest and to find the # corresponding species. This is the method performing most of the work. @nests = [] @id = 0 populate_nests(scop_ast) # Remove inner-loop (nested) species. This removes all species that are # found within another species. For completeness, this might be desired in # some cases. # TODO: Make this an option @nests.each do |nest| if nest.has_species? remove_inner_species(get_children(nest)) end end @nests.delete_if{ |n| n.removed } # Iterate over the nests/statements to optimize the copies. Currently, # this will only look at loop nests with a depth of 1. Re-call the memory # copy optimisations method every time a change is made. # TODO: Investigate what the depth should be. basenests = @nests.select{ |n| n.depth == 1 } recursive_copy_optimisations(basenests,) # Kernel fusion is enabled (1,2,3,4) or disabled (0) if [:fusion] > 0 # Test if fusion is legal and perform the actual transformation kernel_fusion(@nests, [:fusion]) end # Delete the to-be-removed code (because of fusion) @nests.each do |nest| if nest.removed scop_ast.remove_once(nest.code) end end @nests.delete_if{ |n| n.removed } # Insert the species and memory copy annotations into the original code. # Don't do this if the user specified that he is not interested in the # memory copy annotations. insert_copies(scop_ast) unless [:no_memory_annotations] insert_species(scop_ast) # Create the modified SCoP and remove the quotes from the pragma's # FIXME: This is a hack for now, this has conflicts with strings in code modified_scop = INDENT+SCOP_START+NL+scop_ast.to_s+NL+INDENT+SCOP_END+NL modified_scop = modified_scop.gsub(PRAGMA_DELIMITER_START,'') modified_scop = modified_scop.gsub(PRAGMA_DELIMITER_END,'') # Print the result SCoP puts modified_scop if ![:silent] # Store the result @result[:species_code] = preprocessor.target_code.gsub(preprocessor.scop_code,modified_scop) end |
#remove_inner_species(nests) ⇒ Object
This method removes all species in the current loop nest (called recursively). It assumes these species should be removed.
193 194 195 196 197 198 199 200 201 202 |
# File 'lib/adarwin/engine.rb', line 193 def remove_inner_species(nests) nests.each do |nest| nest. = [] nest.copyouts = [] nest.species = '' nest.removed = true children = get_children(nest) remove_inner_species(children) if children end end |
#write_output ⇒ Object
This method writes the output code to a file.
148 149 150 151 152 153 154 155 |
# File 'lib/adarwin/engine.rb', line 148 def write_output # Populate the species file # TODO: The filename is fixed, make this an optional argument File.open(File.join([:application].split('.').first+'_species'+'.c'),'w') do |target| target.puts @result[:species_code] end end |