GCC GIMPLE C++ API, how to insert a call to a member-function from another member-function?

209 views Asked by At

I'm trying to write a plugin that adds support for [[invariant]] contracts in classes/structs to complete the Contracts support that was added recently in GCC.

This boils down to basically translating something like the below:

class Heap {
private:
    // ... fields

    [[invariant]]
    void check_invariants()
    {
        assert(size >= 0);
        assert(capacity >= 0);
        assert(size <= capacity);
    }

public:
    // constructor, push, pop, etc
};

Into a form where calls to this->invariant(); are made on the entry/exit of every member function that is not the [[invariant]] method itself.

So e.g., push() becomes:

void push(int value)
{
    this->check_invariants(); // AUTO-GENERATED
    data[size] = value;
    size++;
    this->check_invariants(); // AUTO-GENERATED
}

Now, I've managed to get most of this functionality I think, but I'm running into an issue where I can't seem to get the insertion of the call to this->check_invariants(); to work.

What I have is:

/**
 * Create a function call to 'this->invariant()' and insert it before the given
 */
static void
insert_instrumentation_fn(gimple* curr_stmt)
{
  tree ctype   = DECL_CONTEXT(current_function_decl);
  tree fn_decl = lookup_member(ctype,                       // class type
                               get_identifier("invariant"), // function name
                               1,                           // allow protected access
                               0,                           // do not raise error if not found
                               tf_warning_or_error          // error handling
  );

  // Extract the function declaration from the member function
  if (TREE_CODE(fn_decl) == BASELINK)
  {
    fn_decl = BASELINK_FUNCTIONS(fn_decl);
  }

  // build new method call
  // call is of the form: this->invariant()
  // Get the 'this' pointer
  tree              this_ptr = cp_build_fold_indirect_ref(DECL_ARGUMENTS(current_function_decl));
  vec<tree, va_gc>* args     = make_tree_vector_single(this_ptr);

  tree call = build_new_method_call(this_ptr,  // object
                                    fn_decl,   // function
                                    &args,     // arguments
                                    NULL_TREE, // conversion function
                                    LOOKUP_NORMAL,
                                    NULL,      // function decl
                                    tf_warning_or_error);

  // build gimple call
  gimple* call_stmt = gimple_build_call(call, 0);
  // insert call before current statement
  gimple_stmt_iterator gsi = gsi_for_stmt(curr_stmt);
  gsi_insert_before(&gsi, call_stmt, GSI_SAME_STMT);
}

The issue is that this code produces an error like the below:

[build] > Inspecting function '__ct_base '
[build]      Found invariant attribute: invariant
[build]      adding function call before *this_3(D) ={v} {CLOBBER};
[build] /home/user/projects/gcc-invariant-plugin/test/test.cpp: In constructor 'Heap::Heap(int)':
[build] /home/user/projects/gcc-invariant-plugin/test/test.cpp:66:1: error: call to non-function 'void Heap::invariant()'

NOTE: I based this code off of examples in the GCC gcc/cp/coroutines.cc code generator I found:

tree ash_m = lookup_member (coro_frame_type, coro_self_handle_id, 1,
                0, tf_warning_or_error);
tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
                       false, tf_warning_or_error);
/* So construct the self-handle from the frame address.  */
tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1,
                0, tf_warning_or_error);
r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp);
vec<tree, va_gc> *args = make_tree_vector_single (r);
tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL,
                  NULL, tf_warning_or_error)
0

There are 0 answers