Skip to content

Commit 3e164de

Browse files
authored
JIT/AArch64: Support shifted immediate (#7165)
* JIT/AArch64: Support shifted immediate As pointed out by MikePall in [1], shifted immediate value is supported. See [2]. For example, `add x0, x1, #4096` would be encoded by DynASM into `add x0, x1, #1, lsl #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 #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
1 parent b83bfb2 commit 3e164de

File tree

3 files changed

+166
-17
lines changed

3 files changed

+166
-17
lines changed

ext/opcache/jit/zend_jit_arm64.dasc

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,10 @@ typedef struct TLVDescriptor TLVDescriptor;
122122

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

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

173+
/* Determine whether "val" falls into two allowed ranges:
174+
* Range 1: [0, 0xfff]
175+
* Range 2: LSL #12 to Range 1
176+
* Used to guard the immediate encoding for add/adds/sub/subs/cmp/cmn instructions. */
177+
static bool arm64_may_encode_imm12(const int64_t val)
178+
{
179+
return (val >= 0 && (val <= MAX_IMM12 || !(val & 0xffffffffff000fff)));
180+
}
181+
175182
/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */
176183
static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
177184
{
@@ -364,9 +371,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
364371
|.macro CMP_32_WITH_CONST, reg, val, tmp_reg
365372
|| if (val == 0) {
366373
| cmp reg, wzr
367-
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
374+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
368375
| cmp reg, #val
369-
|| } else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
376+
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
370377
| cmn reg, #-val
371378
|| } else {
372379
| LOAD_32BIT_VAL tmp_reg, val
@@ -377,9 +384,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
377384
|.macro CMP_64_WITH_CONST_32, reg, val, tmp_reg
378385
|| if (val == 0) {
379386
| cmp reg, xzr
380-
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= CMP_IMM) {
387+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
381388
| cmp reg, #val
382-
|| } else if (((int32_t)(val)) < 0 && ((int32_t)(val)) >= -CMP_IMM) {
389+
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
383390
| cmn reg, #-val
384391
|| } else {
385392
| LOAD_32BIT_VAL tmp_reg, val
@@ -390,9 +397,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
390397
|.macro CMP_64_WITH_CONST, reg, val, tmp_reg
391398
|| if (val == 0) {
392399
| cmp reg, xzr
393-
|| } else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= CMP_IMM) {
400+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
394401
| cmp reg, #val
395-
|| } else if (((int64_t)(val)) < 0 && ((int64_t)(val)) >= -CMP_IMM) {
402+
|| } else if (arm64_may_encode_imm12((int64_t)(-val))) {
396403
| cmn reg, #-val
397404
|| } else {
398405
| LOAD_64BIT_VAL tmp_reg, val
@@ -406,7 +413,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
406413
|.macro ADD_SUB_32_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
407414
|| if (val == 0) {
408415
| add_sub_ins dst_reg, src_reg1, wzr
409-
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
416+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
410417
| add_sub_ins dst_reg, src_reg1, #val
411418
|| } else {
412419
| LOAD_32BIT_VAL tmp_reg, val
@@ -417,7 +424,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
417424
|.macro ADD_SUB_64_WITH_CONST_32, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
418425
|| if (val == 0) {
419426
| add_sub_ins dst_reg, src_reg1, xzr
420-
|| } else if (((int32_t)(val)) > 0 && ((int32_t)(val)) <= ADD_SUB_IMM) {
427+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
421428
| add_sub_ins dst_reg, src_reg1, #val
422429
|| } else {
423430
| LOAD_32BIT_VAL tmp_reg, val
@@ -428,7 +435,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
428435
|.macro ADD_SUB_64_WITH_CONST, add_sub_ins, dst_reg, src_reg1, val, tmp_reg
429436
|| if (val == 0) {
430437
| add_sub_ins dst_reg, src_reg1, xzr
431-
|| } else if (((int64_t)(val)) > 0 && ((int64_t)(val)) <= ADD_SUB_IMM) {
438+
|| } else if (arm64_may_encode_imm12((int64_t)(val))) {
432439
| add_sub_ins dst_reg, src_reg1, #val
433440
|| } else {
434441
| LOAD_64BIT_VAL tmp_reg, val
@@ -723,7 +730,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size)
723730

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

82628269
if (func) {
8263-
|| if (used_stack <= ADD_SUB_IMM) {
8270+
|| if (arm64_may_encode_imm12((int64_t)used_stack)) {
82648271
| MEM_UPDATE_ZTS add, ldr, str, #used_stack, executor_globals, vm_stack_top, REG2, TMP1
82658272
|| } else {
82668273
| LOAD_32BIT_VAL TMP1w, used_stack
@@ -9104,7 +9111,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
91049111
| LOAD_IP_ADDR (func->op_array.opcodes + num_args)
91059112
} else {
91069113
| ldr REG0, EX->func
9107-
|| ZEND_ASSERT((num_args * sizeof(zend_op)) <= ADD_SUB_IMM);
9114+
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(num_args * sizeof(zend_op))));
91089115
if (GCC_GLOBAL_REGS) {
91099116
| ldr IP, [REG0, #offsetof(zend_op_array, opcodes)]
91109117
if (num_args) {
@@ -9210,7 +9217,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
92109217
| ble >3
92119218
| // zval *var = EX_VAR_NUM(num_args);
92129219
| add REG1, FP, REG1, lsl #4
9213-
|| ZEND_ASSERT(ZEND_CALL_FRAME_SLOT * sizeof(zval) <= ADD_SUB_IMM);
9220+
|| ZEND_ASSERT(arm64_may_encode_imm12((int64_t)(ZEND_CALL_FRAME_SLOT * sizeof(zval))));
92149221
| add REG1, REG1, #(ZEND_CALL_FRAME_SLOT * sizeof(zval))
92159222
|2:
92169223
| SET_Z_TYPE_INFO REG1, IS_UNDEF, TMP1w

ext/opcache/tests/jit/add_007.phpt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
--TEST--
2+
JIT ADD: 007 Addition with immediate values
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.file_update_protection=0
7+
opcache.jit_buffer_size=1M
8+
opcache.protect_memory=1
9+
--EXTENSIONS--
10+
opcache
11+
--SKIPIF--
12+
<?php
13+
if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
14+
--FILE--
15+
<?php
16+
function foo($a) {
17+
$b = 0;
18+
$c = 31;
19+
$d = 0xfff;
20+
$e = 0x1000;
21+
$f = 0xfff000;
22+
$g = 0xff001; // Cannot be encoded into imm12 field
23+
$h = 0x1000000; // Cannot be encoded into imm12 field
24+
$i = 0xf12345678; // Cannot be encoded into imm12 field
25+
$j = -31; // Cannot be encoded into imm12 field
26+
27+
$a = $a + $b;
28+
$a = $a + $c;
29+
$a = $a + $d;
30+
$a = $a + $e;
31+
$a = $a + $f;
32+
$a = $a + $g;
33+
$a = $a + $h;
34+
$a = $a + $i;
35+
$a = $a + $j;
36+
var_dump($a);
37+
}
38+
39+
function bar($a) {
40+
$b = 0;
41+
$c = 31;
42+
$d = 0xfff;
43+
$e = 0x1000;
44+
$f = 0xfff000;
45+
$g = 0xff001; // Cannot be encoded into imm12 field
46+
$h = 0x1000000; // Cannot be encoded into imm12 field
47+
$i = 0xf12345678; // Cannot be encoded into imm12 field
48+
$j = -31; // Cannot be encoded into imm12 field
49+
50+
$a = $a - $b;
51+
$a = $a - $c;
52+
$a = $a - $d;
53+
$a = $a - $e;
54+
$a = $a - $f;
55+
$a = $a - $g;
56+
$a = $a - $h;
57+
$a = $a - $i;
58+
$a = $a - $j;
59+
var_dump($a);
60+
}
61+
62+
foo(42);
63+
bar(0x1f12345678);
64+
?>
65+
--EXPECT--
66+
int(64764532386)
67+
int(68684873728)

ext/opcache/tests/jit/cmp_005.phpt

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
--TEST--
2+
JIT CMP: 005 Comparisons with immediate values
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.file_update_protection=0
7+
opcache.jit_buffer_size=1M
8+
opcache.protect_memory=1
9+
--EXTENSIONS--
10+
opcache
11+
--SKIPIF--
12+
<?php
13+
if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
14+
--FILE--
15+
<?php
16+
function foo($a) {
17+
$b = 0;
18+
$c = 31;
19+
$d = 0xfff;
20+
$e = 0x1000;
21+
$f = 0xfff000;
22+
$g = 0xff001; // Cannot be encoded into imm12 field
23+
$h = 0x1000000; // Cannot be encoded into imm12 field
24+
$i = 0xf12345678; // Cannot be encoded into imm12 field
25+
26+
var_dump($a > $b ? 1 : 0);
27+
var_dump($a > $c ? 1 : 0);
28+
var_dump($a > $d ? 1 : 0);
29+
var_dump($a > $e ? 1 : 0);
30+
var_dump($a > $f ? 1 : 0);
31+
var_dump($a > $g ? 1 : 0);
32+
var_dump($a > $h ? 1 : 0);
33+
var_dump($a > $i ? 1 : 0);
34+
}
35+
36+
function bar($a) {
37+
$b = 0;
38+
$c = -31;
39+
$d = -4095; // negation of 0xfff
40+
$e = -4096; // negation of 0x1000
41+
$f = -16773120; // negation of 0xfff000
42+
$g = -1044481; // negation of 0xff001
43+
$h = -16777216; // negation of 0x1000000
44+
$i = -64729929336; // negation of 0xf12345678
45+
46+
var_dump($a > $b ? 1 : 0);
47+
var_dump($a > $c ? 1 : 0);
48+
var_dump($a > $d ? 1 : 0);
49+
var_dump($a > $e ? 1 : 0);
50+
var_dump($a > $f ? 1 : 0);
51+
var_dump($a > $g ? 1 : 0);
52+
var_dump($a > $h ? 1 : 0);
53+
var_dump($a > $i ? 1 : 0);
54+
}
55+
56+
foo(42);
57+
bar(42);
58+
?>
59+
--EXPECT--
60+
int(1)
61+
int(1)
62+
int(0)
63+
int(0)
64+
int(0)
65+
int(0)
66+
int(0)
67+
int(0)
68+
int(1)
69+
int(1)
70+
int(1)
71+
int(1)
72+
int(1)
73+
int(1)
74+
int(1)
75+
int(1)

0 commit comments

Comments
 (0)