Need advice on bounds checking approaches
Greg McGary
gkm@eng.ascend.com
Fri Mar 24 11:18:00 GMT 2000
The final implementation phase for bounded pointers is to generate the
code that does the checks. There are some choices to make, and I'd
appreciate hearing from experienced maintainers how best to do it.
The primary goal is to do this in a way that supports optimizing away
redundant checks.
Checks need to be generated at the time of pointer dereference or
array reference. Let's focus on pointer dereference.
Consider this code:
void
foo (char *p)
{
*p = 1;
}
Recall that the type char * has been transformed internally to a
bounded pointer type like this:
struct charp { char *value, *base, *extent; };
So, our function is internally equivalent to this:
void
foo (struct charp p)
{
*p.value = 1;
}
One place to generate checks is in the IR, transforming the function
into something like this:
void
foo (struct charp p)
{
*({ if (p.value < p.base || p.value >= p.extent)
abort ();
p.value; }) = 1;
}
This is easy to implement, but seems to have drawbacks for
optimization: The if statement expands to a sequence of jumps whose
presence creates new basic-block boundaries. Also, the resulting RTL
isn't readily identifiable as a bounds check
An alternate approach is to transform the function like so:
void
foo (struct charp p)
{
*__builtin_check_bounds (p) = 1;
}
Where __builtin_check_bounds accepts a bounded pointer argument and
returns a simple pointer value, and as a side effect, injects
bounds-checking RTL nodes into the insn stream
DEF_RTL_EXPR(CHECK_BOUNDS, "check_bounds", "eee", 'x')
The three args are pointer value, base & extent.
Now, the optimization passes (CSE, most likely), can easily identify
redundant checks and eliminate them. Moreover, if a machine has a
bounds-checking instruction, or a better than normal insn sequence for
bounds checking, it can define an insn to recognize the "check_bounds"
pattern.
E.g., the most efficient way to check bounds on i960 is with this
sequence (ptr & bas stand for registers holding those BP components):
cmpo ptr, bas ; cc=100 on failure
concmp ext, ptr ; cc=010 on failure
faultle.f
If a machine can't do anything better then it will need to default
to something like this (in pseudo asm):
cmp ptr, bas
blt 0f
cmp ptr, ext
blt 1f
0: call abort
1:
Question: with the above plan, is there a way to provide a default
expansion of the "check_bounds" pattern into primitive RTL
(comparisons, conditional branches and call to abort) for those
targets that don't define an insn for "check_bounds"?
The i960 will define an insn for this, so that it can do better,
but most other targets won't be able to do better and it would be nice
to avoid having to hack every MD file.
Is there some other, better way to go?
Thanks,
Greg
More information about the Gcc
mailing list