From 0221ceeccd467f21a48f18d4e493174eef9a5b03 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:11:10 -0500 Subject: [PATCH 1/5] ext/gettext/gettext.c: handle NULLs from bindtextdomain() According to POSIX, bindtextdomain() returns "the implementation- defined default directory pathname used by the gettext family of functions" when its second parameter is NULL (i.e. when you are querying the directory corresponding to some text domain and that directory has not yet been set). Its PHP counterpart is feeding that result direclty to RETURN_STRING, but this can go wrong in two ways: 1. If an error occurs, even POSIX-compliant implementations may return NULL. 2. At least one non-compliant implementation (musl) lacks a default directory and returns NULL whenever the domain has not yet been bound. In either of those cases, PHP segfaults on the NULL string. In this commit we check for the NULL, and RETURN_FALSE when it happens rather than crashing. This partially addresses GH #13696 --- ext/gettext/gettext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 15af3cb9b57ff..eba2a591e9f1b 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -167,7 +167,7 @@ PHP_FUNCTION(bindtextdomain) char *domain; size_t domain_len; zend_string *dir = NULL; - char *retval, dir_name[MAXPATHLEN]; + char *retval, dir_name[MAXPATHLEN], *btd_result; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) { RETURN_THROWS(); @@ -181,7 +181,16 @@ PHP_FUNCTION(bindtextdomain) } if (dir == NULL) { - RETURN_STRING(bindtextdomain(domain, NULL)); + btd_result = bindtextdomain(domain, NULL); + if (btd_result == NULL) { + /* POSIX-compliant implementations can return + * NULL if an error occured. 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. */ + RETURN_FALSE; + } + RETURN_STRING(btd_result); } if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) { From bfb0e367f2a2823149be67fad00b66511750f2bf Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:17:16 -0500 Subject: [PATCH 2/5] ext/gettext/tests: fix libintl return values under musl Musl has two quirks that are leading to failed internationalization tests. First is that the return value of bindtextdomain(..., NULL) will always be false, rather than an "implementation-defined default directory," because musl does not have an implementation-defined default directory. One test needs a special case for this. Second is that the musl implementation of bind_textdomain_codeset() always returns NULL. The POSIX-correctness of this is debatable, but it is roughly equivalent to correct, because musl only support UTF-8, so the NULL value indicating that the codeset is unchanged from the locale's codeset (UTF-8) is accurate. PHP's bind_textdomain_codeset() function however treats NULL as failure, unconditionally: * https://github.com/php/doc-en/issues/4311 * https://github.com/php/php-src/issues/17163 This unfortunately causes false to be returned consistently on musl -- even when nothing unexpected has happened -- and naturally this is affecting several tests. For now we change two tests to accept "false" in addition to "UTF-8" so that they may pass on musl. If PHP's bind_textdomain_codeset() is updated to differentiate between NULL and NULL-with-errno-set, these tests can also be updated once again to reject the NULL-with-errno result. This partially addresses GH #13696 --- ext/gettext/tests/bug53251.phpt | 28 +++++++++++++------ ...ettext_bind_textdomain_codeset-retval.phpt | 12 ++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/ext/gettext/tests/bug53251.phpt b/ext/gettext/tests/bug53251.phpt index 6f37642925d37..d568be6bc079a 100644 --- a/ext/gettext/tests/bug53251.phpt +++ b/ext/gettext/tests/bug53251.phpt @@ -8,18 +8,28 @@ if (getenv('SKIP_REPEAT')) die('skip gettext leaks global state across requests' ?> --FILE-- --EXPECT-- bool(true) -bool(true) -bool(false) -string(5) "UTF-8" -string(5) "UTF-8" diff --git a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt index 47c821648fccb..941bab79bffa2 100644 --- a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt +++ b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt @@ -5,13 +5,21 @@ gettext --FILE-- --EXPECT-- bool(false) -string(5) "UTF-8" +bool(true) Done --CREDITS-- Florian Holzhauer fh-pt@fholzhauer.de From 471e94ce612ebf9b2f34490804f32c1ad89b6f33 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 19 Dec 2024 09:20:06 -0500 Subject: [PATCH 3/5] ext/gettext/config.m4: symlink en_US.UTF-8 test bits to en_US for musl The gettext() family of functions under musl does not support codeset suffixes like ".UTF-8", because the only codeset it understands is UTF-8. (Yes, it is annoying that it doesn't support the suffix for the codeset that it does understand; no, I am not in charge.) Thanks to this, we have six failing tests on musl, * FAIL Gettext basic test with en_US locale that should be on nearly every system [ext/gettext/tests/gettext_basic-enus.phpt] * FAIL Test if bindtextdomain() returns string id if no directory path is set( if directory path is 'null') [ext/gettext/tests/gettext_bindtextdomain-cwd.phpt] * FAIL Test dcgettext() functionality [ext/gettext/tests/gettext_dcgettext.phpt] * FAIL Test dgettext() functionality [ext/gettext/tests/gettext_dgettext.phpt] * FAIL Test if dngettext() returns the correct translations (optionally plural). [ext/gettext/tests/gettext_dngettext-plural.phpt] * FAIL Test ngettext() functionality [ext/gettext/tests/gettext_ngettext.phpt] These are all fixed by symlinking the en_US.UTF-8 message data to en_US, where musl is able to find it. This does not make the situation any better for developers (who don't know what libc their users will be running), but that problem is inhereted from C and is not the fault of the gettext extension. This partially addresses GH #13696 --- .gitignore | 3 +++ ext/gettext/config.m4 | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 49acc9f2e1788..449963153f36b 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,9 @@ php /ext/*/configure.ac /ext/*/run-tests.php +# Generated by ./configure if libc might be musl +/ext/gettext/tests/locale/en_US + # ------------------------------------------------------------------------------ # Generated by Windows build system # ------------------------------------------------------------------------------ diff --git a/ext/gettext/config.m4 b/ext/gettext/config.m4 index 9e304d82b8d29..b3a6c35d6c006 100644 --- a/ext/gettext/config.m4 +++ b/ext/gettext/config.m4 @@ -25,6 +25,17 @@ if test "$PHP_GETTEXT" != "no"; then AC_CHECK_LIB(c, bindtextdomain, [ GETTEXT_LIBS= GETTEXT_CHECK_IN_LIB=c + + dnl If libintl.h is provided by libc, it's possible that libc is musl. + dnl The gettext family of functions under musl ignores the codeset + dnl suffix on directories like "en_US.UTF-8"; instead they look only + dnl in "en_US". To accomodate that, we symlink some test data from one + dnl to the other. + AC_MSG_NOTICE([symlinking en_US.UTF-8 messages to en_US in case you are on musl]) + _linkdest="${srcdir%/}"/ext/gettext/tests/locale/en_US + AS_IF([test ! -e "${_linkdest}"],[ + ln -s en_US.UTF-8 "${_linkdest}" + ]) ],[ AC_MSG_ERROR(Unable to find required gettext library) ]) From 33c12e0776a536929f2343bc172cc6d1f19e6b07 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 19 Dec 2024 20:29:35 +0300 Subject: [PATCH 4/5] Update IR IR commit: 79483000c2a4b918221fa3097ca47b48b3519447 --- ext/opcache/jit/ir/ir.c | 11 +++- ext/opcache/jit/ir/ir.h | 44 ++++++++++----- ext/opcache/jit/ir/ir_aarch64.dasc | 7 ++- ext/opcache/jit/ir/ir_cfg.c | 42 ++++++++++++-- ext/opcache/jit/ir/ir_check.c | 4 ++ ext/opcache/jit/ir/ir_gcm.c | 13 +++-- ext/opcache/jit/ir/ir_private.h | 1 + ext/opcache/jit/ir/ir_x86.dasc | 91 +++++++++++++++++++++++++----- 8 files changed, 170 insertions(+), 43 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index c45b1efc21eeb..17c3954591693 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -330,6 +330,7 @@ static ir_ref ir_next_const(ir_ctx *ctx) static void ir_grow_top(ir_ctx *ctx) { + ir_ref old_insns_limit = ctx->insns_limit; ir_insn *buf = ctx->ir_base - ctx->consts_limit; if (ctx->insns_limit < 1024 * 4) { @@ -341,6 +342,12 @@ static void ir_grow_top(ir_ctx *ctx) } buf = ir_mem_realloc(buf, (ctx->consts_limit + ctx->insns_limit) * sizeof(ir_insn)); ctx->ir_base = buf + ctx->consts_limit; + + if (ctx->use_lists) { + ctx->use_lists = ir_mem_realloc(ctx->use_lists, ctx->insns_limit * sizeof(ir_use_list)); + memset(ctx->use_lists + old_insns_limit, 0, + (ctx->insns_limit - old_insns_limit) * sizeof(ir_use_list)); + } } static ir_ref ir_next_insn(ir_ctx *ctx) @@ -1152,7 +1159,7 @@ void ir_build_def_use_lists(ir_ctx *ctx) ir_ref n, i, j, *p, def; ir_insn *insn; uint32_t edges_count; - ir_use_list *lists = ir_mem_calloc(ctx->insns_count, sizeof(ir_use_list)); + ir_use_list *lists = ir_mem_calloc(ctx->insns_limit, sizeof(ir_use_list)); ir_ref *edges; ir_use_list *use_list; @@ -1207,7 +1214,7 @@ void ir_build_def_use_lists(ir_ctx *ctx) ir_ref n, i, j, *p, def; ir_insn *insn; size_t linked_lists_size, linked_lists_top = 0, edges_count = 0; - ir_use_list *lists = ir_mem_calloc(ctx->insns_count, sizeof(ir_use_list)); + ir_use_list *lists = ir_mem_calloc(ctx->insns_limit, sizeof(ir_use_list)); ir_ref *edges; ir_use_list *use_list; ir_ref *linked_lists; diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index c4f0926e08589..433c59472f560 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -528,11 +528,12 @@ void ir_strtab_free(ir_strtab *strtab); #define IR_OPT_INLINE (1<<16) #define IR_OPT_FOLDING (1<<17) #define IR_OPT_CFG (1<<18) /* merge BBs, by remove END->BEGIN nodes during CFG construction */ -#define IR_OPT_CODEGEN (1<<19) -#define IR_GEN_NATIVE (1<<20) -#define IR_GEN_CODE (1<<21) /* C or LLVM */ +#define IR_OPT_MEM2SSA (1<<19) +#define IR_OPT_CODEGEN (1<<20) +#define IR_GEN_NATIVE (1<<21) +#define IR_GEN_CODE (1<<22) /* C or LLVM */ -#define IR_GEN_CACHE_DEMOTE (1<<22) /* Demote the generated code from closest CPU caches */ +#define IR_GEN_CACHE_DEMOTE (1<<23) /* Demote the generated code from closest CPU caches */ /* debug related */ #ifdef IR_DEBUG @@ -751,13 +752,15 @@ ir_ref ir_binding_find(const ir_ctx *ctx, ir_ref ref); /* Def -> Use lists */ void ir_build_def_use_lists(ir_ctx *ctx); +/* SSA Construction */ +int ir_mem2ssa(ir_ctx *ctx); + /* CFG - Control Flow Graph (implementation in ir_cfg.c) */ int ir_build_cfg(ir_ctx *ctx); -int ir_remove_unreachable_blocks(ir_ctx *ctx); int ir_build_dominators_tree(ir_ctx *ctx); int ir_find_loops(ir_ctx *ctx); int ir_schedule_blocks(ir_ctx *ctx); -void ir_build_prev_refs(ir_ctx *ctx); +void ir_reset_cfg(ir_ctx *ctx); /* SCCP - Sparse Conditional Constant Propagation (implementation in ir_sccp.c) */ int ir_sccp(ir_ctx *ctx); @@ -929,7 +932,7 @@ IR_ALWAYS_INLINE void *ir_jit_compile(ir_ctx *ctx, int opt_level, size_t *size) } return ir_emit_code(ctx, size); - } else if (opt_level == 1 || opt_level == 2) { + } else if (opt_level > 0) { if (!(ctx->flags & IR_OPT_FOLDING)) { // IR_ASSERT(0 && "IR_OPT_FOLDING must be set in ir_init() for -O1 and -O2"); return NULL; @@ -938,14 +941,29 @@ IR_ALWAYS_INLINE void *ir_jit_compile(ir_ctx *ctx, int opt_level, size_t *size) ir_build_def_use_lists(ctx); - if (opt_level == 2 - && !ir_sccp(ctx)) { - return NULL; + if (ctx->flags & IR_OPT_MEM2SSA) { + if (!ir_build_cfg(ctx) + || !ir_build_dominators_tree(ctx) + || !ir_mem2ssa(ctx)) { + return NULL; + } } - if (!ir_build_cfg(ctx) - || !ir_build_dominators_tree(ctx) - || !ir_find_loops(ctx) + if (opt_level > 1) { + ir_reset_cfg(ctx); + if (!ir_sccp(ctx)) { + return NULL; + } + } + + if (!ctx->cfg_blocks) { + if (!ir_build_cfg(ctx) + || !ir_build_dominators_tree(ctx)) { + return NULL; + } + } + + if (!ir_find_loops(ctx) || !ir_gcm(ctx) || !ir_schedule(ctx) || !ir_match(ctx) diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 27595ad31248d..6b397f27b31ad 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -1095,6 +1095,8 @@ binop_fp: } } return IR_SKIPPED | IR_NOP; + case IR_NOP: + return IR_SKIPPED | IR_NOP; default: break; } @@ -5603,7 +5605,8 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) } else if (def_flags & IR_USE_MUST_BE_IN_REG) { if (insn->op == IR_VLOAD && ctx->live_intervals[ctx->vregs[i]] - && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1) { + && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1 + && ir_is_same_mem_var(ctx, i, ctx->ir_base[insn->op2].op3)) { /* pass */ } else if (insn->op != IR_PARAM) { reg = ir_get_free_reg(insn->type, available); @@ -5704,7 +5707,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) if (reg != IR_REG_NONE && IR_REGSET_IN(available, reg)) { IR_REGSET_EXCL(available, reg); ctx->regs[i][j] = reg | IR_REG_SPILL_LOAD; - } else if (j > 1 && input == insn->op1 && ctx->regs[i][1] != IR_REG_NONE) { + } else if (IR_IS_FOLDABLE_OP(insn->op) && j > 1 && input == insn->op1 && ctx->regs[i][1] != IR_REG_NONE) { ctx->regs[i][j] = ctx->regs[i][1]; } else if (use_flags & IR_USE_MUST_BE_IN_REG) { reg = ir_get_free_reg(ctx->ir_base[input].type, available); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 0a36d5d9880d7..7a71208d7823c 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -8,6 +8,8 @@ #include "ir.h" #include "ir_private.h" +static int ir_remove_unreachable_blocks(ir_ctx *ctx); + IR_ALWAYS_INLINE void _ir_add_successors(const ir_ctx *ctx, ir_ref ref, ir_worklist *worklist) { ir_use_list *use_list = &ctx->use_lists[ref]; @@ -57,6 +59,24 @@ IR_ALWAYS_INLINE void _ir_add_predecessors(const ir_insn *insn, ir_worklist *wor } } +void ir_reset_cfg(ir_ctx *ctx) +{ + ctx->cfg_blocks_count = 0; + ctx->cfg_edges_count = 0; + if (ctx->cfg_blocks) { + ir_mem_free(ctx->cfg_blocks); + ctx->cfg_blocks = NULL; + if (ctx->cfg_edges) { + ir_mem_free(ctx->cfg_edges); + ctx->cfg_edges = NULL; + } + if (ctx->cfg_map) { + ir_mem_free(ctx->cfg_map); + ctx->cfg_map = NULL; + } + } +} + int ir_build_cfg(ir_ctx *ctx) { ir_ref n, *p, ref, start, end; @@ -330,11 +350,15 @@ static void ir_remove_merge_input(ir_ctx *ctx, ir_ref merge, ir_ref from) } } i--; + for (j = i + 1; j <= n; j++) { + ir_insn_set_op(insn, j, IR_UNUSED); + } if (i == 1) { insn->op = IR_BEGIN; insn->inputs_count = 1; use_list = &ctx->use_lists[merge]; if (use_list->count > 1) { + n++; for (k = 0, p = &ctx->use_edges[use_list->refs]; k < use_list->count; k++, p++) { use = *p; use_insn = &ctx->ir_base[use]; @@ -347,12 +371,14 @@ static void ir_remove_merge_input(ir_ctx *ctx, ir_ref merge, ir_ref from) if (ir_bitset_in(life_inputs, j - 1)) { use_insn->op1 = ir_insn_op(use_insn, j); } else if (input > 0) { - ir_use_list_remove_all(ctx, input, use); + ir_use_list_remove_one(ctx, input, use); } } use_insn->op = IR_COPY; - use_insn->op2 = IR_UNUSED; - use_insn->op3 = IR_UNUSED; + use_insn->inputs_count = 1; + for (j = 2; j <= n; j++) { + ir_insn_set_op(use_insn, j, IR_UNUSED); + } ir_use_list_remove_all(ctx, merge, use); } } @@ -360,9 +386,9 @@ static void ir_remove_merge_input(ir_ctx *ctx, ir_ref merge, ir_ref from) } else { insn->inputs_count = i; - n++; use_list = &ctx->use_lists[merge]; if (use_list->count > 1) { + n++; for (k = 0, p = &ctx->use_edges[use_list->refs]; k < use_list->count; k++, p++) { use = *p; use_insn = &ctx->ir_base[use]; @@ -378,9 +404,13 @@ static void ir_remove_merge_input(ir_ctx *ctx, ir_ref merge, ir_ref from) } i++; } else if (input > 0) { - ir_use_list_remove_all(ctx, input, use); + ir_use_list_remove_one(ctx, input, use); } } + use_insn->inputs_count = i - 1; + for (j = i; j <= n; j++) { + ir_insn_set_op(use_insn, j, IR_UNUSED); + } } } } @@ -390,7 +420,7 @@ static void ir_remove_merge_input(ir_ctx *ctx, ir_ref merge, ir_ref from) } /* CFG constructed after SCCP pass doesn't have unreachable BBs, otherwise they should be removed */ -int ir_remove_unreachable_blocks(ir_ctx *ctx) +static int ir_remove_unreachable_blocks(ir_ctx *ctx) { uint32_t b, *p, i; uint32_t unreachable_count = 0; diff --git a/ext/opcache/jit/ir/ir_check.c b/ext/opcache/jit/ir/ir_check.c index f41957a264782..656f8dbe7c1a2 100644 --- a/ext/opcache/jit/ir/ir_check.c +++ b/ext/opcache/jit/ir/ir_check.c @@ -362,6 +362,10 @@ bool ir_check(const ir_ctx *ctx) break; } } + if (count == 0 && (insn->op == IR_END || insn->op == IR_LOOP_END)) { + /* Dead block */ + break; + } fprintf(stderr, "ir_base[%d].op (%s) must have 1 successor (%d)\n", i, ir_op_name[insn->op], count); ok = 0; diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index ed1cd7e39be78..12103a174d07c 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -356,9 +356,11 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) } } else { j = i = ctx->cfg_map[use]; - IR_ASSERT(i > 0); - while (ir_sparse_set_in(&data->totally_useful, ctx->cfg_blocks[j].idom)) { - j = ctx->cfg_blocks[j].idom; + if (i) { + IR_ASSERT(i > 0); + while (ir_sparse_set_in(&data->totally_useful, ctx->cfg_blocks[j].idom)) { + j = ctx->cfg_blocks[j].idom; + } } clone = ir_hashtab_find(&hash, j); if (clone == IR_INVALID_VAL) { @@ -941,8 +943,9 @@ int ir_schedule(ir_ctx *ctx) for (p = &ctx->use_edges[use_list->refs]; count > 0; p++, count--) { ir_ref use = *p; - if (!_xlat[use]) { - ir_insn *use_insn = &ctx->ir_base[use]; + ir_insn *use_insn = &ctx->ir_base[use]; + if (!_xlat[use] && (_blocks[use] || use_insn->op == IR_PARAM)) { + IR_ASSERT(_blocks[use] == b || use_insn->op == IR_PARAM); if (use_insn->op == IR_PARAM || use_insn->op == IR_VAR || use_insn->op == IR_PI diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index fe4a79426862f..f88ba75496921 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -1120,6 +1120,7 @@ struct _ir_block { uint32_t loop_depth; }; +void ir_build_prev_refs(ir_ctx *ctx); uint32_t ir_skip_empty_target_blocks(const ir_ctx *ctx, uint32_t b); uint32_t ir_next_block(const ir_ctx *ctx, uint32_t b); void ir_get_true_false_blocks(const ir_ctx *ctx, uint32_t b, uint32_t *true_block, uint32_t *false_block); diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index 284e1480d3835..bc90dc5966ab5 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1456,11 +1456,29 @@ op2_const: case IR_SEXT: case IR_ZEXT: case IR_TRUNC: - case IR_BITCAST: case IR_PROTO: case IR_FP2FP: flags = IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; break; + case IR_BITCAST: + insn = &ctx->ir_base[ref]; + if (IR_IS_TYPE_INT(insn->type) && IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) { + flags = IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; + } else { + flags = IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; + } + break; + case IR_FP2INT: + flags = IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; + break; + case IR_INT2FP: + flags = IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; + insn = &ctx->ir_base[ref]; + if (IR_IS_CONST_REF(insn->op1)) { + constraints->tmp_regs[0] = IR_TMP_REG(1, ctx->ir_base[insn->op1].type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n = 1; + } + break; case IR_ABS_INT: flags = IR_DEF_CONFLICTS_WITH_INPUT_REGS | IR_USE_MUST_BE_IN_REG | IR_OP1_MUST_BE_IN_REG; break; @@ -2878,6 +2896,8 @@ store_int: } } return IR_SKIPPED | IR_NOP; + case IR_NOP: + return IR_SKIPPED | IR_NOP; default: break; } @@ -6202,20 +6222,33 @@ static void ir_emit_cond(ir_ctx *ctx, ir_ref def, ir_insn *insn) IR_ASSERT(def_reg != IR_REG_NONE); - if (op2_reg != IR_REG_NONE && IR_REG_SPILLED(op2_reg)) { + if (op2 != op3) { + if (op2_reg != IR_REG_NONE && IR_REG_SPILLED(op2_reg)) { + op2_reg = IR_REG_NUM(op2_reg); + ir_emit_load(ctx, type, op2_reg, op2); + if (op1 == op2) { + op1_reg = op2_reg; + } + } + if (op3_reg != IR_REG_NONE && IR_REG_SPILLED(op3_reg)) { + op3_reg = IR_REG_NUM(op3_reg); + ir_emit_load(ctx, type, op3_reg, op3); + if (op1 == op2) { + op1_reg = op3_reg; + } + } + } else if (op2_reg != IR_REG_NONE && IR_REG_SPILLED(op2_reg)) { op2_reg = IR_REG_NUM(op2_reg); ir_emit_load(ctx, type, op2_reg, op2); + op3_reg = op2_reg; if (op1 == op2) { op1_reg = op2_reg; } - if (op3 == op2) { - op3_reg = op2_reg; - } - } - if (op3_reg != IR_REG_NONE && op3 != op2 && IR_REG_SPILLED(op3_reg)) { + } else if (op3_reg != IR_REG_NONE && IR_REG_SPILLED(op3_reg)) { op3_reg = IR_REG_NUM(op3_reg); ir_emit_load(ctx, type, op3_reg, op3); - if (op1 == op2) { + op2_reg = op3_reg; + if (op1 == op3) { op1_reg = op3_reg; } } @@ -6710,7 +6743,19 @@ static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn) |.endif } } else if (IR_IS_CONST_REF(insn->op1)) { - IR_ASSERT(0); + int64_t val; + + if (ir_type_size[src_type] == 1) { + val = ctx->ir_base[insn->op1].val.i8; + } else if (ir_type_size[src_type] == 2) { + val = ctx->ir_base[insn->op1].val.i16; + } else if (ir_type_size[src_type] == 4) { + val = ctx->ir_base[insn->op1].val.i32; + } else { + IR_ASSERT(ir_type_size[src_type] == 8); + val = ctx->ir_base[insn->op1].val.i64; + } + ir_emit_mov_imm_int(ctx, dst_type, def_reg, val); } else { ir_mem mem; @@ -6809,7 +6854,19 @@ static void ir_emit_zext(ir_ctx *ctx, ir_ref def, ir_insn *insn) |.endif } } else if (IR_IS_CONST_REF(insn->op1)) { - IR_ASSERT(0); + uint64_t val; + + if (ir_type_size[src_type] == 1) { + val = ctx->ir_base[insn->op1].val.u8; + } else if (ir_type_size[src_type] == 2) { + val = ctx->ir_base[insn->op1].val.u16; + } else if (ir_type_size[src_type] == 4) { + val = ctx->ir_base[insn->op1].val.u32; + } else { + IR_ASSERT(ir_type_size[src_type] == 8); + val = ctx->ir_base[insn->op1].val.u64; + } + ir_emit_mov_imm_int(ctx, dst_type, def_reg, val); } else { ir_mem mem; @@ -7117,6 +7174,8 @@ static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) } |.endif } + } else if (IR_IS_CONST_REF(insn->op1)) { + IR_ASSERT(0); } else { ir_mem mem; bool src64 = ir_type_size[src_type] == 8; @@ -9559,7 +9618,7 @@ static void ir_emit_sse_sqrt(ir_ctx *ctx, ir_ref def, ir_insn *insn) if (IR_REG_SPILLED(op3_reg)) { op3_reg = IR_REG_NUM(op3_reg); - ir_emit_load(ctx, IR_ADDR, op3_reg, insn->op3); + ir_emit_load(ctx, insn->type, op3_reg, insn->op3); } | ASM_FP_REG_REG_OP sqrts, insn->type, def_reg, op3_reg @@ -9581,7 +9640,7 @@ static void ir_emit_sse_round(ir_ctx *ctx, ir_ref def, ir_insn *insn, int round_ if (IR_REG_SPILLED(op3_reg)) { op3_reg = IR_REG_NUM(op3_reg); - ir_emit_load(ctx, IR_ADDR, op3_reg, insn->op3); + ir_emit_load(ctx, insn->type, op3_reg, insn->op3); } if (ctx->mflags & IR_X86_AVX) { @@ -10019,7 +10078,8 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) } else if (def_flags & IR_USE_MUST_BE_IN_REG) { if (insn->op == IR_VLOAD && ctx->live_intervals[ctx->vregs[i]] - && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1) { + && ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1 + && ir_is_same_mem_var(ctx, i, ctx->ir_base[insn->op2].op3)) { /* pass */ } else if (insn->op != IR_PARAM) { reg = ir_get_free_reg(insn->type, available); @@ -10120,7 +10180,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) if (reg != IR_REG_NONE && IR_REGSET_IN(available, reg)) { IR_REGSET_EXCL(available, reg); ctx->regs[i][j] = reg | IR_REG_SPILL_LOAD; - } else if (j > 1 && input == insn->op1 && ctx->regs[i][1] != IR_REG_NONE) { + } else if (IR_IS_FOLDABLE_OP(insn->op) && j > 1 && input == insn->op1 && ctx->regs[i][1] != IR_REG_NONE) { ctx->regs[i][j] = ctx->regs[i][1]; } else if (use_flags & IR_USE_MUST_BE_IN_REG) { reg = ir_get_free_reg(ctx->ir_base[input].type, available); @@ -11018,7 +11078,8 @@ void ir_fix_thunk(void *thunk_entry, void *addr) addr_ptr = (void**)(code + 6 + *offset_ptr); *addr_ptr = addr; } else { - int32_t *addr_ptr; + typedef IR_SET_ALIGNED(1, int32_t unaligned_int32_t); + unaligned_int32_t *addr_ptr; code[0] = 0xe9; addr_ptr = (int32_t*)(code + 1); From a23ecc0a7592621916e974048b0f918c39478c5c Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 19 Dec 2024 18:30:17 +0100 Subject: [PATCH 5/5] NEWS for GH-17168 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 5c6b7bbe4f616..00332140e87c5 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c). (nielsdos, cmb) +- Gettext: + . Fixed bug GH-17202 (Segmentation fault ext/gettext/gettext.c + bindtextdomain()). (Michael Orlitzky) + - Iconv: . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos)