diff options
author | nickpdemarco <nickpdemarco@gmail.com> | 2023-10-11 14:43:05 -0400 |
---|---|---|
committer | nickpdemarco <nickpdemarco@gmail.com> | 2023-10-11 14:43:05 -0400 |
commit | 9cda199e40c920fcc9c53082b0dabb17d31a8b7f (patch) | |
tree | a4e9a03a9feea17376f5990e19c12818d32ec95e /lib/compilers | |
parent | 55a2b1455f823026c78b9219f93799af00ef60ae (diff) | |
parent | 10796b3696cf1eef928de8c750b4d3350ee0c2db (diff) | |
download | compiler-explorer-9cda199e40c920fcc9c53082b0dabb17d31a8b7f.tar.gz compiler-explorer-9cda199e40c920fcc9c53082b0dabb17d31a8b7f.zip |
Merge main, resolve conflicts with vala
Diffstat (limited to 'lib/compilers')
69 files changed, 2685 insertions, 329 deletions
diff --git a/lib/compilers/_all.ts b/lib/compilers/_all.ts index efb91b8d0..03593ba60 100644 --- a/lib/compilers/_all.ts +++ b/lib/compilers/_all.ts @@ -27,6 +27,7 @@ export {AnalysisTool} from './analysis-tool.js'; export {AssemblyCompiler} from './assembly.js'; export {AvrGcc6502Compiler} from './avrgcc6502.js'; export {BeebAsmCompiler} from './beebasm.js'; +export {C3Compiler} from './c3c.js'; export {CarbonCompiler} from './carbon.js'; export {Cc65Compiler} from './cc65.js'; export {CircleCompiler} from './circle.js'; @@ -37,7 +38,10 @@ export {ClangCLCompiler} from './clangcl.js'; export {ClangCudaCompiler} from './clang.js'; export {ClangHipCompiler} from './clang.js'; export {ClangIntelCompiler} from './clang.js'; +export {ClangHexagonCompiler} from './clang.js'; +export {ClangDxcCompiler} from './clang.js'; export {CleanCompiler} from './clean.js'; +export {CompCertCompiler} from './compcert.js'; export {CppFrontCompiler} from './cppfront.js'; export {CprocCompiler} from './cproc.js'; export {CLSPVCompiler} from './clspv.js'; @@ -46,6 +50,7 @@ export {CSharpCompiler} from './dotnet.js'; export {DartCompiler} from './dart.js'; export {DefaultCompiler} from './default.js'; export {DMDCompiler} from './dmd.js'; +export {EDGCompiler} from './edg.js'; export {EllccCompiler} from './ellcc.js'; export {ErlangCompiler} from './erlang.js'; export {EWARMCompiler} from './ewarm.js'; @@ -57,6 +62,7 @@ export {FPCCompiler} from './pascal.js'; export {FSharpCompiler} from './dotnet.js'; export {GCCCompiler} from './gcc.js'; export {GCCRSCompiler} from './gccrs.js'; +export {GCCCobolCompiler} from './gcccobol.js'; export {GnuCobolCompiler} from './gnucobol.js'; export {GolangCompiler} from './golang.js'; export {HaskellCompiler} from './haskell.js'; @@ -71,13 +77,16 @@ export {LDCCompiler} from './ldc.js'; export {LLCCompiler} from './llc.js'; export {LLVMmcaTool} from './llvm-mca.js'; export {LLVMMOSCompiler} from './llvm-mos.js'; +export {MovfuscatorCompiler} from './movfuscator.js'; export {MLIRCompiler} from './mlir.js'; export {GM2Compiler} from './gm2.js'; export {MrustcCompiler} from './mrustc.js'; +export {SnowballCompiler} from './snowball.js'; export {NasmCompiler} from './nasm.js'; export {NimCompiler} from './nim.js'; export {NvccCompiler} from './nvcc.js'; export {NvrtcCompiler} from './nvrtc.js'; +export {NvcppCompiler} from './nvcpp.js'; export {OCamlCompiler} from './ocaml.js'; export {OptCompiler} from './opt.js'; export {OSACATool} from './osaca.js'; @@ -97,14 +106,20 @@ export {SolidityCompiler} from './solidity.js'; export {SPIRVCompiler} from './spirv.js'; export {SwiftCompiler} from './swift.js'; export {TenDRACompiler} from './tendra.js'; +export {TIC2000} from './tic2000.js'; export {TinyCCompiler} from './tinyc.js'; export {ToitCompiler} from './toit.js'; export {TurboCCompiler} from './turboc.js'; export {TypeScriptNativeCompiler} from './typescript-native.js'; +export {VCompiler} from './v.js'; +export {ValaCompiler} from './vala.js'; export {VBCompiler} from './dotnet.js'; +export {V8Compiler} from './v8.js'; export {Win32Compiler} from './win32.js'; export {Win32Vc6Compiler} from './win32-vc6.js'; export {Win32VcCompiler} from './win32-vc.js'; +export {Win32MingWGcc} from './win32-mingw-gcc.js'; +export {Win32MingWClang} from './win32-mingw-clang.js'; export {WineVcCompiler} from './wine-vc.js'; export {WslVcCompiler} from './wsl-vc.js'; export {ValCompiler} from './val.js'; diff --git a/lib/compilers/ada.ts b/lib/compilers/ada.ts index be8434596..a379449ea 100644 --- a/lib/compilers/ada.ts +++ b/lib/compilers/ada.ts @@ -24,11 +24,13 @@ // POSSIBILITY OF SUCH DAMAGE. import path from 'path'; +import {unwrap} from '../assert.js'; import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; import * as utils from '../utils.js'; +import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js'; export class AdaCompiler extends BaseCompiler { static get key() { @@ -37,9 +39,17 @@ export class AdaCompiler extends BaseCompiler { constructor(info: PreliminaryCompilerInfo, env) { super(info, env); + + this.outputFilebase = 'example'; + this.compiler.supportsGccDump = true; this.compiler.removeEmptyGccDump = true; + // this is not showing-up in the --help, so argument parser doesn't + // automatically detect the support. + this.compiler.stackUsageArg = '-fstack-usage'; + this.compiler.supportsStackUsageOutput = true; + // used for all GNAT related panes (Expanded code, Tree) this.compiler.supportsGnatDebugViews = true; } @@ -76,6 +86,7 @@ export class AdaCompiler extends BaseCompiler { inputFilename: string, outputFilename: string, libraries, + overrides: ConfiguredOverrides, ) { backendOptions = backendOptions || {}; @@ -99,6 +110,10 @@ export class AdaCompiler extends BaseCompiler { gnatmake_opts.push(`--RTS=${this.compiler.adarts}`); } + if (this.compiler.supportsStackUsageOutput && backendOptions.produceStackUsageInfo) { + gnatmake_opts.push(unwrap(this.compiler.stackUsageArg)); + } + if (!filters.execute && backendOptions.produceGnatDebug && this.compiler.supportsGnatDebugViews) // This is using stdout gnatmake_opts.push('-gnatGL'); diff --git a/lib/compilers/analysis-tool.ts b/lib/compilers/analysis-tool.ts index c7d96afd1..073540d1d 100644 --- a/lib/compilers/analysis-tool.ts +++ b/lib/compilers/analysis-tool.ts @@ -35,7 +35,7 @@ export class AnalysisTool extends BaseCompiler { super( { // Default is to disable all "cosmetic" filters - disabledFilters: ['labels', 'directives', 'commentOnly', 'trim'], + disabledFilters: ['labels', 'directives', 'commentOnly', 'trim', 'debugCalls'], ...info, }, env, @@ -56,6 +56,7 @@ export class AnalysisTool extends BaseCompiler { libraryCode: false, trim: false, binaryObject: false, + debugCalls: false, }; } } diff --git a/lib/compilers/argument-parsers.ts b/lib/compilers/argument-parsers.ts index b87d59a5e..4586a7eb4 100644 --- a/lib/compilers/argument-parsers.ts +++ b/lib/compilers/argument-parsers.ts @@ -30,49 +30,108 @@ import _ from 'underscore'; import {logger} from '../logger.js'; import * as props from '../properties.js'; import * as utils from '../utils.js'; +import fs from 'fs-extra'; +import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js'; export class BaseParser { + static setCompilerSettingsFromOptions(compiler, options) {} + static hasSupport(options, forOption) { return _.keys(options).find(option => option.includes(forOption)); } - static parseLines(stdout, optionRegex: RegExp) { + static hasSupportStartsWith(options, forOption) { + return _.keys(options).find(option => option.startsWith(forOption)); + } + + static getExamplesRoot(): string { + return props.get('builtin', 'sourcePath', './examples/'); + } + + static getDefaultExampleFilename() { + return 'c++/default.cpp'; + } + + static getExampleFilepath(): string { + let filename = path.join(this.getExamplesRoot(), this.getDefaultExampleFilename()); + if (!path.isAbsolute(filename)) filename = path.join(process.cwd(), filename); + + return filename; + } + + static parseLines(stdout, optionWithDescRegex: RegExp, optionWithoutDescRegex?: RegExp) { let previousOption: false | string = false; const options = {}; utils.eachLine(stdout, line => { - const match = line.match(optionRegex); - if (!match) { - if (previousOption && line.trim().length > 0) { - if (options[previousOption].description.endsWith('-')) - options[previousOption].description += line.trim(); - else { - if (options[previousOption].description.length > 0) - options[previousOption].description += ' ' + line.trim(); - else options[previousOption].description = line.trim(); - } - } else { - previousOption = false; + const match1 = line.match(optionWithDescRegex); + if (match1 && match1[1] && match1[2]) { + previousOption = match1[1].trim(); + if (previousOption) { + options[previousOption] = { + description: this.spaceCompress(match1[2].trim()), + timesused: 0, + }; } return; + } else if (optionWithoutDescRegex) { + const match2 = line.match(optionWithoutDescRegex); + if (match2 && match2[1]) { + previousOption = match2[1].trim(); + + if (previousOption) { + options[previousOption] = { + description: '', + timesused: 0, + }; + } + return; + } } - if (match) previousOption = match[1]; - if (previousOption) { - options[previousOption] = { - description: match[2].trim(), - timesused: 0, - }; + if (previousOption && line.trim().length > 0) { + if (options[previousOption].description.endsWith('-')) + options[previousOption].description += line.trim(); + else { + if (options[previousOption].description.length > 0) { + const combined = options[previousOption].description + ' ' + line.trim(); + options[previousOption].description = combined; + } else { + options[previousOption].description = line.trim(); + } + } + + options[previousOption].description = this.spaceCompress(options[previousOption].description); + } else { + previousOption = false; } }); return options; } + static spaceCompress(text: string): string { + return text.replaceAll(' ', ' '); + } + + static async getPossibleTargets(compiler): Promise<string[]> { + return []; + } + + static async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + return []; + } + + static async getPossibleEditions(compiler): Promise<string[]> { + return []; + } + static async getOptions(compiler, helpArg) { - const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; + const optionFinder1 = /^ *(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*) +(.*)/i; + const optionFinder2 = /^ *(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*)/i; const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; + const options = + result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder1, optionFinder2) : {}; compiler.possibleArguments.populateOptions(options); return options; } @@ -83,22 +142,30 @@ export class BaseParser { } export class GCCParser extends BaseParser { - static async setCompilerSettingsFromOptions(compiler, options) { + static async checkAndSetMasmIntelIfSupported(compiler) { + // -masm= may be available but unsupported by the compiler. + const res = await compiler.execCompilerCached(compiler.compiler.exe, [ + '-fsyntax-only', + '--target-help', + '-masm=intel', + ]); + if (res.code === 0) { + compiler.compiler.intelAsm = '-masm=intel'; + compiler.compiler.supportsIntel = true; + } + } + + static override async setCompilerSettingsFromOptions(compiler, options) { const keys = _.keys(options); logger.debug(`gcc-like compiler options: ${keys.join(' ')}`); - if (BaseParser.hasSupport(options, '-masm=')) { - // -masm= may be available but unsupported by the compiler. - const res = await compiler.execCompilerCached(compiler.compiler.exe, [ - '-fsyntax-only', - '--target-help', - '-masm=intel', - ]); - if (res.code === 0) { - compiler.compiler.intelAsm = '-masm=intel'; - compiler.compiler.supportsIntel = true; - } + if (this.hasSupport(options, '-masm=')) { + await this.checkAndSetMasmIntelIfSupported(compiler); + } + if (this.hasSupport(options, '-fstack-usage')) { + compiler.compiler.stackUsageArg = '-fstack-usage'; + compiler.compiler.supportsStackUsageOutput = true; } - if (BaseParser.hasSupport(options, '-fdiagnostics-color')) { + if (this.hasSupport(options, '-fdiagnostics-color')) { if (compiler.compiler.options) compiler.compiler.options += ' '; compiler.compiler.options += '-fdiagnostics-color=always'; } @@ -111,46 +178,100 @@ export class GCCParser extends BaseParser { // not produce anything. compiler.compiler.removeEmptyGccDump = true; } + if (this.hasSupportStartsWith(options, '-march=')) compiler.compiler.supportsMarch = true; + if (this.hasSupportStartsWith(options, '--target=')) compiler.compiler.supportsTargetIs = true; + if (this.hasSupportStartsWith(options, '--target ')) compiler.compiler.supportsTarget = true; } static override async parse(compiler) { const results = await Promise.all([ - GCCParser.getOptions(compiler, '-fsyntax-only --help'), - GCCParser.getOptions(compiler, '-fsyntax-only --target-help'), - GCCParser.getOptions(compiler, '-fsyntax-only --help=common'), - GCCParser.getOptions(compiler, '-fsyntax-only --help=warnings'), - GCCParser.getOptions(compiler, '-fsyntax-only --help=optimizers'), + this.getOptions(compiler, '-fsyntax-only --help'), + this.getOptions(compiler, '-fsyntax-only --target-help'), + this.getOptions(compiler, '-fsyntax-only --help=common'), + this.getOptions(compiler, '-fsyntax-only --help=warnings'), + this.getOptions(compiler, '-fsyntax-only --help=optimizers'), + this.getOptions(compiler, '-fsyntax-only --help=target'), ]); const options = Object.assign({}, ...results); await this.setCompilerSettingsFromOptions(compiler, options); return compiler; } + static override async getPossibleTargets(compiler): Promise<string[]> { + const re = /Known valid arguments for -march= option:\s+(.*)/; + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['-fsyntax-only', '--target-help']); + const match = result.stdout.match(re); + if (match) { + return match[1].split(' '); + } else { + return []; + } + } + + static getLanguageSpecificHelpFlags(): string[] { + return ['-fsyntax-only', '--help=c++']; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + const possible: CompilerOverrideOptions = []; + const options = await this.getOptionsStrict(compiler, this.getLanguageSpecificHelpFlags()); + for (const opt in options) { + if (opt.startsWith('-std=') && !options[opt].description?.startsWith('Deprecated')) { + const stdver = opt.substring(5); + possible.push({ + name: stdver + ': ' + options[opt].description, + value: stdver, + }); + } + } + return possible; + } + static override async getOptions(compiler, helpArg) { - const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; + const optionFinder1 = /^ *(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*) +(.*)/i; + const optionFinder2 = /^ *(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*)/i; const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; + const options = + result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder1, optionFinder2) : {}; compiler.possibleArguments.populateOptions(options); return options; } + + static async getOptionsStrict(compiler, helpArgs: string[]) { + const optionFinder = /^ {2}(--?[\d+,<=>[\]a-z|-]*) *(.*)/i; + const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArgs); + return result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder) : {}; + } } export class ClangParser extends BaseParser { static mllvmOptions = new Set<string>(); - static setCompilerSettingsFromOptions(compiler, options) { - logger.debug(`clang-like compiler options: ${_.keys(options).join(' ')}`); - if (BaseParser.hasSupport(options, '-fsave-optimization-record')) { + static override setCompilerSettingsFromOptions(compiler, options) { + const keys = _.keys(options); + logger.debug(`clang-like compiler options: ${keys.join(' ')}`); + + if (keys.length === 0) { + logger.error(`compiler options appear empty for ${compiler.compiler.id}`); + } + + if (this.hasSupport(options, '-fsave-optimization-record')) { compiler.compiler.optArg = '-fsave-optimization-record'; compiler.compiler.supportsOptOutput = true; } - if (BaseParser.hasSupport(options, '-emit-llvm')) { + if (this.hasSupport(options, '-fstack-usage')) { + compiler.compiler.stackUsageArg = '-fstack-usage'; + compiler.compiler.supportsStackUsageOutput = true; + } + + if (this.hasSupport(options, '-emit-llvm')) { compiler.compiler.supportsIrView = true; compiler.compiler.irArg = ['-Xclang', '-emit-llvm', '-fsyntax-only']; + compiler.compiler.minIrArgs = ['-emit-llvm']; } if ( - BaseParser.hasSupport(options, '-mllvm') && + this.hasSupport(options, '-mllvm') && this.mllvmOptions.has('--print-before-all') && this.mllvmOptions.has('--print-after-all') ) { @@ -161,76 +282,239 @@ export class ClangParser extends BaseParser { if (this.mllvmOptions.has('--print-module-scope')) { compiler.compiler.llvmOptModuleScopeArg = ['-mllvm', '-print-module-scope']; } - if (BaseParser.hasSupport(options, '-fno-discard-value-names')) { + if (this.hasSupport(options, '-fno-discard-value-names')) { compiler.compiler.llvmOptNoDiscardValueNamesArg = ['-fno-discard-value-names']; } } - if (BaseParser.hasSupport(options, '-fcolor-diagnostics')) compiler.compiler.options += ' -fcolor-diagnostics'; - if (BaseParser.hasSupport(options, '-fno-crash-diagnostics')) - compiler.compiler.options += ' -fno-crash-diagnostics'; + if (this.hasSupport(options, '-fcolor-diagnostics')) compiler.compiler.options += ' -fcolor-diagnostics'; + if (this.hasSupport(options, '-fno-crash-diagnostics')) compiler.compiler.options += ' -fno-crash-diagnostics'; + + if (this.hasSupportStartsWith(options, '--target=')) compiler.compiler.supportsTargetIs = true; + if (this.hasSupportStartsWith(options, '--target ')) compiler.compiler.supportsTarget = true; + } + + static getMainHelpOptions(): string[] { + return ['--help']; + } + + static getHiddenHelpOptions(exampleFile: string): string[] { + return ['-mllvm', '--help-list-hidden', exampleFile, '-c']; + } + + static getStdVersHelpOptions(exampleFile: string): string[] { + return ['-std=c++9999999', exampleFile, '-c']; + } + + static getTargetsHelpOptions(): string[] { + return ['--print-targets']; } static override async parse(compiler) { try { - const options = await ClangParser.getOptions(compiler, '--help'); + const options = await this.getOptions(compiler, this.getMainHelpOptions().join(' ')); - const EXAMPLES_PATH = props.get('builtin', 'sourcePath', './examples/'); - let filename = path.join(EXAMPLES_PATH, 'c++/default.cpp'); - if (!path.isAbsolute(filename)) filename = path.join(process.cwd(), filename); + const filename = this.getExampleFilepath(); this.mllvmOptions = new Set( - _.keys(await ClangParser.getOptions(compiler, `-mllvm --help-list-hidden ${filename} -c`, false)), + _.keys(await this.getOptions(compiler, this.getHiddenHelpOptions(filename).join(' '), false, true)), ); this.setCompilerSettingsFromOptions(compiler, options); return compiler; } catch (error) { - logger.error('Error while trying to generate llvm backend arguments'); - logger.debug(error); + logger.error(`Error while trying to generate llvm backend arguments for ${compiler.compiler.id}: ${error}`); + return null; } } - static override async getOptions(compiler, helpArg, populate = true) { - const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; - const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; - if (populate) { - compiler.possibleArguments.populateOptions(options); + static getRegexMatchesAsStdver(match, maxToMatch): CompilerOverrideOptions { + if (!match) return []; + if (!match[maxToMatch]) return []; + + const arr: CompilerOverrideOptions = []; + + for (let i = 1; i < maxToMatch; i++) { + if (!match[i]) return []; + + arr.push({ + name: match[i] + ': ' + match[maxToMatch], + value: match[i], + }); + } + + return arr; + } + + static extractPossibleStdvers(lines: string[]): CompilerOverrideOptions { + const possible: CompilerOverrideOptions = []; + const re1 = /note: use '([\w\d+:]*)' for '(.*)' standard/; + const re2 = /note: use '([\w\d+:]*)' or '([\w\d+:]*)' for '(.*)' standard/; + const re3 = /note: use '([\w\d+:]*)', '([\w\d+:]*)', or '([\w\d+:]*)' for '(.*)' standard/; + const re4 = /note: use '([\w\d+:]*)', '([\w\d+:]*)', '([\w\d+:]*)', or '([\w\d+:]*)' for '(.*)' standard/; + for (const line of lines) { + let match = line.match(re1); + let stdvers = this.getRegexMatchesAsStdver(match, 2); + possible.push(...stdvers); + if (stdvers.length > 0) continue; + + match = line.match(re2); + stdvers = this.getRegexMatchesAsStdver(match, 3); + possible.push(...stdvers); + if (stdvers.length > 0) continue; + + match = line.match(re3); + stdvers = this.getRegexMatchesAsStdver(match, 4); + possible.push(...stdvers); + if (stdvers.length > 0) continue; + + match = line.match(re4); + stdvers = this.getRegexMatchesAsStdver(match, 5); + possible.push(...stdvers); } + return possible; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + let possible: CompilerOverrideOptions = []; + + // clang doesn't have a --help option to get the std versions, we'll have to compile with a fictional stdversion to coax a response + const filename = this.getExampleFilepath(); + + const result = await compiler.execCompilerCached(compiler.compiler.exe, this.getStdVersHelpOptions(filename), { + ...compiler.getDefaultExecOptions(), + createAndUseTempDir: true, + }); + if (result.stderr) { + const lines = utils.splitLines(result.stderr); + + possible = this.extractPossibleStdvers(lines); + possible.sort((a, b) => { + return a.value === b.value ? 0 : a.value > b.value ? 1 : -1; + }); + } + + return possible; + } + + static extractPossibleTargets(lines: string[]): string[] { + const re = /\s+([\w-]*)\s*-\s.*/; + return lines + .map(line => { + const match = line.match(re); + if (match) { + return match[1]; + } else { + return false; + } + }) + .filter(Boolean) as string[]; + } + + static override async getPossibleTargets(compiler): Promise<string[]> { + const result = await compiler.execCompilerCached(compiler.compiler.exe, this.getTargetsHelpOptions()); + return this.extractPossibleTargets(utils.splitLines(result.stdout)); + } + + static override async getOptions(compiler, helpArg, populate = true, isolate = false) { + const optionFinderWithDesc = /^ {2}?(--?[#\d+,<=>[\]a-zA-Z|-]*\s?[\d+,<=>[\]a-zA-Z|-]*)\s+([A-Z].*)/; + const optionFinderWithoutDesc = /^ {2}?(--?[#\d+,<=>[\]a-z|-]*\s?[\d+,<=>[\]a-z|-]*)/i; + const execOptions = isolate ?? {...compiler.getDefaultExecOptions(), createAndUseTempDir: true}; + const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' '), execOptions); + const options = + result.code === 0 + ? this.parseLines(result.stdout + result.stderr, optionFinderWithDesc, optionFinderWithoutDesc) + : {}; + if (populate) compiler.possibleArguments.populateOptions(options); return options; } } +export class GCCCParser extends GCCParser { + static override getLanguageSpecificHelpFlags(): string[] { + return ['-fsyntax-only', '--help=c']; + } + + static override getDefaultExampleFilename() { + return 'c/default.c'; + } +} + +export class ClangCParser extends ClangParser { + static override getDefaultExampleFilename() { + return 'c/default.c'; + } + + static override getStdVersHelpOptions(exampleFile: string): string[] { + return ['-std=c9999999', exampleFile, '-c']; + } +} + +export class CircleParser extends ClangParser { + static override async getOptions(compiler, helpArg) { + const optionFinder1 = /^ +(--?[#\d,<=>[\]a-z|_.-]*) +- (.*)/i; + const optionFinder2 = /^ +(--?[#\d,<=>[\]a-z|_.-]*)/i; + const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); + const options = result.code === 0 ? this.parseLines(result.stdout, optionFinder1, optionFinder2) : {}; + compiler.possibleArguments.populateOptions(options); + return options; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + const possible: CompilerOverrideOptions = []; + const optionFinder = /^ {4}=([\w+]*) +- +(.*)/i; + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['--help']); + let isInStdVerSection = false; + for (const line of utils.splitLines(result.stdout)) { + if (!isInStdVerSection && line.startsWith(' --std=')) { + isInStdVerSection = true; + continue; + } else if (isInStdVerSection && line.startsWith(' --')) { + break; + } + + if (!isInStdVerSection) continue; + + const match = line.match(optionFinder); + if (match) { + const stdver = match[1]; + const desc = match[2]; + possible.push({ + name: stdver + ': ' + desc, + value: stdver, + }); + } + } + return possible; + } +} + export class LDCParser extends BaseParser { - static setCompilerSettingsFromOptions(compiler, options) { - if (BaseParser.hasSupport(options, '--fsave-optimization-record')) { + static override setCompilerSettingsFromOptions(compiler, options) { + if (this.hasSupport(options, '--fsave-optimization-record')) { compiler.compiler.optArg = '--fsave-optimization-record'; compiler.compiler.supportsOptOutput = true; } - if ( - BaseParser.hasSupport(options, '--print-before-all') && - BaseParser.hasSupport(options, '--print-after-all') - ) { + if (this.hasSupport(options, '--print-before-all') && this.hasSupport(options, '--print-after-all')) { compiler.compiler.supportsLLVMOptPipelineView = true; compiler.compiler.llvmOptArg = ['--print-before-all', '--print-after-all']; compiler.compiler.llvmOptModuleScopeArg = []; compiler.compiler.llvmOptNoDiscardValueNamesArg = []; - if (BaseParser.hasSupport(options, '--print-module-scope')) { + if (this.hasSupport(options, '--print-module-scope')) { compiler.compiler.llvmOptModuleScopeArg = ['--print-module-scope']; } - if (BaseParser.hasSupport(options, '--fno-discard-value-names')) { + if (this.hasSupport(options, '--fno-discard-value-names')) { compiler.compiler.llvmOptNoDiscardValueNamesArg = ['--fno-discard-value-names']; } } - if (BaseParser.hasSupport(options, '--enable-color')) { + if (this.hasSupport(options, '--enable-color')) { compiler.compiler.options += ' --enable-color'; } } static override async parse(compiler) { - const options = await LDCParser.getOptions(compiler, '--help-hidden'); + const options = await this.getOptions(compiler, '--help-hidden'); this.setCompilerSettingsFromOptions(compiler, options); return compiler; } @@ -238,7 +522,7 @@ export class LDCParser extends BaseParser { static override async getOptions(compiler, helpArg, populate = true) { const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; + const options = result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder) : {}; if (populate) { compiler.possibleArguments.populateOptions(options); } @@ -248,28 +532,96 @@ export class LDCParser extends BaseParser { export class ErlangParser extends BaseParser { static override async parse(compiler) { - await ErlangParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } export class PascalParser extends BaseParser { static override async parse(compiler) { - await PascalParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); + return compiler; + } +} + +export class ICCParser extends GCCParser { + static override async setCompilerSettingsFromOptions(compiler, options) { + const keys = _.keys(options); + if (this.hasSupport(options, '-masm=')) { + compiler.compiler.intelAsm = '-masm=intel'; + compiler.compiler.supportsIntel = true; + } + if (this.hasSupport(options, '-fdiagnostics-color')) { + if (compiler.compiler.options) compiler.compiler.options += ' '; + compiler.compiler.options += '-fdiagnostics-color=always'; + } + if (_.find(keys, key => key.startsWith('-fdump-'))) { + compiler.compiler.supportsGccDump = true; + compiler.compiler.removeEmptyGccDump = true; + } + if (this.hasSupportStartsWith(options, '-march=')) compiler.compiler.supportsMarch = true; + if (this.hasSupportStartsWith(options, '--target=')) compiler.compiler.supportsTargetIs = true; + if (this.hasSupportStartsWith(options, '--target ')) compiler.compiler.supportsTarget = true; + } + + static extractPossibleStdvers(lines: string[]): CompilerOverrideOptions { + const stdverRe = /-std=<std>/; + const descRe = /^\s{12}([\w\d+]*)\s+(.*)/; + const possible: CompilerOverrideOptions = []; + let foundStdver = false; + let skipLine = false; + for (const line of lines) { + if (skipLine) { + skipLine = false; + continue; + } + + if (!foundStdver) { + const match = line.match(stdverRe); + if (match) { + foundStdver = true; + skipLine = true; + } + } else { + const descMatch = line.match(descRe); + if (descMatch) { + possible.push({ + name: descMatch[1] + ': ' + descMatch[2], + value: descMatch[1], + }); + } else { + break; + } + } + } + return possible; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['--help']); + const lines = utils.splitLines(result.stdout); + + return this.extractPossibleStdvers(lines); + } + + static override async parse(compiler) { + const results = await Promise.all([this.getOptions(compiler, '-fsyntax-only --help')]); + const options = Object.assign({}, ...results); + await this.setCompilerSettingsFromOptions(compiler, options); return compiler; } } export class ISPCParser extends BaseParser { - static async setCompilerSettingsFromOptions(compiler, options) { - if (BaseParser.hasSupport(options, '--x86-asm-syntax')) { + static override async setCompilerSettingsFromOptions(compiler, options) { + if (this.hasSupport(options, '--x86-asm-syntax')) { compiler.compiler.intelAsm = '--x86-asm-syntax=intel'; compiler.compiler.supportsIntel = true; } } static override async parse(compiler) { - const options = await ISPCParser.getOptions(compiler, '--help'); + const options = await this.getOptions(compiler, '--help'); await this.setCompilerSettingsFromOptions(compiler, options); return compiler; } @@ -277,7 +629,7 @@ export class ISPCParser extends BaseParser { static override async getOptions(compiler, helpArg) { const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); const optionFinder = /^\s*\[(--?[\d\s()+,/<=>a-z{|}-]*)]\s*(.*)/i; - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; + const options = result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder) : {}; compiler.possibleArguments.populateOptions(options); return options; } @@ -285,28 +637,28 @@ export class ISPCParser extends BaseParser { export class JavaParser extends BaseParser { static override async parse(compiler) { - await JavaParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } export class KotlinParser extends BaseParser { static override async parse(compiler) { - await KotlinParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } export class ScalaParser extends BaseParser { static override async parse(compiler) { - await ScalaParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } export class VCParser extends BaseParser { static override async parse(compiler) { - await VCParser.getOptions(compiler, '/help'); + await this.getOptions(compiler, '/help'); return compiler; } @@ -364,6 +716,43 @@ export class VCParser extends BaseParser { return options; } + static extractPossibleStdvers(lines: string[]): CompilerOverrideOptions { + const stdverRe = /\/std:<(.*)>\s.*/; + const descRe = /(c\+\+.*) - (.*)/; + const possible: CompilerOverrideOptions = []; + const stdverValues: string[] = []; + for (const line of lines) { + if (stdverValues.length === 0) { + const match = line.match(stdverRe); + if (match) { + stdverValues.push(...match[1].split('|')); + } + } else { + const descMatch = line.match(descRe); + if (descMatch) { + if (stdverValues.includes(descMatch[1])) { + possible.push({ + name: descMatch[1] + ': ' + descMatch[2], + value: descMatch[1], + }); + } else { + break; + } + } else { + break; + } + } + } + return possible; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['/help']); + const lines = utils.splitLines(result.stdout); + + return this.extractPossibleStdvers(lines); + } + static override async getOptions(compiler, helpArg) { const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); const optionFinder = /^\s*(\/[\w#+,.:<=>[\]{|}-]*)\s*(.*)/i; @@ -374,24 +763,94 @@ export class VCParser extends BaseParser { } export class RustParser extends BaseParser { - static async setCompilerSettingsFromOptions(compiler, options) { - if (BaseParser.hasSupport(options, '--color')) { + static override async setCompilerSettingsFromOptions(compiler, options) { + if (this.hasSupport(options, '--color')) { if (compiler.compiler.options) compiler.compiler.options += ' '; compiler.compiler.options += '--color=always'; } + if (this.hasSupportStartsWith(options, '--target=')) compiler.compiler.supportsTargetIs = true; + if (this.hasSupportStartsWith(options, '--target ')) compiler.compiler.supportsTarget = true; } static override async parse(compiler) { const results = await Promise.all([ - RustParser.getOptions(compiler, '--help'), - RustParser.getOptions(compiler, '-C help'), - RustParser.getOptions(compiler, '--help -v'), + this.getOptions(compiler, '--help'), + this.getOptions(compiler, '-C help'), + this.getOptions(compiler, '--help -v'), ]); const options = Object.assign({}, ...results); await this.setCompilerSettingsFromOptions(compiler, options); return compiler; } + static override async getPossibleEditions(compiler): Promise<string[]> { + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['--help']); + const re = /--edition ([\d|]*)/; + + const match = result.stdout.match(re); + if (match && match[1]) { + return match[1].split('|'); + } + + return []; + } + + static override async getPossibleTargets(compiler): Promise<string[]> { + const result = await compiler.execCompilerCached(compiler.compiler.exe, ['--print', 'target-list']); + return utils.splitLines(result.stdout).filter(Boolean); + } + + static parseRustHelpLines(stdout) { + let previousOption: false | string = false; + const options = {}; + + const doubleOptionFinder = /^\s{4}(-\w, --\w*\s?[\w[\]:=]*)\s*(.*)/i; + const singleOptionFinder = /^\s{8}(--[\w-]*\s?[\w[\]:=|-]*)\s*(.*)/i; + const singleComplexOptionFinder = /^\s{4}(-\w*\s?[\w[\]:=]*)\s*(.*)/i; + + utils.eachLine(stdout, line => { + let description = ''; + + const match1 = line.match(doubleOptionFinder); + const match2 = line.match(singleOptionFinder); + const match3 = line.match(singleComplexOptionFinder); + if (!match1 && !match2 && !match3) { + if (previousOption && line.trim().length > 0) { + if (options[previousOption].description.endsWith('-')) + options[previousOption].description += line.trim(); + else { + if (options[previousOption].description.length > 0) + options[previousOption].description += ' ' + line.trim(); + else options[previousOption].description = line.trim(); + } + } else { + previousOption = false; + } + return; + } else { + if (match1) { + previousOption = match1[1].trim(); + if (match1[2]) description = match1[2].trim(); + } else if (match2) { + previousOption = match2[1].trim(); + if (match2[2]) description = match2[2].trim(); + } else if (match3) { + previousOption = match3[1].trim(); + if (match3[2]) description = match3[2].trim(); + } + } + + if (previousOption) { + options[previousOption] = { + description: description, + timesused: 0, + }; + } + }); + + return options; + } + static override async getOptions(compiler, helpArg) { const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); let options = {}; @@ -399,11 +858,9 @@ export class RustParser extends BaseParser { if (helpArg === '-C help') { const optionFinder = /^\s*(-c\s*[\d=a-z-]*)\s--\s(.*)/i; - options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder); + options = this.parseLines(result.stdout + result.stderr, optionFinder); } else { - const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; - - options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder); + options = this.parseRustHelpLines(result.stdout + result.stderr); } } compiler.possibleArguments.populateOptions(options); @@ -413,42 +870,42 @@ export class RustParser extends BaseParser { export class MrustcParser extends BaseParser { static override async parse(compiler) { - await MrustcParser.getOptions(compiler, '--help'); + await this.getOptions(compiler, '--help'); return compiler; } } export class NimParser extends BaseParser { static override async parse(compiler) { - await NimParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } export class CrystalParser extends BaseParser { static override async parse(compiler) { - await CrystalParser.getOptions(compiler, 'build'); + await this.getOptions(compiler, 'build'); return compiler; } } export class TypeScriptNativeParser extends BaseParser { static override async parse(compiler) { - await TypeScriptNativeParser.getOptions(compiler, '--help'); + await this.getOptions(compiler, '--help'); return compiler; } } export class TurboCParser extends BaseParser { static override async parse(compiler) { - await TurboCParser.getOptions(compiler, ''); + await this.getOptions(compiler, ''); return compiler; } } export class ToitParser extends BaseParser { static override async parse(compiler) { - await ToitParser.getOptions(compiler, '-help'); + await this.getOptions(compiler, '-help'); return compiler; } } @@ -461,13 +918,171 @@ export class JuliaParser extends BaseParser { compiler.compilerWrapperPath, helpArg, ]); - const options = result.code === 0 ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; + const options = result.code === 0 ? this.parseLines(result.stdout + result.stderr, optionFinder) : {}; compiler.possibleArguments.populateOptions(options); return options; } static override async parse(compiler) { - await JuliaParser.getOptions(compiler, '--help'); + await this.getOptions(compiler, '--help'); return compiler; } } + +export class Z88dkParser extends BaseParser { + static override async getPossibleTargets(compiler): Promise<string[]> { + const configPath = path.join(path.dirname(compiler.compiler.exe), '../share/z88dk/lib/config'); + const targets: string[] = []; + const dir = await fs.readdir(configPath); + dir.forEach(filename => { + if (filename.toLowerCase().endsWith('.cfg')) { + targets.push(filename.substring(0, filename.length - 4)); + } + }); + return targets; + } +} + +export class ZigCxxParser extends ClangParser { + static override getMainHelpOptions(): string[] { + return ['c++', '--help']; + } + + static override getHiddenHelpOptions(exampleFile: string): string[] { + return ['c++', '-mllvm', '--help-list-hidden', exampleFile, '-S', '-o', '/tmp/output.s']; + } + + static override getStdVersHelpOptions(exampleFile: string): string[] { + return ['c++', '-std=c++9999999', exampleFile, '-S', '-o', '/tmp/output.s']; + } + + static override getTargetsHelpOptions(): string[] { + return ['c++', '--print-targets']; + } +} + +export class GccFortranParser extends GCCParser { + static override getDefaultExampleFilename() { + return 'fortran/default.f90'; + } + + static override getLanguageSpecificHelpFlags(): string[] { + return ['-fsyntax-only', '--help=fortran']; + } +} + +export class FlangParser extends ClangParser { + static override getDefaultExampleFilename() { + return 'fortran/default.f90'; + } + + static override hasSupport(options, param) { + // param is available but we get a warning, so lets not use it + if (param === '-fcolor-diagnostics') return undefined; + + return BaseParser.hasSupport(options, param); + } + + static override extractPossibleStdvers(lines: string[]): CompilerOverrideOptions { + const possible: CompilerOverrideOptions = []; + const re1 = /error: Only -std=([\w\d+]*) is allowed currently./; + for (const line of lines) { + const match = line.match(re1); + if (match && match[1]) { + possible.push({ + name: match[1], + value: match[1], + }); + } + } + return possible; + } +} + +export class GHCParser extends GCCParser { + static override async parse(compiler) { + const results = await Promise.all([this.getOptions(compiler, '--help')]); + const options = Object.assign({}, ...results); + await this.setCompilerSettingsFromOptions(compiler, options); + return compiler; + } + + static override async getOptions(compiler, helpArg) { + const optionFinder1 = /^ {4}(-[\w[\]]+)\s+(.*)/i; + const optionFinder2 = /^ {4}(-[\w[\]]+)/; + const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); + const options = result.code === 0 ? this.parseLines(result.stdout, optionFinder1, optionFinder2) : {}; + + compiler.possibleArguments.populateOptions(options); + return options; + } +} + +export class SwiftParser extends ClangParser { + static override async parse(compiler) { + const results = await Promise.all([this.getOptions(compiler, '--help')]); + const options = Object.assign({}, ...results); + this.setCompilerSettingsFromOptions(compiler, options); + return compiler; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + return []; + } + + static override async getPossibleTargets(compiler): Promise<string[]> { + return []; + } +} + +export class TendraParser extends GCCParser { + static override async parse(compiler) { + const results = await Promise.all([this.getOptions(compiler, '--help')]); + const options = Object.assign({}, ...results); + await this.setCompilerSettingsFromOptions(compiler, options); + return compiler; + } + + static override async getOptions(compiler, helpArg) { + const optionFinder = /^ *(-[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*) : +(.*)/i; + const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); + const options = this.parseLines(result.stdout + result.stderr, optionFinder); + compiler.possibleArguments.populateOptions(options); + return options; + } + + static override async getPossibleStdvers(compiler): Promise<CompilerOverrideOptions> { + return []; + } + + static override async getPossibleTargets(compiler): Promise<string[]> { + return []; + } +} + +export class GolangParser extends GCCParser { + static override getDefaultExampleFilename() { + return 'go/default.go'; + } + + static override async parse(compiler) { + const results = await Promise.all([ + this.getOptions(compiler, 'build -o ./output.s "-gcflags=-S --help" ' + this.getExampleFilepath()), + ]); + const options = Object.assign({}, ...results); + await this.setCompilerSettingsFromOptions(compiler, options); + return compiler; + } + + static override async getOptions(compiler, helpArg) { + const optionFinder1 = /^\s*(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*)\s+(.*)/i; + const optionFinder2 = /^\s*(--?[#\d+,<=>[\]a-z|-]* ?[\d+,<=>[\]a-z|-]*)/i; + const result = await compiler.execCompilerCached(compiler.compiler.exe, utils.splitArguments(helpArg), { + ...compiler.getDefaultExecOptions(), + createAndUseTempDir: true, + }); + const options = this.parseLines(result.stdout + result.stderr, optionFinder1, optionFinder2); + compiler.possibleArguments.populateOptions(options); + return options; + } +} diff --git a/lib/compilers/assembly.ts b/lib/compilers/assembly.ts index 7b64dd5bd..ff08ffee3 100644 --- a/lib/compilers/assembly.ts +++ b/lib/compilers/assembly.ts @@ -143,6 +143,8 @@ export class AssemblyCompiler extends BaseCompiler { buildFilters.binary = true; buildFilters.execute = false; + const overrides = this.sanitizeCompilerOverrides(key.backendOptions.overrides || []); + const compilerArguments = _.compact( this.prepareArguments( key.options, @@ -151,6 +153,7 @@ export class AssemblyCompiler extends BaseCompiler { inputFilename, outputFilename, key.libraries, + overrides, ), ); diff --git a/lib/compilers/avrgcc6502.ts b/lib/compilers/avrgcc6502.ts index 653cf2314..c293e69c9 100644 --- a/lib/compilers/avrgcc6502.ts +++ b/lib/compilers/avrgcc6502.ts @@ -70,7 +70,7 @@ export class AvrGcc6502Compiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/beebasm.ts b/lib/compilers/beebasm.ts index 60ee212be..e4b425476 100644 --- a/lib/compilers/beebasm.ts +++ b/lib/compilers/beebasm.ts @@ -56,7 +56,7 @@ export class BeebAsmCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/c3c.ts b/lib/compilers/c3c.ts new file mode 100644 index 000000000..a812583ae --- /dev/null +++ b/lib/compilers/c3c.ts @@ -0,0 +1,33 @@ +import path from 'path'; +import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; + +export class C3Compiler extends BaseCompiler { + static get key() { + return 'c3c'; + } + + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + this.compiler.supportsIrView = true; + this.compiler.irArg = ['--emit-llvm']; + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { + return [ + 'compile-only', + '-g', + '-l', + 'pthread', + '--no-strip-unused', + '--no-obj', + '--no-emit-stdlib', + '--emit-asm', + ]; + } + + override getIrOutputFilename(inputFilename: string): string { + return this.filename(path.dirname(inputFilename) + '/output.ll'); + } +} diff --git a/lib/compilers/carbon.ts b/lib/compilers/carbon.ts index deaa65a5c..f99e4c0d6 100644 --- a/lib/compilers/carbon.ts +++ b/lib/compilers/carbon.ts @@ -47,9 +47,9 @@ export class CarbonCompiler extends BaseCompiler { return ['--color', `--trace_file=${outputFilename}`]; } - override processAsm(result, filters, options): ParsedAsmResult { + override async processAsm(result, filters, options): Promise<ParsedAsmResult> { // Really should write a custom parser, but for now just don't filter anything. - return super.processAsm( + return await super.processAsm( result, { labels: false, @@ -63,6 +63,7 @@ export class CarbonCompiler extends BaseCompiler { intel: false, libraryCode: false, trim: false, + debugCalls: false, }, options, ); @@ -95,6 +96,7 @@ export class CarbonCompiler extends BaseCompiler { }, }; result.stdout = []; + result.languageId = 'no-highlight'; } return result; } diff --git a/lib/compilers/cc65.ts b/lib/compilers/cc65.ts index 1d35f7339..2042bad17 100644 --- a/lib/compilers/cc65.ts +++ b/lib/compilers/cc65.ts @@ -121,6 +121,19 @@ export class Cc65Compiler extends BaseCompiler { await this.addArtifactToResult(res, nesFile, ArtifactType.nesrom); } + if (result.compilationOptions?.includes('c64') && (await utils.fileExists(outputFilename))) { + if (!outputFilename.endsWith('.prg')) { + await this.addArtifactToResult( + res, + outputFilename, + ArtifactType.c64prg, + path.basename(outputFilename) + '.prg', + ); + } else { + await this.addArtifactToResult(res, outputFilename, ArtifactType.c64prg); + } + } + return res; } diff --git a/lib/compilers/circle.ts b/lib/compilers/circle.ts index 49d5dff38..5bf81259d 100644 --- a/lib/compilers/circle.ts +++ b/lib/compilers/circle.ts @@ -25,12 +25,18 @@ import path from 'path'; import {BaseCompiler} from '../base-compiler.js'; +import {CircleParser} from './argument-parsers.js'; +import {ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; export class CircleCompiler extends BaseCompiler { static get key() { return 'circle'; } + protected override getArgumentParser() { + return CircleParser; + } + override optionsForFilter(filters, outputFilename) { let options = [`-o=${this.filename(outputFilename)}`]; if (this.compiler.intelAsm && filters.intel && !filters.binary) { @@ -48,13 +54,18 @@ export class CircleCompiler extends BaseCompiler { return path.join(dirPath, outputFilebase); } - override runCompiler(compiler, options, inputFilename, execOptions) { + override async runCompiler( + compiler, + options, + inputFilename, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } execOptions.customCwd = path.dirname(inputFilename); - return super.runCompiler(compiler, options, inputFilename, execOptions); + return await super.runCompiler(compiler, options, inputFilename, execOptions); } } diff --git a/lib/compilers/circt.ts b/lib/compilers/circt.ts index 0331db196..bd8110007 100644 --- a/lib/compilers/circt.ts +++ b/lib/compilers/circt.ts @@ -47,6 +47,7 @@ export class CIRCTCompiler extends BaseCompiler { 'directives', 'commentOnly', 'trim', + 'debugCalls', ], ...compilerInfo, }, diff --git a/lib/compilers/clang.ts b/lib/compilers/clang.ts index d161e0f98..a5534aa36 100644 --- a/lib/compilers/clang.ts +++ b/lib/compilers/clang.ts @@ -27,13 +27,16 @@ import path from 'path'; import _ from 'underscore'; -import type {ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import type {BypassCache, CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ExecutableExecutionOptions, UnprocessedExecResult} from '../../types/execution/execution.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; import {AmdgpuAsmParser} from '../parsers/asm-parser-amdgpu.js'; import {SassAsmParser} from '../parsers/asm-parser-sass.js'; +import {HexagonAsmParser} from '../parsers/asm-parser-hexagon.js'; +import * as utils from '../utils.js'; +import {ArtifactType} from '../../types/tool.interfaces.js'; const offloadRegexp = /^#\s+__CLANG_OFFLOAD_BUNDLE__(__START__|__END__)\s+(.*)$/gm; @@ -68,10 +71,37 @@ export class ClangCompiler extends BaseCompiler { } } + override isCfgCompiler() { + return true; + } + + async addTimeTraceToResult(result: CompilationResult, dirPath: string, outputFilename: string) { + let timeTraceJson = ''; + const outputExt = path.extname(outputFilename); + if (outputExt) { + timeTraceJson = outputFilename.replace(outputExt, '.json'); + } else { + timeTraceJson += '.json'; + } + const jsonFilepath = path.join(dirPath, timeTraceJson); + if (await utils.fileExists(jsonFilepath)) { + this.addArtifactToResult( + result, + jsonFilepath, + ArtifactType.timetrace, + 'Trace events JSON', + (buffer: Buffer) => { + return buffer.toString('utf-8').startsWith('{"traceEvents":['); + }, + ); + } + } + override runExecutable(executable, executeParameters: ExecutableExecutionOptions, homeDir) { if (this.asanSymbolizerPath) { executeParameters.env = { ASAN_SYMBOLIZER_PATH: this.asanSymbolizerPath, + MSAN_SYMBOLIZER_PATH: this.asanSymbolizerPath, ...executeParameters.env, }; } @@ -88,20 +118,61 @@ export class ClangCompiler extends BaseCompiler { return options; } - override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) { const options = super.optionsForFilter(filters, outputFilename); return this.forceDwarf4UnlessOverridden(options); } - override runCompiler(compiler: string, options: string[], inputFilename: string, execOptions: ExecutionOptions) { + override async afterCompilation( + result, + doExecute, + key, + executeParameters, + tools, + backendOptions, + filters, + options, + optOutput, + stackUsageOutput, + bypassCache: BypassCache, + customBuildPath?, + ) { + const compilationInfo = this.getCompilationInfo(key, result, customBuildPath); + + const dirPath = path.dirname(compilationInfo.outputFilename); + const filename = path.basename(compilationInfo.outputFilename); + await this.addTimeTraceToResult(result, dirPath, filename); + + return super.afterCompilation( + result, + doExecute, + key, + executeParameters, + tools, + backendOptions, + filters, + options, + optOutput, + stackUsageOutput, + bypassCache, + customBuildPath, + ); + } + + override async runCompiler( + compiler: string, + options: string[], + inputFilename: string, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } execOptions.customCwd = path.dirname(inputFilename); - return super.runCompiler(compiler, options, inputFilename, execOptions); + return await super.runCompiler(compiler, options, inputFilename, execOptions); } async splitDeviceCode(assembly) { @@ -200,7 +271,7 @@ export class ClangCudaCompiler extends ClangCompiler { override async objdump(outputFilename, result, maxSize) { // For nvdisasm. - const args = [outputFilename, '-c', '-g', '-hex']; + const args = [...this.compiler.objdumperArgs, outputFilename, '-c', '-g', '-hex']; const execOptions = {maxOutput: maxSize, customCwd: path.dirname(outputFilename)}; const objResult = await this.exec(this.compiler.objdumper, args, execOptions); @@ -246,7 +317,7 @@ export class ClangIntelCompiler extends ClangCompiler { } } - override getDefaultExecOptions(): ExecutionOptions { + override getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} { const opts = super.getDefaultExecOptions(); opts.env.PATH = process.env.PATH + path.delimiter + path.dirname(this.compiler.exe); @@ -261,3 +332,39 @@ export class ClangIntelCompiler extends ClangCompiler { return super.runExecutable(executable, executeParameters, homeDir); } } + +export class ClangHexagonCompiler extends ClangCompiler { + static override get key() { + return 'clang-hexagon'; + } + + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + + this.asm = new HexagonAsmParser(); + } +} + +export class ClangDxcCompiler extends ClangCompiler { + static override get key() { + return 'clang-dxc'; + } + + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + + this.compiler.supportsIntel = false; + this.compiler.irArg = ['-Xclang', '-emit-llvm']; + // dxc mode doesn't have -fsave-optimization-record or -fstack-usage + this.compiler.supportsOptOutput = false; + this.compiler.supportsStackUsageOutput = false; + } + + override optionsForFilter( + filters: ParseFiltersAndOutputOptions, + outputFilename: string, + userOptions?: string[], + ): string[] { + return ['--driver-mode=dxc', '-Zi', '-Qembed_debug', '-Fc', this.filename(outputFilename)]; + } +} diff --git a/lib/compilers/clangcl.ts b/lib/compilers/clangcl.ts index 0d1866bc2..6b65a4f85 100644 --- a/lib/compilers/clangcl.ts +++ b/lib/compilers/clangcl.ts @@ -29,6 +29,7 @@ import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.in import {Win32Compiler} from './win32.js'; import {unwrap} from '../assert.js'; +import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js'; export class ClangCLCompiler extends Win32Compiler { static override get key() { @@ -40,11 +41,18 @@ export class ClangCLCompiler extends Win32Compiler { this.compiler.supportsIrView = true; this.compiler.irArg = ['-Xclang', '-emit-llvm']; + this.compiler.minIrArgs = ['-emit-llvm']; this.compiler.supportsIntel = false; this.compiler.includeFlag = '/clang:-isystem'; } - override async generateIR(inputFilename: string, options: string[], filters: ParseFiltersAndOutputOptions) { + override async generateIR( + inputFilename: string, + options: string[], + irOptions: LLVMIrBackendOptions, + produceCfg: boolean, + filters: ParseFiltersAndOutputOptions, + ) { // These options make Clang produce an IR const newOptions = options .filter(option => option !== '/FA' && !option.startsWith('/Fa')) @@ -56,13 +64,17 @@ export class ClangCLCompiler extends Win32Compiler { const output = await this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions); if (output.code !== 0) { - return [{text: 'Failed to run compiler to get IR code'}]; + return { + asm: [{text: 'Failed to run compiler to get IR code'}], + }; } - const ir = await this.processIrOutput(output, filters); - return ir.asm; + const ir = await this.processIrOutput(output, irOptions, filters); + return { + asm: ir.asm, + }; } - override getIrOutputFilename(inputFilename: string, filters: ParseFiltersAndOutputOptions): string { + override getIrOutputFilename(inputFilename: string): string { return this.filename(path.dirname(inputFilename) + '/output.s.obj'); } diff --git a/lib/compilers/clean.ts b/lib/compilers/clean.ts index ca1debdae..43d8a0c6f 100644 --- a/lib/compilers/clean.ts +++ b/lib/compilers/clean.ts @@ -106,7 +106,7 @@ export class CleanCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { const tmpDir = path.dirname(inputFilename); const moduleName = path.basename(inputFilename, '.icl'); diff --git a/lib/compilers/clspv.ts b/lib/compilers/clspv.ts index 28c5d7ddb..9b276f299 100644 --- a/lib/compilers/clspv.ts +++ b/lib/compilers/clspv.ts @@ -66,7 +66,7 @@ export class CLSPVCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { const sourceDir = path.dirname(inputFilename); const spvBinFilename = this.getPrimaryOutputFilename(sourceDir, this.outputFilebase); diff --git a/lib/compilers/compcert.ts b/lib/compilers/compcert.ts new file mode 100644 index 000000000..e1e7dc837 --- /dev/null +++ b/lib/compilers/compcert.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2023, 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 {BaseCompiler} from '../base-compiler.js'; + +export class CompCertCompiler extends BaseCompiler { + static get key() { + return 'compcert'; + } +} diff --git a/lib/compilers/dart.ts b/lib/compilers/dart.ts index b64762092..de67ec8b9 100644 --- a/lib/compilers/dart.ts +++ b/lib/compilers/dart.ts @@ -31,6 +31,7 @@ import {DartAsmParser} from '../parsers/asm-parser-dart.js'; import * as utils from '../utils.js'; import {BaseParser} from './argument-parsers.js'; +import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js'; export class DartCompiler extends BaseCompiler { constructor(info: PreliminaryCompilerInfo, env) { @@ -49,6 +50,7 @@ export class DartCompiler extends BaseCompiler { inputFilename: string, outputFilename: string, libraries, + overrides: ConfiguredOverrides, ) { let options = this.optionsForFilter(filters, outputFilename, userOptions); diff --git a/lib/compilers/dosbox-compiler.ts b/lib/compilers/dosbox-compiler.ts index 59f0847c5..b4b08c716 100644 --- a/lib/compilers/dosbox-compiler.ts +++ b/lib/compilers/dosbox-compiler.ts @@ -155,7 +155,7 @@ export class DosboxCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { return super.runCompiler( compiler, diff --git a/lib/compilers/dotnet.ts b/lib/compilers/dotnet.ts index 95ac84ad7..2c9c9a0b4 100644 --- a/lib/compilers/dotnet.ts +++ b/lib/compilers/dotnet.ts @@ -40,6 +40,8 @@ import * as exec from '../exec.js'; import {DotNetAsmParser} from '../parsers/asm-parser-dotnet.js'; import * as utils from '../utils.js'; +const AssemblyName = 'CompilerExplorer'; + class DotNetCompiler extends BaseCompiler { private readonly sdkBaseDir: string; private readonly sdkVersion: string; @@ -47,10 +49,12 @@ class DotNetCompiler extends BaseCompiler { private readonly buildConfig: string; private readonly clrBuildDir: string; private readonly langVersion: string; + private readonly corerunPath: string; + private readonly disassemblyLoaderPath: string; private readonly crossgen2Path: string; private readonly sdkMajorVersion: number; - private crossgen2VersionString: string; + private versionString: string; constructor(compilerInfo: PreliminaryCompilerInfo, env) { super(compilerInfo, env); @@ -66,9 +70,11 @@ class DotNetCompiler extends BaseCompiler { this.clrBuildDir = this.compilerProps<string>(`compiler.${this.compiler.id}.clrDir`); this.langVersion = this.compilerProps<string>(`compiler.${this.compiler.id}.langVersion`); + this.corerunPath = path.join(this.clrBuildDir, 'corerun'); this.crossgen2Path = path.join(this.clrBuildDir, 'crossgen2', 'crossgen2'); this.asm = new DotNetAsmParser(); - this.crossgen2VersionString = ''; + this.versionString = ''; + this.disassemblyLoaderPath = path.join(this.clrBuildDir, 'DisassemblyLoader', 'DisassemblyLoader.dll'); } get compilerOptions() { @@ -86,6 +92,9 @@ class DotNetCompiler extends BaseCompiler { '--singlemethodgenericarg', '--codegenopt', '--codegen-options', + '--maxgenericcycle', + '--maxgenericcyclebreadth', + '--max-vectort-bitwidth', ]; } @@ -99,6 +108,11 @@ class DotNetCompiler extends BaseCompiler { '--optimize-space', '--Ot', '--optimize-time', + '--enable-generic-cycle-detection', + '--inputbubble', + '--compilebubblegenerics', + '--aot', + '--crossgen2', ]; } @@ -107,7 +121,7 @@ class DotNetCompiler extends BaseCompiler { <PropertyGroup> <TargetFramework>${this.targetFramework}</TargetFramework> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - <AssemblyName>CompilerExplorer</AssemblyName> + <AssemblyName>${AssemblyName}</AssemblyName> <LangVersion>${this.langVersion}</LangVersion> <EnableDefaultCompileItems>false</EnableDefaultCompileItems> <Nullable>enable</Nullable> @@ -115,15 +129,20 @@ class DotNetCompiler extends BaseCompiler { </PropertyGroup> <ItemGroup> <Compile Include="${sourceFile}" /> + <Reference Include="DisassemblyLoader" HintPath="${this.disassemblyLoaderPath}" /> </ItemGroup> </Project> `; - const projectFilePath = path.join(programDir, `CompilerExplorer${this.lang.extensions[0]}proj`); + const projectFilePath = path.join(programDir, `${AssemblyName}${this.lang.extensions[0]}proj`); await fs.writeFile(projectFilePath, projectFileContent); } - setCompilerExecOptions(execOptions: ExecutionOptions, programDir: string) { + setCompilerExecOptions( + execOptions: ExecutionOptions & {env: Record<string, string>}, + programDir: string, + skipNuget: boolean = false, + ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } @@ -138,13 +157,16 @@ class DotNetCompiler extends BaseCompiler { // directory here. execOptions.env.DOTNET_CLI_HOME = programDir; execOptions.env.DOTNET_ROOT = path.join(this.clrBuildDir, '.dotnet'); - // Place nuget packages in the output directory. - execOptions.env.NUGET_PACKAGES = path.join(programDir, '.nuget'); // Try to be less chatty execOptions.env.DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 'true'; execOptions.env.DOTNET_NOLOGO = 'true'; execOptions.customCwd = programDir; + + if (!skipNuget) { + // Place nuget packages in the output directory. + execOptions.env.NUGET_PACKAGES = path.join(programDir, '.nuget'); + } } override async buildExecutable(compiler, options, inputFilename, execOptions) { @@ -152,7 +174,7 @@ class DotNetCompiler extends BaseCompiler { const inputFilenameSafe = this.filename(inputFilename); const sourceFile = path.basename(inputFilenameSafe); await this.writeProjectfile(dirPath, true, sourceFile); - return await this.buildToDll(compiler, options, inputFilename, execOptions); + return await this.buildToDll(compiler, inputFilename, execOptions); } override async doCompilation(inputFilename, dirPath, key, options, filters, backendOptions, libraries, tools) { @@ -164,9 +186,8 @@ class DotNetCompiler extends BaseCompiler { async buildToDll( compiler: string, - options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { const programDir = path.dirname(inputFilename); const nugetConfigPath = path.join(programDir, 'nuget.config'); @@ -194,51 +215,203 @@ class DotNetCompiler extends BaseCompiler { return compilerResult; } + async publishAot( + compiler: string, + ilcOptions: string[], + ilcSwitches: string[], + inputFilename: string, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ): Promise<CompilationResult> { + const programDir = path.dirname(inputFilename); + + this.setCompilerExecOptions(execOptions, programDir, true); + + // serialize ['a', 'b', 'c;d', '*e*'] into a=b;c%3Bd=%2Ae%2A; + const msbuildOptions = ilcOptions + .map(val => val.replaceAll('*', '%2A').replaceAll(';', '%3B')) + .reduce((acc, val, idx) => (idx % 2 === 0 ? (acc ? `${acc}${val}` : `${val}`) : `${acc}=${val};`), ''); + + // serialize ['a', 'b', 'c', 'd'] into a;b;c;d + const msbuildSwitches = ilcSwitches.join(';'); + + const toolVersion = await fs.readFile(`${this.clrBuildDir}/aot/package-version.txt`, 'utf8'); + const jitOutFile = `${programDir}/jitout`; + + ilcOptions.push('--codegenopt', `JitDisasmAssemblies=${AssemblyName}`); + + await fs.writeFile( + path.join(programDir, `${AssemblyName}${this.lang.extensions[0]}proj`), + `<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>${this.targetFramework}</TargetFramework> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <AssemblyName>${AssemblyName}</AssemblyName> + <LangVersion>${this.langVersion}</LangVersion> + <EnableDefaultCompileItems>false</EnableDefaultCompileItems> + <Nullable>enable</Nullable> + <OutputType>Library</OutputType> + <RuntimeIdentifier>linux-x64</RuntimeIdentifier> + </PropertyGroup> + <ItemGroup> + <Compile Include="${path.basename(this.filename(inputFilename))}" /> + <PackageReference + Include="Microsoft.DotNet.ILCompiler;runtime.linux-x64.Microsoft.DotNet.ILCompiler" + Version="${toolVersion}" /> + <IlcArg Include="${msbuildOptions}${msbuildSwitches};--codegenopt=JitStdOutFile=${jitOutFile}" /> + </ItemGroup> + </Project>`, + ); + + // prettier-ignore + const publishOptions = ['publish', '-c', this.buildConfig, '-v', 'q', '--nologo', '/clp:NoSummary', + '--property', 'PublishAot=true', '--packages', `${this.clrBuildDir}/aot`]; + + // .NET 7 doesn't support JitStdOutFile, so higher max output is needed for stdout + execOptions.maxOutput = 1024 * 1024 * 1024; + + const compilerResult = await super.runCompiler(compiler, publishOptions, inputFilename, execOptions); + if (compilerResult.code !== 0) { + return compilerResult; + } + + await fs.createFile(this.getOutputFilename(programDir, this.outputFilebase)); + + const version = '// ilc ' + toolVersion; + const output = await fs.readFile(jitOutFile); + + // .NET 7 doesn't support JitStdOutFile, so read from stdout + const outputString = output.length ? output.toString().split('\n') : compilerResult.stdout.map(o => o.text); + + await fs.writeFile( + this.getOutputFilename(programDir, this.outputFilebase), + `${version}\n\n${outputString.reduce((a, n) => `${a}\n${n}`, '')}`, + ); + + return compilerResult; + } + override async runCompiler( compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { - const crossgen2Options: string[] = []; - const configurableOptions = this.configurableOptions; + const corerunArgs: string[] = []; + // prettier-ignore + const toolOptions: string[] = [ + '--codegenopt', + this.sdkMajorVersion === 6 ? 'NgenDisasm=*' : 'JitDisasm=*', + '--codegenopt', + this.sdkMajorVersion < 8 ? 'JitDiffableDasm=1' : 'JitDisasmDiffable=1', + '--parallelism', '1', + ]; + const toolSwitches: string[] = []; const programDir = path.dirname(inputFilename); const programOutputPath = path.join(programDir, 'bin', this.buildConfig, this.targetFramework); const programDllPath = path.join(programOutputPath, 'CompilerExplorer.dll'); + // prettier-ignore + const envVarFileContents = [ + 'DOTNET_EnableWriteXorExecute=0', + 'DOTNET_JitDisasm=*', + 'DOTNET_JitDisasmAssemblies=CompilerExplorer', + 'DOTNET_TieredCompilation=0', + this.sdkMajorVersion < 8 ? 'DOTNET_JitDiffableDasm=1' : 'DOTNET_JitDisasmDiffable=1', + ]; + let isAot = false; + let isCrossgen2 = this.sdkMajorVersion === 6; - for (const configurableOption of configurableOptions) { - const optionIndex = options.indexOf(configurableOption); - if (optionIndex === -1 || optionIndex === options.length - 1) { + while (options.length > 0) { + const currentOption = options.shift(); + if (!currentOption) { continue; } - crossgen2Options.push(options[optionIndex], options[optionIndex + 1]); - } - const configurableSwitches = this.configurableSwitches; - for (const configurableSwitch of configurableSwitches) { - const switchIndex = options.indexOf(configurableSwitch); - if (switchIndex === -1) { - continue; + if (currentOption === '-e' || currentOption === '--env') { + const envVar = options.shift(); + if (envVar) { + const [name] = envVar.split('='); + const normalizedName = name.trim().toUpperCase(); + if ( + normalizedName === 'DOTNET_JITDISASM' || + normalizedName === 'DOTNET_JITDUMP' || + normalizedName === 'DOTNET_JITDISASMASSEMBILES' + ) { + continue; + } + envVarFileContents.push(envVar); + } + } else if (currentOption === '-p' || currentOption === '--property') { + const property = options.shift(); + if (property) { + corerunArgs.push('-p', property); + } + } else if (this.configurableSwitches.indexOf(currentOption) !== -1) { + if (currentOption === '--aot') { + isAot = true; + } else if (currentOption === '--crossgen2') { + isCrossgen2 = true; + } else { + toolSwitches.push(currentOption); + } + } else if (this.configurableOptions.indexOf(currentOption) !== -1) { + const value = options.shift(); + if (value) { + toolOptions.push(currentOption, value); + } } - crossgen2Options.push(options[switchIndex]); } this.setCompilerExecOptions(execOptions, programDir); - const compilerResult = await this.buildToDll(compiler, options, inputFilename, execOptions); - if (compilerResult.code !== 0) { - return compilerResult; - } + let compilerResult; - const crossgen2Result = await this.runCrossgen2( - execOptions, - this.clrBuildDir, - programDllPath, - crossgen2Options, - this.getOutputFilename(programDir, this.outputFilebase), - ); + if (isAot) { + if (!fs.existsSync(`${this.clrBuildDir}/aot`)) { + return { + code: -1, + timedOut: false, + didExecute: false, + stdout: [], + stderr: [{text: '--aot is not supported with this version.'}], + }; + } + + compilerResult = await this.publishAot(compiler, toolOptions, toolSwitches, inputFilename, execOptions); + } else { + compilerResult = await this.buildToDll(compiler, inputFilename, execOptions); + if (compilerResult.code !== 0) { + return compilerResult; + } - if (crossgen2Result.code !== 0) { - return crossgen2Result; + if (isCrossgen2) { + const crossgen2Result = await this.runCrossgen2( + execOptions, + this.clrBuildDir, + programDllPath, + toolOptions, + toolSwitches, + this.getOutputFilename(programDir, this.outputFilebase), + ); + + if (crossgen2Result.code !== 0) { + return crossgen2Result; + } + } else { + const envVarFilePath = path.join(programDir, '.env'); + await fs.writeFile(envVarFilePath, envVarFileContents.join('\n')); + + const corerunResult = await this.runCorerunForDisasm( + execOptions, + this.clrBuildDir, + envVarFilePath, + programDllPath, + corerunArgs, + this.getOutputFilename(programDir, this.outputFilebase), + ); + + if (corerunResult.code !== 0) { + return corerunResult; + } + } } return compilerResult; @@ -256,7 +429,7 @@ class DotNetCompiler extends BaseCompiler { ): Promise<BasicExecutionResult> { const programDir = path.dirname(executable); const programOutputPath = path.join(programDir, 'bin', this.buildConfig, this.targetFramework); - const programDllPath = path.join(programOutputPath, 'CompilerExplorer.dll'); + const programDllPath = path.join(programOutputPath, `${AssemblyName}.dll`); const execOptions = this.getDefaultExecOptions(); execOptions.maxOutput = maxSize; execOptions.timeoutMs = this.env.ceProps('binaryExecTimeoutMs', 2000); @@ -269,9 +442,8 @@ class DotNetCompiler extends BaseCompiler { execOptions.env.CORE_ROOT = this.clrBuildDir; execOptions.input = executeParameters.stdin; const execArgs = ['-p', 'System.Runtime.TieredCompilation=false', programDllPath, ...executeParameters.args]; - const corerun = path.join(this.clrBuildDir, 'corerun'); try { - const execResult: UnprocessedExecResult = await exec.sandbox(corerun, execArgs, execOptions); + const execResult: UnprocessedExecResult = await exec.sandbox(this.corerunPath, execArgs, execOptions); return this.processExecutionResult(execResult); } catch (err: UnprocessedExecResult | any) { if (err.code && err.stderr) { @@ -287,51 +459,70 @@ class DotNetCompiler extends BaseCompiler { } } - async ensureCrossgen2Version(execOptions: ExecutionOptions) { - if (!this.crossgen2VersionString) { - this.crossgen2VersionString = '// crossgen2 '; - + async checkRuntimeVersion() { + if (!this.versionString) { const versionFilePath = `${this.clrBuildDir}/version.txt`; - const versionResult = await this.exec(this.crossgen2Path, ['--version'], execOptions); - if (versionResult.code === 0) { - this.crossgen2VersionString += versionResult.stdout; - } else if (fs.existsSync(versionFilePath)) { + if (fs.existsSync(versionFilePath)) { const versionString = await fs.readFile(versionFilePath); - this.crossgen2VersionString += versionString; + this.versionString = versionString.toString(); } else { - this.crossgen2VersionString += '<unknown version>'; + this.versionString = '<unknown version>'; } } } + async runCorerunForDisasm( + execOptions: ExecutionOptions, + coreRoot: string, + envPath: string, + dllPath: string, + options: string[], + outputPath: string, + ) { + await this.checkRuntimeVersion(); + + const corerunOptions = ['--clr-path', coreRoot, '--env', envPath].concat([ + ...options, + this.disassemblyLoaderPath, + dllPath, + ]); + const compilerExecResult = await this.exec(this.corerunPath, corerunOptions, execOptions); + const result = this.transformToCompilationResult(compilerExecResult, dllPath); + + await fs.writeFile( + outputPath, + `// coreclr ${this.versionString}\n\n${result.stdout.map(o => o.text).reduce((a, n) => `${a}\n${n}`, '')}`, + ); + + return result; + } + async runCrossgen2( execOptions: ExecutionOptions, bclPath: string, dllPath: string, - options: string[], + toolOptions: string[], + toolSwitches: string[], outputPath: string, ) { - await this.ensureCrossgen2Version(execOptions); + await this.checkRuntimeVersion(); - /* eslint-disable */ + // prettier-ignore const crossgen2Options = [ '-r', path.join(bclPath, '/'), + '-r', this.disassemblyLoaderPath, dllPath, - '-o', 'CompilerExplorer.r2r.dll', - '--codegenopt', this.sdkMajorVersion < 7 ? 'NgenDisasm=*' : 'JitDisasm=*', - '--codegenopt', this.sdkMajorVersion < 8 ? 'JitDiffableDasm=1' : 'JitDisasmDiffable=1', - '--parallelism', '1', - '--inputbubble', - '--compilebubblegenerics', - ].concat(options); - /* eslint-enable */ + '-o', `${AssemblyName}.r2r.dll`, + ].concat(toolOptions).concat(toolSwitches); const compilerExecResult = await this.exec(this.crossgen2Path, crossgen2Options, execOptions); const result = this.transformToCompilationResult(compilerExecResult, dllPath); await fs.writeFile( outputPath, - `${this.crossgen2VersionString}\n\n${result.stdout.map(o => o.text).reduce((a, n) => `${a}\n${n}`, '')}`, + `// crossgen2 ${this.versionString}\n\n${result.stdout + .map(o => o.text) + .reduce((a, n) => `${a}\n${n}`, '')}`, ); return result; diff --git a/lib/compilers/edg.ts b/lib/compilers/edg.ts new file mode 100644 index 000000000..7d2d785aa --- /dev/null +++ b/lib/compilers/edg.ts @@ -0,0 +1,42 @@ +// Copyright (c) 2023, 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 type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; + +export class EDGCompiler extends BaseCompiler { + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + } + + static get key() { + return 'edg'; + } + + override getSharedLibraryPathsAsArguments() { + // EDG does not have an equivalent to -Wl,-rpath in its driver, return + // an empty list. + return []; + } +} diff --git a/lib/compilers/flang.ts b/lib/compilers/flang.ts index 17a1165d0..669715084 100644 --- a/lib/compilers/flang.ts +++ b/lib/compilers/flang.ts @@ -27,12 +27,17 @@ import path from 'path'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {FortranCompiler} from './fortran.js'; +import {FlangParser} from './argument-parsers.js'; export class FlangCompiler extends FortranCompiler { static override get key() { return 'flang'; } + protected override getArgumentParser(): any { + return FlangParser; + } + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { let options = ['-o', this.filename(outputFilename)]; if (this.compiler.intelAsm && filters.intel && !filters.binary) { diff --git a/lib/compilers/fortran.ts b/lib/compilers/fortran.ts index 5f13eb4ac..da2e9b53a 100644 --- a/lib/compilers/fortran.ts +++ b/lib/compilers/fortran.ts @@ -27,17 +27,26 @@ import path from 'path'; import type {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; import * as utils from '../utils.js'; +import {GccFortranParser} from './argument-parsers.js'; export class FortranCompiler extends BaseCompiler { static get key() { return 'fortran'; } + protected override getArgumentParser(): any { + return GccFortranParser; + } + + override getStdVerOverrideDescription(): string { + return 'Change the Fortran standard version of the compiler.'; + } + override async runCompiler( compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/gcccobol.ts b/lib/compilers/gcccobol.ts new file mode 100644 index 000000000..5c8664cb9 --- /dev/null +++ b/lib/compilers/gcccobol.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2023, 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 {GCCCompiler} from './gcc.js'; + +export class GCCCobolCompiler extends GCCCompiler { + static override get key() { + return 'gcccobol'; + } +} diff --git a/lib/compilers/gnucobol.ts b/lib/compilers/gnucobol.ts index 65582e8bd..04a2d537e 100644 --- a/lib/compilers/gnucobol.ts +++ b/lib/compilers/gnucobol.ts @@ -22,14 +22,11 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -import _ from 'underscore'; - import path from 'path'; -import type {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import type {ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; -import * as utils from '../utils.js'; import {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import {CompilationEnvironment} from '../compilation-env.js'; @@ -53,8 +50,12 @@ export class GnuCobolCompiler extends BaseCompiler { } override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { - let options = ['-o', this.filename(outputFilename), '-I', this.includeDir, '-L', this.libDir]; - if (!filters.binary) options = options.concat('-S'); + let options = ['-o', this.filename(outputFilename), '-I', this.includeDir, '-L', this.libDir, '-A', '-g']; + if (this.compiler.intelAsm && filters.intel && !filters.binary && !filters.binaryObject) { + options = options.concat(this.compiler.intelAsm.split(' ')); + } + if (!filters.binary && !filters.binaryObject) options = options.concat('-S'); + else if (filters.binaryObject) options = options.concat('-c'); else options = options.concat('-x'); return options; } @@ -63,7 +64,7 @@ export class GnuCobolCompiler extends BaseCompiler { return 'asm'; } - override getDefaultExecOptions(): ExecutionOptions { + override getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} { const result = super.getDefaultExecOptions(); result.env.COB_CONFIG_DIR = this.configDir; result.env.COB_COPY_DIR = this.copyDir; @@ -95,6 +96,26 @@ export class GnuCobolCompiler extends BaseCompiler { return objdumpResult; } + override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string { + let filename; + if (key && key.backendOptions && key.backendOptions.customOutputFilename) { + filename = key.backendOptions.customOutputFilename; + } else if (key.filters.binary) { + // note: interesting fact about gnucobol, if you name the outputfile output.s it will always output assembly + filename = outputFilebase; + } else if (key.filters.binaryObject) { + filename = `${outputFilebase}.o`; + } else { + filename = `${outputFilebase}.s`; + } + + if (dirPath) { + return path.join(dirPath, filename); + } else { + return filename; + } + } + override getExecutableFilename(dirPath: string, outputFilebase: string) { return path.join(dirPath, outputFilebase); } diff --git a/lib/compilers/golang.ts b/lib/compilers/golang.ts index 1914b3806..903278461 100644 --- a/lib/compilers/golang.ts +++ b/lib/compilers/golang.ts @@ -31,7 +31,7 @@ import {unwrap} from '../assert.js'; import {BaseCompiler} from '../base-compiler.js'; import * as utils from '../utils.js'; -import {ClangParser} from './argument-parsers.js'; +import {GolangParser} from './argument-parsers.js'; // Each arch has a list of jump instructions in // Go source src/cmd/asm/internal/arch. @@ -60,9 +60,20 @@ export class GolangCompiler extends BaseCompiler { constructor(compilerInfo: PreliminaryCompilerInfo, env) { super(compilerInfo, env); - const goroot = this.compilerProps<string | undefined>(`compiler.${this.compiler.id}.goroot`); - const goarch = this.compilerProps<string | undefined>(`compiler.${this.compiler.id}.goarch`); - const goos = this.compilerProps<string | undefined>(`compiler.${this.compiler.id}.goos`); + const group = this.compiler.group; + + const goroot = this.compilerProps<string | undefined>( + 'goroot', + this.compilerProps<string | undefined>(`group.${group}.goroot`), + ); + const goarch = this.compilerProps<string | undefined>( + 'goarch', + this.compilerProps<string | undefined>(`group.${group}.goarch`), + ); + const goos = this.compilerProps<string | undefined>( + 'goos', + this.compilerProps<string | undefined>(`group.${group}.goos`), + ); this.GOENV = {}; if (goroot) { @@ -215,7 +226,7 @@ export class GolangCompiler extends BaseCompiler { result.asm = this.convertNewGoL(out); result.stderr = []; result.stdout = utils.parseOutput(logging, result.inputFilename); - return Promise.all([result, '']); + return Promise.all([result, '', '']); } override getSharedLibraryPathsAsArguments() { @@ -245,13 +256,19 @@ export class GolangCompiler extends BaseCompiler { } override getDefaultExecOptions() { - return { + const options = { ...super.getDefaultExecOptions(), + }; + + options.env = { + ...options.env, ...this.GOENV, }; + + return options; } - override getArgumentParser() { - return ClangParser; + override getArgumentParser(): any { + return GolangParser; } } diff --git a/lib/compilers/haskell.ts b/lib/compilers/haskell.ts index 4446127e7..de5f054d8 100644 --- a/lib/compilers/haskell.ts +++ b/lib/compilers/haskell.ts @@ -28,7 +28,7 @@ import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; -import {ClangParser} from './argument-parsers.js'; +import {GHCParser} from './argument-parsers.js'; export class HaskellCompiler extends BaseCompiler { static get key() { @@ -76,7 +76,7 @@ export class HaskellCompiler extends BaseCompiler { return [libPathFlag + '.', ...this.getSharedLibraryPaths(libraries).map(path => libPathFlag + path)]; } - override getArgumentParser() { - return ClangParser; + override getArgumentParser(): any { + return GHCParser; } } diff --git a/lib/compilers/hlsl.ts b/lib/compilers/hlsl.ts index e7be44b58..7050355c1 100644 --- a/lib/compilers/hlsl.ts +++ b/lib/compilers/hlsl.ts @@ -26,8 +26,10 @@ import path from 'path'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; +import {SPIRVAsmParser} from '../parsers/asm-parser-spirv.js'; export class HLSLCompiler extends BaseCompiler { + protected spirvAsm: SPIRVAsmParser; static get key() { return 'hlsl'; } @@ -36,6 +38,7 @@ export class HLSLCompiler extends BaseCompiler { super(info, env); this.compiler.supportsIntel = false; + this.spirvAsm = new SPIRVAsmParser(this.compilerProps); } /* eslint-disable no-unused-vars */ @@ -71,4 +74,16 @@ export class HLSLCompiler extends BaseCompiler { override getIrOutputFilename(inputFilename: string) { return this.getOutputFilename(path.dirname(inputFilename), this.outputFilebase).replace('.s', '.dxil'); } + + override async processAsm(result, filters, options) { + if (this.isSpirv(result.asm)) { + return this.spirvAsm.processAsm(result.asm, filters); + } + + return super.processAsm(result, filters, options); + } + + isSpirv(code) { + return code.startsWith('; SPIR-V'); + } } diff --git a/lib/compilers/hook.ts b/lib/compilers/hook.ts index c731cc530..14eb27b8d 100644 --- a/lib/compilers/hook.ts +++ b/lib/compilers/hook.ts @@ -64,7 +64,7 @@ export class HookCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { const dirPath = path.dirname(inputFilename); const outputFilename = this.getOutputFilename(dirPath); @@ -72,10 +72,10 @@ export class HookCompiler extends BaseCompiler { return super.runCompiler(compiler, options, inputFilename, execOptions); } - override processAsm(result, filters, options) { + override async processAsm(result, filters, options) { // Ignoring `trim` filter because it is not supported by Hook. filters.trim = false; - const _result = super.processAsm(result, filters, options); + const _result = await super.processAsm(result, filters, options); const commentRegex = /^\s*;(.*)/; const instructionRegex = /^\s{2}(\d+)(.*)/; const asm = _result.asm; diff --git a/lib/compilers/ispc.ts b/lib/compilers/ispc.ts index bfd097abd..c44fdb20a 100644 --- a/lib/compilers/ispc.ts +++ b/lib/compilers/ispc.ts @@ -32,6 +32,7 @@ import {asSafeVer} from '../utils.js'; import {ISPCParser} from './argument-parsers.js'; import {unwrap} from '../assert.js'; +import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js'; export class ISPCCompiler extends BaseCompiler { static get key() { @@ -56,9 +57,20 @@ export class ISPCCompiler extends BaseCompiler { return options; } - override async generateIR(inputFilename: string, options: string[], filters: ParseFiltersAndOutputOptions) { - const newOptions = [...options, ...unwrap(this.compiler.irArg), '-o', this.getIrOutputFilename(inputFilename)]; - return super.generateIR(inputFilename, newOptions, filters); + override async generateIR( + inputFilename: string, + options: string[], + irOptions: LLVMIrBackendOptions, + produceCfg: boolean, + filters: ParseFiltersAndOutputOptions, + ) { + const newOptions = [ + ...options, + ...unwrap(this.compiler.irArg), + '-o', + this.getIrOutputFilename(inputFilename, filters), + ]; + return super.generateIR(inputFilename, newOptions, irOptions, produceCfg, filters); } override getArgumentParser() { diff --git a/lib/compilers/java.ts b/lib/compilers/java.ts index bfcc19e76..429e2f513 100644 --- a/lib/compilers/java.ts +++ b/lib/compilers/java.ts @@ -35,6 +35,7 @@ import {logger} from '../logger.js'; import * as utils from '../utils.js'; import {JavaParser} from './argument-parsers.js'; +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; export class JavaCompiler extends BaseCompiler { static get key() { @@ -48,7 +49,7 @@ export class JavaCompiler extends BaseCompiler { super( { // Default is to disable all "cosmetic" filters - disabledFilters: ['labels', 'directives', 'commentOnly', 'trim'], + disabledFilters: ['labels', 'directives', 'commentOnly', 'trim', 'debugCalls'], ...compilerInfo, }, env, @@ -70,6 +71,7 @@ export class JavaCompiler extends BaseCompiler { .filter(f => f.endsWith('.class')) .map(async classFile => { const args = [ + ...this.compiler.objdumperArgs, // Prints out disassembled code, i.e., the instructions that comprise the Java bytecodes, // for each of the methods in the class. '-c', @@ -127,7 +129,7 @@ export class JavaCompiler extends BaseCompiler { } override async handleInterpreting(key, executeParameters) { - const compileResult = await this.getOrBuildExecutable(key); + const compileResult = await this.getOrBuildExecutable(key, BypassCache.None); if (compileResult.code === 0) { executeParameters.args = [ '-Xss136K', // Reduce thread stack size @@ -236,7 +238,7 @@ export class JavaCompiler extends BaseCompiler { return this.filterUserOptionsWithArg(userOptions, oneArgForbiddenList); } - override processAsm(result) { + override async processAsm(result) { // Handle "error" documents. if (!result.asm.includes('\n') && result.asm[0] === '<') { return [{text: result.asm, source: null}]; @@ -349,7 +351,11 @@ export class JavaCompiler extends BaseCompiler { method.instructions[currentInstr].instrOffset !== instrOffset ) { if (currentSourceLine === -1) { - logger.error('Skipping over instruction even though currentSourceLine == -1'); + // TODO: Triage for #2986 + logger.error( + 'Skipping over instruction even though currentSourceLine == -1', + JSON.stringify(method.instructions.slice(0, currentInstr + 10)), + ); } else { // instructions without explicit line number get assigned the last explicit/same line number method.instructions[currentInstr].sourceLine = currentSourceLine; diff --git a/lib/compilers/julia.ts b/lib/compilers/julia.ts index c72a52b98..292fcd31d 100644 --- a/lib/compilers/julia.ts +++ b/lib/compilers/julia.ts @@ -58,7 +58,7 @@ export class JuliaCompiler extends BaseCompiler { return []; } - override processAsm(result, filters, options) { + override async processAsm(result, filters, options) { const lineRe = /^<(\d+) (\d+) ([^ ]+) ([^>]*)>$/; const bytecodeLines = result.asm.split('\n'); const bytecodeResult: ParsedAsmResultLine[] = []; @@ -108,7 +108,7 @@ export class JuliaCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/kotlin.ts b/lib/compilers/kotlin.ts index 0b5cce585..9b1bab103 100644 --- a/lib/compilers/kotlin.ts +++ b/lib/compilers/kotlin.ts @@ -22,6 +22,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; @@ -98,7 +99,7 @@ export class KotlinCompiler extends JavaCompiler { ...key, options: ['-include-runtime', '-d', 'example.jar'], }; - const compileResult = await this.getOrBuildExecutable(alteredKey); + const compileResult = await this.getOrBuildExecutable(alteredKey, BypassCache.None); executeParameters.args = [ '-Xss136K', // Reduce thread stack size '-XX:CICompilerCount=2', // Reduce JIT compilation threads. 2 is minimum diff --git a/lib/compilers/llvm-mos.ts b/lib/compilers/llvm-mos.ts index 473e93263..9ff68a784 100644 --- a/lib/compilers/llvm-mos.ts +++ b/lib/compilers/llvm-mos.ts @@ -90,6 +90,15 @@ export class LLVMMOSCompiler extends ClangCompiler { if (await utils.fileExists(nesFile)) { await this.addArtifactToResult(res, nesFile, ArtifactType.nesrom); } + } else if (this.compiler.exe.includes('c64')) { + let prgFile = outputFilename; + if (outputFilename.endsWith('.elf')) { + prgFile = outputFilename.substr(0, outputFilename.length - 4); + } + + if (await utils.fileExists(prgFile)) { + await this.addArtifactToResult(res, prgFile, ArtifactType.c64prg); + } } return res; diff --git a/lib/compilers/mlir.ts b/lib/compilers/mlir.ts index 51bd6da88..59d5d5439 100644 --- a/lib/compilers/mlir.ts +++ b/lib/compilers/mlir.ts @@ -48,6 +48,7 @@ export class MLIRCompiler extends BaseCompiler { 'directives', 'commentOnly', 'trim', + 'debugCalls', ], ...compilerInfo, }, @@ -78,4 +79,26 @@ export class MLIRCompiler extends BaseCompiler { ): any[] { return []; } + + override async processAsm(result, filters, options) { + // at some point maybe a custom parser can be written, for now just don't filter anything + return super.processAsm( + result, + { + labels: false, + binary: false, + commentOnly: false, + demangle: false, + optOutput: false, + directives: false, + dontMaskFilenames: false, + execute: false, + intel: false, + libraryCode: false, + trim: false, + debugCalls: false, + }, + options, + ); + } } diff --git a/lib/compilers/movfuscator.ts b/lib/compilers/movfuscator.ts new file mode 100644 index 000000000..3a0ddf69a --- /dev/null +++ b/lib/compilers/movfuscator.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2023, 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 {ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; +import * as exec from '../exec.js'; + +export class MovfuscatorCompiler extends BaseCompiler { + static get key() { + return 'movfuscator'; + } + + override isCfgCompiler(compilerVersion: string) { + return true; + } + + override async exec(filepath: string, args: string[], execOptions: ExecutionOptions) { + return await exec.execute(filepath, args, { + ...execOptions, + env: { + MOVBUILDDIR: '/opt/compiler-explorer/movfuscator-trunk/build', + ...execOptions.env, + }, + }); + } +} diff --git a/lib/compilers/mrustc.ts b/lib/compilers/mrustc.ts index 2e67ae600..5bb54825e 100644 --- a/lib/compilers/mrustc.ts +++ b/lib/compilers/mrustc.ts @@ -60,7 +60,7 @@ export class MrustcCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { // mrustc will always invoke a C compiler on its C output to create a final exec/object. // There's no easy way to disable this last step, so simply faking it with 'true' works. diff --git a/lib/compilers/nasm.ts b/lib/compilers/nasm.ts index 14b4d043e..f591ff362 100644 --- a/lib/compilers/nasm.ts +++ b/lib/compilers/nasm.ts @@ -22,6 +22,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {AssemblyCompiler} from './assembly.js'; @@ -38,6 +39,7 @@ export class NasmCompiler extends AssemblyCompiler { inputFilename: string, outputFilename: string, libraries, + overrides: ConfiguredOverrides, ) { let options = super.prepareArguments( userOptions, @@ -46,6 +48,7 @@ export class NasmCompiler extends AssemblyCompiler { inputFilename, outputFilename, libraries, + overrides, ); let fmode; diff --git a/lib/compilers/nvcc.ts b/lib/compilers/nvcc.ts index e293de18d..91b261d1c 100644 --- a/lib/compilers/nvcc.ts +++ b/lib/compilers/nvcc.ts @@ -36,6 +36,7 @@ import {SassAsmParser} from '../parsers/asm-parser-sass.js'; import {asSafeVer} from '../utils.js'; import {ClangParser} from './argument-parsers.js'; +import _ from 'underscore'; export class NvccCompiler extends BaseCompiler { static get key() { @@ -56,7 +57,7 @@ export class NvccCompiler extends BaseCompiler { // * would be nice to try and filter unused `.func`s from e.g. clang output override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) { - const opts = ['-o', this.filename(outputFilename), '-g', '-lineinfo']; + const opts = ['-o', this.filename(outputFilename), '-g', '-lineinfo', '--keep-device-functions']; if (!filters.execute) { opts.push('-c', '-keep', '-keep-dir', Path.dirname(outputFilename)); if (!filters.binary) { @@ -102,15 +103,34 @@ export class NvccCompiler extends BaseCompiler { override async postProcess(result, outputFilename: string, filters: ParseFiltersAndOutputOptions) { const maxSize = this.env.ceProps('max-asm-size', 64 * 1024 * 1024); const optPromise = result.hasOptOutput ? this.processOptOutput(result.optPath) : Promise.resolve(''); + const postProcess = _.compact(this.compiler.postProcess); const asmPromise = ( filters.binary ? this.objdump(outputFilename, {}, maxSize, filters.intel, filters.demangle, false, false, filters) - : fs.readFile(outputFilename, {encoding: 'utf8'}) + : (async () => { + if (result.asmSize === undefined) { + result.asm = '<No output file>'; + return result; + } + if (result.asmSize >= maxSize) { + result.asm = + '<No output: generated assembly was too large' + + ` (${result.asmSize} > ${maxSize} bytes)>`; + return result; + } + if (postProcess.length > 0) { + return await this.execPostProcess(result, postProcess, outputFilename, maxSize); + } else { + const contents = await fs.readFile(outputFilename, {encoding: 'utf8'}); + result.asm = contents.toString(); + return result; + } + })() ).then(asm => { result.asm = typeof asm === 'string' ? asm : asm.asm; return result; }); - return Promise.all([asmPromise, optPromise]); + return Promise.all([asmPromise, optPromise, '']); } override async extractDeviceCode(result, filters, compilationInfo: CompilationInfo) { diff --git a/lib/compilers/nvcpp.ts b/lib/compilers/nvcpp.ts new file mode 100644 index 000000000..b588c692b --- /dev/null +++ b/lib/compilers/nvcpp.ts @@ -0,0 +1,129 @@ +// Copyright (c) 2023, 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 * as fs from 'fs/promises'; +import path from 'path'; + +import {CompilationInfo} from '../../types/compilation/compilation.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; +import {SassAsmParser} from '../parsers/asm-parser-sass.js'; +import {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import {unwrap} from '../assert.js'; + +export class NvcppCompiler extends BaseCompiler { + protected deviceAsmParser: SassAsmParser; + protected cuobjdump: string | undefined; + + static get key() { + return 'nvcpp'; + } + + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + + this.cuobjdump = this.compilerProps<string | undefined>( + 'compiler.' + this.compiler.id + '.cuobjdump', + undefined, + ); + + this.deviceAsmParser = new SassAsmParser(this.compilerProps); + + this.compiler.supportsDeviceAsmView = true; + } + + async nvdisasm(result, outputFilename: string, maxOutput: number) { + const disasmResult = await this.exec(unwrap(this.compiler.nvdisasm), ['-c', '-g', '-hex', outputFilename], { + maxOutput, + customCwd: path.dirname(outputFilename), + }); + + const newResult = {asm: ''}; + + if (disasmResult.code === 0) { + newResult.asm = disasmResult.stdout; + } else { + newResult.asm = `<No output: ${path.basename(unwrap(this.compiler.nvdisasm))} returned ${ + disasmResult.code + }>`; + } + return newResult; + } + + async extractDeviceBinariesFromExecutable(result, compilationInfo: CompilationInfo) { + if (this.cuobjdump) { + const execOptions = this.getDefaultExecOptions(); + execOptions.customCwd = result.dirPath; + + if (!result.buildsteps) result.buildsteps = []; + + await this.exec(this.cuobjdump, ['-xelf', 'all', compilationInfo.executableFilename], execOptions); + + // couldn't test this, does this happen? + await this.exec(this.cuobjdump, ['-xptx', 'all', compilationInfo.executableFilename], execOptions); + + return true; + } + + return false; + } + + override async extractDeviceCode(result, filters, compilationInfo: CompilationInfo) { + const {dirPath} = result; + const {demangle} = filters; + const devices = {...result.devices}; + + if (filters.binary) { + await this.extractDeviceBinariesFromExecutable(result, compilationInfo); + } + + if (dirPath) { + const files = await fs.readdir(dirPath); + const maxSize = this.env.ceProps('max-asm-size', 64 * 1024 * 1024); + await Promise.all( + files + .filter(f => f.endsWith('.ptx') || f.endsWith('.cubin')) + .map(async name => { + const type = name.endsWith('.ptx') ? 'PTX' : 'SASS'; + const {asm} = + type === 'PTX' + ? {asm: await fs.readFile(path.join(dirPath, name), 'utf8')} + : await this.nvdisasm(result, path.join(dirPath, name), maxSize); + const archAndCode = name.split('.').slice(1, -1).join(', ') || ''; + const nameAndArch = type + (archAndCode ? ` (${archAndCode.toLowerCase()})` : ''); + Object.assign(devices, { + [nameAndArch]: await this.postProcessAsm( + { + okToCache: demangle, + ...this.deviceAsmParser.process(asm, {...filters, binary: type === 'SASS'}), + }, + {...filters, binary: type === 'SASS'}, + ), + }); + }), + ); + result.devices = devices; + } + return result; + } +} diff --git a/lib/compilers/osaca.ts b/lib/compilers/osaca.ts index 0ecff9e92..f8df4982a 100644 --- a/lib/compilers/osaca.ts +++ b/lib/compilers/osaca.ts @@ -59,6 +59,7 @@ export class OSACATool extends AnalysisTool { libraryCode: false, trim: false, binaryObject: false, + debugCalls: false, }; } diff --git a/lib/compilers/pascal-win.ts b/lib/compilers/pascal-win.ts index 00b4bcba8..e73d0335b 100644 --- a/lib/compilers/pascal-win.ts +++ b/lib/compilers/pascal-win.ts @@ -99,7 +99,7 @@ export class PascalWinCompiler extends BaseCompiler { outputFilename = this.getOutputFilename(path.dirname(outputFilename)); } - let args = ['-d', outputFilename]; + let args = [...this.compiler.objdumperArgs, '-d', outputFilename]; if (intelAsm) args = args.concat(['-M', 'intel']); return this.exec(this.compiler.objdumper, args, {maxOutput: 1024 * 1024 * 1024}).then(objResult => { if (objResult.code === 0) { @@ -151,7 +151,7 @@ export class PascalWinCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/pascal.ts b/lib/compilers/pascal.ts index 01b04b607..c91ba6a8b 100644 --- a/lib/compilers/pascal.ts +++ b/lib/compilers/pascal.ts @@ -62,7 +62,7 @@ export class FPCCompiler extends BaseCompiler { return []; } - override processAsm(result, filters) { + override async processAsm(result, filters) { // TODO: Pascal doesn't have a demangler exe, it's the only compiler that's weird like this this.demangler = new (unwrap(this.demanglerClass))(null as any, this); return this.asm.process(result.asm, filters); @@ -90,7 +90,7 @@ export class FPCCompiler extends BaseCompiler { options = options.concat(this.compiler.intelAsm.split(' ')); } - filters.preProcessLines = _.bind(this.preProcessLines, this); + filters.preProcessLines = this.preProcessLines.bind(this); if (filters.binary) { filters.dontMaskFilenames = true; @@ -186,7 +186,7 @@ export class FPCCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); @@ -198,7 +198,7 @@ export class FPCCompiler extends BaseCompiler { const projectFile = path.join(dirPath, this.dprFilename); execOptions.customCwd = dirPath; if (this.nasmPath) { - execOptions.env = _.clone(process.env); + execOptions.env = _.clone(process.env) as Record<string, string>; execOptions.env.PATH = execOptions.env.PATH + ':' + this.nasmPath; } diff --git a/lib/compilers/pony.ts b/lib/compilers/pony.ts index 081f84ebb..6906f58e1 100644 --- a/lib/compilers/pony.ts +++ b/lib/compilers/pony.ts @@ -30,6 +30,7 @@ import type {CompilationResult, ExecutionOptions} from '../../types/compilation/ import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; import {unwrap} from '../assert.js'; +import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js'; export class PonyCompiler extends BaseCompiler { static get key() { @@ -62,7 +63,13 @@ export class PonyCompiler extends BaseCompiler { return source; } - override async generateIR(inputFilename: string, options: string[], filters: ParseFiltersAndOutputOptions) { + override async generateIR( + inputFilename: string, + options: string[], + irOptions: LLVMIrBackendOptions, + produceCfg: boolean, + filters: ParseFiltersAndOutputOptions, + ) { const newOptions = _.filter(options, option => !['--pass', 'asm'].includes(option)).concat( unwrap(this.compiler.irArg), ); @@ -73,17 +80,21 @@ export class PonyCompiler extends BaseCompiler { const output = await this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions); if (output.code !== 0) { - return [{text: 'Failed to run compiler to get IR code'}]; + return { + asm: [{text: 'Failed to run compiler to get IR code'}], + }; } - const ir = await this.processIrOutput(output, filters); - return ir.asm; + const ir = await this.processIrOutput(output, irOptions, filters); + return { + asm: ir.asm, + }; } override async runCompiler( compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/ptxas.ts b/lib/compilers/ptxas.ts index 4073a8139..40157ded0 100644 --- a/lib/compilers/ptxas.ts +++ b/lib/compilers/ptxas.ts @@ -93,7 +93,7 @@ export class PtxAssembler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); @@ -121,7 +121,7 @@ export class PtxAssembler extends BaseCompiler { override async objdump(outputFilename, result: any, maxSize: number) { const dirPath = path.dirname(outputFilename); - const args = ['-c', '-g', '-hex', outputFilename]; + const args = [...this.compiler.objdumperArgs, '-c', '-g', '-hex', outputFilename]; const objResult = await this.exec(this.compiler.objdumper, args, {maxOutput: maxSize, customCwd: dirPath}); result.asm = objResult.stdout; if (objResult.code === 0) { diff --git a/lib/compilers/python.ts b/lib/compilers/python.ts index 6bca3ce4f..0d9cc8dde 100644 --- a/lib/compilers/python.ts +++ b/lib/compilers/python.ts @@ -46,7 +46,7 @@ export class PythonCompiler extends BaseCompiler { resolvePathFromAppRoot('etc', 'scripts', 'disasms', 'dis_all.py'); } - override processAsm(result) { + override async processAsm(result) { const lineRe = /^\s{0,4}(\d+)(.*)/; const bytecodeLines = result.asm.split('\n'); diff --git a/lib/compilers/racket.ts b/lib/compilers/racket.ts index 9c808dba7..0811058d1 100644 --- a/lib/compilers/racket.ts +++ b/lib/compilers/racket.ts @@ -41,7 +41,7 @@ export class RacketCompiler extends BaseCompiler { super( { // Disable output filters, as they currently don't do anything - disabledFilters: ['labels', 'directives', 'commentOnly', 'trim'], + disabledFilters: ['labels', 'directives', 'commentOnly', 'trim', 'debugCalls'], ...info, }, env, @@ -74,7 +74,7 @@ export class RacketCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { if (!execOptions) { execOptions = this.getDefaultExecOptions(); @@ -123,7 +123,7 @@ export class RacketCompiler extends BaseCompiler { return result; } - override processAsm(result: any, filters: any, options: any) { + override async processAsm(result: any, filters: any, options: any) { // TODO: Process and highlight decompiled output return { asm: [{text: result.asm}], diff --git a/lib/compilers/rga.ts b/lib/compilers/rga.ts index 21d839ccd..645dfc4bb 100644 --- a/lib/compilers/rga.ts +++ b/lib/compilers/rga.ts @@ -105,7 +105,7 @@ Please supply an ASIC from the following options:`, compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ): Promise<CompilationResult> { if (!execOptions) { execOptions = this.getDefaultExecOptions(); diff --git a/lib/compilers/ruby.ts b/lib/compilers/ruby.ts index f19cf1638..07886c3b8 100644 --- a/lib/compilers/ruby.ts +++ b/lib/compilers/ruby.ts @@ -48,7 +48,7 @@ export class RubyCompiler extends BaseCompiler { return 'asmruby'; } - override processAsm(result) { + override async processAsm(result) { const lineRe = /\(\s*(\d+)\)(?:\[[^\]]+])?$/; const fileRe = /ISeq:.*?@(.*?):(\d+) /; const baseFile = path.basename(this.compileFilename); diff --git a/lib/compilers/rust.ts b/lib/compilers/rust.ts index 09fba6952..98683f6d9 100644 --- a/lib/compilers/rust.ts +++ b/lib/compilers/rust.ts @@ -35,6 +35,8 @@ import type {BuildEnvDownloadInfo} from '../buildenvsetup/buildenv.interfaces.js import {parseRustOutput} from '../utils.js'; import {RustParser} from './argument-parsers.js'; +import {CompilerOverrideType} from '../../types/compilation/compiler-overrides.interfaces.js'; +import {SemVer} from 'semver'; export class RustCompiler extends BaseCompiler { linker: string; @@ -50,19 +52,60 @@ export class RustCompiler extends BaseCompiler { this.compiler.supportsLLVMOptPipelineView = true; this.compiler.supportsRustMirView = true; - const isNightly = info.name === 'nightly' || info.semver === 'nightly'; + const isNightly = this.isNightly(); // Macro expansion (-Zunpretty=expanded) and HIR (-Zunpretty=hir-tree) // are only available for Nightly this.compiler.supportsRustMacroExpView = isNightly; this.compiler.supportsRustHirView = isNightly; this.compiler.irArg = ['--emit', 'llvm-ir']; + this.compiler.minIrArgs = ['--emit=llvm-ir']; this.compiler.llvmOptArg = ['-C', 'llvm-args=-print-after-all -print-before-all']; this.compiler.llvmOptModuleScopeArg = ['-C', 'llvm-args=-print-module-scope']; this.compiler.llvmOptNoDiscardValueNamesArg = isNightly ? ['-Z', 'fewer-names=no'] : []; this.linker = this.compilerProps<string>('linker'); } + private isNightly() { + return ( + this.compiler.name === 'nightly' || + this.compiler.semver === 'nightly' || + this.compiler.semver === 'beta' || + this.compiler.semver.includes('master') || + this.compiler.semver.includes('trunk') + ); + } + + override async populatePossibleOverrides() { + const possibleEditions = await RustParser.getPossibleEditions(this); + if (possibleEditions.length > 0) { + let defaultEdition: undefined | string = undefined; + if (!this.compiler.semver || this.isNightly()) { + defaultEdition = '2021'; + } else { + const compilerVersion = new SemVer(this.compiler.semver); + if (compilerVersion.compare('1.56.0') >= 0) { + defaultEdition = '2021'; + } + } + + this.compiler.possibleOverrides?.push({ + name: CompilerOverrideType.edition, + display_title: 'Edition', + description: + 'The default edition for Rust compilers is usually 2015. ' + + 'Some editions might not be available for older compilers.', + flags: ['--edition', '<value>'], + values: possibleEditions.map(ed => { + return {name: ed, value: ed}; + }), + default: defaultEdition, + }); + } + + await super.populatePossibleOverrides(); + } + override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) { return []; } diff --git a/lib/compilers/snowball.ts b/lib/compilers/snowball.ts new file mode 100644 index 000000000..613e69276 --- /dev/null +++ b/lib/compilers/snowball.ts @@ -0,0 +1,95 @@ +// Copyright (c) 2023, 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 _ from 'underscore'; + +import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {unwrap} from '../assert.js'; +import {BaseCompiler} from '../base-compiler.js'; + +export class SnowballCompiler extends BaseCompiler { + linker: string; + + static get key() { + return 'snowball'; + } + + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + this.compiler.supportsIntel = false; + this.compiler.supportsIrView = true; + this.compiler.supportsLLVMOptPipelineView = true; + this.compiler.supportsCfg = true; + + this.compiler.irArg = ['--emit', 'llvm-ir']; + this.linker = this.compilerProps<string>('linker'); + } + + override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) { + return []; + } + + override getSharedLibraryLinks(libraries: any[]): string[] { + return []; + } + + override orderArguments( + options: string[], + inputFilename: string, + libIncludes: string[], + libOptions: string[], + libPaths: string[], + libLinks: string[], + userOptions: string[], + staticLibLinks: string[], + ) { + return options.concat(userOptions, libIncludes, libOptions, libPaths, libLinks, staticLibLinks, [ + '-f', + this.filename(inputFilename), + ]); + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) { + let options = ['build', '--silent', '-o', this.filename(outputFilename)]; + + const userRequestedEmit = _.any(unwrap(userOptions), opt => opt.includes('--emit')); + if (filters.binary) { + options = options.concat(['--emit', 'exec']); + } else if (filters.binaryObject) { + options = options.concat(['--emit', 'lib']); + } else { + if (!userRequestedEmit) { + options = options.concat('--emit', 'asm'); + } + // TODO: + // if (filters.intel) options = options.concat('--llvm-args', '--x86-asm-syntax=intel'); + } + return options; + } + + override isCfgCompiler(/*compilerVersion*/) { + return true; + } +} diff --git a/lib/compilers/solidity.ts b/lib/compilers/solidity.ts index fee0f4afe..13812b052 100644 --- a/lib/compilers/solidity.ts +++ b/lib/compilers/solidity.ts @@ -65,7 +65,7 @@ export class SolidityCompiler extends BaseCompiler { return path.join(dirPath, 'contracts/combined.json'); } - override processAsm(result) { + override async processAsm(result) { // Handle "error" documents. if (!result.asm.includes('\n') && result.asm[0] === '<') { return {asm: [{text: result.asm}]}; @@ -235,7 +235,7 @@ export class SolidityCompiler extends BaseCompiler { processPossibleTagOpcode(opcode, contractFunctions); } else { - processPossibleTagOpcode(opcode, generatedSources[opcode.source]); + processPossibleTagOpcode(opcode, generatedSources[opcode.source] || []); } } diff --git a/lib/compilers/spirv.ts b/lib/compilers/spirv.ts index 60ce5d338..a709a43f8 100644 --- a/lib/compilers/spirv.ts +++ b/lib/compilers/spirv.ts @@ -34,6 +34,8 @@ import {logger} from '../logger.js'; import {SPIRVAsmParser} from '../parsers/asm-parser-spirv.js'; import * as utils from '../utils.js'; import {unwrap} from '../assert.js'; +import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js'; +import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js'; export class SPIRVCompiler extends BaseCompiler { protected translatorPath: string; @@ -59,6 +61,7 @@ export class SPIRVCompiler extends BaseCompiler { inputFilename: string, outputFilename: string, libraries, + overrides: ConfiguredOverrides, ) { let options = this.optionsForFilter(filters, outputFilename); backendOptions = backendOptions || {}; @@ -118,7 +121,7 @@ export class SPIRVCompiler extends BaseCompiler { compiler: string, options: string[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { const sourceDir = path.dirname(inputFilename); const bitcodeFilename = path.join(sourceDir, this.outputFilebase + '.bc'); @@ -166,7 +169,7 @@ export class SPIRVCompiler extends BaseCompiler { compiler: string, options: any[], inputFilename: string, - execOptions: ExecutionOptions, + execOptions: ExecutionOptions & {env: Record<string, string>}, ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); @@ -199,7 +202,13 @@ export class SPIRVCompiler extends BaseCompiler { ); } - override async generateIR(inputFilename: string, options: string[], filters: ParseFiltersAndOutputOptions) { + override async generateIR( + inputFilename: string, + options: string[], + irOptions: LLVMIrBackendOptions, + produceCfg: boolean, + filters: ParseFiltersAndOutputOptions, + ) { const newOptions = _.filter(options, option => option !== '-fcolor-diagnostics').concat('-emit-llvm'); const execOptions = this.getDefaultExecOptions(); @@ -213,9 +222,13 @@ export class SPIRVCompiler extends BaseCompiler { ); if (output.code !== 0) { logger.error('Failed to run compiler to get IR code'); - return output.stderr; + return { + asm: output.stderr, + }; } - const ir = await this.processIrOutput(output, filters); - return ir.asm; + const ir = await this.processIrOutput(output, irOptions, filters); + return { + asm: ir.asm, + }; } } diff --git a/lib/compilers/swift.ts b/lib/compilers/swift.ts index caa07237e..6b4406aeb 100644 --- a/lib/compilers/swift.ts +++ b/lib/compilers/swift.ts @@ -24,19 +24,29 @@ import {BaseCompiler} from '../base-compiler.js'; -import {ClangParser} from './argument-parsers.js'; +import {SwiftParser} from './argument-parsers.js'; + +import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; export class SwiftCompiler extends BaseCompiler { static get key() { return 'swift'; } + constructor(info: PreliminaryCompilerInfo, env) { + super(info, env); + this.compiler.supportsLLVMOptPipelineView = true; + this.compiler.llvmOptArg = ['-Xllvm', '-print-after-all', '-Xllvm', '-print-before-all']; + this.compiler.llvmOptModuleScopeArg = ['-Xllvm', '-print-module-scope']; + this.compiler.llvmOptNoDiscardValueNamesArg = []; + } + override getSharedLibraryPathsAsArguments() { return []; } override getArgumentParser() { - return ClangParser; + return SwiftParser; } override isCfgCompiler(/*compilerVersion*/) { diff --git a/lib/compilers/tendra.ts b/lib/compilers/tendra.ts index 47323d3f2..3bfad871d 100644 --- a/lib/compilers/tendra.ts +++ b/lib/compilers/tendra.ts @@ -23,6 +23,7 @@ // POSSIBILITY OF SUCH DAMAGE. import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {TendraParser} from './argument-parsers.js'; import {GCCCompiler} from './gcc.js'; @@ -36,4 +37,8 @@ export class TenDRACompiler extends GCCCompiler { if (!filters.binary) options = options.concat('-S'); return options; } + + protected override getArgumentParser(): any { + return TendraParser; + } } diff --git a/lib/compilers/tic2000.ts b/lib/compilers/tic2000.ts new file mode 100644 index 000000000..864881511 --- /dev/null +++ b/lib/compilers/tic2000.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2023, 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 path from 'path'; + +import {BaseCompiler} from '../base-compiler.js'; +import {TiC2000AsmParser} from '../parsers/asm-parser-tic2000.js'; + +export class TIC2000 extends BaseCompiler { + static get key() { + return 'tic2000'; + } + + constructor(info, env) { + super(info, env); + this.outputFilebase = this.compileFilename.split('.')[0]; + this.asm = new TiC2000AsmParser(this.compilerProps); + } + + override optionsForFilter(filters, outputFilename) { + const options = ['-g', '-c', '-n', '--output_file=' + outputFilename]; + + filters.preProcessLines = this.preProcessLines.bind(this); + + return options; + } + + override getOutputFilename(dirPath, outputFilebase) { + return path.join(dirPath, `${outputFilebase}.asm`); + } + + preProcessLines(asmLines) { + let i = 0; + + while (i < asmLines.length) { + // Regex for determining the file line and column of the following source lines + const match = asmLines[i].match(/^\s*\.dwpsn\s+file\s+(".*"),line\s+(\d+),column\s+(\d+)/); + i++; + if (match) { + // Add two lines stating the file and location to allow parsing the source location by the standard + // parser + asmLines.splice(i, 0, ' .file 1 ' + match[1], ' .loc 1 ' + match[2] + ' ' + match[3]); + i += 2; + } + } + + return asmLines; + } +} diff --git a/lib/compilers/turboc.ts b/lib/compilers/turboc.ts index 93df469f3..e09fed67b 100644 --- a/lib/compilers/turboc.ts +++ b/lib/compilers/turboc.ts @@ -53,12 +53,12 @@ export class TurboCCompiler extends DosboxCompiler { return {stdout: [this.compiler.explicitVersion], stderr: [], code: 0}; } const execOptions = this.getDefaultExecOptions(); - const versionFlag = ''; + const versionFlag = []; execOptions.timeoutMs = 0; execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]); try { - return this.execCompilerCached(this.compiler.exe, [versionFlag], execOptions); + return this.execCompilerCached(this.compiler.exe, versionFlag, execOptions); } catch (err) { logger.error(`Unable to get version for compiler '${this.compiler.exe}' - ${err}`); return null; diff --git a/lib/compilers/typescript-native.ts b/lib/compilers/typescript-native.ts index 32017f66b..11790ce50 100644 --- a/lib/compilers/typescript-native.ts +++ b/lib/compilers/typescript-native.ts @@ -22,10 +22,14 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -import type {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import Semver from 'semver'; +import path from 'path'; + import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; +import {asSafeVer} from '../utils.js'; import {TypeScriptNativeParser} from './argument-parsers.js'; @@ -36,98 +40,90 @@ export class TypeScriptNativeCompiler extends BaseCompiler { tscJit: string; tscSharedLib: string; + tscNewOutput: boolean; + tscAsmOutput: boolean; constructor(compilerInfo: PreliminaryCompilerInfo, env) { super(compilerInfo, env); - this.compiler.supportsIntel = false; - this.compiler.supportsIrView = true; - - this.tscJit = this.compilerProps<string>(`compiler.${this.compiler.id}.exe`); + this.tscJit = this.compiler.exe; this.tscSharedLib = this.compilerProps<string>(`compiler.${this.compiler.id}.sharedlibs`); + this.tscNewOutput = Semver.gt(asSafeVer(this.compiler.semver || '0.0.0'), '0.0.32', true); + this.tscAsmOutput = Semver.gt(asSafeVer(this.compiler.semver || '0.0.0'), '0.0.34', true); + + this.compiler.irArg = ['--emit=llvm']; + this.compiler.supportsIntel = this.tscAsmOutput; + this.compiler.supportsIrView = true; } override getSharedLibraryPathsAsArguments() { return []; } - override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { - return [this.filename(outputFilename)]; + override optionsForFilter( + filters: ParseFiltersAndOutputOptions, + outputFilename: string, + userOptions?: string[], + ): string[] { + return []; } - override async handleInterpreting(key, executeParameters) { - executeParameters.args = [ - '--emit=jit', - this.tscSharedLib ? '--shared-libs=' + this.tscSharedLib : '-nogc', - ...executeParameters.args, - ]; - - return await super.handleInterpreting(key, executeParameters); - } + override optionsForBackend(backendOptions: Record<string, any>, outputFilename: string): string[] { + const addOpts: string[] = []; - override async runCompiler( - compiler: string, - options: string[], - inputFilename: string, - execOptions: ExecutionOptions, - ): Promise<CompilationResult> { - // These options make Clang produce an IR - const newOptions = ['--emit=mlir-llvm', inputFilename]; + addOpts.push(this.tscAsmOutput ? '--emit=asm' : '--emit=mlir'); if (!this.tscSharedLib) { - newOptions.push('-nogc'); + addOpts.push('-nogc'); } - const output = await this.runCompilerRawOutput( - this.tscJit, - newOptions, - this.filename(inputFilename), - execOptions, - ); - if (output.code !== 0) { - return { - code: output.code, - timedOut: false, - stdout: [], - stderr: [ - { - text: 'Failed to run compiler to get MLIR code', - }, - ], - }; + if (this.tscNewOutput) { + addOpts.push('-o=' + this.filename(outputFilename)); } - return {code: 0, timedOut: false, stdout: [], stderr: []}; + return addOpts; } - override async generateIR(inputFilename: string, options: string[], filters: ParseFiltersAndOutputOptions) { - // These options make Clang produce an IR - const newOptions = ['--emit=llvm', inputFilename]; + override getIrOutputFilename(inputFilename: string, filters: ParseFiltersAndOutputOptions): string { + const outputFilename = this.getOutputFilename(path.dirname(inputFilename), this.outputFilebase); + // As per #4054, if we are asked for binary mode, the output will be in the .s file, no .ll will be emited + if (!filters.binary) { + return outputFilename.replace('.s', '.ll'); + } + return outputFilename; + } - if (!this.tscSharedLib) { - newOptions.push('-nogc'); + override async generateIR( + inputFilename: string, + options: string[], + irOptions: LLVMIrBackendOptions, + produceCfg: boolean, + filters: ParseFiltersAndOutputOptions, + ) { + const newOptions = [...options.filter(e => !e.startsWith('--emit=') && !e.startsWith('-o='))]; + if (this.tscNewOutput) { + newOptions.push('-o=' + this.getIrOutputFilename(inputFilename, filters)); } - const execOptions = this.getDefaultExecOptions(); - // TODO: maybe this isn't needed? - execOptions.maxOutput = 1024 * 1024 * 1024; - - const output = await this.runCompilerRawOutput( - this.tscJit, - newOptions, - this.filename(inputFilename), - execOptions, - ); - if (output.code !== 0) { - return [{text: 'Failed to run compiler to get IR code'}]; + return await super.generateIR(inputFilename, newOptions, irOptions, produceCfg, filters); + } + + override async processIrOutput(output, irOptions: LLVMIrBackendOptions, filters: ParseFiltersAndOutputOptions) { + if (this.tscNewOutput) { + return await super.processIrOutput(output, irOptions, filters); } - filters.commentOnly = false; - filters.libraryCode = true; - filters.directives = true; + return this.llvmIr.process(output.stderr.map(l => l.text).join('\n'), irOptions); + } + + override async handleInterpreting(key, executeParameters) { + executeParameters.args = [ + '--emit=jit', + this.tscSharedLib ? '--shared-libs=' + this.tscSharedLib : '-nogc', + ...executeParameters.args, + ]; - const ir = await this.llvmIr.process(output.stderr, filters); - return ir.asm; + return await super.handleInterpreting(key, executeParameters); } override isCfgCompiler() { diff --git a/lib/compilers/v.ts b/lib/compilers/v.ts new file mode 100644 index 000000000..d5b5abf10 --- /dev/null +++ b/lib/compilers/v.ts @@ -0,0 +1,230 @@ +// Copyright (c) 2023, 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 path from 'path'; + +import {unwrap} from '../assert.js'; +import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; +import {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; + +const V_DEFAULT_BACKEND = 'c'; + +export class VCompiler extends BaseCompiler { + outputFileExt = `.${V_DEFAULT_BACKEND}`; + + static get key() { + return 'v'; + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) { + const options = unwrap(userOptions); + if (options) { + if (options.includes('-h') || options.includes('--help')) { + return []; + } + + const backend = this.getBackendFromOptions(options); + const outputFileExt = this.getFileExtForBackend(backend); + if (outputFileExt !== undefined) { + this.outputFileExt = outputFileExt; + } + } + + const compilerOptions: string[] = []; + if (!filters.binary) { + compilerOptions.push('-o'); + compilerOptions.push(this.filename(this.patchOutputFilename(outputFilename))); + } + + if (!filters.labels) { + compilerOptions.push('-skip-unused'); + } + + return compilerOptions; + } + + override async processAsm(result: any, filters, options: string[]): Promise<any> { + const backend = this.getBackendFromOptions(options); + switch (backend) { + case 'c': + case 'js': + case 'js_node': + case 'js_browser': + case 'js_freestanding': + case 'go': + return this.processCLike(result, filters); + default: + return this.asm.process(result.asm, filters); + } + } + + override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) { + return []; + } + + override getSharedLibraryLinks(libraries: any[]): string[] { + return []; + } + + override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string { + return path.join(dirPath, 'output' + this.outputFileExt); + } + + override async runCompiler( + compiler: string, + options: string[], + inputFilename: string, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ): Promise<CompilationResult> { + if (!execOptions) { + execOptions = super.getDefaultExecOptions(); + } + + const tmpDir = path.dirname(inputFilename); + execOptions.env['VMODULES'] = path.join(tmpDir, '.vmodules'); + execOptions.env['VTMP'] = tmpDir; + + if (!execOptions.customCwd) { + execOptions.customCwd = tmpDir; + } + + const result = await this.exec(compiler, options, execOptions); + return { + ...this.transformToCompilationResult(result, inputFilename), + languageId: this.getCompilerResultLanguageId(), + }; + } + + getBackendFromOptions(options: string[]): string { + const backendOpt = options.indexOf('-b'); + if (backendOpt >= 0 && options[backendOpt + 1]) return options[backendOpt + 1].toLowerCase(); + if (options.includes('-native')) return 'native'; + if (options.includes('-interpret')) return 'interpret'; + + return V_DEFAULT_BACKEND; // default V backend + } + + getFileExtForBackend(backend: string): string | undefined { + switch (backend) { + case 'c': + case 'go': + case 'wasm': + return '.' + backend; + case 'js': + case 'js_node': + case 'js_browser': + case 'js_freestanding': + return '.js'; + case 'native': + return ''; + default: + return undefined; + } + } + + patchOutputFilename(outputFilename: string): string { + const parts = outputFilename.split('.'); + + if (this.outputFileExt === '') { + parts.pop(); + return parts.join('.'); + } + + parts[parts.length - 1] = this.outputFileExt.split('.')[1]; + return parts.join('.'); + } + + removeUnusedLabels(input: string[]): string[] { + const output: string[] = []; + + const lineRe = /^.*main__.*$/; + const mainFunctionCall = '\tmain__main();'; + + let scopeDepth = 0; + let insertNewLine = false; + + for (const lineNo in input) { + const line = input[lineNo]; + if (!line) continue; + + if (insertNewLine) { + output.push(''); + insertNewLine = false; + } + + if ((scopeDepth === 0 && line.match(lineRe) && line !== mainFunctionCall) || scopeDepth > 0) { + const opening = (line.match(/{/g) || []).length - 1; + const closing = (line.match(/}/g) || []).length - 1; + scopeDepth += opening - closing; + + output.push(line); + + insertNewLine = scopeDepth === 0; + } + } + + return output; + } + + removeWhitespaceLines = (input: string[]) => input.map(line => line.trimStart()).filter(line => line !== ''); + removeComments = (input: string[]) => + input + .filter(line => !line.trimStart().startsWith('//')) + .map(line => line.split('//')[0].replaceAll(/(\/\*).*?(\*\/)/g, '')); + removeDirectives = (input: string[]) => input.filter(line => !line.trimStart().startsWith('#')); + + async processCLike(result, filters): Promise<any> { + let lines = result.asm.split('\n'); + + // remove non-user defined code + if (!filters.labels) lines = this.removeUnusedLabels(lines); + + // remove comments + if (!filters.commentOnly) lines = this.removeComments(lines); + + // remove whitespace + if (filters.trim) lines = this.removeWhitespaceLines(lines); + + // remove preprocessor directives + if (!filters.directives) lines = this.removeDirectives(lines); + + // finally, remove unnecessary newlines to make the output nicer + const finalLines: string[] = []; + let emptyLineEncountered = false; + + for (const lineNo in lines) { + const line = lines[lineNo]; + + if (line.trimStart() === '') { + if (emptyLineEncountered) continue; + emptyLineEncountered = true; + } else emptyLineEncountered = false; + + finalLines.push(line); + } + + return {asm: finalLines.map(line => ({text: line}))}; + } +} diff --git a/lib/compilers/v8.ts b/lib/compilers/v8.ts new file mode 100644 index 000000000..c5b1a832c --- /dev/null +++ b/lib/compilers/v8.ts @@ -0,0 +1,63 @@ +// Copyright (c) 2023, 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 path from 'path'; +import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; + +export class V8Compiler extends BaseCompiler { + static get key() { + return 'v8'; + } + + constructor(compilerInfo: PreliminaryCompilerInfo, env) { + super(compilerInfo, env); + this.compiler.demangler = ''; + this.demanglerClass = null; + } + + override getIrOutputFilename(inputFilename: string): string { + return this.filename(path.dirname(inputFilename) + '/code.asm'); + } + + public override getOutputFilename(dirPath: string, outputFilebase: string, key?: any) { + let filename; + if (key && key.backendOptions && key.backendOptions.customOutputFilename) { + filename = key.backendOptions.customOutputFilename; + } else { + filename = 'code.asm'; + } + + if (dirPath) { + return path.join(dirPath, filename); + } else { + return filename; + } + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string) { + return []; + } +} diff --git a/lib/compilers/vala.ts b/lib/compilers/vala.ts new file mode 100644 index 000000000..d36975dec --- /dev/null +++ b/lib/compilers/vala.ts @@ -0,0 +1,109 @@ +// Copyright (c) 2023, 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 path from 'path'; + +import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; +import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import {BaseCompiler} from '../base-compiler.js'; + +export class ValaCompiler extends BaseCompiler { + static get key() { + return 'vala'; + } + + ccPath: string; + pkgconfigPath: string; + + constructor(compiler: PreliminaryCompilerInfo, env) { + super(compiler, env); + this.ccPath = this.compilerProps<string>(`compiler.${this.compiler.id}.cc`); + this.pkgconfigPath = this.compilerProps<string>(`compiler.${this.compiler.id}.pkgconfigpath`); + } + + override getCompilerResultLanguageId() { + return 'c'; + } + + override getDefaultExecOptions() { + const execOptions = super.getDefaultExecOptions(); + if (this.ccPath) { + execOptions.env.CC = this.ccPath; + } + + if (this.pkgconfigPath) { + execOptions.env.PKG_CONFIG_PATH = this.pkgconfigPath; + } + + return execOptions; + } + + override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: any) { + const options = ['-g']; + if (!filters.binary) { + options.push('-C'); // Save transpiled C code + } else { + options.push('-o', this.filename(outputFilename)); + } + + return options; + } + + override async objdump( + outputFilename, + result: any, + maxSize: number, + intelAsm, + demangle, + staticReloc: boolean, + dynamicReloc: boolean, + filters: ParseFiltersAndOutputOptions, + ) { + const objdumpResult = await super.objdump( + outputFilename, + result, + maxSize, + intelAsm, + demangle, + staticReloc, + dynamicReloc, + filters, + ); + + objdumpResult.languageId = 'asm'; + return objdumpResult; + } + + override getSharedLibraryPathsAsArguments(libraries, libDownloadPath) { + return []; + } + + override getSharedLibraryLinks(libraries: any[]): string[] { + return []; + } + + override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string { + return path.join(dirPath, 'example.c'); + } +} diff --git a/lib/compilers/win32-mingw-clang.ts b/lib/compilers/win32-mingw-clang.ts new file mode 100644 index 000000000..6a1990a12 --- /dev/null +++ b/lib/compilers/win32-mingw-clang.ts @@ -0,0 +1,114 @@ +// Copyright (c) 2023, 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 path from 'path'; + +import { + BuildResult, + BypassCache, + CompilationResult, + ExecutionOptions, +} from '../../types/compilation/compilation.interfaces.js'; +import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; + +import {copyNeededDlls} from '../win-utils.js'; + +import {ClangCompiler} from './clang.js'; + +export class Win32MingWClang extends ClangCompiler { + static override get key() { + return 'win32-mingw-clang'; + } + + getExtraPaths(): string[] { + return [path.normalize(path.dirname(this.compiler.exe))]; + } + + override optionsForFilter( + filters: ParseFiltersAndOutputOptions, + outputFilename: string, + userOptions?: string[], + ): string[] { + if (filters.binary) { + filters.dontMaskFilenames = true; + } + + return super.optionsForFilter(filters, outputFilename, userOptions); + } + + override orderArguments( + options: string[], + inputFilename: string, + libIncludes: string[], + libOptions: string[], + libPaths: string[], + libLinks: string[], + userOptions: string[], + staticLibLinks: string[], + ) { + const newUserOptions = userOptions.filter(opt => !opt.startsWith('-l')); + const newLinkOptions = userOptions.filter(opt => opt.startsWith('-l')); + + return options.concat( + newUserOptions, + [this.filename(inputFilename)], + libIncludes, + libOptions, + libPaths, + newLinkOptions, + libLinks, + staticLibLinks, + ); + } + + override getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} { + const options = super.getDefaultExecOptions(); + if (!options.env) options.env = {}; + if (!options.env.PATH) options.env.PATH = ''; + options.env.PATH = this.getExtraPaths().join(path.delimiter); + + return options; + } + + override async buildExecutableInFolder(key, dirPath: string): Promise<BuildResult> { + const result = await super.buildExecutableInFolder(key, dirPath); + + if (result.code === 0) { + await copyNeededDlls( + dirPath, + result.executableFilename, + this.exec, + this.compiler.objdumper, + this.getDefaultExecOptions(), + ); + } + + return result; + } + + override async handleExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { + const execOptions = this.getDefaultExecOptions(); + return super.handleExecution(key, {...executeParameters, env: execOptions.env}, bypassCache); + } +} diff --git a/lib/compilers/win32-mingw-gcc.ts b/lib/compilers/win32-mingw-gcc.ts new file mode 100644 index 000000000..715b1e0c1 --- /dev/null +++ b/lib/compilers/win32-mingw-gcc.ts @@ -0,0 +1,114 @@ +// Copyright (c) 2023, 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 path from 'path'; + +import { + BuildResult, + BypassCache, + CompilationResult, + ExecutionOptions, +} from '../../types/compilation/compilation.interfaces.js'; +import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; + +import {copyNeededDlls} from '../win-utils.js'; + +import {GCCCompiler} from './gcc.js'; + +export class Win32MingWGcc extends GCCCompiler { + static override get key() { + return 'win32-mingw-gcc'; + } + + getExtraPaths(): string[] { + return [path.normalize(path.dirname(this.compiler.exe))]; + } + + override optionsForFilter( + filters: ParseFiltersAndOutputOptions, + outputFilename: string, + userOptions?: string[], + ): string[] { + if (filters.binary) { + filters.dontMaskFilenames = true; + } + + return super.optionsForFilter(filters, outputFilename, userOptions); + } + + override orderArguments( + options: string[], + inputFilename: string, + libIncludes: string[], + libOptions: string[], + libPaths: string[], + libLinks: string[], + userOptions: string[], + staticLibLinks: string[], + ) { + const newUserOptions = userOptions.filter(opt => !opt.startsWith('-l')); + const newLinkOptions = userOptions.filter(opt => opt.startsWith('-l')); + + return options.concat( + newUserOptions, + [this.filename(inputFilename)], + libIncludes, + libOptions, + libPaths, + newLinkOptions, + libLinks, + staticLibLinks, + ); + } + + override getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} { + const options = super.getDefaultExecOptions(); + if (!options.env) options.env = {}; + if (!options.env.PATH) options.env.PATH = ''; + options.env.PATH = this.getExtraPaths().join(path.delimiter); + + return options; + } + + override async buildExecutableInFolder(key, dirPath: string): Promise<BuildResult> { + const result = await super.buildExecutableInFolder(key, dirPath); + + if (result.code === 0) { + await copyNeededDlls( + dirPath, + result.executableFilename, + this.exec, + this.compiler.objdumper, + this.getDefaultExecOptions(), + ); + } + + return result; + } + + override async handleExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { + const execOptions = this.getDefaultExecOptions(); + return super.handleExecution(key, {...executeParameters, env: execOptions.env}, bypassCache); + } +} diff --git a/lib/compilers/win32.ts b/lib/compilers/win32.ts index 3466ff392..8f2cff314 100644 --- a/lib/compilers/win32.ts +++ b/lib/compilers/win32.ts @@ -36,6 +36,7 @@ import {AsmParser} from '../parsers/asm-parser.js'; import {PELabelReconstructor} from '../pe32-support.js'; import * as utils from '../utils.js'; import {unwrap} from '../assert.js'; +import type {ConfiguredOverrides} from '../../types/compilation/compiler-overrides.interfaces.js'; export class Win32Compiler extends BaseCompiler { static get key() { @@ -50,6 +51,10 @@ export class Win32Compiler extends BaseCompiler { this.binaryAsmParser = new AsmParser(this.compilerProps); } + override getStdverFlags(): string[] { + return ['/std:<value>']; + } + override newTempDir() { return new Promise<string>((resolve, reject) => { temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.TMP}, (err, dirPath) => { @@ -59,8 +64,8 @@ export class Win32Compiler extends BaseCompiler { }); } - override getExecutableFilename(dirPath: string, outputFilebase: string) { - return this.getOutputFilename(dirPath, outputFilebase) + '.exe'; + override getExecutableFilename(dirPath: string, outputFilebase: string, key?) { + return this.getOutputFilename(dirPath, outputFilebase, key) + '.exe'; } override getObjdumpOutputFilename(defaultOutputFilename: string) { @@ -98,6 +103,7 @@ export class Win32Compiler extends BaseCompiler { inputFilename: string, outputFilename: string, libraries, + overrides: ConfiguredOverrides, ) { let options = this.optionsForFilter(filters, outputFilename, userOptions); backendOptions = backendOptions || {}; @@ -168,7 +174,7 @@ export class Win32Compiler extends BaseCompiler { } } - override processAsm(result, filters /*, options*/) { + override async processAsm(result, filters /*, options*/) { if (filters.binary) { filters.dontMaskFilenames = true; return this.binaryAsmParser.process(result.asm, filters); diff --git a/lib/compilers/wine-vc.ts b/lib/compilers/wine-vc.ts index 14eec9b40..461085092 100644 --- a/lib/compilers/wine-vc.ts +++ b/lib/compilers/wine-vc.ts @@ -49,7 +49,12 @@ export class WineVcCompiler extends BaseCompiler { return 'Z:' + fn; } - override runCompiler(compiler: string, options: string[], inputFilename: string, execOptions: ExecutionOptions) { + override async runCompiler( + compiler: string, + options: string[], + inputFilename: string, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } @@ -59,7 +64,7 @@ export class WineVcCompiler extends BaseCompiler { execOptions.customCwd = execOptions.customCwd.substr(2); } - return super.runCompiler(compiler, options, inputFilename, execOptions); + return await super.runCompiler(compiler, options, inputFilename, execOptions); } override getArgumentParser() { diff --git a/lib/compilers/wsl-vc.ts b/lib/compilers/wsl-vc.ts index 5195341af..1908cb05f 100644 --- a/lib/compilers/wsl-vc.ts +++ b/lib/compilers/wsl-vc.ts @@ -60,7 +60,7 @@ export class WslVcCompiler extends Win32VcCompiler { // NPM temp package: https://www.npmjs.com/package/temp, see Affixes override newTempDir() { return new Promise<string>((resolve, reject) => { - temp.mkdir({prefix: 'compiler-explorer-compiler', dir: process.env.winTmp}, (err, dirPath) => { + temp.mkdir({prefix: 'compiler-explorer-compiler', dir: unwrap(process.env.winTmp)}, (err, dirPath) => { if (err) reject(`Unable to open temp file: ${err}`); else resolve(dirPath); }); @@ -82,7 +82,12 @@ export class WslVcCompiler extends Win32VcCompiler { return super.exec(compiler, args, options); } - override runCompiler(compiler: string, options: string[], inputFilename: string, execOptions: ExecutionOptions) { + override async runCompiler( + compiler: string, + options: string[], + inputFilename: string, + execOptions: ExecutionOptions & {env: Record<string, string>}, + ) { if (!execOptions) { execOptions = this.getDefaultExecOptions(); } @@ -94,6 +99,6 @@ export class WslVcCompiler extends Win32VcCompiler { const directoryPath = inputDirectory.substring(2).trim(); execOptions.customCwd = path.join('/mnt', driveLetter, directoryPath); - return super.runCompiler(compiler, options, inputFilename, execOptions); + return await super.runCompiler(compiler, options, inputFilename, execOptions); } } diff --git a/lib/compilers/z88dk.ts b/lib/compilers/z88dk.ts index 85d234336..ff16d07e3 100644 --- a/lib/compilers/z88dk.ts +++ b/lib/compilers/z88dk.ts @@ -32,6 +32,7 @@ import {BaseCompiler} from '../base-compiler.js'; import {logger} from '../logger.js'; import {AsmParserZ88dk} from '../parsers/asm-parser-z88dk.js'; import * as utils from '../utils.js'; +import {Z88dkParser} from './argument-parsers.js'; export class z88dkCompiler extends BaseCompiler { static get key() { @@ -44,6 +45,14 @@ export class z88dkCompiler extends BaseCompiler { this.asm = new AsmParserZ88dk(this.compilerProps); } + protected override getArgumentParser() { + return Z88dkParser; + } + + override getTargetFlags(): string[] { + return ['+<value>']; + } + public override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string { let filename; if (key && key.backendOptions && key.backendOptions.customOutputFilename) { @@ -71,8 +80,17 @@ export class z88dkCompiler extends BaseCompiler { userOptions: string[], staticLibLinks: string[], ) { - return userOptions.concat( - options, + let targetOpt = options.filter(opt => opt.startsWith('+')); + const withoutTarget = options.filter(opt => !opt.startsWith('+')); + const withoutTargetUser = userOptions.filter(opt => !opt.startsWith('+')); + + if (targetOpt.length === 0) { + targetOpt = userOptions.filter(opt => opt.startsWith('+')); + } + + return targetOpt.concat( + withoutTargetUser, + withoutTarget, [this.filename(inputFilename)], libIncludes, libOptions, @@ -90,7 +108,7 @@ export class z88dkCompiler extends BaseCompiler { } } - override getDefaultExecOptions(): ExecutionOptions { + override getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} { const opts = super.getDefaultExecOptions(); opts.env.ZCCCFG = path.join(path.dirname(this.compiler.exe), '../share/z88dk/lib/config'); opts.env.PATH = process.env.PATH + path.delimiter + path.dirname(this.compiler.exe); @@ -134,7 +152,7 @@ export class z88dkCompiler extends BaseCompiler { } } - const args = [outputFilename]; + const args = [...this.compiler.objdumperArgs, outputFilename]; if (this.externalparser) { const objResult = await this.externalparser.objdumpAndParseAssembly(result.dirPath, args, filters); diff --git a/lib/compilers/zig.ts b/lib/compilers/zig.ts index df2f3575f..b0d55432b 100644 --- a/lib/compilers/zig.ts +++ b/lib/compilers/zig.ts @@ -54,6 +54,7 @@ export class ZigCompiler extends BaseCompiler { } else { this.compiler.irArg = ['--emit', 'llvm-ir']; } + this.compiler.minIrArgs = ['--emit llvm-ir', '-femit-llvm-ir']; } override getSharedLibraryPathsAsArguments(): string[] { diff --git a/lib/compilers/zigcxx.ts b/lib/compilers/zigcxx.ts index 2d9b8ff3e..39a2e1621 100644 --- a/lib/compilers/zigcxx.ts +++ b/lib/compilers/zigcxx.ts @@ -28,6 +28,7 @@ import type {CompilerOutputOptions, ParseFiltersAndOutputOptions} from '../../ty import {asSafeVer} from '../utils.js'; import {ClangCompiler} from './clang.js'; +import {ZigCxxParser} from './argument-parsers.js'; export class ZigCXX extends ClangCompiler { private readonly needsForcedBinary: boolean; @@ -43,6 +44,10 @@ export class ZigCXX extends ClangCompiler { Semver.lt(asSafeVer(this.compiler.semver), '0.9.0', true); } + protected override getArgumentParser(): any { + return ZigCxxParser; + } + override preProcess(source: string, filters: CompilerOutputOptions): string { if (this.needsForcedBinary) { // note: zig versions > 0.6 don't emit asm, only binary works - https://github.com/ziglang/zig/issues/8153 |