asm directive: symbolic label syntax

Helllo,

I have tried to adapt gnu as code for Apple Silicon, and I have found explanations for most differences. There is one issue I still have:

In inline C assembly, the following works on the GNU toolchain:

asm (
  "MOV X4, %2\n"
  "loop: LDRB W5, [%1], #1\n"
  "CMP W5, #'z'\n"
  "BGT cont\n"
  "CMP W5, #'a'\n"
  "BLT cont\n"
  "SUB W5, W5, #('a'-'A')\n"
  "cont: STRB W5, [%2], #1\n"
  "CMP W5, #0\n"
  "B.NE loop\n“
// …
);

However, on the clang toolchain, I apparently must replace the forward label ‚cont‘ with a numeric value, like this (note: the backward label 'loop‘ still works):

asm
(
  "MOV X4, %2\n"
  "loop: LDRB W5, [%1], #1\n"
  "CMP W5, #'z'\n"
  "BGT 2f\n"
  "CMP W5, #'a'\n"
  "BLT 2f\n"
  "SUB W5, W5, #('a'-'A')\n"
  "2: STRB W5, [%2], #1\n"
  "CMP W5, #0\n"
  "B.NE loop\n“
//…
);

When using as by itself and not in inline assembly, the numeric label works.

Is there a reason for this? Is this a bug? Can I find any sort of documentation on the (inline) assembly syntax for clang?

Thanks a lot!

Alex

https://github.com/below/HelloSilicon#listing-9-8

Do you actually have a complete self-contained example? Try godbolt.org.

Joerg

Thanks a lot for your answer, and yes, I do:

Working code (using numeric label): https://github.com/below/HelloSilicon/blob/main/Chapter%2009/uppertst4.c
Non-working (original) code: https://github.com/below/HelloSilicon/blob/4c4b2911c43644adfd3b78aee093857444f28472/Chapter%209/uppertst4.c

Compile using the makefile in the same folder, or using "clang -o uppertst4 uppertst4.c“

On godbolt, running the original code through armv8-a-clang 11.0.1 or trunk, everything seems to work fine. There is an unrelated warning, but no errors. The output shows both the loop and the cont labels: https://godbolt.org/z/xY5afcYdW

Compiling the code with the symbolic cont label on an M1 using clang-1300.0.29.3 produces the errors:

uppertst4.c:22:4: error: conditional branch requires assembler-local label. 'cont' is external.
                "BGT cont\n"
                 ^
<inline asm>:4:1: note: instantiated into assembly here
BGT cont
^
uppertst4.c:24:4: error: conditional branch requires assembler-local label. 'cont' is external.
                "BLT cont\n"
                 ^
<inline asm>:6:1: note: instantiated into assembly here
BLT cont
^
uppertst4.c:28:4: error: conditional branch requires assembler-local label. 'loop' is external.
                "B.NE loop\n"
                 ^
<inline asm>:10:1: note: instantiated into assembly here
B.NE loop
^

Notably, now for both loop and cont, while it is sufficient to replace cont with a numeric label.

I would be glad to find out if I am doing it wrong, or if this is an issue in clang. And now my curiosity is raised why godbolt appears to differ from clang on macOS …

Thanks again

Alex

Does the behavior change if you declare “cont” as a global symbol? Does the symbol have to be “cont”? Would “.Lcont” suffice? Many targets treat leading “.L” as implicitly local symbols.

Hello,

one thing I should stress: This appears to be a C-Frontend issue (and thus I am posting it here) because when using as directly, this problem does not occur: https://github.com/below/HelloSilicon/blob/main/Chapter%2005/upper.s

I have taken your suggestions: Changing the name of „cont“ to something that does not sound like a keyword does not help.
Adding a ".global _faz\n“ (or ".global faz“) does not change anything, neither does „BGT .Lfaz\n“ seem to have any effect.

Let me know if you have any other ideas

Thanks

Alex

Thanks for everyone on this list!

The big difference between my native assembly code and the inline code is, that the cfe generates the directive: ‘.subsections_via_symbols’ , and I will have to do some searching to understand precisely what this does …

But because of this, indeed, labels must be prefixed with „L“. Like

        b.lt Lcont
        sub w5, w5, #32
Lcont:
        strb w5, [x9], #1

It also keeps my curios why the gcc frontend seems to behave differently here

Can you post a godbolt link? that would also show which command line options you’re using.

So this is with gas and -fno-integrated-as?

Hello Brian (and everyone who is reading this),

allow me to be very verbose (-vvvv) so that you, and others, can understand what I did, why I did it, and what I found:

## Motivation

The main objective was to provide an "Apple Companion Guide" to the book „Programming with 64-Bit ARM Assembly Language“ by Stephen Smith. The book uses the RaspberryPi 4, Linux and accordingly the GNU toolchain and GNU syntax. I wanted to provide both information on how Apple aarch64 devices differ, as well as modified, runnable source code. You can see all of what I did here: https://github.com/below/HelloSilicon

## The Problem

I ran into an issue when it came to an example using inline-assembly in C. Here is the original code, which works on ARM64 Linux, simply by invoking gcc on the file without any flags: https://github.com/below/HelloSilicon/blob/4c4b2911c43644adfd3b78aee093857444f28472/Chapter%209/uppertst4.c

When compiling the same exact file with clang on an M1 Mac (again, no flags), there is a trivial warning that we will ignore, and the following errors:

uppertst4.c:22:4: error: conditional branch requires assembler-local label. 'cont' is external.
               "BGT    cont\n"
                ^
<inline asm>:4:1: note: instantiated into assembly here
BGT     cont
^
uppertst4.c:24:4: error: conditional branch requires assembler-local label. 'cont' is external.
               "BLT    cont\n"
                ^
<inline asm>:6:1: note: instantiated into assembly here
BLT     cont
^
uppertst4.c:28:4: error: conditional branch requires assembler-local label. 'loop' is external.
               "B.NE   loop\n"
                ^
<inline asm>:10:1: note: instantiated into assembly here
B.NE    loop
^

## The Provisional Solution

I was able to solve the issue by replacing the `cont` label with a numeric value, 2, and the branches with `BGT 2f` and `BLT 2f`. Curiously, after this change, `loop` could stay like it was.

## Finding The Real Problem

Other than the trivial warnings, godbolt is absolutely happy with the file: https://godbolt.org/z/6s8bs4rW9

Next, I let clang create assembly output using `clang -S uppertst4.c` (on the M1 Mac) on the original, GNU, sourcefile. This also produces no errors, however trying to invoke the clang assembler `as` did:

% as uppertst4.s      
uppertst4.s:33:2: error: conditional branch requires assembler-local label. 'cont' is external.
        b.gt    cont
        ^
uppertst4.s:35:2: error: conditional branch requires assembler-local label. 'cont' is external.
        b.lt    cont
        ^
uppertst4.s:40:2: error: conditional branch requires assembler-local label. 'loop' is external.
        b.ne    loop
        ^

As I had a [working, standalone assembly file](https://github.com/below/HelloSilicon/blob/main/Chapter%2005/upper.s) which uses both `cont` and `loop`, I took a „divide and conquer“ approach: I deleted and added lines to the two files, until I would know what precisely caused the issue.

It turned out to be on the very last line, clang adds the compiler directive: `.subsections_via_symbols`.

llvm does it whenever it outputs assembly for a MachO binary, the source code tells us:


  if (TT.isOSBinFormatMachO()) {
    // Funny Darwin hack: This flag tells the linker that no global symbols
    // contain code that falls through to other global symbols (e.g. the obvious
    // implementation of multiple entry points).  If this doesn't occur, the
    // linker can safely perform dead code stripping.  Since LLVM never
    // generates code that does this, it is always safe to set.
    OutStreamer->emitAssemblerFlag(MCAF_SubsectionsViaSymbols);
  }

https://github.com/llvm/llvm-project/blob/89b57061f7b769e9ea9bf6ed686e284f3e55affe/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp#L568

The part about "Since LLVM never generates code that does this“ of course leaves out code that llvm did not generate, such as inline-assembly.

## The Real Solution

Because the C-Frontend created its own symbolic forward label, I learned that to remedy my issue, I needed to prefix `cont` with `L`, whereever using it. Here is the final file for ARM64 on Mac: https://github.com/below/HelloSilicon/blob/main/Chapter%2009/uppertst4.c

## What I still don’t know

While I did not take the time to fully understand what `. subsections_via_symbols` does, and still being a learner about these things, three questions remain:

1) What precisely was the violation of the inline code? Are there "global symbols which contain code that falls through to other global symbols“?

2) Why is the `loop` label apparently not a problem? Just to be sure, I changed it’s name to something not beginning with `l`, but that did not cause any issue either

3) Is there a substantial issue in my the standalone assembly code that I should know about?

So thank you for bearing with me. If you have any questions of input, please let me know!

Alex