target-i386: Re-introduce optimal breakpoint removal

Before the last patch, we had an efficient loop that disabled
local breakpoints on task switch. Re-add that, but in a more
general way that handles changes to the global enable bits too.

Backports commit 36eb6e096729f9aade3a6af7dbe4d0a990335d7e from qemu
This commit is contained in:
Richard Henderson 2018-02-16 16:07:30 -05:00 committed by Lioncash
parent 0ad95f8341
commit 77b129a1c6
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -82,13 +82,35 @@ static void hw_breakpoint_remove(CPUX86State *env, int index)
void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7)
{
int i;
target_ulong old_dr7 = env->dr[7];
for (i = 0; i < DR7_MAX_BP; i++) {
hw_breakpoint_remove(env, i);
}
env->dr[7] = new_dr7;
for (i = 0; i < DR7_MAX_BP; i++) {
hw_breakpoint_insert(env, i);
/* If nothing is changing except the global/local enable bits,
then we can make the change more efficient. */
if (((old_dr7 ^ new_dr7) & ~0xff) == 0) {
/* Fold the global and local enable bits together into the
global fields, then xor to show which registers have
changed collective enable state. */
int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff;
for (i = 0; i < DR7_MAX_BP; i++) {
if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) {
hw_breakpoint_remove(env, i);
}
}
env->dr[7] = new_dr7;
for (i = 0; i < DR7_MAX_BP; i++) {
if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) {
hw_breakpoint_insert(env, i);
}
}
} else {
for (i = 0; i < DR7_MAX_BP; i++) {
hw_breakpoint_remove(env, i);
}
env->dr[7] = new_dr7;
for (i = 0; i < DR7_MAX_BP; i++) {
hw_breakpoint_insert(env, i);
}
}
}
#endif