Create an MLIR `func.FuncOp` for the given FunctionDef AST node. For the top-level FunctionDef, this will add the `FuncOp` to the `ModuleOp` body, annotate the `FuncOp` with `cudaq-entrypoint` if it is an Entry Point CUDA-Q kernel, and visit the rest of the FunctionDef body.
(self, node)
| 1785 | |
| 1786 | @trace.traced("ast_bridge.visit_function_def") |
| 1787 | def visit_FunctionDef(self, node): |
| 1788 | """Create an MLIR `func.FuncOp` for the given FunctionDef AST node. For |
| 1789 | the top-level FunctionDef, this will add the `FuncOp` to the `ModuleOp` |
| 1790 | body, annotate the `FuncOp` with `cudaq-entrypoint` if it is an Entry |
| 1791 | Point CUDA-Q kernel, and visit the rest of the FunctionDef body. If this |
| 1792 | is an inner FunctionDef, this will treat the function as a CC lambda |
| 1793 | function and add the cc.callable-typed value to the symbol table, keyed |
| 1794 | on the FunctionDef name. |
| 1795 | |
| 1796 | We keep track of the top-level function name as well as its internal |
| 1797 | MLIR name, prefixed with the __nvqpp__mlirgen__ prefix. |
| 1798 | """ |
| 1799 | |
| 1800 | if self.buildingFunctionBody: |
| 1801 | for decorator in getattr(node, 'decorator_list', []): |
| 1802 | qname = _get_qualified_name(decorator) |
| 1803 | if qname == 'kernel' or ( |
| 1804 | qname is not None and qname.endswith('.kernel') and |
| 1805 | self.isCudaqName(qname[:qname.rfind('.kernel')])): |
| 1806 | self.emitFatalError( |
| 1807 | "nested @cudaq.kernel definitions are not allowed", |
| 1808 | node) |
| 1809 | |
| 1810 | # This is an inner function def, we will treat it as a cc.callable |
| 1811 | # (cc.create_lambda) |
| 1812 | self.debug_msg(lambda: f'Visiting inner FunctionDef {node.name}') |
| 1813 | lambdaFct = self.__createFunctionWithinKernel( |
| 1814 | node.args.args, node.body) |
| 1815 | assignNode = ast.Assign() |
| 1816 | assignNode.targets = [ast.Name(node.name)] |
| 1817 | assignNode.value = lambdaFct |
| 1818 | assignNode.lineno = node.lineno |
| 1819 | self.visit_Assign(assignNode) |
| 1820 | return |
| 1821 | |
| 1822 | with self.ctx, InsertionPoint(self.module.body), self.loc: |
| 1823 | |
| 1824 | # Get the potential documentation string |
| 1825 | self.docstring = ast.get_docstring(node) |
| 1826 | |
| 1827 | # Add uniqueness. In MLIR, we require unique symbols (`bijective` |
| 1828 | # function between symbols and artifacts) even if Python allows |
| 1829 | # hiding symbols and replacing symbols (dynamic `injective` function |
| 1830 | # between scoped symbols and artifacts). |
| 1831 | self.name = node.name + ".." + hex(self.uniqueId) |
| 1832 | |
| 1833 | # the full function name in MLIR is `__nvqpp__mlirgen__` + the |
| 1834 | # function name |
| 1835 | fullName = nvqppPrefix + self.name |
| 1836 | |
| 1837 | # Determine whether this kernel will be an entry point (no quantum |
| 1838 | # types in the signature). Run this check before creating the |
| 1839 | # `FuncOp` so a rejection does not leave an orphaned op in the module. |
| 1840 | anyQuantumType = any( |
| 1841 | self.isQuantumType(ty) for ty in self.signature.arg_types) |
| 1842 | if not anyQuantumType: |
| 1843 | # `cudaq::measure_handle` is device-only and must not appear |
| 1844 | # either directly or transitively in the parameter or return |
nothing calls this directly
no test coverage detected