Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#2210 clean call: Implement parameter preparation on ARM and AArch64. #2260

Merged
merged 6 commits into from
Mar 5, 2017

Conversation

egrimley
Copy link
Contributor

@egrimley egrimley commented Mar 2, 2017

The implementation should efficiently handle any (reasonable) number
of arguments that are immediate integers or full-size registers.
Test cases are added in cleancallparams.dll.c.

Fixes #2210

The implementation should efficiently handle any (reasonable) number
of arguments that are immediate integers or full-size registers.
Test cases are added in cleancallparams.dll.c.

Fixes #2210
@egrimley egrimley requested a review from derekbruening March 2, 2017 11:44
Copy link
Contributor

@derekbruening derekbruening left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code seems good in general, mostly I have requests for comments, some code movement, etc.

{
return ((opnd_is_reg(opnd) && opnd_get_reg(opnd) != DR_REG_XSP) ||
IF_X64_ELSE((opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) == 0),
false));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code would be easier to read as IF_X64(|| ...) rather than having an ||false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no "||false" in there...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? For non-x64 it is "||\n false"

/* str r(...), [sp, #(i * 4)] */
PRE(ilist, instr, XINST_CREATE_store
(dcontext, opnd_create_base_disp(DR_REG_XSP, DR_REG_NULL, 0,
i * 4, OPSZ_PTR), arg));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XSP_SZ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's weird. I thought I'd searched for all suspicious mentions of "4" and "8"...

instr_t *mark = INSTR_CREATE_label(dcontext);
PRE(ilist, instr, mark);
uint num_regs = num_args < NUM_REGPARM ? num_args : NUM_REGPARM;
signed char regs[NUM_REGPARM];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to figure out what type this holds: please add a comment. At first it seemed like it should be reg_id_t but it's using some custom encoding.

ASSERT(num_args == 0 || args != NULL);
/* FIXME i#1551, i#1569: we only support limited number of args for now. */
ASSERT_NOT_IMPLEMENTED(num_args <= NUM_REGPARM);
/* We expect every arg to be an integer or a full-size register. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a memory location? The docs do not say it's disallowed, and as we have to copy the arg into a reg anyway, a user is likely to assume we support it. I was assuming we supported memory args (and it was just NYI, as the existing code shows). Certainly we do on x86.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention this in the review summary as this is a more major thing: current docs do not say memory args are disallowed. Is that what we want to do, update the docs to disallow them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it would be hard to add memory operands, but then they should also be a test of memory operands, which would probably mean porting "cleancall", so probably that should be a separate commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly unsure about which memory operands should be accepted:

constant offset from a register: Yes.
register offset from a register: A pain to implement but hard to justify excluding.
REL_ADDR_kind: Is this useful? (It would be AArch32-only.)
MEM_INSTR_kind: Does that make any sense here?
ABS_ADDR_kind: Does not exist for ARM/AArch64.

(dcontext, regparms[i], TLS_REG0_SLOT));
insert_get_mcontext_base(dcontext, ilist, loc, regparms[i]);
PRE(ilist, loc, instr_create_restore_from_dc_via_reg
ASSERT(opnd_is_immed_int((args[i])) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user is likely to break this, this should be a CLIENT_ASSERT, not an internal sanity check. However, see above: we should either support memory args (seems simple enough if they're < NUM_REGPARM), or we need to document that they are not supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for this commit I should probably do a CLIENT_ASSERT that also accepts memory args, followed by an ASSERT_NOT_IMPLEMENTED that rejects the memory args.

}
} while (i < num_regs);

/* From now on it is safe to use LR as a temporary. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe more explanation would make this clearer (and asserts?): I assume the logic is that the only thing to worry about is LR as a param, and since it's not a calling conv arg reg, it would have be moved into one by the loop above.

PRE(ilist, instr, XINST_CREATE_move(dcontext, opnd_create_reg(DR_REG_LR),
opnd_create_reg(regparms[i])));
do {
tmp = regs[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I think I understand what it's doing, shifting one into LR and everything else one down, then repeat, but comments on the approach would help, and on whether there might be a more efficient method but it would be much more complex and not worthwhile, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A rotation of n registers is implemented with (n + 1) moves in the obvious way. I don't think there's a better way.

} while (tmp != first);
}

/* Set up stack arguments that are (non-zero) constants or SP. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above: IMHO these should be up by the other stack args

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No guarantee of a free register earlier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment should explain this: it was not obvious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment near the start of the function: /* The strategy here is to first set up ... (Yes, I do split infinitives!)

}
return 0;

return (uint)stack_inc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason it can't be declared as a uint?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might involve a lot of casts to keep Microsoft compilers happy. Though Microsoft compilers won't be looking at this code in the short term...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it to be a signed type locally because I pass -stack_inc to functions that take an int. Perhaps the return type should be be ptr_int_t or ptr_uint_t: it's an increment to a pointer type and in theory one could have 2^32 arguments on a 64-bit system.

#elif defined(AARCHXX)
PRE(ilist, instr, INSTR_CREATE_add(dcontext, opnd_create_reg(REG_XSP),
opnd_create_reg(REG_XSP),
OPND_CREATE_INT32(stack_for_params)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove all the ifdefs and use XINST_CREATE_add(), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, probably. I don't understand why the X86 code is done how it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean why it's OP_lea? Because OP_add clobbers the flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But XINST_CREATE_add() will generate an OP_lea on X86?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at the docs for XINST_CREATE_add versus XINST_CREATE_add_s and the implementations. We deliberately set it up that way after some discussion.

The implementation should efficiently handle any (reasonable) number
of arguments that are immediate integers or full-size registers.
Test cases are added in cleancallparams.dll.c.

Memory arguments are not yet implemented.
Copy link
Contributor

@derekbruening derekbruening left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing FIXME/XXX i# comments but otherwise looks good

@@ -596,20 +595,34 @@ insert_parameter_preparation(dcontext_t *dcontext, instrlist_t *ilist, instr_t *
ptr_int_t stack_inc = 0;
uint i, j;

/* We expect every arg to be an integer or a full-size register. */
/* We expect every arg to be an immediate integer, a full-size register,
* or a simple memory reference (NYI).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have i# and XXX or FIXME

reg_get_size(opnd_get_reg(args[i])) == OPSZ_PTR) ||
opnd_is_base_disp(args[i]),
"insert_parameter_preparation: bad argument type");
ASSERT_NOT_IMPLEMENTED(!opnd_is_base_disp(args[i]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best to have XXX or FIXME with i#

@egrimley egrimley merged commit e06883c into master Mar 5, 2017
@egrimley egrimley deleted the i2210-aarchxx-clean-call branch March 5, 2017 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants