Skip to content

Commit

Permalink
i#3044 AArch64 SVE codec: change LDR/STR and PRF to use byte offsets (D…
Browse files Browse the repository at this point in the history
…ynamoRIO#6230)

For the current decode/encode functions of:

LDR <Zt>, [<Xn|SP>{, #<imm>, MUL VL}]
LDR <Pt>, [<Xn|SP>{, #<imm>, MUL VL}]
STR <Zt>, [<Xn|SP>{, #<imm>, MUL VL}]
STR <Pt>, [<Xn|SP>{, #<imm>, MUL VL}]
PRFB <prfop>, <Pg>, [<Xn|SP>{, #<imm>, MUL VL}]
PRFH <prfop>, <Pg>, [<Xn|SP>{, #<imm>, MUL VL}]
PRFW <prfop>, <Pg>, [<Xn|SP>{, #<imm>, MUL VL}]
PRFD <prfop>, <Pg>, [<Xn|SP>{, #<imm>, MUL VL}]

Vector indexing is used in the memory operand at the IR level. However
the IR must always refer to the address in terms of the base register
value plus a byte offset displacement. This patch changes the
decode/encode functions for these instructions to expect byte offsets
at the IR level, converting to vector length offsets within the codec.

Issues DynamoRIO#3044, DynamoRIO#5365
  • Loading branch information
AssadHashmi authored and ivankyluk committed Jul 28, 2023
1 parent 5f1db45 commit aaed450
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 295 deletions.
4 changes: 2 additions & 2 deletions clients/drdisas/test_simple.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
00000000 udf $0x0000
f94017a0 ldr +0x28(%x29)[8byte] -> %x0
a9be7bfd stp %x29 %x30 %sp $0xffffffffffffffe0 -> -0x20(%sp)[16byte] %sp
e58057a1 str %z1 -> +0x05(%x29)[32byte]
85865e6b ldr +0x37(%x19)[32byte] -> %z11
e58057a1 str %z1 -> +0xa0(%x29)[32byte]
85865e6b ldr +0x06e0(%x19)[32byte] -> %z11
disassembly failed: invalid instruction: not enough bytes: 0x88
#elif defined(ARM) && !defined(thumb)
f2436813 vtst.8 %d3 %d3 -> %d22
Expand Down
81 changes: 66 additions & 15 deletions core/ir/aarch64/codec.c
Original file line number Diff line number Diff line change
Expand Up @@ -5178,9 +5178,19 @@ static inline bool
decode_opnd_svemem_gpr_simm6_vl(uint enc, int opcode, byte *pc, OUT opnd_t *opnd)
{
const int offset = extract_int(enc, 16, 6);
IF_RETURN_FALSE(offset < -32 || offset > 31)
const reg_id_t rn = decode_reg(extract_uint(enc, 5, 5), true, true);
const opnd_size_t mem_transfer = op_is_prefetch(opcode) ? OPSZ_0 : OPSZ_SVE_VL;
*opnd = opnd_create_base_disp(rn, DR_REG_NULL, 0, offset, mem_transfer);

/* As specified in the AArch64 SVE reference manual for contiguous prefetch
* instructions, the immediate index value is a vector index into memory, NOT
* an element or byte index. In DynamoRIO's IR, base-displacement operands
* should always refer to the address as a base register value + the linear
* memory displacement. So when creating the address operand here, it should be
* multiplied by the current vector register length in bytes.
*/
int vl_bytes = dr_get_sve_vl() / 8;
*opnd = opnd_create_base_disp(rn, DR_REG_NULL, 0, offset * vl_bytes, mem_transfer);

return true;
}
Expand All @@ -5193,9 +5203,21 @@ encode_opnd_svemem_gpr_simm6_vl(uint enc, int opcode, byte *pc, opnd_t opnd,
if (!opnd_is_base_disp(opnd) || opnd_get_index(opnd) != DR_REG_NULL ||
opnd_get_size(opnd) != mem_transfer)
return false;
if (!reg_is_gpr(opnd_get_base(opnd)))
return false;

/* As described in decode_opnd_svemem_gpr_simm6_vl(), disp is a multiple of
* vector length at the IR level, transformed to a vector index in the
* encoding.
*/
int vl_bytes = dr_get_sve_vl() / 8;
if ((opnd_get_disp(opnd) % vl_bytes) != 0)
return false;
int disp = opnd_get_disp(opnd) / vl_bytes;
IF_RETURN_FALSE(disp < -32 || disp > 31)

uint imm6;
if (!try_encode_int(&imm6, 6, 0, opnd_get_disp(opnd)))
if (!try_encode_int(&imm6, 6, 0, disp))
return false;

uint rn;
Expand Down Expand Up @@ -5302,14 +5324,27 @@ decode_opnd_svemem_gpr_simm9_vl(uint enc, int opcode, byte *pc, OUT opnd_t *opnd
{
uint simm9 = (extract_uint(enc, 16, 6) << 3) | extract_uint(enc, 10, 3);
int offset9 = extract_int(simm9, 0, 9);
if (offset9 < -256 || offset9 > 255)
return false;
IF_RETURN_FALSE(offset9 < -256 || offset9 > 255)

bool is_vector = TEST(1u << 14, enc);

// Transfer size depends on whether we are transferring a Z register or a P register.
opnd_size_t memory_transfer_size = TEST(1u << 14, enc) ? OPSZ_SVE_VL : OPSZ_SVE_PL;
/* Transfer size depends on whether we are transferring a Z or a P register. */
opnd_size_t memory_transfer_size = is_vector ? OPSZ_SVE_VL : OPSZ_SVE_PL;

*opnd = opnd_create_base_disp(decode_reg(extract_uint(enc, 5, 5), true, true),
DR_REG_NULL, 0, offset9, memory_transfer_size);
/* As specified in the AArch64 SVE reference manual for unpredicated vector
* register load LDR and store STR instructions, the immediate index value is a
* vector index into memory, NOT an element or byte index. In DynamoRIO's IR,
* base-displacement operands should always refer to the address as a base
* register value + the linear memory displacement. So when creating the
* address operand here, it should be multiplied by the current vector or
* predicate register length in bytes.
*/
int vl_bytes = dr_get_sve_vl() / 8;
int pl_bytes = vl_bytes / 8;
int mul_len = is_vector ? vl_bytes : pl_bytes;
*opnd =
opnd_create_base_disp(decode_reg(extract_uint(enc, 5, 5), true, true),
DR_REG_NULL, 0, offset9 * mul_len, memory_transfer_size);
return true;
}

Expand All @@ -5321,16 +5356,32 @@ encode_opnd_svemem_gpr_simm9_vl(uint enc, int opcode, byte *pc, opnd_t opnd,
bool is_x;
uint rn;

// Transfer size depends on whether we are transferring a Z register or a P register.
opnd_size_t memory_transfer_size = TEST(1u << 14, enc) ? OPSZ_SVE_VL : OPSZ_SVE_PL;
bool is_vector = TEST(1u << 14, enc);

/* Transfer size depends on whether we are transferring a Z or a P register. */
opnd_size_t memory_transfer_size = is_vector ? OPSZ_SVE_VL : OPSZ_SVE_PL;

if (!opnd_is_base_disp(opnd) || opnd_get_size(opnd) != memory_transfer_size)
return false;
disp = opnd_get_disp(opnd);
if (disp < -256 || disp > 255)
return false;
if (!encode_reg(&rn, &is_x, opnd_get_base(opnd), true) || !is_x)
return false;
/* As described in decode_opnd_svemem_gpr_simm9_vl(), disp is a multiple of
* vector or predicate length at the IR level, transformed to a vector or
* predicate index in the encoding.
*/
int vl_bytes = dr_get_sve_vl() / 8;
int pl_bytes = vl_bytes / 8;
if (is_vector) {
if ((opnd_get_disp(opnd) % vl_bytes) != 0)
return false;
} else {
if ((opnd_get_disp(opnd) % pl_bytes) != 0)
return false;
}

disp =
is_vector ? (opnd_get_disp(opnd) / vl_bytes) : (opnd_get_disp(opnd) / pl_bytes);
IF_RETURN_FALSE(disp < -256 || disp > 255)
IF_RETURN_FALSE(!encode_reg(&rn, &is_x, opnd_get_base(opnd), true) || !is_x)

*enc_out = (rn << 5) | (BITS(disp, 8, 3) << 16) | (BITS(disp, 2, 0) << 10);
return true;
}
Expand Down
Loading

0 comments on commit aaed450

Please sign in to comment.