From 01410750b1271f6058422ee62428217bd5abaa8f Mon Sep 17 00:00:00 2001 From: Kaushik Kulkarni Date: Thu, 22 Mar 2018 13:07:34 -0500 Subject: [PATCH] Added support for multiple assignment scalars. --- loopy/kernel/creation.py | 4 +- loopy/kernel/function_interface.py | 85 +++++++++++++++++++----------- loopy/target/c/__init__.py | 4 ++ loopy/transform/register_knl.py | 4 +- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index c0c8e73be..165607a05 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -1914,8 +1914,8 @@ def scope_functions(kernel): type(insn)) # Need to combine the scoped functions into a dict - from loopy.kernel.function_interface import InKernelCallable - scoped_function_dict = dict((func, InKernelCallable(func)) for func in + from loopy.kernel.function_interface import CallableOnScalar + scoped_function_dict = dict((func, CallableOnScalar(func)) for func in scoped_functions) return kernel.copy(instructions=new_insns, scoped_functions=scoped_function_dict) diff --git a/loopy/kernel/function_interface.py b/loopy/kernel/function_interface.py index e0c086eb8..bbd6e43cc 100644 --- a/loopy/kernel/function_interface.py +++ b/loopy/kernel/function_interface.py @@ -107,7 +107,6 @@ def get_kw_pos_association(kernel): # }}} - # {{{ template class @@ -141,10 +140,13 @@ class InKernelCallable(ImmutableRecord): # {{{ sanity checks if not isinstance(name, str): - raise LoopyError("name of a CallableOnScalar should be a string") + raise LoopyError("name of an InKernelCallable should be a string") # }}} + if name_in_target is not None and subkernel is not None: + subkernel = subkernel.copy(name=name_in_target) + super(InKernelCallable, self).__init__(name=name, subkernel=subkernel, arg_id_to_dtype=arg_id_to_dtype, @@ -246,15 +248,6 @@ class InKernelCallable(ImmutableRecord): class CallableOnScalar(InKernelCallable): - def __init__(self, name, arg_id_to_dtype=None, - arg_id_to_descr=None, name_in_target=None): - - super(CallableOnScalar, self).__init__(name=name, - subkernel=None, - arg_id_to_dtype=arg_id_to_dtype, - arg_id_to_descr=arg_id_to_descr, - name_in_target=name_in_target) - def with_types(self, arg_id_to_dtype, target): if self.arg_id_to_dtype is not None: @@ -335,34 +328,64 @@ class CallableOnScalar(InKernelCallable): # TODO: Need to add support for functions like sincos(x) # which would give multiple outputs but takes in scalar arguments - raise NotImplementedError("emit_call_insn only applies for" - " CallableKernels") + # FIXME: needs to get information about whether the callable has should + # do pass by reference by all values or should return one value for + # pass by value return. - # }}} + # For example: The code generation of `sincos` would be different for + # C-Target and OpenCL-target. - def __eq__(self, other): - return (self.name == other.name - and self.arg_id_to_descr == other.arg_id_to_descr - and self.arg_id_to_dtype == other.arg_id_to_dtype - and self.subkernel == other.subkernel) + # Currently doing pass by value for all the assignees. - def __hash__(self): - return hash((self.name, self.subkernel, self.name_in_target)) + assert self.is_ready_for_code_gen() + from loopy.kernel.instruction import CallInstruction -class CallableKernel(InKernelCallable): + assert isinstance(insn, CallInstruction) - def __init__(self, name, subkernel, arg_id_to_dtype=None, - arg_id_to_descr=None, name_in_target=None): + parameters = insn.expression.parameters + assignees = insn.assignees - if name_in_target is not None and subkernel is not None: - subkernel = subkernel.copy(name=name_in_target) + par_dtypes = tuple(expression_to_code_mapper.infer_type(par) for par in + parameters) + arg_dtypes = tuple(self.arg_id_to_dtype[i] for i, _ in + enumerate(parameters)) - super(CallableKernel, self).__init__(name=name, - subkernel=subkernel, - arg_id_to_dtype=arg_id_to_dtype, - arg_id_to_descr=arg_id_to_descr, - name_in_target=name_in_target) + assignee_dtypes = tuple(self.arg_id_to_dtype[-i-1] for i, _ in + enumerate(assignees)) + + from loopy.expression import dtype_to_type_context + from pymbolic.mapper.stringifier import PREC_NONE + from pymbolic import var + + c_parameters = [ + expression_to_code_mapper(par, PREC_NONE, + dtype_to_type_context(self.target, tgt_dtype), + tgt_dtype).expr + for par, par_dtype, tgt_dtype in zip( + parameters, par_dtypes, arg_dtypes)] + + for i, (a, tgt_dtype) in enumerate(zip(assignees, assignee_dtypes)): + if tgt_dtype != expression_to_code_mapper.infer_type(a): + raise LoopyError("Type Mismach in funciton %s. Expected: %s" + "Got: %s" % (self.name, tgt_dtype, + expression_to_code_mapper.infer_type(a))) + c_parameters.append( + var("&")( + expression_to_code_mapper(a, PREC_NONE, + dtype_to_type_context(self.target, tgt_dtype), + tgt_dtype).expr)) + + from pymbolic import var + return var(self.name_in_target)(*c_parameters) + + raise NotImplementedError("emit_call_insn only applies for" + " CallableKernels") + + # }}} + + +class CallableKernel(InKernelCallable): def with_types(self, arg_id_to_dtype, target): diff --git a/loopy/target/c/__init__.py b/loopy/target/c/__init__.py index 5ebcd67e1..2fb902830 100644 --- a/loopy/target/c/__init__.py +++ b/loopy/target/c/__init__.py @@ -953,6 +953,10 @@ class CASTBuilder(ASTBuilderBase): expression_to_code_mapper=ecm) from cgen import ExpressionStatement + # FIXME: Depending on the function this can be either an + # ExpressionStatement or Assignment. + # Refer: CallableOnScalar::emit_call_insn. It is discussed in detail + # over there. return ExpressionStatement( CExpression(self.get_c_expression_to_code_mapper(), in_knl_callable_as_call)) diff --git a/loopy/transform/register_knl.py b/loopy/transform/register_knl.py index f43550b5b..05a298d11 100644 --- a/loopy/transform/register_knl.py +++ b/loopy/transform/register_knl.py @@ -25,7 +25,7 @@ THE SOFTWARE. from loopy.kernel import LoopKernel from loopy.kernel.creation import FunctionScoper from loopy.diagnostic import LoopyError -from loopy.kernel.function_interface import InKernelCallable +from loopy.kernel.function_interface import CallableKernel from loopy.kernel.instruction import (MultiAssignmentBase, CallInstruction, CInstruction, _DataObliviousInstruction) @@ -97,7 +97,7 @@ def register_callable_kernel(parent, function_name, child): raise LoopyError("%s is already being used as a funciton name -- maybe" "use a different name for registering the subkernel") - scoped_functions[function_name] = InKernelCallable(name=function_name, + scoped_functions[function_name] = CallableKernel(name=function_name, subkernel=child) # returning the parent kernel with the new scoped function dictionary -- GitLab