跳转至

Macro and Preprocessing

概述

宏长期以来是 C/C++ 编程的基础能力。为同时顺畅地操作 C++ 与 tileflow 程序,宏展开不可或缺。本节展示 Choreo 的宏处理能力。

类对象宏(Object-Like Macros)

Choreo 预处理器支持 C/C++ 类对象宏(object-like macros),以连接 host 代码与 tileflow 代码。类对象宏 仅做纯文本替换,不接受参数。下例说明:

#define M 256
#define N 32
#define K 64

__co__ auto matmul(f32 [M, N] lhs, f32 [N, K] rhs) { /*...*/ }

void foo() {
  choreo::f32 a[M][K];
  choreo::f32 b[N][K];
  // ...
  auto res = matmul(choreo::make_spanview<2>(a, {M, K}),
                    choreo::make_spanview<2>(b, {N, K}));
}
在代码片段中,Choreo 函数 matmul 的输入并非动态形状;Choreo 预处理器在 Choreo 编译之前将 MNK 替换为 2563264,从而生成静态形状输入的 tileflow 函数。由此,程序员可使不同代码路径保持一致并便于维护。

注意:截至目前,Choreo 尚不支持带参宏。因此如下代码:

#define MAX(a, b) (((a) > (b)) ? (a) : (b))
无法生效。

注释

Choreo 支持 C 风格注释,即 /*...*///...,依托 choreo 预处理器已提供的能力。

条件编译

Choreo 亦支持 C/C++ 中大量使用的条件编译,包括 #if / #ifdef / #ifndef / #else / #endif。下例展示用法:

#define PATH0
// some host code
__co__ foo() {
#ifdef PATH0
// some code related to PATH0
#else
// other code
#endif
}

// host control
#ifdef PATH0
// ...
#else
// ...
#endif
由此,host 与 choreo 代码可在同一套预处理流程中受控。

注意:choreo 预处理器的能力仍在增强;完全复刻 C 预处理的可能性不大。除非确有必要,Choreo 不会引入既有 C 特性。

与 C++ 预处理的差异

关于 choreo 预处理的重要一点是:其触发时机远早于 C++ 预处理。choreo 编译流程如下所示:

chore-preprocessing -> choreo compilation -> c/c++ preprocessing -> c/c++ compilation

choreo 预处理的主要目标是使 host/device 宏作为整体协同工作,但该流程有时会导致行为差异。从实现角度,Choreo 预处理器仅替换/条件编译 tileflow 函数内部的代码,其余预处理交给 C++ 预处理器,从而将 Choreo 预处理限制在较小范围内。

预定义宏

为模拟目标 native 编译,choreo 预处理还会从目标平台读取内置宏。例如,生成 CUDA/Cute 代码时全局定义 __CUDA__,而 __CUDA_ARCH__ 仅在 CUDA/Cute device 代码编译时设置。 因此,这些宏既可用于 tileflow 函数,也可用于 host 代码。