ROSE Compiler Framework/Inliner

From Wikibooks, open books for an open world
Jump to navigation Jump to search

The ROSE Inliner inlines functions at function callsites.

Background[edit]

An inline function is one for which the compiler copies the code from the function definition directly into the code of the calling function rather than creating a separate set of instructions in memory. Instead of transferring control to and from the function code segment, a modified copy of the function body may be substituted directly for the function call. In this way, the performance overhead of a function call is avoided. The inline specifier is only a suggestion to the compiler that an inline expansion can be performed; the compiler is free to ignore the suggestion.

User Instructions[edit]

You must enable EDG 5.0 to inline C++11 code

  • --enable-edg_version=5.0

Source code[edit]

API and implementation:

// main API
bool doInline(SgFunctionCallExp*, bool)

Algorithm[edit]


216 // Main inliner code.  Accepts a function call as a parameter, and inlines
217 // only that single function call.  Returns true if it succeeded, and false
218 // otherwise.  The function call must be to a named function, static member
219 // function, or non-virtual non-static member function, and the function
220 // must be known (not through a function pointer or member function
221 // pointer).  Also, the body of the function must already be visible.
222 // Recursive procedures are handled properly (when allowRecursion is set), by
223 // inlining one copy of the procedure into itself.  Any other restrictions on
224 // what can be inlined are bugs in the inliner code.
225 bool
226 doInline(SgFunctionCallExp* funcall, bool allowRecursion)


Eligibility check

  • a named function,
  • static member function, or
  • non-virtual non-static member function, // skip virtual functions
  • the function must be known (not through a function pointer or member function pointer). // empty function reference expression
  • the body of the function must already be visible. // skip functions with empty body


Steps

  • eligibility check: skip things which cannot be inlined
  • obtain the list of actual argument
  • make a copy of the body of the function to be inlined
  • rename labels in an inlined function definition. goto statements to them will be updated.
  • in the function body
    • create local variables , one per formal argument, initialize each with the actual argument
    • build a paramMap: mapping formal arguments (SgInitializedName) to new local variables (SgVariableSymbol)
    • this pointer is handled similarly: create a local variable , initialized with the caller's this pointer
    • replace variable references in the body with actual arguments // ReplaceParameterUseVisitor(paramMap).traverse(funbody_copy, postorder);
    • insert a label to indicate the end of the inlined function body // rose_inline_end__

Limitations of this algorithm is not very clean:

  • It generates new local variables and a label.

example[edit]

--------------input----------------
bash-4.2$ cat specimen25_1.C
 template<typename T>
 void swap(T& x, T& y)
 {
   T tmp = x;
   x = y;
   y = tmp;
 }
 
int foo (int a, int b)
{
   swap(a,b);
}
 
int main()
{
}
 
----- command line  -------------
bash-4.2$ inlineEverything -c -skip-postprocessing specimen26_1.C
 
-----------output: with postprocessing (cleanup) --------------
// output without postprocessing: cleanup
template < typename T >
 void swap ( T & x, T & y )
 {
   T tmp = x;
   x = y;
   y = tmp;
 }

int foo(int a,int b)
{
{
    int &x__2 = a;    // local variables for each formal arguments, initialized with actual arguments
    int &y__3 = b;

    int tmp = x__2;   // variable references are replace with the local variables
    x__2 = y__3;
    y__3 = tmp;
    rose_inline_end__4:   // a label to indicate the end of the outlined function body. 
    ;
  }
}

int main()
{
}

postprocessing[edit]

inlineEverything.C has a clean up step to make the outlined code better

  • cleanupInlinedCode(sageProject);
    • remove unused labels
    • remove jumps to next statement
    • simpleCopyAndConstantPropagation() // In code with declarations such as "int foo = bar", where foo and bar are not modified, replace "foo" with "bar" and remove the declaration
    • remove null statements
    • move declaration to first use
    • doSubexpressionExpansionSmart () // Replaces all uses of a variable by its initialing expression. Requires that initname has an assign initializer Replaces all uses of initname in initname's scope by copy of its initializer expression. Then removes initname.
  • changeAllMembersToPublic(sageProject);

This step does lots of things and can easily trigger some bugs.

  • Even wrose, the cleanup step operates on the entire AST, including C++ headers.

Testing[edit]

Test directory with an example translator and test input files

By looking into Makefile.am, the example translator's source code will generate an executable named "inlineEverything" in your buildtree.

This is the tool you can try to inline your sample code.

  • inlineEverything

The same Makefile.am's make check rules contain sample command lines to use the tool.

To test one individual input file (such as template_functions.C), type

  • make inlineEverything_template_functions.C.passed

Command line options[edit]

inlineEverything --help

---------------------Tool-Specific Help-----------------------------------
This is a program transformation tool to inline function calls in your C/C++ or Fortran code.
Usage: inlineEverything -c [options] input.c


The optional options include: 
 -skip-postprocessing: Skip postprocessing which cleanups code
 -process-headers:     Process calls within header files

postprocessing[edit]


--------------input----------------
bash-4.2$ cat specimen25_1.C
 template<typename T>
 void swap(T& x, T& y)
 {
   T tmp = x;
   x = y;
   y = tmp;
 }
 
int foo (int a, int b)
{
   swap(a,b);
}
 
int main()
{
}
 
----- command line  -------------
bash-4.2$ inlineEverything -c specimen26_1.C
 
-----------output: with postprocessing (cleanup) --------------
bash-4.2$ cat rose_specimen25_1.C 
template < typename T >
 void swap ( T & x, T & y )
 {
   T tmp = x;
   x = y;
   y = tmp;
 }
 
int foo(int a,int b)
{
{
    int tmp = a;
    a = b;
    b = tmp;
  }
}
 
int main()
{
}



// output without postprocessing: cleanup
template < typename T >
 void swap ( T & x, T & y )
 {
   T tmp = x;
   x = y;
   y = tmp;
 }

int foo(int a,int b)
{
{
    int &x__2 = a;
    int &y__3 = b;
    int tmp = x__2;
    x__2 = y__3;
    y__3 = tmp;
    rose_inline_end__4:
    ;
  }
}

int main()
{
}


Correctness checking[edit]

make check only check if the number of inlined functions is the same as expected.

# Note: must use the name convention of specimenXX_N.C , in which N is the number of function calls inlined.   
# The specimens are named so that the number between the "_" and next "." is the number of function calls that
# we expect this specimen to inline.
inlineEverything_specimens =                    \
        specimen01_1.C   ..




inlineEverything_test_targets = $(addprefix inlineEverything_, $(addsuffix .passed, $(inlineEverything_specimens)))
TEST_TARGETS += $(inlineEverything_test_targets)
$(inlineEverything_test_targets): inlineEverything_%.passed: % inlineEverything inlineEverything.conf
        @$(RTH_RUN)                                                                                             \
                TITLE="inlineEverything $< [$@]"                                                                \
                SPECIMEN="$(abspath $<)"                                                                        \
                NINLINE="$$(echo $(notdir $<) |sed --regexp-extended 's/specimen[0-9]+_([0-9]+).*/\1/')"        \
                TRANSLATOR="$$(pwd)/inlineEverything"                                                           \
                $(srcdir)/inlineEverything.conf $@


 cat inlineEverything.conf 
# Test configuration file (see "scripts/rth_run.pl --help" for details)
# Tests the inliner

# Run the tests in subdirectories for ease of cleanup.
subdir = yes

# Run the test and then make sure the output contains a certain string
cmd = ${VALGRIND} ${TRANSLATOR} -rose:verbose 0 ${SPECIMEN} -o a.out |tee ${TEMP_FILE_0}
cmd = grep "Test inlined ${NINLINE} function" ${TEMP_FILE_0}
cmd = cat -n rose_*
cmd = ./a.out

# Extra stuff that might be useful to specify in the makefile
title = ${TITLE}
disabled = ${DISABLED}
timeout = ${TIMEOUT}

examples[edit]

Input code:


#include <vector>
typedef int    Index_t ;

struct Domain
{
  public:
    // non-reference type
    Index_t  numNode()            { return m_numNode ; }

    void AllocateNodeElemIndexes()
    {
      Index_t numNode = this->numNode() ;
    }

#if 0  // the best inline result should look like the following
    void AllocateNodeElemIndexes_inlined()
    {
      Index_t numNode = m_numNode; // call site 1 inlined
    }
#endif

  private:
    Index_t   m_numNode ;
} domain;

Output code

#include <vector>

typedef int Index_t;

struct Domain {

  // non-reference type
  inline Index_t numNode()
  {
    return (this) -> m_numNode;
  }

  inline void AllocateNodeElemIndexes()
  {

//x. split declaration + initializer into two parts 
//  for the code pattern of  var =func(x); 
// a temporary variable to transfer value of initializer

    Index_t rose_temp__3;

//x. a new code block to embed the function body
    {
      struct Domain *this__1 = this__1;
      {
        rose_temp__3 = this__1 -> m_numNode;

//x. goto the label after function call
        goto rose_inline_end__2;
      }

//x. label after the function call
rose_inline_end__2:
      ;
    }

    Index_t numNode = rose_temp__3;
  }

  Index_t m_numNode;

} domain;

Tutorial[edit]

Official documentation about how to call the inlining API is:


Troubleshooting[edit]

TEST inlineEverything ../../../../../../sourcetree/tests/nonsmoke/functional/roseTests/astInliningTests/template_functions.C [inlineEverything_template_functions.C.passed]

inlineEverything_template_functions.C [out]: Note: C++11 input files to ROSE are NOT supported using EDG 4.9 configuration with GNU compilers 4.9 and greater (configure ROSE using EDG 4.12)