• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

krakjoe / parallel / 20264594980

16 Dec 2025 10:23AM UTC coverage: 93.383% (-3.4%) from 96.815%
20264594980

Pull #356

github

web-flow
Merge 8e1bf9bdd into 14042a874
Pull Request #356: convert `ZEND_INIT_FCALL` to `ZEND_INIT_DYNAMIC_CALL`

1 of 6 new or added lines in 1 file covered. (16.67%)

102 existing lines in 3 files now uncovered.

2907 of 3113 relevant lines covered (93.38%)

4450.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.53
/src/cache.c
1
/*
2
  +----------------------------------------------------------------------+
3
  | parallel                                                             |
4
  +----------------------------------------------------------------------+
5
  | Copyright (c) Joe Watkins 2019-2024                                  |
6
  +----------------------------------------------------------------------+
7
  | This source file is subject to version 3.01 of the PHP license,      |
8
  | that is bundled with this package in the file LICENSE, and is        |
9
  | available through the world-wide-web at the following url:           |
10
  | http://www.php.net/license/3_01.txt                                  |
11
  | If you did not receive a copy of the PHP license and are unable to   |
12
  | obtain it through the world-wide-web, please send a note to          |
13
  | license@php.net so we can mail you a copy immediately.               |
14
  +----------------------------------------------------------------------+
15
  | Author: krakjoe                                                      |
16
  +----------------------------------------------------------------------+
17
 */
18
#ifndef HAVE_PARALLEL_CACHE
19
#define HAVE_PARALLEL_CACHE
20

21
#include "parallel.h"
22

23
static struct {
24
    pthread_mutex_t mutex;
25
    HashTable       table;
26
    struct {
27
        size_t      size;
28
        size_t      used;
29
        void       *mem;
30
        void       *block;
31
    } memory;
32
} php_parallel_cache_globals = {PTHREAD_MUTEX_INITIALIZER};
33

34
#define PCG(e) php_parallel_cache_globals.e
35
#define PCM(e) PCG(memory).e
36

37
#define PARALLEL_CACHE_CHUNK \
38
    PARALLEL_PLATFORM_ALIGNED((1024 * 1024) * 8)
39

40
/* {{{ */
41
static zend_always_inline void* php_parallel_cache_alloc(size_t size) {
1,758✔
42
    void *mem;
1,758✔
43
    size_t aligned =
1,758✔
44
        PARALLEL_PLATFORM_ALIGNED(size);
148✔
45

46
    ZEND_ASSERT(size < PARALLEL_CACHE_CHUNK);
1,758✔
47

48
    if ((PCM(used) + aligned) >= PCM(size)) {
1,758✔
49
        PCM(size) = PARALLEL_PLATFORM_ALIGNED(
×
50
            PCM(size) + PARALLEL_CACHE_CHUNK);
51
        PCM(mem) = (void*) realloc(PCM(mem), PCM(size));
×
52

53
        if (!PCM(mem)) {
×
54
            /* out of memory */
55
            return NULL;
56
        }
57

58
        PCM(block) = (void*)(((char*)PCM(mem)) + PCM(used));
×
59
    }
60

61
    mem = PCM(block);
1,758✔
62
    PCM(block) =
1,758✔
63
        (void*)(((char*)PCM(block)) + aligned);
1,758✔
64
    PCM(used) += aligned;
1,758✔
65

66
    return mem;
1,758✔
67
}
68

69
static zend_always_inline void* php_parallel_cache_copy_mem(void *source, zend_long size) {
1,758✔
70
    void *destination =
1,758✔
71
        php_parallel_cache_alloc(size);
28✔
72

73
    memcpy(destination, source, size);
1,730✔
74

75
    return destination;
1,758✔
76
} /* }}} */
77

78
static zend_always_inline HashTable* php_parallel_cache_statics(HashTable *statics) { /* {{{ */
14✔
79
    HashTable *cached = zend_hash_index_find_ptr(&PCG(table), (zend_ulong) statics);
28✔
80

81
    if (cached) {
×
82
        return cached;
83
    }
84

85
    cached = php_parallel_copy_hash_persistent(
14✔
86
                statics,
87
                php_parallel_copy_string_interned,
88
                php_parallel_cache_copy_mem);
89

90
    return zend_hash_index_update_ptr(&PCG(table), (zend_ulong) statics, cached);
14✔
91
} /* }}} */
92

UNCOV
93
static zend_always_inline void php_parallel_cache_type(zend_type *type) { /* {{{ */
×
UNCOV
94
    zend_type *single;
×
95

UNCOV
96
    if (!ZEND_TYPE_IS_SET(*type)) {
×
97
        return;
98
    }
99

UNCOV
100
    if (ZEND_TYPE_HAS_LIST(*type)) {
×
UNCOV
101
        zend_type_list *list = ZEND_TYPE_LIST(*type);
×
102

UNCOV
103
        list = php_parallel_cache_copy_mem(
×
UNCOV
104
                    list, ZEND_TYPE_LIST_SIZE(list->num_types));
×
105

UNCOV
106
        if (ZEND_TYPE_USES_ARENA(*type)) {
×
UNCOV
107
            ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT;
×
108
        }
109

UNCOV
110
        ZEND_TYPE_SET_PTR(*type, list);
×
111
    }
112

UNCOV
113
    ZEND_TYPE_FOREACH(*type, single) {
×
UNCOV
114
        if (ZEND_TYPE_HAS_NAME(*single)) {
×
UNCOV
115
            zend_string *name = ZEND_TYPE_NAME(*single);
×
116

UNCOV
117
            ZEND_TYPE_SET_PTR(
×
118
                *single,
119
                php_parallel_copy_string_interned(name));
120
        }
UNCOV
121
    } ZEND_TYPE_FOREACH_END();
×
122
} /* }}} */
123

124

125
/* {{{ */
126
static zend_op_array* php_parallel_cache_create(const zend_function *source, bool statics) {
1,610✔
127
    zend_op_array *cached = php_parallel_cache_copy_mem((void*) source, sizeof(zend_op_array));
1,610✔
128

129
    cached->fn_flags |= ZEND_ACC_IMMUTABLE;
1,610✔
130

131
    if (statics && cached->static_variables) {
1,610✔
132
        cached->static_variables =
28✔
133
            php_parallel_cache_statics(cached->static_variables);
28✔
134
    }
135

136
#if PHP_VERSION_ID >= 80200
137
    ZEND_MAP_PTR_INIT(cached->static_variables_ptr, cached->static_variables);
1,096✔
138
#else
139
    ZEND_MAP_PTR_INIT(cached->static_variables_ptr, &cached->static_variables);
514✔
140
#endif
141

142
    ZEND_MAP_PTR_INIT(cached->run_time_cache, NULL);
1,610✔
143

144
#if PHP_VERSION_ID >= 80100
145
    if (cached->num_dynamic_func_defs) {
1,370✔
146
            uint32_t it = 0;
120✔
147
            
148
            cached->dynamic_func_defs = php_parallel_cache_copy_mem(
240✔
149
                                            cached->dynamic_func_defs,
120✔
150
                                            sizeof(zend_op_array*) * cached->num_dynamic_func_defs);
120✔
151
            
152
            while (it < cached->num_dynamic_func_defs) {
260✔
153
                cached->dynamic_func_defs[it] = 
280✔
154
                (zend_op_array*) php_parallel_cache_create(
140✔
155
                        (zend_function*) cached->dynamic_func_defs[it], statics);
140✔
156
            it++;
140✔
157
            }
158
    }
159
#endif
160

161
    if (!cached->refcount) {
1,610✔
162
        goto _php_parallel_cached_function_return;
1,610✔
163
    }
164

UNCOV
165
    cached->refcount  = NULL;
×
166

UNCOV
167
    if (cached->last_literal) {
×
UNCOV
168
        zval     *literal = cached->literals,
×
UNCOV
169
                 *end     = literal + cached->last_literal;
×
UNCOV
170
        zval     *slot    = php_parallel_cache_copy_mem(
×
UNCOV
171
                                    cached->literals,
×
172
                                        sizeof(zval) * cached->last_literal);
173

UNCOV
174
        cached->literals = slot;
×
175

UNCOV
176
        while (literal < end) {
×
UNCOV
177
            if (Z_TYPE_P(literal) == IS_ARRAY) {
×
UNCOV
178
                ZVAL_ARR(slot,
×
179
                    php_parallel_copy_hash_persistent(
180
                        Z_ARRVAL_P(literal),
181
                        php_parallel_copy_string_interned,
182
                        php_parallel_cache_copy_mem));
UNCOV
183
            } else if (Z_TYPE_P(literal) == IS_STRING) {
×
UNCOV
184
                ZVAL_STR(slot,
×
185
                    php_parallel_copy_string_interned(Z_STR_P(literal)));
186
            }
187

UNCOV
188
                Z_TYPE_FLAGS_P(slot) &= ~(IS_TYPE_REFCOUNTED|IS_TYPE_COLLECTABLE);
×
UNCOV
189
            literal++;
×
UNCOV
190
            slot++;
×
191
        }
192
    }
193

UNCOV
194
    if (cached->last_var) {
×
UNCOV
195
        zend_string **vars = cached->vars;
×
UNCOV
196
        uint32_t      it = 0,
×
UNCOV
197
                      end = cached->last_var;
×
UNCOV
198
        zend_string **heap = php_parallel_cache_alloc(cached->last_var * sizeof(zend_string*));
×
199

UNCOV
200
        while (it < end) {
×
UNCOV
201
            heap[it] =
×
UNCOV
202
                php_parallel_copy_string_interned(vars[it]);
×
UNCOV
203
            it++;
×
204
        }
UNCOV
205
        cached->vars = heap;
×
206
    }
207

UNCOV
208
    if (cached->last) {
×
UNCOV
209
        zend_op *opcodes = php_parallel_cache_copy_mem(cached->opcodes, sizeof(zend_op) * cached->last);
×
UNCOV
210
        zend_op *opline  = opcodes,
×
UNCOV
211
                *end     = opline + cached->last;
×
212

UNCOV
213
        while (opline < end) {
×
NEW
214
            if (opline->opcode == ZEND_INIT_FCALL) {
×
NEW
215
                opline->opcode = ZEND_INIT_DYNAMIC_CALL;
×
NEW
216
                opline->op1_type = IS_UNUSED;
×
NEW
217
                opline->op1.var = 0;
×
NEW
218
                zend_vm_set_opcode_handler_ex(opline, 0, 0, 0);
×
219
            }
220

UNCOV
221
            if (opline->op1_type == IS_CONST) {
×
222
#if ZEND_USE_ABS_CONST_ADDR
223
                opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)cached->literals - (char*)source->op_array.literals));
224
#else
UNCOV
225
                opline->op1.constant =
×
UNCOV
226
                    (char*)(cached->literals +
×
UNCOV
227
                            ((zval*)((char*)(source->op_array.opcodes + (opline - opcodes)) +
×
UNCOV
228
                            (int32_t)opline->op1.constant) - source->op_array.literals)) -
×
229
                            (char*)opline;
230
#endif
UNCOV
231
                if (opline->opcode == ZEND_SEND_VAL
×
UNCOV
232
                 || opline->opcode == ZEND_SEND_VAL_EX
×
UNCOV
233
                 || opline->opcode == ZEND_QM_ASSIGN) {
×
UNCOV
234
                    zend_vm_set_opcode_handler_ex(opline, 0, 0, 0);
×
235
                }
236
            }
UNCOV
237
            if (opline->op2_type == IS_CONST) {
×
238
#if ZEND_USE_ABS_CONST_ADDR
239
                opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)cached->literals - (char*)source->op_array.literals));
240
#else
UNCOV
241
                opline->op2.constant =
×
UNCOV
242
                    (char*)(cached->literals +
×
UNCOV
243
                            ((zval*)((char*)(source->op_array.opcodes + (opline - opcodes)) +
×
UNCOV
244
                            (int32_t)opline->op2.constant) - source->op_array.literals)) -
×
245
                            (char*)opline;
246
#endif
247
            }
248
#if ZEND_USE_ABS_JMP_ADDR
249
            switch (opline->opcode) {
250
                case ZEND_JMP:
251
                case ZEND_FAST_CALL:
252
                    opline->op1.jmp_addr = &opcodes[opline->op1.jmp_addr - source->op_array.opcodes];
253
                break;
254
#if PHP_VERSION_ID < 80200
255
                case ZEND_JMPZNZ:
256
#endif
257
                case ZEND_JMPZ:
258
                case ZEND_JMPNZ:
259
                case ZEND_JMPZ_EX:
260
                case ZEND_JMPNZ_EX:
261
                case ZEND_JMP_SET:
262
                case ZEND_COALESCE:
263
                case ZEND_FE_RESET_R:
264
                case ZEND_FE_RESET_RW:
265
                case ZEND_ASSERT_CHECK:
266
                    opline->op2.jmp_addr = &opcodes[opline->op2.jmp_addr - source->op_array.opcodes];
267
                    break;
268

269
                case ZEND_CATCH:
270
                    if (!(opline->extended_value & ZEND_LAST_CATCH)) {
271
                        opline->op2.jmp_addr = &opcodes[opline->op2.jmp_addr - source->op_array.opcodes];
272
                    }
273
                    break;
274
            }
275
#endif
276

UNCOV
277
            opline++;
×
278
        }
UNCOV
279
        cached->opcodes = opcodes;
×
280
    }
281

UNCOV
282
    if (cached->arg_info) {
×
UNCOV
283
        zend_arg_info *it    = cached->arg_info,
×
UNCOV
284
                      *end   = it + cached->num_args,
×
285
                      *info;
286

UNCOV
287
        if (cached->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
×
UNCOV
288
            it--;
×
289
        }
UNCOV
290
        if (cached->fn_flags & ZEND_ACC_VARIADIC) {
×
UNCOV
291
            end++;
×
292
        }
293

UNCOV
294
        cached->arg_info = info = php_parallel_cache_copy_mem(it, (end - it) * sizeof(zend_arg_info));
×
295

UNCOV
296
         while (it < end) {
×
UNCOV
297
            if (info->name) {
×
UNCOV
298
                info->name =
×
UNCOV
299
                    php_parallel_copy_string_interned(it->name);
×
300
            }
301
            
UNCOV
302
            php_parallel_cache_type(&info->type);
×
303

UNCOV
304
            info++;
×
UNCOV
305
            it++;
×
306
        }
UNCOV
307
        if (cached->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
×
UNCOV
308
            cached->arg_info++;
×
309
        }
310
    }
311

UNCOV
312
    if (cached->try_catch_array) {
×
UNCOV
313
        cached->try_catch_array =
×
UNCOV
314
            php_parallel_cache_copy_mem(
×
UNCOV
315
                cached->try_catch_array,
×
UNCOV
316
                    sizeof(zend_try_catch_element) * cached->last_try_catch);
×
317
    }
318

UNCOV
319
    if (cached->live_range) {
×
UNCOV
320
        cached->live_range =
×
UNCOV
321
            php_parallel_cache_copy_mem(
×
UNCOV
322
                cached->live_range,
×
UNCOV
323
                sizeof(zend_live_range) * cached->last_live_range);
×
324
    }
325

UNCOV
326
    if (cached->function_name)
×
UNCOV
327
        cached->function_name =
×
UNCOV
328
            php_parallel_copy_string_interned(cached->function_name);
×
329

UNCOV
330
    if (cached->filename)
×
UNCOV
331
        cached->filename =
×
UNCOV
332
            php_parallel_copy_string_interned(cached->filename);
×
333

UNCOV
334
    if (cached->doc_comment)
×
UNCOV
335
        cached->doc_comment =
×
UNCOV
336
            php_parallel_copy_string_interned(cached->doc_comment);
×
337

UNCOV
338
_php_parallel_cached_function_return:
×
339
    return cached;
1,610✔
340
}
341

342
/* {{{ */
343
static zend_always_inline zend_function* php_parallel_cache_function_ex(const zend_function *source, bool statics) {
1,918✔
344
    zend_op_array *cached;
1,918✔
345

346
    pthread_mutex_lock(&PCG(mutex));
1,918✔
347

348
    if ((cached = zend_hash_index_find_ptr(&PCG(table), (zend_ulong) source->op_array.opcodes))) {
2,366✔
349
        goto _php_parallel_cached_function_return;
448✔
350
    }
351

352
    cached = php_parallel_cache_create(source, statics);
1,470✔
353

354
    zend_hash_index_add_ptr(
3,388✔
355
        &PCG(table),
356
        (zend_ulong) source->op_array.opcodes,
1,470✔
357
        cached);
358

359
_php_parallel_cached_function_return:
1,918✔
360
    pthread_mutex_unlock(&PCG(mutex));
1,918✔
361

362
    return (zend_function*) cached;
1,918✔
363
} /* }}} */
364

365
zend_function* php_parallel_cache_closure(const zend_function *source, zend_function *closure) { /* {{{ */
1,894✔
366
    zend_op_array *cache;
1,894✔
367

368
    cache =
1,894✔
369
        (zend_op_array*)
370
            php_parallel_cache_function_ex(
1,894✔
371
                (zend_function*) source, 0);
372

373
    if (!closure) {
1,894✔
374
        closure = php_parallel_copy_mem(
1,450✔
375
            cache, sizeof(zend_op_array), 1);
376
    } else {
377
        memcpy(closure, cache, sizeof(zend_op_array));
444✔
378
    }
379

380
    if (source->op_array.static_variables) {
1,894✔
381
        HashTable *statics =
900✔
382
            ZEND_MAP_PTR_GET(
450✔
383
                source->op_array.static_variables_ptr);
384

385
        if (statics) {
450✔
386
        closure->op_array.static_variables =
720✔
387
            php_parallel_copy_hash_ctor(statics, 1);
360✔
388

389
#if PHP_VERSION_ID >= 80200
390
        ZEND_MAP_PTR_INIT(
240✔
391
            closure->op_array.static_variables_ptr,
392
            closure->op_array.static_variables);
393
#else
394
        ZEND_MAP_PTR_INIT(
120✔
395
            closure->op_array.static_variables_ptr,
396
            &closure->op_array.static_variables);
397
#endif
398
        }
399
    }
400

401
#if PHP_VERSION_ID >= 80100
402
    if (source->op_array.num_dynamic_func_defs) {
1,610✔
403
        uint32_t it = 0;
130✔
404
        /* Use regular persistent memory for dynamic_func_defs array, not cache pool */
405
        closure->op_array.dynamic_func_defs = pemalloc(
130✔
406
            sizeof(zend_op_array*) * source->op_array.num_dynamic_func_defs, 1);
407
        memcpy(closure->op_array.dynamic_func_defs,
410✔
408
            source->op_array.dynamic_func_defs,
130✔
409
            sizeof(zend_op_array*) * source->op_array.num_dynamic_func_defs);
130✔
410
        while (it < source->op_array.num_dynamic_func_defs) {
280✔
411
            closure->op_array.dynamic_func_defs[it] = (zend_op_array*) php_parallel_cache_closure((zend_function*) source->op_array.dynamic_func_defs[it], NULL);
150✔
412
            it++;
150✔
413
        }
414
    }
415
#endif
416

417
    return closure;
1,894✔
418
} /* }}} */
419

420
zend_function* php_parallel_cache_function(const zend_function *source) { /* {{{ */
24✔
421
    return php_parallel_cache_function_ex(source, 1);
24✔
422
} /* }}} */
423

424
/* {{{ */
425
PHP_MINIT_FUNCTION(PARALLEL_CACHE)
2,084✔
426
{
427
    zend_hash_init(&PCG(table), 32, NULL, NULL, 1);
2,084✔
428

429
    PCM(size) = PARALLEL_CACHE_CHUNK;
2,084✔
430
    PCM(mem) =
2,084✔
431
        PCM(block) =
2,084✔
432
            malloc(PCM(size));
2,084✔
433

434
    if (!PCM(mem)) {
2,084✔
435
        /* out of memory */
436
    }
2,084✔
437

438
    return SUCCESS;
2,084✔
439
}
440

441
PHP_MSHUTDOWN_FUNCTION(PARALLEL_CACHE)
2,084✔
442
{
443
    zend_hash_destroy(&PCG(table));
2,084✔
444

445
    if (PCM(mem))
2,084✔
446
        free(PCM(mem));
2,084✔
447

448
    return SUCCESS;
2,084✔
449
} /* }}} */
450
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc