nx_compute_node.nx
buildroot/runtime/nx_compute_node.nx
about
nx_compute_node.nx -- typed compute node, foundation of the
ComfyUI-replacement DAG.
ComfyUI's node is a Python class with INPUT_TYPES / RETURN_TYPES +
a `def execute(self, **kwargs)`. Stringly-typed everywhere; the
type system only catches mismatches at runtime; the workflow JSON
is opaque; failures have no audit trail.
Our compute node is:
* **typed** -- NodeKind sealed enum + OpCode sealed enum + typed
input/output ports. Type mismatches caught at graph-build time.
* **content-addressed** -- each node carries a 32-byte SHA-256
hash of (op_code, params, input hashes). Identical sub-graphs
produce identical hashes -> deterministic caching.
* **audit-able** -- every node carries a stable id; the runner
emits a span per execution (compose with nx_trace_emit).
* **flat-array layout** -- inputs/params live in i64 buffers per
the substrate convention; no Python dict-of-anything.
This module ships the NODE primitive only. nx_compute_graph adds
the DAG container + topo sort. The runner (executes a graph
against tensor inputs) lands once we have real kernels.
genealogy_id: comfyui_node_pattern + mlir_op + tvm_relay +
jax_jaxpr + onnx_node + tensorflow_xla_hlo
lineage_id: substrate_compute_node_v1
dependencies 4 imports · 4 importers
imports: nx_syscalls.nxnx_tier.nxnx_tensor.nxnx_sha256.nx
imported by: nx_compute_graph.nxnx_compute_graph_test.nxnx_compute_runner.nxnx_compute_runner_test.nx
structs
| 139 | struct ComputeNode |
consts
| 44 | const NX_CN_NODE_CONST: nx_int = 0 // pre-loaded tensor (weight blob) |
| 45 | const NX_CN_NODE_INPUT: nx_int = 1 // graph-level input port |
| 46 | const NX_CN_NODE_OUTPUT: nx_int = 2 // graph-level output port |
| 47 | const NX_CN_NODE_TENSOR_OP: nx_int = 3 // elementwise / reduction / shape |
| 48 | const NX_CN_NODE_KERNEL: nx_int = 4 // heavy op dispatched to a kernel |
| 49 | const NX_CN_NODE_CONTROL: nx_int = 5 // sub-graph / scheduler hook |
| 50 | const NX_CN_NODE_N_KINDS: nx_int = 6 |
| 64 | const NX_CN_OP_ADD: nx_int = 0 |
| 65 | const NX_CN_OP_SUB: nx_int = 1 |
| 66 | const NX_CN_OP_MUL: nx_int = 2 |
| 67 | const NX_CN_OP_DIV: nx_int = 3 |
| 68 | const NX_CN_OP_NEG: nx_int = 4 |
| 69 | const NX_CN_OP_RELU: nx_int = 5 |
| 70 | const NX_CN_OP_GELU: nx_int = 6 |
| 71 | const NX_CN_OP_SILU: nx_int = 7 |
| 72 | const NX_CN_OP_SWISH: nx_int = 8 |
| 73 | const NX_CN_OP_SOFTMAX: nx_int = 9 |
| 74 | const NX_CN_OP_SIGMOID: nx_int = 10 |
| 75 | const NX_CN_OP_TANH: nx_int = 11 |
| 76 | const NX_CN_OP_RESHAPE: nx_int = 12 |
| 77 | const NX_CN_OP_PERMUTE: nx_int = 13 |
| 78 | const NX_CN_OP_SLICE: nx_int = 14 |
| 79 | const NX_CN_OP_CONCAT: nx_int = 15 |
| 80 | const NX_CN_OP_SPLIT: nx_int = 16 |
| 81 | const NX_CN_OP_SUM_REDUCE: nx_int = 17 |
| 82 | const NX_CN_OP_MEAN_REDUCE: nx_int = 18 |
| 83 | const NX_CN_OP_MAX_REDUCE: nx_int = 19 |
| 84 | const NX_CN_OP_MIN_REDUCE: nx_int = 20 |
| 85 | const NX_CN_OP_TRANSPOSE: nx_int = 21 |
| 86 | const NX_CN_OP_VIEW: nx_int = 22 |
| 87 | const NX_CN_OP_N_OPS: nx_int = 23 |
| 100 | const NX_CN_K_MATMUL: nx_int = 0 // standard matmul (gemm) |
| 101 | const NX_CN_K_GEMM: nx_int = 1 // alpha*A@B + beta*C |
| 102 | const NX_CN_K_DOT: nx_int = 2 // 1-D dot product |
| 103 | const NX_CN_K_CONV2D: nx_int = 3 |
| 104 | const NX_CN_K_CONV2D_DEPTHWISE: nx_int = 4 |
| 105 | const NX_CN_K_CONV2D_TRANSPOSE: nx_int = 5 |
| 106 | const NX_CN_K_LAYERNORM: nx_int = 6 |
| 107 | const NX_CN_K_RMSNORM: nx_int = 7 |
| 108 | const NX_CN_K_GROUPNORM: nx_int = 8 |
| 109 | const NX_CN_K_BATCHNORM: nx_int = 9 |
| 110 | const NX_CN_K_ATTENTION_QKV: nx_int = 10 // self-attention |
| 111 | const NX_CN_K_ATTENTION_CROSS: nx_int = 11 // cross-attention |
| 112 | const NX_CN_K_EMBEDDING_LOOKUP: nx_int = 12 |
| 113 | const NX_CN_K_ROTARY_EMBED: nx_int = 13 // RoPE |
| 114 | const NX_CN_K_FFT: nx_int = 14 |
| 115 | const NX_CN_K_IFFT: nx_int = 15 |
| 116 | const NX_CN_K_N_KINDS: nx_int = 16 |
| 134 | const NX_CN_MAX_INPUTS: nx_int = 8 // covers attention's Q+K+V+mask+... |
| 135 | const NX_CN_MAX_OUTPUTS: nx_int = 4 // most ops are 1; split takes a few |
| 136 | const NX_CN_MAX_PARAMS: nx_int = 16 // axis, scale, dim, etc. |
| 137 | const NX_CN_HASH_BYTES: nx_int = 32 // SHA-256 |
| 158 | const NX_CN_BYTES: nx_int = 88 // 11 fields * 8 |
| 273 | const NX_CN_HASH_HDR_BYTES: nx_int = 24 // kind + op + n_inputs + n_params (each 8 bytes? or 4?) |
functions
| 52 | func nx_cn_node_kind_is_valid(k: nx_int) -> nx_int |
| 89 | func nx_cn_op_is_valid(o: nx_int) -> nx_int |
| 118 | func nx_cn_kernel_is_valid(k: nx_int) -> nx_int |
| 167 | func nx_cn_alloc(node_id: nx_int, kind: nx_int, op_code: nx_int, |
| 204 | func nx_cn_set_input(n: *ComputeNode, input_slot: nx_int, |
| 216 | func nx_cn_set_param(n: *ComputeNode, slot: nx_int, value: nx_int) -> nx_int called by 1: main |
| 229 | func nx_cn_set_output_tensor_id(n: *ComputeNode, port: nx_int, |
| 239 | func nx_cn_get_input_src_node(n: *ComputeNode, slot: nx_int) -> nx_int |
| 245 | func nx_cn_get_input_src_port(n: *ComputeNode, slot: nx_int) -> nx_int |
| 251 | func nx_cn_get_param(n: *ComputeNode, slot: nx_int) -> nx_int |
| 257 | func nx_cn_get_output_tensor_id(n: *ComputeNode, port: nx_int) -> nx_int |
| 277 | func _cn_write_i64_le(buf: *u8, off: nx_int, value: nx_int) -> nx_int called by 1: nx_cn_compute_hash |
| 291 | func nx_cn_compute_hash(n: *ComputeNode) -> nx_int |