diff options
Diffstat (limited to 'lib/base-compiler.js')
-rw-r--r-- | lib/base-compiler.js | 148 |
1 files changed, 108 insertions, 40 deletions
diff --git a/lib/base-compiler.js b/lib/base-compiler.js index 461e842ee..a2c7e0eac 100644 --- a/lib/base-compiler.js +++ b/lib/base-compiler.js @@ -753,11 +753,33 @@ export class BaseCompiler { return [{text: 'Internal error; unable to open output path'}]; } - async generateGccDump(inputFilename, options, gccDumpOptions) { - // Maybe we should not force any RTL dump and let user hand-pick what he needs - const addOpts = []; - /* if not defined, consider it true */ + /** + * @returns {{filename_suffix: string, name: string, command_prefix: string}} + * `filename_suffix`: dump file name suffix if GCC default dump names is used + * + * `name`: the name to be displayed in the UI + * + * `command_prefix`: command prefix to be used in case this dump is to be + * created using a targeted option (eg. -fdump-rtl-expand) + */ + fromInternalGccDumpName(internalDumpName, selectedPasses) { + if (!selectedPasses) + selectedPasses = ['ipa', 'tree', 'rtl']; + + const internalNameRe = new RegExp('^\\s*(' + selectedPasses.join('|') +')-([\\w_-]+).*ON$'); + const match = internalDumpName.match(internalNameRe); + if (match) + return { + filename_suffix: `${match[1][0]}.${match[2]}`, + name: match[2] + ' (' + match[1] + ')', + command_prefix: `-fdump-${match[1]}-${match[2]}`, + }; + else + return null; + } + getGccDumpOptions(gccDumpOptions, removeEmptyPasses) { + const addOpts = ['-fdump-passes']; // Build dump options to append to the end of the -fdump command-line flag. // GCC accepts these options as a list of '-' separated names that may // appear in any order. @@ -793,17 +815,35 @@ export class BaseCompiler { flags += '-all'; } - if (gccDumpOptions.treeDump !== false) { - addOpts.push('-fdump-tree-all' + flags); - } - if (gccDumpOptions.rtlDump !== false) { - addOpts.push('-fdump-rtl-all' + flags); - } - if (gccDumpOptions.ipaDump !== false) { - addOpts.push('-fdump-ipa-all' + flags); + // If we want to remove the passes that won't produce anything from the + // drop down menu, we need to ask for all dump files and see what's + // really created. This is currently only possible with regular GCC, not + // for compilers that us libgccjit. The later can't easily move dump + // files outside of the tempdir created on the fly. + if (removeEmptyPasses){ + if (gccDumpOptions.treeDump !== false) { + addOpts.push('-fdump-tree-all' + flags); + } + if (gccDumpOptions.rtlDump !== false) { + addOpts.push('-fdump-rtl-all' + flags); + } + if (gccDumpOptions.ipaDump !== false) { + addOpts.push('-fdump-ipa-all' + flags); + } + } else { + // If not dumping everything, create a specific command like + // -fdump-tree-fixup_cfg1-some-flags=stdout + if (gccDumpOptions.pass) { + const dumpCmd = gccDumpOptions.pass.command_prefix + flags + '=stdout'; + addOpts.push(dumpCmd); + } } - const newOptions = options.concat(addOpts); + return addOpts; + } + + async generateGccDump(inputFilename, options, gccDumpOptions, removeEmptyPasses) { + const newOptions = options.concat(this.getGccDumpOptions(gccDumpOptions, removeEmptyPasses)); const execOptions = this.getDefaultExecOptions(); // A higher max output is needed for when the user includes headers @@ -811,7 +851,8 @@ export class BaseCompiler { return this.processGccDumpOutput( gccDumpOptions, - await this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)); + await this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions), + removeEmptyPasses); } async checkOutputFileAndDoPostProcess(asmResult, outputFilename, filters) { @@ -1170,7 +1211,9 @@ export class BaseCompiler { ] = await Promise.all([ this.runCompiler(this.compiler.exe, options, inputFilenameSafe, execOptions), (makeAst ? this.generateAST(inputFilename, options) : ''), - (makeGccDump ? this.generateGccDump(inputFilename, options, backendOptions.produceGccDump) : ''), + (makeGccDump ? this.generateGccDump(inputFilename, options, + backendOptions.produceGccDump, + this.compiler.removeEmptyGccDump) : ''), (makeGnatDebug ? this.generateGnatDebug(inputFilename, options) : ''), (makeIr ? this.generateIR(inputFilename, options, filters) : ''), (makeRustMir ? this.generateRustMir(inputFilename, options) : ''), @@ -1624,15 +1667,14 @@ export class BaseCompiler { } - async processGccDumpOutput(opts, result) { + async processGccDumpOutput(opts, result, removeEmptyPasses) { const rootDir = path.dirname(result.inputFilename); - const allFiles = await fs.readdir(rootDir); if (opts.treeDump === false && opts.rtlDump === false && opts.ipaDump === false) { return { all: [], - selectedPass: '', - currentPassOutput: 'Nothing selected for dump:\nselect at least one of Tree/RTL filter', + selectedPass: null, + currentPassOutput: 'Nothing selected for dump:\nselect at least one of Tree/IPA/RTL filter', syntaxHighlight: false, }; } @@ -1643,34 +1685,60 @@ export class BaseCompiler { currentPassOutput: '<No pass selected>', syntaxHighlight: false, }; + const selectedPasses = []; + + if (opts.treeDump) selectedPasses.push('tree'); + if (opts.ipaDump) selectedPasses.push('ipa'); + if (opts.rtlDump) selectedPasses.push('rtl'); + + let dumpFileName; let passFound = false; - // Phase letter is one of {i, l, r, t} - // {outpufilename}.{extension}.{passNumber}{phaseLetter}.{phaseName} - const dumpFilenameRegex = /^.+?\..+?\.(\d+?[ilrt]\..+)$/; - for (let filename of allFiles) { - const match = dumpFilenameRegex.exec(filename); - if (match) { - const pass = match[1]; - output.all.push(pass); - const filePath = path.join(rootDir, filename); - if (opts.pass === pass && (await fs.stat(filePath)).isFile()) { + + for (const obj of Object.values(result.stderr)) { + const selectizeObject = this.fromInternalGccDumpName(obj.text, selectedPasses); + if (selectizeObject){ + if (opts.pass && opts.pass.name === selectizeObject.name) passFound = true; - output.currentPassOutput = await fs.readFile(filePath, 'utf-8'); - if (/^\s*$/.test(output.currentPassOutput)) { - output.currentPassOutput = 'File for selected pass is empty.'; - } else { - output.syntaxHighlight = true; - } + + if (removeEmptyPasses){ + const f = fs.readdirSync(rootDir).filter(fn => fn.endsWith(selectizeObject.filename_suffix)); + + // pass is enabled, but the dump hasn't produced anything: + // don't add it to the drop down menu + if (f.length === 0) + continue; + + if (opts.pass && opts.pass.name === selectizeObject.name) + dumpFileName = path.join(rootDir, f[0]); } + + output.all.push(selectizeObject); } } - if (opts.pass && !passFound) { - output.currentPassOutput = `Pass '${opts.pass}' was requested -but is not valid anymore with current filters. -Please select another pass or change filters.`; - } + if (opts.pass && passFound){ + output.currentPassOutput = ''; + if (removeEmptyPasses) { + if (dumpFileName) + output.currentPassOutput = await fs.readFile(dumpFileName, 'utf-8'); + // else leave the currentPassOutput empty. Can happen when some + // UI options are changed and a now disabled pass is still + // requested. + } else { + for (const obj of Object.values(result.stdout)) { + output.currentPassOutput += obj.text + '\n'; + } + } + if (/^\s*$/.test(output.currentPassOutput)) { + output.currentPassOutput = `Pass '${opts.pass.name}' was requested +but nothing was dumped. Possible causes are: + - pass is not valid in this (maybe you changed the compiler options); + - pass is valid but did not emit anything (eg. it was not executed).`; + } else { + output.syntaxHighlight = true; + } + } return output; } |