Skip to content
angavrilov edited this page Sep 13, 2010 · 4 revisions

This page describes how GPU code and variables can be defined.

Note: see the Definer Syntax page for an explanation of the def macro.

GPU compiler macros

  • (def (gpu-macro e) name (args…) …)

Defines a GPU-specific macro. The behavior requirements are similar to normal compiler macros.

GPU type expanders

  • (def (gpu-type e :gpu-only t) name (args…) …)

This definition is equivalent to deftype, but expanders defined this way are also accessible to the GPU code translator.

If :gpu-only is true, an ordinary deftype is not performed, so effectively the type is defined only for GPU code.

GPU module definitions

GPU code is packaged in modules, which can contain a number of variables and functions that are compiled together. A module can be defined in the following way:

(def (gpu-module eas) name
  specs...)

The following item specifications are supported:

  • (:conc-name prefix)
    Explicitly sets a prefix to prepend to variable and kernel names in order to generate accessor wrapper names. The default is module name + “-”, as with standard structure accessors.
  • (:variable name type)
  • (:global name type)
    Defines a module variable. It can be written from the GPU code.

Variables can either have a primitive scalar type, or point to a specialized array of primitive items. If all dimensions of an array are specified as constants, it is statically allocated; otherwise you have to allocate an appropriately typed buffer and assign it to the variable from the host.

  • (:variables type names…)
  • (:globals type names…)
    A shortcut for defining many globals of the same type.
  • (:constant name type)
  • (:constants type names…)
    Define module variables that can only be read from GPU code. They can still be modified from lisp.

Appropriately marking variables constant allows their values to be cached by the GPU hardware, thus boosting the read performance tremendously. Note that since GPU code cannot allocate memory, and therefore has no need to change internal pointers used to implement dynamic array globals, they are automatically declared constant.

  • (:kernel name (args…) body…)
    Defines a kernel, i.e. a GPU function callable from ordinary lisp code.

All kernel argument types must be declared. Optional and rest arguments are not allowed. Keyword and &aux arguments are supported, but they are evaluated in lisp code before the real GPU function is invoked. This causes &aux arguments to behave differently from top-level let bindings, thus making them actually useful.

Kernels accept additional pre-defined keyword arguments that specify things like the number of threads to launch.

On the lisp side, a GPU module definition creates wrapper symbol macros and functions for all listed variables and kernels. Their names are derived in a way similar to struct field accessors. If the export-accessor-names flag is passed to the definer, all of their names are exported.
The export-slot-names flag controls whether the short names are exported for use inside with-gpu-module.

The wrappers implicitly depend on the active GPU context of the current thread; each context has its own copy of the module variables.

Exposing a GPU module in lexical scope

The following macro can be used to make module variable and kernel names accessible in the lexical context in a way similar to with-slots:

(with-gpu-module name
  code...)

Where the name parameter is the symbol used previously in a global module definition (the gpu-module definer defines it as a symbol macro that expands to the internal module object). The code within the scope of this macro can access the GPU module items via the names used in their specifications.

It is also possible to define an ad-hoc module by passing a specification list instead of the name:

(with-gpu-module
    ((:variable foo ...)
     (:kernel bar () ...))
  code...)

Such modules are anonymous and can only be accessed by the code within the scope of the macro. Recompiling or reloading the containing function will cause a new module instance to be created, thus leaking the old one and losing access to its variable contents.

This can be avoided by specifying an explicit name via a (:name name) clause. When it is used, the resulting module is global like the ones declared at the file scope (and can conflict on the name with them, or other named ad-hoc modules), but has no global accessors or symbol-macro bindings for its name. Such modules can also be accessed via (with-gpu-module name …), but unlike normal global modules it won’t work from within the same compilation unit.

Clone this wiki locally