Skip to content

Commit a61d5cf

Browse files
committed
Add a dumb bump allocator for code objects
1 parent 126acc1 commit a61d5cf

File tree

1 file changed

+75
-3
lines changed

1 file changed

+75
-3
lines changed

Objects/codeobject.c

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "pycore_tuple.h" // _PyTuple_ITEMS()
1212
#include "clinic/codeobject.c.h"
1313

14+
#include <sys/mman.h>
15+
1416
static PyObject* code_repr(PyCodeObject *co);
1517

1618
static const char *
@@ -543,6 +545,73 @@ remove_column_info(PyObject *locations)
543545
return res;
544546
}
545547

548+
#define BUMP_CHUNK_SIZE (1024 * 1024 * 1024) // 1GiB, must be a power of 2
549+
550+
// Process-global state for the bump allocator
551+
static struct {
552+
char *current_ptr; // Current allocation position
553+
char *end_ptr; // End of current memory region
554+
} code_allocator_state = {NULL, NULL};
555+
556+
// Extend the bump allocator's memory region
557+
static int
558+
extend_code_bump_allocator(Py_ssize_t min_size)
559+
{
560+
// Calculate how much to allocate (at least BUMP_CHUNK_SIZE, rounded up)
561+
Py_ssize_t size = (min_size + BUMP_CHUNK_SIZE - 1) & ~(BUMP_CHUNK_SIZE - 1);
562+
563+
// Try to extend the existing region
564+
void *ptr = mmap(code_allocator_state.end_ptr, size,
565+
PROT_READ | PROT_WRITE,
566+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
567+
568+
if (ptr == MAP_FAILED) {
569+
// If MAP_FIXED fails, try without it
570+
ptr = mmap(NULL, size,
571+
PROT_READ | PROT_WRITE,
572+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
573+
574+
if (ptr == MAP_FAILED) {
575+
return -1;
576+
}
577+
578+
// We got memory but not contiguous
579+
code_allocator_state.current_ptr = ptr;
580+
code_allocator_state.end_ptr = ptr + size;
581+
} else {
582+
if (code_allocator_state.current_ptr == NULL) {
583+
code_allocator_state.current_ptr = ptr;
584+
code_allocator_state.end_ptr = ptr + size;
585+
} else {
586+
code_allocator_state.end_ptr += size;
587+
}
588+
}
589+
590+
return 0;
591+
}
592+
593+
// Allocate memory from the bump allocator
594+
static void*
595+
code_bump_allocate(Py_ssize_t n_bytes)
596+
{
597+
// Align the allocation to 8 bytes
598+
n_bytes = (n_bytes + 7) & ~7;
599+
600+
// Check if we have enough space
601+
if (code_allocator_state.current_ptr + n_bytes > code_allocator_state.end_ptr) {
602+
// Need to extend
603+
if (extend_code_bump_allocator(n_bytes) < 0) {
604+
return NULL;
605+
}
606+
}
607+
608+
// Allocate from the bump pointer
609+
void *result = code_allocator_state.current_ptr;
610+
code_allocator_state.current_ptr += n_bytes;
611+
612+
return result;
613+
}
614+
546615
/* The caller is responsible for ensuring that the given data is valid. */
547616

548617
PyCodeObject *
@@ -580,13 +649,15 @@ _PyCode_New(struct _PyCodeConstructor *con)
580649
con->linetable = replacement_locations;
581650
}
582651

583-
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
584-
PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
652+
Py_ssize_t nitems = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
653+
Py_ssize_t size = _PyObject_VAR_SIZE(&PyCode_Type, nitems);
654+
PyCodeObject *co = (PyCodeObject *)code_bump_allocate(size);
585655
if (co == NULL) {
586656
Py_XDECREF(replacement_locations);
587657
PyErr_NoMemory();
588658
return NULL;
589659
}
660+
PyObject_InitVar(co, &PyCode_Type, nitems);
590661
init_code(co, con);
591662
Py_XDECREF(replacement_locations);
592663
return co;
@@ -1744,7 +1815,8 @@ code_dealloc(PyCodeObject *co)
17441815
PyObject_ClearWeakRefs((PyObject*)co);
17451816
}
17461817
free_monitoring_data(co->_co_monitoring);
1747-
PyObject_Free(co);
1818+
1819+
// Data not actually freed as it comes from the the bump allocator.
17481820
}
17491821

17501822
static PyObject *

0 commit comments

Comments
 (0)