Block chaining is one of the key performance factors of tcg. Currently tcg doesn't allow chaining across page boundary, an example can be found in gen_goto_tb() in target/riscv/translate.c.
For user-mode tcg, it's possible to enable cross-page chaining with careful attentions, assume there are chains like this: preceding page -> 1st page -> 2nd page Nth page -> 2nd page There are 2 situations to consider: 1. First page should not jump to 2nd page directly anymore, if there is a new breakpoint added to 3rd page, otherwise the breakpoint might not be hit. One method to address this problem is when receiving gdb commands, call tb_flush() to invalidate all the TBs, and make sure each TB can only contain single instruction later, no matter the new JIT-ed TBs use chain or not, the tcg core loop always has the chance to check if there is any breakpoint on each instruction. There could be other methods, but current tcg has already done this. 2. The protection of 2nd page has changed by mprotect/munmap, e.g. from executable (X) to non-executable (NX), it's an error if the 1st page jumps to 2nd page without checking the new protection. The point here is to invalidate TBs in 2nd page and unlink all the TBs which jumps to it, including 1st page and others(Nth in above chart). This is already done in page_set_flags(). A small testcase runs on user-mode guest: void *page = mmap(NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); memcpy(page, func_add, pagesize); f = (FUNC)page; f(1, 1); // good mprotect(f, pagesize, PROT_READ | PROT_EXEC); f(1, 2); // good mprotect(f, pagesize, PROT_READ); f(1, 3); // segfault So it looks like current tcg implementation is ready to enable cross-page chaining for user-mode. Correct? diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 7bda43ff61..822644c7a4 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -25,8 +25,12 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) return false; } +#ifdef CONFIG_USER_ONLY + return true; +#else /* Check for the dest on the same page as the start of the TB. */ return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; +#endif } void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, Thanks, Fei.