Exemplo n.º 1
0
def get_kernel_params(
    members: PyKokkosMembers,
    is_hierarchical: bool,
    is_workload: bool,
    real: Optional[str]
) -> Dict[str, str]:
    """
    Get the parameters of the kernel. The parameters include the fields, the views,
    the views holding the reduction results, and the view holding the timer results.
    Also add parameters for the parameters of the execution policy.

    :param members: an object containing the fields and views
    :param is_hierarchical: does the workunit use hierarchical parallelism
    :param real: the precision for which to generate a binding
    :returns: a dict mapping from argument name to type
    """

    s = cppast.Serializer()
    params: Dict[str, str] = {}
    for n, t in members.fields.items():
        params[n.declname] = s.serialize(t)

    for n, t in members.views.items():
        # skip subviews
        if t is None:
            continue
        layout: str = f"{Keywords.DefaultExecSpace.value}::array_layout"
        params[n.declname] = cpp_view_type(t, space=Keywords.ArgMemSpace.value, layout=layout, real=real)

    if not is_workload:
        params[Keywords.KernelName.value] = "const std::string&"

        if is_hierarchical:
            params[Keywords.LeagueSize.value] = "int"
            params[Keywords.TeamSize.value] = "int"
            params[Keywords.VectorLength.value] = "int"
        else:
            params[Keywords.ThreadsBegin.value] = "int"
            params[Keywords.ThreadsEnd.value] = "int"

    for result in members.reduction_result_queue:
        view_name = f"reduction_result_{result}"
        view_type = cppast.ClassType("View1D")
        view_type.add_template_param(cppast.DeclRefExpr("double"))
        view_type.add_template_param(cppast.DeclRefExpr("HostSpace"))
        params[view_name] = cpp_view_type(view_type, space="Kokkos::HostSpace", layout="Kokkos::LayoutRight")

    for result in members.timer_result_queue:
        view_name = f"timer_result_{result}"
        view_type = cppast.ClassType("View1D")
        view_type.add_template_param(cppast.DeclRefExpr("double"))
        view_type.add_template_param(cppast.DeclRefExpr("HostSpace"))
        params[view_name] = cpp_view_type(view_type, space="Kokkos::HostSpace", layout="Kokkos::LayoutRight")

    return params
Exemplo n.º 2
0
def generate_functor(
    name: str,
    members: PyKokkosMembers,
    workunits: Dict[cppast.DeclRefExpr, Tuple[str, cppast.MethodDecl]],
    functions: List[cppast.MethodDecl],
) -> cppast.RecordDecl:
    """
    Generate the functor source

    :param name: the functor class name
    :param members: an object containing the fields and views
    :param workunits: a dict mapping from workunit name to a tuple of operation type and source
    :param functions: a list of KOKKOS_FUNCTIONS defined in the functor
    :param has_real: whether the function contains a pk.real datatype
    :returns: the cppast representation of the functor
    """

    fields: Dict[cppast.DeclRefExpr, cppast.PrimitiveType] = members.fields
    views: Dict[cppast.DeclRefExpr, cppast.ClassType] = members.views

    decls: List[cppast.DeclStmt] = []

    # Create the tags needed to call individual workunits. Tags in Kokkos are empty structs.
    for n in workunits:
        tag = cppast.RecordDecl(cppast.ClassType(n.declname), [])
        tag.is_definition = True
        decls.append(tag)

    for n, t in fields.items():
        decls.append(cppast.DeclStmt(cppast.FieldDecl(t, n)))

    for n, t in views.items():
        # skip subviews
        if t is None:
            continue
        view_type: str = get_view_type(t)
        decls.append(cppast.DeclStmt(cppast.FieldDecl(view_type, n)))

    decls.append(generate_constructor(name, fields, views))
    for _, s in workunits.values():
        decls.append(s)

    for f in functions:
        decls.append(cppast.DeclStmt(f))

    struct = cppast.RecordDecl(cppast.ClassType(name), decls)
    struct.is_definition = True

    if members.has_real:
        struct.add_template_param(Keywords.RealPrecision.value)
        s = cppast.Serializer()

    return struct
Exemplo n.º 3
0
    def generate_subview(self, node: ast.Assign, view_name: str) -> cppast.DeclStmt:
        subview_args: List[cppast.Expr] = [cppast.DeclRefExpr(view_name)]

        slice_node = node.value
        for dim in slice_node.slice.dims:
            if isinstance(dim, ast.Index):
                subview_args.append(self.visit(dim))
            else:
                if dim.lower is None and dim.upper is None: 
                    subview_args.append(cppast.DeclRefExpr("Kokkos::ALL"))
                elif dim.lower is not None and dim.upper is not None:
                    make_pair = cppast.CallExpr("std::make_pair",
                            [self.visit(dim.lower), self.visit(dim.upper)])
                    subview_args.append(make_pair)
                else:
                    self.error(
                            slice_node, "Partial slice not supported, use [n:m] or [:]")

        if len(node.targets) > 1:
            self.error(node, "Multiple declarations of subview not supported")

        auto = cppast.ClassType("auto")
        target = node.targets[0]
        target_ref = cppast.DeclRefExpr(target.id)
        if target_ref in self.views:
            self.error(
                node, "Redeclaration of existing subview")
        else:
            self.views[target_ref] = None
            self.subviews[target_ref.declname] = view_name

        call = cppast.CallExpr("Kokkos::subview", subview_args)
        decl = cppast.DeclStmt(cppast.VarDecl(auto, self.visit(target), call))

        return decl 
Exemplo n.º 4
0
    def visit_FunctionDef(
        self, node: ast.FunctionDef
    ) -> Union[cppast.ConstructorDecl, cppast.MethodDecl]:
        name: str = node.name
        return_type: Optional[cppast.ClassType]

        if name == "__init__":
            name = node.parent.name
            return_type = None
        elif self.is_void_function(node):
            return_type = cppast.ClassType("void")
        else:
            return_type = visitors_util.get_type(node.returns, self.pk_import)

        if len(node.args.args) == 0 or node.args.args[0].arg != "self":
            self.error(node, "Static functions are not supported")

        params: List[cppast.ParmVarDecl] = self.visit(node.args)
        body = cppast.CompoundStmt([self.visit(b) for b in node.body])
        attributes: str = "KOKKOS_FUNCTION"

        if return_type is None:
            return cppast.ConstructorDecl(attributes, name, params, body)
        else:
            return cppast.MethodDecl(attributes, return_type, name, params,
                                     body)
Exemplo n.º 5
0
    def visit_ClassDef(self, node: ast.ClassDef) -> cppast.RecordDecl:
        name: str = node.name
        # Add class as allowed type
        visitors_util.allowed_types[name] = name

        member_variables: Dict[cppast.DeclRefExpr,
                               cppast.Type] = self.get_member_variables(node)
        decls: List[cppast.DeclStmt] = []

        if len(member_variables) == 0:
            self.error(node,
                       "Missing constructor or no member variables detected")

        for n, t in member_variables.items():
            decls.append(cppast.DeclStmt(cppast.FieldDecl(t, n)))

        for b in node.body:
            decls.append(self.visit(b))

        if not self.has_default_constructor(node):
            decls.append(
                cppast.ConstructorDecl("KOKKOS_FUNCTION", name, [], None))

        classdef = cppast.RecordDecl(cppast.ClassType(name), decls)
        classdef.is_definition = True

        return classdef
Exemplo n.º 6
0
    def visit_Subscript(self, node: ast.Subscript) -> Union[cppast.ArraySubscriptExpr, cppast.CallExpr]:
        current_node: ast.Subscript = node
        slices: List = []
        dim: int = 0

        while isinstance(current_node, ast.Subscript):
            index = current_node.slice

            if sys.version_info.minor <= 8:
                # In Python >= 3.9, ast.Index is deprecated
                # (see # https://docs.python.org/3/whatsnew/3.9.html)
                # Instead of ast.Index, value will be used directly

                if not isinstance(index, ast.Index):
                    self.error(
                        current_node, "Slices not supported, use simple indices")

            slices.insert(0, index)
            current_node = current_node.value
            dim += 1

        name: str = visitors_util.get_node_name(current_node)
        ref = cppast.DeclRefExpr(name)

        if ref not in self.views and name not in self.lists:
            self.error(current_node, "Unknown view or list")

        dim_map: List = [cppast.ClassType("View1D"),
                         cppast.ClassType("View2D"),
                         cppast.ClassType("View3D"),
                         cppast.ClassType("View4D"),
                         cppast.ClassType("View5D"),
                         cppast.ClassType("View6D"),
                         cppast.ClassType("View7D"),
                         cppast.ClassType("View8D")]

        if name in self.lists:
            indices: List[cppast.Expr] = [self.visit(s) for s in slices]
            subscript = cppast.ArraySubscriptExpr(ref, indices)

            return subscript

        if (
            ref in self.views
            and (
                self.views[ref] is None  # For views added in @pk.main
                or self.views[ref].typename == dim_map[dim - 1].typename
            )
        ):
            args: List[cppast.Expr] = [self.visit(s) for s in slices]
            subscript = cppast.CallExpr(ref, args)

            return subscript

        self.error(node, f"'{name}' is not a View{dim}D")
Exemplo n.º 7
0
    def visit_FunctionDef(
            self, node: ast.FunctionDef
    ) -> Union[str, Tuple[str, cppast.MethodDecl]]:
        if self.is_nested_call(node):
            params: List[cppast.ParmVarDecl] = [
                a for a in self.visit(node.args)
            ]
            body = cppast.CompoundStmt([self.visit(b) for b in node.body])

            workunit = cppast.LambdaExpr("[&]", params, body)
            self.nested_work_units[node.name] = workunit

            return ""

        else:
            operation: Optional[str] = self.get_operation_type(node)
            if operation is None:
                self.error(node.args, "Incorrect types in workunit definition")

            tag_type = cppast.ClassType(f"const {node.name}")
            tag_type.is_reference = True
            tag = cppast.ParmVarDecl(tag_type, cppast.DeclRefExpr(""))

            params: List[cppast.ParmVarDecl] = [tag]
            params.extend(self.visit(node.args))

            body = cppast.CompoundStmt([self.visit(b) for b in node.body])
            attributes: str = "KOKKOS_FUNCTION"
            decltype = cppast.ClassType("void")
            declname: str = "operator()"

            method = cppast.MethodDecl(attributes, decltype, declname, params,
                                       body)
            method.is_const = True

            return (operation, method)
Exemplo n.º 8
0
    def visit_FunctionDef(self, node: ast.FunctionDef) -> cppast.MethodDecl:
        if not self.is_valid_kokkos_function(node):
            self.error(node, "Invalid Kokkos function")

        return_type: cppast.ClassType
        if self.is_void_function(node):
            return_type = cppast.ClassType("void")
        else:
            return_type = visitors_util.get_type(node.returns, self.pk_import)

        if return_type is None:
            self.error(node, "Return type is not supported for translation")

        params: List[cppast.ParmVarDecl] = self.visit(node.args)

        name: str = node.name
        body = cppast.CompoundStmt([self.visit(b) for b in node.body])
        attributes: str = "KOKKOS_FUNCTION"

        method = cppast.MethodDecl(attributes, return_type, name, params, body)
        method.is_const = True

        return method
Exemplo n.º 9
0
def get_type(annotation: Union[ast.Attribute, ast.Name, ast.Subscript],
             pk_import: str) -> Optional[cppast.Type]:
    if isinstance(annotation, ast.Attribute):
        if annotation.value.id == pk_import:
            type_name: str = get_node_name(annotation)

            if type_name in view_dtypes:
                return cppast.PrimitiveType(view_dtypes[type_name])

            if type_name in allowed_types:
                type_name = allowed_types[type_name]

            return cppast.ClassType(type_name)

    if isinstance(annotation, ast.Name):
        type_name: str = annotation.id

        if type_name == "int":
            return cppast.PrimitiveType(cppast.BuiltinType.INT)

        if type_name == "float":
            return cppast.PrimitiveType(cppast.BuiltinType.DOUBLE)

        if type_name == "bool":
            return cppast.PrimitiveType(cppast.BuiltinType.BOOL)

        if type_name in allowed_types:
            return cppast.ClassType(type_name)

        if type_name == "List":
            return None

    if isinstance(annotation, ast.Subscript):
        value: Union[ast.Name, ast.Attribute] = annotation.value
        subscript: ast.Index = annotation.slice

        id: str = ""
        if isinstance(value, ast.Name):
            id = value.id
        elif isinstance(value, ast.Attribute):
            id = value.value.id

        if id == "List":
            if sys.version_info.minor <= 8:
                # In Python >= 3.9, ast.Index is deprecated
                # (see # https://docs.python.org/3/whatsnew/3.9.html)
                value = subscript.value
            else:
                value = subscript
            member_type: cppast.Type = get_type(value, pk_import)

            return member_type

        if id == pk_import:
            type_name: str = get_node_name(value)

            if sys.version_info.minor <= 8:
                # In Python >= 3.9, ast.Index is deprecated
                # (see # https://docs.python.org/3/whatsnew/3.9.html)
                dtype_node: ast.Attribute = subscript.value
            else:
                dtype_node: ast.Attribute = subscript

            if type_name == "Acc":
                return get_type(dtype_node, pk_import)

            dtype: cppast.PrimitiveType = get_type(dtype_node, pk_import)
            if dtype is None:
                return None

            view_type = cppast.ClassType(type_name)
            view_type.add_template_param(dtype)

            return view_type

    return None
Exemplo n.º 10
0
    def visit_Assign(self, node: ast.Assign) -> cppast.Stmt:
        target = node.targets[0]

        if isinstance(node.value, ast.Call):
            name: str = visitors_util.get_node_name(node.value.func)

            # Create Timer object
            if name == "Timer":
                decltype = cppast.ClassType("Kokkos::Timer")
                declname = cppast.DeclRefExpr("timer")
                return cppast.DeclStmt(cppast.VarDecl(decltype, declname,
                                                      None))

            # Call Timer.seconds()
            if name == "seconds":
                target_name: str = visitors_util.get_node_name(target)
                if target_name not in self.timer_result_queue:
                    self.timer_result_queue.append(target_name)

                call = cppast.CallStmt(self.visit(node.value))
                target_ref = cppast.DeclRefExpr(target_name)
                target_view_ref = cppast.DeclRefExpr(
                    f"timer_result_{target_name}")
                subscript = cppast.ArraySubscriptExpr(
                    target_view_ref, [cppast.IntegerLiteral(0)])
                assign_op = cppast.BinaryOperatorKind.Assign

                # Holds the result of the reduction temporarily
                temp_ref = cppast.DeclRefExpr("pk_acc")
                target_assign = cppast.AssignOperator([target_ref], temp_ref,
                                                      assign_op)
                view_assign = cppast.AssignOperator([subscript], target_ref,
                                                    assign_op)

                return cppast.CompoundStmt([call, target_assign, view_assign])

            if name in ("BinSort", "BinOp1D", "BinOp3D"):
                args: List = node.value.args
                # if not isinstance(args[0], ast.Attribute):
                #     self.error(node.value, "First argument has to be a view")

                view = cppast.DeclRefExpr(visitors_util.get_node_name(args[0]))
                if view not in self.views:
                    self.error(args[0], "Undefined view")

                view_type: cppast.ClassType = self.views[view]
                is_subview: bool = view_type is None
                if is_subview:
                    parent_view = cppast.DeclRefExpr(
                        self.subviews[view.declname])
                    view_type = self.views[parent_view]

                view_type_str: str = visitors_util.cpp_view_type(view_type)

                if name != "BinSort":
                    dimension: int = 1 if name == "BinOp1D" else 3
                    cpp_type = cppast.DeclRefExpr(
                        BinOp.get_type(dimension, view_type_str))

                    # Do not translate the first argument (view)
                    constructor = cppast.CallExpr(
                        cpp_type, [self.visit(a) for a in args[1:]])

                else:
                    bin_op_type: str = f"decltype({visitors_util.get_node_name(args[1])})"
                    cpp_type = cppast.DeclRefExpr(
                        BinSort.get_type(view_type_str, bin_op_type))

                    binsort_args: List[cppast.DeclRefExpr] = [
                        self.visit(a) for a in args
                    ]
                    constructor = cppast.CallExpr(cpp_type, binsort_args)

                cpp_target: cppast.DeclRefExpr = self.visit(target)
                auto_type = cppast.ClassType("auto")

                return cppast.DeclStmt(
                    cppast.VarDecl(auto_type, cpp_target, constructor))

            if name in ("get_bin_count", "get_bin_offsets",
                        "get_permute_vector"):
                if not isinstance(target,
                                  ast.Attribute) or target.value.id != "self":
                    self.error(
                        node,
                        "Views defined in pk.main must be an instance variable"
                    )

                cpp_target: str = visitors_util.get_node_name(target)
                cpp_device_target = f"pk_d_{cpp_target}"
                cpp_target_ref = cppast.DeclRefExpr(cpp_device_target)
                sorter: cppast.DeclRefExpr = self.visit(node.value.func.value)

                initial_target_ref = cppast.DeclRefExpr(
                    f"_pk_{cpp_target_ref.declname}")

                function = cppast.MemberCallExpr(sorter,
                                                 cppast.DeclRefExpr(name), [])

                # Add to the dict of declarations made in pk.main
                if name == "get_permute_vector":
                    # This occurs when a workload is executed multiple times
                    # Initially the view has not been defined in the workload,
                    # so it needs to be classified as a pkmain_view.
                    if cpp_target in self.views:
                        self.views[cpp_target_ref].add_template_param(
                            cppast.PrimitiveType(cppast.BuiltinType.INT))

                        return cppast.AssignOperator(
                            [cpp_target_ref], function,
                            cppast.BinaryOperatorKind.Assign)
                        # return f"{cpp_target} = {sorter}.{name}();"

                    self.pkmain_views[cpp_target_ref] = cppast.ClassType(
                        "View1D")
                else:
                    self.pkmain_views[cpp_target_ref] = None

                auto_type = cppast.ClassType("auto")
                decl = cppast.DeclStmt(
                    cppast.VarDecl(auto_type, initial_target_ref, function))

                # resize the workload's vector to match the generated vector
                resize_call = cppast.CallStmt(
                    cppast.CallExpr(cppast.DeclRefExpr("Kokkos::resize"), [
                        cpp_target_ref,
                        cppast.MemberCallExpr(initial_target_ref,
                                              cppast.DeclRefExpr("extent"),
                                              [cppast.IntegerLiteral(0)])
                    ]))

                copy_call = cppast.CallStmt(
                    cppast.CallExpr(cppast.DeclRefExpr("Kokkos::deep_copy"),
                                    [cpp_target_ref, initial_target_ref]))

                # Assign to the functor after resizing
                functor = cppast.DeclRefExpr("pk_f")
                functor_access = cppast.MemberExpr(functor, cpp_target)
                functor_assign = cppast.AssignOperator(
                    [functor_access], cpp_target_ref,
                    cppast.BinaryOperatorKind.Assign)

                return cppast.CompoundStmt(
                    [decl, resize_call, copy_call, functor_assign])

        # Assign result of parallel_reduce
        if type(target) not in {ast.Name, ast.Subscript
                                } and target.value.id == "self":
            target_name: str = visitors_util.get_node_name(target)
            if target_name not in self.reduction_result_queue:
                self.reduction_result_queue.append(target_name)

            call = cppast.CallStmt(self.visit(node.value))
            target_ref = cppast.DeclRefExpr(target_name)
            target_view_ref = cppast.DeclRefExpr(
                f"reduction_result_{target_name}")
            subscript = cppast.ArraySubscriptExpr(target_view_ref,
                                                  [cppast.IntegerLiteral(0)])
            assign_op = cppast.BinaryOperatorKind.Assign

            # Holds the result of the reduction temporarily
            temp_ref = cppast.DeclRefExpr("pk_acc")
            target_assign = cppast.AssignOperator([target_ref], temp_ref,
                                                  assign_op)
            view_assign = cppast.AssignOperator([subscript], target_ref,
                                                assign_op)

            return cppast.CompoundStmt([call, target_assign, view_assign])

        return super().visit_Assign(node)