Commit 89bddd8399c for php
commit 89bddd8399cf1354c7efa29b07011183d783713a
Author: Nora Dossche <7771979+ndossche@users.noreply.github.com>
Date: Fri Sep 25 17:45:47 2026 +0200
Implement fast path for string accesses with long dimensions in JIT (#23767)
diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index c7716eb96ff..627b1f9437a 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -12749,25 +12749,62 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit,
ir_IF_TRUE(if_type);
}
}
- jit_SET_EX_OPLINE(jit, opline);
str_ref = jit_Z_PTR(jit, op1_addr);
- if (opline->opcode != ZEND_FETCH_DIM_IS) {
- ir_ref ref;
- if ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
- ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_offset_r_helper),
- str_ref, jit_Z_LVAL(jit, op2_addr));
- } else {
- ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_r_helper),
- str_ref, jit_ZVAL_ADDR(jit, op2_addr));
- }
+ /* Inlined offset read for integer offsets into strings. */
+ if (opline->opcode != ZEND_FETCH_DIM_IS
+ && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING
+ && (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG) {
+ ir_ref offset_ref = jit_Z_LVAL(jit, op2_addr);
+ ir_ref len_ref = ir_LOAD_L(ir_ADD_OFFSET(str_ref, offsetof(zend_string, len)));
+ ir_ref real_offset_ref = offset_ref;
+ if (!op2_range || op2_range->min < 0) {
+ // JIT: if (offset < 0) offset += ZSTR_LEN(str);
+ /* Branchless way to add -1 for negative offsets to the string length. */
+ real_offset_ref = ir_ADD_L(offset_ref,
+ ir_AND_L(len_ref,
+ ir_SAR_L(offset_ref, ir_CONST_LONG(SIZEOF_ZEND_LONG * 8 - 1))));
+ }
+
+ /* An offset that is still negative wraps around and fails this check as well. */
+ ir_ref if_in_range = ir_IF(ir_ULT(real_offset_ref, len_ref));
+
+ ir_IF_TRUE(if_in_range);
+ // JIT: result = ZSTR_CHAR((uint8_t)ZSTR_VAL(str)[offset]);
+ ir_ref ref = ir_LOAD_A(
+ ir_ADD_A(
+ ir_CONST_ADDR(zend_one_char_string),
+ ir_MUL_A(
+ ir_ZEXT_A(
+ ir_LOAD_U8(
+ ir_ADD_A(ir_ADD_OFFSET(str_ref, offsetof(zend_string, val)),
+ ir_BITCAST_A(real_offset_ref)))),
+ ir_CONST_ADDR(sizeof(zend_string*)))));
+ ir_ref fast_path = ir_END();
+
+ ir_IF_FALSE_cold(if_in_range);
+ jit_SET_EX_OPLINE(jit, opline);
+ ir_ref slow_ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_offset_r_helper),
+ str_ref, offset_ref);
+
+ ir_MERGE_WITH(fast_path);
+ ref = ir_PHI_2(IR_ADDR, slow_ref, ref);
jit_set_Z_PTR(jit, res_addr, ref);
jit_set_Z_TYPE_INFO(jit, res_addr, IS_STRING);
} else {
- ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_is_helper),
- str_ref,
- jit_ZVAL_ADDR(jit, op2_addr),
- jit_ZVAL_ADDR(jit, res_addr));
+ jit_SET_EX_OPLINE(jit, opline);
+ if (opline->opcode != ZEND_FETCH_DIM_IS) {
+ ir_ref ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_r_helper),
+ str_ref, jit_ZVAL_ADDR(jit, op2_addr));
+
+ jit_set_Z_PTR(jit, res_addr, ref);
+ jit_set_Z_TYPE_INFO(jit, res_addr, IS_STRING);
+ } else {
+ ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_dim_str_is_helper),
+ str_ref,
+ jit_ZVAL_ADDR(jit, op2_addr),
+ jit_ZVAL_ADDR(jit, res_addr));
+ }
}
ir_END_list(end_inputs);
}