Update defaults for variability of pointed-to types.

Now, if rate qualifiers aren't used to specify otherwise, varying
pointers point to uniform types by default.  As before, uniform
pointers point to varying types by default.

   float *foo;  // varying pointer to uniform float
   float * uniform foo;  // uniform pointer to varying float

These defaults seem to require the least amount of explicit
uniform/varying qualifiers for most common cases, though TBD if it
would be easier to have a single rule that e.g. the pointed-to type
is always uniform by default.
This commit is contained in:
Matt Pharr
2012-02-17 16:52:03 -08:00
parent ad429db7e8
commit 6d7ff7eba2
54 changed files with 187 additions and 131 deletions

View File

@@ -1499,38 +1499,46 @@ Pointer Types
It is possible to have pointers to data in memory; pointer arithmetic,
changing values in memory with pointers, and so forth is supported as in C.
::
float a = 0;
float *pa = &a;
*pa = 1; // now a == 1
Also as in C, arrays are silently converted into pointers:
::
float a[10] = { ... };
float *pa = a; // pointer to first element of a
float *pb = a + 5; // pointer to 5th element of a
As with other basic types, pointers can be both ``uniform`` and
``varying``. By default, they are varying. The placement of the
``uniform`` qualifier to declare a ``uniform`` pointer may be initially
surprising, but it matches the form of how for example a pointer that is
itself ``const`` (as opposed to pointing to a ``const`` type) is declared
in C.
``varying``.
** Like other types in ``ispc``, pointers are ``varying`` by default, if an
explicit ``uniform`` qualifier isn't provided. However, the default
variability of the pointed-to type (``uniform`` or ``varying``) is the
opposite of the pointer variability. ** This rule will be illustrated and
explained in examples below.
For example, the ``ptr`` variable in the code below is a varying pointer to
``uniform float`` values. Each program instance has a separate pointer
value and the assignment to ``*ptr`` generally represents a scatter to
memory.
::
uniform float f = 0;
uniform float * uniform pf = &f;
uniform float a[] = ...;
int index = ...;
float * ptr = &a[index];
*ptr = 1;
A ``uniform`` pointer can be declared with an appropriately-placed
qualifier:
::
float f = 0;
float * uniform pf = &f; // uniform pointer to a varying float
*pf = 1;
A subtlety comes in when a uniform pointer points to a varying datatype.
In this case, each program instance accesses a distinct location in memory
(because the underlying varying datatype is itself laid out with a separate
location in memory for each program instance.)
The placement of the ``uniform`` qualifier to declare a ``uniform`` pointer
may be initially surprising, but it matches the form of how, for example, a
pointer that is itself ``const`` (as opposed to pointing to a ``const``
type) is declared in C. (Reading the declaration from right to left gives
its meaning: a uniform pointer to a float that is varying.)
A subtlety comes in in cases like the where a uniform pointer points to a
varying datatype. In this case, each program instance accesses a distinct
location in memory (because the underlying varying datatype is itself laid
out with a separate location in memory for each program instance.)
::
@@ -1538,17 +1546,26 @@ location in memory for each program instance.)
float * uniform pa = &a;
*pa = programIndex; // same as (a = programIndex)
Also as in C, arrays are silently converted into pointers:
::
float a[10] = { ... };
float * uniform pa = a; // pointer to first element of a
float * uniform pb = a + 5; // pointer to 5th element of a
Any pointer type can be explicitly typecast to another pointer type, as
long as the source type isn't a ``varying`` pointer when the destination
type is a ``uniform`` pointer. Like other types, ``uniform`` pointers can
be typecast to be ``varying`` pointers, however.
type is a ``uniform`` pointer.
::
float *pa = ...;
int *pb = (int *)pa; // legal, but beware
Like other types, ``uniform`` pointers can be typecast to be ``varying``
pointers, however.
Any pointer type can be assigned to a ``void`` pointer without a type cast:
::
@@ -1992,7 +2009,7 @@ based on C++'s ``new`` and ``delete`` operators:
::
int count = ...;
int *ptr = new uniform int[count];
int *ptr = new int[count];
// use ptr...
delete[] ptr;
@@ -2002,13 +2019,22 @@ that memory. Uses of ``new`` and ``delete`` in ``ispc`` programs are
serviced by corresponding calls the system C library's ``malloc()`` and
``free()`` functions.
Note that the rules for ``uniform`` and ``varying`` for ``new`` are
analogous to the corresponding rules are for pointers (as described in
`Pointer Types`_.) Specifically, if a specific rate qualifier isn't
provided with the ``new`` expression, then the default is that a "varying"
``new`` is performed, where each program instance performs a unique
allocation. The allocated type, in turn, is by default ``uniform`` for
``varying`` ``new`` expressions, and ``varying`` for ``uniform`` new
expressions.
After a pointer has been deleted, it is illegal to access the memory it
points to. However, note that deletion happens on a per-program-instance
basis. In other words, consider the following code:
points to. However, that deletion happens on a per-program-instance basis.
In other words, consider the following code:
::
int *ptr = new uniform int[count];
int *ptr = new int[count];
// use ptr
if (count > 1000)
delete[] ptr;
@@ -2033,7 +2059,9 @@ gang of program instances. A ``new`` statement can be qualified with
While a regular call to ``new`` returns a ``varying`` pointer (i.e. a
distinct pointer to separately-allocated memory for each program instance),
a ``uniform new`` performs a single allocation and returns a ``uniform``
pointer.
pointer. Recall that with a ``uniform`` ``new``, the default variability
of the allocated type is ``varying``, so the above code is allocating an
array of ten ``varying float`` values.
When using ``uniform new``, it's important to be aware of a subtlety; if
the returned pointer is stored in a varying pointer variable (as may be
@@ -2043,7 +2071,7 @@ statement, which is an error: effectively
::
float *ptr = uniform new float[10];
varying float * ptr = uniform new float[10];
// use ptr...
delete ptr; // ERROR: varying pointer is deleted
@@ -2052,28 +2080,31 @@ executing program instance, which is an error (unless it happens that only
a single program instance is active in the above code.)
When using ``new`` statements, it's important to make an appropriate choice
of ``uniform`` or ``varying`` (as always, the default), for both the
``new`` operator itself as well as the type of data being allocated, based
on the program's needs. Consider the following four memory allocations:
of ``uniform`` or ``varying``, for both the ``new`` operator itself as well
as the type of data being allocated, based on the program's needs.
Consider the following four memory allocations:
::
uniform float * uniform p1 = uniform new uniform float[10];
float * uniform p2 = uniform new float[10];
uniform float * p3 = new uniform float[10];
float * p4 = new float[10];
float * p3 = new float[10];
varying float * p4 = new varying float[10];
Assuming that a ``float`` is 4 bytes in memory and if the gang size is 8
program instances, then the first allocation represents a single allocation
of 40 bytes, the second is a single allocation of 8*4*10 = 320 bytes, the
third is 8 allocations of 40 bytes, and the last performs 8 allocations of
80 bytes each.
of 10 ``uniform float`` values (40 bytes), the second is a single
allocation of 10 ``varying float`` values (8*4*10 = 320 bytes), the third
is 8 allocations of 10 ``uniform float`` values (8 allocations of 40 bytes
each), and the last performs 8 allocations of 320 bytes each.
Note in particular that varying allocations of varying data types are rarely
desirable in practice. In that case, each program instance is performing a
separate allocation of ``varying float`` memory. In this case, it's likely
that the program instances will only access a single element of each
``varying float``, which is wasteful.
``varying float``, which is wasteful. (This in turn is partially why the
allocated type is uniform by default with both pointers and ``new``
statements.)
Although ``ispc`` doesn't support constructors or destructors like C++, it
is possible to provide initializer values with ``new`` statements: