return 1;
}
-static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
+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)
{
HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
+ const zend_op *next_opline = NULL;
+
+ if (trace) {
+ ZEND_ASSERT(trace->op == ZEND_JIT_TRACE_VM || trace->op == ZEND_JIT_TRACE_END);
+ ZEND_ASSERT(trace->opline != NULL);
+ next_opline = trace->opline;
+ }
// TODO: Implement for match instructions
if (opline->opcode == ZEND_MATCH) {
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 (jump_zv != NULL) {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
+ if (next_opline) {
+ const zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
+
+ ZEND_ASSERT(target == next_opline);
} else {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
+ 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
}
- | 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 (jump_zv != NULL) {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
+ if (next_opline) {
+ const zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
+
+ ZEND_ASSERT(target == next_opline);
} else {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
+ 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
}
- | jmp =>b
}
} else {
ZEND_UNREACHABLE();
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
uint32_t op1_info = OP1_INFO();
zend_jit_addr op1_addr = OP1_ADDR();
- int b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
+ const zend_op *default_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
+ const zend_op *target;
+ int default_b = 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;
+ const void *exit_addr;
+
+ if (next_opline) {
+ if (next_opline != opline + 1) {
+ exit_point = zend_jit_trace_get_exit_point(opline, opline + 1, NULL, 0);
+ fallback_label = zend_jit_trace_get_exit_addr(exit_point);
+ }
+ if (next_opline != default_opline) {
+ exit_point = zend_jit_trace_get_exit_point(opline, default_opline, NULL, 0);
+ default_label = zend_jit_trace_get_exit_addr(exit_point);
+ }
+ }
if (opline->opcode == ZEND_SWITCH_LONG) {
if (op1_info & MAY_BE_LONG) {
|.cold_code
|1:
| // ZVAL_DEREF(op)
- | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
+ if (fallback_label) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label
+ } else {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
+ }
| GET_ZVAL_PTR FCARG2a, op1_addr
- | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, >3
+ if (fallback_label) {
+ | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, &fallback_label
+ } else {
+ | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_LONG, >3
+ }
| mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.lval)]
| jmp >2
|.code
|2:
} else {
if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) {
- | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
+ if (fallback_label) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, &fallback_label
+ } else {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_LONG, >3
+ }
}
| GET_ZVAL_LVAL ZREG_FCARG2a, op1_addr
}
Bucket *p = jumptable->arData;
| cmp FCARG2a, jumptable->nNumUsed
- | jae >3
+ if (default_label) {
+ | jae &default_label
+ } else if (next_opline) {
+ | jae >3
+ } else {
+ | jae =>default_b
+ }
|.if X64
if (!IS_32BIT(dasm_end)) {
| lea r0, aword [>4]
|.else
| jmp aword [FCARG2a * 4 + >4]
|.endif
- |3:
|.cold_code
|.align aword
|4:
p = jumptable->arData;
do {
if (Z_TYPE(p->val) == IS_UNDEF) {
- | .aword =>b
+ if (default_label) {
+ | .aword &default_label
+ } else if (next_opline) {
+ | .aword >3
+ } else {
+ | .aword =>default_b
+ }
} else {
- int b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL(p->val)) - op_array->opcodes];
- | .aword =>b
+ 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(opline, target, NULL, 0);
+ exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ | .aword &exit_addr
+ }
}
p++;
count--;
} while (count);
|.code
+ |3:
} else {
| LOAD_ADDR FCARG1a, jumptable
| EXT_CALL zend_hash_index_find, r0
| test r0, r0
- | jz =>b
+ 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*))
|.else
| jmp aword [r0 + >4]
|.endif
- |3:
|.cold_code
|.align aword
|4:
ZEND_HASH_FOREACH_VAL(jumptable, val) {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(val)) - op_array->opcodes];
- | .aword =>b
+ 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(opline, target, NULL, 0);
+ exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ | .aword &exit_addr
+ }
} ZEND_HASH_FOREACH_END();
|.code
+ |3:
}
}
} else if (opline->opcode == ZEND_SWITCH_STRING) {
|.cold_code
|1:
| // ZVAL_DEREF(op)
- | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
+ if (fallback_label) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, &fallback_label
+ } else {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_REFERENCE, >3
+ }
| GET_ZVAL_PTR FCARG2a, op1_addr
- | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, >3
+ if (fallback_label) {
+ | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, &fallback_label
+ } else {
+ | IF_NOT_Z_TYPE FCARG2a + offsetof(zend_reference, val), IS_STRING, >3
+ }
| mov FCARG2a, aword [FCARG2a + offsetof(zend_reference, val.value.ptr)]
| jmp >2
|.code
|2:
} else {
if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_STRING)) {
- | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
+ if (fallback_label) {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, &fallback_label
+ } else {
+ | IF_NOT_ZVAL_TYPE op1_addr, IS_STRING, >3
+ }
}
| GET_ZVAL_PTR FCARG2a, op1_addr
}
| LOAD_ADDR FCARG1a, jumptable
| EXT_CALL zend_hash_find, r0
| test r0, r0
- | jz =>b
+ 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*))
|.else
| jmp aword [r0 + >4]
|.endif
- |3:
|.cold_code
|.align aword
|4:
ZEND_HASH_FOREACH_VAL(jumptable, val) {
- b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(val)) - op_array->opcodes];
- | .aword =>b
+ 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(opline, target, NULL, 0);
+ exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ | .aword &exit_addr
+ }
} ZEND_HASH_FOREACH_END();
|.code
+ |3:
}
} else {
ZEND_UNREACHABLE();