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.
Help with inline assembler
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
-
- Experienced Member
- Posts: 99
- Joined: Mon Dec 14, 2009 1:01 pm
The constant value 36 should be included in the second group of input operands:
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.
Code: Select all
asm("ldw %0, %1[%2]" : "=r"(k) : "r"(f), "r"(36));
Compiling with -S and having a look at the output?The reference to the array gets put in a register, but is there a way to know exactly which one? It changes.
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
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?
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?
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
You can get the address by changing the type of the argument to be an array:
This compiles to:
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));
}
Code: Select all
test:
ldc r1, 0x24
ldw r0, r0[r1]
retsp 0x0
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
Thanks Richard. Will try this later today.
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
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?
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?
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
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).
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).
-
- XCore Expert
- Posts: 844
- Joined: Sun Jul 11, 2010 1:31 am
The second time you use "h" and "l" (in the MACCS), they are both input and output,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?
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.
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
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-)
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-)
-
- Respected Member
- Posts: 283
- Joined: Fri Mar 19, 2010 4:49 am
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.
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.