llvm / llvm/llvm-project

[clang] Top-level __asm__ in the global module fragment is silently dropped — unless -Wunsafe-buffer-usage is enabled

Open
#213,822 1 comment 0 reactions 0 assignees View on GitHub
inline-asm
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

while testing a Clang build of [mbun](https://github.com/Sunrisepeak/mbun), I found a behavior difference between Clang and GCC: Clang drops symbols in the
global module fragment (GMF), while GCC does not.

Note: This text was AI-assisted.

Reproduction:
## Summary

In a C++20 module interface unit, a top-level `__asm__` declaration in the
global module fragment (GMF) is silently discarded by clang: no warning, no
error, and the symbols defined by the asm are missing from the object file.
The failure surfaces only at link time as `undefined reference`, which is very
hard to diagnose. Two things make this reportable:

1. **No diagnostic.** GCC accepts the same code as an extension and warns
(`-Wglobal-module`: "global module fragment contents must be from
preprocessor inclusion"). Clang does neither: it just drops the asm.
2. **Codegen depends on an unrelated warning flag.** Enabling
`-Wunsafe-buffer-usage` makes clang emit the exact same asm. Same source,
same compiler version — whether the symbols exist in the `.o` is controlled
by a warning flag that has nothing to do with code generation.

## Reproducer

`gmf_asm.cppm`:

```cpp
module;
__asm__(
".text\n"
".globl repro_symbol\n"
".type repro_symbol, @function\n"
"repro_symbol:\n"
" ret\n");
export module gmf_asm;
export int f() { return 42; }
```

`main.cc`:

```cpp
extern "C" void repro_symbol();
int main() { repro_symbol(); }
```

Commands (clang 20.1.8):

```console
$ clang++ -std=c++26 -c gmf_asm.cppm -o gmf_asm.o
# compiles cleanly, no diagnostics
$ nm gmf_asm.o | grep repro_symbol
# (nothing — the symbol is gone)
$ clang++ -std=c++26 gmf_asm.o main.cc
/usr/bin/ld: main.cc:(.text+0x10): undefined reference to `repro_symbol'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

# Same source with -Wunsafe-buffer-usage: the asm IS emitted
$ clang++ -std=c++26 -Wunsafe-buffer-usage -c gmf_asm.cppm -o gmf_asm.o
$ nm gmf_asm.o | grep repro_symbol
0000000000000000 T repro_symbol
```

The same drop happens when the asm reaches the GMF via `#include` (the
common real-world pattern, e.g. FFI trampolines included from a header in
the GMF).

## GCC comparison

```console
$ g++ -std=c++26 -fmodules -c gmf_asm.cppm
warning: global module fragment contents must be from preprocessor inclusion [-Wglobal-module]
$ nm gmf_asm.o | grep repro_symbol
0000000000000000 T repro_symbol
```

GCC diagnoses the non-conforming GMF content but emits the asm anyway, so the
program links. Clang neither diagnoses nor emits.

## Standard position

[module.global.frag]/3 restricts the GMF to preprocessing directives only, so
the source is ill-formed — clang is not required to emit the symbol. But a
"shall" violation is generally expected to produce a diagnostic, and silently
dropping a symbol that the rest of the TU (and other TUs) reference leads to a
mystifying link failure many steps away from the cause. The observed
flag-dependent codegen (`-Wunsafe-buffer-usage` flips the behavior) also
suggests this is an accidental gap in the default GMF handling path rather
than a deliberate policy.

## Expected behavior

Either, in the default configuration:

- emit the asm (matching GCC's lenient behavior), or
- at minimum, diagnose the non-preprocessing GMF content (analogous to GCC's
`-Wglobal-module`), so users never hit a silent undefined reference.

## Environment

- Ubuntu clang 20.1.8 (`++20250804090239+87f0227cb601-1~exp1`)
- Linux x86_64, `-std=c++26` (also reproduces with `-std=c++20`)

Contributor guide

Open the contributing guide

Research direction

Start by compiling the provided gmf_asm.cppm reproducer with and without -Wunsafe-buffer-usage, then inspect Clang's handling of top-level asm in the global module fragment. Compare the object-file output and diagnostics with the commands shown. Done means the invalid GMF content no longer disappears silently: Clang should either emit a diagnostic or consistently emit the asm.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.