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,
|
UnprocessedExecResult,
|
||||||
} from '../types/execution/execution.interfaces.js';
|
} from '../types/execution/execution.interfaces.js';
|
||||||
import type {CompilerOutputOptions, ParseFiltersAndOutputOptions} from '../types/features/filters.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 {Language} from '../types/languages.interfaces.js';
|
||||||
import type {Library, LibraryVersion, SelectedLibraryVersion} from '../types/libraries/libraries.interfaces.js';
|
import type {Library, LibraryVersion, SelectedLibraryVersion} from '../types/libraries/libraries.interfaces.js';
|
||||||
import type {ResultLine} from '../types/resultline/resultline.interfaces.js';
|
import type {ResultLine} from '../types/resultline/resultline.interfaces.js';
|
||||||
@@ -270,16 +271,16 @@ export class BaseCompiler implements ICompiler {
|
|||||||
if (!this.compiler.instructionSet) {
|
if (!this.compiler.instructionSet) {
|
||||||
const isets = new InstructionSets();
|
const isets = new InstructionSets();
|
||||||
if (this.buildenvsetup) {
|
if (this.buildenvsetup) {
|
||||||
isets
|
this.compiler.instructionSet = isets.getCompilerInstructionSetHint(
|
||||||
.getCompilerInstructionSetHint(this.buildenvsetup.compilerArch, this.compiler.exe)
|
this.buildenvsetup.compilerArch,
|
||||||
.then(res => (this.compiler.instructionSet = res))
|
this.compiler.exe,
|
||||||
.catch(() => {});
|
);
|
||||||
} else {
|
} else {
|
||||||
const temp = new BuildEnvSetupBase(this.compiler, this.env);
|
const temp = new BuildEnvSetupBase(this.compiler, this.env);
|
||||||
isets
|
this.compiler.instructionSet = isets.getCompilerInstructionSetHint(
|
||||||
.getCompilerInstructionSetHint(temp.compilerArch, this.compiler.exe)
|
temp.compilerArch,
|
||||||
.then(res => (this.compiler.instructionSet = res))
|
this.compiler.exe,
|
||||||
.catch(() => {});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,6 +475,63 @@ export class BaseCompiler implements ICompiler {
|
|||||||
return undefined;
|
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(
|
async runCompiler(
|
||||||
compiler: string,
|
compiler: string,
|
||||||
options: string[],
|
options: string[],
|
||||||
@@ -493,6 +551,7 @@ export class BaseCompiler implements ICompiler {
|
|||||||
return {
|
return {
|
||||||
...this.transformToCompilationResult(result, inputFilename),
|
...this.transformToCompilationResult(result, inputFilename),
|
||||||
languageId: this.getCompilerResultLanguageId(filters),
|
languageId: this.getCompilerResultLanguageId(filters),
|
||||||
|
instructionSet: this.getInstructionSetFromCompilerArgs(options),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3485,6 +3544,15 @@ but nothing was dumped. Possible causes are:
|
|||||||
return [];
|
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> {
|
async getPossibleToolchains(): Promise<CompilerOverrideOptions> {
|
||||||
return this.env.getPossibleToolchains();
|
return this.env.getPossibleToolchains();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,15 +80,15 @@ export class InstructionSets {
|
|||||||
path: ['/msp430-'],
|
path: ['/msp430-'],
|
||||||
},
|
},
|
||||||
powerpc: {
|
powerpc: {
|
||||||
target: ['powerpc'],
|
target: ['powerpc', 'ppc64', 'ppc'],
|
||||||
path: ['/powerpc-', '/powerpc64-', '/powerpc64le-'],
|
path: ['/powerpc-', '/powerpc64-', '/powerpc64le-'],
|
||||||
},
|
},
|
||||||
riscv64: {
|
riscv64: {
|
||||||
target: ['rv64'],
|
target: ['rv64', 'riscv64'],
|
||||||
path: ['/riscv64-'],
|
path: ['/riscv64-'],
|
||||||
},
|
},
|
||||||
riscv32: {
|
riscv32: {
|
||||||
target: ['rv32'],
|
target: ['rv32', 'riscv32'],
|
||||||
path: ['/riscv32-'],
|
path: ['/riscv32-'],
|
||||||
},
|
},
|
||||||
sh: {
|
sh: {
|
||||||
@@ -182,35 +182,31 @@ export class InstructionSets {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompilerInstructionSetHint(compilerArch: string | boolean, exe: string): Promise<InstructionSet> {
|
getCompilerInstructionSetHint(compilerArch: string | boolean, exe?: string): InstructionSet {
|
||||||
return new Promise(resolve => {
|
if (compilerArch && typeof compilerArch === 'string') {
|
||||||
if (compilerArch && typeof compilerArch === 'string') {
|
for (const [instructionSet, method] of Object.entries(this.supported) as [
|
||||||
for (const [instructionSet, method] of Object.entries(this.supported) as [
|
InstructionSet,
|
||||||
InstructionSet,
|
InstructionSetMethod,
|
||||||
InstructionSetMethod,
|
][]) {
|
||||||
][]) {
|
for (const target of method.target) {
|
||||||
for (const target of method.target) {
|
if (compilerArch.includes(target)) {
|
||||||
if (compilerArch.includes(target)) {
|
return instructionSet;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} 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 compilerPickerElement: JQuery<HTMLElement>;
|
||||||
private compilerPicker: CompilerPicker;
|
private compilerPicker: CompilerPicker;
|
||||||
private compiler: CompilerInfo | null;
|
private compiler: CompilerInfo | null;
|
||||||
|
private recentInstructionSet: InstructionSet | null;
|
||||||
private currentLangId: string | null;
|
private currentLangId: string | null;
|
||||||
private filters: Toggles;
|
private filters: Toggles;
|
||||||
private optButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
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) {
|
setAssembly(result: Partial<CompilationResult>, filteredCount = 0) {
|
||||||
|
this.recentInstructionSet = result.instructionSet || null;
|
||||||
|
|
||||||
const asm = result.asm || this.fakeAsm('<No output>');
|
const asm = result.asm || this.fakeAsm('<No output>');
|
||||||
this.assembly = asm;
|
this.assembly = asm;
|
||||||
if (!this.editor.getModel()) return;
|
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)
|
this.isWordAsmKeyword(e.target.position.lineNumber, currentWord)
|
||||||
) {
|
) {
|
||||||
try {
|
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;
|
if (!response) return;
|
||||||
this.decorations.asmToolTip = [
|
this.decorations.asmToolTip = [
|
||||||
{
|
{
|
||||||
@@ -3691,7 +3697,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.compiler?.supportsAsmDocs) {
|
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) {
|
if (asmHelp) {
|
||||||
this.alertSystem.alert(opcode + ' help', asmHelp.html + appendInfo(asmHelp.url), {
|
this.alertSystem.alert(opcode + ' help', asmHelp.html + appendInfo(asmHelp.url), {
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ import {beforeAll, describe, expect, it} from 'vitest';
|
|||||||
import {BaseCompiler} from '../lib/base-compiler.js';
|
import {BaseCompiler} from '../lib/base-compiler.js';
|
||||||
import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js';
|
import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js';
|
||||||
import {CompilationEnvironment} from '../lib/compilation-env.js';
|
import {CompilationEnvironment} from '../lib/compilation-env.js';
|
||||||
|
import {ClangCompiler} from '../lib/compilers/clang.js';
|
||||||
import {Win32Compiler} from '../lib/compilers/win32.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 {CompilerOverrideType, ConfiguredOverrides} from '../types/compilation/compiler-overrides.interfaces.js';
|
||||||
import {CompilerInfo} from '../types/compiler.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']);
|
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';
|
import {InstructionSets} from '../lib/instructionsets.js';
|
||||||
|
|
||||||
describe('InstructionSets', async () => {
|
describe('InstructionSets', () => {
|
||||||
it('should recognize aarch64 for clang target', async () => {
|
it('should recognize aarch64 for clang target', () => {
|
||||||
const isets = new InstructionSets();
|
const isets = new InstructionSets();
|
||||||
|
|
||||||
await expect(
|
expect(
|
||||||
isets.getCompilerInstructionSetHint('aarch64-linux-gnu', '/opt/compiler-explorer/clang-11.0.1/bin/clang++'),
|
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();
|
const isets = new InstructionSets();
|
||||||
|
|
||||||
await expect(
|
expect(
|
||||||
isets.getCompilerInstructionSetHint(
|
isets.getCompilerInstructionSetHint(
|
||||||
false,
|
false,
|
||||||
'/opt/compiler-explorer/arm64/gcc-12.1.0/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++',
|
'/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();
|
const isets = new InstructionSets();
|
||||||
|
|
||||||
await expect(
|
expect(isets.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++')).toBe('amd64');
|
||||||
isets.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++'),
|
|
||||||
).resolves.toEqual('amd64');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {ParsedAsmResultLine} from '../asmresult/asmresult.interfaces.js';
|
|||||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||||
import {BasicExecutionResult, ConfiguredRuntimeTools} from '../execution/execution.interfaces.js';
|
import {BasicExecutionResult, ConfiguredRuntimeTools} from '../execution/execution.interfaces.js';
|
||||||
import {ParseFiltersAndOutputOptions} from '../features/filters.interfaces.js';
|
import {ParseFiltersAndOutputOptions} from '../features/filters.interfaces.js';
|
||||||
|
import {InstructionSet} from '../instructionsets.js';
|
||||||
import {ResultLine} from '../resultline/resultline.interfaces.js';
|
import {ResultLine} from '../resultline/resultline.interfaces.js';
|
||||||
import {Artifact, ToolResult} from '../tool.interfaces.js';
|
import {Artifact, ToolResult} from '../tool.interfaces.js';
|
||||||
|
|
||||||
@@ -226,6 +227,8 @@ export type CompilationResult = {
|
|||||||
parsingTime?: number;
|
parsingTime?: number;
|
||||||
|
|
||||||
source?: string; // todo: this is a crazy hack, we should get rid of it
|
source?: string; // todo: this is a crazy hack, we should get rid of it
|
||||||
|
|
||||||
|
instructionSet?: InstructionSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExecutionOptions = {
|
export type ExecutionOptions = {
|
||||||
|
|||||||
Reference in New Issue
Block a user