Repository: compiler-explorer/compiler-explorer
Pull request #7267 (2025-01-13 09:37:19)

Add Hexagon-clang support for LLVM IR
<!-- THIS COMMENT IS INVISIBLE IN THE FINAL PR, BUT FEEL FREE TO REMOVE IT
Thanks for taking the time to improve CE. We really appreciate it.
  Before opening the PR, please make sure that the tests & linter pass their checks,
  by running `make check`.
  In the best case scenario, you are also adding tests to back up your changes,
  but don't sweat it if you don't. We can discuss them at a later date.
Feel free to append your name to the CONTRIBUTORS.md file
Thanks again, we really appreciate this!
-->

prasmish: Hi @mattgodbolt @jeremy-rifkin, this PR adds support for Hexagon-clang in the LLVM IR language. Could you please review it when you have a moment?
prasmish: @junlarsen Could you also take a look? Thanks!

Patch:
diff --git a/.idea/runConfigurations/Local_instance.xml b/.idea/runConfigurations/Local_instance.xml
index 827015b12..6a74fd3e6 100644
--- a/.idea/runConfigurations/Local_instance.xml
+++ b/.idea/runConfigurations/Local_instance.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="Local instance" type="NodeJSConfigurationType" path-to-node="/opt/compiler-explorer/node/bin/node" node-parameters="--loader ts-node/esm" path-to-js-file="app.ts" working-dir="$PROJECT_DIR$">
+  <configuration default="false" name="Local instance" type="NodeJSConfigurationType" path-to-node="/opt/compiler-explorer/node/bin/node" path-to-js-file="app.ts" typescript-loader="bundled" working-dir="$PROJECT_DIR$">
     <method v="2" />
   </configuration>
 </component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/Local_instance_C___only.xml b/.idea/runConfigurations/Local_instance_C___only.xml
index 8f5d5597b..c256ad3b6 100644
--- a/.idea/runConfigurations/Local_instance_C___only.xml
+++ b/.idea/runConfigurations/Local_instance_C___only.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="Local instance C++ only" type="NodeJSConfigurationType" application-parameters=" --language c++" path-to-node="/opt/compiler-explorer/node/bin/node" node-parameters="--loader ts-node/esm" path-to-js-file="app.ts" working-dir="$PROJECT_DIR$">
+  <configuration default="false" name="Local instance C++ only" type="NodeJSConfigurationType" application-parameters=" --language c++" path-to-node="/opt/compiler-explorer/node/bin/node" path-to-js-file="app.ts" typescript-loader="bundled" working-dir="$PROJECT_DIR$">
     <method v="2" />
   </configuration>
 </component>
\ No newline at end of file
diff --git a/etc/config/ispc.amazon.properties b/etc/config/ispc.amazon.properties
index 475213481..639085c5a 100644
--- a/etc/config/ispc.amazon.properties
+++ b/etc/config/ispc.amazon.properties
@@ -1,54 +1,77 @@
 compilers=&ispc
 defaultCompiler=ispc1253
+objdumper=/opt/compiler-explorer/gcc-14.2.0/bin/objdump
 
 group.ispc.compilers=ispc1253:ispc1250:ispc1240:ispc1230:ispc1220:ispc1210:ispc1200:ispc1190:ispc1180:ispc1170:ispc1161:ispc1160:ispc1150:ispc1141:ispc1140:ispc1130:ispc1120:ispc1110:ispc1100:ispc192:ispc191:ispc-trunk
 group.ispc.isSemVer=true
 group.ispc.baseName=ispc
 compiler.ispc1253.exe=/opt/compiler-explorer/ispc-1.25.3/bin/ispc
 compiler.ispc1253.semver=1.25.3
+compiler.ispc1253.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1250.exe=/opt/compiler-explorer/ispc-1.25.0/bin/ispc
 compiler.ispc1250.semver=1.25.0
+compiler.ispc1250.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1240.exe=/opt/compiler-explorer/ispc-1.24.0/bin/ispc
 compiler.ispc1240.semver=1.24.0
+compiler.ispc1240.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1230.exe=/opt/compiler-explorer/ispc-1.23.0/bin/ispc
 compiler.ispc1230.semver=1.23.0
+compiler.ispc1230.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1220.exe=/opt/compiler-explorer/ispc-1.22.0/bin/ispc
 compiler.ispc1220.semver=1.22.0
+compiler.ispc1220.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1210.exe=/opt/compiler-explorer/ispc-1.21.0/bin/ispc
 compiler.ispc1210.semver=1.21.0
+compiler.ispc1210.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1200.exe=/opt/compiler-explorer/ispc-1.20.0/bin/ispc
 compiler.ispc1200.semver=1.20.0
+compiler.ispc1200.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1190.exe=/opt/compiler-explorer/ispc-1.19.0/bin/ispc
 compiler.ispc1190.semver=1.19.0
+compiler.ispc1190.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1180.exe=/opt/compiler-explorer/ispc-1.18.0/bin/ispc
 compiler.ispc1180.semver=1.18.0
+compiler.ispc1180.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1170.exe=/opt/compiler-explorer/ispc-1.17.0/bin/ispc
 compiler.ispc1170.semver=1.17.0
+compiler.ispc1170.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1161.exe=/opt/compiler-explorer/ispc-1.16.1/bin/ispc
 compiler.ispc1161.semver=1.16.1
+compiler.ispc1161.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1160.exe=/opt/compiler-explorer/ispc-1.16.0/bin/ispc
 compiler.ispc1160.semver=1.16.0
+compiler.ispc1160.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1150.exe=/opt/compiler-explorer/ispc-1.15.0/bin/ispc
 compiler.ispc1150.semver=1.15.0
+compiler.ispc1150.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1141.exe=/opt/compiler-explorer/ispc-1.14.1/bin/ispc
 compiler.ispc1141.semver=1.14.1
+compiler.ispc1141.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1140.exe=/opt/compiler-explorer/ispc-1.14.0/bin/ispc
 compiler.ispc1140.semver=1.14.0
+compiler.ispc1140.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1130.exe=/opt/compiler-explorer/ispc-1.13.0/bin/ispc
 compiler.ispc1130.semver=1.13.0
+compiler.ispc1130.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1120.exe=/opt/compiler-explorer/ispc-1.12.0/bin/ispc
 compiler.ispc1120.semver=1.12.0
+compiler.ispc1120.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1110.exe=/opt/compiler-explorer/ispc-1.11.0/bin/ispc
 compiler.ispc1110.semver=1.11.0
+compiler.ispc1110.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc1100.exe=/opt/compiler-explorer/ispc-1.10.0/bin/ispc
 compiler.ispc1100.semver=1.10.0
+compiler.ispc1100.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc192.exe=/opt/compiler-explorer/ispc-1.9.2/ispc
 compiler.ispc192.semver=1.9.2
+compiler.ispc192.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc191.exe=/opt/compiler-explorer/ispc-1.9.1/ispc
 compiler.ispc191.semver=1.9.1
+compiler.ispc191.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 compiler.ispc-trunk.exe=/opt/compiler-explorer/ispc-trunk/bin/ispc
 compiler.ispc-trunk.semver=(trunk)
 compiler.ispc-trunk.isNightly=true
+compiler.ispc-trunk.executableLinker=/opt/compiler-explorer/gcc-14.2.0/bin/gcc
 
 #################################
 #################################
diff --git a/etc/config/ispc.defaults.properties b/etc/config/ispc.defaults.properties
index c8e124747..cc2c9dc29 100644
--- a/etc/config/ispc.defaults.properties
+++ b/etc/config/ispc.defaults.properties
@@ -1,6 +1,10 @@
-supportsBinary=false
+supportsBinary=true
+supportsBinaryObject=true
+supportsExecute=true
 compilerType=ispc
-
+objdumper=objdump
+stubRe=\bmain\b
+stubText=extern "C" uniform int main() { return 0; }
 
 #################################
 #################################
diff --git a/etc/config/llvm.amazon.properties b/etc/config/llvm.amazon.properties
index c75f54cf6..8cec56218 100644
--- a/etc/config/llvm.amazon.properties
+++ b/etc/config/llvm.amazon.properties
@@ -1,4 +1,4 @@
-compilers=&irclang:&llc:&opt
+compilers=&irclang:&irhexagon-clang:&llc:&opt
 defaultCompiler=llctrunk
 
 demangler=/opt/compiler-explorer/gcc-14.2.0/bin/c++filt
@@ -61,6 +61,18 @@ compiler.irclang-assertions-trunk.exe=/opt/compiler-explorer/clang-assertions-tr
 compiler.irclang-assertions-trunk.objdumper=/opt/compiler-explorer/gcc-snapshot/bin/objdump
 compiler.irclang-assertions-trunk.semver=(assertions trunk)
 
+group.irhexagon-clang.groupName=Hexagon clang
+group.irhexagon-clang.baseName=hexagon-clang
+group.irhexagon-clang.compilers=irhexagonclang1605
+group.irhexagon-clang.demangler=/opt/compiler-explorer/clang-trunk/bin/llvm-cxxfilt
+group.irhexagon-clang.options=-x ir
+
+compiler.irhexagonclang1605.semver=16.0.5
+compiler.irhexagonclang1605.name=hexagon-clang 16.0.5
+compiler.irhexagonclang1605.exe=/opt/compiler-explorer/clang+llvm-16.0.5-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/clang++
+compiler.irhexagonclang1605.compilerType=clang-hexagon
+compiler.irhexagonclang1605.compilerCategories=clang-hexagon
+
 group.llc.compilers=llc32:llc33:llc391:llc400:llc401:llc500:llc600:llc700:llc800:llc900:llc1000:llc1001:llc1100:llc1101:llc1200:llc1201:llc1300:llc1400:llc1500:llc1600:llc1701:llc1810:llc1910:llctrunk:llc-assertions-trunk
 group.llc.compilerType=llc
 group.llc.supportsExecute=false
diff --git a/etc/config/sponsors.yaml b/etc/config/sponsors.yaml
index 677b295df..c68de6d5f 100644
--- a/etc/config/sponsors.yaml
+++ b/etc/config/sponsors.yaml
@@ -45,6 +45,15 @@ levels:
         topIconShowEvery: 3
         displayType: SideBySide
         statsId: jetbrains
+      - name: Cppcon
+        description: Join the CppCon family live &amp; in-person this September in Aurora, Colorado, USA! Pre-register for CppCon 2025 at <a href="https://www.CppCon.org" target="_blank">www.CppCon.org</a>.
+        img: https://static.ce-cdn.net/cppcon/cppcon-white-gold-5.svg
+        icon: https://static.ce-cdn.net/cppcon/cppcon-blue-gold-5.svg
+        icon_dark: https://static.ce-cdn.net/cppcon/cppcon-white-gold-5.svg
+        url: https://cppcon.org/
+        displayType: SideBySide
+        topIconShowEvery: 3
+        statsId: cppcon
   - name: Patreon Legends
     description: 'These amazing people have pledged at the highest level of support. Huge thanks to these fine folks:'
     class: legendary
diff --git a/lib/base-compiler.ts b/lib/base-compiler.ts
index 5b9ac7447..23d814dc0 100644
--- a/lib/base-compiler.ts
+++ b/lib/base-compiler.ts
@@ -1190,17 +1190,12 @@ export class BaseCompiler {
 
         const libIncludes = this.getIncludeArguments(libraries, dirPath);
         const libOptions = this.getLibraryOptions(libraries);
-        let libLinks: string[] = [];
-        let libPaths: string[] = [];
-        let libPathsAsFlags: string[] = [];
-        let staticLibLinks: string[] = [];
-
-        if (filters.binary) {
-            libLinks = (this.getSharedLibraryLinks(libraries).filter(Boolean) as string[]) || [];
-            libPathsAsFlags = this.getSharedLibraryPathsAsArguments(libraries, undefined, toolchainPath, dirPath);
-            libPaths = this.getSharedLibraryPaths(libraries, dirPath);
-            staticLibLinks = (this.getStaticLibraryLinks(libraries, libPaths).filter(Boolean) as string[]) || [];
-        }
+        const {libLinks, libPathsAsFlags, staticLibLinks} = this.getLibLinkInfo(
+            filters,
+            libraries,
+            toolchainPath,
+            dirPath,
+        );
 
         userOptions = this.filterUserOptions(userOptions) || [];
         [options, overrides] = this.fixIncompatibleOptions(options, userOptions, overrides);
@@ -1218,6 +1213,25 @@ export class BaseCompiler {
         );
     }
 
+    protected getLibLinkInfo(
+        filters: ParseFiltersAndOutputOptions,
+        libraries: SelectedLibraryVersion[],
+        toolchainPath: string,
+        dirPath: string,
+    ) {
+        let libLinks: string[] = [];
+        let libPathsAsFlags: string[] = [];
+        let staticLibLinks: string[] = [];
+
+        if (filters.binary) {
+            libLinks = (this.getSharedLibraryLinks(libraries).filter(Boolean) as string[]) || [];
+            libPathsAsFlags = this.getSharedLibraryPathsAsArguments(libraries, undefined, toolchainPath, dirPath);
+            const libPaths = this.getSharedLibraryPaths(libraries, dirPath);
+            staticLibLinks = (this.getStaticLibraryLinks(libraries, libPaths).filter(Boolean) as string[]) || [];
+        }
+        return {libLinks, libPathsAsFlags, staticLibLinks};
+    }
+
     protected fixIncompatibleOptions(
         options: string[],
         userOptions: string[],
@@ -1651,9 +1665,14 @@ export class BaseCompiler {
         return utils.changeExtension(inputFilename, '.ll');
     }
 
-    getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string {
-        let filename;
-        if (key && key.backendOptions && key.backendOptions.customOutputFilename) {
+    isCacheKey(key: CacheKey | CompilationCacheKey | undefined): key is CacheKey {
+        return key !== undefined && (key as CacheKey).backendOptions !== undefined;
+    }
+
+    getOutputFilename(dirPath: string, outputFilebase: string, key?: CacheKey | CompilationCacheKey): string {
+        let filename: string;
+
+        if (this.isCacheKey(key) && key.backendOptions.customOutputFilename) {
             filename = key.backendOptions.customOutputFilename;
         } else {
             filename = `${outputFilebase}.s`;
@@ -1803,7 +1822,7 @@ export class BaseCompiler {
 
     buildExecutable(compiler: string, options: string[], inputFilename: string, execOptions: ExecutionOptionsWithEnv) {
         // default implementation, but should be overridden by compilers
-        return this.runCompiler(compiler, options, inputFilename, execOptions);
+        return this.runCompiler(compiler, options, inputFilename, execOptions, {execute: true, binary: true});
     }
 
     protected maskPathsInArgumentsForUser(args: string[]): string[] {
diff --git a/lib/compilers/ispc.ts b/lib/compilers/ispc.ts
index 271b527b8..29dbcb2c9 100644
--- a/lib/compilers/ispc.ts
+++ b/lib/compilers/ispc.ts
@@ -22,20 +22,31 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 
+import fs from 'fs-extra';
 import Semver from 'semver';
 
+import {
+    CacheKey,
+    CompilationCacheKey,
+    CompilationResult,
+    ExecutionOptionsWithEnv,
+} from '../../types/compilation/compilation.interfaces.js';
 import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js';
 import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
 import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
+import {SelectedLibraryVersion} from '../../types/libraries/libraries.interfaces.js';
 import type {ResultLine} from '../../types/resultline/resultline.interfaces.js';
 import {unwrap} from '../assert.js';
 import {BaseCompiler} from '../base-compiler.js';
 import {CompilationEnvironment} from '../compilation-env.js';
 import {asSafeVer} from '../utils.js';
+import * as utils from '../utils.js';
 
 import {ISPCParser} from './argument-parsers.js';
 
 export class ISPCCompiler extends BaseCompiler {
+    private readonly executableLinker: string;
+
     static get key() {
         return 'ispc';
     }
@@ -44,16 +55,40 @@ export class ISPCCompiler extends BaseCompiler {
         super(info, env);
         this.compiler.supportsIrView = true;
         this.compiler.irArg = ['--emit-llvm-text'];
+        // TODO(#7150) do away with this kind of thing and share the same linker for everything.
+        this.executableLinker = this.compilerProps<string>(`compiler.${this.compiler.id}.executableLinker`);
     }
 
     override couldSupportASTDump(version: string) {
         return Semver.gte(asSafeVer(this.compiler.semver), '1.18.0', true);
     }
 
+    override getOutputFilename(dirPath: string, outputFilebase: string, key?: CacheKey | CompilationCacheKey): string {
+        const outputFilename = super.getOutputFilename(dirPath, outputFilebase, key);
+        if (this.isCacheKey(key)) {
+            // ispc gets a bit annoyed about the output filename extension. If we're in binary object mode we _have_ to
+            // output a `.o` file, else ispc complains.
+            if (key.filters.binary || key.filters.binaryObject) {
+                return utils.changeExtension(outputFilename, '.o');
+            }
+        }
+        return outputFilename;
+    }
+
+    override getExecutableFilename(dirPath: string, outputFilebase: string, key?: CacheKey | CompilationCacheKey) {
+        // ispc gets a bit annoyed about the output filename extension. If we're in binary object mode we _have_ to
+        // output a `.o` file, else ispc complains. We can't defer this to getOutputFilename, else we'll rename the
+        // output of executed but non-binary, non-binary-object files, which need to remain `.s`.
+        return utils.changeExtension(super.getOutputFilename(dirPath, outputFilebase, key), '.o');
+    }
+
     override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) {
-        let options = ['--target=avx2-i32x8', '--emit-asm', '-g', '-o', this.filename(outputFilename)];
+        const options = ['--target=avx2-i32x8', '-g', '-o', this.filename(outputFilename)];
+        if (!filters.binary && !filters.binaryObject) {
+            options.push('--emit-asm');
+        }
         if (this.compiler.intelAsm && filters.intel && !filters.binary) {
-            options = options.concat(this.compiler.intelAsm.split(' '));
+            options.push(...this.compiler.intelAsm.split(' '));
         }
         return options;
     }
@@ -91,7 +126,43 @@ export class ISPCCompiler extends BaseCompiler {
         );
     }
 
+    override getLibLinkInfo(
+        filters: ParseFiltersAndOutputOptions,
+        libraries: SelectedLibraryVersion[],
+        toolchainPath: string,
+        dirPath: string,
+    ) {
+        // Prevent any library linking flags from being passed to ispc during compilation.
+        return {libLinks: [], libPathsAsFlags: [], staticLibLinks: []};
+    }
+
     override isCfgCompiler() {
         return true;
     }
+
+    async linkExecutable(options: string[], execOptions: ExecutionOptionsWithEnv) {
+        // Rely on the fact we definitely put a `-o` in the options.
+        const outputFile = options[options.indexOf('-o') + 1];
+        const renamedFile = outputFile + '.tmp';
+        await fs.rename(outputFile, renamedFile);
+        return await this.exec(this.executableLinker, ['-o', outputFile, renamedFile], execOptions);
+    }
+
+    override async runCompiler(
+        compiler: string,
+        options: string[],
+        inputFilename: string,
+        execOptions: ExecutionOptionsWithEnv,
+        filters?: ParseFiltersAndOutputOptions,
+    ): Promise<CompilationResult> {
+        const result = await super.runCompiler(compiler, options, inputFilename, execOptions, filters);
+        if (result.code !== 0) return result;
+        if (filters?.binary) {
+            const linkResult = await this.linkExecutable(options, execOptions);
+            if (linkResult.code !== 0) {
+                return {...result, ...this.transformToCompilationResult(linkResult, inputFilename)};
+            }
+        }
+        return result;
+    }
 }
diff --git a/lib/parsers/asm-parser.ts b/lib/parsers/asm-parser.ts
index 70034d264..0b6af58da 100644
--- a/lib/parsers/asm-parser.ts
+++ b/lib/parsers/asm-parser.ts
@@ -99,7 +99,7 @@ export class AsmParser extends AsmRegex implements IAsmParser {
         this.labelFindMips = /[$.A-Z_a-z][\w$.]*/g;
         this.mipsLabelDefinition = /^\$[\w$.]+:/;
         this.dataDefn =
-            /^\s*\.(ascii|asciz|[1248]?byte|dc(?:\.[abdlswx])?|dcb(?:\.[bdlswx])?|ds(?:\.[bdlpswx])?|double|dword|fill|float|half|hword|int|long|octa|quad|short|single|skip|space|string(?:8|16|32|64)?|value|word|xword|zero)/;
+            /^\s*\.(ascii|asciz|base64|[1248]?byte|dc(?:\.[abdlswx])?|dcb(?:\.[bdlswx])?|ds(?:\.[bdlpswx])?|double|dword|fill|float|half|hword|int|long|octa|quad|short|single|skip|space|string(?:8|16|32|64)?|value|word|xword|zero)/;
         this.fileFind = /^\s*\.(?:cv_)?file\s+(\d+)\s+"([^"]+)"(\s+"([^"]+)")?.*/;
         // Opcode expression here matches LLVM-style opcodes of the form `%blah = opcode`
         this.hasOpcodeRe = /^\s*(%[$.A-Z_a-z][\w$.]*\s*=\s*)?[A-Za-z]/;


Test patch:
diff --git a/test/filters-cases/bug-7280.asm b/test/filters-cases/bug-7280.asm
new file mode 100644
index 000000000..f6e5260cc
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm
@@ -0,0 +1,17 @@
+        .arch armv8-a
+        .file   "example.cpp"
+        .text
+        .global a
+        .section        .rodata
+        .align  3
+.LC0:
+        .string "a"
+        .base64 "AGIA"
+        .data
+        .align  3
+        .type   a, %object
+        .size   a, 8
+a:
+        .xword  .LC0
+        .ident  "GCC: (GNU) 15.0.1 20250115 (experimental)"
+        .section        .note.GNU-stack,"",@progbits
diff --git a/test/filters-cases/bug-7280.asm.binary.directives.labels.comments.json b/test/filters-cases/bug-7280.asm.binary.directives.labels.comments.json
new file mode 100644
index 000000000..720205208
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.binary.directives.labels.comments.json
@@ -0,0 +1,4 @@
+{
+  "asm": [],
+  "labelDefinitions": {}
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.comments.json b/test/filters-cases/bug-7280.asm.directives.comments.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.comments.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.json b/test/filters-cases/bug-7280.asm.directives.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.labels.comments.json b/test/filters-cases/bug-7280.asm.directives.labels.comments.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.labels.comments.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.labels.comments.library.dontMaskFilenames.json b/test/filters-cases/bug-7280.asm.directives.labels.comments.library.dontMaskFilenames.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.labels.comments.library.dontMaskFilenames.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.labels.comments.library.json b/test/filters-cases/bug-7280.asm.directives.labels.comments.library.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.labels.comments.library.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.labels.json b/test/filters-cases/bug-7280.asm.directives.labels.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.labels.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.directives.library.json b/test/filters-cases/bug-7280.asm.directives.library.json
new file mode 100644
index 000000000..805e10c8f
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.directives.library.json
@@ -0,0 +1,49 @@
+{
+  "asm": [
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 1,
+    "a": 4
+  }
+}
\ No newline at end of file
diff --git a/test/filters-cases/bug-7280.asm.none.json b/test/filters-cases/bug-7280.asm.none.json
new file mode 100644
index 000000000..d3d3c00b6
--- /dev/null
+++ b/test/filters-cases/bug-7280.asm.none.json
@@ -0,0 +1,141 @@
+{
+  "asm": [
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 22,
+            "startCol": 21
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .arch armv8-a"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .file   \"example.cpp\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .text"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 18,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .global a"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .section        .rodata"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .align  3"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": ".LC0:"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 19,
+            "startCol": 18
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .string \"a\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .base64 \"AGIA\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .data"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .align  3"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 18,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .type   a, %object"
+    },
+    {
+      "labels": [
+        {
+          "name": "a",
+          "range": {
+            "endCol": 18,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .size   a, 8"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "a:"
+    },
+    {
+      "labels": [
+        {
+          "name": ".LC0",
+          "range": {
+            "endCol": 21,
+            "startCol": 17
+          }
+        }
+      ],
+      "source": null,
+      "text": "        .xword  .LC0"
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .ident  \"GCC: (GNU) 15.0.1 20250115 (experimental)\""
+    },
+    {
+      "labels": [],
+      "source": null,
+      "text": "        .section        .note.GNU-stack,\"\",@progbits"
+    }
+  ],
+  "labelDefinitions": {
+    ".LC0": 7,
+    "a": 14
+  }
+}
\ No newline at end of file