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.
- data/CHANGELOG +117 -0
- data/LICENSE +9 -0
- data/README.rdoc +126 -0
- data/Rakefile +107 -0
- data/VERSION +1 -0
- data/bin/bones +20 -0
- data/examples/applications/ffos.c +552 -0
- data/examples/benchmarks/2mm.c +70 -0
- data/examples/benchmarks/3mm.c +81 -0
- data/examples/benchmarks/adi.c +81 -0
- data/examples/benchmarks/atax.c +65 -0
- data/examples/benchmarks/bicg.c +67 -0
- data/examples/benchmarks/cholesky.c +64 -0
- data/examples/benchmarks/common.h +168 -0
- data/examples/benchmarks/correlation.c +97 -0
- data/examples/benchmarks/covariance.c +77 -0
- data/examples/benchmarks/doitgen.c +63 -0
- data/examples/benchmarks/durbin.c +76 -0
- data/examples/benchmarks/dynprog.c +67 -0
- data/examples/benchmarks/fdtd-2d-apml.c +114 -0
- data/examples/benchmarks/fdtd-2d.c +74 -0
- data/examples/benchmarks/floyd-warshall.c +50 -0
- data/examples/benchmarks/gemm.c +69 -0
- data/examples/benchmarks/gemver.c +89 -0
- data/examples/benchmarks/gesummv.c +64 -0
- data/examples/benchmarks/gramschmidt.c +84 -0
- data/examples/benchmarks/jacobi-1d-imper.c +55 -0
- data/examples/benchmarks/jacobi-2d-imper.c +61 -0
- data/examples/benchmarks/lu.c +57 -0
- data/examples/benchmarks/ludcmp.c +91 -0
- data/examples/benchmarks/mvt.c +65 -0
- data/examples/benchmarks/overview.txt +38 -0
- data/examples/benchmarks/reg_detect.c +82 -0
- data/examples/benchmarks/saxpy.c +45 -0
- data/examples/benchmarks/seidel-2d.c +51 -0
- data/examples/benchmarks/symm.c +74 -0
- data/examples/benchmarks/syr2k.c +65 -0
- data/examples/benchmarks/syrk.c +62 -0
- data/examples/benchmarks/trisolv.c +57 -0
- data/examples/benchmarks/trmm.c +57 -0
- data/examples/chunk/example1.c +54 -0
- data/examples/chunk/example2.c +44 -0
- data/examples/chunk/example3.c +59 -0
- data/examples/chunk/example4.c +55 -0
- data/examples/chunk/example5.c +52 -0
- data/examples/element/example1.c +46 -0
- data/examples/element/example10.c +50 -0
- data/examples/element/example11.c +47 -0
- data/examples/element/example12.c +56 -0
- data/examples/element/example2.c +46 -0
- data/examples/element/example3.c +58 -0
- data/examples/element/example4.c +49 -0
- data/examples/element/example5.c +56 -0
- data/examples/element/example6.c +46 -0
- data/examples/element/example7.c +54 -0
- data/examples/element/example8.c +45 -0
- data/examples/element/example9.c +48 -0
- data/examples/neighbourhood/example1.c +54 -0
- data/examples/neighbourhood/example2.c +55 -0
- data/examples/neighbourhood/example3.c +82 -0
- data/examples/neighbourhood/example4.c +52 -0
- data/examples/shared/example1.c +45 -0
- data/examples/shared/example2.c +51 -0
- data/examples/shared/example3.c +55 -0
- data/examples/shared/example4.c +52 -0
- data/examples/shared/example5.c +48 -0
- data/lib/bones.rb +266 -0
- data/lib/bones/algorithm.rb +541 -0
- data/lib/bones/engine.rb +386 -0
- data/lib/bones/preprocessor.rb +161 -0
- data/lib/bones/species.rb +196 -0
- data/lib/bones/structure.rb +94 -0
- data/lib/bones/variable.rb +169 -0
- data/lib/bones/variablelist.rb +72 -0
- data/lib/castaddon.rb +27 -0
- data/lib/castaddon/index.rb +40 -0
- data/lib/castaddon/node.rb +753 -0
- data/lib/castaddon/type.rb +37 -0
- data/skeletons/CPU-C/common/epilogue.c +0 -0
- data/skeletons/CPU-C/common/globals.c +17 -0
- data/skeletons/CPU-C/common/globals_kernel.c +1 -0
- data/skeletons/CPU-C/common/header.c +0 -0
- data/skeletons/CPU-C/common/mem_copy_D2H.c +0 -0
- data/skeletons/CPU-C/common/mem_copy_H2D.c +0 -0
- data/skeletons/CPU-C/common/mem_epilogue.c +0 -0
- data/skeletons/CPU-C/common/mem_prologue.c +3 -0
- data/skeletons/CPU-C/common/prologue.c +0 -0
- data/skeletons/CPU-C/common/timer_1_start.c +0 -0
- data/skeletons/CPU-C/common/timer_1_stop.c +0 -0
- data/skeletons/CPU-C/common/timer_2_start.c +20 -0
- data/skeletons/CPU-C/common/timer_2_stop.c +8 -0
- data/skeletons/CPU-C/kernel/default.host.c +3 -0
- data/skeletons/CPU-C/kernel/default.kernel.c +15 -0
- data/skeletons/CPU-C/skeletons.txt +24 -0
- data/skeletons/CPU-OPENCL-AMD/common/epilogue.c +6 -0
- data/skeletons/CPU-OPENCL-AMD/common/globals.c +155 -0
- data/skeletons/CPU-OPENCL-AMD/common/globals_kernel.c +4 -0
- data/skeletons/CPU-OPENCL-AMD/common/header.c +0 -0
- data/skeletons/CPU-OPENCL-AMD/common/mem_copy_D2H.c +8 -0
- data/skeletons/CPU-OPENCL-AMD/common/mem_copy_H2D.c +4 -0
- data/skeletons/CPU-OPENCL-AMD/common/mem_epilogue.c +3 -0
- data/skeletons/CPU-OPENCL-AMD/common/mem_prologue.c +6 -0
- data/skeletons/CPU-OPENCL-AMD/common/prologue.c +24 -0
- data/skeletons/CPU-OPENCL-AMD/common/timer_1_start.c +5 -0
- data/skeletons/CPU-OPENCL-AMD/common/timer_1_stop.c +9 -0
- data/skeletons/CPU-OPENCL-AMD/common/timer_2_start.c +16 -0
- data/skeletons/CPU-OPENCL-AMD/common/timer_2_stop.c +11 -0
- data/skeletons/CPU-OPENCL-AMD/kernel/D-element-to-1-shared.host.c +67 -0
- data/skeletons/CPU-OPENCL-AMD/kernel/D-element-to-1-shared.kernel.cl +72 -0
- data/skeletons/CPU-OPENCL-AMD/kernel/default.host.c +14 -0
- data/skeletons/CPU-OPENCL-AMD/kernel/default.kernel.cl +13 -0
- data/skeletons/CPU-OPENCL-AMD/skeletons.txt +26 -0
- data/skeletons/CPU-OPENCL-INTEL/common/epilogue.c +3 -0
- data/skeletons/CPU-OPENCL-INTEL/common/globals.c +154 -0
- data/skeletons/CPU-OPENCL-INTEL/common/globals_kernel.c +4 -0
- data/skeletons/CPU-OPENCL-INTEL/common/header.c +31 -0
- data/skeletons/CPU-OPENCL-INTEL/common/mem_copy_D2H.c +5 -0
- data/skeletons/CPU-OPENCL-INTEL/common/mem_copy_H2D.c +3 -0
- data/skeletons/CPU-OPENCL-INTEL/common/mem_epilogue.c +3 -0
- data/skeletons/CPU-OPENCL-INTEL/common/mem_prologue.c +4 -0
- data/skeletons/CPU-OPENCL-INTEL/common/prologue.c +24 -0
- data/skeletons/CPU-OPENCL-INTEL/common/timer_1_start.c +5 -0
- data/skeletons/CPU-OPENCL-INTEL/common/timer_1_stop.c +9 -0
- data/skeletons/CPU-OPENCL-INTEL/common/timer_2_start.c +16 -0
- data/skeletons/CPU-OPENCL-INTEL/common/timer_2_stop.c +11 -0
- data/skeletons/CPU-OPENCL-INTEL/kernel/D-element-to-1-shared.host.c +67 -0
- data/skeletons/CPU-OPENCL-INTEL/kernel/D-element-to-1-shared.kernel.cl +72 -0
- data/skeletons/CPU-OPENCL-INTEL/kernel/default.host.c +14 -0
- data/skeletons/CPU-OPENCL-INTEL/kernel/default.kernel.cl +13 -0
- data/skeletons/CPU-OPENCL-INTEL/skeletons.txt +26 -0
- data/skeletons/CPU-OPENMP/common/epilogue.c +0 -0
- data/skeletons/CPU-OPENMP/common/globals.c +37 -0
- data/skeletons/CPU-OPENMP/common/globals_kernel.c +6 -0
- data/skeletons/CPU-OPENMP/common/header.c +0 -0
- data/skeletons/CPU-OPENMP/common/mem_copy_D2H.c +0 -0
- data/skeletons/CPU-OPENMP/common/mem_copy_H2D.c +0 -0
- data/skeletons/CPU-OPENMP/common/mem_epilogue.c +0 -0
- data/skeletons/CPU-OPENMP/common/mem_prologue.c +3 -0
- data/skeletons/CPU-OPENMP/common/prologue.c +0 -0
- data/skeletons/CPU-OPENMP/common/timer_1_start.c +12 -0
- data/skeletons/CPU-OPENMP/common/timer_1_stop.c +0 -0
- data/skeletons/CPU-OPENMP/common/timer_2_start.c +18 -0
- data/skeletons/CPU-OPENMP/common/timer_2_stop.c +8 -0
- data/skeletons/CPU-OPENMP/kernel/D-element-to-1-shared.host.c +27 -0
- data/skeletons/CPU-OPENMP/kernel/D-element-to-1-shared.kernel.c +46 -0
- data/skeletons/CPU-OPENMP/kernel/default.host.c +11 -0
- data/skeletons/CPU-OPENMP/kernel/default.kernel.c +18 -0
- data/skeletons/CPU-OPENMP/skeletons.txt +26 -0
- data/skeletons/GPU-CUDA/common/epilogue.c +0 -0
- data/skeletons/GPU-CUDA/common/globals.c +31 -0
- data/skeletons/GPU-CUDA/common/globals_kernel.c +4 -0
- data/skeletons/GPU-CUDA/common/header.c +0 -0
- data/skeletons/GPU-CUDA/common/mem_copy_D2H.c +3 -0
- data/skeletons/GPU-CUDA/common/mem_copy_H2D.c +3 -0
- data/skeletons/GPU-CUDA/common/mem_epilogue.c +3 -0
- data/skeletons/GPU-CUDA/common/mem_prologue.c +5 -0
- data/skeletons/GPU-CUDA/common/prologue.c +6 -0
- data/skeletons/GPU-CUDA/common/timer_1_start.c +6 -0
- data/skeletons/GPU-CUDA/common/timer_1_stop.c +10 -0
- data/skeletons/GPU-CUDA/common/timer_2_start.c +6 -0
- data/skeletons/GPU-CUDA/common/timer_2_stop.c +10 -0
- data/skeletons/GPU-CUDA/kernel/2xN-N-chunk-1-N-to-D-element.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/2xN-N-chunk-1-N-to-D-element.kernel.cu +105 -0
- data/skeletons/GPU-CUDA/kernel/D-element-to-1-shared.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/D-element-to-1-shared.kernel.cu +119 -0
- data/skeletons/GPU-CUDA/kernel/D-element-to-N-shared.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/D-element-to-N-shared.kernel.cu +166 -0
- data/skeletons/GPU-CUDA/kernel/N-N-chunk-1-N-to-D-element.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/N-N-chunk-1-N-to-D-element.kernel.cu +69 -0
- data/skeletons/GPU-CUDA/kernel/N-neighbourhood-N-to-N-element.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/N-neighbourhood-N-to-N-element.kernel.cu +42 -0
- data/skeletons/GPU-CUDA/kernel/default.host.c +3 -0
- data/skeletons/GPU-CUDA/kernel/default.kernel.cu +28 -0
- data/skeletons/GPU-CUDA/skeletons.txt +30 -0
- data/skeletons/GPU-OPENCL-AMD/common/epilogue.c +3 -0
- data/skeletons/GPU-OPENCL-AMD/common/globals.c +155 -0
- data/skeletons/GPU-OPENCL-AMD/common/globals_kernel.c +4 -0
- data/skeletons/GPU-OPENCL-AMD/common/header.c +0 -0
- data/skeletons/GPU-OPENCL-AMD/common/mem_copy_D2H.c +4 -0
- data/skeletons/GPU-OPENCL-AMD/common/mem_copy_H2D.c +4 -0
- data/skeletons/GPU-OPENCL-AMD/common/mem_epilogue.c +3 -0
- data/skeletons/GPU-OPENCL-AMD/common/mem_prologue.c +3 -0
- data/skeletons/GPU-OPENCL-AMD/common/prologue.c +24 -0
- data/skeletons/GPU-OPENCL-AMD/common/timer_1_start.c +5 -0
- data/skeletons/GPU-OPENCL-AMD/common/timer_1_stop.c +9 -0
- data/skeletons/GPU-OPENCL-AMD/common/timer_2_start.c +4 -0
- data/skeletons/GPU-OPENCL-AMD/common/timer_2_stop.c +11 -0
- data/skeletons/GPU-OPENCL-AMD/kernel/D-element-to-1-shared.host.c +67 -0
- data/skeletons/GPU-OPENCL-AMD/kernel/D-element-to-1-shared.kernel.cl +72 -0
- data/skeletons/GPU-OPENCL-AMD/kernel/default.host.c +14 -0
- data/skeletons/GPU-OPENCL-AMD/kernel/default.kernel.cl +13 -0
- data/skeletons/GPU-OPENCL-AMD/skeletons.txt +26 -0
- data/skeletons/verification/header.c +2 -0
- data/skeletons/verification/timer_start.c +4 -0
- data/skeletons/verification/timer_stop.c +6 -0
- data/skeletons/verification/verify_results.c +23 -0
- data/test/bones/test_algorithm.rb +40 -0
- data/test/bones/test_common.rb +54 -0
- data/test/bones/test_preprocessor.rb +46 -0
- data/test/bones/test_species.rb +21 -0
- data/test/bones/test_variable.rb +84 -0
- data/test/test_helper.rb +106 -0
- 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
|
+
|