Added tests and documentation for soa<> rate qualifier.

This commit is contained in:
Matt Pharr
2012-03-05 09:50:26 -08:00
parent db5db5aefd
commit 7adb250b59
46 changed files with 1155 additions and 158 deletions

View File

@@ -94,6 +94,7 @@ Contents:
* `Short Vector Types`_
* `Array Types`_
* `Struct Types`_
* `Structure of Array Types`_
+ `Declarations and Initializers`_
+ `Expressions`_
@@ -151,7 +152,6 @@ Contents:
+ `Data Layout`_
+ `Data Alignment and Aliasing`_
+ `Restructuring Existing Programs to Use ISPC`_
+ `Understanding How to Interoperate With the Application's Data`_
* `Disclaimer and Legal Information`_
@@ -1950,6 +1950,77 @@ still has only a single ``a`` member, since ``a`` was declared with
indexing operation in the last line results in an error.
Structure of Array Types
------------------------
If data can be laid out in memory so that the executing program instances
access it via loads and stores of contiguous sections of memory, overall
performance can be improved noticably. One way to improve this memory
access coherence is to lay out structures in "structure of arrays" (SOA)
format in memory; the benefits from SOA layout are discussed in more detail
in the `Use "Structure of Arrays" Layout When Possible`_ section in the
ispc Performance Guide.
.. _Use "Structure of Arrays" Layout When Possible: perf.html#use-structure-of-arrays-layout-when-possible
``ispc`` provides two key language-level capabilities for laying out and
accessing data in SOA format:
* An ``soa`` keyword that transforms a regular ``struct`` into an SOA version
of the struct.
* Array indexing syntax for SOA arrays that transparently handles SOA
indexing.
As an example, consider a simple struct declaration:
::
struct Point { float x, y, z; };
With the ``soa`` rate qualifier, an array of SOA variants of this structure
can be declared:
::
soa<8> Point pts[...];
The in-memory layout of the ``Point``s has had the SOA transformation
applied, such that there are 8 ``x`` values in memory followed by 8 ``y``
values, and so forth. Here is the effective declaration of ``soa<8>
Point``:
::
struct { uniform float x[8], y[8], z[8]; };
Given an array of SOA data, array indexing (and pointer arithmetic) is done
so that the appropriate values from the SOA array are accessed. For
example, given:
::
soa<8> Point pts[...];
uniform float x = pts[10].x;
The generated code effectively accesses the second 8-wide SOA structure and
then loads the third ``x`` value from it. In general, one can write the
same code to access arrays of SOA elements as one would write to access
them in AOS layout.
Note that it directly follows from SOA layout that the layout of a single
element of the array isn't contiguous in memory--``pts[1].x`` and
``pts[1].y`` are separated by 7 ``float`` values in the above example.
There are a few limitations to the current implementation of SOA types in
``ispc``; these may be relaxed in future releases:
* It's illegal to typecast to ``soa`` data to ``void`` pointers.
* Reference types are illegal in SOA structures
* All members of SOA structures must have no rate qualifiers--specifically,
it's illegal to have an explicitly-qualified ``uniform`` or ``varying``
member of a structure that has ``soa`` applied to it.
Declarations and Initializers
-----------------------------
@@ -3375,8 +3446,9 @@ Converting Between Array-of-Structures and Structure-of-Arrays Layout
Applications often lay data out in memory in "array of structures" form.
Though convenient in C/C++ code, this layout can make ``ispc`` programs
less efficient than they would be if the data was laid out in "structure of
arrays" form. (See the section `Understanding How to Interoperate With the
Application's Data`_ for extended discussion of this topic.)
arrays" form. (See the section `Use "Structure of Arrays" Layout When
Possible`_ in the performance guide for extended discussion of this topic.)
The standard library does provide a few functions that efficiently convert
between these two formats, for cases where it's not possible to change the
@@ -3921,9 +3993,9 @@ program instances, ``ispc`` prohibits any varying types from being used in
parameters to functions with the ``export`` qualifier. (``ispc`` also
prohibits passing structures that themselves have varying types as members,
etc.) Thus, all datatypes that is shared with the application must have
the ``uniform`` qualifier applied to them. (See `Understanding How to
Interoperate With the Application's Data`_ for more discussion of how to
load vectors of SoA or AoSoA data from the application.)
the ``uniform`` or ``soa`` rate qualifier applied to them. (See `Use
"Structure of Arrays" Layout When Possible`_ in the Performance Guide for
more discussion of how to load vectors of SOA data from the application.)
Similarly, ``struct`` types shared with the application can also have
embedded pointers.
@@ -3941,7 +4013,7 @@ On the ``ispc`` side, the corresponding ``struct`` declaration is:
// ispc
struct Foo {
uniform float * uniform foo, * uniform bar;
float * uniform foo, * uniform bar;
};
There is one subtlety related to data layout to be aware of: ``ispc``
@@ -4018,156 +4090,6 @@ program instances improves performance.
.. _ispc Performance Tuning Guide: http://ispc.github.com/perf.html
Understanding How to Interoperate With the Application's Data
-------------------------------------------------------------
One of ``ispc``'s key goals is to be able to interoperate with the
application's data, in whatever layout it is stored in. You don't need to
worry about reformatting of data or the overhead of a driver model that
abstracts the data layout. This section illustrates some of the
alternatives with a simple example of computing the length of a large
number of vectors.
Consider for starters a ``Vector`` data-type, defined in C as:
::
struct Vector { float x, y, z; };
We might have (still in C) an array of ``Vector`` s defined like this:
::
Vector vectors[1024];
This is called an "array of structures" (AoS) layout. To compute the
lengths of these vectors in parallel, you can write ``ispc`` code like
this:
::
export void length(uniform Vector vectors[1024], uniform float len[]) {
foreach (index = 0 ... 1024) {
float x = vectors[index].x;
float y = vectors[index].y;
float z = vectors[index].z;
float l = sqrt(x*x + y*y + z*z);
len[index] = l;
}
}
The problem with this implementation is that the indexing into the array of
structures, ``vectors[index].x`` is relatively expensive. On a target
machine that supports four-wide Intel® SSE, this turns into four loads of
single ``float`` values from non-contiguous memory locations, which are
then packed into a four-wide register corresponding to ``float x``. Once the
values are loaded into the local ``x``, ``y``, and ``z`` variables,
SIMD-efficient computation can proceed; getting to that point is
relatively inefficient.
(As described previously in `Converting Between Array-of-Structures and
Structure-of-Arrays Layout`_, this computation could be written more
efficiently using standard library routines to convert from the AoS layout,
if we were given a flat array of ``float`` values.)
An alternative data layout would be the "structure of arrays" (SoA). In C,
the data would be declared as:
::
float x[1024], y[1024], z[1024];
The ``ispc`` code might be:
::
export void length(uniform float x[1024], uniform float y[1024],
uniform float z[1024], uniform float len[]) {
foreach (index = 0 ... 1024) {
float xx = x[index];
float yy = y[index];
float zz = z[index];
float l = sqrt(xx*xx + yy*yy + zz*zz);
len[index] = l;
}
}
In this example, the loads into ``xx``, ``yy``, and ``zz`` are single
vector loads of an entire gang's worth of values into the corresponding
registers. This processing is more efficient than the multiple scalar
loads that are required with the AoS layout above.
A final alternative is "array of structures of arrays" (AoSoA), a hybrid
between these two. A structure is declared that stores a small number of
``x``, ``y``, and ``z`` values in contiguous memory locations:
::
struct Vector16 {
float x[16], y[16], z[16];
};
The ``ispc`` code has an outer loop over ``Vector16`` elements and
then an inner loop that peels off values from the element members:
::
#define N_VEC (1024/16)
export void length(uniform Vector16 v[N_VEC], uniform float len[]) {
foreach (i = 0 ... N_VEC, j = 0 ... 16) {
float x = v[i].x[j];
float y = v[i].y[j];
float z = v[i].z[j];
float l = sqrt(x*x + y*y + z*z);
len[16*i+j] = l;
}
}
}
One advantage of the AoSoA layout is that the memory accesses to load
values are to nearby memory locations, where as with SoA, each of the three
loads above is to locations separated by a few thousand bytes. Thus, AoSoA
can be more cache friendly. For structures with many members, this
difference can lead to a substantial improvement.
With some additional complexity, ``ispc`` can also generate code that
efficiently processes data in AoSoA layout where the inner array length is
less than the machine vector width. For example, consider doing
computation with this AoSoA structure definition on a machine with an
8-wide vector unit (for example, an Intel® AVX target):
::
struct Vector4 {
float x[4], y[4], z[4];
};
The ``ispc`` code to process this loads elements four at a time from
``Vector4`` instances until it has a full ``programCount`` number of
elements to work with and then proceeds with the computation.
::
#define N_VEC (1024/4)
export void length(uniform Vector4 v[N_VEC], uniform float len[]) {
for (uniform int i = 0; i < N_VEC; i += programCount / 4) {
float x, y, z;
for (uniform int j = 0; j < programCount / 4; ++j) {
if (programIndex >= 4 * j &&
programIndex < 4 * (j+1)) {
int index = (programIndex & 0x3);
x = v[i+j].x[index];
y = v[i+j].y[index];
z = v[i+j].z[index];
}
}
float l = sqrt(x*x + y*y + z*z);
len[4*i + programIndex] = l;
}
}
Disclaimer and Legal Information
================================

View File

@@ -13,6 +13,7 @@ the most out of ``ispc`` in practice.
+ `Improving Control Flow Coherence With "foreach_tiled"`_
+ `Using Coherent Control Flow Constructs`_
+ `Use "uniform" Whenever Appropriate`_
+ `Use "Structure of Arrays" Layout When Possible`_
* `Tips and Techniques`_
@@ -247,6 +248,76 @@ but it's always best to provide the compiler with as much help as possible
to understand the actual form of your computation.
Use "Structure of Arrays" Layout When Possible
----------------------------------------------
In general, memory access performance (for both reads and writes) is best
when the running program instances access a contiguous region of memory; in
this case efficient vector load and store instructions can often be used
rather than gathers and scatters. As an example of this issue, consider an
array of a simple point datatype laid out and accessed in conventional
"array of structures" (AOS) layout:
::
struct Point { float x, y, z; };
uniform Point pts[...];
float v = pts[programIndex].x;
In the above code, the access to ``pts[programIndex].x`` accesses
non-sequential memory locations, due to the ``y`` and ``z`` values between
the desired ``x`` values in memory. A "gather" is required to get the
value of ``v``, with a corresponding decrease in performance.
If ``Point`` was defined as a "structure of arrays" (SOA) type, the access
can be much more efficient:
::
struct Point8 { float x[8], y[8], z[8]; };
uniform Point8 pts8[...];
int majorIndex = programIndex / 8;
int minorIndex = programIndex % 8;
float v = pts8[majorIndex].x[minorIndex];
In this case, each ``Point8`` has 8 ``x`` values contiguous in memory
before 8 ``y`` values and then 8 ``z`` values. If the gang size is 8 or
less, the access for ``v`` will have the same value of ``majorIndex`` for
all program instances and will access consecutive elements of the ``x[8]``
array with a vector load. (For larger gang sizes, two 8-wide vector loads
would be issues, which is also quite efficient.)
However, the syntax in the above code is messy; accessing SOA data in this
fashion is much less elegant than the corresponding code for accessing the
data with AOS layout. The ``soa`` qualifier in ``ispc`` can be used to
cause the corresponding transformation to be made to the ``Point`` type,
while preserving the clean syntax for data access that comes with AOS
layout:
::
soa<8> Point pts[...];
float v = pts[programIndex].x;
Thanks to having SOA layout a first-class concept in the language's type
system, it's easy to write functions that convert data between the
layouts. For example, the ``aos_to_soa`` function below converts ``count``
elements of the given ``Point`` type from AOS to 8-wide SOA layout. (It
assumes that the caller has pre-allocated sufficient space in the
``pts_soa`` output array.
::
void aos_to_soa(uniform Point pts_aos[], uniform int count,
soa<8> pts_soa[]) {
foreach (i = 0 ... count)
pts_soa[i] = pts_aos[i];
}
Analogously, a function could be written to convert back from SOA to AOS if
needed.
Tips and Techniques
===================
@@ -339,6 +410,12 @@ based on the index, it can be worth doing. See the example
``examples/volume_rendering`` in the ``ispc`` distribution for the use of
this technique in an instance where it is beneficial to performance.
Understanding Memory Read Coalescing
------------------------------------
XXXX todo
Avoid 64-bit Addressing Calculations When Possible
--------------------------------------------------

View File

@@ -0,0 +1,30 @@
struct Point { float x, y[3], z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
//CO soa<8> Point pts[10];
uniform Point pts[80];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

21
tests/soa-1.ispc Normal file
View File

@@ -0,0 +1,21 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
for (uniform int i = 0; i < 8*10; ++i) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
RET[programIndex] = pts[programIndex].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10*programIndex;
}

23
tests/soa-10.ispc Normal file
View File

@@ -0,0 +1,23 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
uniform Point up = pts[1];
RET[programIndex] = up.y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10;
}

15
tests/soa-11.ispc Normal file
View File

@@ -0,0 +1,15 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[2] = { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
{ { 13, 14, 15, 16 }, { 17, 18, 19, 20, }, { 21, 22, 23, 24 } } };
RET[programIndex] = pts[1].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = 6;
}

17
tests/soa-12.ispc Normal file
View File

@@ -0,0 +1,17 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<4> Point pts[2] = { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
{ { 13, 14, 15, 16 }, { 17, 18, 19, 20, }, { 21, 22, 23, 24 } } };
RET[programIndex] = pts[programIndex & 1].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = (programIndex & 1) ? 6 : 5;
}

26
tests/soa-13.ispc Normal file
View File

@@ -0,0 +1,26 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
uniform Point up = { b, 3, 170 };
pts[(int64)1] = up;
RET[programIndex] = pts[(int64)programIndex].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*programIndex;
RET[1] = 170;
}

57
tests/soa-14.ispc Normal file
View File

@@ -0,0 +1,57 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
//CO for (uniform int s = 0; s < 1; ++s) { // num to print
//CO for (uniform int i = 0; i < 3; ++i) { // num float in unif struct
//CO for (uniform int j = 0; j < 8; ++j, ++ptr) // soa width
//CO print("% ", *ptr);
//CO print("\n");
//CO }
//CO print("\n");
//CO }
}
soa<8> Point * uniform aossoa(uniform Point aospts[], uniform int count) {
uniform int roundUp = (count + 7) & ~0x7;
uniform int nAlloc = roundUp / 8;
soa<8> Point * uniform ret = uniform new soa<8> Point[nAlloc];
foreach (i = 0 ... count) {
//CO varying Point gp = { programIndex+1, 2*programIndex+1, 3*programIndex+1 };
//CO ret[i] = gp;
//CO ret[i].x = gp.x;
//CO ret[i].y = gp.y;
//CO ret[i].z = gp.z;
//CO print("%: % % %\n", i, gp.x, gp.y, gp.z);
ret[i] = aospts[i];
}
//CO p((uniform float * uniform)aospts);
//CO print("----\n");
//CO p((uniform float * uniform)ret);
return ret;
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
uniform Point pts[programCount+4];
foreach (i = 0 ... programCount+4) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * uniform soaPts = aossoa(pts, programCount+4);
RET[programIndex] = soaPts[programIndex+3].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*(programIndex+3);
}

48
tests/soa-15.ispc Normal file
View File

@@ -0,0 +1,48 @@
struct Point { float x, y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("delta %\n", ((uniform float * varying)(&pts[2+programIndex]) -
//CO (uniform float * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

48
tests/soa-16.ispc Normal file
View File

@@ -0,0 +1,48 @@
struct Point { double x; double y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("delta %\n", ((uniform float * varying)(&pts[2+programIndex]) -
//CO (uniform float * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

50
tests/soa-17.ispc Normal file
View File

@@ -0,0 +1,50 @@
struct Point { double x; float y[3], z; };
export uniform int width() { return programCount; }
static void p(uniform float *uniform ptr) {
for (uniform int s = 0; s < 4; ++s) {
for (uniform int i = 0; i < 5; ++i) {
for (uniform int j = 0; j < 4; ++j, ++ptr)
print("% ", *ptr);
print("\n");
}
print("\n");
}
}
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
soa<4> Point pts[10];
//CO uniform Point pts[40];
//CO foreach (i = 0 ... 40) {
for (uniform int i = 0; i < 40; ++i) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
//CO p((uniform float * uniform)&pts[0]);
//CO print("one size %\n", sizeof(soa<4> Point));
//CO print("delta %\n", ((uniform int8 * varying)(&pts[2+programIndex]) -
//CO (uniform int8 * uniform)&pts[0]));
float a = aFOO[programIndex];
a *= -1;
Point vp = { a, { 2*a, 3*a, 4*a }, {5*a} };
pts[2+programIndex] = vp;
//CO p((uniform float * uniform)&pts[0]);
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = -4 * (programIndex-1);
RET[0] = 2;
RET[1] = 12;
}

25
tests/soa-18.ispc Normal file
View File

@@ -0,0 +1,25 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * ptr = &pts[programIndex];
++ptr;
ptr->y = -programIndex;
RET[programIndex] = pts[1+programIndex].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = -programIndex;
}

24
tests/soa-19.ispc Normal file
View File

@@ -0,0 +1,24 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * ptr = &pts[6+programIndex];
ptr->y = -programIndex;;
RET[programIndex] = pts[6+programIndex].y;
}
export void result(uniform float RET[]) {
RET[programIndex] = -programIndex;
}

21
tests/soa-2.ispc Normal file
View File

@@ -0,0 +1,21 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
RET[programIndex] = pts[programIndex].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*programIndex;
}

22
tests/soa-20.ispc Normal file
View File

@@ -0,0 +1,22 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
soa<8> Point * ptr = &pts[6+programIndex];
RET[programIndex] = ptr - pts;
}
export void result(uniform float RET[]) {
RET[programIndex] = 6 + programIndex;
}

32
tests/soa-21.ispc Normal file
View File

@@ -0,0 +1,32 @@
struct Point { float x, y, z; };
struct Foo {
int x;
Point pts[10];
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
foreach (i = 0 ... 32) {
pts[i].x = b*i;
pts[i].z = -b*i;
for (uniform int j = 0; j < 10; ++j) {
pts[i].pts[j].x = j + 100*i;
pts[i].pts[j].y = j + 1000*i;
pts[i].pts[j].z = j + 10000*i;
}
}
soa<8> Foo * ptr = &pts[7+programIndex];
RET[programIndex] = ptr->pts[3].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10000 * (7 + programIndex) + 3;
}

35
tests/soa-22.ispc Normal file
View File

@@ -0,0 +1,35 @@
struct Point { float x, y, z; };
struct Foo {
int x;
Point *pts[3];
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
//CO uniform Foo pts[32];
foreach (i = 0 ... 32) {
pts[i].x = b*i;
pts[i].z = -b*i;
for (uniform int j = 0; j < 3; ++j) {
pts[i].pts[j] = new uniform Point[4];
for (uniform int k = 0; k < 4; ++k) {
pts[i].pts[j][k].x = 100*i+10*j+k;
pts[i].pts[j][k].y = -1234;
pts[i].pts[j][k].z = -(100*i+10*j+k);
}
}
}
RET[programIndex] = pts[programIndex].pts[programIndex % 3][programIndex % 4].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = -(100*programIndex+10*(programIndex % 3)+(programIndex % 4));
}

28
tests/soa-23.ispc Normal file
View File

@@ -0,0 +1,28 @@
struct Point { float x, y, z; };
struct Foo {
float<3> vec;
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
//CO uniform Foo pts[32];
foreach (i = 0 ... 32) {
pts[i].vec.x = b*i;
pts[i].vec.y = -b*i;
pts[i].vec.z = 2*b*i;
pts[i].z = i;
}
RET[programIndex] = pts[programIndex+2].vec.y;
}
export void result(uniform float RET[]) {
RET[programIndex] = -(5 * (programIndex+2));
}

32
tests/soa-24.ispc Normal file
View File

@@ -0,0 +1,32 @@
struct Point { float x, y, z; };
struct Foo {
float<3> vec;
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
//CO uniform Foo pts[32];
foreach (i = 0 ... 32) {
pts[i].vec.x = b*i;
pts[i].vec.y = -b*i;
pts[i].vec.z = 2*b*i;
pts[i].z = i;
}
pts[programIndex+2].vec.z *= -1;
float<3> vl = pts[programIndex].vec;
RET[programIndex] = vl.z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 10 * programIndex;
if (programIndex >= 2)
RET[programIndex] *= -1;
}

32
tests/soa-25.ispc Normal file
View File

@@ -0,0 +1,32 @@
struct Point { float x, y, z; };
struct Foo {
float<3> vec;
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
//CO uniform Foo pts[32];
foreach (i = 0 ... 32) {
pts[i].vec.x = b*i;
pts[i].vec.y = -b*i;
pts[i].vec.z = 2*b*i;
pts[i].z = i;
}
pts[2].vec.x *= -1;
float<3> vl = pts[programIndex].vec;
RET[programIndex] = vl.x;
}
export void result(uniform float RET[]) {
RET[programIndex] = 5 * programIndex;
if (programIndex == 2)
RET[programIndex] *= -1;
}

28
tests/soa-26.ispc Normal file
View File

@@ -0,0 +1,28 @@
struct Point { float x, y, z; };
struct Foo {
float<3> vec;
int8 z;
};
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Foo * uniform pts = uniform new soa<8> Foo[4];
//CO uniform Foo pts[32];
for (uniform int i = 0; i < 32; ++i) {
pts[i].vec.x = b*i;
pts[i].vec.y = -b*i;
pts[i].vec.z = 2*b*i;
pts[i].z = i;
}
RET[programIndex] = pts[9].vec.y;
}
export void result(uniform float RET[]) {
RET[programIndex] = -45;
}

24
tests/soa-3.ispc Normal file
View File

@@ -0,0 +1,24 @@
struct Point { float x, y[3], z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
//CO uniform Point pts[80];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y[0] = 2*b*i;
pts[i].y[1] = 2*b*i+1;
pts[i].y[2] = 2*b*i+2;
pts[i].z = 3*b*i;
}
RET[programIndex] = pts[programIndex].y[2];
}
export void result(uniform float RET[]) {
RET[programIndex] = 10*programIndex + 2;
}

24
tests/soa-4.ispc Normal file
View File

@@ -0,0 +1,24 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[8];
foreach (i = 0 ... 64) {
pts[i].x = 0;
pts[i].y = 0;
pts[i].z = 0;
}
Point pv = { a, b, -a };
pts[8+programIndex] = pv;
RET[programIndex] = pts[8+programIndex].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = -(1 + programIndex);
}

24
tests/soa-5.ispc Normal file
View File

@@ -0,0 +1,24 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[8];
foreach (i = 0 ... 64) {
pts[i].x = 0;
pts[i].y = 0;
pts[i].z = 0;
}
Point pv = { a, b, -a };
pts[6+programIndex] = pv;
RET[programIndex] = pts[6+programIndex].x;
}
export void result(uniform float RET[]) {
RET[programIndex] = (1 + programIndex);
}

25
tests/soa-6.ispc Normal file
View File

@@ -0,0 +1,25 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[8];
foreach (i = 0 ... 64) {
pts[i].x = -42;
pts[i].y = 0;
pts[i].z = 0;
}
Point pv = { a, b, -a };
pts[8+programIndex] = pv;
RET[programIndex] = pts[6+programIndex].x;
}
export void result(uniform float RET[]) {
RET[programIndex] = (1 + programIndex - 2);
RET[0] = RET[1] = -42;
}

27
tests/soa-7.ispc Normal file
View File

@@ -0,0 +1,27 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[8];
foreach (i = 0 ... 64) {
pts[i].x = -42;
pts[i].y = 0;
pts[i].z = 0;
}
Point pv = { a, b, -a };
pts[8+programIndex].x = pv.x;
pts[8+programIndex].y = pv.y;
pts[8+programIndex].z = pv.z;
RET[programIndex] = pts[6+programIndex].x;
}
export void result(uniform float RET[]) {
RET[programIndex] = (1 + programIndex - 2);
RET[0] = RET[1] = -42;
}

25
tests/soa-8.ispc Normal file
View File

@@ -0,0 +1,25 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[8];
foreach (i = 0 ... 64) {
pts[i].x = -42;
pts[i].y = 0;
pts[i].z = 0;
}
Point pv = { a, b, -a };
pts[7+programIndex] = pv;
RET[programIndex] = pts[8+programIndex].x;
}
export void result(uniform float RET[]) {
RET[programIndex] = (2 + programIndex);
RET[programCount-1] = -42;
}

25
tests/soa-9.ispc Normal file
View File

@@ -0,0 +1,25 @@
struct Point { float x, y, z; };
export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex];
soa<8> Point pts[10];
foreach (i = 0 ... 80) {
pts[i].x = b*i;
pts[i].y = 2*b*i;
pts[i].z = 3*b*i;
}
uniform Point up = { b, 3, 170 };
pts[1] = up;
RET[programIndex] = pts[programIndex].z;
}
export void result(uniform float RET[]) {
RET[programIndex] = 15*programIndex;
RET[1] = 170;
}

View File

@@ -0,0 +1,5 @@
// Type conversion from "const uniform int32" to "uniform int32 * varying" for initializer is not possible
int voo() {
int * varying foo = 1;
}

View File

@@ -1,4 +1,4 @@
// Type conversion only possible from atomic types
// Type conversion from "varying struct P" to "varying unsigned int32" for item count is not possible
struct P { int x; };

3
tests_errors/soa-1.ispc Normal file
View File

@@ -0,0 +1,3 @@
// Illegal to provide soa<4> qualifier with non-struct type "void"
soa<4> void foo() { }

25
tests_errors/soa-10.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Can't convert between types "uniform struct Foo" and "soa<4> struct Foo" with different SOA widths
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
xyz= uf;
}

25
tests_errors/soa-11.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Type conversion from "const uniform int32" to "soa<4> struct Foo" for assignment operator is not possible
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
xyz = 0;
}

25
tests_errors/soa-12.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Can't convert between types "const uniform int32" and "soa<4> float" with different SOA widths
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
xyz.x = 0;
}

25
tests_errors/soa-13.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Can't apply unary operator to SOA type "soa<4> struct Foo"
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
++xyz;
}

25
tests_errors/soa-14.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Can't apply unary operator to SOA type "soa<4> float"
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
-xyz.x;
}

25
tests_errors/soa-15.ispc Normal file
View File

@@ -0,0 +1,25 @@
// Illegal to use binary operator - with SOA type "soa<4> float"
struct Pt { float x, y, z; };
struct Foo {
//CO uniform int a;
//CO int a;
//CO float *x;
//CO float y[5], z;
double d[5];
float x, y, z;
//Pt p;
};
extern void bar(float);
uniform Foo uf;
varying Foo vf;
int index;
export void x(uniform Foo &x, soa<8> Foo f[]) {
soa<4> Foo xyz;
xyz.x = xyz.y-xyz.x;
}

6
tests_errors/soa-2.ispc Normal file
View File

@@ -0,0 +1,6 @@
// soa<3> width illegal. Value must be positive power of two
struct F { float a, b, c; };
soa<3> F farray[10];

6
tests_errors/soa-3.ispc Normal file
View File

@@ -0,0 +1,6 @@
// syntax error, unexpected '-', expecting int32 constant
struct F { float a, b, c; };
soa<-4> F farray[10];

6
tests_errors/soa-4.ispc Normal file
View File

@@ -0,0 +1,6 @@
// syntax error, unexpected '-', expecting int32 constant
struct F { float a, b, c; };
soa<-4> F farray[10];

6
tests_errors/soa-5.ispc Normal file
View File

@@ -0,0 +1,6 @@
// "uniform" qualifier and "soa<4>" qualifier can't both be used
struct F { float a, b, c; };
uniform soa<4> F farray[10];

7
tests_errors/soa-6.ispc Normal file
View File

@@ -0,0 +1,7 @@
// Can't convert between types "uniform struct Point" and "soa<4> struct Point" with different SOA widths
struct Point { float a, b, c; };
void foo(soa<8> Point pts[]) {
soa<4> Point x = pts[0];
}

7
tests_errors/soa-7.ispc Normal file
View File

@@ -0,0 +1,7 @@
// Can't convert between types "uniform struct Point" and "soa<4> struct Point" with different SOA widths
struct Point { float a, b, c; };
void foo(soa<8> Point pts[]) {
soa<4> Point x = pts[0];
}

5
tests_errors/soa-8.ispc Normal file
View File

@@ -0,0 +1,5 @@
// Unable to apply SOA conversion to struct due to "uniform int32" member "x" with bound "uniform"
struct Point { uniform int x; float a, b, c; };
void foo(soa<8> Point pts[]);

9
tests_errors/soa-9.ispc Normal file
View File

@@ -0,0 +1,9 @@
// Can't convert from pointer to SOA type "soa<8> struct A * uniform" to pointer to non-SOA type "void * varying"
struct A { float a, b; };
soa<8> A as[100];
void foo() {
void *ptr = &as[0];
}