mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Dynamic instruction set for documentation (#6173)
This commit is contained in:
@@ -71,6 +71,7 @@ import {
|
||||
UnprocessedExecResult,
|
||||
} from '../types/execution/execution.interfaces.js';
|
||||
import type {CompilerOutputOptions, ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js';
|
||||
import {InstructionSet} from '../types/instructionsets.js';
|
||||
import type {Language} from '../types/languages.interfaces.js';
|
||||
import type {Library, LibraryVersion, SelectedLibraryVersion} from '../types/libraries/libraries.interfaces.js';
|
||||
import type {ResultLine} from '../types/resultline/resultline.interfaces.js';
|
||||
@@ -270,16 +271,16 @@ export class BaseCompiler implements ICompiler {
|
||||
if (!this.compiler.instructionSet) {
|
||||
const isets = new InstructionSets();
|
||||
if (this.buildenvsetup) {
|
||||
isets
|
||||
.getCompilerInstructionSetHint(this.buildenvsetup.compilerArch, this.compiler.exe)
|
||||
.then(res => (this.compiler.instructionSet = res))
|
||||
.catch(() => {});
|
||||
this.compiler.instructionSet = isets.getCompilerInstructionSetHint(
|
||||
this.buildenvsetup.compilerArch,
|
||||
this.compiler.exe,
|
||||
);
|
||||
} else {
|
||||
const temp = new BuildEnvSetupBase(this.compiler, this.env);
|
||||
isets
|
||||
.getCompilerInstructionSetHint(temp.compilerArch, this.compiler.exe)
|
||||
.then(res => (this.compiler.instructionSet = res))
|
||||
.catch(() => {});
|
||||
this.compiler.instructionSet = isets.getCompilerInstructionSetHint(
|
||||
temp.compilerArch,
|
||||
this.compiler.exe,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,6 +475,63 @@ export class BaseCompiler implements ICompiler {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getTargetHintFromCompilerArgs(args: string[]): string | undefined {
|
||||
const allFlags = this.getAllPossibleTargetFlags();
|
||||
|
||||
if (allFlags.length > 0) {
|
||||
for (const possibleFlag of allFlags) {
|
||||
// possible.flags contains either something like ['--target', '<value>'] or ['--target=<value>'], we want the flags without <value>
|
||||
const filteredFlags: string[] = [];
|
||||
let targetFlagOffset = -1;
|
||||
for (const [i, flag] of possibleFlag.entries()) {
|
||||
if (flag.includes(c_value_placeholder)) {
|
||||
filteredFlags.push(flag.replace(c_value_placeholder, ''));
|
||||
targetFlagOffset = i;
|
||||
} else {
|
||||
filteredFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetFlagOffset === -1) continue;
|
||||
|
||||
// try to find matching flags in args
|
||||
let foundFlag = -1;
|
||||
for (const arg of args) {
|
||||
if (arg.startsWith(filteredFlags[foundFlag + 1])) {
|
||||
foundFlag = foundFlag + 1;
|
||||
}
|
||||
|
||||
if (foundFlag === targetFlagOffset) {
|
||||
if (arg.length > filteredFlags[foundFlag].length) {
|
||||
return arg.substring(filteredFlags[foundFlag].length);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getInstructionSetFromCompilerArgs(args: string[]): InstructionSet {
|
||||
try {
|
||||
const archHint = this.getTargetHintFromCompilerArgs(args);
|
||||
if (archHint) {
|
||||
const isets = new InstructionSets();
|
||||
return isets.getCompilerInstructionSetHint(archHint, this.compiler.exe);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Unexpected error in getInstructionSetFromCompilerArgs(): ', e);
|
||||
}
|
||||
|
||||
if (this.compiler.instructionSet) {
|
||||
return this.compiler.instructionSet;
|
||||
} else {
|
||||
return 'amd64';
|
||||
}
|
||||
}
|
||||
|
||||
async runCompiler(
|
||||
compiler: string,
|
||||
options: string[],
|
||||
@@ -493,6 +551,7 @@ export class BaseCompiler implements ICompiler {
|
||||
return {
|
||||
...this.transformToCompilationResult(result, inputFilename),
|
||||
languageId: this.getCompilerResultLanguageId(filters),
|
||||
instructionSet: this.getInstructionSetFromCompilerArgs(options),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3485,6 +3544,15 @@ but nothing was dumped. Possible causes are:
|
||||
return [];
|
||||
}
|
||||
|
||||
getAllPossibleTargetFlags(): string[][] {
|
||||
const all: string[][] = [];
|
||||
if (this.compiler.supportsMarch) all.push([`-march=${c_value_placeholder}`]);
|
||||
if (this.compiler.supportsTargetIs) all.push([`--target=${c_value_placeholder}`]);
|
||||
if (this.compiler.supportsTarget) all.push(['--target', c_value_placeholder]);
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
async getPossibleToolchains(): Promise<CompilerOverrideOptions> {
|
||||
return this.env.getPossibleToolchains();
|
||||
}
|
||||
|
||||
@@ -80,15 +80,15 @@ export class InstructionSets {
|
||||
path: ['/msp430-'],
|
||||
},
|
||||
powerpc: {
|
||||
target: ['powerpc'],
|
||||
target: ['powerpc', 'ppc64', 'ppc'],
|
||||
path: ['/powerpc-', '/powerpc64-', '/powerpc64le-'],
|
||||
},
|
||||
riscv64: {
|
||||
target: ['rv64'],
|
||||
target: ['rv64', 'riscv64'],
|
||||
path: ['/riscv64-'],
|
||||
},
|
||||
riscv32: {
|
||||
target: ['rv32'],
|
||||
target: ['rv32', 'riscv32'],
|
||||
path: ['/riscv32-'],
|
||||
},
|
||||
sh: {
|
||||
@@ -182,35 +182,31 @@ export class InstructionSets {
|
||||
};
|
||||
}
|
||||
|
||||
async getCompilerInstructionSetHint(compilerArch: string | boolean, exe: string): Promise<InstructionSet> {
|
||||
return new Promise(resolve => {
|
||||
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)) {
|
||||
resolve(instructionSet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const [instructionSet, method] of Object.entries(this.supported) as [
|
||||
InstructionSet,
|
||||
InstructionSetMethod,
|
||||
][]) {
|
||||
for (const path of method.path) {
|
||||
if (exe.includes(path)) {
|
||||
resolve(instructionSet);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve(this.defaultInstructionset);
|
||||
});
|
||||
return this.defaultInstructionset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
private compilerPickerElement: JQuery<HTMLElement>;
|
||||
private compilerPicker: CompilerPicker;
|
||||
private compiler: CompilerInfo | null;
|
||||
private recentInstructionSet: InstructionSet | null;
|
||||
private currentLangId: string | null;
|
||||
private filters: Toggles;
|
||||
private optButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
@@ -1469,6 +1470,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
}
|
||||
|
||||
setAssembly(result: Partial<CompilationResult>, filteredCount = 0) {
|
||||
this.recentInstructionSet = result.instructionSet || null;
|
||||
|
||||
const asm = result.asm || this.fakeAsm('<No output>');
|
||||
this.assembly = asm;
|
||||
if (!this.editor.getModel()) return;
|
||||
@@ -3615,7 +3618,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
this.isWordAsmKeyword(e.target.position.lineNumber, currentWord)
|
||||
) {
|
||||
try {
|
||||
const response = await Compiler.getAsmInfo(currentWord.word, unwrap(this.compiler.instructionSet));
|
||||
const response = await Compiler.getAsmInfo(
|
||||
currentWord.word,
|
||||
unwrap(this.recentInstructionSet || this.compiler.instructionSet),
|
||||
);
|
||||
if (!response) return;
|
||||
this.decorations.asmToolTip = [
|
||||
{
|
||||
@@ -3691,7 +3697,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
|
||||
try {
|
||||
if (this.compiler?.supportsAsmDocs) {
|
||||
const asmHelp = await Compiler.getAsmInfo(word.word, unwrap(this.compiler.instructionSet));
|
||||
const asmHelp = await Compiler.getAsmInfo(
|
||||
word.word,
|
||||
unwrap(this.recentInstructionSet || this.compiler.instructionSet),
|
||||
);
|
||||
if (asmHelp) {
|
||||
this.alertSystem.alert(opcode + ' help', asmHelp.html + appendInfo(asmHelp.url), {
|
||||
onClose: () => {
|
||||
|
||||
@@ -27,7 +27,9 @@ import {beforeAll, describe, expect, it} from 'vitest';
|
||||
import {BaseCompiler} from '../lib/base-compiler.js';
|
||||
import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js';
|
||||
import {CompilationEnvironment} from '../lib/compilation-env.js';
|
||||
import {ClangCompiler} from '../lib/compilers/clang.js';
|
||||
import {Win32Compiler} from '../lib/compilers/win32.js';
|
||||
import {splitArguments} from '../lib/utils.js';
|
||||
import {CompilerOverrideType, ConfiguredOverrides} from '../types/compilation/compiler-overrides.interfaces.js';
|
||||
import {CompilerInfo} from '../types/compiler.interfaces.js';
|
||||
|
||||
@@ -714,3 +716,39 @@ describe('getDefaultExecOptions', () => {
|
||||
expect(paths).toEqual(['/usr/local/ninja', '/tmp/p1', '/tmp/p2']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Target hints', () => {
|
||||
let ce: CompilationEnvironment;
|
||||
|
||||
const noExecuteSupportCompilerInfo = makeFakeCompilerInfo({
|
||||
exe: '/usr/bin/clang++',
|
||||
lang: 'c++',
|
||||
supportsTargetIs: true,
|
||||
supportsTarget: true,
|
||||
ldPath: [],
|
||||
libPath: [],
|
||||
extraPath: [],
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
ce = makeCompilationEnvironment({
|
||||
languages,
|
||||
props: {
|
||||
environmentPassThrough: '',
|
||||
ninjaPath: '/usr/local/ninja',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should determine the target for Clang', async () => {
|
||||
const compiler = new ClangCompiler(noExecuteSupportCompilerInfo, ce);
|
||||
|
||||
const args =
|
||||
'-gdwarf-4 -g -o output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-13.2.0 -fcolor-diagnostics -fno-crash-diagnostics --target=riscv64 example.cpp -isystem/opt/compiler-explorer/libs/abseil';
|
||||
const argArray = splitArguments(args);
|
||||
const hint = compiler.getTargetHintFromCompilerArgs(argArray);
|
||||
expect(hint).toBe('riscv64');
|
||||
const iset = await compiler.getInstructionSetFromCompilerArgs(argArray);
|
||||
expect(iset).toBe('riscv64');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,31 +26,29 @@ import {describe, expect, it} from 'vitest';
|
||||
|
||||
import {InstructionSets} from '../lib/instructionsets.js';
|
||||
|
||||
describe('InstructionSets', async () => {
|
||||
it('should recognize aarch64 for clang target', async () => {
|
||||
describe('InstructionSets', () => {
|
||||
it('should recognize aarch64 for clang target', () => {
|
||||
const isets = new InstructionSets();
|
||||
|
||||
await expect(
|
||||
expect(
|
||||
isets.getCompilerInstructionSetHint('aarch64-linux-gnu', '/opt/compiler-explorer/clang-11.0.1/bin/clang++'),
|
||||
).resolves.toEqual('aarch64');
|
||||
).toBe('aarch64');
|
||||
});
|
||||
|
||||
it('should recognize gcc aarch64 from filepath', async () => {
|
||||
it('should recognize gcc aarch64 from filepath', () => {
|
||||
const isets = new InstructionSets();
|
||||
|
||||
await expect(
|
||||
expect(
|
||||
isets.getCompilerInstructionSetHint(
|
||||
false,
|
||||
'/opt/compiler-explorer/arm64/gcc-12.1.0/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++',
|
||||
),
|
||||
).resolves.toEqual('aarch64');
|
||||
).toBe('aarch64');
|
||||
});
|
||||
|
||||
it('should default to amd64 when not apparent', async () => {
|
||||
it('should default to amd64 when not apparent', () => {
|
||||
const isets = new InstructionSets();
|
||||
|
||||
await expect(
|
||||
isets.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++'),
|
||||
).resolves.toEqual('amd64');
|
||||
expect(isets.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++')).toBe('amd64');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ import {ParsedAsmResultLine} from '../asmresult/asmresult.interfaces.js';
|
||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||
import {BasicExecutionResult, ConfiguredRuntimeTools} from '../execution/execution.interfaces.js';
|
||||
import {ParseFiltersAndOutputOptions} from '../features/filters.interfaces.js';
|
||||
import {InstructionSet} from '../instructionsets.js';
|
||||
import {ResultLine} from '../resultline/resultline.interfaces.js';
|
||||
import {Artifact, ToolResult} from '../tool.interfaces.js';
|
||||
|
||||
@@ -226,6 +227,8 @@ export type CompilationResult = {
|
||||
parsingTime?: number;
|
||||
|
||||
source?: string; // todo: this is a crazy hack, we should get rid of it
|
||||
|
||||
instructionSet?: InstructionSet;
|
||||
};
|
||||
|
||||
export type ExecutionOptions = {
|
||||
|
||||
Reference in New Issue
Block a user