Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9aa8e88
Remove zend_jit_vm_kind (#19299)
arnaud-lb Aug 4, 2025
4e21924
Fix GH-17951: Addition of max_memory_limit INI
frederikpyt Mar 10, 2025
cd80ed6
Implement changes to GH-17951 according to ML discussion
iluuu1994 Aug 4, 2025
433c00b
[skip ci] tree: Fix various typos (#19366)
alexandre-daubois Aug 4, 2025
ac1cd9c
Update IR
dstogov Aug 4, 2025
ef98a6e
Merge branch 'PHP-8.4'
dstogov Aug 4, 2025
17b8706
streams: add const specifier
Girgias Jul 12, 2025
b35dbe4
streams: reduce scope of variables
Girgias Jul 12, 2025
b3551cc
streams: use type bool instead of type int
Girgias Jul 12, 2025
841afdc
streams: use type zend_result instead of type int
Girgias Jul 12, 2025
c9836b0
streams: use type size_t instead of type unsigned int
Girgias Jul 12, 2025
67d6160
streams: use int type instead of long
Girgias Jul 12, 2025
02af9ac
streams: use precomputed persistant variable
Girgias Jul 12, 2025
4b6ebcc
streams: remove confusing step variable
Girgias Jul 12, 2025
fb2585f
streams: use -1 directly instead of FAILURE macro
Girgias Jul 12, 2025
0d0f774
streams: use call in if statement directly
Girgias Jul 12, 2025
c50a715
streams: remove useless casts
Girgias Jul 12, 2025
b46c5e6
streams: refactor implementation of stream_select()
Girgias Jul 12, 2025
cd13ba7
streams: use RETURN_BOOL() when possible
Girgias Jul 12, 2025
fa85b38
streams: voidify php_stream_filter_prepend_ex()
Girgias Jul 12, 2025
93ec0ac
streams: drop unused includes
Girgias Jul 12, 2025
051414b
streams: use an enum for return type of _php_stream_make_seekable()
Girgias Jul 12, 2025
a02b2b8
streams: use %zu printf specifier for size_t
Girgias Jul 12, 2025
9e334af
streams: use type php_socket_t instead of type int
Girgias Jul 12, 2025
4d5bdef
streams: refactor statbuf_from_array()
Girgias Jul 12, 2025
9d29283
Fix filtering of INI directives to respect leading whitespaces
madmajestro Aug 1, 2025
bd7ffc9
Merge branch 'PHP-8.3' into PHP-8.4
iluuu1994 Aug 4, 2025
0d15bca
Merge branch 'PHP-8.4'
iluuu1994 Aug 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,11 @@ PHP 8.5 UPGRADE NOTES
. Added fatal_error_backtraces to control whether fatal errors should include
a backtrace.
RFC: https://wiki.php.net/rfc/error_backtraces_v2
. Added startup-only max_memory_limit INI setting to control the maximum
memory_limit that may be configured at startup or runtime. Exceeding this
value emits a warning, unless set to -1, and sets memory_limit to the
current max_memory_limit instead.
ML discussion: https://externals.io/message/127108

- Opcache:
. Added opcache.file_cache_read_only to support a read-only
Expand Down
20 changes: 16 additions & 4 deletions Zend/zend_ini.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,16 @@ ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_
zend_unregister_ini_entries_ex(module_number, module_type);
return FAILURE;
}

zend_string *prev_value = p->value;

if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
(!p->on_modify || p->on_modify(p, Z_STR_P(default_value), p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS)) {

p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
/* Skip assigning the value if the handler has already done so. */
if (p->value == prev_value) {
p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
}
} else {
p->value = ini_entry->value ?
zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
Expand Down Expand Up @@ -389,14 +395,20 @@ ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new
zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
}

zend_string *prev_value = ini_entry->value;
duplicate = zend_string_copy(new_value);

if (!ini_entry->on_modify
|| ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
zend_string_release(ini_entry->value);
if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
zend_string_release(prev_value);
}
/* Skip assigning the value if the handler has already done so. */
if (ini_entry->value == prev_value) {
ini_entry->value = duplicate;
} else {
zend_string_release(duplicate);
}
ini_entry->value = duplicate;
} else {
zend_string_release(duplicate);
return FAILURE;
Expand Down
2 changes: 1 addition & 1 deletion build/Makefile.global
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ PHP_TEST_SHARED_EXTENSIONS = ` \
. $$i; $(top_srcdir)/build/shtool echo -n -- " -d zend_extension=$(top_builddir)/modules/$$dlname"; \
done; \
fi`
PHP_DEPRECATED_DIRECTIVES_REGEX = '^(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?|session\.sid_(length|bits_per_character))[\t\ ]*='
PHP_DEPRECATED_DIRECTIVES_REGEX = '^[\t\ ]*(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?|session\.sid_(length|bits_per_character))[\t\ ]*='

test: all
@if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \
Expand Down
2 changes: 1 addition & 1 deletion ext/gettext/gettext.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ PHP_FUNCTION(bindtextdomain)
btd_result = bindtextdomain(ZSTR_VAL(domain), NULL);
if (btd_result == NULL) {
/* POSIX-compliant implementations can return
* NULL if an error occured. On musl you will
* NULL if an error occurred. On musl you will
* also get NULL if the domain is not yet
* bound, because musl has no default directory
* to return in that case. */
Expand Down
15 changes: 12 additions & 3 deletions ext/opcache/jit/ir/ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
} else if (insn->val.c == '\0') {
fprintf(f, "'\\0'");
} else {
fprintf(f, "%u", insn->val.c);
fprintf(f, "%u", (unsigned char)insn->val.c);
}
break;
case IR_I8:
Expand Down Expand Up @@ -247,6 +247,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted
#define ir_op_flag_S1X1 (ir_op_flag_S | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_S2 (ir_op_flag_S | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_S2X1 (ir_op_flag_S | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_S3 (ir_op_flag_S | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_SN (ir_op_flag_S | IR_OP_FLAG_VAR_INPUTS)
#define ir_op_flag_E (IR_OP_FLAG_CONTROL|IR_OP_FLAG_BB_END)
#define ir_op_flag_E1 (ir_op_flag_E | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
Expand Down Expand Up @@ -803,7 +804,9 @@ ir_ref ir_proto(ir_ctx *ctx, uint8_t flags, ir_type ret_type, uint32_t params_co
proto->flags = flags;
proto->ret_type = ret_type;
proto->params_count = params_count;
memcpy(proto->param_types, param_types, params_count);
if (params_count) {
memcpy(proto->param_types, param_types, params_count);
}
return ir_strl(ctx, (const char *)proto, offsetof(ir_proto_t, param_types) + params_count);
}

Expand Down Expand Up @@ -1080,7 +1083,7 @@ ir_ref ir_emit_N(ir_ctx *ctx, uint32_t opt, int32_t count)
ir_ref *p, ref = ctx->insns_count;
ir_insn *insn;

IR_ASSERT(count >= 0);
IR_ASSERT(count >= 0 && count < 65536);
while (UNEXPECTED(ref + count/4 >= ctx->insns_limit)) {
ir_grow_top(ctx);
}
Expand Down Expand Up @@ -2973,6 +2976,12 @@ void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val)
ctx->control = ir_emit2(ctx, IR_CASE_VAL, switch_ref, val);
}

void _ir_CASE_RANGE(ir_ctx *ctx, ir_ref switch_ref, ir_ref v1, ir_ref v2)
{
IR_ASSERT(!ctx->control);
ctx->control = ir_emit3(ctx, IR_CASE_RANGE, switch_ref, v1, v2);
}

void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref)
{
IR_ASSERT(!ctx->control);
Expand Down
17 changes: 11 additions & 6 deletions ext/opcache/jit/ir/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ typedef enum _ir_type {
_(IF_TRUE, S1X1, src, prb, ___) /* IF TRUE proj. */ \
_(IF_FALSE, S1X1, src, prb, ___) /* IF FALSE proj. */ \
_(CASE_VAL, S2X1, src, def, prb) /* switch proj. */ \
_(CASE_RANGE, S3, src, def, def) /* switch proj. */ \
_(CASE_DEFAULT, S1X1, src, prb, ___) /* switch proj. */ \
_(MERGE, SN, src, src, src) /* control merge */ \
_(LOOP_BEGIN, SN, src, src, src) /* loop start */ \
Expand Down Expand Up @@ -854,6 +855,9 @@ void ir_gdb_unregister_all(void);
bool ir_gdb_present(void);

/* IR load API (implementation in ir_load.c) */
#define IR_RESOLVE_SYM_ADD_THUNK (1<<0)
#define IR_RESOLVE_SYM_SILENT (1<<1)

struct _ir_loader {
uint32_t default_func_flags;
bool (*init_module) (ir_loader *loader, const char *name, const char *filename, const char *target);
Expand All @@ -870,7 +874,7 @@ struct _ir_loader {
bool (*sym_data_end) (ir_loader *loader, uint32_t flags);
bool (*func_init) (ir_loader *loader, ir_ctx *ctx, const char *name);
bool (*func_process) (ir_loader *loader, ir_ctx *ctx, const char *name);
void*(*resolve_sym_name) (ir_loader *loader, const char *name, bool add_thunk);
void*(*resolve_sym_name) (ir_loader *loader, const char *name, uint32_t flags);
bool (*has_sym) (ir_loader *loader, const char *name);
bool (*add_sym) (ir_loader *loader, const char *name, void *addr);
};
Expand All @@ -884,11 +888,12 @@ int ir_load_llvm_bitcode(ir_loader *loader, const char *filename);
int ir_load_llvm_asm(ir_loader *loader, const char *filename);

/* IR save API (implementation in ir_save.c) */
#define IR_SAVE_CFG (1<<0) /* add info about CFG */
#define IR_SAVE_CFG_MAP (1<<1) /* add info about CFG block assignment */
#define IR_SAVE_USE_LISTS (1<<2) /* add info about def->use lists */
#define IR_SAVE_RULES (1<<3) /* add info about selected code-generation rules */
#define IR_SAVE_REGS (1<<4) /* add info about selected registers */
#define IR_SAVE_CFG (1<<0) /* add info about CFG */
#define IR_SAVE_CFG_MAP (1<<1) /* add info about CFG block assignment */
#define IR_SAVE_USE_LISTS (1<<2) /* add info about def->use lists */
#define IR_SAVE_RULES (1<<3) /* add info about selected code-generation rules */
#define IR_SAVE_REGS (1<<4) /* add info about selected registers */
#define IR_SAVE_SAFE_NAMES (1<<5) /* add '@' prefix to symbol names */

void ir_print_proto(const ir_ctx *ctx, ir_ref proto, FILE *f);
void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f);
Expand Down
94 changes: 85 additions & 9 deletions ext/opcache/jit/ir/ir_aarch64.dasc
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ binop_fp:
case IR_IF_TRUE:
case IR_IF_FALSE:
case IR_CASE_VAL:
case IR_CASE_RANGE:
case IR_CASE_DEFAULT:
case IR_MERGE:
case IR_LOOP_BEGIN:
Expand Down Expand Up @@ -4366,11 +4367,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn)
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_type type = insn->type;
ir_reg def_reg = ctx->regs[def][0];
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
ir_reg op2_reg = ctx->regs[def][2];
ir_reg tmp_reg = ctx->regs[def][3];
int32_t offset;

if (ctx->use_lists[def].count == 1) {
/* dead load */
return;
}
IR_ASSERT(def_reg != IR_REG_NONE && tmp_reg != IR_REG_NONE);
if (op2_reg != IR_REG_NONE) {
if (IR_REG_SPILLED(op2_reg)) {
Expand All @@ -4394,11 +4399,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn)
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_type type = insn->type;
ir_reg def_reg = ctx->regs[def][0];
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
ir_reg op2_reg = ctx->regs[def][2];
ir_reg tmp_reg = ctx->regs[def][3];
int32_t offset;

if (ctx->use_lists[def].count == 1) {
/* dead load */
return;
}
IR_ASSERT(def_reg != IR_REG_NONE && tmp_reg != IR_REG_NONE);
if (op2_reg != IR_REG_NONE) {
if (IR_REG_SPILLED(op2_reg)) {
Expand Down Expand Up @@ -4465,6 +4474,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)
int count = 0;
ir_val min, max;
ir_reg op1_reg, op2_reg, tmp_reg;
bool has_case_range = 0;

type = ctx->ir_base[insn->op2].type;
if (IR_IS_TYPE_SIGNED(type)) {
Expand Down Expand Up @@ -4493,6 +4503,22 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)
max.u64 = (int64_t)IR_MAX(max.u64, val->val.u64);
}
count++;
} else if (use_insn->op == IR_CASE_RANGE) {
has_case_range = 1;
val = &ctx->ir_base[use_insn->op2];
IR_ASSERT(!IR_IS_SYM_CONST(val->op));
ir_insn *val2 = &ctx->ir_base[use_insn->op3];
IR_ASSERT(!IR_IS_SYM_CONST(val2->op));
if (IR_IS_TYPE_SIGNED(type)) {
IR_ASSERT(IR_IS_TYPE_SIGNED(val->type));
min.i64 = IR_MIN(min.i64, val->val.i64);
max.i64 = IR_MAX(max.i64, val2->val.i64);
} else {
IR_ASSERT(!IR_IS_TYPE_SIGNED(val->type));
min.u64 = (int64_t)IR_MIN(min.u64, val->val.u64);
max.u64 = (int64_t)IR_MAX(max.u64, val2->val.u64);
}
count++;
} else {
IR_ASSERT(use_insn->op == IR_CASE_DEFAULT);
default_label = ir_skip_empty_target_blocks(ctx, use_block);
Expand All @@ -4510,7 +4536,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)
}

/* Generate a table jmp or a sequence of calls */
if (count > 2 && (max.i64-min.i64) < count * 8) {
if (!has_case_range && count > 2 && (max.i64-min.i64) < count * 8) {
int *labels = ir_mem_malloc(sizeof(int) * (max.i64 - min.i64 + 1));

for (i = 0; i <= (max.i64 - min.i64); i++) {
Expand Down Expand Up @@ -4615,6 +4641,38 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn)

}
| beq =>label
} else if (use_insn->op == IR_CASE_RANGE) {
val = &ctx->ir_base[use_insn->op2];
IR_ASSERT(!IR_IS_SYM_CONST(val->op));
label = ir_skip_empty_target_blocks(ctx, use_block);
if (aarch64_may_encode_imm12(val->val.i64)) {
| ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i64
} else {
ir_emit_load_imm_int(ctx, type, tmp_reg, val->val.i64);
| ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg

}
if (IR_IS_TYPE_SIGNED(type)) {
| blt >1
} else {
| blo >1
}
val = &ctx->ir_base[use_insn->op3];
IR_ASSERT(!IR_IS_SYM_CONST(val->op));
label = ir_skip_empty_target_blocks(ctx, use_block);
if (aarch64_may_encode_imm12(val->val.i64)) {
| ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i64
} else {
ir_emit_load_imm_int(ctx, type, tmp_reg, val->val.i64);
| ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg

}
if (IR_IS_TYPE_SIGNED(type)) {
| ble =>label
} else {
| bls =>label
}
|1:
}
}
if (default_label) {
Expand Down Expand Up @@ -4935,6 +4993,28 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn)
return;
}

/* Move op2 to a tmp register before epilogue if it's in
* used_preserved_regs, because it will be overridden. */

ir_reg op2_reg = IR_REG_NONE;
if (!IR_IS_CONST_REF(insn->op2)) {
op2_reg = ctx->regs[def][2];
IR_ASSERT(op2_reg != IR_REG_NONE);

if (IR_REG_SPILLED(op2_reg)) {
op2_reg = IR_REG_INT_TMP;
ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2);
} else if (IR_REGSET_IN((ir_regset)ctx->used_preserved_regs, IR_REG_NUM(op2_reg))) {
ir_reg orig_op2_reg = op2_reg;
op2_reg = IR_REG_INT_TMP;

ir_type type = ctx->ir_base[insn->op2].type;
| ASM_REG_REG_OP mov, type, op2_reg, IR_REG_NUM(orig_op2_reg)
} else {
op2_reg = IR_REG_NUM(op2_reg);
}
}

ir_emit_epilogue(ctx);

if (IR_IS_CONST_REF(insn->op2)) {
Expand All @@ -4947,13 +5027,8 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn)
| br Rx(IR_REG_INT_TMP)
}
} else {
ir_reg op2_reg = ctx->regs[def][2];

IR_ASSERT(op2_reg != IR_REG_NONE);
if (IR_REG_SPILLED(op2_reg)) {
op2_reg = IR_REG_NUM(op2_reg);
ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2);
}
IR_ASSERT(!IR_REGSET_IN((ir_regset)ctx->used_preserved_regs, op2_reg));
| br Rx(op2_reg)
}
}
Expand Down Expand Up @@ -5590,6 +5665,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx)
case IR_IF_TRUE:
case IR_IF_FALSE:
case IR_CASE_VAL:
case IR_CASE_RANGE:
case IR_CASE_DEFAULT:
case IR_MERGE:
case IR_LOOP_BEGIN:
Expand Down
2 changes: 2 additions & 0 deletions ext/opcache/jit/ir/ir_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ extern "C" {
#define ir_LOOP_END() _ir_LOOP_END(_ir_CTX)
#define ir_SWITCH(_val) _ir_SWITCH(_ir_CTX, (_val))
#define ir_CASE_VAL(_switch, _val) _ir_CASE_VAL(_ir_CTX, (_switch), (_val))
#define ir_CASE_RANGE(_switch, _v1, _v2) _ir_CASE_RANGE(_ir_CTX, (_switch), (_v1), (_v2))
#define ir_CASE_DEFAULT(_switch) _ir_CASE_DEFAULT(_ir_CTX, (_switch))
#define ir_RETURN(_val) _ir_RETURN(_ir_CTX, (_val))
#define ir_IJMP(_addr) _ir_IJMP(_ir_CTX, (_addr))
Expand Down Expand Up @@ -682,6 +683,7 @@ ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset);
void _ir_UNREACHABLE(ir_ctx *ctx);
ir_ref _ir_SWITCH(ir_ctx *ctx, ir_ref val);
void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val);
void _ir_CASE_RANGE(ir_ctx *ctx, ir_ref switch_ref, ir_ref v1, ir_ref v2);
void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref);
void _ir_RETURN(ir_ctx *ctx, ir_ref val);
void _ir_IJMP(ir_ctx *ctx, ir_ref addr);
Expand Down
Loading