diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index ad785d3b891..d01a8c41359 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -650,6 +650,26 @@ IR_ALWAYS_INLINE ir_mem IR_MEM(ir_reg base, int32_t offset, ir_reg index, int32_ || } |.endmacro +/* Like ASM_REG_IMM_OP, but op1 accepts r16,r32,r64 (not r8) */ +|.macro ASM_REG16_IMM_OP, op, type, op1, op2 +|| switch (ir_type_size[type]) { +|| default: +|| IR_ASSERT(0); +|| case 1: +|| case 2: +| op Rw(op1), (op2 & 0xffff) +|| break; +|| case 4: +| op Rd(op1), op2 +|| break; +|.if X64 +|| case 8: +| op Rq(op1), op2 +|| break; +|.endif +|| } +|.endmacro + |.macro ASM_MEM_REG_OP, op, type, op1, op2 | ASM_EXPAND_OP1_MEM ASM_EXPAND_TYPE_MEM_REG, op, type, op1, op2 |.endmacro @@ -1066,6 +1086,7 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(SSE_CEIL) \ _(SSE_TRUNC) \ _(SSE_NEARBYINT) \ + _(BIT_OP) \ #define IR_LEA_FIRST IR_LEA_OB #define IR_LEA_LAST IR_LEA_O_SYM @@ -1400,6 +1421,7 @@ op2_const: case IR_DIV_PWR2: case IR_OP_INT: case IR_OP_FP: + case IR_BIT_OP: flags = IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; break; case IR_MOD_PWR2: @@ -2280,6 +2302,9 @@ binop_fp: // return IR_COPY_INT; } else if (op2_insn->val.i64 == -1) { // -1 + } else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64) && !IR_IS_SIGNED_32BIT(op2_insn->val.i64)) { + /* OR(X, PWR2) => BTS */ + return IR_BIT_OP; } } goto binop_int; @@ -2294,6 +2319,9 @@ binop_fp: // 0 } else if (op2_insn->val.i64 == -1) { // return IR_COPY_INT; + } else if (IR_IS_POWER_OF_TWO(~op2_insn->val.u64) && !IR_IS_SIGNED_32BIT(op2_insn->val.i64)) { + /* AND(X, ~PWR2) => BTR */ + return IR_BIT_OP; } } goto binop_int; @@ -4341,6 +4369,45 @@ static void ir_emit_mul_div_mod_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn) } } +static void ir_emit_bit_op(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_ref op1 = insn->op1; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); + ir_reg op1_reg = ctx->regs[def][1]; + + IR_ASSERT(IR_IS_CONST_REF(insn->op2)); + IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[insn->op2].op)); + IR_ASSERT(def_reg != IR_REG_NONE); + + if (op1_reg != IR_REG_NONE && IR_REG_SPILLED(op1_reg)) { + op1_reg = IR_REG_NUM(op1_reg); + ir_emit_load(ctx, type, op1_reg, op1); + } + if (def_reg != op1_reg) { + if (op1_reg != IR_REG_NONE) { + ir_emit_mov(ctx, type, def_reg, op1_reg); + } else { + ir_emit_load(ctx, type, def_reg, op1); + } + } + if (insn->op == IR_OR) { + uint32_t bit = IR_LOG2(ctx->ir_base[insn->op2].val.u64); + + | ASM_REG16_IMM_OP, bts, type, def_reg, bit + } else { + IR_ASSERT(insn->op == IR_AND); + uint32_t bit = IR_LOG2(~ctx->ir_base[insn->op2].val.u64); + + | ASM_REG16_IMM_OP, btr, type, def_reg, bit + } + if (IR_REG_SPILLED(ctx->regs[def][0])) { + ir_emit_store(ctx, type, def, def_reg); + } +} + static void ir_emit_sdiv_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn) { ir_backend_data *data = ctx->data; @@ -10668,6 +10735,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) case IR_MOD_PWR2: ir_emit_mul_div_mod_pwr2(ctx, i, insn); break; + case IR_BIT_OP: + ir_emit_bit_op(ctx, i, insn); + break; case IR_SDIV_PWR2: ir_emit_sdiv_pwr2(ctx, i, insn); break;