Pointer Resolve Transformation

From emmtrix Wiki
Jump to navigation Jump to search

Pointer analysis is a static code analysis technique that determines the set of memory locations a pointer variable can reference at different points in a program. This information is essential for many compiler optimizations, program verifications, and static transformations, as pointers introduce indirect memory accesses that complicate program analysis. By resolving pointer targets, developers and tools can better understand data dependencies, control flow, and potential side effects within a program.

In the context of code transformations, the results of pointer analysis can be leveraged to simplify code by replacing pointer accesses with direct variable references wherever possible. This not only improves code readability but also enables further static analyses and transformations that require more explicit memory access patterns.

The Pointer Resolve Transformation in emmtrix Studio is one such application of pointer analysis. It uses the information gathered during analysis to eliminate pointer usage where safe and feasible, paving the way for enhanced optimization and dependency analysis.

Pointer Resolve Transformation in emmtrix Studio

emmtrix Studio supports pointer resolving both through #pragma directives and via the graphical user interface (GUI). The pointer resolve transformation tries to replace pointers with the actual values they point to and to eliminate pointer arithmetic. This is a helper transformation that can be used to enable further transformations that cannot handle pointers. E.g. the transformation is used by emmtrix Dependency Analysis to improve the analysis results.

Limitations

The transformation resolution has the following limitations:

  • The transformation does not support pointers to pointers.
  • The transformation cannot replace pointers that may point to different variables at runtime.
  • The transformation can only replace pointers to variables that are visible at the point to be replaced. Typical cases are:
    • Local variables are only visible in the function they are declared in. If a pointer to a local variable is passed to another function, the transformation cannot replace the pointer there.
    • Global variables are visible in all functions but a global variable may not be visible to a different translation unit, i.e. because it is declared static.
  • Pointer arithmetic introduces an additional offset variable to store the difference between the pointer and the actual variable. This has further limitations:
    • Offset variables accross function boundaries would require to rewrite the function signature to include the offset variable. This is currently not supported.
    • Casts to different pointer types are not supported.
  • Multidimensional arrays are not supported.

Example

This example shows a simple program that has a get function that returns a pointer to a global variable and a set function that writes a value to the variable the pointer points to. The example has two pointer accesses that are replaced by the global variable g: the pointer access in the set function and the pointer access in the printf function.

Before After
int g;

int* get(void) {
	return &g;
}

void set(int *ptr, int val) {
	*ptr = val;
}

#pragma EMX_TRANSFORMATION PtrResolve
int main(void) {
	int *ptr = get();

	set(ptr, 10);
	
	printf("%d\n", *ptr);
}
int g;

int *get(void) {
	return &g;
}

void set(int *ptr, int val) {
	g = val;
}


int main(void) {
	int *ptr = get();
	
	set(ptr, 10);

	printf("%d\n", g);
}

The following examples show a case where one function is called two times with different pointers. In general, it would not be possible to replace the pointers with the actual variables because the pointers point to different variables. However, emmtrix Studio incorporates a function duplication mechanism that duplicates functions multiple times to aid static program analysis. This mechanism is used to resolve the pointers in the following example.

int a;
int b;
int c;

void swap(int* x, int* y) {
	int z = *x;
	*x = *y;
	*y = z;
}

#pragma EMX_TRANSFORMATION PtrResolve
int main(void) {
	swap(&a, &b);
	swap(&b, &c);
}
int a;
int b;
int c;

void swap(int *x, int *y) {
	int z = b;
	b = c;
	c = z;
}

void swap_duplicate2(int *x, int *y) {
	int z = a;
	a = b;
	b = z;
}

int main(void) {
	swap_duplicate2(&a, &b);
	swap(&b, &c);
}

The following example shows a case with pointer arithmetic. A pointer is incremented in a loop. The transformation replaces the pointer with an offset variable that is incremented in the loop. The pointer access within the loop is replaced with an array access to the global variable g_table and the offset variable. The res variable is not replaced because the local idx variable is not visible in the index_search function.

const int g_table[8] = { 1, 4, 6, 8, 10, 12, 14, 16 };

static void index_search(const int *table, int x, int *res) {
	int i = 0;

	while (x >= *table) {
		i++;
		table++;
	}

	*res = i;
}

#pragma EMX_TRANSFORMATION PtrResolve
int main(void) {
	int idx;

	index_search(g_table, 11, &idx);

	printf("%d\n", idx);
}
const int g_table[8] = {1, 4, 6, 8, 10, 12, 14, 16};

static void index_search(const int *table, int x, int *res) {
	int i = 0;
	long table_off = 0;

	while (x >= g_table[table_off]) {
		i = i + 1;
		table_off = table_off + 1;
	}

	*res = i;
}


int main(void) {
	int idx;

	index_search(g_table, 11, &idx);

	printf("%d\n", idx);
}

Parameters

The following parameters can be configured (each parameter includes its pragma keyword and default value):

Parameter Default Value Description
No parameters

External Links