Help with inline assembler

Technical questions regarding the XTC tools and programming with XMOS.
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Help with inline assembler

Post by bearcat »

Having trouble figuring out how to reference a value using inline assembler for an array of structures passed as an argument to a function. Like below:

void test(structure &f, int a)
int k;
asm("ldw %0, %1[%2]" : "=r" (k) : "r" (f) : "r" (36));

But during build: f is not defined. It is an array of structures, so I can't just reference it directly and there are multiple arrays.

Worst case I can have only a single array and manually pass a pointer as an unsigned. But that will cost me at least one extra instruction and will not be very pretty.

The reference to the array gets put in a register, but is there a way to know exactly which one? It changes.


User avatar
Jamie
Experienced Member
Posts: 99
Joined: Mon Dec 14, 2009 1:01 pm

Post by Jamie »

The constant value 36 should be included in the second group of input operands:

Code: Select all

asm("ldw %0, %1[%2]" : "=r"(k) : "r"(f), "r"(36));
And with f referencing a structure, compiling gives the error "error: non-scalar type in asm input operand". So it seems you can't use a (non-scalar) structure type as an assembly operand. Passing array references works fine though.
The reference to the array gets put in a register, but is there a way to know exactly which one? It changes.
Compiling with -S and having a look at the output?
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

Interestingly, the integer parameter always gets put in R0, even if I reorder the parameters. But the reference changes registers randomly with each build. So can't hard code the register.

I am going to have to change structures, if I must. The IIR filter code that I need this for goes from 35 instructions to around 25 using hand tuned assembler. Changing the structures around may well add a couple instructions which is not optimum.

Is there some "internal" compiler variable used at build time for this &f reference that I can use?
richard
Respected Member
Posts: 318
Joined: Tue Dec 15, 2009 12:46 am

Post by richard »

You can get the address by changing the type of the argument to be an array:

Code: Select all

struct s {
  int x;
};

void test(struct s f[], int a)
{
  int k;
  asm("ldw %0, %1[%2]" : "=r" (k) : "r" (f), "r" (36));
}
This compiles to:

Code: Select all

test:
          ldc       r1, 0x24
          ldw r0, r0[r1]
          retsp     0x0
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

Thanks Richard. Will try this later today.
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

Well the above tip doesn't work with an ARRAY or structures. My example above did not match my words. The call to the function doesn't allow an indexed structure with an array parameter. But I did figure out that if I reinterpret to an array in the function call you can use the above and this does give the correct reference. But you can now longer use the structure in your function. I also learned that I needed the load index immediate instruction instead. Lucky for me my structure was less than 12 words.

But, now the assembler is not compiling correctly. See the following code snippet:
int iirHigh( int sample, int f[])
{
int h, l, j, k;
asm("ldw %0, %1[9]" : "=r" (l) : "r" (f));
asm("ldc %0, 0" : "=r" (h) : ) ;
asm("ldw %0, %1[0]" : "=r" (j) : "r" (f));
asm("maccs %0, %1, %2, %3" : "=r" (h), "=r" (l) : "r" (j), "r" (sample));

The dissassembly:
0x00010184 <iirHigh>: ldw (2rus) r2, r1[0x9]
0x00010186 <iirHigh+2>: ldc (ru6) r2, 0x0
0x00010188 <iirHigh+4>: ldw (2rus) r2, r1[0x0]
0x0001018a <iirHigh+6>: maccs (l4r) r3, r2, r2, r0

r1 holds the correct index. r0 is the input. But both L and J are assigned to r2??? There are 12 registers to use. The routine should only need 6. Is there a limit of variables for inline assembly?

Ideas?
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

All right, I figured out my problem. I was trying to use XC to allocate assembler registers for me with variables thinking that the other registers were off limits. That was not the right train of thought at all!

Place any variables, or parameters into registers you know first thing, then have at it with all the rest of the registers (r0-r11) by hardcoding them in the instructions as you go. I presume I can then place them "back" into variables at the end (need to verify this).
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

bearcat wrote: int iirHigh( int sample, int f[])
{
int h, l, j, k;
asm("ldw %0, %1[9]" : "=r" (l) : "r" (f));
asm("ldc %0, 0" : "=r" (h) : ) ;
asm("ldw %0, %1[0]" : "=r" (j) : "r" (f));
asm("maccs %0, %1, %2, %3" : "=r" (h), "=r" (l) : "r" (j), "r" (sample));

The dissassembly:
0x00010184 <iirHigh>: ldw (2rus) r2, r1[0x9]
0x00010186 <iirHigh+2>: ldc (ru6) r2, 0x0
0x00010188 <iirHigh+4>: ldw (2rus) r2, r1[0x0]
0x0001018a <iirHigh+6>: maccs (l4r) r3, r2, r2, r0

r1 holds the correct index. r0 is the input. But both L and J are assigned to r2??? There are 12 registers to use. The routine should only need 6. Is there a limit of variables for inline assembly?

Ideas?
The second time you use "h" and "l" (in the MACCS), they are both input and output,
so the correct constraint is "+r", not "=r".

For what it's worth, you would be much better off using real assembler for this
function, not inline assembler, i.e. in a .s file.
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

Thanks for everyone's help. I had not seen the +r syntax yet. Will give that a try.

An assembler .S would be better and I may eventually do that. But there is a learning curve to that I don't want to spend the time on so far. Not nearly as much documentation or examples.

I do miss the days of only assembler for embeded code, though. 8-)
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

Well, I thought I had this figured out before. Not!

No you can't use any registers that are not allocated as variables, you will overwrite something else. No the "+r" syntax does not work.

One of the big reasons I see with using inline assembly is not having to push and pop all your registers and any other overhead (for a small function). Correct me if I am wrong, but if I use a .S assembly file I will have to do this for all the registers I use plus any overhead.

For a 25 line function that adds at least 8+ lines of code in my case. Just using XC was not that much worse.

But, I think I have figured it out ( :lol: ) If you only "consume" the variables once ("=r") and use "r" all the rest of the time, it seems to generate correct code. I still need to verify operation, but dissassembly looks correct.