Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clang source-level coverage profiling instrumentation does not honor the attribute naked #127498

Open
miyuki opened this issue Feb 17, 2025 · 1 comment

Comments

@miyuki
Copy link
Collaborator

miyuki commented Feb 17, 2025

Clang source-level instrumentation (the -fprofile-instr-generate option) does not honor the function attribute naked and generates a prologue that might interfere with the function body. For example, the following code

extern "C" __attribute__((naked))
void semihost(unsigned op __attribute__((unused)),
              unsigned int* c __attribute__((unused))) {
  __asm(
    "PUSH {R4, LR}  \n"
    "SVC  #0xab     \n"
    "POP  {R4, PC}  \n"
  );
}

When compiled with the current trunk build of Clang, using the flags -target arm-none-eabi -march=armv8-a -O -fprofile-instr-generate produces the following LLVM IR for the function semihost:

define dso_local void @semihost(i32 noundef %0, ptr noundef %1) #0 {
  %3 = load i64, ptr @__profc_semihost, align 8
  %4 = add i64 %3, 1
  store i64 %4, ptr @__profc_semihost, align 8
  tail call void asm sideeffect "PUSH {R4, LR}  \0ASVC  #0xab     \0APOP  {R4, PC}  \0A", ""() #1
  unreachable
}

Which, in turn, gets compiled into the following assembly:

semihost:
        movw    r0, :lower16:.L__profc_semihost
        movt    r0, :upper16:.L__profc_semihost
        ldrd    r2, r3, [r0]
        adds    r2, r2, #1
        adc     r3, r3, #0
        strd    r2, r3, [r0]
        push    {r4, lr}
        svc     #171
        pop     {r4, pc}

This is incorrect because the function parameters are passed in the registers r0 and r1, and they get overwritten.

I think the correct behavior would be not to instrument naked functions, similarly to how it is done for IR-level coverage: #108552

@llvmbot
Copy link
Member

llvmbot commented Feb 17, 2025

@llvm/issue-subscribers-clang-codegen

Author: Mikhail Maltsev (miyuki)

Clang source-level instrumentation (the `-fprofile-instr-generate` option) does not honor the function attribute `naked` and generates a prologue that might interfere with the function body. For example, the following code
extern "C" __attribute__((naked))
void semihost(unsigned op __attribute__((unused)),
              unsigned int* c __attribute__((unused))) {
  __asm(
    "PUSH {R4, LR}  \n"
    "SVC  #<!-- -->0xab     \n"
    "POP  {R4, PC}  \n"
  );
}

When compiled with the current trunk build of Clang, using the flags -target arm-none-eabi -march=armv8-a -O -fprofile-instr-generate produces the following LLVM IR for the function semihost:

define dso_local void @<!-- -->semihost(i32 noundef %0, ptr noundef %1) #<!-- -->0 {
  %3 = load i64, ptr @<!-- -->__profc_semihost, align 8
  %4 = add i64 %3, 1
  store i64 %4, ptr @<!-- -->__profc_semihost, align 8
  tail call void asm sideeffect "PUSH {R4, LR}  \0ASVC  #<!-- -->0xab     \0APOP  {R4, PC}  \0A", ""() #<!-- -->1
  unreachable
}

Which, in turn, gets compiled into the following assembly:

semihost:
        movw    r0, :lower16:.L__profc_semihost
        movt    r0, :upper16:.L__profc_semihost
        ldrd    r2, r3, [r0]
        adds    r2, r2, #<!-- -->1
        adc     r3, r3, #<!-- -->0
        strd    r2, r3, [r0]
        push    {r4, lr}
        svc     #<!-- -->171
        pop     {r4, pc}

This is incorrect because the function parameters are passed in the registers r0 and r1, and they get overwritten.

I think the correct behavior would be not to instrument naked functions, similarly to how it is done for IR-level coverage: #108552

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants