Basic Concepts[Basic]

3 Basic Concepts[Basic]

HLSL inherits a significant portion of its language semantics from C and C++. Some of this is a result of intentional adoption of syntax early in the development of the language and some a side-effect of the Clang-based implementation of DXC.

This chapter includes a lot of definitions that are inherited from C and C++. Some are identical to C or C++, others are slightly different. HLSL is neither a subset nor a superset of C or C++, and cannot be simply described in terms of C or C++. This specification includes all necessary definitions for clarity.

3.1 Preamble[Basic.preamble]

An entity is a value, object, function, enumerator, type, class member, bit-field, template, template specialization, namespace, or pack.

A name is a use of an identifier (5.2.4), operator-function-id ([Overload.operator]), conversion-function-id (10.2), or template-id (11) that denotes any entity or label (6.1).

Every name that denotes an entity is introduced by a declaration. Every name that denotes a label is introduced by a labeled statement (6.1)1.

A variable is introduced by the declaration of a reference other than a non-static data member of an object. The variable’s name denotes the reference or object.

Whenever a name is encountered it is necessary to determine if the name denotes an entity that is a type or template. The process for determining if a name refers to a type or template is called name lookup.

Two names are the same name if:

  • they are identifiers comprised of the same character sequence, or

  • they are operator-function-ids formed with the same operator, or

  • they are conversion-function-ids formed with the same type, or

  • they are template-ids that refer to the same class or function.

This section matches isoCPP section [basic] except for the exclusion of goto and literal operators.

3.2 Declarations and definitions[Basic.Decl]

A declaration (7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If a declaration introduces names, it specifies the interpretation and attributes of these names. A declaration may also have effects such as:

  • verifying a static assertion (7),

  • use of attributes (7), and

  • controlling template instantiation (11.1).

A declaration is a definition unless:

  • it declares a function without specifying the function’s body (7.5),

  • it is a parameter declaration in a function declaration that does not specify the function’s body (7.5),

  • it is a global or namespace member declaration without the static specifier2,

  • it declares a static data member in a class definition,

  • it is a class name declaration,

  • it is a template parameter,

  • it is a typedef declaration (7),

  • it is an alias-declaration (7),

  • it is a using-declaration (7),

  • it is a static_assert-declaration (7),

  • it is an empty-declaration (7),

  • or a using-directive (7).

The two examples below are adapted from isoCPP [basic.def]. All but one of the following are definitions:

int f(int x) return x+1; // defines f and x struct S int a;int b;; // defines S, S::a, and S::b struct X // defines X int x; // defines non-static member x static int y; // declares static data member y ; int X::y = 1; // defines X::y enum up, down ; // defines up and down namespace N // defines N int d; // declares N::d static int i; // defines N::i

All of the following are declarations:

int a; // declares a const int c; // declares c X anX; // declares anX int f(int); // declares f struct S; // declares S typedef int Int; // declares Int using N::d; // declares d using Float = float; // declares Float cbuffer CB // does not declare CB int z; // declares z tbuffer TB // does not declare TB int w; // declares w

3.3 One-Definition Rule[Basic.ODR]

The isoCPP One-definition rule is adopted as defined in isoCPP [basic.def.odr].

3.4 Scope[Basic.Scope]

3.5 Name Lookup[Basic.Lookup]

3.6 Storage Duration[Basic.Storage]

The storage duration of an object is the portion of the program’s execution time during which the object exists in memory. An object may have one of the following storage durations:

  • static storage duration

  • automatic storage duration

  • program storage duration

  • groupshared storage duration

3.6.1 Static Storage Duration[Basic.Storage.Static]

An object whose name is declared with the static storage specifier has static storage duration. Such an object is created when the thread begins execution and destroyed when the thread ends execution.

3.6.2 Automatic Storage Duration[Basic.Storage.Auto]

An object whose name is declared in a block (including function parameters) without the static storage specifier has automatic storage duration. Such an object is created when the block in which it is declared is entered and destroyed when the block is exited.

3.6.3 Program Storage Duration[Basic.Storage.Program]

An object whose name is declared in a global, namespace, or cbuffer scope without the static storage specifier has program storage duration. Such an object is created when the program begins execution and destroyed when the program ends execution.

3.6.4 Groupshared Storage Duration[Basic.Storage.Groupshared]

An object whose name is declared with the groupshared storage specifier has groupshared storage duration. Such an object is created when the thread group begins execution and destroyed when the thread group ends.

3.7 Program and linkage[Basic.Linkage]

A translation unit (2.1) is comprised of a sequence of declarations:

translation-unit:
declaration-sequenceopt

A program is one or more translation units linked together. A program built from a single translation unit, bypassing a linking step is called freestanding.

A program is said to be fully linked, when it contains no unresolved external declarations, and all exported declarations are entry point declarations (3.8). A program is said to be partially linked, when it contains at least one unresolved external declaration or at least one exported declaration that is not an entry point.

An implementation may generate programs as fully linked or partially linked as requested by the user, and a runtime may allow fully linked or partially linked programs as the implementation allows.

A name has linkage if it can refer to the same entity as a name introduced by a declaration in another scope. If a variable, function, or another entity with the same name is declared in several scopes, but does not have sufficient linkage, then several instances of the entity are generated.

  • A name with no linkage may not be referred to by names from any other scope.

  • A name with internal linkage may be referred to by names from other scopes within the same translation unit.

  • A name with external linkage may be referred to by names from other scopes within the same translation unit, and by names from scopes of other translation units.

  • A name with program linkage may be referred to by names from other scopes within the same translation unit, by names from scopes of other translation units, by names from scopes of other programs, and by a runtime implementation.

When merging translation units through linking or generating a freestanding program only names with program linkage must be retained in the final program.

3.7.1 Program Linkage[Basic.Linkage.Program]

Entities with program linkage can be referred to from other partially linked programs or a runtime implementation.

The following entities have program linkage:

  • entry point functions (3.8)

  • functions marked with export keyword (7.7)

  • declarations contained within an export-declaration-group (7.7)

3.7.2 External Linkage[Basic.Linkage.External]

Entities with external linkage can be referred to from the scopes in the other translation units and enable linking between them.

The following entities in HLSL have external linkage:

  • global variables that are not marked static or groupshared 3

  • static data members of classes or template classes

Linkage of functions (including template functions) that are not entry points or marked with export keyword is implementation dependent. 4

3.7.3 Internal Linkage[Basic.Linkage.Internal]

Entities with internal linkage can be referred to from all scopes in the current translation unit.

The following entities in HLSL have internal linkage:

  • global variables marked as static or groupshared

  • all entities declared in an unnamed namespace or a namespace within an unnamed namespace

  • enumerations

  • classes or template classes, their member functions, and nested classes and enumerations

3.7.4 No Linkage[Basic.Linkage.NoLinkage]

An entity with no linkage can be referred to only from the scope it is in.

Any of the following entites declared at function scope or block scopes derived from function scope have no linkage:

  • local variables

  • local classes and their member functions

  • other entities declared at function scope or block scopes derived from function scope that such as typedefs, enumerations, and enumerators

3.8 Start[Basic.Start]

A fully linked program shall contain one or more global functions, which are the designated starting points for the program. These global functions are called entry points, because they denote the location where execution inside the program begins.

Entry point functions have different requirements based on the target runtime and execution mode (3.8.1).

Parameters to entry functions and entry function return types must be of scalar, vector, or non-intangible class type (3.9). Scalar and vector parameters and return types must be annotated with semantic annotations (7.6.1). Class type input and output parameters must have all fields annotated with semantic annotations.

3.8.1 Execution Mode[Basic.Start.Mode]

A runtime may define a set of execution modes in an implementation defined way. Each execution mode will have a set of implementation defined rules which restrict available language functionality as appropriate for the execution mode.

3.9 Types[Basic.types]

The object representation of an object of type T is the sequence of N bytes taken up by the object of type T, where N equals sizeof(T)5. The object representation of an object may be different based on the memory space it is stored in (1.7.1).

The value representation of an object is the set of bits that hold the value of type T. Bits in the object representation that are not part of the value representation are padding bits.

An object type is a type that is not a function type, not a reference type, and not a void type.

A class type is a data type declared with either the class or struct keywords (10). A class type T may be declared as incomplete at one point in a translation unit via a forward declaration, and complete later with a full definition. The type T is the same type throughout the translation unit.

There are special implementation-defined types such as handle types, which fall into a category of standard intangible types. Intangible types are types that have no defined object representation or value representation, as such the size is unknown at compile time. Usage restrictions for objects of intangible type are documented in 3.9.3.

A class type T is an intangible class type if it contains a base class or members of intangible class type, standard intangible type, or arrays of such types. Standard intangible types and intangible class types are collectively called intangible types(12).

An object type is an incomplete type if the compiler lacks sufficient information to determine the size of an object of type T, and it is not an intangible type. It is a complete type if the compiler has sufficient information to determine the size of an object of type T, or if the type is known to be an intangible type. An object may not be defined to have an incomplete type.

Arithmetic types (3.9.1), enumeration types, and cv-qualified versions of these types are collectively called scalar types.

Vectors of scalar types declared with the built-in vector<T,N> template are vector types. Vector lengths must be between 1 and 4 (i.e. 1 ≤ N ≤ 4 ).

Matrices of scalar types declared with the built-in matrix<T,N,M> template are matrix types. Matrix dimensions, N and M, must be between 1 and 4 (i.e. 1 ≤ N ≤ 4 ).

3.9.1 Arithmetic Types[Basic.types.arithmetic]

There are three standard signed integer types: int16_t, int32_t, and int64_t. Each of the signed integer types is explicitly named for the size in bits of the type’s object representation. There is also the type alias int which is an alias of int32_t. There is one minimum precision signed integer type: min16int. The minimum precision signed integer type is named for the required minimum value representation size in bits. The object representation of min16int is int. The standard signed integer types and minimum precision signed integer type are collectively called signed integer types.

There are three standard unsigned integer types: uint16_t, uint32_t, and uint64_t. Each of the unsigned integer types is explicitly named for the size in bits of the type’s object representation. There is also the type alias uint which is an alias of uint32_t. There is one minimum precision unsigned integer type: min16uint. The minimum precision unsigned integer type is named for the required minimum value representation size in bits. The object representation of min16uint is uint. The standard unsigned integer types and minimum precision unsigned integer type are collectively called unsigned integer types.

The minimum precision signed integer types and minimum precision unsigned integer types are collectively called minimum precision integer types. The standard signed integer types and standard unsigned integer types are collectively called standard integer types. The signed integer types and unsigned integer types are collectively called integer types. Integer types inherit the object representation of integers defined in isoC236. Integer types shall satisfy the constraints defined in isoCPP, section basic.fundamental.

There are three standard floating point types: half, float, and double. The float type is a 32-bit floating point type. The double type is a 64-bit floating point type. Both the float and double types have object representations as defined in IEEE754. The half type may be either 16-bit or 32-bit as controlled by implementation defined compiler settings. If half is 32-bit it will have an object representation as defined in IEEE754, otherwise it will have an object representation matching the binary16 format defined in IEEE7547. There is one minimum precision floating point type: min16float. The minimum precision floating point type is named for the required minimum value representation size in bits. The object representation of min16float is float8. The standard floating point types and minimum precision floating point type are collectively called floating point types.

Integer and floating point types are collectively called arithmetic types.

The void type is inherited from isoCPP, which defines it as having an empty set of values and being an incomplete type that can never be completed. The void type is used to signify the return type of a function that returns no value. Any expression can be explicitly converted to void.

3.9.2 Scalarized Type Compatability[Basic.types.scalarized]

All types T have a scalarized representation, SR(T), which is a list of one or more types representing each scalar element of T.

Scalarized representations are determined as follows:

  • The scalarized representation of an array T[n] is SR(T0), ..SR(Tn).

  • The scalarized representation of a vector vector<T,n> is T0, ..Tn.

  • The scalarized representation of a matrix matrix<T,n, m> is T0, ..Tn × m.

  • The scalarized representation of a class type T, SR(T) is computed recursively as SR(T::base), SR(T::0), ..SR(T::n) where (T::base) is T’s base class if it has one, and T : :n represents the n non-static members of T.

  • The scalarized representation for an enumeration type is the underlying arithmetic type.

  • The scalarized representation for arithmetic, intangible types, and any other type T is T.

Two types cv1 T1 and cv2 T2 are scalar-layout-compatible types if T1 and T2 are the same type or if the sequence of types defined by the scalar representation SR(T1) and scalar representation SR(T2) are identical.

3.9.3 Usage of Intangible Types[Basic.types.intangible]

The following usage restrictions apply to intangible types:

  • Instances of objects of intangible type may only be declared in the Thread address space (1.7.1).

  • An object of intangible type may not be loaded or stored to any address space other than the Thread address space (1.7.1).9

  • An object of intangible type may not be a parameter or return type of a function with program linkage or external linkage (3.7.1 and 3.7.2).

3.10 Lvalues and rvalues[Basic.lval]

Expressions are classified by the type(s) of values they produce. The valid types of values produced by expressions are:

  1. An lvalue represents a function or object.

  2. An rvalue represents a temporary object.

  3. An xvalue (expiring value) represents an object near the end of its lifetime.

  4. A cxvalue (casted expiring value) is an xvalue which, on expiration, assigns its value to a bound lvalue.

  5. A glvalue is an lvalue, xvalue, or cxvalue.

  6. A prvalue is an rvalue that is not an xvalue.