mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2026-07-22 01:46:48 -04:00
## Summary Three related fixes prompted by driving the deployed MCP endpoint after #8644 / #8685 merged: 1. **`list_libraries` match also searches version strings.** The natural LLM query "find Boost 1.88" returned zero results — `list_libraries({language:"c++", match:"boost 1.88"})` only matched against `{id, name}`, so the version token never matched anything. 2. **HPPA + Go cross-compiler `instructionSet` retags.** Discovered via `list_compilers(match="gcc 16", instructionSet="amd64")` returning *only* `hppag1610` (HPPA cross-compiler) and missing plain `g161` (x86-64 GCC). Two layered causes — HPPA had no entry in `InstructionSetsList`, so the heuristic in `lib/instructionsets.ts` defaulted HPPA cross-compilers to `amd64`; and 12 Go cross-compiler groups (`386gl`, `arm32gl`, `arm64gl`, `mips*gl`, `ppc64*gl`, `riscvgl`, `s390xgl`, `wasmgl`) had never been tagged at all. 3. **Defensive bucketing in MCP `pickLatest`.** Belt-and-braces fix: bucket key is now `(lang, instructionSet, group)` so different compiler families that happen to share an arch tag can't fight for the same `latestPerMajor` slot. ## Changes - `lib/mcp/tools/libraries.ts` — extend `applyMatch` haystack to include `versions[].version` (human form, e.g. `"1.88.0"`) and `versions[].id` (e.g. `"188"`). - `lib/mcp/tools/compilers.ts` — `pickLatest` bucket key now includes `c.group`. - `types/instructionsets.ts` + `lib/instructionsets.ts` — new `'hppa'` entry. - `etc/config/{ada,c++,c,d,fortran,gimple,objc++,objc}.amazon.properties` — `group.<gnathppa|gcchppa|cgcchppa|gdchppa|gimplehppa|objcppgcchppa|objchppa>.instructionSet=hppa`. - `etc/config/go.amazon.properties` — explicit `instructionSet=` for all 13 Go arch groups (12 cross + amd64gl for symmetry). - `test/mcp/mcp-tests.ts` — covers the bucketing-by-group case. - `test/instructionsets-tests.ts` — covers the hppa target-string branch. ## Follow-up #8690 — drop the `lib/instructionsets.ts` path-based heuristic entirely in favour of required `instructionSet` properties (the path branch is Amazon-install-specific and silently mistags compilers on any non-Amazon layout). ## Test plan - [x] `npm run test -- --run mcp` — passes (existing + 2 new) - [x] `npm run test -- --run instructionsets` — passes (existing + 1 new) - [x] `npm run test:props` — 90/90 pass - [x] `make pre-commit` — exits 0 - [ ] Drive the staging endpoint with `list_libraries({language:"c++", match:"boost 1.88"})` and confirm Boost is returned with its versions. - [ ] Drive the staging endpoint with `list_compilers({match:"gcc 16", instructionSet:"amd64", latestPerMajor:true})` and confirm `g161` (x86-64) appears and `hppag1610` does not. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
253 lines
7.5 KiB
TypeScript
253 lines
7.5 KiB
TypeScript
// Copyright (c) 2021, Compiler Explorer Authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import {InstructionSet} from '../types/instructionsets.js';
|
|
|
|
type InstructionSetMethod = {
|
|
target: string[];
|
|
path: string[];
|
|
};
|
|
|
|
export class InstructionSets {
|
|
private defaultInstructionset: InstructionSet = 'amd64';
|
|
private supported: Record<InstructionSet, InstructionSetMethod>;
|
|
|
|
constructor() {
|
|
this.supported = {
|
|
aarch64: {
|
|
target: ['aarch64'],
|
|
path: ['/aarch64-'],
|
|
},
|
|
arm32: {
|
|
target: ['arm'],
|
|
path: ['/arm-'],
|
|
},
|
|
avr: {
|
|
target: ['avr'],
|
|
path: ['/avr-'],
|
|
},
|
|
c6x: {
|
|
target: ['c6x'],
|
|
path: ['/tic6x-'],
|
|
},
|
|
dex: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
ebpf: {
|
|
target: ['bpf'],
|
|
path: ['/bpf-'],
|
|
},
|
|
ez80: {
|
|
target: ['ez80'],
|
|
path: [],
|
|
},
|
|
hppa: {
|
|
target: ['hppa'],
|
|
path: ['/hppa-'],
|
|
},
|
|
kvx: {
|
|
target: ['kvx'],
|
|
path: ['/kvx-', '/k1-'],
|
|
},
|
|
loongarch: {
|
|
target: ['loongarch'],
|
|
path: ['/loongarch64-'],
|
|
},
|
|
m68k: {
|
|
target: ['m68k'],
|
|
path: ['/m68k-'],
|
|
},
|
|
mips: {
|
|
target: ['mips'],
|
|
path: ['/mips', '/mipsel-', '/mips64el-', '/mips64-', '/nanomips-'],
|
|
},
|
|
mrisc32: {
|
|
target: ['mrisc32'],
|
|
path: [],
|
|
},
|
|
msp430: {
|
|
target: ['msp430'],
|
|
path: ['/msp430-'],
|
|
},
|
|
powerpc: {
|
|
target: ['powerpc', 'ppc64', 'ppc'],
|
|
path: ['/powerpc-', '/powerpc64-', '/powerpc64le-'],
|
|
},
|
|
riscv64: {
|
|
target: ['rv64', 'riscv64'],
|
|
path: ['/riscv64-'],
|
|
},
|
|
riscv32: {
|
|
target: ['rv32', 'riscv32'],
|
|
path: ['/riscv32-'],
|
|
},
|
|
sh: {
|
|
target: ['sh'],
|
|
path: ['/sh-'],
|
|
},
|
|
sparc: {
|
|
target: ['sparc', 'sparc64'],
|
|
path: ['/sparc-', '/sparc64-'],
|
|
},
|
|
s390x: {
|
|
target: ['s390x'],
|
|
path: ['/s390x-'],
|
|
},
|
|
vax: {
|
|
target: ['vax'],
|
|
path: ['/vax-'],
|
|
},
|
|
wasm32: {
|
|
target: ['wasm32'],
|
|
path: [],
|
|
},
|
|
wasm64: {
|
|
target: ['wasm64'],
|
|
path: [],
|
|
},
|
|
xtensa: {
|
|
target: ['xtensa'],
|
|
path: ['/xtensa-'],
|
|
},
|
|
z180: {
|
|
target: ['z180'],
|
|
path: [],
|
|
},
|
|
z80: {
|
|
target: ['z80'],
|
|
path: [],
|
|
},
|
|
6502: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
wdc65c816: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
core: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
java: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
llvm: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
perl: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
python: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
mpy: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
ptx: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
x86: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
amd64: {
|
|
target: ['x86_64'],
|
|
path: ['/x86_64'],
|
|
},
|
|
evm: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
eravm: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
mos6502: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
sass: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
beam: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
hook: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
spirv: {
|
|
target: [],
|
|
path: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
// Return the first spelling of the target for the instruction set,
|
|
// or null if data is missing from the 'supported' table.
|
|
getInstructionSetTarget(instructionSet: InstructionSet): string | null {
|
|
if (!(instructionSet in this.supported)) return null;
|
|
if (this.supported[instructionSet].target.length === 0) return null;
|
|
return this.supported[instructionSet].target[0];
|
|
}
|
|
|
|
getCompilerInstructionSetHint(compilerArch: string | boolean, exe?: string): InstructionSet {
|
|
if (compilerArch && typeof compilerArch === 'string') {
|
|
for (const [instructionSet, method] of Object.entries(this.supported) as [
|
|
InstructionSet,
|
|
InstructionSetMethod,
|
|
][]) {
|
|
for (const target of method.target) {
|
|
if (compilerArch.includes(target)) {
|
|
return instructionSet;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (const [instructionSet, method] of Object.entries(this.supported) as [
|
|
InstructionSet,
|
|
InstructionSetMethod,
|
|
][]) {
|
|
for (const path of method.path) {
|
|
if (exe?.includes(path)) {
|
|
return instructionSet;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.defaultInstructionset;
|
|
}
|
|
}
|