diff --git a/include/mruby/irep.h b/include/mruby/irep.h index 924e72260..1719866c8 100644 --- a/include/mruby/irep.h +++ b/include/mruby/irep.h @@ -82,6 +82,7 @@ struct mrb_irep { #define MRB_ISEQ_NO_FREE 1 #define MRB_IREP_NO_FREE 2 #define MRB_IREP_STATIC (MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE) +#define MRB_IREP_CONSOLIDATED 4 /* pool/syms/reps packed with irep struct */ MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb); diff --git a/src/load.c b/src/load.c index 6f9954b82..f106c7e6e 100644 --- a/src/load.c +++ b/src/load.c @@ -59,6 +59,95 @@ str_to_double(mrb_state *mrb, const char *p) } #endif +#define ALIGN_UP(x, a) (((x) + (a) - 1) & ~((size_t)(a) - 1)) + +/* + * Lightweight prescan of pool/syms binary data to learn plen and slen + * without allocating. Must stay in sync with the pool/syms parsing + * in read_irep_record_1(). + */ +static mrb_bool +prescan_pool_syms(const uint8_t *src, const uint8_t *end, uint16_t *plenp, uint16_t *slenp) +{ + uint16_t plen; + int i; + + if (src + sizeof(uint16_t) > end) return FALSE; + plen = bin_to_uint16(src); + src += sizeof(uint16_t); + + for (i = 0; i < plen; i++) { + if (src >= end) return FALSE; + switch (*src++) { + case IREP_TT_INT32: + src += sizeof(uint32_t); + break; + case IREP_TT_INT64: + src += sizeof(uint32_t) * 2; + break; + case IREP_TT_BIGINT: + if (src >= end) return FALSE; + src += bin_to_uint8(src) + 2; + break; + case IREP_TT_FLOAT: + src += sizeof(double); + break; + case IREP_TT_STR: + if (src + sizeof(uint16_t) > end) return FALSE; + src += sizeof(uint16_t) + bin_to_uint16(src) + 1; + break; + default: + return FALSE; + } + if (src > end) return FALSE; + } + + if (src + sizeof(uint16_t) > end) return FALSE; + *plenp = plen; + *slenp = bin_to_uint16(src); + return TRUE; +} + +/* + * Single consolidated allocation for irep struct + pool + syms + reps arrays. + * Memory layout: [mrb_irep] [mrb_irep_pool[plen]] [mrb_irep*[rlen]] [mrb_sym[slen]] + * Ordered by descending alignment to minimize inter-array padding. + */ +static mrb_irep* +irep_alloc_consolidated(mrb_state *mrb, uint16_t plen, uint16_t slen, uint16_t rlen) +{ + size_t off = sizeof(mrb_irep); + size_t pool_off = 0, syms_off = 0, reps_off = 0; + uint8_t *block; + mrb_irep *irep; + + /* pool (8-byte aligned: contains int64_t/double in union) */ + if (plen > 0) { + pool_off = ALIGN_UP(off, 8); + off = pool_off + sizeof(mrb_irep_pool) * plen; + } + /* reps (pointer-aligned: naturally follows 8-byte-aligned pool) */ + if (rlen > 0) { + reps_off = ALIGN_UP(off, sizeof(void*)); + off = reps_off + sizeof(mrb_irep*) * rlen; + } + /* syms (4-byte aligned: naturally follows pointer-aligned reps) */ + if (slen > 0) { + syms_off = ALIGN_UP(off, sizeof(mrb_sym)); + off = syms_off + sizeof(mrb_sym) * slen; + } + + block = (uint8_t*)mrb_calloc(mrb, 1, off); + irep = (mrb_irep*)block; + irep->flags = MRB_IREP_CONSOLIDATED; + irep->refcnt = 1; + if (plen > 0) irep->pool = (const mrb_irep_pool*)(block + pool_off); + if (slen > 0) irep->syms = (const mrb_sym*)(block + syms_off); + if (rlen > 0) irep->reps = (const struct mrb_irep *const*)(block + reps_off); + + return irep; +} + static mrb_bool read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_t *len, uint8_t flags, mrb_irep **irepp) { @@ -66,43 +155,58 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_ const uint8_t *src = bin; ptrdiff_t diff; uint16_t tt, pool_data_len, snl; + uint16_t nlocals, nregs, rlen, clen; + uint32_t ilen; int plen; mrb_irep_pool *pool; mrb_sym *syms; + mrb_irep *irep; int ai = mrb_gc_arena_save(mrb); - mrb_irep *irep = mrb_add_irep(mrb); - - *irepp = irep; /* skip record size */ src += sizeof(uint32_t); - /* number of local variable */ - irep->nlocals = bin_to_uint16(src); + /* parse header into local variables */ + nlocals = bin_to_uint16(src); src += sizeof(uint16_t); - - /* number of register variable */ - irep->nregs = bin_to_uint16(src); + nregs = bin_to_uint16(src); src += sizeof(uint16_t); - - /* number of child irep */ - irep->rlen = bin_to_uint16(src); + rlen = bin_to_uint16(src); src += sizeof(uint16_t); /* Binary Data Section */ /* ISEQ BLOCK (and CATCH HANDLER TABLE BLOCK) */ - irep->clen = bin_to_uint16(src); /* number of catch handler */ + clen = bin_to_uint16(src); /* number of catch handler */ src += sizeof(uint16_t); - irep->ilen = bin_to_uint32(src); + ilen = bin_to_uint32(src); src += sizeof(uint32_t); + /* prescan pool/syms to learn counts for consolidated allocation */ + { + const uint8_t *pool_start = src; + uint16_t pre_plen, pre_slen; + if (ilen > 0) { + size_t iseq_len; + if (SIZE_ERROR_MUL(ilen, sizeof(mrb_code))) return FALSE; + iseq_len = sizeof(mrb_code) * ilen + + sizeof(struct mrb_irep_catch_handler) * clen; + if (src + iseq_len > end) return FALSE; + pool_start = src + iseq_len; + } + if (!prescan_pool_syms(pool_start, end, &pre_plen, &pre_slen)) return FALSE; + irep = irep_alloc_consolidated(mrb, pre_plen, pre_slen, rlen); + } + *irepp = irep; + irep->nlocals = nlocals; + irep->nregs = nregs; + irep->rlen = rlen; + irep->clen = clen; + irep->ilen = ilen; + if (irep->ilen > 0) { size_t data_len = sizeof(mrb_code) * irep->ilen + sizeof(struct mrb_irep_catch_handler) * irep->clen; mrb_static_assert(sizeof(struct mrb_irep_catch_handler) == 13); - if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { - return FALSE; - } if (src + data_len > end) return FALSE; if ((flags & FLAG_SRC_MALLOC) == 0) { irep->iseq = (mrb_code*)src; @@ -121,10 +225,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_ src += sizeof(uint16_t); if (src > end) return FALSE; if (plen > 0) { - if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { - return FALSE; - } - irep->pool = pool = (mrb_irep_pool*)mrb_calloc(mrb, sizeof(mrb_irep_pool), plen); + pool = (mrb_irep_pool*)irep->pool; /* in consolidated block */ for (i = 0; i < plen; i++) { mrb_bool st = (flags & FLAG_SRC_MALLOC)==0; @@ -217,10 +318,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_ src += sizeof(uint16_t); if (src > end) return FALSE; if (irep->slen > 0) { - if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { - return FALSE; - } - irep->syms = syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); + syms = (mrb_sym*)irep->syms; /* in consolidated block */ for (i = 0; i < irep->slen; i++) { snl = bin_to_uint16(src); /* symbol name length */ @@ -263,8 +361,7 @@ read_irep_record(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_t return FALSE; } - reps = (mrb_irep**)mrb_calloc(mrb, (*irepp)->rlen, sizeof(mrb_irep*)); - (*irepp)->reps = (const mrb_irep**)reps; + reps = (mrb_irep**)(*irepp)->reps; /* in consolidated block */ bin += *len; for (i=0; i<(*irepp)->rlen; i++) { diff --git a/src/state.c b/src/state.c index bfb729d51..f4ecbaa96 100644 --- a/src/state.c +++ b/src/state.c @@ -134,8 +134,10 @@ void mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { int i; + mrb_bool consolidated; if (irep->flags & MRB_IREP_NO_FREE) return; + consolidated = (irep->flags & MRB_IREP_CONSOLIDATED) != 0; if (!(irep->flags & MRB_ISEQ_NO_FREE)) mrb_free(mrb, (void*)irep->iseq); if (irep->pool) { @@ -145,15 +147,15 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep) mrb_free(mrb, (void*)irep->pool[i].u.str); } } - mrb_free(mrb, (void*)irep->pool); + if (!consolidated) mrb_free(mrb, (void*)irep->pool); } - mrb_free(mrb, (void*)irep->syms); + if (!consolidated) mrb_free(mrb, (void*)irep->syms); if (irep->reps) { for (i=0; irlen; i++) { if (irep->reps[i]) mrb_irep_decref(mrb, (mrb_irep*)irep->reps[i]); } - mrb_free(mrb, (void*)irep->reps); + if (!consolidated) mrb_free(mrb, (void*)irep->reps); } mrb_free(mrb, (void*)irep->lv); mrb_debug_info_free(mrb, irep->debug_info);