class Bones::Preprocessor
This is the C99 pre-processor for Bones
. It has two tasks:
-
To remove all lines starting with ‘#’ (pre-processor directives).
-
To detect all pragma’s forming algorithm classes from the source.
Attributes:¶ ↑
-
header_code
- All the code that was removed by the pre-processor but was not relevant toBones
. This contains for example includes and defines. -
algorithms
- An array of identified algorithms, each of classBones::Algorithm
. -
target_code
- The processed code containing noBones
directives nor other pre-processor directives (such as includes and defines).
Constants
- COPYIN
Copy
in directive.- COPYOUT
Copy
out directive.- DEFAULT_NAME
Providing a default name in case a algorithm is not named.
- IDENTIFIER
Denotes the start of an algorithmic species.
- REGEXP_PREFIX
A regular expression captures a prefix in a algorithm (e.g. unordered/multiple).
- SCOP_END
Enf of the scop
- SCOP_START
Start of the scop
- SPECIES_END
This directive denotes the end of a algorithm. It is based on the
IDENTIFIER
constant.- SPECIES_START
This directive denotes the start of a algorithm. It is based on the
IDENTIFIER
constant.- SYNC
Synchronise directive.
- WHITESPACE
Regular expression to identify whitespaces (tabs, spaces).
Attributes
Public Class Methods
This is the method which initializes the preprocessor. Initialization requires the target source code to process, which is then set as the class variable +@source_code+.
# File lib/bones/preprocessor.rb 47 def initialize(source_code,directory,filename,scheduler) 48 @source_code = source_code 49 @target_code = [] 50 @header_code = '' 51 @device_header = '' 52 @directory = directory 53 @filename = filename 54 @algorithms = Array.new 55 @copies = Array.new 56 @defines = {} 57 @found_algorithms = 0 58 @scheduler = scheduler 59 end
Public Instance Methods
This is the method to perform the actual preprocessing. This method takes care of all the pre-processor tasks. The output is stored in the three attributes header_code
, algorithms
, and target_code
.
# File lib/bones/preprocessor.rb 65 def process 66 algorithm_code = '' 67 species = nil 68 found = 0 69 alloc_index, free_index = 0, 0 70 block_comment = false 71 a_scop_was_found = false 72 scop = 0 73 scop_copies = [] 74 75 # Process the file line by line 76 @source_code.each_line.with_index do |line,index| 77 78 # Don't consider one-line comments 79 if !(line =~ /^#{WHITESPACE}\/\//) 80 81 # Found the start of a block comment 82 if line =~ /\/\*/ 83 block_comment = true 84 end 85 86 # Search for the end of the block comment 87 if block_comment 88 if line =~ /\*\// 89 block_comment = false 90 end 91 @target_code << line 92 93 # Not in a block-comment 94 else 95 96 if line =~ /^#{WHITESPACE}#/ 97 98 # Keep 'include' statements as header code 99 if line =~ /^#{WHITESPACE}#include/ 100 @header_code += line 101 if line =~ /"(.*)"/ 102 process_header($1) 103 end 104 105 # Process 'define' statements for the algorithm code, but also keep as header code 106 elsif line =~ /^#{WHITESPACE}#define/ 107 @header_code += line 108 @device_header += line 109 match = line.split(/\/\//)[0].scan(/^#{WHITESPACE}#define\s+(\w+)\s+(\S*)/) 110 @defines[match.first[0].to_sym] = match.first[1] 111 112 # Found the start of algorithm marker 113 elsif line =~ /^#{WHITESPACE}#{SPECIES_START}/ 114 if found == 0 115 line = replace_defines(line,@defines) 116 prefix, input, output = marker_to_algorithm(line) 117 puts MESSAGE+'Found algorithm "'+(prefix+' '+input+' '+ARROW+' '+output).lstrip+'"' if VERBOSE 118 species = Bones::Species.new(prefix,input,output) 119 @found_algorithms = @found_algorithms + 1 120 end 121 found = found + 1 122 #@target_code << "int bones_temp_species_start = '#{line.gsub(NL,'')}';"+NL 123 124 # Found the end of algorithm marker 125 elsif line =~ /^#{WHITESPACE}#{SPECIES_END}/ 126 if found == 1 127 name = line.strip.scan(/^#{WHITESPACE}#{SPECIES_END} (.+)/).join 128 name = DEFAULT_NAME if name == '' 129 @algorithms.push(Bones::Algorithm.new(name,@filename,index.to_s,species,algorithm_code)) 130 algorithm_code = '' 131 end 132 found = found - 1 133 #@target_code << "int bones_temp_species_end = '#{line.gsub(NL,'')}';"+NL 134 135 # Found a sync marker 136 elsif @scheduler && line =~ /^#{WHITESPACE}#{SYNC}/ 137 sync = line.strip.scan(/^#{WHITESPACE}#{SYNC} (.+)/).join 138 @target_code << "bones_synchronize(#{sync});"+NL 139 140 # Found a copyin marker 141 elsif @scheduler && line =~ /^#{WHITESPACE}#{COPYIN}/ 142 copies = line.strip.scan(/^#{WHITESPACE}#{COPYIN} (.+)/).join.split(WEDGE).map{ |c| c.strip } 143 copies.each_with_index do |copy,copynum| 144 name = copy.split('[').first 145 domain = copy.scan(/\[(.+)\]/).join.split(DIM_SEP) 146 deadline = copy.split('|').last 147 @copies.push(Bones::Copy.new(scop,name,domain,deadline,'in',"#{index*100+copynum}")) 148 scop_copies.push(@copies[-1]) 149 @target_code << "bones_copyin_#{index*100+copynum}_#{name}(#{name});"+NL 150 end 151 152 # Found a copyout marker 153 elsif @scheduler && line =~ /^#{WHITESPACE}#{COPYOUT}/ 154 copies = line.strip.scan(/^#{WHITESPACE}#{COPYOUT} (.+)/).join.split(WEDGE).map{ |c| c.strip } 155 copies.each_with_index do |copy,copynum| 156 name = copy.split('[').first 157 domain = copy.scan(/\[(.+)\]/).join.split(DIM_SEP) 158 deadline = copy.split('|').last 159 @copies.push(Bones::Copy.new(scop,name,domain,deadline,'out',"#{index*100+copynum}")) 160 scop_copies.push(@copies[-1]) 161 @target_code << "bones_copyout_#{index*100+copynum}_#{name}(#{name});"+NL 162 end 163 end 164 165 # Check if it was a 'pragma scop' / 'pragma endscop' line 166 if line =~ /^#{WHITESPACE}#{SCOP_START}/ 167 scop += 1 168 scop_copies = [] 169 alloc_index = @target_code.length 170 a_scop_was_found = true 171 @target_code << 'bones_timer_start();'+NL 172 elsif line =~ /^#{WHITESPACE}#{SCOP_END}/ 173 free_index = @target_code.length 174 @target_code << 'bones_timer_stop();'+NL 175 176 # Add frees and mallocs 177 if @scheduler 178 alloc_code, free_code = '', '' 179 included_copies = [] 180 scop_copies.each do |copy| 181 if !included_copies.include?(copy.name) 182 alloc_code += copy.get_function_call('alloc')+NL 183 free_code += copy.get_function_call('free')+NL 184 included_copies << copy.name 185 end 186 end 187 @target_code.insert(alloc_index, alloc_code) 188 @target_code << free_code 189 end 190 end 191 192 else 193 if found > 0 194 algorithm_line = replace_defines(line,@defines) 195 @target_code << algorithm_line 196 algorithm_code += algorithm_line if line !~ /^#{WHITESPACE}#/ 197 else 198 @target_code << line 199 end 200 end 201 end 202 else 203 @target_code << line 204 end 205 end 206 puts WARNING+'Begin/end kernel mismatch ('+@found_algorithms.to_s+' versus '+@algorithms.length.to_s+'), probably missing a "'+SPECIES_END+'"' unless @algorithms.length == @found_algorithms 207 208 # Print warning if there is no SCoP found 209 if !a_scop_was_found 210 puts WARNING+'No "#pragma scop" and "#pragma endscop" found!' 211 end 212 213 # Join the array 214 @target_code = @target_code.join('') 215 end
This is the method to preprocess a header file. Currently, it only searches for defines and adds those to a list. In the meanwhile, it also handles ifdef’s.
# File lib/bones/preprocessor.rb 220 def process_header(filename) 221 ifdefs = [true] 222 223 # Process the file line by line 224 block_comment = false 225 File.read(File.join(@directory,filename)).each_line.with_index do |line,index| 226 227 # Don't consider one-line comments 228 if !(line =~ /^#{WHITESPACE}\/\//) 229 230 # Found the start of a block comment 231 if line =~ /\/\*/ 232 block_comment = true 233 end 234 235 # Search for the end of the block comment 236 if block_comment 237 if line =~ /\*\// 238 block_comment = false 239 end 240 241 # Not in a block-comment 242 else 243 if line =~ /^#{WHITESPACE}#/ 244 245 # Process 'include' statements 246 if line =~ /^#{WHITESPACE}#include/ && ifdefs.last 247 if line =~ /"(.*)"/ 248 process_header($1) 249 end 250 251 # Process 'define' statements 252 elsif line =~ /^#{WHITESPACE}#define/ && ifdefs.last 253 match = line.split(/\/\//)[0].scan(/^#{WHITESPACE}#define\s+(\w+)\s+(\S*)/) 254 @defines[match.first[0].to_sym] = match.first[1].strip 255 256 # Process 'ifdef' statements 257 elsif line =~ /^#{WHITESPACE}#ifdef#{WHITESPACE}(\w+)/ 258 valid = (ifdefs.last) ? @defines.has_key?($1.to_sym) : false 259 ifdefs.push(valid) 260 261 # Process 'endif' statements 262 elsif line =~ /^#{WHITESPACE}#endif/ 263 ifdefs.pop 264 end 265 end 266 end 267 end 268 end 269 end
Private Instance Methods
Method to extract the algorithm details from a marker found in code.
# File lib/bones/preprocessor.rb 275 def marker_to_algorithm(marker) 276 algorithm = marker.strip.scan(/^#{WHITESPACE}#{SPECIES_START} (.+)/).join 277 prefix = '' 278 if algorithm =~ REGEXP_PREFIX 279 split = algorithm.partition(' ') 280 prefix = split[0] 281 algorithm = split[2] 282 end 283 input = algorithm.split(ARROW)[0].strip 284 output = algorithm.split(ARROW)[1].strip 285 return prefix, input, output 286 end