From: Dmitry Stogov Date: Mon, 24 Aug 2020 12:22:52 +0000 (+0300) Subject: JIT for MATCH and CASE_STRICT instructions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ef7904b35c55c49f9a94e9baec4cf8a25d5772e4;p=php JIT for MATCH and CASE_STRICT instructions --- diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 2956591490..fdce38f4d6 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2633,6 +2633,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: + case ZEND_CASE_STRICT: if ((opline->result_type & IS_TMP_VAR) && (i + 1) <= end && ((opline+1)->opcode == ZEND_JMPZ diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 6ad0800036..62b0b9d6a1 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -257,6 +257,7 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op case ZEND_IS_SMALLER: case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_CASE: + case ZEND_CASE_STRICT: case ZEND_ISSET_ISEMPTY_CV: case ZEND_ISSET_ISEMPTY_VAR: case ZEND_ISSET_ISEMPTY_DIM_OBJ: @@ -1420,6 +1421,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_CASE: case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: + case ZEND_CASE_STRICT: case ZEND_FETCH_DIM_R: case ZEND_FETCH_DIM_IS: case ZEND_BW_OR: @@ -3726,6 +3728,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto done; case ZEND_IS_IDENTICAL: case ZEND_IS_NOT_IDENTICAL: + case ZEND_CASE_STRICT: op1_info = OP1_INFO(); CHECK_OP1_TRACE_TYPE(); op2_info = OP2_INFO(); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 49459267e7..ffe96e65bb 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -7197,7 +7197,7 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t uint32_t not_identical_label = (uint32_t)-1; if (smart_branch_opcode && !exit_addr) { - if (opline->opcode == ZEND_IS_IDENTICAL) { + if (opline->opcode != ZEND_IS_NOT_IDENTICAL) { if (smart_branch_opcode == ZEND_JMPZ) { not_identical_label = target_label; } else if (smart_branch_opcode == ZEND_JMPNZ) { @@ -7208,7 +7208,7 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t } else { ZEND_UNREACHABLE(); } - } else if (opline->opcode == ZEND_IS_NOT_IDENTICAL) { + } else { if (smart_branch_opcode == ZEND_JMPZ) { identical_label = target_label; } else if (smart_branch_opcode == ZEND_JMPNZ) { @@ -7219,8 +7219,6 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t } else { ZEND_UNREACHABLE(); } - } else { - ZEND_UNREACHABLE(); } } @@ -7343,12 +7341,15 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t } if ((op1_info & op2_info & MAY_BE_ANY) == 0) { - if (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && + if ((opline->opcode != ZEND_CASE_STRICT && + (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) || ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) { | SAVE_VALID_OPLINE opline, r0 - | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline + if (opline->opcode != ZEND_CASE_STRICT) { + | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline + } | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline } if (smart_branch_opcode) { @@ -7458,7 +7459,8 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t | cmp byte [FCARG1a + offsetof(zval, u1.v.type)], Z_TYPE_P(val) if (smart_branch_opcode) { - if (opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) { + if (opline->opcode != ZEND_CASE_STRICT + && opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) { | jne >8 | SAVE_VALID_OPLINE opline, r0 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline @@ -7488,7 +7490,8 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t | lea eax, [eax + 2] | SET_ZVAL_TYPE_INFO res_addr, eax } - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && + if (opline->opcode != ZEND_CASE_STRICT + && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { | SAVE_VALID_OPLINE opline, r0 | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline @@ -7511,13 +7514,16 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t | LOAD_ZVAL_ADDR FCARG2a, op2_addr } | EXT_CALL zend_is_identical, r0 - if (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && + if ((opline->opcode != ZEND_CASE_STRICT && + (opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) || ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) { | mov aword T1, r0 // save | SAVE_VALID_OPLINE opline, r0 - | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline + if (opline->opcode != ZEND_CASE_STRICT) { + | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline + } | FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline zend_jit_check_exception_undef_result(Dst, opline); | mov r0, aword T1 // restore @@ -11840,6 +11846,81 @@ static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const ze return 1; } +static int zend_jit_hash_jmp(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, HashTable *jumptable, int default_b, const void *default_label, const zend_op *next_opline, zend_jit_trace_info *trace_info) +{ + uint32_t count; + Bucket *p; + const zend_op *target; + int b; + int32_t exit_point; + const void *exit_addr; + + | test r0, r0 + if (default_label) { + | jz &default_label + } else if (next_opline) { + | jz >3 + } else { + | jz =>default_b + } + | LOAD_ADDR FCARG1a, jumptable + | sub r0, aword [FCARG1a + offsetof(HashTable, arData)] + | mov FCARG1a, (sizeof(Bucket) / sizeof(void*)) + |.if X64 + | cqo + |.else + | cdq + |.endif + | idiv FCARG1a + |.if X64 + if (!IS_32BIT(dasm_end)) { + | lea FCARG1a, aword [>4] + | jmp aword [FCARG1a + r0] + } else { + | jmp aword [r0 + >4] + } + |.else + | jmp aword [r0 + >4] + |.endif + |.jmp_table + |.align aword + |4: + if (trace_info) { + trace_info->jmp_table_size += zend_hash_num_elements(jumptable); + } + + count = jumptable->nNumUsed; + p = jumptable->arData; + do { + if (Z_TYPE(p->val) == IS_UNDEF) { + if (default_label) { + | .aword &default_label + } else if (next_opline) { + | .aword >3 + } else { + | .aword =>default_b + } + } else { + target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val)); + if (!next_opline) { + b = ssa->cfg.map[target - op_array->opcodes]; + | .aword =>b + } else if (next_opline == target) { + | .aword >3 + } else { + exit_point = zend_jit_trace_get_exit_point(target, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + | .aword &exit_addr + } + } + p++; + count--; + } while (count); + |.code + + return 1; +} + static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info) { HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2)); @@ -11851,53 +11932,45 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o next_opline = trace->opline; } - // TODO: Implement for match instructions - if (opline->opcode == ZEND_MATCH) { - // Since the match expression doesn't have a IS_IDENTICAL/JMPNZ chain - // we can't skip the jumptable and thus can't JIT the function - return 0; - } - if (opline->op1_type == IS_CONST) { zval *zv = RT_CONSTANT(opline, opline->op1); - zval *jump_zv; + zval *jump_zv = NULL; int b; if (opline->opcode == ZEND_SWITCH_LONG) { if (Z_TYPE_P(zv) == IS_LONG) { jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv)); - if (next_opline) { - const zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); - - ZEND_ASSERT(target == next_opline); - } else { - if (jump_zv != NULL) { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes]; - } else { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes]; - } - | jmp =>b - } } } else if (opline->opcode == ZEND_SWITCH_STRING) { if (Z_TYPE_P(zv) == IS_STRING) { jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(zv), 1); - if (next_opline) { - const zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); - - ZEND_ASSERT(target == next_opline); - } else { - if (jump_zv != NULL) { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes]; - } else { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes]; - } - | jmp =>b - } + } + } else if (opline->opcode == ZEND_MATCH) { + if (Z_TYPE_P(zv) == IS_LONG) { + jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv)); + } else if (Z_TYPE_P(zv) == IS_STRING) { + jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(zv), 1); } } else { ZEND_UNREACHABLE(); } + if (next_opline) { + const zend_op *target; + + if (jump_zv != NULL) { + target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); + } else { + target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); + } + ZEND_ASSERT(target == next_opline); + } else { + if (jump_zv != NULL) { + b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes]; + } else { + b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes]; + } + | jmp =>b + } } else { zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; uint32_t op1_info = OP1_INFO(); @@ -11906,7 +11979,6 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o const zend_op *target; int default_b = next_opline ? -1 : ssa->cfg.map[default_opline - op_array->opcodes]; int b; - zval *val; int32_t exit_point; const void *fallback_label = NULL; const void *default_label = NULL; @@ -12015,53 +12087,9 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o } else { | LOAD_ADDR FCARG1a, jumptable | EXT_CALL zend_hash_index_find, r0 - | test r0, r0 - if (default_label) { - | jz &default_label - } else if (next_opline) { - | jz >3 - } else { - | jz =>default_b - } - | LOAD_ADDR FCARG1a, jumptable - | sub r0, aword [FCARG1a + offsetof(HashTable, arData)] - | mov FCARG1a, (sizeof(Bucket) / sizeof(void*)) - |.if X64 - | cqo - |.else - | cdq - |.endif - | idiv FCARG1a - |.if X64 - if (!IS_32BIT(dasm_end)) { - | lea FCARG1a, aword [>4] - | jmp aword [FCARG1a + r0] - } else { - | jmp aword [r0 + >4] - } - |.else - | jmp aword [r0 + >4] - |.endif - |.jmp_table - |.align aword - |4: - if (trace_info) { - trace_info->jmp_table_size += zend_hash_num_elements(jumptable); + if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { + return 0; } - ZEND_HASH_FOREACH_VAL(jumptable, val) { - target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(val)); - if (!next_opline) { - b = ssa->cfg.map[target - op_array->opcodes]; - | .aword =>b - } else if (next_opline == target) { - | .aword >3 - } else { - exit_point = zend_jit_trace_get_exit_point(target, 0); - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - | .aword &exit_addr - } - } ZEND_HASH_FOREACH_END(); - |.code |3: } } @@ -12100,55 +12128,87 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o } | LOAD_ADDR FCARG1a, jumptable | EXT_CALL zend_hash_find, r0 - | test r0, r0 - if (default_label) { - | jz &default_label - } else if (next_opline) { - | jz >3 - } else { - | jz =>default_b + if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { + return 0; + } + |3: + } + } else if (opline->opcode == ZEND_MATCH) { + if (op1_info & (MAY_BE_LONG|MAY_BE_STRING)) { + if (op1_info & MAY_BE_REF) { + | LOAD_ZVAL_ADDR FCARG2a, op1_addr + | ZVAL_DEREF FCARG2a, op1_info + op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG2a, 0); } | LOAD_ADDR FCARG1a, jumptable - | sub r0, aword [FCARG1a + offsetof(HashTable, arData)] - | mov FCARG1a, (sizeof(Bucket) / sizeof(void*)) - |.if X64 - | cqo - |.else - | cdq - |.endif - | idiv FCARG1a - |.if X64 - if (!IS_32BIT(dasm_end)) { - | lea FCARG1a, aword [>4] - | jmp aword [FCARG1a + r0] - } else { - | jmp aword [r0 + >4] + if (op1_info & MAY_BE_LONG) { + if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { + if (op1_info & MAY_BE_STRING) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >5 + } else if (op1_info & MAY_BE_UNDEF) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >6 + } else if (default_label) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &default_label + } else if (next_opline) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3 + } else { + | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, =>default_b + } + } + | GET_ZVAL_LVAL ZREG_FCARG2a, op1_addr + | EXT_CALL zend_hash_index_find, r0 + if (op1_info & MAY_BE_STRING) { + | jmp >2 + } } - |.else - | jmp aword [r0 + >4] - |.endif - |.jmp_table - |.align aword - |4: - if (trace_info) { - trace_info->jmp_table_size += zend_hash_num_elements(jumptable); - } - ZEND_HASH_FOREACH_VAL(jumptable, val) { - target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(val)); - if (!next_opline) { - b = ssa->cfg.map[target - op_array->opcodes]; - | .aword =>b - } else if (next_opline == target) { - | .aword >3 + if (op1_info & MAY_BE_STRING) { + |5: + if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_STRING))) { + if (op1_info & MAY_BE_UNDEF) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >6 + } else if (default_label) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &default_label + } else if (next_opline) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3 + } else { + | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, =>default_b + } + } + | GET_ZVAL_PTR FCARG2a, op1_addr + | EXT_CALL zend_hash_find, r0 + } + |2: + if (!zend_jit_hash_jmp(Dst, opline, op_array, ssa, jumptable, default_b, default_label, next_opline, trace_info)) { + return 0; + } + } + if (op1_info & MAY_BE_UNDEF) { + |6: + if (op1_info & (MAY_BE_ANY-(MAY_BE_LONG|MAY_BE_STRING))) { + if (default_label) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, &default_label + } else if (next_opline) { + | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >3 } else { - exit_point = zend_jit_trace_get_exit_point(target, 0); - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - | .aword &exit_addr + | IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, =>default_b } - } ZEND_HASH_FOREACH_END(); - |.code - |3: + } + | // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var)))); + | SAVE_VALID_OPLINE opline, r0 + | mov FCARG1d, opline->op1.var + | EXT_CALL zend_jit_undefined_op_helper, r0 + if (!zend_jit_check_exception_undef_result(Dst, opline)) { + return 0; + } } + if (default_label) { + | jmp &default_label + } else if (next_opline) { + | jmp >3 + } else { + | jmp =>default_b + } + |3: } else { ZEND_UNREACHABLE(); }