From f1bb0d31fd13ab6404ec408e834d9bb2bde85dc1 Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Tue, 15 Jun 2021 02:52:02 +0000 Subject: [PATCH 1/2] 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] https://github.com/LuaJIT/LuaJIT/pull/718 [2] https://github.com/LuaJIT/LuaJIT/blob/v2.1/dynasm/dasm_arm64.lua#L342 Change-Id: I4870048b9b8e6c429b73a4803af2a3b2d5ec0fbb --- ext/opcache/jit/zend_jit_arm64.dasc | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index e095e572f6e9e..411cade094672 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -122,7 +122,7 @@ 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 @@ -172,6 +172,11 @@ static bool arm64_may_use_adrp(const void *addr) return 0; } +static bool arm64_may_encode_imm12(const int64_t val) +{ + return (val >= 0 && (val < (1<<12) || !(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) { @@ -364,9 +369,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 @@ -377,9 +382,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 @@ -390,9 +395,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 @@ -406,7 +411,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 @@ -417,7 +422,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 @@ -428,7 +433,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 From 4b579971d43dbfb23541c72c1cd08d102f07115d Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Mon, 21 Jun 2021 05:30:27 +0000 Subject: [PATCH 2/2] 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 --- ext/opcache/jit/zend_jit_arm64.dasc | 18 ++++--- ext/opcache/tests/jit/add_007.phpt | 67 ++++++++++++++++++++++++++ ext/opcache/tests/jit/cmp_005.phpt | 75 +++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 ext/opcache/tests/jit/add_007.phpt create mode 100644 ext/opcache/tests/jit/cmp_005.phpt diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 411cade094672..6986e1e41b091 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -125,9 +125,7 @@ typedef struct TLVDescriptor TLVDescriptor; /* 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 @@ -172,9 +170,13 @@ 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 < (1<<12) || !(val & 0xffffffffff000fff))); + return (val >= 0 && (val <= MAX_IMM12 || !(val & 0xffffffffff000fff))); } /* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */ @@ -728,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 { @@ -936,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) @@ -8265,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 @@ -9109,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) { @@ -9215,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 diff --git a/ext/opcache/tests/jit/add_007.phpt b/ext/opcache/tests/jit/add_007.phpt new file mode 100644 index 0000000000000..ec3005f50d651 --- /dev/null +++ b/ext/opcache/tests/jit/add_007.phpt @@ -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-- + +--FILE-- + +--EXPECT-- +int(64764532386) +int(68684873728) diff --git a/ext/opcache/tests/jit/cmp_005.phpt b/ext/opcache/tests/jit/cmp_005.phpt new file mode 100644 index 0000000000000..c1d7eabeb7645 --- /dev/null +++ b/ext/opcache/tests/jit/cmp_005.phpt @@ -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-- + +--FILE-- + $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)