Getting GCC for manual assembly optimization

In an attempt to force GCC not to generate a load-modify-store operation every time I do |= or &= , I defined the following macros:

 #define bset(base, offset, mask) bmanip(set, base, offset, mask) #define bclr(base, offset, mask) bmanip(clr, base, offset, mask) #define bmanip(op, base, offset, mask) \ asm("pshx");\ asm("ldx " #base);\ asm("b" #op " " #offset ",x " #mask);\ asm("pulx") 

And they work great; a disassembled binary is perfect.

The problem arises when I use several in sequence:

 inline void spi_init() { bset(_io_ports, M6811_DDRD, 0x38); bset(_io_ports, M6811_PORTD, 0x20); bset(_io_ports, M6811_SPCR, (M6811_SPE | M6811_DWOM | M6811_MSTR)); } 

This leads to:

 00002227 <spi_init>: 2227: 3c pshx 2228: fe 10 00 ldx 0x1000 <_io_ports> 222b: 1c 09 38 bset 0x9,x, #0x38 222e: 38 pulx 222f: 3c pshx 2230: fe 10 00 ldx 0x1000 <_io_ports> 2233: 1c 08 20 bset 0x8,x, #0x20 2236: 38 pulx 2237: 3c pshx 2238: fe 10 00 ldx 0x1000 <_io_ports> 223b: 1c 28 70 bset 0x28,x, #0x70 223e: 38 pulx 223f: 39 rts 

Is there a way to get GCC (3.3.6-m68hc1x-20060122) to automatically optimize redundant stack operations?

+8
assembly gcc 68hc11
source share
1 answer

gcc will always issue assembly instructions that you say to emit. Therefore, instead of explicitly writing code to load registers with the value you want to manipulate, instead you want to tell gcc to do this on your behalf. You can do this with case restrictions.

Unfortunately, the 6811 code generator does not seem to be a standard part of gcc --- I do not review the documentation in the manual. Therefore, I cannot point you to a specific bit of the document. But the general bit that you should read is here: http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm

The syntax is fancy, but a summary:

 asm("instructions" : outputs : inputs); 

... where inputs and outputs are lists of constraints that tell gcc what value to put where. Classic example:

 asm("fsinx %1,%0" : "=f" (result) : "f" (angle)); 

f indicates that the named value should go into the floating point register; = indicates exit; then the register names are replaced in the instructions.

So, you probably want something like this:

 asm("b" #op " " #offset ",%0 " #mask : "=Z" (i) : "0" (i)); 

... where i is the variable containing the value you want to change. Z you need to find in 6811 gcc docs --- this is a constraint that represents a case that is valid for the asm command that is created. 0 indicates that the input shares the register with output 0 and is used for read / write values.

Since you told gcc which register you want i be, it can integrate this knowledge into its register allocator and find the least costly way to get i where you need it with the least amount of code, (Sometimes there is no extra code.)

The built-in gcc assembly is highly distorted and strange, but quite powerful. It is worth spending some time to fully understand the system of restrictions in order to make the best use of it.

(By the way, I don’t know the code 6811, but did you forget to put the result of the operation somewhere? I expect to see that stx matches stx .)

Update: Oh, I see what bset is doing bset --- it writes the result back to the memory location, right? It is still doable, but it is a little painful. You need to tell gcc that you are modifying this memory location so that it does not rely on any cached value. You will need to have an output parameter with a m constraint that represents this location. Check documents.

+9
source share

All Articles