Exophase said:
... so you don't have to continually perform bitplane interleaving at runtime. That is, if uae4all isn't already doing something like this. Could you point me in the direction of the functions you're trying to optimize?
I don't think that bitplane interleaving can be avoided, and I don't plan to rewrite the whole graphics engine. That would be too much work for the time I have available outside of my real-life job.
For starters, I've rewritten pfield_doline_n6(). It was simplest to work with, with clean, straightforward code, no branches, etc, and AmigaSYS4AGA boots in 64 colors, so it was also quick to test.
In unoptimized form, pure assembler code gave me some 10-15% overall speed increase. Unfortunately, it doesn't make much difference when you're getting +2 over already measly 14 fps, but I'm learning plenty of new stuff about ARM processors, so that kind of compensates.
I have that version optimized now, to the best of my understanding of ARM11 timings at least, but I haven't had the time to test it out yet. And since I was working under assumption that register ordering is different for db and ia suffixes, I'll have to make some reordering of instructions, but that's a piece of cake.
I'm expecting something like 20% final speed improvement with that function alone.
8-bitplane version of the function is next on the list, with one big difference from the 6-bitplane one -- I could fit almost everything in registers with 6 bitplanes. 8-bitplane version will have to access memory more often. After that one, I'm pretty much in the dark.
I am now trying to find some way to profile (and, if possible, optimize) all the functions in the emulator. IDE is Carbide C++ 2.3, it's supposed to be using GCCE 3, so I've been playing around with GCCE profiling options, but I keep getting "unresolved reference __gcov_init" errors. I will try it again after updating IDE 2.6 and GCCE to 4.
Here are some parts of the code, excluding the repeating sequences of ands and xors, which make for some 80% of the loop, and can't be optimized any further. If you have any general ideas on what else I could try, that would be welcome.
A couple of possibly dumb question first:
- Does value of link register have to be restored on function exit? I'm using it just like any other register for processing, so it gets corrupted. I could fix that with loading lr first, then "mov pc,lr" at the exit, but if it doesn't have to be restored, why bother.
- I'm assuming that 3-instruction generation of unsupported 32-bit constants (like 0x55555555) is faster than ldr-ing them directly from memory. Correct? Any other ideas on loading unsupported constants would be great as well.
- Can stack become 2-byte aligned? I'm assuming that it can't, since registers are always stored as full words, but initial allocation of stack space is still bugging me. I don't see any reason why it couldn't be initially allocated at halfword boundary, and that would require some more checks to get it to 8-byte alignment (and saving of some ldm/stm cycles in the loop).
Also, ignore (very) possible stack corruptions and wrong variable offsets, and any general bugs I've introduced after optimizing something that was actually working before optimization.
Couple of debugging sessions will fix all of that.
It's a shame text formatting is not preserved when posting, so code becomes rather unreadable, but comments explain all the major points of (attempted) optimizations.
-----------------------------
PFIELD_DOLINE_N6:
;@ (
;@ r0=uae_u32 *pixels,
;@ r1=int wordcount,
;@ r2=uae_u8 *line_data[lineno],
;@ r3=uae_u32 max_words
;@ )
stmdb sp!,{r4-r8,r10-r12,lr}
;@ Stack is forced to 8-byte alignement for less load/store
;@ cycles in the loop.
msr cpsr_f,#0 ;@ Clear all flags
tst sp,#4 ;@ Not 8-byte aligned?
;@ Reserve stack space for bitplane pointers.
sub sp,sp,#24
;@ 8-byte forced alignment.
;@ Flag Q in CPSR status field is used as indicator of
;@ forced alignement, since it won't be changed during
;@ execution of the loop.
subne sp,sp,#4 ;@ Align
msrne cpsr_f,#0x08 ;@ Set flag Q
;@ PUSH &pixels & wordcount, free r0 & r1 for use
stmdb sp!,{r0,r1}
;@ NOTE: ry have been selected for bx so that data doesn't have to be
;@ reshuffled before the "stmia rn,{rx...}" instruction at the
;@ end of the loop
;@
;@ bx <=> ry:
;@ b0 <=> r0
;@ b1 <=> r2
;@ b2 <=> r4
;@ b3 <=> r6
;@ b4 <=> r1
;@ b5 <=> r3
;@ b6 <=> r5
;@ b7 <=> r7
;@ Bitplane pointers are kept on the stack. They are PEEK-ed
;@ from the stack at the end of each iteration.
;@ Get bitplane pointers in temporary registers
;@ (registers unused before the computation of bx),
;@ so that bx can be LDR-ed directly into appropriate ry
;@ registers inside the loop.
add r0,r2,r3 ;@ b2 <=> DATA_POINTER (5)
add r0,r2,r3,lsl #2
add r8,r2,r3,lsl #2 ;@ b3 <=> DATA_POINTER (4)
add r10,r2,r3 ;@ b4 <=> DATA_POINTER (3)
add r10,r2,r3,lsl #1
add r11,r2,r3,lsl #1 ;@ b5 <=> DATA_POINTER (2)
add r12,r2,r3 ;@ b6 <=> DATA_POINTER (1)
mov lr,r2 ;@ b7 <=> DATA_POINTER (0)
;@ Adjust initial pointers by -4, so that loop can use
;@ pre-indexed mode with register update.
sub r0,r0,#4
sub r8,r8,#4
sub r10,r10,#4
sub r11,r11,#4
sub r12,r12,#4
sub lr,lr,#4
LINE_N6_LOOP: ;@ do { // initial wordcount should always be > 0
;@ Get bx and advance bitmap pointers to the next word.
;@ Then store new values of bitmap pointers over their
;@ old values on the stack.
;@ b7(r7) = GETLONG ((uae_u32 *)real_bplpt[0]); real_bplpt[0] += 4;
;@ b6(r5) = GETLONG ((uae_u32 *)real_bplpt[1]); real_bplpt[1] += 4;
;@ b5(r3) = GETLONG ((uae_u32 *)real_bplpt[2]); real_bplpt[2] += 4;
;@ b4(r1) = GETLONG ((uae_u32 *)real_bplpt[3]); real_bplpt[3] += 4;
;@ b3(r6) = GETLONG ((uae_u32 *)real_bplpt[4]); real_bplpt[4] += 4;
;@ b2(r4) = GETLONG ((uae_u32 *)real_bplpt[5]); real_bplpt[5] += 4;
ldr r7,[lr,#4]!
ldr r5,[r12,#4]!
ldr r3,[r11,#4]!
ldr r1,[r10,#4]!
ldr r6,[r8,#4]!
ldr r4,[r0,#4]!
;@ Don't PUSH, just POKE new bitplane pointers over their old values
stmia sp,{r0,r8,r10-r12,lr}
;@ 1st bitmask (0x55555555)
mov r0,#0x0055 ;@ r0, the first register stored
;@ (first for which interregister lock is removed)
orr r0,r0,r0,lsl #8
orr r0,r0,r0,lsl #16
;@ MERGE (b2(r4), b3(r6), 0x55555555, 1);
eor r8,r4,r6,lsr #1 ;@ tmp = (a ^ (b >> shift))
and r8,r0,r8 ;@ tmp = mask & tmp
eor r4,r4,r8 ;@ a ^= tmp
eor r6,r6,r8,lsl #1 ;@ b ^= (tmp << shift)
...
;@ 2nd bitmask (0x33333333 = 0x55555555 >>^ 2)
mov lr,lr,ror #2
;@ MERGE_0(b0(r0), b2(r4), 0x33333333, 2);
and r0,lr,r4,lsr #2 ;@ a = tmp = mask & (b>>shift);
eor r4,r4,r0,lsl #2 ;@ b ^= (tmp << shift)
...
;@ 3rd bitmask (0x0f0f0f0f)
mov lr,#0x000f
orr lr,lr,lr,lsl #8
orr lr,lr,lr,lsl #16
;@ MERGE (b0(r0), b4(r1), 0x0f0f0f0f, 4);
eor r12,r0,r1,lsr #4 ;@ tmp = (a ^ (b >> shift))
and r12,lr,r12 ;@ tmp = mask & tmp
eor r0,r0,r12 ;@ a ^= tmp
eor r1,r1,r12,lsl #4 ;@ b ^= (tmp << shift)
...
;@ 4th bitmask (0x00ff00ff)
mov lr,#0x00ff
orr lr,lr,lr,lsl #16
;@ MERGE (b0(r0), b1(r2), 0x00ff00ff, 8);
eor r8,r0,r2,lsr #8 ;@ tmp = (a ^ (b >> shift))
and r8,lr,r8 ;@ tmp = mask & tmp
eor r0,r0,r8 ;@ a ^= tmp
eor r2,r2,r8,lsl #8 ;@ b ^= (tmp << shift)
...
;@ 5th bitmask (0x0000ffff)
mvn lr,#0
lsr lr,#16
;@ Get &pixels from the stack
ldr r12,[sp,#8*4+4]
;@ MERGE (b0(r0), b2(r4), 0x0000ffff, 16);
eor r8,r0,r1,lsr #16 ;@ tmp = (a ^ (b >> shift))
and r8,lr,r8 ;@ tmp = mask & tmp
eor r0,r0,r8 ;@ a ^= tmp
eor r4,r4,r8,lsl #16 ;@ b ^= (tmp << shift)
...
;@ DO_SWLONG(pixels, b0);
;@ DO_SWLONG(pixels + 4, b2);
rev r0,r0 ;@ Reverse byte order...
rev r4,r4
;@ Get wordcount from the stack
ldr r10,[sp,#8*4]
...
subs r10,r8,#1 ;@ wordcount--
mov r8,r12 ;@ copy of &pixels to avoid r12 lock below
;@ Store bx all at once
stmia r12,{r0-r7}
;@ pixels += 8;
addne r8,r8,#8*4
strne r10,[sp,#8*4] ;@ Store wordcount
strne r8,[sp,#8*4+4] ;@ Store &pixels
;@ Don't POP, just PEEK bitplane pointers for next iteration
ldmneia sp,{r0,r8,r10-r12,lr}
bne LINE_N6_LOOP ;@ } while (wordcount > 0)
;@ Free reserved stack space.
;@ Forced 8-byte alignement of stack is indicated by Q bit set.
;@ SP is also manually adjusted, because of possible forced alignement.
mrs r0,cpsr
lsls r0,r0,#5 ;@ Shift Q bit into C flag
addcc sp,sp,#24+9*4 ;@ Reserved space was 24 bytes
addcs sp,sp,#28+9*4 ;@ Reserved space was 28 bytes
msrcs cpsr_f,#0 ;@ Clear all flags
ldmia sp,{r4-r8,r10-r12,pc}
;@ DoLine_pData:
;@ .align 4
;@ .long DoLine_Data
;@ ---
;@ .data
;@ .align 4
;@ DoLine_Data:
;@ ;@ Bitmasks
;@ .long 0x55555555
;@ .long 0x33333333
;@ .long 0x0f0f0f0f
;@ .long 0x00ff00ff
;@ .long 0x0000ffff
-----------------------------