Saturday, November 6, 2010

After spellunking for months trying to get REG_DEAD notes into the compiled RTL, it turns out that they are not necessary anymore. Apparently this changes somewhere in the 3.X versions of GCC (I want to say 3.5, but I'm not sure about that. I read about this at work earlier, and I don't remember the details right now. Not really important now.)

I read a lot of posts from the GCC developers, and apparently, I shouldn't need to modify anything beyond the machine-dependant code to achieve everything I'm looking for. This is really good to know, since that should help reduce the time spent researching the GCC front end. Although, I'm kinda glad I did that work now.

So I'm going to implement the optimizations listed in September as peepholes. Should be pretty straightforward, really.

Repeating the optimization list from above:

Baseline:
mov Rx, Rx (14 cycles)

These all assume compared register will be dead
Compare to 2: dect G (10 cycles)
Compare to 1: dec G (10 cycles)
Compare to -1: inc G (10 cycles)
Compare to -2: inct G (10 cycles)

A<0 -> inv A; A>=0 (10 cycles) lt
A<=0 -> neg A; A>=0 (12 cycles) le
A==0 -> neg A; A==0 (12 cycles) eq x
A!=0 -> neg A; A!=0 (12 cycles) ne x
A>0 -> neg A; A<0 (12 cycles) gt
A>=0 -> inv A; A<0 (10 cycles) ge

lt (<)
le (<=)
eq (==)
ne (!=)
gt (>)
ge (>=)
ltu (< unsigned)
leu (<= unsigned)
gtu (> unsigned)
geu (>= unsigned)

I might not use the C pattern though..
Assume instructions are in slow mem, registers are fast

inct r1; inct r2 (4+10+1 + 4+10+1 = 30 cycles) %100
inc r1; inc r2 (4+10+1 + 4+10+1 = 30 cycles) %100

c *r1+, *r2+ (4+14 + 8+4 + 8+4 = 42 cycles) %140
cb *r1+, *r2+ (4+14 + 6+4 + 6+4 = 38 cycles) %126

So this form saves two bytes, but is about a third slower, and is difficult to induce. I think I'll pass on this.

Tuesday, November 2, 2010

That last frame problem has been solved. I had written that the frame pointer had a role in determining whether R11 should be saved. That was a mistake, one has nothing to do with the other. This was seen when optimization was off because the frame pointer is aways used at this optimization level.

I don't know if this was causing problems yet, but frame_pointer_needed and df_ever_alive() were being factored into the R11 save calculation as well. These are always set for R11 since it's the return pointer, but it only really needs to be saved if the function is not a leaf.

Friday, October 29, 2010

I found another problem with the stack frame stuff. Poop. I noticed with Tursi's test code that the epilogue was popping one too many words. It turns out that the epilogue code assumes that if there is any stack to save, the return pointer will be saved, and so one more word needs to be popped. This has been fixed.

Fixed the mistaken leaf-ness mentioned above. The variable current_function_is_leaf is evaluated late, but the function which drives this, leaf_function_p(), appears to be valid some time earlier.

Uggh, not another problem... When no optimization is called for, the frame pointer is used to store local values. Unfortunately, the current mapping between the frame and stack pointers is wrong, and results in bad addresses being calculated for local values. There is zero offset between stack and frame, and locals are indexed off frame. No provision is made for the saved registers on the bottom of the stack.

Thursday, October 28, 2010

I've been working on lifetime calculation for REG_DEAD notes, but I got a message from the AtariAge forums. Tursi was trying to use the compiler, and found some stack layout problems. I got a chance to look at that today. I've found three problems, and fixed two of them.

In one of the prologue forms, the location of the saved registers was mistakenly calculated to be at the top of the stack. This is the only place where that assumption was made.

In the event of a call frame being needed without saved registers, no space was being allocated for the frame registers, The epilogue was fine in this case, and would result in a crash somewhere down the line.

The last problem is that the leaf-ness of a function seems to be calculated after tms9900_starting_frame_offset is called. This means that the frame offset calculation assumes that the link register needs to be saved, and leaves space for it. However, when the prologue is called, we know that the function is a leaf, and no space is saved for the frame, and stack corruption results. I need to find a way to check for leaf-ness earlier in the function construction. Somehow.

Monday, September 13, 2010

The optimizations would be nice, but the REG_NOTEs are not always (alright, hardly ever) present to select the optimizations above. So I need to get more invasive. I've made this test program to test the "neg; jeq" optimization.

extern int func();
int top()
{
int a = func();
if(a == 0)
{
func();
}
return(0);
}

Check out the debug output files, and track down where the live register usage is tracked. I should be able to root around in the internals to properly check for valid cases.

Saturday, September 11, 2010

I've just finished the updated string constant handling in GAS, it's now TI compliant, and looks pretty good. Of course, more testing is required. So now, it's time to get the compiler optimizations in.

So here's what I've got:

These all assume compared register will be dead
Compare to 2: dect G (10 cycles)
Compare to 1: dec G (10 cycles)
Compare to 0: abs G (12/14 cycles)
Compare to -1: inc G (10 cycles)
Compare to -2: inct G (10 cycles)

I could use neg (12 cycles), and inverted comparisons.

c *r1+, *r2+ to increment two registers at the same time. Hard to make a test for this...

A<0 : inv A; jgt
A<=0: << no optimization >>
A==0: neg A; jeq
A!=0: neg A; jne
A>0 : neg A; jlt
A>=0: inv A; jlt

Monday, September 6, 2010

I've found a quick test for zero compare from AtariAge: "abs Rn". This is two clocks faster than the "mov Rn, Rn", but destroys the value. In order to use this, I'll need to check to make sure the register is no longer used. A similar test would be "inv Rn", this saves four clocks.

Another thing I've seen is to use "cmp *Ra+, *Rb+" to increment two registers at the same time. That could be an intersting optimization, but hard to use.