Mupen64Plus


Ari64 said:
Yeah but then you'd have to keep the odd numbered registers available, which will increase register pressure. So it's not a clear win. I'm working on a few other optimizations right now, maybe I will revisit this later.

A strategy that tries hard for this could bite you in the ass, surely. Just seems like it'd be worthwhile to do if the situation happens to arrive, which with a preferred mapping it might. I would say that you'd be very very slightly better off storing low and high register halves separately anyway, to improve cache usage a miniscule amount. At the very least I don't see any disadvantage to it, but please let me know if I'm wrong.

At any rate, I really should stop nagging you about such insignificant little things.. just promise to keep us posted when you come up with your next great idea okay? ;D I'm very impressed with your work.

One question - do you have any counts on how many "out of RAM" accesses happen per cycle? If it's small enough then it could be worth it to try to move that to a SIGSEGV handler too. Of course, this is only if enough of the address space will agree with it.
 
Last edited by a moderator:
 @Exophase I don't know about Ari64 but I always work/think better when I have someone to bounce ideas off of..... it either keeps you from doing stupid things or if your doing well.. its a confidence boost that someone else is thinking along the same line as yourself 
 
Pickle said:
You talked about this before, would it be beneficial to keep more recompiled code in memory? I was using top for my measurments and it reported only 30% being used.
You were running Super Mario 64, which is only 8MB total. It doesn't need a lot of memory.

I allocate 16 MB for the recompiled code, and it actually takes quite a lot to fill it up. I have seen it happen when running Majora's Mask, which swaps things in and out of memory, loading new code in place of what was there before.

The limit isn't due to a lack of memory, but a limitation in the ARM architecture. In the ARM instruction set, branches can only address +/- 32MB. 16MB is allocated for the recompiled code, and the rest of the emulator is mapped into adjacent memory, so everything can stay within a 32MB address space.
 
Last edited by a moderator:
That damn branch limit is really hurting gpSP on Symbian (someone recently worked on porting it). It can't execute code from BSS memory so it has to heap allocate it, but that stuff lives on the other side of the world from the text section. Stupid Nokia :|

I think if the OS does let you execute from global data then it should come near the text section, which will probably not take more than a MB or two at the most. So it should be realistic to allow for way more than 16MB, if it comes after the code section.
 
cb88 said:
 @Exophase I don't know about Ari64 but I always work/think better when I have someone to bounce ideas off of..... it either keeps you from doing stupid things or if your doing well.. its a confidence boost that someone else is thinking along the same line as yourself 
There was nothing wrong with the idea. I tried it, and it turns out it's a little more complicated than expected, so it's not clear at this point how well it will work.
 
Last edited by a moderator:
Exophase said:
That damn branch limit is really hurting gpSP on Symbian (someone recently worked on porting it). It can't execute code from BSS memory so it has to heap allocate it, but that stuff lives on the other side of the world from the text section. Stupid Nokia :|
I guess there is no mmap on symbian?

Exophase said:
I think if the OS does let you execute from global data then it should come near the text section, which will probably not take more than a MB or two at the most. So it should be realistic to allow for way more than 16MB, if it comes after the code section.
The text section is less than 1MB, so it would be possible to use 31MB. I doubt any games actually need this much.
 
Last edited by a moderator:
Ari64 said:
I guess there is no mmap on symbian?

No, and if there was that I'd tell him to look for a mprotect alternative too. Symbian sucks.

Ari64 said:
The text section is less than 1MB, so it would be possible to use 31MB. I doubt any games actually need this much.

Yeah, that B)
 
Last edited by a moderator:
I don 't understand much. Just a lowly end user here. But I know all this programmer talk means progress. Thank you everybody!
 
Ari64 said:
500 Mhz. This kernel doesn't have the /sys/devices/system/cpu/cpu0/cpufreq interface so I'd have to do the uboot hack to change it.

Maybe I should find Notaz's patches...
You can compile this into your kernel or just use some userspace tool that can write directly to OMAP registers, I think Angstrom comes with devmem2. Calculate ((MHZ << 8) | 0x10000c) and write to register 0x48004940.

Exophase said:
Ari64 said:
I guess there is no mmap on symbian?

No, and if there was that I'd tell him to look for a mprotect alternative too. Symbian sucks.
Agreed, not to mention it not providing direct access to the framebuffer. I've had my Virtua Racing recompiler running there but had to use indirect jumps between translated and all remaining code.
 
Last edited by a moderator:
Exophase said:
One question - do you have any counts on how many "out of RAM" accesses happen per cycle? If it's small enough then it could be worth it to try to move that to a SIGSEGV handler too. Of course, this is only if enough of the address space will agree with it.

All of the I/O in the N64 is memory-mapped, so this happens pretty frequently. In many cases it is possible to calculate the target address in advance, eg

Code:
LUI r1, a4600000
SW r9, (r1)
In this case the compiler will figure out that the address is a4600000 and avoid a conditional branch.
 
Last edited by a moderator:
Ari64 said:
Exophase said:
One question - do you have any counts on how many "out of RAM" accesses happen per cycle? If it's small enough then it could be worth it to try to move that to a SIGSEGV handler too. Of course, this is only if enough of the address space will agree with it.

All of the I/O in the N64 is memory-mapped, so this happens pretty frequently. In many cases it is possible to calculate the target address in advance, eg

Code:
 LUI r1, a4600000
 SW r9, (r1)
In this case the compiler will figure out that the address is a4600000 and avoid a conditional branch.

Then, do you know how many per-frame can't be detected via constant propagation? For any given game.
 
Last edited by a moderator:
Man, I'm bad. Pickle says that only the audio is working right now and I wish I could *see* it. It's hilarious that I have an addiction to a device I've never felt.
 
I made many of the optimizations discussed.

http://hotfile.com/dl/11644731/b4591c8/mupen64plus-arm-20090906.tar.gz.html


-Made the register elimination recursively evaluate loops

-Schedule loads to avoid the load-use penalty

-Improved code generation for ANDI, MFC1, and MTC1 instructions

-Eliminated redundant checks of the coprocessor status register (FPU unusable)

-When it is possible to precompute the target address, generate read/write calls inline instead of as a stub.

-Fixed a bug in the constant propegation (can not precalculate a load address across a branch)

-Fixed unnecessary write of a 64-bit register followed by a 32-bit read of the same value
 
To Ari64:

You're using ldr pc + literal pool for movimm. I had a patch committed to QEMU that uses movw + movt. Here is the code:

Code:
static inline void tcg_out_movi32(TCGContext *s,
                int cond, int rd, int32_t arg)
{
    int offset = (uint32_t) arg - ((uint32_t) s->code_ptr + 8);

    /* TODO: This is very suboptimal, we can easily have a constant
     * pool somewhere after all the instructions.  */

    if (arg < 0 && arg > -0x100)
        return tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff);

    if (offset < 0x100 && offset > -0x100)
        return offset >= 0 ?
                tcg_out_dat_imm(s, cond, ARITH_ADD, rd, 15, offset) :
                tcg_out_dat_imm(s, cond, ARITH_SUB, rd, 15, -offset);

#ifdef __ARM_ARCH_7A__
    /* use movw/movt */
    /* movw */
    tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12)
              | ((arg << 4) & 0x000f0000) | (arg & 0xfff));
    if (arg & 0xffff0000)
        /* movt */
        tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12)
                  | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff));
#else
    tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, arg & 0xff);
    if (arg & 0x0000ff00)
        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
                        ((arg >>  8) & 0xff) | 0xc00);
    if (arg & 0x00ff0000)
        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
                        ((arg >> 16) & 0xff) | 0x800);
    if (arg & 0xff000000)
        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
                        ((arg >> 24) & 0xff) | 0x400);
#endif
}
Note the code is still suboptimal for "standard" constant generation, but the point is to show movw/movt usage :)

I think you could give this a try: from a memory point of view the consumption is identical (1 instr + 1 word vs 2 instr). From the memory subsystem point of view, the movw/movt has the advantage of potentially less D/I caches and TLB thrashing. From a ld-use penalty, I can't say I don't know A8 well enough.
 
Ari64 said:
I made many of the optimizations discussed.

http://hotfile.com/dl/11644731/b4591c8/mupen64plus-arm-20090906.tar.gz.html


-Made the register elimination recursively evaluate loops

-Schedule loads to avoid the load-use penalty

-Improved code generation for ANDI, MFC1, and MTC1 instructions

-Eliminated redundant checks of the coprocessor status register (FPU unusable)

-When it is possible to precompute the target address, generate read/write calls inline instead of as a stub.

-Fixed a bug in the constant propegation (can not precalculate a load address across a branch)

-Fixed unnecessary write of a 64-bit register followed by a 32-bit read of the same value
And the result was................... (the suspense is killing me!)
 
Last edited by a moderator:
This thread is not for us to make random comments in. Go use the General Section and leave this section clear for the devs to communicate with one another.
 
Laurent said:
Note the code is still suboptimal for "standard" constant generation, but the point is to show movw/movt usage :)

I think you could give this a try: from a memory point of view the consumption is identical (1 instr + 1 word vs 2 instr). From the memory subsystem point of view, the movw/movt has the advantage of potentially less D/I caches and TLB thrashing. From a ld-use penalty, I can't say I don't know A8 well enough.

Load-use is one cycle like on ARM9 (not two cycles like on ARM7 or 0 cycles like in NEON). There's only one load-store pipe so you can't schedule them together, but what I'm curious about is if movt is dependent on the register it's moving to in the same stage movw moves to it. Because if it's not, then the two integer pipes should be able to perform them simultaneously. Even then, it'll still take more issue time in the pipes, but the cache benefits will probably outweigh it.

Ari64, I haven't looked at the code, but if you weren't using the wide move instructions then I'm curious if you're using the wide add/sub instructions where possible/necessary.
 
Last edited by a moderator:
Exophase said:
Even then, it'll still take more issue time in the pipes, but the cache benefits will probably outweigh it.
And you potentially don't waste one D-TLB entry provided you don't have the lit pool at all :)
Ari64, I haven't looked at the code, but if you weren't using the wide move instructions then I'm curious if you're using the wide add/sub instructions where possible/necessary.
These are Thumb2 only instructions.
 
Last edited by a moderator:
Back
Top