diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 55ee6c05bb4b47c30483d4d55a96b19970edec5e..e76d508effee9ce57f18dbf4c46683879f110f06 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -88,6 +88,9 @@ jobs:
             env:
                 DOWNSTREAM_PROJECT: ${{ matrix.downstream_project }}
             run: |
+                curl -L -O -k https://tiker.net/ci-support-v0
+                . ./ci-support-v0
+
                 if test "$DOWNSTREAM_PROJECT" = "mirgecom"; then
                     git clone "https://github.com/illinois-ceesd/$DOWNSTREAM_PROJECT.git"
                 else
@@ -95,7 +98,10 @@ jobs:
                 fi
                 cd "$DOWNSTREAM_PROJECT"
                 echo "*** $DOWNSTREAM_PROJECT version: $(git rev-parse --short HEAD)"
+
+                transfer_requirements_git_urls ../requirements.txt ./requirements.txt
                 sed -i "/egg=grudge/ c git+file://$(readlink -f ..)#egg=grudge" requirements.txt
+
                 # Avoid slow or complicated tests in downstream projects
                 export PYTEST_ADDOPTS="-k 'not (slowtest or octave or mpi)'"
                 if test "$DOWNSTREAM_PROJECT" = "mirgecom"; then
@@ -107,9 +113,6 @@ jobs:
                 else
                     sed -i "/mpi4py/ d" requirements.txt
                 fi
-
-                curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/master/ci-support.sh
-                . ./ci-support.sh
                 build_py_project_in_conda_env
                 test_py_project
 
diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py
index f5f7c66499b2adeadc470b9be416a06d5074048a..fd6b9c78979f6f0b0d8e7fb55c77a6dcecd1b8d8 100644
--- a/grudge/symbolic/compiler.py
+++ b/grudge/symbolic/compiler.py
@@ -33,6 +33,7 @@ from pymbolic.primitives import Variable, Subscript
 from sys import intern
 from functools import reduce
 
+from loopy import ScalarCallable
 from loopy.version import LOOPY_USE_LANGUAGE_VERSION_2018_2  # noqa: F401
 
 
@@ -83,7 +84,7 @@ class LoopyKernelDescriptor:
     @memoize_method
     def scalar_args(self):
         import loopy as lp
-        return [arg.name for arg in self.loopy_kernel.args
+        return [arg.name for arg in self.loopy_kernel.default_entrypoint.args
                 if isinstance(arg, lp.ValueArg)
                 and arg.name not in ["nelements", "nunit_dofs"]]
 
@@ -923,53 +924,48 @@ class ToLoopyExpressionMapper(mappers.IdentityMapper):
 
 # {{{ bessel handling
 
-BESSEL_PREAMBLE = """//CL//
-#include <pyopencl-bessel-j.cl>
-#include <pyopencl-bessel-y.cl>
-"""
-
-
-def bessel_preamble_generator(preamble_info):
-    from loopy.target.pyopencl import PyOpenCLTarget
-    if not isinstance(preamble_info.kernel.target, PyOpenCLTarget):
-        raise NotImplementedError("Only the PyOpenCLTarget is supported as of now")
-
-    if any(func.name in ["bessel_j", "bessel_y"]
-            for func in preamble_info.seen_functions):
-        yield ("50-grudge-bessel", BESSEL_PREAMBLE)
-
-
-def bessel_function_mangler(kernel, name, arg_dtypes):
-    from loopy.types import NumpyType
-    if name == "bessel_j" and len(arg_dtypes) == 2:
-        n_dtype, x_dtype, = arg_dtypes
+class BesselFunction(ScalarCallable):
+    def with_types(self, arg_id_to_dtype, clbl_inf_ctx):
+        from loopy.types import NumpyType
 
-        # *technically* takes a float, but let's not worry about that.
-        if n_dtype.numpy_dtype.kind != "i":
-            raise TypeError("%s expects an integer first argument")
+        for i in arg_id_to_dtype:
+            if not (-1 <= i <= 1):
+                raise TypeError(f"{self.name} can only take 2 arguments.")
 
-        from loopy.kernel.data import CallMangleInfo
-        return CallMangleInfo(
-                "bessel_jv",
-                (NumpyType(np.float64),),
-                (NumpyType(np.int32), NumpyType(np.float64)),
-                )
+        if (arg_id_to_dtype.get(0) is None) or (arg_id_to_dtype.get(1) is None):
+            # not enough info about input types
+            return self, clbl_inf_ctx
 
-    elif name == "bessel_y" and len(arg_dtypes) == 2:
-        n_dtype, x_dtype, = arg_dtypes
+        n_dtype = arg_id_to_dtype[0]
 
         # *technically* takes a float, but let's not worry about that.
         if n_dtype.numpy_dtype.kind != "i":
-            raise TypeError("%s expects an integer first argument")
+            raise TypeError(f"{self.name} expects an integer first argument")
 
-        from loopy.kernel.data import CallMangleInfo
-        return CallMangleInfo(
-                "bessel_yn",
-                (NumpyType(np.float64),),
-                (NumpyType(np.int32), NumpyType(np.float64)),
-                )
-
-    return None
+        if self.name == "bessel_j":
+            name_in_target = "bessel_jv"
+        else:
+            assert self.name == "bessel_y"
+            name_in_target = "bessel_yn"
+
+        return (self.copy(name_in_target=name_in_target,
+                          arg_id_to_dtype={-1: NumpyType(np.float64),
+                                           0: NumpyType(np.int32),
+                                           1: NumpyType(np.float64),
+                                           }),
+                clbl_inf_ctx)
+
+    def generate_preambles(self, target):
+        from loopy import PyOpenCLTarget
+        if not isinstance(target, PyOpenCLTarget):
+            raise NotImplementedError("Only the PyOpenCLTarget is supported as"
+                                      "of now.")
+
+        yield ("50-grudge-bessel", """//CL//
+                                   #include <pyopencl-bessel-j.cl>
+                                   #include <pyopencl-bessel-y.cl>
+                                   """)
+        return
 
 # }}}
 
@@ -1046,10 +1042,8 @@ class ToLoopyInstructionMapper:
                 self.dd_inference_mapper(expr)
                 for expr in insn.exprs)
 
-        knl = lp.register_preamble_generators(knl,
-                [bessel_preamble_generator])
-        knl = lp.register_function_manglers(knl,
-                [bessel_function_mangler])
+        knl = lp.register_callable(knl, "bessel_j", BesselFunction("bessel_j"))
+        knl = lp.register_callable(knl, "bessel_y", BesselFunction("bessel_y"))
 
         input_mappings = {}
         output_mappings = {}