Dynamic instruction set for documentation (#6173)

This commit is contained in:
Patrick Quist
2024-04-22 18:40:56 +02:00
committed by GitHub
parent b0825e79df
commit adb8bd774a
6 changed files with 162 additions and 50 deletions

View File

@@ -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();
} }

View File

@@ -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;
});
} }
} }

View File

@@ -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: () => {

View File

@@ -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');
});
});

View File

@@ -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');
}); });
}); });

View File

@@ -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 = {