Skip to content

Commit

Permalink
JIT/AArch64: Support shifted immediate (php#7165)
Browse files Browse the repository at this point in the history
* JIT/AArch64: Support shifted immediate

As pointed out by MikePall in [1], shifted immediate value is supported.
See [2]. For example, `add x0, x1, php#4096` would be encoded by DynASM
into `add x0, x1, #1, lsl php#12` directly.

In this patch, a helper is added to check whether an immediate value is
in the two allowed ranges: (1) 0 to 4095, and (2) LSL php#12 on all the
values from the first range.

Note that this helper works for add/adds/sub/subs/cmp/cmn instructions.

[1] LuaJIT/LuaJIT#718
[2]
https://github.com/LuaJIT/LuaJIT/blob/v2.1/dynasm/dasm_arm64.lua#L342

Change-Id: I4870048b9b8e6c429b73a4803af2a3b2d5ec0fbb

* Deprecatd CMP_IMM/ADD_SUB_IMM and add test cases

Macros CMP_IMM and ADD_SUB_IMM are deprecated and instead we use
this helper to guard the immediate encoding.

Add two 64-bit only test cases, since 64-bit integers are used
and tested inside.

Change-Id: I0b42d4617b40372e2f4ce5b6ad31a4ddb7d89e49
  • Loading branch information
shqking authored Jun 23, 2021
1 parent b83bfb2 commit 3e164de
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 17 deletions.
41 changes: 24 additions & 17 deletions ext/opcache/jit/zend_jit_arm64.dasc
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,10 @@ typedef struct TLVDescriptor TLVDescriptor;

#define IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))

/* Encoding of immediate. TODO: shift mode may be supported in the near future. */
/* Encoding of immediate. */
#define MAX_IMM12 0xfff // maximum value for imm12
#define MAX_IMM16 0xffff // maximum value for imm16
#define CMP_IMM MAX_IMM12 // cmp insn
#define MOVZ_IMM MAX_IMM16 // movz insn
#define ADD_SUB_IMM MAX_IMM12 // add/sub/adds/subs insn
#define LDR_STR_PIMM64 (MAX_IMM12*8) // ldr/str insn for 64-bit register: pimm is imm12 * 8
#define LDR_STR_PIMM32 (MAX_IMM12*4) // ldr/str insn for 32-bit register: pimm is imm12 * 4
#define LDRB_STRB_PIMM MAX_IMM12 // ldrb/strb insn
Expand Down Expand Up @@ -172,6 +170,15 @@ static bool arm64_may_use_adrp(const void *addr)
return 0;
}

/* Determine whether "val" falls into two allowed ranges:
* Range 1: [0, 0xfff]
* Range 2: LSL #12 to Range 1
* Used to guard the immediate encoding for add/adds/sub/subs/cmp/cmn instructions. */
static bool arm64_may_encode_imm12(const int64_t val)
{
return (val >= 0 && (val <= MAX_IMM12 || !(val & 0xffffffffff000fff)));
}

/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */
static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
{
Expand Down Expand Up @@ -364,9 +371,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro CMP_32_WITH_CONST, reg, val, tmp_reg
|| if (val == 0) {
| cmp reg, wzr
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| cmp reg, #val
|| } else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
| cmn reg, #-val
|| } else {
| LOAD_32BIT_VAL tmp_reg, val
Expand All @@ -377,9 +384,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro CMP_64_WITH_CONST_32, reg, val, tmp_reg
|| if (val == 0) {
| cmp reg, xzr
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| cmp reg, #val
|| } else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
| cmn reg, #-val
|| } else {
| LOAD_32BIT_VAL tmp_reg, val
Expand All @@ -390,9 +397,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro CMP_64_WITH_CONST, reg, val, tmp_reg
|| if (val == 0) {
| cmp reg, xzr
|| } else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| cmp reg, #val
|| } else if (((int64_t)(val)) < 0 && ((int64_t)(val)) >= -CMP_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
| cmn reg, #-val
|| } else {
| LOAD_64BIT_VAL tmp_reg, val
Expand All @@ -406,7 +413,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro ADD_SUB_32_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
|| if (val == 0) {
| add_sub_ins dst_reg, src_reg1, wzr
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| add_sub_ins dst_reg, src_reg1, #val
|| } else {
| LOAD_32BIT_VAL tmp_reg, val
Expand All @@ -417,7 +424,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro ADD_SUB_64_WITH_CONST_32, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
|| if (val == 0) {
| add_sub_ins dst_reg, src_reg1, xzr
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| add_sub_ins dst_reg, src_reg1, #val
|| } else {
| LOAD_32BIT_VAL tmp_reg, val
Expand All @@ -428,7 +435,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
|.macro ADD_SUB_64_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
|| if (val == 0) {
| add_sub_ins dst_reg, src_reg1, xzr
|| } else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= ADD_SUB_IMM) {
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
| add_sub_ins dst_reg, src_reg1, #val
|| } else {
| LOAD_64BIT_VAL tmp_reg, val
Expand Down Expand Up @@ -723,7 +730,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)

/* Update IP with 32-bit immediate 'val'. */
|.macro ADD_IP_WITH_CONST, val, tmp_reg
|| ZEND_ASSERT(val >= 0 && val <= ADD_SUB_IMM);
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val)));
|| if (GCC_GLOBAL_REGS) {
| add IP, IP, #val
|| } else {
Expand Down Expand Up @@ -931,7 +938,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
* the computation result is stored back into 'addr'.
* Note: it should be guaranteed that 'val' can be encoded into add/sub instruction. */
|.macro LONG_ADD_SUB_WITH_IMM, add_sub_ins, addr, val, tmp_reg1, tmp_reg2
|| ZEND_ASSERT(val >= 0 && val <= ADD_SUB_IMM);
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(val)));
|| if (Z_MODE(addr) == IS_MEM_ZVAL) {
|| if (((uint32_t)(Z_OFFSET(addr))) > LDR_STR_PIMM64) {
| LOAD_32BIT_VAL tmp_reg2, Z_OFFSET(addr)
Expand Down Expand Up @@ -8260,7 +8267,7 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
}

if (func) {
|| if (used_stack <= ADD_SUB_IMM) {
|| if (arm64_may_encode_imm12((int64_t)used_stack)) {
| MEM_UPDATE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
|| } else {
| LOAD_32BIT_VAL TMP1w, used_stack
Expand Down Expand Up @@ -9104,7 +9111,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
| LOAD_IP_ADDR (func->op_array.opcodes + num_args)
} else {
| ldr REG0, EX->func
|| ZEND_ASSERT((num_args * sizeof(zend_op)) <= ADD_SUB_IMM);
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(num_args * sizeof(zend_op))));
if (GCC_GLOBAL_REGS) {
| ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
if (num_args) {
Expand Down Expand Up @@ -9210,7 +9217,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
| ble >3
| // zval *var = EX_VAR_NUM(num_args);
| add REG1, FP, REG1, lsl #4
|| ZEND_ASSERT(ZEND_CALL_FRAME_SLOT * sizeof(zval) <= ADD_SUB_IMM);
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(ZEND_CALL_FRAME_SLOT * sizeof(zval))));
| add REG1, REG1, #(ZEND_CALL_FRAME_SLOT * sizeof(zval))
|2:
| SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w
Expand Down
67 changes: 67 additions & 0 deletions ext/opcache/tests/jit/add_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
--TEST--
JIT ADD: 007 Addition with immediate values
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_update_protection=0
opcache.jit_buffer_size=1M
opcache.protect_memory=1
--EXTENSIONS--
opcache
--SKIPIF--
<?php
if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
--FILE--
<?php
function foo($a) {
$b = 0;
$c = 31;
$d = 0xfff;
$e = 0x1000;
$f = 0xfff000;
$g = 0xff001; // Cannot be encoded into imm12 field
$h = 0x1000000; // Cannot be encoded into imm12 field
$i = 0xf12345678; // Cannot be encoded into imm12 field
$j = -31; // Cannot be encoded into imm12 field

$a = $a + $b;
$a = $a + $c;
$a = $a + $d;
$a = $a + $e;
$a = $a + $f;
$a = $a + $g;
$a = $a + $h;
$a = $a + $i;
$a = $a + $j;
var_dump($a);
}

function bar($a) {
$b = 0;
$c = 31;
$d = 0xfff;
$e = 0x1000;
$f = 0xfff000;
$g = 0xff001; // Cannot be encoded into imm12 field
$h = 0x1000000; // Cannot be encoded into imm12 field
$i = 0xf12345678; // Cannot be encoded into imm12 field
$j = -31; // Cannot be encoded into imm12 field

$a = $a - $b;
$a = $a - $c;
$a = $a - $d;
$a = $a - $e;
$a = $a - $f;
$a = $a - $g;
$a = $a - $h;
$a = $a - $i;
$a = $a - $j;
var_dump($a);
}

foo(42);
bar(0x1f12345678);
?>
--EXPECT--
int(64764532386)
int(68684873728)
75 changes: 75 additions & 0 deletions ext/opcache/tests/jit/cmp_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
--TEST--
JIT CMP: 005 Comparisons with immediate values
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_update_protection=0
opcache.jit_buffer_size=1M
opcache.protect_memory=1
--EXTENSIONS--
opcache
--SKIPIF--
<?php
if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
--FILE--
<?php
function foo($a) {
$b = 0;
$c = 31;
$d = 0xfff;
$e = 0x1000;
$f = 0xfff000;
$g = 0xff001; // Cannot be encoded into imm12 field
$h = 0x1000000; // Cannot be encoded into imm12 field
$i = 0xf12345678; // Cannot be encoded into imm12 field

var_dump($a > $b ? 1 : 0);
var_dump($a > $c ? 1 : 0);
var_dump($a > $d ? 1 : 0);
var_dump($a > $e ? 1 : 0);
var_dump($a > $f ? 1 : 0);
var_dump($a > $g ? 1 : 0);
var_dump($a > $h ? 1 : 0);
var_dump($a > $i ? 1 : 0);
}

function bar($a) {
$b = 0;
$c = -31;
$d = -4095; // negation of 0xfff
$e = -4096; // negation of 0x1000
$f = -16773120; // negation of 0xfff000
$g = -1044481; // negation of 0xff001
$h = -16777216; // negation of 0x1000000
$i = -64729929336; // negation of 0xf12345678

var_dump($a > $b ? 1 : 0);
var_dump($a > $c ? 1 : 0);
var_dump($a > $d ? 1 : 0);
var_dump($a > $e ? 1 : 0);
var_dump($a > $f ? 1 : 0);
var_dump($a > $g ? 1 : 0);
var_dump($a > $h ? 1 : 0);
var_dump($a > $i ? 1 : 0);
}

foo(42);
bar(42);
?>
--EXPECT--
int(1)
int(1)
int(0)
int(0)
int(0)
int(0)
int(0)
int(0)
int(1)
int(1)
int(1)
int(1)
int(1)
int(1)
int(1)
int(1)

0 comments on commit 3e164de

Please sign in to comment.