Skip to content

Add L() macro for local labels and enable .subsections_via_symbols on aarch64-darwin - #6090

Open
topolarity wants to merge 5 commits into
OpenMathLib:developfrom
topolarity:ct/asm-local-labels
Open

topolarity wants to merge 5 commits into
OpenMathLib:developfrom
topolarity:ct/asm-local-labels

Conversation

@topolarity

Copy link
Copy Markdown

On macOS, linker de-duplication and pruning is done at the symbol level.

As seen in #6089, it is important to be careful about how these symbols are exposed even if not using -Wl,-dead-strip.

This change is complementary to #6088, since -Wl,-dead-strip cannot prune this assembly code on macOS without .subsections_via_symbols.

On arm64:

Program develop #6088 #6088 + PR
runtime only 4.50 MB 2.41 MB 0.007 MB
dgemm 4.52 MB 2.52 MB 0.17 MB
dgesv + daxpy + dgemv + dnrm2 4.51 MB 2.66 MB 0.32 MB
every cblas_d* 4.72 MB 3.11 MB 0.99 MB

On x86-64:

Program develop #6088 #6088 + PR
runtime only 33.2 MB 0.03 MB 0.03 MB
dgemm 33.2 MB (broken) 0.57 MB (broken) 0.80 MB
dgesv + daxpy + dgemv + dnrm2 33.2 MB (broken) 3.19 MB (broken) 3.54 MB
every cblas_d* 33.4 MB (broken) 5.93 MB (broken) 7.06 MB

Another massive diff, although hopefully easy to review. Commits:

  1. Adds the L(...) macro
  2. Mechanical .L -> L(...) replacement for x86-64
  3. Mechanical .L -> L(...) replacement for arm64
  4. Various AI-driven touch-ups to kernels whose labels did not match the pattern from 2 / 3
  5. Add .subsections_via_symbols on arm64-darwin

topolarity and others added 3 commits October 1, 2026 23:17
Nothing uses it yet.  L(name) is .Lname, except on Mach-O, where the
assembler's prefix for local labels is "L" and a ".L" label is an ordinary
symbol.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
No change to any object except on Mach-O.  The kernels name their labels
.Lxxx, which makes them local to the assembler on ELF and COFF, but not on
Mach-O, where that prefix is "L": there every label is a symbol.  Since
EPILOGUE is .subsections_via_symbols on Darwin, the linker takes each such
symbol for the start of a block of its own.  It does not see that one block
falls through into the next, and

  * folds a block that is byte for byte the same as one of another kernel
    into that one (on by default when linking at -O2), and
  * with -dead_strip, removes a block that no branch refers to,

either of which leaves the code before the block running on into whatever
comes next.  With Apple clang 21 (ld-1267), libopenblas.dylib built for
NEHALEM returns garbage from cblas_sasum(5, {3,-1,2,-4,1}, 1) (sasum_k has
lost its final "ret"), and openblas_utest crashes in its first test.

With L(name) the labels are local on Mach-O as well, and a kernel is one
block.

Generated by asm-labels/use_local_label_macro.py, which rewrites every
".Lname" token of kernel/x86_64/*.S and nothing else: 23906 labels in 249
files.  For an ELF target, all 255 files preprocess to the same text as
before, and the 2619 kernel objects of a DYNAMIC_ARCH build are byte for
byte the same.

Generated-by: asm-labels/use_local_label_macro.py
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
No change to any object: on ELF and COFF L(name) is .Lname, as before, and
on Mach-O, where it is Lname, the labels become local to the assembler
without changing the code (the arm64 kernels do not use
.subsections_via_symbols, so the linker did not act on the label symbols).

Preparation for using that directive on Darwin, which needs the labels of
a kernel to be local: with it, the assembler cannot even branch
conditionally to a label that is a symbol ("conditional branch requires
assembler-local label").

Generated by asm-labels/use_local_label_macro.py: 6565 labels in 76 files.
The two SME kernels, which do not include common.h, are not touched.  For an
ELF target the 476 kernel objects of a DYNAMIC_ARCH build are byte for byte
the same, and on Darwin their text is.

Generated-by: asm-labels/use_local_label_macro.py
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@topolarity
topolarity force-pushed the ct/asm-local-labels branch from 66d3364 to 292b50b Compare October 2, 2026 03:50
topolarity and others added 2 commits October 2, 2026 00:11
A few kernels name labels in ways that the previous two commits did not
reach:

  * x86_64/dgemm_kernel_6x4_piledriver.S: ._L_<n>_<kind>
  * arm64/nrm2.S, znrm2.S, zscal.S: no prefix at all
  * arm64/sgemm_direct_sme1_*.S: .M_Loop, .K_Loop, ...
  * arm64/dznrm2_thunderx2t99_fast.c: .Lname, in inline assembly
  * x86_64/dgemm_kernel_4x8_skylakex.c: .label<n>, in inline assembly

All but the fourth are symbols for every assembler.  Spell them L(name).
The SME kernels include common.h for that, and end in EPILOGUE like the
others.

The two C kernels, which no KERNEL file uses at present, get an L() of
their own, which makes the string.  clang always uses
.subsections_via_symbols for C, so on Darwin the first did not assemble
("conditional branch requires assembler-local label") and the second had
its loops split into blocks.

The text of each object is unchanged, on ELF and on Darwin, except that of
dgemm_kernel_4x8_skylakex.c on Darwin, where the jumps to its labels are now
short; the label symbols are gone from it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Apple's linker never removes anything from an object without this
directive, so a static link with -dead_strip kept every assembly kernel it
had loaded, used or not.  With it, a kernel that nothing refers to is left
out, as the C kernels already are.

This is safe now that every label inside a kernel is L(name), local to the
assembler: each kernel object defines one symbol, its entry point, and so is
one block for the linker.  EPILOGUE, until now empty, is added to the five
kernels that did not end in it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant