diff options
author | Richard Henderson <rth@twiddle.net> | 2015-09-15 11:45:09 -0700 |
---|---|---|
committer | Eduardo Habkost <ehabkost@redhat.com> | 2015-10-23 12:59:27 -0200 |
commit | 36eb6e096729f9aade3a6af7dbe4d0a990335d7e (patch) | |
tree | 386b80943fc45bd5742babd5cc7d51bdf825a4b8 /target-i386 | |
parent | 93d00d0fbe4711061834730fb70525d167b6f908 (diff) |
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.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Diffstat (limited to 'target-i386')
-rw-r--r-- | target-i386/bpt_helper.c | 34 |
1 files changed, 28 insertions, 6 deletions
diff --git a/target-i386/bpt_helper.c b/target-i386/bpt_helper.c index f14788a1e9..23ce828491 100644 --- a/target-i386/bpt_helper.c +++ b/target-i386/bpt_helper.c @@ -82,14 +82,36 @@ static void hw_breakpoint_remove(CPUX86State *env, int index) void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) { + target_ulong old_dr7 = env->dr[7]; int i; - 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 |