aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/base-compiler.js148
-rw-r--r--lib/compilers/ada.js1
-rw-r--r--lib/compilers/argument-parsers.js5
-rw-r--r--static/panes/compiler.js6
-rw-r--r--static/panes/gccdump-view.js61
5 files changed, 159 insertions, 62 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;
}
diff --git a/lib/compilers/ada.js b/lib/compilers/ada.js
index 7d61d70c4..971564edb 100644
--- a/lib/compilers/ada.js
+++ b/lib/compilers/ada.js
@@ -33,6 +33,7 @@ export class AdaCompiler extends BaseCompiler {
constructor(info, env) {
super(info, env);
this.compiler.supportsGccDump = true;
+ this.compiler.removeEmptyGccDump = true;
this.compiler.supportsIntel = true;
this.compiler.supportsGnatDebugView = true;
}
diff --git a/lib/compilers/argument-parsers.js b/lib/compilers/argument-parsers.js
index c64de8549..1944711a1 100644
--- a/lib/compilers/argument-parsers.js
+++ b/lib/compilers/argument-parsers.js
@@ -100,6 +100,11 @@ export class GCCParser extends BaseParser {
// This check is not infallible, but takes care of Rust and Swift being picked up :)
if (_.find(keys, key => key.startsWith('-fdump-'))) {
compiler.compiler.supportsGccDump = true;
+
+ // By default, consider the compiler to be a regular GCC (eg. gcc,
+ // g++) and do the extra work of filtering out enabled pass that did
+ // not produce anything.
+ compiler.compiler.removeEmptyGccDump = true;
}
}
diff --git a/static/panes/compiler.js b/static/panes/compiler.js
index 82ca5da26..a3276d4d6 100644
--- a/static/panes/compiler.js
+++ b/static/panes/compiler.js
@@ -1334,11 +1334,11 @@ Compiler.prototype.onGccDumpFiltersChanged = function (id, filters, reqCompile)
}
};
-Compiler.prototype.onGccDumpPassSelected = function (id, passId, reqCompile) {
+Compiler.prototype.onGccDumpPassSelected = function (id, passObject, reqCompile) {
if (this.id === id) {
- this.gccDumpPassSelected = passId;
+ this.gccDumpPassSelected = passObject;
- if (reqCompile && passId !== '') {
+ if (reqCompile && passObject !== null) {
this.compile();
}
}
diff --git a/static/panes/gccdump-view.js b/static/panes/gccdump-view.js
index b43b9e8e4..42f72c799 100644
--- a/static/panes/gccdump-view.js
+++ b/static/panes/gccdump-view.js
@@ -1,4 +1,5 @@
// Copyright (c) 2017, Marc Poulhiès - Kalray Inc.
+// Copyright (c) 2021, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -54,7 +55,7 @@ function GccDump(hub, container, state) {
var gccdump_picker = this.domRoot[0].querySelector('.gccdump-pass-picker');
this.selectize = new TomSelect(gccdump_picker, {
- sortField: 'name',
+ sortField: -1, // do not sort
valueField: 'name',
labelField: 'name',
searchField: ['name'],
@@ -78,7 +79,24 @@ function GccDump(hub, container, state) {
if (state && state.selectedPass) {
this.state.selectedPass = state.selectedPass;
- this.eventHub.emit('gccDumpPassSelected', this.state._compilerid, state.selectedPass, false);
+
+ // To keep URL format stable wrt GccDump, only a string of the form 'r.expand' is stored.
+ // Old links also have the pass number prefixed but this can be ignored.
+ // Create the object that will be used instead of this bare string.
+ var selectedPassRe = /[0-9]*(i|t|r)\.([\w-_]*)/;
+ var passType = {
+ i: 'ipa',
+ r: 'rtl',
+ t: 'tree',
+ };
+ var match = state.selectedPass.match(selectedPassRe);
+ var selectedPassO = {
+ filename_suffix: match[1] + '.' + match[2],
+ name: match[2] + ' (' + passType[match[1]] + ')',
+ command_prefix: '-fdump-' + passType[match[1]] + '-' + match[2],
+ };
+
+ this.eventHub.emit('gccDumpPassSelected', this.state._compilerid, selectedPassO, false);
}
// until we get our first result from compilation backend with all fields,
@@ -211,10 +229,21 @@ GccDump.prototype.onUiReady = function () {
};
GccDump.prototype.onPassSelect = function (passId) {
+ var selectedPass = this.selectize.options[passId];
+
if (this.inhibitPassSelect !== true) {
- this.eventHub.emit('gccDumpPassSelected', this.state._compilerid, passId, true);
+ this.eventHub.emit('gccDumpPassSelected', this.state._compilerid, selectedPass, true);
}
- this.state.selectedPass = passId;
+
+ // To keep shared URL compatible, we keep on storing only a string in the
+ // state and stick to the original format.
+ // Previously, we were simply storing the full file suffix (the part after [...]):
+ // [file.c.]123t.expand
+ // We don't have the number now, but we can store the file suffix without this number
+ // (the number is useless and should probably have never been there in the
+ // first place).
+
+ this.state.selectedPass = selectedPass.filename_suffix;
this.saveState();
};
@@ -235,23 +264,17 @@ GccDump.prototype.updatePass = function (filters, selectize, gccDumpOutput) {
// trigger new compilation
this.inhibitPassSelect = true;
- _.each(selectize.options, function (p) {
- if (passes.indexOf(p.name) === -1) {
- selectize.removeOption(p.name);
- }
- }, this);
+ selectize.clear(true);
+ selectize.clearOptions(true);
_.each(passes, function (p) {
- selectize.addOption({
- name: p,
- });
+ selectize.addOption(p);
}, this);
- if (gccDumpOutput.selectedPass && gccDumpOutput.selectedPass !== '') {
- selectize.addItem(gccDumpOutput.selectedPass, true);
- } else {
+ if (gccDumpOutput.selectedPass)
+ selectize.addItem(gccDumpOutput.selectedPass.name, true);
+ else
selectize.clear(true);
- }
this.eventHub.emit('gccDumpPassSelected', this.state._compilerid, gccDumpOutput.selectedPass, false);
@@ -271,9 +294,9 @@ GccDump.prototype.onCompileResult = function (id, compiler, result) {
// if result contains empty selected pass, probably means
// we requested an invalid/outdated pass.
- if (result.gccDumpOutput.selectedPass === '') {
+ if (!result.gccDumpOutput.selectedPass) {
this.selectize.clear(true);
- this.state.selectedPass = '';
+ this.state.selectedPass = null;
}
this.updatePass(this.filters, this.selectize, result.gccDumpOutput);
this.showGccDumpResults(currOutput);
@@ -285,7 +308,7 @@ GccDump.prototype.onCompileResult = function (id, compiler, result) {
}
} else {
this.selectize.clear(true);
- this.state.selectedPass = '';
+ this.state.selectedPass = null;
this.updatePass(this.filters, this.selectize, false);
this.uiIsReady = false;
this.onUiNotReady();