OpenCL学习笔记 - 关键字 宏定义

Keywords - 关键字

在OpenCL C中下面的字作为关键字保留:

  • C99作为关键字保留的字
  • OpenCL C数据类型(表4.1,4.2,4.6)
  • 地址空间修饰符:__global, global, __local, local, __constant, constant, __privateprivate
  • 函数修饰符:__kernelkernel
  • 访问修饰符:__read_only, read_only, __write_only, write_only, __read_writeread_write

Preprocessor Directives and Macros - 预处理器命令和宏

OpenCL支持C99定义的预处理的命令。包括:

# non-directive
#if
#ifdef
#elif
#else
#endif
#include
#define
#undef
#line
#error
#pragma

还定义了defined操作符。

下面的例子说明了#if, #elif, #else和#endif预处理器宏的用法。在这里例子中,我们使用预处理器宏来确定kernel中使用的是那个算术运算。

#define OP_ADD 1
#define OP_SUBTRACT 2
#define OP_MULTIPLY 3
#define OP_DIVIDE 4
kernel void
foo(global float *dst, global float *srcA, global float *srcB)
{
size_t id = get_global_id(0);
#if OP_TYPE == OP_ADD
dst[id] = srcA[id] + srcB[id];
#elif OP_TYPE == OP_SUBTRACT
dst[id] = srcA[id] – srcB[id];
#elif OP_TYPE == OP_MULTIPLY
dst[id] = srcA[id] * srcB[id];
#elif OP_TYPE == OP_DIVIDE
dst[id] = srcA[id] / srcB[id];
#else
dst[id] = NAN;
#endif
}

为了让代码中的OP_TYPE有合适的值,应用程序调用clBuildProgram函数。如下:

// build program so that kernel foo does an add operation
err = clBuildProgram(program, 0, NULL,
"-DOP_TYPE=1", NULL, NULL);

Pragma Directives - 编译提示命令

#pragma命令用法如下:
#pragma pp-tokensopt new-line
在#pragma命令中,如果在pragma后面没有紧跟着预处理标记(preprocessing token)OPENCL(在所有的宏之前),那么程序的行为将是依据具体的实现而定的(implementation-defined)。这样可能产生翻译错误,也可能使得翻译器或结果程序不正常。所有这种实现不认识的pragma指令将被忽略。如果pragma后没有紧跟预处理标记OPENCL ,那么宏都是无效的。
下面将介绍标准pragma命令。
Floating-Point Pragma
FP_CONTRACT浮点编译提示(floating-point pragma)用来控制实现的合约表达式(contract expression)的开关。 FP_CONTRACT编译提示定义如下:
#pragma OPENCL FP_CONTRACT on-off-switch
on-off-switch: one of ON OFF DEFAULT
关于 #pragma OPENCL FP_CONTRACT的详细介绍可以在第五章“Floating-Point Pragmas”中找到。

Compiler Directive for Optional Extensions - 针对可选扩展的比编译器命令

#pragma OPENCL EXTENSION命令控制OpenCL编译器在语言扩展方面的行为。该命令的定义如下,其中extension_name是扩展的名字。
#pragma OPENCL EXTENSION extension_name: behavior
#pragma OPENCL EXTENSION all : behavior
behavior: enable or disable
extension_name的格式为cl_khr_,同时有一个形如cl__的名字作为供应商扩展。扩展必须得到OpenCL工作组的允许。标志all表示编译器支持的所有扩展。behavior可选的值如表4.9所示。
表4.9
enable 使extension_name所代表的扩展有效。如果不支持extension_name或者使用all,则报告一个错误。
disable 扩展extension_name排除在语言定义外。如果使用all,编译器把代码作为无扩展的核心版本处理。 如果不支持extension_name,会产生警告。
指令#pragma OPENCL EXTENSION是设置每个语言扩展行为的、简单的、低级的机制。它没有定义那些组合是合适的,这些是在其他地方定义的。此类命令的顺序会影响结果。后出现的命令会覆盖前面的命令。使用all将使用所有的扩展,覆盖之前的扩展指令,但只有使用disable作为behavior的时候。
任何一个在OpenCL程序中使用到的、扩展的语言特征(比如预处理宏、数据类型、内建函数),都必须先激活(enable)。下面的例子说明了如何激活双精度浮点数扩展:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
double x = 2.0;
如果不支持这个扩展,在double x = 2.0会产生一个编译错误。如果支持,在这条命令之后的程序中就可以使用双精度浮点数扩展。
类似的,cl_khr_3d_image_writes扩展添加了一些内置的函数,用于写3D的image:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
kernel void my_func(write_only image3d_t img, ...)
{
float4 coord, clr;
...
write_imagef(img, coord, clr);
}
只有激活对应的扩展,才能使用这些内置的函数(如上例中的write_imagef)。否则将产生编译错误。
初始时编译器是忽略所有的扩展的,等同于下面的命令:
#pragma OPENCL EXTENSION all : disable
语言的扩展会影响OpenCL语言的语法或句法或内置函数,同时还穿件了一个预处理的#define,与扩展的名字相对应。当且仅当扩展被实现所支持的时候,才能使用这个#define。比如,某个扩展增加了扩展字符串cl_khr_fp64,那么它应该也包含了一个为cl_khr_fp64的预处理#define。kernel可以使用这个#define来做如下的事情:
#ifdef cl_khr_fp64
// do something using this extension
#else
// do something else or #error
#endif

Macros - 宏

下面是可以使用的、预定义好的宏:
  • __FILE__:当前源文件的预测名(presumed name),一个字符串。
  • __LINE__:当前代码行在源文件中的行数,一个整数常量。
  • CL_VERSION_1_0:整数100,表示OpenCL 1.0。
  • CL_VERSION_1_1:整数110,表示OpenCL 1.1。
  • __OPENCL_VERSION__:为设备支持的、一个表示OpenCL版本的整数。这个反应了OpenCL 1.1 说明中表4.3中所示的语言版本和设备的功能。本文的OpenCL版本为1.1,即是__OPENCL_VERSION__的值为110。
  • __ENDIAN_LITTLE__:用来确定设备是little-endian还是big-endian。little-endian时为整数常量1;否则是未定义。
  • __kernel_exec(X, typen)(和kernel_exec(X, typen))定义为
__kernel __attribute__((work_group_size_hint(X, 1, 1))) \
__attribute__((vec_type_hint(typen))).
  • __IMAGE_SUPPORT__:用来确定设备是否支持图像(image)。支持则值为1;否则,未定义。
  • __FAST_RELAXED_MATH__:用来确定clBuildProgram中的生成选项中是否指明了-cl-fast-relaxed-math优化选项。指明了,则值为1;否则,未定义。
现在的OpenCL不支持C99规范中定义的宏名称,保留在将来使用。

Restrictions - 限制

OpenCL C实现了一下的限制。在本章的前面的部分已经介绍了一些。这里我们将它们一一列出。
kernel函数的限制
  • 指针类型的kernel函数的参数必须用global, constant或者local修饰。
  • kernel函数的参数不能是指针的指针。
  • kernel函数的参数不能是一下的内建数据类型:bool, half, size_t, ptrdiff_t, intptr_t, uintptr_t或者event_t
  • kernel函数返回值必须是void。
  • 如果以结构体作为kernel函数的参数,那么该结构体的成员不能有OpenCL对象(比如缓冲区(buffer),图像(image))。

  • 不支持位域结构体成员(bit field struct member)。
  • 不支持长度可变的数组和有未定义大小的数组的结构体。
  • 不支持可变参数宏(variadic macros)和可变参数函数。
  • 不支持预定义标识符,比如__func__
  • 不支持递归。
  • 不能使用C99标准头文件中的库函数,也不能包括这些标准头文件,它们有:assert.h, ctype.h, complex.h, errno.h, fenv.h, float.h, inttypes.h, limits.h, locale.h, setjmp.h, signal.h, stdarg.h, stdio.h, stdlib.h, string.h, tgmath.h, time.h, wchar.hwctype.h
  • 图像类型image2d_timage3d_t只能作为函数的参数,不能在函数内部用来申明局部变量,也不能做为函数的返回值。不能修改图像函数参数。对图像类型,不能用private, local, constant地址空间修饰符,也不能使用read_write访问修饰符(保留,在将来使用)。图像类型不能申明变量、结构体或共同体的域,不能申明图像数组,不能什么指向图像的指针,图像不能作为函数返回的类型。
  • 采样器类型(sampler type, sampler_t)只能作为函数参数,或者一个program范围的变量,或者kernel函数的最外层范围的变量。不是在kernel函数的最外层范围定义的采样器的行为是由具体实现定义的。不能改变采样器参数或变量。采样器类型不能作为结构体或共同体的域,不能申明采样器的数组或者指向采样器的指针,采样器也不能作为函数的返回类型。对采样器类型,不能使用local或global地址空间修饰符。
  • 事件类型(event type, event_t)可以作为除了kernel函数之外的函数的参数类型,或者用来申明函数内部的变量。可以申明事件类型的数组,可以申明指向事件的指针,比如event_t *event_ptr。不能修改一个事件参数或者变量。事件类型不能作为结构体或者共同的域,不能在program范围申明变量,不能用local, constantglobal地址空间修饰符。
  • 在kernel中irreducible control flow(不可逆的控制流)的表现是由具体事项而定的。在使用goto时,会经常碰到irreducible control flow。一个irreducible control flow的例子是:使用goto跳进一个嵌套的循环或者Duff’s device。






你可能感兴趣的:(OpenCL学习笔记 - 关键字 宏定义)