Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions examples/dl-activations/relu3.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ def vrelu3_embedded_ks_checkpointed_map():


embedded_cpp_entry_points = """
namespace ks {
ks::tensor<1, ks::Float> entry(ks::allocator * $alloc, ks::tensor<1, ks::Float> t) {
return ks::vrelu3($alloc, t);
}
ks::tensor<1, ks::Float> entry_vjp(ks::allocator * $alloc, ks::tensor<1, ks::Float> t, ks::tensor<1, ks::Float> dret) {
return ks::sufrev_vrelu3($alloc, t, dret);
#include "knossos-entry-points.h"

ks::tensor<1, ks::Float> entry(ks::tensor<1, ks::Float> t) {
return ks::vrelu3(&ks::entry_points::g_alloc, t);
}

ks::tensor<1, ks::Float> entry_vjp(ks::tensor<1, ks::Float> t, ks::tensor<1, ks::Float> dret) {
return ks::sufrev_vrelu3(&ks::entry_points::g_alloc, t, dret);
}
"""

Expand Down
82 changes: 82 additions & 0 deletions src/python/ksc/cgen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from ksc import utils


scalar_type_to_cpp_map = {
"Integer": "ks::Integer",
"Float": "ks::Float",
"Bool": "ks::Bool",
"String": "std::string",
}


def ks_cpp_type(t):
if t.is_scalar:
return scalar_type_to_cpp_map[t.kind]
elif t.is_tuple:
return (
"ks::Tuple<"
+ ", ".join(ks_cpp_type(child) for child in t.tuple_elems())
+ ">"
)
elif t.is_tensor:
return f"ks::tensor<{t.tensor_rank}, {ks_cpp_type(t.tensor_elem_type)}>"
else:
raise ValueError(f'Unable to generate C++ type for "{t}"')
Comment on lines +4 to +24

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

N.B. Overlaps with Cgen



def generate_cpp_entry_points(bindings_to_generate, decls):
decls_by_name = {decl.name: decl for decl in decls}

def lookup_decl(structured_name):
if structured_name not in decls_by_name:
raise ValueError(f"No ks definition found for binding: {structured_name}")
return decls_by_name[structured_name]

cpp_entry_points = "".join(
generate_cpp_entry_point(binding_name, lookup_decl(structured_name))
for binding_name, structured_name in bindings_to_generate
)

return f"""
#include "knossos-entry-points.h"

namespace ks {{
namespace entry_points {{
namespace generated {{
{cpp_entry_points}
}}
}}
}}
"""


def generate_cpp_entry_point(cpp_function_name, decl):
return_type = decl.return_type
arg_types = [arg.type_ for arg in decl.args]
if len(arg_types) == 1 and arg_types[0].is_tuple:
arg_types = arg_types[0].children
arg_names = [f"arg{i}" for i in range(len(arg_types))]

args = ", ".join(
f"{ks_cpp_type(arg_type)} {arg_name}"
for arg_type, arg_name in zip(arg_types, arg_names)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm always uneasy with zip because I wonder whether trailing elements are being silently discarded. Perhaps the following instead?

arg_types_names = ((arg.type_, f"arg{i}") for (i, arg) in enumerate(decl.args))
...
    for arg_type, arg_name in arg_types_names

(Even inlining arg_types_names would probably be fine.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this but need to do something about the other uses of arg_names in this function. I've pushed a change with one way of doing this - what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that version looks good. I think if I were doing it I would do

arg_types_names = [(arg.type_, f"arg{i}") for (i, arg) in enumerate(decl.args)]
...
    for arg_type, arg_name in arg_types_names
...
arg_names = [arg_name for (_, arg_name) in arg_types_names]

but that's not necessarily to everyone's taste.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the first line has to be arg_types = ... because we need to do de-oneargification as well. (I probably should write the function multiargify rather than inlining it here, but I don't think that solves the problem.) I tried enumerate(multiargify(...)) but that seemed to be doing too much in one line of code. Anyway, it's looking a bit better now - happy to come back to this but I'll merge here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, fine by me

)
cpp_declaration = f"{ks_cpp_type(return_type)} {cpp_function_name}({args})"

ks_function_name = utils.encode_name(decl.name.mangled())
arg_list = ", ".join(["&g_alloc"] + arg_names)
cpp_call = f"ks::{ks_function_name}({arg_list})"
args_streamed = ' << ", " '.join(f" << {arg}" for arg in arg_names)

return f"""
{cpp_declaration} {{
if (g_logging) {{
std::cerr << "{ks_function_name}("{args_streamed} << ") =" << std::endl;
auto ret = {cpp_call};
std::cerr << ret << std::endl;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how useful this is if ret is a size one million tensor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, maybe this code is past its sell-by date: it's originally from #762, when we needed to debug issues to do with python object lifetimes. So, it was intended for debugging rather than routine logging. Though we're making changes in this area at the moment so maybe it's worth keeping a little while longer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, fine by me.

return ret;
}} else {{
return {cpp_call};
}}
}}
"""
19 changes: 11 additions & 8 deletions src/python/ksc/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from torch.utils import cpp_extension

from ksc import utils
from ksc import cgen, utils
from ksc.parse_ks import parse_ks_filename

preserve_temporary_files = False

Expand Down Expand Up @@ -52,6 +53,7 @@ def generate_cpp_from_ks(ks_str, use_aten=False):
e = subprocess.run(ksc_command, capture_output=True, check=True,)
print(e.stdout.decode("ascii"))
print(e.stderr.decode("ascii"))
decls = list(parse_ks_filename(fkso.name))
except subprocess.CalledProcessError as e:
print(f"Command failed:\n{' '.join(ksc_command)}")
print(f"files {fks.name} {fkso.name} {fcpp.name}")
Expand All @@ -62,7 +64,7 @@ def generate_cpp_from_ks(ks_str, use_aten=False):

# Read from CPP back to string
with open(fcpp.name) as f:
out = f.read()
generated_cpp = f.read()

# only delete these file if no error
if not preserve_temporary_files:
Expand All @@ -79,7 +81,7 @@ def _():
os.unlink(fcpp.name)
os.unlink(fkso.name)

return out
return generated_cpp, decls


def build_py_module_from_cpp(cpp_str, profiling=False, use_aten=False):
Expand Down Expand Up @@ -156,22 +158,23 @@ def mangled_with_type(structured_name):
return structured_name.mangled()

bindings = [
(python_name, utils.encode_name(mangled_with_type(structured_name)))
for (python_name, structured_name) in bindings_to_generate
(python_name, "ks::entry_points::generated::" + python_name)
for (python_name, _) in bindings_to_generate
]

cpp_ks_functions = generate_cpp_from_ks(ks_str, use_aten=use_aten)
cpp_ks_functions, decls = generate_cpp_from_ks(ks_str, use_aten=use_aten)
cpp_entry_points = cgen.generate_cpp_entry_points(bindings_to_generate, decls)
cpp_pybind_module_declaration = generate_cpp_pybind_module_declaration(
bindings, python_module_name
)

return cpp_ks_functions + cpp_pybind_module_declaration
return cpp_ks_functions + cpp_entry_points + cpp_pybind_module_declaration


def generate_cpp_pybind_module_declaration(bindings_to_generate, python_module_name):
def m_def(python_name, cpp_name):
return f"""
m.def("{python_name}", ks::entry_points::with_ks_allocator("{cpp_name}", &ks::{cpp_name}));
m.def("{python_name}", &{cpp_name});
"""

return (
Expand Down