Intel® oneAPI DPC++/C++ Compiler Developer Guide and Reference

ID 767253
Date 3/22/2024
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

SDLT Primitives

Primitives represent the data we want to work over in SIMD. They can be more than just data structures. As a C++ object, it can have its own methods that modify its data.

Rules

  • Must be Plain Old Data (POD)
    • Has trivial copy constructor
    • Has trivial move constructor
    • Has trivial destructor
    • No virtual functions or virtual bases
  • No reference data members
  • No unions
  • No bit fields
  • No bool types
    • Comparison semantics not efficient in SIMD
    • Use 32-bit integer and compare against known values like 0 or 1 explicitly
  • Data members need to be public or declare SDLT_PRIMITIVE_FRIEND in the object's definition

Current Limitations

  • No pointer data members
  • No C++11 strongly typed enums—use integers instead.
  • No array based data members.
  • copy constructor and assignment operator (=) defined by individual member assignment—strongly encouraged to facilitate better code generation

They may seem like large restrictions, but often code can easily be re-factored to meet this requirement. For example:

class Point3d {
    // methods...
protected:
    double v[3];
};

can be re-factored to have a public data member for each element in the array and update methods to use the x, y, and z data members rather than the array v.

class Point3d {
public:
    // methods...
    double x;
    double y;
    double z;
};

For better code generation, explicitly define a copy constructor and assignment operator (=) by individual member assignment.

SDLT_PRIMITIVE Macro

Once an object meets the criteria above, we can consider it a Primitive type in SDLT. In order for Container's to import and export the Primitive, it has to understand its data layout. Unfortunately C++11 lacks compile time reflection, so the user must provide SDLT with a description of your structure's data layout. This is easily done with the SDLT_PRIMITIVE helper macro that accepts a struct type followed by a comma separated list of its data members.

SDLT_PRIMITIVE(STRUCT_NAME, DATA_MEMBER_1, ...)

Example Usage:

struct UserObject 
{
    float x;
    float y;
    double acceleration;
    int behavior;
};

SDLT_PRIMITIVE(UserObject, x, y, acceleration, behavior)

An object must be declared as a Primitive before it can be used in a Container. However, built-in types like float, double, int, etc. do not need to be declared as a Primitive before use with a Container. Built-in's are automatically considered Primitives by SDLT.

Nested Primitives are supported, but the nested Primitive must be declared before the outer Primitive is. Example: Axis Aligned Bounding Box made up of two 3d points

struct Point3s
{
    float x;
    float y;
    float z;
}; 

struct AABB 
{
    Point3s topLeft;
    Point3s bottomRight;
};

SDLT_PRIMITIVE(Point3s, x, y, z)
SDLT_PRIMITIVE(AABB, topLeft, bottomRight)

Notice the struct definitions themselves do not derive from SDLT or use any of its nomenclature. This independence allows classes to be used in code not using SDLT and only code that does use SDLT Containers needs to see the Primitive declarations.