Implicit Type Conversions
Implicit type conversion is an automatic type conversion done by the compiler whenever data from different types is intermixed. Implicit conversions of scalar built-in types defined in Table 4.1 (except void, double,1 and half 2) are supported. When an implicit conversion is done, it is not just a reinterpretation of the expression's value but a conversion of that value to an equivalent value in the new type.
Consider the following example:
float f = 3; // implicit conversion to float value 3.0 int i = 5.23f; // implicit conversion to integer value 5
In this example, the value 3 is converted to a float value 3.0f and then assigned to f. The value 5.23f is converted to an int value 5 and then assigned to i. In the second example, the fractional part of the float value is dropped because integers cannot support fractional values; this is an example of an unsafe type conversion.
Implicit conversions for pointer types follow the rules described in the C99 specification. Implicit conversions between built-in vector data types are disallowed. For example:
float4 f; int4 i; f = i; // illegal implicit conversion between vector data types
There are graphics shading languages such as OpenGL Shading Language (GLSL) and the DirectX Shading Language (HLSL) that do allow implicit conversions between vector types. However, prior art for vector casts in C doesn't support conversion casts. The AltiVec Technology Programming Interface Manual (www.freescale.com/files/32bit/doc/ref_manual/ALTIVECPIM.pdf?fsrch=1), Section 2.4.6, describes the function of casts between vector types. The casts are conversion-free. Thus, any conforming AltiVec compiler has this behavior. Examples include XL C, GCC, MrC, Metrowerks, and Green Hills. IBM's Cell SPE C language extension (C/C++ Language Extensions for Cell Broadband Engine Architecture; see Section 1.4.5) has the same behavior. GCC and ICC have adopted the conversion-free cast model for SSE (http://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Vector-Extensions.html#Vector-Extensions). The following code example shows the behavior of these compilers:
#include <stdio.h> // Declare some vector types. This should work on most compilers // that try to be GCC compatible. Alternatives are provided // for those that don't conform to GCC behavior in vector // type declaration. // Here a vFloat is a vector of four floats, and // a vInt is a vector of four 32-bit ints. #if 1 // This should work on most compilers that try // to be GCC compatible // cc main.c -Wall -pedantic typedef float vFloat __attribute__ ((__vector_size__(16))); typedef int vInt __attribute__ ((__vector_size__(16))); #define init_vFloat(a, b, c, d) (const vFloat) {a, b, c, d} #else //Not GCC compatible #if defined( __SSE2__ ) // depending on compiler you might need to pass // something like -msse2 to turn on SSE2 #include <emmintrin.h> typedef __m128 vFloat; typedef __m128i vInt; static inline vFloat init_vFloat(float a, float b, float c, float d); static inline vFloat init_vFloat(float a, float b, float c, float d) { union{ vFloat v; float f[4];}u; u.f[0] = a; u.f[1] = b; u.f[2] = c; u.f[3] = d; return u.v; } #elif defined( __VEC__ ) // depending on compiler you might need to pass // something like -faltivec or -maltivec or // "Enable AltiVec Extensions" to turn this part on #include <altivec.h> typedef vector float vFloat; typedef vector int vInt; #if 1 // for compliant compilers #define init_vFloat(a, b, c, d) (const vFloat) (a, b, c, d) #else // for FSF GCC #define init_vFloat(a, b, c, d) (const vFloat) {a, b, c, d} #endif #endif #endif void print_vInt(vInt v) { union{ vInt v; int i[4]; }u; u.v = v; printf("vInt: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", u.i[0], u.i[1], u.i[2], u.i[3]); } void print_vFloat(vFloat v) { union{ vFloat v; float i[4]; }u; u.v = v; printf("vFloat: %f %f %f %f\n", u.i[0], u.i[1], u.i[2], u.i[3]); } int main(void) { vFloat f = init_vFloat(1.0f, 2.0f, 3.0f, 4.0f); vInt i; print_vFloat(f); printf("assign with cast: vInt i = (vInt) f;\n" ); i = (vInt) f; print_vInt(i); return 0; }
The output of this code example demonstrates that conversions between vector data types implemented by some C compilers3 such as GCC are cast-free.
vFloat: 1.000000 2.000000 3.000000 4.000000 assign with cast: vInt i = (vInt) f; vInt: 0x3f800000 0x40000000 0x40400000 0x40800000
So we have prior art in C where casts between vector data types do not perform conversions as opposed to graphics shading languages that do perform conversions. The OpenCL working group decided it was best to make implicit conversions between vector data types illegal. It turns out that this was the right thing to do for other reasons, as discussed in the section "Explicit Conversions" later in this chapter.
Usual Arithmetic Conversions
Many operators that expect operands of arithmetic types (integer or floating-point types) cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. For this purpose, all vector types are considered to have a higher conversion rank than scalars. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions.
If the operands are of more than one vector type, then a compile-time error will occur. Implicit conversions between vector types are not permitted.
Otherwise, if there is only a single vector type, and all other operands are scalar types, the scalar types are converted to the type of the vector element, and then widened into a new vector containing the same number of elements as the vector, by duplication of the scalar value across the width of the new vector. A compile-time error will occur if any scalar operand has greater rank than the type of the vector element. For this purpose, the rank order is defined as follows:
- The rank of a floating-point type is greater than the rank of another floating-point type if the floating-point type can exactly represent all numeric values in the second floating-point type. (For this purpose, the encoding of the floating-point value is used, rather than the subset of the encoding usable by the device.)
- The rank of any floating-point type is greater than the rank of any integer type.
- The rank of an integer type is greater than the rank of an integer type with less precision.
- The rank of an unsigned integer type is greater than the rank of a signed integer type with the same precision.
- bool has a rank less than any other type.
- The rank of an enumerated type is equal to the rank of the compatible integer type.
- For all types T1, T2, and T3, if T1 has greater rank than T2, and T2 has greater rank than T3, then T1 has greater rank than T3.
Otherwise, if all operands are scalar, the usual arithmetic conversions apply as defined by Section 6.3.1.8 of the C99 specification.
Following are a few examples of legal usual arithmetic conversions with vectors and vector and scalar operands:
short a; int4 b; int4 c = b + a;
In this example, the variable a, which is of type short, is converted to an int4 and the vector addition is then performed.
int a; float4 b; float4 c = b + a;
In the preceding example, the variable a, which is of type int, is converted to a float4 and the vector addition is then performed.
float4 a; float4 b; float4 c = b + a;
In this example, no conversions need to be performed because a, b, and c are all the same type.
Here are a few examples of illegal usual arithmetic conversions with vectors and vector and scalar operands:
int a; short4 b; short4 c = b + a; // cannot convert & widen int to short4 double a; float4 b; float4 c = b + a; // cannot convert & widen double to float4 int4 a; float4 b; float4 c = b + a; // cannot cast between different vector types