Skip to content

Commit 2d28feb

Browse files
committed
Add a dumb bump allocator for code objects
1 parent 483729f commit 2d28feb

File tree

1 file changed

+74
-3
lines changed

1 file changed

+74
-3
lines changed

Objects/codeobject.c

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "clinic/codeobject.c.h"
1111

1212
#include <stdbool.h>
13+
#include <sys/mman.h>
1314

1415
static PyObject* code_repr(PyCodeObject *co);
1516

@@ -543,6 +544,73 @@ remove_column_info(PyObject *locations)
543544
return res;
544545
}
545546

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

548616
PyCodeObject *
@@ -580,13 +648,15 @@ _PyCode_New(struct _PyCodeConstructor *con)
580648
con->linetable = replacement_locations;
581649
}
582650

583-
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
584-
PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
651+
Py_ssize_t nitems = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
652+
Py_ssize_t size = _PyObject_VAR_SIZE(&PyCode_Type, nitems);
653+
PyCodeObject *co = (PyCodeObject *)code_bump_allocate(size);
585654
if (co == NULL) {
586655
Py_XDECREF(replacement_locations);
587656
PyErr_NoMemory();
588657
return NULL;
589658
}
659+
PyObject_InitVar(co, &PyCode_Type, nitems);
590660
init_code(co, con);
591661
Py_XDECREF(replacement_locations);
592662
return co;
@@ -1744,7 +1814,8 @@ code_dealloc(PyCodeObject *co)
17441814
PyObject_ClearWeakRefs((PyObject*)co);
17451815
}
17461816
free_monitoring_data(co->_co_monitoring);
1747-
PyObject_Free(co);
1817+
1818+
// Data not actually freed as it comes from the the bump allocator.
17481819
}
17491820

17501821
static PyObject *

0 commit comments

Comments
 (0)