blob: 117afa3c0bba7e3d5476df4d23faa62fbd6f3769 [file]
#include "analyzer-decls.h"
typedef __SIZE_TYPE__ size_t;
void *
hide_from_optimizer (const void *ptr) __attribute__((noinline));
void *
hide_from_optimizer (const void *ptr)
{
return (void *)ptr;
}
unsigned char global_buf[1024];
void
test_pointer_eq (unsigned *p, int i, int j)
{
unsigned char local_declared_before_local_buf;
unsigned char local_buf[16];
unsigned char local_declared_after_local_buf;
__analyzer_eval (p == hide_from_optimizer (p)); // { dg-warning "TRUE" }
__analyzer_eval (local_buf == &local_buf[0]); // { dg-warning "TRUE" }
/* Comparisons with NULL. */
__analyzer_eval (hide_from_optimizer (local_buf) == NULL); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (NULL) == NULL); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (NULL) == p); // { dg-warning "UNKNOWN" }
/* Same concrete base region. */
{
// Same concrete offsets
__analyzer_eval (local_buf == hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
// Different concrete offsets
__analyzer_eval (local_buf == hide_from_optimizer (&local_buf[1])); // { dg-warning "FALSE" }
// Same symbolic offset
__analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[i])); // { dg-warning "TRUE" }
// Possibly different symbolic offset
{
__analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
if (i == j)
{
__analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
// { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
}
else
{
__analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "FALSE" "ideal behavior" { xfail *-*-* } }
// { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
}
}
}
/* Different concrete base regions. */
{
__analyzer_eval (local_buf == hide_from_optimizer (global_buf)); // { dg-warning "FALSE" }
// Within the buffer, we're definitely not pointing at other locals
__analyzer_eval (&local_buf[0] == hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "FALSE" }
__analyzer_eval (&local_buf[15] == hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "FALSE" }
/* Outside the valid bounds of the buffer is undefined behavior.
We currently happen to return FALSE for such attempts to form
pointers to neighboring locals. */
__analyzer_eval (&local_buf[-1] == hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "FALSE" }
__analyzer_eval (&local_buf[16] == hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "FALSE" }
}
/* Concrete vs symbolic where we know they're different
(buf is local, so param p can't point to it). */
__analyzer_eval (local_buf == hide_from_optimizer (p)); // { dg-warning "FALSE" }
/* Concrete vs symbolic where we don't know they're different
(glob is global, so param p could point to it). */
__analyzer_eval (global_buf == hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
// What about symbolic, could different offsets be the same?
// TODO: what about overflow of locals, to try to alias (undefined behavior)
}
void
test_nonempty_heap_allocs (void)
{
int i;
// Known non-zero size
void *p = __builtin_malloc (1024);
void *q = __builtin_malloc (1024);
__analyzer_eval (hide_from_optimizer (p) == p); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (p) == q); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (p) == &i); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (q) == &i); // { dg-warning "FALSE" }
__builtin_free (p);
__builtin_free (q);
}
void
test_maybe_empty_heap_allocs (size_t sz_p, size_t sz_q)
{
int i;
/* These could be zero-sized buffers, which some implementations
return null for. */
void *p = __builtin_malloc (sz_p);
void *q = __builtin_malloc (sz_q);
__analyzer_eval (hide_from_optimizer (p) == p); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (p) == q); // { dg-warning "UNKNOWN" }
__analyzer_eval (hide_from_optimizer (p) == &i); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (q) == &i); // { dg-warning "FALSE" }
__builtin_free (p);
__builtin_free (q);
}
void
test_one_past_end (void)
{
int arr[10];
int *end = &arr[10];
__analyzer_eval (hide_from_optimizer (end) == &arr[10]); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (end) == &arr[9]); // { dg-warning "FALSE" }
}
void
test_string_literals (void)
{
const char *abc = "abc";
const char *def = "def";
__analyzer_eval (hide_from_optimizer (abc) == &abc[0]); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (abc) == &abc[1]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
// { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
__analyzer_eval (hide_from_optimizer (abc) == &def[0]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
// { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
__analyzer_eval (hide_from_optimizer (abc) == &def[1]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
// { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
}
struct coord {int x; int y; };
void
test_struct_pointers (void)
{
struct coord c1;
struct coord c2;
__analyzer_eval (hide_from_optimizer (&c1) == &c1.x); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (&c1) == &c1.y); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (&c1) == &c2.x); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (&c1) == &c2.y); // { dg-warning "FALSE" }
}
union u {int x; char y; };
void
test_union_pointers (void)
{
union u u1;
union u u2;
__analyzer_eval (hide_from_optimizer (&u1) == &u1.x); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (&u1) == &u1.y); // { dg-warning "TRUE" }
__analyzer_eval (hide_from_optimizer (&u1) == &u2.x); // { dg-warning "FALSE" }
__analyzer_eval (hide_from_optimizer (&u1) == &u2.y); // { dg-warning "FALSE" }
}