Skip to content
Snippets Groups Projects

Instantiate plugin

Description

The instantiate plugin is meant to build function specialization to C functions in a project when the original specification (or prototype of the function) cannot be efficiently used by some plugins.

For example, the specification of a function like memcpy cannot be efficiently used by a plugin like WP because it involves void* pointers and requires to deduce that bytes equality implies (typed) data equality. While for example it is easy to produce a typed specification for a type int:

/*@ requires valid_dest: \valid(dest + (0 .. len-1));
    requires valid_read_src: \valid_read(src + (0 .. len-1));
    requires separation: \separated(dest + (0 .. len-1), src + (0 .. len-1));
    ensures copied: ∀ ℤ j0; 0 ≤ j0 < len ⇒ *(dest + j0) ≡ *(src + j0);
    ensures result: \result ≡ dest;
    assigns *(dest + (0 .. len - 1)), \result;
    assigns *(dest + (0 .. len - 1)) \from *(src + (0 .. len - 1));
    assigns \result \from dest;
 */
int *memcpy_int(int *dest, int const *src, size_t len);

That can be easily used by WP.

The role of the Instantiate plugin is to find each call for which an instantiation generator is available and if the call is well-typed according to the generator (for example for memcpy if dest and src are both pointers to integers), to replace this call with a call to an automatically generated specialization corresponding to the call.

Options

  • -instantiate activate function replacement
  • -instantiate-fct=f,... only replace calls in specified functions (default to all)
  • -instantiate-(no-)<function_name> activate or deactivate a particular function, defaults to true.

Available functions

string.h

int memcmp(void const* s1, void const* s2, size_t len);

The parameters s1 and s2 must have the same types, except that qualifiers and typedefs are ignored. The type of s1 or s2 cannot be void*.

void* memcpy(void * dest, void const* src, size_t len);

The parameters dest and src must have the same types, except that qualifiers and typedefs are ignored. The type of dest or src cannot be void*.

void* memmove(void * dest, void const* src, size_t len);

The parameters dest and src must have the same types, except that qualifiers and typedefs are ignored. The type of dest or src cannot be void*.

void* memset(void * ptr, int value, size_t len);

If ptr is of char* type (include signed and unsigned variants), value must be a char.

If ptr is of any other pointer type (except void*), value must be a constant that statically evaluates to 0x00 or 0xFF.

stdlib.h

void* calloc(size_t num, size_t size);

{
  type* res = calloc(...) ;
}

calloc is typed according to the variable that receives its result. It must be a pointer type different from void*. If the pointed type has a flexible array member, num is assumed to evaluate to 1 (a precondition is created for this).

void free(void* ptr);

The type of ptr must be different from void*.

void* malloc(size_t num, size_t size);

{
  type* res = calloc(...) ;
}

malloc is typed according to the variable that receives its result. It must be a pointer type different from void*.

Adding a new instantiator

To add a new instantiator, one has to register a new module implementing the Instantiator_builder.Generator_sig module type to the Transform module. The lasts steps of the code should look like:

let () = Transform.register (module struct
    module Hashtbl = ...
    type override_key = ...

    let function_name = "my_function"
    let well_typed_call = ...
    let key_from_call = ...
    let retype_args = ...
    let generate_prototype = ...
    let generate_spec = ...
    let args_for_original = ...
  end)

The role and types of each function is documented in Instantiator_builder.mli.