mix c and assembly cortex-m0

Viewed 604

I want write a routine for cortex-m0 in GCC C where mix C and assembly code. I am trying something like this

__attribute__((naked)) uint8_t dummy(uint8_t value)
{
    asm volatile (
            "push   {r1-r7, lr}\n\t"
             // R0 contains input value
             //here i do some operation on registers r0..r7         
             //.....
             //I guess result will be in the R0 register
            "MOV R0, R3\n\t"

            "pop    {r1-r7, pc}\n\t"
        );
}

but unfortunately i have problems:

  • my program crashes because of this.
  • compiler claims that my "dummy" routine doesn't have return value

EDIT, WORKING CODE:I moved the code to the separate .s file and final solution looks like this

    .syntax unified
    .arch armv6-m
    .text
    .thumb
    .thumb_func
    .align 1
    .globl    dummymethod
    .type    dummymethod, %function
dummymethod:
    .fnstart
            PUSH    {r1-r7, lr}
            // here some operation on registers R0..R7

            MOV R0, R3 //result is in R0

            pop {r1-r7, pc}

    .pool
    .cantunwind
    .fnend
    .size   dummymethod,.-dummymethod
2 Answers

use real assembly

.cpu cortex-m4
.thumb
.globl dummy
.thumb_func
dummy:
    mov r0,r3
    bx lr

the calling convention allows for modification of r0-r3, return in r0, you are not touching r4,r5,r6,r7,lr so no need to push those.

ugly but you can assemble it with gcc, gnu assembler, as, is preferred (arm-whatever-as)

Falling off the end of a naked function is not actually an error. Warning in that case was a GCC bug that was fixed somewhere before GCC 5.4. (But code-gen is the same; older GCC versions that warn don't break your code.)

There are only a few minor benefits to writing a naked function in C + asm:

  • Having the compiler emit thumb vs. ARM directives if you have code that can compile for either mode. (Cortex-M0 is thumb-only so no benefit for your case.)
  • debug info, dynamic library function size, and other metadata stuff like that.
  • get the compiler to do C++ name mangling for you instead of declaring it extern "C", if you were writing C++.
  • You can maintain it next to some actual C code it interacts with.

Otherwise you might as well just write a separate asm file. It still can't inline like a non-naked wrapper function around a GNU C Extended asm statement could.

I'm not an ARM expert, but there's nothing wrong with the way you were using inline asm / naked function. The body of a naked is basically the only sane use-case for GNU C "Basic" asm (no constraints).

If you compile this with the right GCC options, GCC should emit the same directives you were using in stand-along asm. (Although not .cantunwind in case that matters.)


From Godbolt, gcc5.4.1 -O2 -Wall -mcpu=cortex-m0 -mthumb -fverbose-asm compiles it cleanly (no warnings). This is the asm output, with directives unfiltered, but some manual editing to remove directives and labels I think are just clutter (including debug info):

        .syntax unified
        .cpu cortex-m0
        .fpu softvfp

        .text
        .global dummy
        .code   16
        .thumb_func
        .type   dummy, %function
dummy:
        .syntax divided
        push   {r1-r7, lr}
        MOV R0, R3
        pop    {r1-r7, pc}

        .thumb
        .syntax unified
        .cfi_endproc
        .size   dummy, .-dummy

Since you're on a thumb-only CPU you don't need thumb interworking via bx lr, but as @old timer points out: if you can leave LR untouched during your function then you can just return with bx lr instead of popping it back into PC.

Also note that the first arg is passed in R0, so reading R3 is reading a register that your prototype says is not an input.


Moving this to a stand-alone asm file is totally fine, just as valid, and probably a better choice. But since you asked about doing it with a naked function, that should also work.

Related