/* Test for MIN and MAX expressions involving pointers.
  { dg-do compile }
  { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */

#include "range.h"

#define INT_MAX __INT_MAX__

#define MIN(x, y) ((x) < (y) ? (x) :  (y))
#define MAX(x, y) ((x) < (y) ? (y) :  (x))

typedef __SIZE_TYPE__ size_t;

void* memset (void*, int, size_t);
#define memset(...) sink (memset (__VA_ARGS__))

void sink (void*, ...);

volatile int cond, vi;
char* volatile ptr;

void test_min (void)
{
  const int i1 = SR (1, INT_MAX);
  const int i2 = SR (2, INT_MAX);

  {
    /* Exercise both pointers pointing to a different unknown object plus
       positive constant offset.  Since PTR is volatile P1 and P2 cannot
       normally be considered to point to the same object.  It can only
       be inferred from the MIN expression.  */
    char *p1 = ptr + 1;
    char *p2 = ptr + 2;

    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, INT_MAX);
    // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
    memset (q, 0, DIFF_MAX - 2);
    memset (q, 0, DIFF_MAX);
    // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
    // { dg-warning "writing 9223372036854775807 bytes into a region of size 9223372036854775806" "lp64" { target lp64 } .-2 }
  }

  {
    /* Exercise both pointers pointing to a different unknown object plus
       variable offset.  */
    char *p1 = ptr + vi;
    char *p2 = ptr + vi;

    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, INT_MAX);
  }

  {
    /* Exercise both pointers pointing to the same object plus constant
       offset.  */
    char a2[2];               // { dg-message "at offset 1 into destination object 'a2' of size 2" "note" }
    char *p1 = a2 + 1;
    char *p2 = a2 + 2;

    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);         // { dg-warning "writing 2 bytes into a region of size 1 " }
  }

  {
    /* Exercise both pointers pointing to the same object plus offset
       in a known range.  */
    char a3[3];               // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
    char *pi = a3 + i1;
    char *pj = a3 + i2;

    char *q = MIN (pi, pj);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);         // { dg-warning "writing 3 bytes into a region of size 2 " }
  }

  {
    /* Exercise both pointers pointing to the same object plus variable
       offset.  Verify that no offset is mentioned in the note (since
       its unknown, printing its full range is unnecessary).  */
    char a4[4];               // { dg-message ": destination object 'a4'" "note" }
    char *pi = a4 + vi;
    char *pj = a4 + vi;

    char *q = MIN (pi, pj);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);
    memset (q, 0, 4);
    memset (q, 0, 5);         // { dg-warning "writing 5 bytes into a region of size 4 " }
  }

  {
    /* Exercise a pointer pointing to a known object with one pointing
       to an unknown object.  */
    char a5[5];               // { dg-message ": destination object 'a5'" "note" }
    char *p = ptr;
    char *q = MIN (p, a5);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 5);
    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
  }

  {
    /* Exercise a pointer pointing to a known object plus constant offset
       with one pointing to an unknown object.  */
    char a6[6];               // { dg-message "(at offset 1 into )?destination object 'a6'" "note" }
    char *p1 = ptr;
    char *p2 = a6 + 1;
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 6);
    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
  }

  {
    /* Exercise a pointer pointing to a known object with one pointing
       to an unknown object plus constant offset.  */
    char a7[7];               // { dg-message ": destination object 'a7'" "note" }
    char *p1 = a7;
    char *p2 = ptr + 1;
    /* Since p1 points to a7[0] it must be less than any pointer to a7
       plus positive offset, and so Q == P1.  */
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);
    memset (q, 0, 7);
    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 7 " }
  }

  {
    /* Exercise a pointer pointing to a known object plus constant offset
       with one pointing to an unknown object plus a different constant
       offset.  */
    char a8[8];               // { dg-message "at offset 1 into destination object 'a8'" "note" }
    char *p1 = a8 + 1;
    char *p2 = ptr + 2;
    /* Since P1 points to A8[1] it must be less than or equal to any
       pointer to A8 plus positive offset.  Either way, Q must point
       to A8[1].  */
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 7);
    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 7 " }
  }

  {
    /* Same as above but with larger offsets.  */
    char a9[9];               // { dg-message "at offset 3 into destination object 'a9'" "note" }
    char *p1 = a9 + 3;
    char *p2 = ptr + 4;
    /* Since P1 points to A9[3] it must be less than or equal to any
       pointer anywhere into A9 plus 4, so Q must point to A9[3].  */
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 6);
    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
  }

  {
    /* Same as above but with the offsets reversed.  */
    char a10[10];              // { dg-message "at offset 5 into destination object 'a10'" "note" }
    char *p1 = a10 + 10;
    char *p2 = ptr + 5;
    /* Since P1 points just past the end of A10 it could be either less
       or equal to another pointer anywhere into A10 plus 3 because
       the other pointer itself could start at a non-zero offset that's
       not reflected in the determined offset).  */
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 5);
    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
  }

  {
    char a3[3];               // { dg-message ": destination object 'a3'" "note" }
    char *p1 = ptr;
    char *p2 = a3 + i1;
    char *q = MIN (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);
    memset (q, 0, 4);         // { dg-warning "writing 4 bytes into a region of size 3 " }
  }
}


void test_max (void)
{
  const int i1 = SR (1, INT_MAX);
  const int i2 = SR (2, INT_MAX);

  {
    /* Exercise both pointers pointing to the same object plus constant
       offset.  */
    char a2[2];
    char *pi = a2 + 1;
    char *pj = a2 + 2;

    char *q = MAX (pi, pj);

    memset (q, 0, 1);         // { dg-warning "writing 1 byte into a region of size 0 " }
    memset (q, 0, 2);         // { dg-warning "writing 2 bytes into a region of size 0 " }
  }

  {
    /* Exercise both pointers pointing to the same object plus offset
       in a known range.  */
    char a3[3];               // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
    char *pi = a3 + i1;
    char *pj = a3 + i2;

    char *q = MAX (pi, pj);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);         // { dg-warning "writing 3 bytes into a region of size 2 " }
  }

  {
    /* Exercise both pointers pointing to the same object plus variable
       offset.  Verify that no offset is mentioned in the note (since
       its unknown, printing its full range is unnecessary).  */
    char a4[4];               // { dg-message ": destination object 'a4'" "note" }
    char *pi = a4 + vi;
    char *pj = a4 + vi;

    char *q = MAX (pi, pj);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 3);
    memset (q, 0, 4);
    memset (q, 0, 5);         // { dg-warning "writing 5 bytes into a region of size 4 " }
  }

  {
    /* Exercise a pointer pointing to a known object with one pointing
       to an unknown object.  */
    char a5[5];               // { dg-message ": destination object 'a5'" "note" }
    char *p = ptr;
    char *q = MAX (p, a5);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 5);
    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
  }

  {
    /* Exercise a pointer pointing to a known object plus constant offset
       with one pointing to an unknown object.  */
    char a6[6];               // { dg-message "at offset 1 into destination object 'a6'" "note" }
    char *p1 = ptr;
    char *p2 = a6 + 1;
    char *q = MAX (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 5);
    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 5 " }
  }

  {
    /* Exercise a pointer pointing to a known object with one pointing
       to an unknown object plus constant offset.  */
    char a7[7];               // { dg-message "at offset 1 into destination object 'a7'" "note" }
    char *p1 = a7;
    char *p2 = ptr + 1;
    /* Since p1 points to a7[0] it must be less than any pointer to a7
       plus positive offset, and so Q == P2.  */
    char *q = MAX (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 6);
    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 6 " }
  }

  {
    /* Exercise a pointer pointing to a known object plus constant offset
       with one pointing to an unknown object plus a different constant
       offset.  */
    char a8[8];               // { dg-message "at offset 2 into destination object 'a8'" "note" }
    char *p1 = a8 + 1;
    char *p2 = ptr + 2;
    /* Since P1 points to A8[1] it must be less than or equal to any
       pointer to A8 plus positive offset.  Either way, Q must point
       to A8[2].  */
    char *q = MAX (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 6);
    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 6 " }
  }

  {
    /* Same as above but with larger offsets.  */
    char a9[9];               // { dg-message "at offset 4 into destination object 'a9'" "note" }
    char *p1 = a9 + 3;
    char *p2 = ptr + 4;
    /* Since P1 points to A9[3] it must be less than or equal to any
       pointer anywhere into A9 plus 4, so Q must point to A9[4].  */
    char *q = MAX (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 5);
    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
  }

  {
    /* Same as above but with the offsets reversed.  */
    char a10[10];              // { dg-message "at offset 10 into destination object 'a10'" "note" }
    char *p1 = a10 + 10;
    char *p2 = ptr + 5;
    /* Since P1 points just past the end of A10 it could be either less
       or equal to another pointer anywhere into A10 plus 3 because
       the other pointer itself could start at a non-zero offset that's
       not reflected in the determaxed offset).  */
    char *q = MAX (p1, p2);

    memset (q, 0, 1);         // { dg-warning "writing 1 byte into a region of size 0 " }
  }

  {
    char a11[11];             // { dg-message "at offset \\\[1, 11] into destination object 'a11'" "note" }
    char *p1 = ptr;
    char *p2 = a11 + i1;
    char *q = MAX (p1, p2);

    memset (q, 0, 1);
    memset (q, 0, 2);
    memset (q, 0, 10);
    memset (q, 0, 11);        // { dg-warning "writing 11 bytes into a region of size 10 " }
  }
}

