ROSE
0.9.6a
|
Here we will look at an example of how to create a function in ROSE and insert it in the code.
The process is broken down into independent pieces, and each one is explained separately.
Let's suppose that we have this input program
In this example, we will create a function called "my_function" that takes an integer by reference and it increments it. The function declaration will be inserted in the code, along with the function call. The resulting code will be :
Now let's see what are the steps involved in doing this task.First, we look at the file list that the project contains. The global object for the first project file is extracted
We create the function return type, and for this example we create a type "int". Then we create a new Sg_File_Info object with the same name as the first file in the project, and we set it to be part of the transformation. The function name is set to "my_function". We create the function type , which is composed out of the return type and wether or not the function has ellipses in the parameter list ( in this case it doesn't). We then create a function declaration which is composed out of the file where the new function belongs to, the function type and the function name. Since we will implemenet the function right after its declaration, we will have to create a function defition, which is composed out of the file info object and the function declaration pointer for which the definition is created. .
Right now we have a function declaration and an empty function definition, which does not have a function body yet.
In the function definition, we need to set the function body ( which is a SgBasicBlock object right now, but we'll change it to SgBlock in the future). We attach an empty function body to the declaration. We also set the parent of the function definition to be the function declaration.
So far, we have created a function declaration and defition, whose return type is "int" and it does not have a function body, nor does it have any paramters in the parameter list. Ok, let's create a parameter and insert it in the function parameter list.
First, we create a variable name, in this example is "var_name". We also create a reference int type (so the variable "var_name" will be of type "int" and it will be passed by reference). Since we decided not to have a default value for the argument, the argument will not have an initializer ( if the argument has a default value, then the initializer would have to be set to the default value).
We need to create an SgInitializedName, since the argument in the function parameter list similar to a variable declaration. By creating an SgInitializedName, we create a new definition for the variable "var_name" which has the function body as the scope. In constructing the SgInitializedName, we call the constructor with the variable name, the variable type (in this case is a reference type) and the file info.
Once the SgInitializedName was created, we can insert it in the function parameter list using "append_arg".
So far we have a function with an int return type, a reference to in type variable "var_name" in the parameter list, and an empty function body.Ok, let's insert a statement into the function body.
Since we decided to insert a statement that increments the function's argument, the statement is of type SgExprStatement (expression statement). In order to create an expression statement, we need an expression. In our case, the expression will be pf type SgPlusPlusOp (unary expression).
First, we create a variable reference ( a variable usage) object of type SgVarRefExp. This object has to have the variable symbol of the variable we want to use. When a variable symbol is created, it needs the variable declaration of the variable we want to use, so we need to find the varable declaration that declares the variable we want to use.
Once we have the SgVarRefExp object, we can create the unary ++ expression (thesecond argument in the SgPlusPlusOp constructor sets wether the ++ operator is prefix or post fix :e.g. ++x or x++). IN our example we decided to have prefix ++ operator.
Now we will create an expression root for the newly created expression. The expression root will have the same type as the variable "var_name" since ++ :int -> int. It will also belong to a statement.
We create an empty statement, and then create an expression root for this statement. Then we create a statement that has the newly created expression. We set the epxression root's parent to the statement, and the expression's parent to the expression root.
Now that we have the statement we insert into the function body using "prepend statement", which inserts the statement at the top of the function body ( top of the block).
Now the function is created, but we have to insert it in the code. Let's look at how to insert a function declaration into code.
We get the global object and insert the function declaration at the very top of the scope, using prepend_declaration. We inserted it at the very top because we want to use the function "my_function" in the main function, therefore "my_function" has to be declared before the main function.
Now let's create a function call
A function call is an expression statement. This expression statement contains an expression root (which has the expression and the expression type) and a function call expression. The function call expression has a SgFunctionRefExp ( function usage, just like in SgVarRefExp_create), a function type and a list of expressions for its arguments. The SgFunctionRefExp contains a function symbol and a function type.
First, we create a function symbol from a function declaration (just like we did with the variable symbol SgVariableSymbol_create). Once we have the symbol and the function type (created above) we can create a "usage" object of the function. In order to create the function call expression, we need the function re expression (usage), the list of expressions for the arguments and the function type (for now the list of expressions is empty, we'll initialize it later SgExprListExp_create)
The expression root is initialized with the function call expression, function type (which is the type of the expression root ) and the expression statement it belongs to (for now it's empty).
Now we create the expression statement with the function call as the expression and set the function call parent's to the expression root.
Now we have a function call statement, but the function call does not have any parameters initialized for its argument
Now, we have to initialize the parameters in the function call epxression. We decided to use variable "x" from the main program as the paramter. Therefore, we have to create a SgVarRefExp object that will represent the usage of variable x, declared with the statement "int x=5;" So, first we find the variable declaration that has the SgInitializedName for x. Once we found it, we create a SgVariableSymbol from that declaration and the a SgVarRefExp object from the variable symbol.
Now we can add it to the list of expressions for the parameter list of the function call, using append_expression.
Now we have the function call object comprehensively initialized, ready to go into the code. In this example we use the LowLevelRewrite mechanism to insert a statement in code. We create a list of the statements that we want to insert. We find the statement where we want to insert the list of statements, and insert them using LowLevelRewrite::insert. If the second parameter is set to true, it inserts the list of statements before the reference statement, otherwise it inserts them after ( in our case we wanted to insert the function call after the declaration of x since the function is called with x as a parameter).
Now the example is complete, we have -created a function declaration -created a function definition -inserted a statement into the function body -initialized the parameter list for the function declaration -created a function call statement -initalized the function call parameter list with a variable.
Here is the whole example