Skip to content

Commit

Permalink
i#1569 AArch64: Correctly mangle conditional branch using stolen reg.
Browse files Browse the repository at this point in the history
The generic recipe does not work for mangling "CBNZ X28, label" and
similar instructions (CBNZ/CBZ/TBNZ/TBZ W28/X28,...), so handle
these instructions separately. The new code requires a small
extension to the temporary encoder.

This special handling of CBNZ/CBZ was not required for AArch32 because
AArch32's CBNZ/CBZ can only use a "low register" (R0-R7), while the
stolen register is R10.

Review-URL: https://codereview.appspot.com/298070043
  • Loading branch information
egrimley-arm committed May 11, 2016
1 parent a38ff93 commit e002084
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 5 deletions.
15 changes: 11 additions & 4 deletions core/arch/aarch64/encode.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ decode_info_init_for_instr(decode_info_t *di, instr_t *instr)
*/
static uint encode_common(byte *pc, instr_t *i)
{
ptr_uint_t off;
ASSERT(((ptr_int_t)pc & 3) == 0);
switch (i->opcode) {
case OP_b:
Expand All @@ -129,13 +130,16 @@ static uint encode_common(byte *pc, instr_t *i)
case OP_cbnz:
case OP_cbz:
ASSERT(i->num_dsts == 0 && i->num_srcs == 2 &&
i->src0.kind == PC_kind &&
(i->src0.kind == PC_kind || i->src0.kind == INSTR_kind) &&
i->srcs[0].kind == REG_kind && i->srcs[0].size == 0 &&
((uint)(i->srcs[0].value.reg - DR_REG_W0) < 32 ||
(uint)(i->srcs[0].value.reg - DR_REG_X0) < 32));
off = ((i->src0.kind == PC_kind) ?
(uint)(i->src0.value.pc - pc) :
(ptr_int_t)(opnd_get_instr(i->src0)->note - i->note));
return (0x34000000 | (i->opcode == OP_cbnz) << 24 |
(uint)((uint)(i->srcs[0].value.reg - DR_REG_X0) < 32) << 31 |
(0x001fffff & (uint)(i->src0.value.pc - pc)) >> 2 << 5 |
(0x001fffff & off) >> 2 << 5 |
((i->srcs[0].value.reg - DR_REG_X0) < 32 ?
(i->srcs[0].value.reg - DR_REG_X0) :
(i->srcs[0].value.reg - DR_REG_W0)));
Expand Down Expand Up @@ -194,12 +198,15 @@ static uint encode_common(byte *pc, instr_t *i)
case OP_tbnz:
case OP_tbz:
ASSERT(i->num_dsts == 0 && i->num_srcs == 3 &&
i->src0.kind == PC_kind &&
(i->src0.kind == PC_kind || i->src0.kind == INSTR_kind) &&
i->srcs[0].kind == REG_kind && i->srcs[0].size == 0 &&
(uint)(i->srcs[0].value.reg - DR_REG_X0) < 32 &&
i->srcs[1].kind == IMMED_INTEGER_kind);
off = ((i->src0.kind == PC_kind) ?
(uint)(i->src0.value.pc - pc) :
(ptr_int_t)(opnd_get_instr(i->src0)->note - i->note));
return (0x36000000 | (i->opcode == OP_tbnz) << 24 |
(0xffff & (uint)(i->src0.value.pc - pc)) >> 2 << 5 |
(0xffff & off) >> 2 << 5 |
(i->srcs[0].value.reg - DR_REG_X0) |
(i->srcs[1].value.immed_int & 31) << 19 |
(i->srcs[1].value.immed_int & 32) << 26);
Expand Down
71 changes: 70 additions & 1 deletion core/arch/aarchxx/mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,71 @@ mangle_gpr_list_write(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr,

#endif /* !AARCH64 */

#ifdef AARCH64
/* We mangle a conditional branch that uses the stolen register like this:
*
* cbz x28, target # x28 is stolen register
* =>
* str x0, [x28] # spill x0
* ldr x0, [x28, #32] # x28 in memory loaded to x0
* cbnz x0, fall
* ldr x0, [x28] # restore x0 (original branch taken)
* b target
* fall:
* ldr x0, [x28] # restore x0 (original branch not taken)
*
* The CBNZ will need special handling when we decode from the cache for
* traces (i#1668).
*/
static void
mangle_cbr_stolen_reg(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *instr, instr_t *next_instr)
{
instr_t *fall = INSTR_CREATE_label(dcontext);
int opcode = instr_get_opcode(instr);
reg_id_t reg = DR_REG_X0;
ushort slot = TLS_REG0_SLOT;
opnd_t opnd;

PRE(ilist, instr, instr_create_save_to_tls(dcontext, reg, slot));
PRE(ilist, instr, instr_create_restore_from_tls(dcontext, reg,
TLS_REG_STOLEN_SLOT));
switch (opcode) {
case OP_cbnz:
case OP_cbz:
opnd = instr_get_src(instr, 1);
opnd = opnd_create_reg(reg_resize_to_opsz(reg, opnd_get_size(opnd)));
PRE(ilist, instr,
instr_create_0dst_2src(dcontext,
(opcode == OP_cbz ? OP_cbnz : OP_cbz),
opnd_create_instr(fall), opnd));
break;
case OP_tbnz:
case OP_tbz:
PRE(ilist, instr,
instr_create_0dst_3src(dcontext,
(opcode == OP_tbz ? OP_tbnz : OP_tbz),
opnd_create_instr(fall),
opnd_create_reg(reg),
instr_get_src(instr, 2)));
break;
default:
ASSERT_NOT_REACHED();
}
PRE(ilist, instr, instr_create_restore_from_tls(dcontext, reg, slot));

/* Replace original instruction with unconditional branch. */
opnd = instr_get_src(instr, 0);
instr_reset(dcontext, instr);
instr_set_opcode(instr, OP_b);
instr_set_num_opnds(dcontext, instr, 0, 1);
instr_set_src(instr, 0, opnd);

PRE(ilist, next_instr, fall);
PRE(ilist, next_instr, instr_create_restore_from_tls(dcontext, reg, slot));
}
#endif /* AARCH64 */

/* On ARM, we need mangle app instr accessing registers pc and dr_reg_stolen.
* We use this centralized mangling routine here to handle complex issues with
* more efficient mangling code.
Expand All @@ -2314,7 +2379,11 @@ mangle_special_registers(dcontext_t *dcontext, instrlist_t *ilist, instr_t *inst
instr_t *next_instr)
{
#ifdef AARCH64
if (instr_uses_reg(instr, dr_reg_stolen) && !instr_is_mbr(instr))
if (!instr_uses_reg(instr, dr_reg_stolen))
return next_instr;
if (instr_is_cbr(instr))
mangle_cbr_stolen_reg(dcontext, ilist, instr, instr_get_next(instr));
else if (!instr_is_mbr(instr))
mangle_stolen_reg(dcontext, ilist, instr, instr_get_next(instr), false);
return next_instr;
#else
Expand Down

0 comments on commit e002084

Please sign in to comment.