4. Calling Convention#
PISA provides high-level C-like abstractions for calling and returning from a function.
4.1. Kernel#
A kernel is an entry point for execution and is defined using the .kernel directive.
Kernel cannot have a return value, but may have zero or more arguments. All kernel
arguments are read-only and have per-kernel scope, i.e. all work-items receive the
same value of each kernel argument.
.kernel @foo() {
// kernel body
}
Note
Kernel can only be invoked from the host program.
4.1.1. Attributes#
Several optional attributes may be specified for the kernels, using
.attribute_name(value) or .attribute_name syntax. They are
declared between .kernel and its name, and are separated by spaces.
.kernel .reqd_work_group_size(256, 2, 1) .vec_type_hint(int2) @test() {
// kernel body
}
.reqd_work_group_size(X, Y, Z)Specifies required work-group size. The value
X,Y, andZspecify the size of each dimension..vec_type_hint(TYPE)Specifies a hint of the computation width of the decorated kernel. The value
TYPEmay beint,int2, or any other scalar or built-in vector type defined by the OpenCL specification.
4.2. Kernel Parameters#
Kernel parameters are decorated using mandatory .param directive. Each argument is
modeled as a byte array stored in an abstract location pointed to by corresponding parameter.
Size of the argument in bytes must be specified in corresponding parameter declaration.
Argument data is loaded into registers using special ld.param instruction.
.kernel @foo(.param[4] %arg0, .param[8] %arg1) {
.reg .32b %larg0, %larg1;
// load contents of %arg0
ld.param.32b %larg0, [%arg0];
// load contents of %arg1[4]
ld.param.32b %larg1, [%arg1 + 4];
}
Important
Kernel parameters can only be referenced by ld.param instruction.
It is the responsibility of the PISA producer to define how to access struct members
via the ld.param instruction. Consider the following naturally aligned struct:
// | x | - | y | y | z | - | - | - | w | w | w | w |
struct {
unsigned char x;
unsigned short y;
unsigned char z;
unsigned int w;
}
.kernel @foo(.param[12] %arg) {
.reg .8b %local_x, %local_z;
.reg .16b %local_y;
.reg .32b %local_w;
// x is byte-aligned at offset 0
ld.param.8b %local_x, [%arg];
// y is aligned to 2 bytes at offset 2
ld.param.16b %local_y, [%arg + 2];
// z is byte-aligned at offset 4
ld.param.8b %local_z, [%arg + 4];
// w is aligned to 4 bytes at offset 8
ld.param.32b %local_w, [%arg + 8];
}
4.2.1. Parameter Attributes#
Several optional attributes may be specified for kernel parameters, using
.attribute_name(value) or .attribute_name syntax. They are
declared between .param and the parameter name, and are separated by spaces.
.kernel @foo(.param[8] .align(4) .addrspace(global) %arg0) {
// kernel body
}
.addrspace(ADDRSPACE)Specifies address space memory object points to.
ADDRSPACEcan beglobal,shared, orconst..align(BYTE)Specifies the alignment of the kernel parameter in the kernel argument buffer. The default alignment is 8 bytes;
BYTEmust be a power of two, and specifies the alignment in bytes..ptr_align(BYTE)Specifies the alignment of the memory pointed to by the kernel parameter.
BYTEmust be a power of two, and specifies alignment of the pointee in bytes.
4.3. Function#
Functions are declared/defined using the .function directive. Functions may have zero
or more arguments, and may return a value to the caller. The return type void indicates
that the function does not return a value.
.function void @foo() {
// function body
}
Note
Functions can only be invoked from kernels or other functions.
4.3.1. Linkage#
By default, functions have internal linkage, meaning that they are only visible within the module in which they are defined.
An .export directive is used to indicate external visibility of a function
across different modules. Exported functions can be invoked by other kernels
and/or functions defined in another module.
// allow access to function @foo from another module
.export .function void @foo() {
// function body
}
An .import directive is used to indicate that a function is defined
outside the current module. Imported functions must not have a function body
defined in the current module, and are only declared with their signature.
.import .function .32b @foo(.reg .32b, .reg .32b);
4.4. Function Arguments#
Function arguments are passed from caller to callee as actual arguments in a call instruction. Each actual argument must be a register variable, and must match the type specified in the function declaration, as no implicit conversion is allowed.
.function void @foo(.reg .32b %arg0, .reg .v2.32b %arg1) {
// function body
}
.function void @caller() {
.reg .32b %a;
.reg .v2.32b %b;
// ... code ...
call void, @foo(%a, %b);
// ... code ...
}
Note
Predicate variables cannot be used as function arguments.
4.5. Function Return#
Function may return a single value to a caller using the return instruction.
The return value may be an immediate or a register variable matching the
return type specified in the function declaration, as no implicit conversion is allowed. If no return
value is needed, void must be specified as the return type.
// function returning no value
.function void @foo() {
// function body
return;
}
// function returning a 32-bit value
.function .32b @foo() {
.reg .32b %rv;
// ... code ...
return %rv;
}
Note
Predicate variables cannot be used as a function return value.
Important
The return instruction must be the last instruction in the body of a non-void function.