Skip to content

Commit 20dd8dc

Browse files
committed
[lang] Direct LLVM/NVVM IR builder.
Signed-off-by: Greg Bonik <gbonik@nvidia.com>
1 parent 9ef7352 commit 20dd8dc

22 files changed

Lines changed: 1793 additions & 64 deletions

‎CMakeLists.txt‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,20 @@ endif()
9494
include(cmake/FetchXLAHeaders.cmake)
9595
fetch_xla_headers()
9696

97+
98+
set(CUSTOM_STATIC_LIBNVVM "" CACHE FILEPATH "Custom NVVM static library to include in the package")
99+
if (CUSTOM_STATIC_LIBNVVM)
100+
message(STATUS "Using custom NVVM static library at ${CUSTOM_STATIC_LIBNVVM}")
101+
endif()
102+
103+
set(CUSTOM_PTXAS "" CACHE FILEPATH "Custom ptxas binary to include in the package")
104+
if (CUSTOM_PTXAS)
105+
message(STATUS "Using custom ptxas binary at ${CUSTOM_PTXAS}")
106+
endif()
107+
108+
97109
add_subdirectory(cext)
110+
add_subdirectory(experimental/cuda-lang/)
98111

99112

100113
option(DISABLE_INTERNAL "disable internal" OFF)

‎cext/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,28 @@ else()
3636
endif()
3737

3838

39+
set(NVVM_INCLUDE_DIR ${CUDAToolkit_NVVM_INCLUDE_DIR})
40+
3941
set(cext_include_dirs
4042
${dlpack_INCLUDE_DIR}
4143
${xla_INCLUDE_DIR}
4244
${Python_INCLUDE_DIRS}
4345
${CUDAToolkit_INCLUDE_DIRS}
46+
${NVVM_INCLUDE_DIR}
4447
)
4548

4649

4750
# Build a static library first, so that we could reuse it for several build targets
4851

4952
add_library(_cext_static STATIC
53+
bitstream.cpp
5054
coroutine_util.cpp
5155
cuda_loader.cpp
5256
cuda_helper.cpp
5357
ipc_util.cpp
5458
memory.cpp
5559
py.cpp
60+
pynvvm.cpp
5661
stream_buffer.cpp
5762
tile_kernel.cpp
5863
xla_ffi.cpp

‎cext/bitstream.cpp‎

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// SPDX-FileCopyrightText: Copyright (c) <2026> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "bitstream.h"
6+
7+
#include "vec.h"
8+
9+
namespace { struct BitstreamWriter {
10+
Vec<uint32_t> words;
11+
uint64_t buf = 0;
12+
unsigned buf_size = 0; // Number of bits in `buf`, always <= 63
13+
14+
static PyTypeObject pytype;
15+
}; }
16+
17+
static void append_fixed(BitstreamWriter* w, uint64_t bits, unsigned width) {
18+
unsigned buf_size = w->buf_size;
19+
uint64_t buf = w->buf | (bits << buf_size);
20+
buf_size += width;
21+
if (buf_size >= 64) {
22+
w->words.push_back(static_cast<uint32_t>(buf));
23+
w->words.push_back(static_cast<uint32_t>(buf >> 32));
24+
buf_size -= 64;
25+
buf = bits >> (width - buf_size);
26+
}
27+
w->buf = buf;
28+
w->buf_size = buf_size;
29+
}
30+
31+
static void append_vbr(BitstreamWriter* w, uint64_t bits, unsigned width) {
32+
while (true) {
33+
uint64_t next = bits >> (width - 1);
34+
if (!next) break;
35+
uint64_t high_bit = (uint64_t(1) << (width - 1));
36+
append_fixed(w, (bits & (high_bit - 1)) | high_bit, width);
37+
bits = next;
38+
}
39+
append_fixed(w, bits, width);
40+
}
41+
42+
static PyObject* py_append_fixed(PyObject* self, PyObject* py_bits, unsigned width) {
43+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
44+
uint64_t bits = pylong_as<uint64_t>(py_bits);
45+
if (PyErr_Occurred()) return nullptr;
46+
if (bits >> width) {
47+
raise(PyExc_OverflowError, "Value ", bits, " is too big for a ",
48+
width, "-bit unsigned integer");
49+
return nullptr;
50+
}
51+
append_fixed(&w, bits, width);
52+
return Py_NewRef(Py_None);
53+
}
54+
55+
static PyObject* py_append_vbr(PyObject* self, PyObject* py_bits, unsigned width) {
56+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
57+
uint64_t bits = pylong_as<uint64_t>(py_bits);
58+
if (PyErr_Occurred()) return nullptr;
59+
append_vbr(&w, bits, width);
60+
return Py_NewRef(Py_None);
61+
}
62+
63+
static PyObject* BitstreamWriter_fixed1(PyObject* self, PyObject* arg) {
64+
return py_append_fixed(self, arg, 1);
65+
}
66+
67+
static PyObject* BitstreamWriter_fixed2(PyObject* self, PyObject* arg) {
68+
return py_append_fixed(self, arg, 2);
69+
}
70+
71+
static PyObject* BitstreamWriter_fixed3(PyObject* self, PyObject* arg) {
72+
return py_append_fixed(self, arg, 3);
73+
}
74+
75+
static PyObject* BitstreamWriter_fixed4(PyObject* self, PyObject* arg) {
76+
return py_append_fixed(self, arg, 4);
77+
}
78+
79+
static PyObject* BitstreamWriter_fixed8(PyObject* self, PyObject* arg) {
80+
return py_append_fixed(self, arg, 8);
81+
}
82+
83+
static PyObject* BitstreamWriter_fixed32(PyObject* self, PyObject* arg) {
84+
return py_append_fixed(self, arg, 32);
85+
}
86+
87+
static PyObject* BitstreamWriter_vbr4(PyObject* self, PyObject* arg) {
88+
return py_append_vbr(self, arg, 4);
89+
}
90+
91+
static PyObject* BitstreamWriter_vbr5(PyObject* self, PyObject* arg) {
92+
return py_append_vbr(self, arg, 5);
93+
}
94+
95+
static PyObject* BitstreamWriter_vbr6(PyObject* self, PyObject* arg) {
96+
return py_append_vbr(self, arg, 6);
97+
}
98+
99+
static PyObject* BitstreamWriter_vbr8(PyObject* self, PyObject* arg) {
100+
return py_append_vbr(self, arg, 8);
101+
}
102+
103+
static void align32(BitstreamWriter* w) {
104+
if (w->buf_size) {
105+
w->words.push_back(static_cast<uint32_t>(w->buf));
106+
if (w->buf_size > 32)
107+
w->words.push_back(static_cast<uint32_t>(w->buf >> 32));
108+
w->buf_size = 0;
109+
w->buf = 0;
110+
}
111+
}
112+
113+
static PyObject* BitstreamWriter_align_to_word(PyObject* self, PyObject* dummy) {
114+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
115+
align32(&w);
116+
return PyLong_FromSize_t(w.words.size());
117+
}
118+
119+
static PyObject* BitstreamWriter_aligned_word(PyObject* self, PyObject* arg) {
120+
uint32_t value = pylong_as<uint32_t>(arg);
121+
if (PyErr_Occurred()) return nullptr;
122+
123+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
124+
align32(&w);
125+
size_t pos = w.words.size();
126+
w.words.push_back(value);
127+
return PyLong_FromSize_t(pos);
128+
}
129+
130+
static PyObject* BitstreamWriter_patch_word(PyObject* self,
131+
PyObject* const* args, Py_ssize_t nargs) {
132+
if (nargs != 2) {
133+
raise(PyExc_TypeError, "patch_word(offset, word) expects exactly 2 arguments, got ", nargs);
134+
return nullptr;
135+
}
136+
size_t offset = pylong_as<size_t>(args[0]);
137+
uint32_t word = pylong_as<uint32_t>(args[1]);
138+
if (PyErr_Occurred())
139+
return nullptr;
140+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
141+
if (offset >= w.words.size()) {
142+
raise(PyExc_IndexError,
143+
"Offset ", offset, " is out of range for stream of ", w.words.size(), " words");
144+
return nullptr;
145+
}
146+
w.words[offset] = word;
147+
return Py_NewRef(Py_None);
148+
}
149+
150+
static PyObject* BitstreamWriter_raw_blob(PyObject* self, PyObject* blob) {
151+
if (!PyByteArray_Check(blob)) {
152+
raise(PyExc_TypeError, "Expected a bytearray argument");
153+
return nullptr;
154+
}
155+
156+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
157+
align32(&w);
158+
const char* data = PyByteArray_AS_STRING(blob);
159+
size_t size = PyByteArray_GET_SIZE(blob);
160+
size_t num_words = (size + 3) / 4;
161+
size_t pos = w.words.size();
162+
w.words.resize(w.words.size() + num_words);
163+
mem_copy(w.words.data() + pos, data, size);
164+
return Py_NewRef(Py_None);
165+
}
166+
167+
static PyObject* BitstreamWriter_to_bytes(PyObject* self, PyObject* dummy) {
168+
BitstreamWriter& w = py_unwrap<BitstreamWriter>(self);
169+
unsigned extra_words = (w.buf_size + 31) / 32;
170+
uint64_t buf = w.buf;
171+
for (unsigned i = 0; i < extra_words; ++i) {
172+
w.words.push_back(static_cast<uint32_t>(buf));
173+
buf >>= 32;
174+
}
175+
PyObject* ret = PyBytes_FromStringAndSize(
176+
reinterpret_cast<const char*>(w.words.data()), w.words.size() * 4);
177+
for (unsigned i = 0; i < extra_words; ++i)
178+
w.words.pop_back();
179+
return ret;
180+
}
181+
182+
static PyMethodDef BitstreamWriter_methods[] = {
183+
{"fixed1", BitstreamWriter_fixed1, METH_O, nullptr},
184+
{"fixed2", BitstreamWriter_fixed2, METH_O, nullptr},
185+
{"fixed3", BitstreamWriter_fixed3, METH_O, nullptr},
186+
{"fixed4", BitstreamWriter_fixed4, METH_O, nullptr},
187+
{"fixed8", BitstreamWriter_fixed8, METH_O, nullptr},
188+
{"fixed32", BitstreamWriter_fixed32, METH_O, nullptr},
189+
{"vbr4", BitstreamWriter_vbr4, METH_O, nullptr},
190+
{"vbr5", BitstreamWriter_vbr5, METH_O, nullptr},
191+
{"vbr6", BitstreamWriter_vbr6, METH_O, nullptr},
192+
{"vbr8", BitstreamWriter_vbr8, METH_O, nullptr},
193+
{"align_to_word", BitstreamWriter_align_to_word, METH_NOARGS, nullptr},
194+
{"aligned_word", BitstreamWriter_aligned_word, METH_O, nullptr},
195+
{"patch_word", reinterpret_cast<PyCFunction>(BitstreamWriter_patch_word),
196+
METH_FASTCALL, nullptr},
197+
{"raw_blob", BitstreamWriter_raw_blob, METH_O, nullptr},
198+
{"to_bytes", BitstreamWriter_to_bytes, METH_NOARGS, nullptr},
199+
{}
200+
};
201+
202+
PyTypeObject BitstreamWriter::pytype = {
203+
.tp_name = "cuda.tile._cext.BitstreamWriter",
204+
.tp_basicsize = sizeof(PythonWrapper<BitstreamWriter>),
205+
.tp_dealloc = pywrapper_dealloc<BitstreamWriter>,
206+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
207+
.tp_methods = BitstreamWriter_methods,
208+
.tp_new = pywrapper_new<BitstreamWriter>,
209+
};
210+
211+
212+
Status bitstream_init(PyObject* m) {
213+
if (PyType_Ready(&BitstreamWriter::pytype) < 0)
214+
return ErrorRaised;
215+
216+
if (PyModule_AddObjectRef(m, "BitstreamWriter",
217+
reinterpret_cast<PyObject*>(&BitstreamWriter::pytype)) < 0)
218+
return ErrorRaised;
219+
220+
return OK;
221+
}
222+
223+

‎cext/bitstream.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-FileCopyrightText: Copyright (c) <2026> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#pragma once
6+
7+
#include "py.h"
8+
9+
Status bitstream_init(PyObject* m);
10+

‎cext/module.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "cuda_helper.h"
99
#include "coroutine_util.h"
1010
#include "xla_ffi_py.h"
11+
#include "bitstream.h"
12+
#include "pynvvm.h"
1113

1214
#ifdef _WIN32
1315
extern "C" int _fltused = 0;
@@ -42,6 +44,11 @@ PyMODINIT_FUNC PyInit__cext() {
4244
if (!xla_ffi_init(m.get()))
4345
return nullptr;
4446

47+
if (!bitstream_init(m.get()))
48+
return nullptr;
49+
50+
if (!pynvvm_init(m.get()))
51+
return nullptr;
4552

4653
return m.release();
4754
}

0 commit comments

Comments
 (0)