aboutsummaryrefslogtreecommitdiffstats
path: root/core/module.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-18 22:33:19 +0100
committereunhhu <[email protected]>2026-10-18 22:33:19 +0100
commit1d255b735e92c2d6b360ed5ea150a1e42f6dd535 (patch)
tree211072355a181bc9ddfa20ecfcb5beda27021ffe /core/module.c
parenta72a1a7acdf6c5ecc4a6668f6b9a3af16ce48555 (diff)
downloadpure-1d255b735e92c2d6b360ed5ea150a1e42f6dd535.tar.gz
pure-1d255b735e92c2d6b360ed5ea150a1e42f6dd535.zip
027's 4f-1: a module's tables sized to it: PureModule's thirty-four tables of states, inputs, message fields, views (the columns the load makes among them, counted from the view and record entries first), handlers, records, record fields, functions and signature items point into one block the load lays out (lay_tables) and pure_module_release gives back, so PureModule is 2,752 bytes, not 24,320; a sanitized build poisons 16 bytes between tables, so reading past one is caught (none was); limits as they were; a test and five of research 044's timers gave the load a module never zeroed (it gives back what one holds), now zeroed; counts: b38 against b37 with batches within 0.25%, without batches matmul -0.45%, join +0.36%, knapsack +0.28%, BFS -0.25%; the bank's queries (s37 and s38 on one aup module in turns, three each) field -0.68%, found -0.51%, large -0.47%, the rest -0.11% to -0.20%; 027's 4f designed in four steps and 4f-1 as made
Diffstat (limited to 'core/module.c')
-rw-r--r--core/module.c73
1 files changed, 71 insertions, 2 deletions
diff --git a/core/module.c b/core/module.c
index e7118f0..f388ae7 100644
--- a/core/module.c
+++ b/core/module.c
@@ -1563,7 +1563,59 @@ PURE_COLD PureResult pure_module_read(const char *path, PureModule *out, char *e
return result;
}
+#ifdef __SANITIZE_ADDRESS__
+#include <sanitizer/asan_interface.h>
+enum { TABLE_GAP = 16 }; /* poisoned between tables: reading past one is caught */
+#else
+enum { TABLE_GAP = 0 };
+#endif
+
+/* The module's tables laid out from base (027's 4f-1), each a run of its entries aligned to 8; the bytes they take. */
+static size_t lay_tables(PureModule *m, uintptr_t base, size_t views, size_t signatures) {
+ size_t at = 0;
+#define LAY(member, count) \
+ (m->member = (void *)(base + at), at += (((size_t)(count) * sizeof(*m->member) + 7) & ~(size_t)7) + TABLE_GAP)
+ LAY(state_names, m->states), LAY(state_type, m->states), LAY(state_ref, m->states), LAY(state_flags, m->states);
+ LAY(state_key, m->states), LAY(state_indexed, m->states), LAY(initial, m->states);
+ LAY(input_names, m->inputs), LAY(input_keys, m->inputs), LAY(input, m->inputs), LAY(field, m->fields);
+ LAY(view_names, views), LAY(view_type, views), LAY(view_table, views), LAY(view_list, views), LAY(view_record, views);
+ LAY(view_field, views), LAY(view_computed, views), LAY(view_code, views), LAY(view_plan, views);
+ LAY(view_arguments, views), LAY(view_body, views), LAY(view_lists, views), LAY(view_range, views);
+ LAY(handler, m->handlers);
+ LAY(record_names, m->records), LAY(record, m->records), LAY(record_nested, m->records), LAY(record_held, m->records);
+ LAY(record_field_names, m->record_fields), LAY(record_field_type, m->record_fields);
+ LAY(record_field_default, m->record_fields);
+ LAY(function, m->functions), LAY(signature_type, signatures), LAY(signature_record, signatures);
+#undef LAY
+ return at;
+}
+
+static bool make_tables(PureModule *m, size_t views, size_t signatures) {
+ size_t size = lay_tables(m, 0, views, signatures);
+ unsigned char *base = calloc(1, size ? size : 1);
+ if (!base) return false;
+ m->tables = base;
+ lay_tables(m, (uintptr_t)base, views, signatures);
+#ifdef __SANITIZE_ADDRESS__
+ for (size_t at = 0; at + TABLE_GAP <= size;) { /* each run's gap, found again as it was laid out */
+ unsigned char *next = base + size;
+#define GAP(member) if ((unsigned char *)m->member > base + at && (unsigned char *)m->member < next) next = (unsigned char *)m->member;
+ GAP(state_type) GAP(state_ref) GAP(state_flags) GAP(state_key) GAP(state_indexed) GAP(initial) GAP(input_names)
+ GAP(input_keys) GAP(input) GAP(field) GAP(view_names) GAP(view_type) GAP(view_table) GAP(view_list) GAP(view_record)
+ GAP(view_field) GAP(view_computed) GAP(view_code) GAP(view_plan) GAP(view_arguments) GAP(view_body) GAP(view_lists)
+ GAP(view_range) GAP(handler) GAP(record_names) GAP(record) GAP(record_nested) GAP(record_held) GAP(record_field_names)
+ GAP(record_field_type) GAP(record_field_default) GAP(function) GAP(signature_type) GAP(signature_record)
+#undef GAP
+ ASAN_POISON_MEMORY_REGION(next - TABLE_GAP, TABLE_GAP);
+ at = (size_t)(next - base);
+ }
+#endif
+ return true;
+}
+
void pure_module_release(PureModule *m) {
+ free(m->tables);
+ m->tables = NULL;
pure_jit_free(m->jit);
m->jit = NULL;
m->batches = m->calls = false;
@@ -1664,6 +1716,23 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
out->constant_offset = (uint32_t *)(out->code + out->code_count);
out->constant_length = out->constant_offset + out->constants;
out->constant_data = (unsigned char *)(out->constant_length + out->constants);
+ /* Its tables, the columns the load makes among the views (027's 4e: a record's fields for each list view of a
+ * record), counted from the bytes first. */
+ size_t records_at = functions_at - (size_t)out->views * VIEW_BYTES - (size_t)out->handlers * HANDLER_BYTES -
+ (size_t)out->record_fields * RECORD_FIELD_BYTES - (size_t)out->records * RECORD_BYTES;
+ size_t made = 0, views_at = records_at + (size_t)out->records * RECORD_BYTES + (size_t)out->record_fields * RECORD_FIELD_BYTES;
+ for (uint32_t i = 0; i < out->views; ++i) {
+ const unsigned char *v = p + views_at + (size_t)i * VIEW_BYTES;
+ uint32_t record = read16(v + 8);
+ if (v[4] == PURE_VIEW_LIST && !read16(v + 6) && record && record <= out->records)
+ made += read16(p + records_at + (size_t)(record - 1) * RECORD_BYTES + 6);
+ }
+ if (out->views + made > PURE_MODULE_VIEWS) REFUSE("views and their columns", out->views);
+ size_t capacity = out->views + made;
+ if (!make_tables(out, capacity, signatures)) {
+ result = PURE_ALLOCATION;
+ REFUSE("out of memory", 0);
+ }
const unsigned char *at = p + PURE_MODULE_HEADER;
const unsigned char *text = p + size - constant_bytes - strings;
if (constant_bytes) memcpy(out->constant_data, p + size - constant_bytes, constant_bytes);
@@ -1879,7 +1948,7 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
if (!list_view(out, i) || !out->view_record[i]) continue;
const PureRecord *r = &out->record[out->view_record[i] - 1];
for (unsigned f = 0; f < r->count; ++f) {
- if (out->views == PURE_MODULE_VIEWS) REFUSE("views and their columns", i);
+ if (out->views == capacity) REFUSE("views and their columns", i);
uint32_t c = out->views++, field = r->first + f;
memcpy(out->view_names[c], out->record_field_names[field], PURE_MODULE_NAME);
out->view_type[c] = out->record_field_type[field];
@@ -2130,7 +2199,7 @@ refused:
#undef REFUSE
}
-static int find(const char (*names)[PURE_MODULE_NAME], unsigned count, const char *name) {
+static int find(char (*names)[PURE_MODULE_NAME], unsigned count, const char *name) {
for (unsigned i = 0; i < count; ++i)
if (!strcmp(names[i], name)) return (int)i;
return -1;