i'm not sure to understand... are both of you saying that you had in SHx/MIPS code a branch instruction into a delay slot of another branch instruction like "blt 0f; b 1f" ??? are you sure you are in a valid code ? if processor doesn't complain, i will expect for the second branch instruction to be ignored.Ari64 said:Yeah, it messes up the recompiler.notaz said:I have some SH2 code where conditional branch lands in delay slot of other conditional branch, and this messes up my recompiler. Do you handle such case on MIPS?
Right now I leave the branch unresolved in cases like this, so when the branch is taken, it jumps back to the recompiler and recompiles that part. It ought to be possible to just compile that one instruction again as part of the branch that jumps to it, though I'd need to make sure the register mapping was correct, and adjust the cycle count to account for it. Kind of a pain in the butt, so I haven't done it yet.
nope, what we meant is this:hlide said:i'm not sure to understand... are both of you saying that you had in SHx/MIPS code a branch instruction into a delay slot of another branch instruction like "blt 0f; b 1f" ??? are you sure you are in a valid code ? if processor doesn't complain, i will expect for the second branch instruction to be ignored.
blt 0f
nop
nop
bgtz whatever
0:add r1,r2,r3
...
ah yes ! that was what you meant by "lands". I would treat it this way :notaz said:nope, what we meant is this:hlide said:i'm not sure to understand... are both of you saying that you had in SHx/MIPS code a branch instruction into a delay slot of another branch instruction like "blt 0f; b 1f" ??? are you sure you are in a valid code ? if processor doesn't complain, i will expect for the second branch instruction to be ignored.
Code:blt 0f nop nop bgtz whatever 0:add r1,r2,r3 ...
bb0 { nop; blt bb2; @bb1; }
bb1 { nop; add r1, r2, r3; bgtz bb3; @bb3; }
bb2 { add r1, r2, r3; @bb3; }
bb3 { ... }
Here's an example. Suppose we have the following MIPS code:hlide said:i'm not sure to understand... are both of you saying that you had in SHx/MIPS code a branch instruction into a delay slot of another branch instruction like "blt 0f; b 1f" ??? are you sure you are in a valid code ? if processor doesn't complain, i will expect for the second branch instruction to be ignored.
0x07466230: add r0, r0, #1 ; ADDIU r16,r16,1
0x07466234: adds r10, r10, #8 ; Cycle count
0x07466238: bpl 0x74664d8 ; Generate interrupt if timer expired
0x0746623c: cmp r3, #1 ; Compare r3
0x07466240: bge 0x746624c ; if r3>0
0x07466244: str r0, [r11, #256] ; Store r16
0x07466248: b 0x746629c ; Jump to translated 80056f54
0x07466278: add r1, r1, #48 ; ADDIU r17,r17,48
0x0746627c: cmn r10, #10 ; Check cycle count
0x07466280: bpl 0x7466528 ; Generate interrupt if timer expired
0x07466284: tst r2, r2 ; Compare r1
0x07466288: beq 0x7466298 ; if r1==0
0x0746628c: add r10, r10, #12 ; Cycle count
0x07466290: add r0, r0, #1 ; ADDIU r16,r16,1
0x07466294: b 0x746624c ; Jump to translated 80056f40
If the game never enables the TLB, then we can avoid that 5% slowdown. There is also some overhead for self-modifying code detection, but this varies from game to game. Many games use the TLB only for data and not executable code.
Hey Exophase, A bit off topic but are you getting a Pandora?Exophase said:He doesn't need a program to help him find if a game enables TLB or not. The emulator can assume it won't then recompile if it does.
DaveC said:Hey Exophase, A bit off topic but are you getting a Pandora?
Surely Temper would run like a dream on it although you would probably have to find a way to slow it down to fullspeed . It would work much nicer than the other PC-engine emus out as I think yours is the only one to run Blazing Lazers past the first boss.
Initially the TLB is not enabled, and any load or store on an address not in physical memory will cause an exception. If the game executes a TLBWI instruction with a valid TLB entry, then the dynamic recompiler changes how it compiles load and store instructions, so that memory addresses are looked up in the mapping table.Exophase said:He doesn't need a program to help him find if a game enables TLB or not. The emulator can assume it won't then recompile if it does.
LW r1,8(r2)
->
add r1, r2, #8
cmp r1, $8388608
bvc handler
ldr r1, [r1]
LW r1,8(r2)
->
add r1, r2, #8
mov r0, #264
add r0, r0, r1, lsr #12
ldr r0, [r11, r0, lsl #2]
tst r0, r0
bmi handler
ldr r1, [r1, r0, lsl #2]
Paper Mario pages code in and out of RAM a lot. It might be worth hacking that game to use the 8MB expansion, page its code in there, and never page it out again, if possible. Or have an alternate mode for games like this where you don't recompile the code every time it's swapped in, but just keep the compiled code in a pool, and point memory at it when it's swapped in.Exophase said:Ari64 said:That doesn't happen. Once the page is marked dirty, it stays that way, and jumps to this block go thru jump_dirty and verify_code, which checks for modifications before it executes that block. The reason for this is mainly because some programs touch the first page of memory, and we don't want to recompile the interrupt handler at 0x80000180 again and again.
This sounds like it could have bad results for anything that heavily overlays code into/out of RAM. You could end up with a lot of blocks that are being verified constantly. Hopefully this is not a scenario that happens on any actual games. It would seem kind of strange for games to do this when they can execute from ROM, to the best of my knowledge (and let the cache deal with the even worse access times).
Paper Mario changes the mapping frequently but maps only a few pages, so it's not a big problem. Most of its code is not in virtual memory, and I only see it paging code in and out when you switch scenes.⬡ said:Paper Mario pages code in and out of RAM a lot. It might be worth hacking that game to use the 8MB expansion, page its code in there, and never page it out again, if possible.
That's basically what it does. If a page is modified, then the compiled blocks go on the 'dirty' list. If some of that code is executed again, then it will check whether what was compiled before matches what is now in memory. If they match, then it will use the existing code from the cache.⬡ said:Or have an alternate mode for games like this where you don't recompile the code every time it's swapped in, but just keep the compiled code in a pool, and point memory at it when it's swapped in.
I don't know if all games strictly adhere to this or not. If you wanted to experiment, you could change the unneeded_registers function to assume certain registers are dead after a JAL or return instruction, then try some games and see if they still work. (You don't need a Pandora to test this; this function works the same on x86.)Exophase said:It's pretty natural that the inputs would be in A0-A3 or so and the return value in V0, that is afterall the ABI. An option to assume that the other registers are dead across a function call is a good enough idea. However, are you really certain NO games violate this? Did you really check all of them? N64's library is small enough that I could sort of believe this.
LOL I was wondering about that...Exophase said:Dude, your username is awesome. Did you come from another dimension to teach us about N64? Should I call you hexagon?
I tried this, and it works in some games and not others. Usually you can get away with discarding V0-V1 during a call and A0-A3 upon return. Sometimes it is also possible to discard AT and T6-T9. The speedup is about 2% when it works. This doesn't seem like a worthwhile optimization due to the minimal speed gain and high failure rate.Exophase said:It's pretty natural that the inputs would be in A0-A3 or so and the return value in V0, that is afterall the ABI. An option to assume that the other registers are dead across a function call is a good enough idea. However, are you really certain NO games violate this? Did you really check all of them? N64's library is small enough that I could sort of believe this.
Ari64 said:I tried this, and it works in some games and not others. Usually you can get away with discarding V0-V1 during a call and A0-A3 upon return. Sometimes it is also possible to discard AT and T6-T9. The speedup is about 2% when it works. This doesn't seem like a worthwhile optimization due to the minimal speed gain and high failure rate.
If one just blindly assumes those registers can be discarded without doing some smart analyses about the usage of registers, games surely happen to fail to run if they were not coded in pure C source (that si, if the emulator needs to dynarec BIOS or custom assembly code). That is the main reason I never attempted it for psx4all. That smart analyser may also be proved difficult to issue efficient code if at the end you still need store registers back when executing, says, a return instruction which is quite common in a C source.Ari64 said:I tried this, and it works in some games and not others. Usually you can get away with discarding V0-V1 during a call and A0-A3 upon return. Sometimes it is also possible to discard AT and T6-T9. The speedup is about 2% when it works. This doesn't seem like a worthwhile optimization due to the minimal speed gain and high failure rate.Exophase said:It's pretty natural that the inputs would be in A0-A3 or so and the return value in V0, that is afterall the ABI. An option to assume that the other registers are dead across a function call is a good enough idea. However, are you really certain NO games violate this? Did you really check all of them? N64's library is small enough that I could sort of believe this.