How to mix c code and asm in a c-function?

149 Views Asked by At

I like to do some optimisation and so I like to substitute a line of c code with 2 lines of assembler.

rb->am += N

the ringbuffer struct rb gets incremented by an variable N

I am struggling to get the asm right. Here comes my pseudo code

void myFunc(struct rb, const uint16_t N)
...
asm(
"mov w0, %0\n"
"add %1, w0"
: rb->am
: N
: w0
);

If I understand the asm keyword its

asm(<instruction> : <input operants> : <output operants> : <clobbers>)

I am currently don't know how to get the syntax right.

W0 is a register. I assume that I need to tell my compiler that I am using that.

rb->am needs to be resolved into a memory address.

N is a function parameter (like rb).

Any idea how to get this right?

[update] The target platform is a dspic33 from microchip. I looked up the programmer manual. The only ADD that alters a uint16 variable at the RAM is working against the W0/WREG.

ADD f {,WREG} -> Destination = f + WREG

Smth like (my pseudo code)

MOV WREG, N
ADD addr(rb->am)

is needed. Here a link to my source https://ww1.microchip.com/downloads/en/DeviceDoc/70000157g.pdf

2

There are 2 best solutions below

2
Stefan Jaritz On BEST ANSWER

Solved:

void util_atomic_add(uint16_t * pV, const uint16_t am) {
    asm volatile (
        "add %1, [%0], [%0]\n"
        : "+r"(pV)
        : "r"(am)
    );
}

void util_atomic_sub(uint16_t * pV, const uint16_t am) {
    asm volatile (
        "subr %1, [%0], [%0]\n"
        : "+r"(pV)
        : "r"(am)
    );
}

Note:

  1. this is a DSPIC33 MCU solution.
  2. creating a c-function that only has the ASM code makes life much easier
  3. in outputs "=m"(pV) gives you the memory - but in my case as indirect address (W*+offs). ADD and SUB at the DSPIC33 do not support indirect addressing. Means you take "&rb->am" and store it in a register. By doing so you can use direct addressing(pD = &rb->am) "+r"(pD) in inputs "r"(am) takes care that the function parameter is stored at some register
  4. check the code on assembler base, compile with optimizations to double check

Thx for the fast and good answers

1
Calum On

Why not use C? The obvious C code generates optimal code for the constraints provided. Well, we had to guess about the structure for rb, but:

struct RB {
  int a,b,c,am;
};

void myFunc(struct RB *rb, uint16_t N) {
  rb->am += N;
}

generates clean code (XC-DSC v3.00, -O1):

_myFunc:
        add.w   w0,#6,w2
        add.w   w1,[w2],[w2]
        return

edit: I 'just' see the note about the atomic increment requirement

Provided inline assembler is not correct, IMHO. "+r" means that the register is changing value, it is not... "memory" is changing value and the register stays the same, this feels more accurate:

void alt_util_atomic_add(uint16_t * pV, const uint16_t am) {
    asm volatile (
        "add %1, [%0], [%0]"
        : /*outputs */
        : /*inputs */"r"(pV), "r"(am)
        : /*clobbers*/ "memory"
    );
}