Around 5% improvement.Nation.A.List said:And the result was................... (the suspense is killing me!)
Last edited by a moderator:
Around 5% improvement.Nation.A.List said:And the result was................... (the suspense is killing me!)
I guess I can try it. It looks like movt/movw is encoded as cmp/tst with the s bit clear. ARM's documentation on this stuff totally sucks.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.
@ari64 Thanks for the feedback. Worth the effort as improvements are cumulative.Ari64 said:Around 5% improvement.Nation.A.List said:And the result was................... (the suspense is killing me!)
You have DDI 406? If not, then do your request here: http://silver.arm.com/browse/AR570Ari64 said:I guess I can try it. It looks like movt/movw is encoded as cmp/tst with the s bit clear. ARM's documentation on this stuff totally sucks.
MOVW
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
cond |0 0 1 1 0 0 0 0|imm4 |Rd |imm12 MOVW
cond |0 0 1 1 0 1 0 0|imm4 |Rd |imm12 MOVT
It's for the debugger. I guess I could nullify this when not compiling with debugging.Laurent said:Another comment: why doing strcpy in new_recompile_block instead of plain assignment?
That's not the point: you could simply do:Ari64 said:It's for the debugger. I guess I could nullify this when not compiling with debugging.
const char *insn[MAXBLOCK];
...
insn[i] = "SLL";
#ifdef USE_DISASS
#define DIS(x,s) insn[x] = s
#else
#define DIS(x,s) do ; while (0)
#endif
...
DIS(i, "SLL");
Laurent said:These are Thumb2 only instructions.
Exophase said:In terms of functionality per instruction I believe Thumb-2 to be the preferred ISA. I'd rather have the extended immediates, movw, movt, wide add/subtract, the bit field operations, compare + branch, and the other less interesting instructions than predicate per instruction.
Laurent said:BTW almost all the T2 instructions you describe are in ARM instruction set (the only missing ones being IT and compare and branch [which is so CISCy that its implementation is probably bad on most advanced cores]).
Ari64 said:I guess I can try it. It looks like movt/movw is encoded as cmp/tst with the s bit clear. ARM's documentation on this stuff totally sucks.
lw $1,0x7fff($2)
->
movw r0,#0x7fff
ldr r1,[r2+r0]
Laurent said:It's basically impossible to make bpred better for T2 than for ARM, without using significantly more silicon for obvious reasons (e.g., alignment less constrained, higher possible density of branches). So again I'd prefer that silicon to be spent and more productive things
Laurent said:As far as address generation goes, MIPS offsets being 15+1 bits while ARM's being 12+1 bits, you'll always need one instruction + one load in the worst case:
What I meant is that no matter whether you have add/sub 16-bit, you might have to use 1 more instruction and movw is enough. Does this sound better to you?Exophase said:Laurent said:As far as address generation goes, MIPS offsets being 15+1 bits while ARM's being 12+1 bits, you'll always need one instruction + one load in the worst case:
I'm well aware of this, which my post should have made obvious. +/-12bit will almost definitely cover substantially more cases than the standard ror8 ARM format, especially for load offsets. "Always, in the worst case" is kind of a funny thing to say.
Laurent said:What I meant is that no matter whether you have add/sub 16-bit, you might have to use 1 more instruction and movw is enough. Does this sound better to you?
Exophase said:Laurent said:What I meant is that no matter whether you have add/sub 16-bit, you might have to use 1 more instruction and movw is enough. Does this sound better to you?
The movw by itself won't work so long as he's range-checking the final address such that the address generation must happen before the load. But even then it doesn't gain you anything because the address itself can be modified with an add/sub for the top 8bits then an address offset for the bottom. Two adds/subtracts wins over a movw and an add no matter what because it uses less registers and may be more parallelizable.
Ari64 said:ADD/SUB/ORI/XORI which do not fit within a shifted 8-bit immediate generate two sequential instructions. ANDI generates a combination of UXTH and/or BIC if possible, otherwise uses a temporary register. The other case where we need a temporary register is SLTI(U), since two sequential subtracts will not properly set the carry/overflow flags.
Ari64 said:LUI, and arithmetic/logic operations with r0 as a source, go into the constant propagation queue, and are filled in later when we determine what the final value is.
Ari64 said:For address generation, the offset is usually aligned, so 12-bit immediates aren't a lot better than shifted 8-bit immediates.
It's not useful for ori, but might be useful for andi. For example,Exophase said:Yes, so movw/movt aren't as useful as they sound. Except for ori, where movw does the same thing.
ANDI r1,r1,0xFF
-> and r1,r1,#0xFF
ANDI r1,r1,0xFFFF
-> uxth r1,r1
ANDI r1,r1,0xFFF
-> uxth r1,r1
-> bic r1,r1,#0xF000
ANDI r1,r1,0xAAAA
-> movw r14,#0xAAAA
-> and r1,r1,r14
It uses 12 registers for the register cache, which is more than enough. In fact, it keeps so much stuff in registers that it's causing a significant number of loads/stores at branches, and I've actually seen a reduction in code size by freeing registers more aggressively. I haven't investigated whether such a strategy would result in improved performance though.Exophase said:I use r14 as a temporary too, but if you can use it for short term register cache that's even better. Although in your case I don't think you call functions from the code that much either.
Ari64 said:Function calls occur for loads/stores that do not go to main memory, DMULT, DDIV, and many floating point operations.