Hi,
Taking the code snippet below,
char* ptr = (char*)0x0000; short size = 0x100; short sum = 0x00; do { sum += *ptr++; } while (size--)
This will cause C51 to allocate "ptr" and "size" in RAM.
MOV R3,#00H MOV R2,#00H MOV R1,#00H MOV DPTR,#pre?1251 MOVX @DPTR,PR0 MOV DPTR,#size?1252 MOV A,#01H MOVX @DPTR,A INC DPTR CLR A MOVX @DPTR,A
and everytime "size" is decremented, or "ptr" is incremented, the values are loaded back into the RAM.
Is there a way to force C51 to not use RAM but use the registers only as "ptr" and "size" will never be used anymore after that. What I mean is, is there a way to force C51 to compile to something like this,
mov dptr, #WORD0(00h) mov r4, #01h ; r4:r5 = size mov r5, #00h mov r6, #00h ; r6:r7 = sum mov r7, #00h loop: movx A, @dptr+#000h addw 006h, A ; r6:r7 += A inc dptr subw 004h, 001h ; r4:r5-- jnc loop ; add code to store r6:r7 into RAM
Any advice would be greatly appreciated. Thanks in advance.
By manually writing in assembly using registers, I manage to speed up the task by 50%. However it would be tedious to do so as the reason to use C is to simplify coding. while wrinting small routines in asm for speed reasons is a very valid approach, it you have to write large amounts of code in asm to achieve your goal, this is the wrong approach unless your product is to ship in millions. Wrinting small routines in asm for speed reasons is hardly 'tedious' and, typically, for the very reason they are small it will be write, debug and forget, so the 'tedium' is just once.
Erik
What I want is for it to increment dptr, eptr, pr0 or pr1 directly and thus avoid re-loading it back from RAM to register in each loop.
Sounds like you are more than happy to take on the compiler's job. Ignore the compiler then, write in assembly. It also sounds like you are not happy with the quality of the code generated by this compiler. By the way, I share your view. It would be reasonble to look for a different compiler. I hear IAR make good compilers, and they happen to have one for the 8051.
Btw, it's not just pointer. If possible, I also want variable which is used as counter to be kept in register without saving it to RAM each time it is incremented or decremented.
Maybe I need to clarify further,
The compiler will eventually load the pointer into dptr, eptr, pr0 or pr1 before read the value it pointed to. When I increment it in C, it will produce a code to increment the variable which is kept in the RAM, then it will load it back to either dptr, eptr, pr0 or pr1 before reading the value again. What I want is for it to increment dptr, eptr, pr0 or pr1 directly and thus avoid re-loading it back from RAM to register in each loop.
C51 produced code,
1. Allocate RAM area for pointer 2. Load pointer with address 3. Loop 3.1 Load pointer from RAM to register (dptr, eptr, pr0 or pr1) 3.2 Load value from dptr, eptr, pr0 or pr1 3.3 ... 3.4 Increment pointer in RAM 4. ...
What I want is,
1. Load address into register (dptr, eptr, pr0, or pr1) 2. Loop 2.1 Load value from dptr, eptr, pr0 or pr1 2.2 ... 2.3 Increment dptr, eptr, pr0 or pr1 3. ...
My mistake. I just simply write a simple code snippet as example to explain what I meant. Thank you for pointing out the mistake in the sample.
However, I have no issue writing the code in ASM to optimize it. The reason for this thread is to find out whether there is a way to at least force C51 to compile to code to avoid using RAM for variable that is used within the scope/loop. If possible, forcing it to use the DPTR, EPTR, PR0, PR1 directly.
the chip I'm using supports additional extended instruction set
Aha. And the reason you had to keep that crucial bit of information to yourself all this time was ...?
I'm using a generic pointer because the address is a far address.
And how were you planning on convincing the compiler that
(char *)0x0000
is something other than a not-far-at-all idata pointer?
Thanks for pointing that out but the chip I'm using supports additional extended instruction set which includes addw and it costs 3 machine cycles. Also since R0 - R7 is mapped to offset 0 to 7 of idata, I decided to access it directly.
Also, the optimization option is enabled and if I never increment the pointer, the pointer will be kept in the register.
It is also an 8-bit processor - which means that multibyte operations have to be coded using multiple single-byte instructions.
If high performance of multibyte operations is a key requirement of your application, then you should seriously reconsider whether the 8051 is an appropriate choice...
But does the taks actually need to be speeded up by 50% - or, indeed, at all?
"However it would be tedious"
So, unless it is really essential, don't do it!
As has already been suggested, if the performance is adequate, there is no point in messing about trying to optimise it!
No, it won't. Not unless you avoid turning on optimization or use a horribly wrong memory model.
addw 006h, A ; r6:r7 += A
Surely you're aware that no machine instruction like 'addw' exists in an 8051, right? And that, the 8051 being a so-called accumulator-based design, the target of all arithmetic machine instruction is the accumulator, not some memory address?
Oh, and if you're looking for efficient code, why are you using generic pointers?
"Is it ANSI required?"
The keyword must be processed according to the standard, which means it does mean something gramatically. But most new compilers totally ignore it for the code generation phase.
Thank you everyone for the information. I have tried using the 'register' keyword before posting the question here but so far it is ignored by the compiler. By manually writing in assembly using registers, I manage to speed up the task by 50%. However it would be tedious to do so as the reason to use C is to simplify coding. I really hope there is a way to ask the compiler do what is needed.
Not exactly. Suppose you specify 10 register variables? How many get used depends on the number available. Even worse you only specify 1 register variables. The compiler uses them all then what? An Error? You move some code around an now it is OK. The register keyword never guaranteed that a variable would be in a register. Only that it would be nice if it were.
Is it ANSI required?
Most newer compilers sees it as a dummy keyword to 100% ignore.
I'm quite sure that's untrue because otherwise most newer compilers would be defective. register still has ssome required semantic effect that compiler writers are not at liberty to ignore: it forbids taking address of any object so qualified, and code violating that rule has to be diagnosed. To do that, the compiler must heed the register keyword.
The starting text in that link said "should be stored".
But it was just added as a hint, letting a developer put some extra priority on the decisions of what variables to allocate to registers.
It was only applicable when compilers were dumb and variables lived in memory or in registers for the full lifetime of a function.
Todays compilers performs huge amounts of lifetime analysis and other optimization steps, so a variable may sometimes be stored in a register, and at other times be stored in RAM. It's just a question of what the compiler have found to be the most efficient alternative for the different parts of the code.
Most newer compilers sees it as a dummy keyword to 100% ignore. We have the volatile keyword to force memory accesses. And we have the optimization flags to tell how much work we want the compiler to spend on tweaking the actual code generated.
View all questions in Keil forum