37 '''OpenCL PSyIR backend. Extends the C PSyIR back-end to generate
38 OpenCL code from PSyIR nodes.
49 '''Implements a PSyIR-to-OpenCL back-end for the PSyIR AST. This writer
50 produces OpenCL code conforming to Version 1.2 of the specification
51 (https://www.khronos.org/registry/OpenCL/specs/opencl-1.2.pdf).
53 :param bool skip_nodes: if skip_nodes is False then an exception \
54 is raised if a visitor method for a PSyIR node has not been \
55 implemented, otherwise the visitor silently continues. This is an \
56 optional argument which defaults to False.
57 :param str indent_string: specifies what to use for indentation. This \
58 is an optional argument that defaults to two spaces.
59 :param int initial_indent_depth: specifies how much indentation to \
60 start with. This is an optional argument that defaults to 0.
61 :param int kernel_local_size: uses the given local_size when generating \
64 :raises TypeError: if kernel_local_size is not an integer.
65 :raises ValueError: if kernel_local_size is not positive.
68 def __init__(self, skip_nodes=False, indent_string=" ",
69 initial_indent_depth=0, kernels_local_size=1):
71 super(OpenCLWriter, self).__init__(
72 skip_nodes, indent_string, initial_indent_depth)
74 if not isinstance(kernels_local_size, int):
76 f
"kernel_local_size should be an integer but found "
77 f
"'{type(kernels_local_size).__name__}'.")
79 if kernels_local_size < 1:
81 f
"kernel_local_size should be a positive integer but found "
82 f
"{kernels_local_size}.")
88 Generate the declaration and initialisation of a variable identifying
89 an OpenCL work-item. IDs are initialised by the OpenCL function:
90 `size_t get_global_id(uint dimindx)`
92 :param symbol: The symbol instance.
93 :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
94 :param int dimension_index: Dimension which the given symbol will \
97 :returns: OpenCL declaration of an OpenCL id variable.
100 :raises VisitorError: if symbol is not a scalar integer
102 if (
not isinstance(symbol.datatype, ScalarType)
or
103 symbol.datatype.intrinsic != ScalarType.Intrinsic.INTEGER):
105 f
"OpenCL work-item identifiers must be scalar integer symbols "
106 f
"but found {symbol}.")
109 code += self.
_nindent_nindent +
"int " + symbol.name
110 code +=
" = get_global_id(" + str(dimension_index) +
");\n"
115 Generate the declaration of an OpenCL variable. This can be either
116 a local variable or a routine argument, so no indention or punctuation
117 is generated by this method.
119 Memory buffers reside in specific levels of the memory hierarchy,
120 and pointers are annotated with the region qualifiers __global,
121 __local, __constant, and __private, reflecting this.
122 At the moment all memory buffers (PSyIR arrays) are allocated at
123 the global address space.
125 :param symbol: The symbol instance.
126 :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
128 :returns: The OpenCL declaration of the given of the symbol.
131 :raises VisitorError: if an array is encountered that does not have \
132 a lower bound of 1 for all of its dimensions.
136 for dim
in symbol.shape:
137 if not isinstance(dim, ArrayType.ArrayBounds):
139 if (
not isinstance(dim.lower, Literal)
or
140 dim.lower.value !=
"1"):
142 f
"The OpenCL backend only supports arrays with a lower"
143 f
" bound of 1 in each dimension. However, array "
144 f
"'{symbol.name}' has a lower bound of "
145 f
"'{self._visit(dim.lower)}' for dimension "
146 f
"{symbol.shape.index(dim)}")
147 prefix +=
"__global "
152 Generate length variable declaration and initialisation for each array
153 dimension of the given symbol. By convention they are named:
154 <name>LEN<DIM>, and initialised using the function:
155 `size_t get_global_size(uint dimindx)`.
158 :param symbol: The symbol instance.
159 :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
160 :param symtab: The symbol table from the given symbol context to \
161 check for name clashes.
162 :type symtab: :py:class:`psyclone.psyir.symbols.SymbolTable`
164 :return: OpenCL declaration and initialisation of length variables.
167 :raises VisitorError: if the array length variable name clashes \
168 with another symbol name.
172 dimensions = len(symbol.shape)
173 for dim
in range(1, dimensions + 1):
174 code += self.
_nindent_nindent +
"int "
175 varname = symbol.name +
"LEN" + str(dim)
178 if symtab
and varname
in symtab:
180 f
"Unable to declare the variable '{varname}' to store the "
181 f
"length of '{symbol.name}' because the Symbol Table "
182 f
"already contains a symbol with the same name.")
184 code += varname +
" = get_global_size("
185 code += str(dim - 1) +
");\n"
189 '''This method is called when a KernelSchedule instance is found in
192 :param node: A KernelSchedule PSyIR node.
193 :type node: :py:class:`psyclone.psyir.nodes.KernelSchedule`
195 :returns: The OpenCL code as a string.
198 :raises VisitorError: if a non-precision symbol is found with a \
212 symtab = node.symbol_table
213 data_args = symtab.data_arguments
219 unresolved_datasymbols = list(set(symtab.unresolved_datasymbols) -
220 set(symtab.precision_datasymbols))
221 if unresolved_datasymbols:
222 symbols_txt =
", ".join(
223 [f
"'{sym.name}'" for sym
in unresolved_datasymbols])
225 f
"Cannot generate OpenCL because the symbol table contains "
226 f
"unresolved data entries (i.e. that have no defined "
227 f
"Interface) which are not used purely to define the "
228 f
"precision of other symbols: {symbols_txt}")
233 code += f
"__attribute__((reqd_work_group_size("\
234 f
"{self._kernels_local_size}, 1, 1)))\n"
235 code +=
"__kernel void " + node.name +
"(\n"
240 for symbol
in data_args:
242 code +=
",\n".join(arguments) +
"\n"
243 code += self.
_nindent_nindent +
"){\n"
246 for symbol
in symtab.automatic_datasymbols:
250 for symbol
in data_args:
254 for index, symbol
in enumerate(symtab.iteration_indices):
258 for child
in node.children:
259 code += self.
_visit_visit(child)
263 code += self.
_nindent_nindent +
"}\n\n"
def gen_local_variable(self, symbol)
def gen_declaration(self, symbol)
def gen_id_variable(self, symbol, dimension_index)
def kernelschedule_node(self, node)
def gen_array_length_variables(self, symbol, symtab=None)
def gen_declaration(self, symbol)