mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
@@ -6,6 +6,7 @@ import {assertNoConsoleOutput, stubConsoleOutput} from '../support/utils';
|
||||
const PANE_DATA_MAP = {
|
||||
executor: {name: 'Executor', selector: 'create-executor'},
|
||||
opt: {name: 'Opt Viewer', selector: 'view-optimization'},
|
||||
stackusage: {name: 'Stack Usage Viewer', selector: 'view-stack-usage'},
|
||||
preprocessor: {name: 'Preprocessor', selector: 'view-pp'},
|
||||
ast: {name: 'Ast Viewer', selector: 'view-ast'},
|
||||
llvmir: {name: 'LLVM IR', selector: 'view-ir'},
|
||||
|
||||
@@ -74,6 +74,7 @@ import {languages} from './languages.js';
|
||||
import {LlvmAstParser} from './llvm-ast.js';
|
||||
import {LlvmIrParser} from './llvm-ir.js';
|
||||
import * as compilerOptInfo from './llvm-opt-transformer.js';
|
||||
import * as StackUsageTransformer from './stack-usage-transformer.js';
|
||||
import {logger} from './logger.js';
|
||||
import {getObjdumperTypeByKey} from './objdumper/index.js';
|
||||
import {Packager} from './packager.js';
|
||||
@@ -1064,6 +1065,9 @@ export class BaseCompiler implements ICompiler {
|
||||
if (this.compiler.supportsOptOutput && backendOptions.produceOptInfo) {
|
||||
options = options.concat(unwrap(this.compiler.optArg));
|
||||
}
|
||||
if (this.compiler.supportsStackUsageOutput && backendOptions.produceStackUsageInfo) {
|
||||
options = options.concat(unwrap(this.compiler.stackUsageArg));
|
||||
}
|
||||
|
||||
const toolchainPath = this.getDefaultOrOverridenToolchainPath(backendOptions.overrides || []);
|
||||
|
||||
@@ -2128,14 +2132,21 @@ export class BaseCompiler implements ICompiler {
|
||||
}
|
||||
|
||||
asmResult.tools = toolsResult;
|
||||
|
||||
if (this.compiler.supportsOptOutput && this.optOutputRequested(options)) {
|
||||
if (this.compiler.supportsOptOutput && backendOptions.produceOptInfo) {
|
||||
const optPath = path.join(dirPath, `${this.outputFilebase}.opt.yaml`);
|
||||
if (await fs.pathExists(optPath)) {
|
||||
asmResult.hasOptOutput = true;
|
||||
asmResult.optPath = optPath;
|
||||
}
|
||||
}
|
||||
if (this.compiler.supportsStackUsageOutput && backendOptions.produceStackUsageInfo) {
|
||||
const suPath = path.join(dirPath, `${this.outputFilebase}.su`);
|
||||
if (await fs.pathExists(suPath)) {
|
||||
asmResult.hasStackUsageOutput = true;
|
||||
asmResult.stackUsagePath = suPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (astResult) {
|
||||
asmResult.hasAstOutput = true;
|
||||
asmResult.astOutput = astResult;
|
||||
@@ -2438,6 +2449,7 @@ export class BaseCompiler implements ICompiler {
|
||||
}
|
||||
|
||||
const optOutput = undefined;
|
||||
const stackUsageOutput = undefined;
|
||||
await this.afterCompilation(
|
||||
fullResult.result,
|
||||
false,
|
||||
@@ -2448,6 +2460,7 @@ export class BaseCompiler implements ICompiler {
|
||||
cacheKey.filters,
|
||||
libsAndOptions.options,
|
||||
optOutput,
|
||||
stackUsageOutput,
|
||||
bypassCache,
|
||||
path.join(dirPath, 'build'),
|
||||
);
|
||||
@@ -2587,7 +2600,7 @@ export class BaseCompiler implements ICompiler {
|
||||
}
|
||||
const inputFilename = writeSummary.inputFilename;
|
||||
|
||||
const [result, optOutput] = await this.doCompilation(
|
||||
const [result, optOutput, stackUsageOutput] = await this.doCompilation(
|
||||
inputFilename,
|
||||
dirPath,
|
||||
key,
|
||||
@@ -2608,6 +2621,7 @@ export class BaseCompiler implements ICompiler {
|
||||
filters,
|
||||
options,
|
||||
optOutput,
|
||||
stackUsageOutput,
|
||||
bypassCache,
|
||||
);
|
||||
})();
|
||||
@@ -2626,6 +2640,7 @@ export class BaseCompiler implements ICompiler {
|
||||
filters,
|
||||
options,
|
||||
optOutput,
|
||||
stackUsageOutput,
|
||||
bypassCache: BypassCache,
|
||||
customBuildPath?,
|
||||
) {
|
||||
@@ -2637,6 +2652,11 @@ export class BaseCompiler implements ICompiler {
|
||||
result.optOutput = optOutput;
|
||||
}
|
||||
|
||||
if (result.hasStackUsageOutput) {
|
||||
delete result.stackUsagePath;
|
||||
result.stackUsageOutput = stackUsageOutput;
|
||||
}
|
||||
|
||||
const compilationInfo = this.getCompilationInfo(key, result, customBuildPath);
|
||||
|
||||
result.tools = _.union(
|
||||
@@ -2748,6 +2768,26 @@ export class BaseCompiler implements ICompiler {
|
||||
|
||||
return output;
|
||||
}
|
||||
async processStackUsageOutput(suPath) {
|
||||
const output = StackUsageTransformer.parse(await fs.readFile(suPath, 'utf-8'));
|
||||
|
||||
if (this.compiler.demangler) {
|
||||
const result = JSON.stringify(output, null, 4);
|
||||
try {
|
||||
const demangleResult = await this.exec(
|
||||
this.compiler.demangler,
|
||||
[...this.compiler.demanglerArgs, '-n', '-p'],
|
||||
{input: result},
|
||||
);
|
||||
return JSON.parse(demangleResult.stdout);
|
||||
} catch (exception) {
|
||||
// swallow exception and return non-demangled output
|
||||
logger.warn(`Caught exception ${exception} during stack usage demangle parsing`);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
couldSupportASTDump(version: string) {
|
||||
const versionRegex = /version (\d+.\d+)/;
|
||||
@@ -2897,6 +2937,7 @@ but nothing was dumped. Possible causes are:
|
||||
const postProcess = _.compact(this.compiler.postProcess);
|
||||
const maxSize = this.env.ceProps('max-asm-size', 64 * 1024 * 1024);
|
||||
const optPromise = result.hasOptOutput ? this.processOptOutput(result.optPath) : '';
|
||||
const stackUsagePromise = result.hasStackUsageOutput ? this.processStackUsageOutput(result.stackUsagePath) : '';
|
||||
const asmPromise =
|
||||
(filters.binary || filters.binaryObject) && this.supportsObjdump()
|
||||
? this.objdump(
|
||||
@@ -2928,7 +2969,7 @@ but nothing was dumped. Possible causes are:
|
||||
return result;
|
||||
}
|
||||
})();
|
||||
return Promise.all([asmPromise, optPromise]);
|
||||
return Promise.all([asmPromise, optPromise, stackUsagePromise]);
|
||||
}
|
||||
|
||||
handlePostProcessResult(result, postResult): CompilationResult {
|
||||
|
||||
@@ -187,6 +187,9 @@ export class ClientStateNormalizer {
|
||||
this.addSpecialOutputToCompiler(component.componentState.id, 'ast', component.componentState.editorid);
|
||||
} else if (component.componentName === 'opt') {
|
||||
this.addSpecialOutputToCompiler(component.componentState.id, 'opt', component.componentState.editorid);
|
||||
} else if (component.componentName === 'stackusage') {
|
||||
this.addSpecialOutputToCompiler(component.componentState.id, 'stackusage',
|
||||
component.componentState.editorid);
|
||||
} else if (component.componentName === 'cfg') {
|
||||
this.addSpecialOutputToCompiler(component.componentState.id, 'cfg', component.componentState.editorid);
|
||||
} else if (component.componentName === 'gccdump') {
|
||||
@@ -321,6 +324,18 @@ class GoldenLayoutComponents {
|
||||
reorderEnabled: true,
|
||||
};
|
||||
}
|
||||
createStackUsageComponent(session, compilerIndex, customSessionId?) {
|
||||
return {
|
||||
type: 'component',
|
||||
componentName: 'stackusage',
|
||||
componentState: {
|
||||
id: compilerIndex,
|
||||
editorid: customSessionId || (session ? session.id : undefined),
|
||||
},
|
||||
isClosable: true,
|
||||
reorderEnabled: true,
|
||||
};
|
||||
}
|
||||
|
||||
createCfgComponent(session, compilerIndex, customSessionId?) {
|
||||
return {
|
||||
@@ -505,6 +520,8 @@ class GoldenLayoutComponents {
|
||||
return this.createAstComponent(session, idxCompiler + 1, customSessionId);
|
||||
} else if (viewtype === 'opt') {
|
||||
return this.createOptComponent(session, idxCompiler + 1, customSessionId);
|
||||
} else if (viewtype === 'stackusage') {
|
||||
return this.createStackUsageComponent(session, idxCompiler + 1, customSessionId);
|
||||
} else if (viewtype === 'cfg') {
|
||||
return this.createCfgComponent(session, idxCompiler + 1, customSessionId);
|
||||
} else if (viewtype === 'gccdump') {
|
||||
@@ -519,6 +536,8 @@ class GoldenLayoutComponents {
|
||||
return this.createAstComponent(null, idxCompiler + 1, false);
|
||||
} else if (viewtype === 'opt') {
|
||||
return this.createOptComponent(null, idxCompiler + 1, false);
|
||||
} else if (viewtype === 'stackusage') {
|
||||
return this.createOptComponent(null, idxCompiler + 1, false);
|
||||
} else if (viewtype === 'cfg') {
|
||||
return this.createCfgComponent(null, idxCompiler + 1, false);
|
||||
} else if (viewtype === 'gccdump') {
|
||||
|
||||
@@ -161,6 +161,10 @@ export class GCCParser extends BaseParser {
|
||||
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 (this.hasSupport(options, '-fdiagnostics-color')) {
|
||||
if (compiler.compiler.options) compiler.compiler.options += ' ';
|
||||
compiler.compiler.options += '-fdiagnostics-color=always';
|
||||
@@ -255,6 +259,11 @@ export class ClangParser extends BaseParser {
|
||||
compiler.compiler.optArg = '-fsave-optimization-record';
|
||||
compiler.compiler.supportsOptOutput = true;
|
||||
}
|
||||
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'];
|
||||
|
||||
@@ -226,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() {
|
||||
|
||||
@@ -110,7 +110,7 @@ export class NvccCompiler extends BaseCompiler {
|
||||
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) {
|
||||
|
||||
57
lib/stack-usage-transformer.ts
Normal file
57
lib/stack-usage-transformer.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023, Compiler Explorer Authors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
type Path = string;
|
||||
|
||||
type StackInfo = {
|
||||
displayString: string;
|
||||
}
|
||||
|
||||
export type StackUsageInfo = StackInfo & {
|
||||
DebugLoc: DebugLoc;
|
||||
Function: string;
|
||||
BytesUsed: number;
|
||||
Qualifier: 'static' | 'dynamic' | 'dynamic,bounded';
|
||||
}
|
||||
|
||||
type DebugLoc = {
|
||||
File: Path;
|
||||
Line: number;
|
||||
Column: number;
|
||||
}
|
||||
|
||||
export function parse(suText: string) {
|
||||
const output: StackUsageInfo[] = [];
|
||||
suText.split('\n').filter(e => e)
|
||||
.forEach(line => {
|
||||
const c = line.split('\t');
|
||||
const pathLocName = c[0].split(':');
|
||||
const lineNumber = +pathLocName[1];
|
||||
const qualifier = c.at(-1);
|
||||
const su = {
|
||||
DebugLoc: {File: pathLocName[0], Line: lineNumber, Column: 0},
|
||||
Function: pathLocName.at(-1),
|
||||
Qualifier: qualifier,
|
||||
BytesUsed: parseInt(c[1]),
|
||||
displayString: c[1] + ' bytes, ' + qualifier,
|
||||
};
|
||||
output.push(su as StackUsageInfo);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export const TOOL_COMPONENT_NAME = 'tool';
|
||||
export const TOOL_INPUT_VIEW_COMPONENT_NAME = 'toolInputView';
|
||||
export const DIFF_VIEW_COMPONENT_NAME = 'diff';
|
||||
export const OPT_VIEW_COMPONENT_NAME = 'opt';
|
||||
export const STACK_USAGE_VIEW_COMPONENT_NAME = 'stackusage';
|
||||
export const FLAGS_VIEW_COMPONENT_NAME = 'flags';
|
||||
export const PP_VIEW_COMPONENT_NAME = 'pp';
|
||||
export const AST_VIEW_COMPONENT_NAME = 'ast';
|
||||
@@ -144,6 +145,15 @@ export type PopulatedOptViewState = StateWithId &
|
||||
treeid: number;
|
||||
};
|
||||
|
||||
export type EmptyStackUsageViewState = EmptyState;
|
||||
export type PopulatedStackUsageViewState = StateWithId &
|
||||
StateWithEditor & {
|
||||
suOutput: unknown;
|
||||
compilerName: string;
|
||||
editorid: number;
|
||||
treeid: number;
|
||||
};
|
||||
|
||||
export type EmptyFlagsViewState = EmptyState;
|
||||
export type PopulatedFlagsViewState = StateWithId & {
|
||||
compilerName: string;
|
||||
|
||||
@@ -84,6 +84,7 @@ import {
|
||||
TOOL_INPUT_VIEW_COMPONENT_NAME,
|
||||
DIFF_VIEW_COMPONENT_NAME,
|
||||
OPT_VIEW_COMPONENT_NAME,
|
||||
STACK_USAGE_VIEW_COMPONENT_NAME,
|
||||
FLAGS_VIEW_COMPONENT_NAME,
|
||||
PP_VIEW_COMPONENT_NAME,
|
||||
AST_VIEW_COMPONENT_NAME,
|
||||
@@ -103,6 +104,7 @@ import {
|
||||
LLVM_OPT_PIPELINE_VIEW_COMPONENT_NAME,
|
||||
EmptyLLVMOptPipelineViewState,
|
||||
PopulatedLLVMOptPipelineViewState,
|
||||
PopulatedStackUsageViewState, EmptyStackUsageViewState,
|
||||
} from './components.interfaces.js';
|
||||
import {ConfiguredOverrides} from './compilation/compiler-overrides.interfaces.js';
|
||||
|
||||
@@ -387,6 +389,35 @@ export function getOptViewWith(
|
||||
};
|
||||
}
|
||||
|
||||
export function getStackUsageView(): ComponentConfig<EmptyStackUsageViewState> {
|
||||
return {
|
||||
type: 'component',
|
||||
componentName: STACK_USAGE_VIEW_COMPONENT_NAME,
|
||||
componentState: {},
|
||||
};
|
||||
}
|
||||
export function getStackUsageViewWith(
|
||||
id: number,
|
||||
source: string,
|
||||
suOutput: unknown,
|
||||
compilerName: string,
|
||||
editorid: number,
|
||||
treeid: number,
|
||||
): ComponentConfig<PopulatedStackUsageViewState> {
|
||||
return {
|
||||
type: 'component',
|
||||
componentName: STACK_USAGE_VIEW_COMPONENT_NAME,
|
||||
componentState: {
|
||||
id,
|
||||
source,
|
||||
suOutput,
|
||||
compilerName,
|
||||
editorid,
|
||||
treeid,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Get an empty flags view component. */
|
||||
export function getFlagsView(): ComponentConfig<EmptyFlagsViewState> {
|
||||
return {
|
||||
|
||||
@@ -126,6 +126,8 @@ export type EventMap = {
|
||||
newSource: (editorId: number, newSource: string) => void;
|
||||
optViewClosed: (compilerId: number) => void;
|
||||
optViewOpened: (compilerId: number) => void;
|
||||
stackUsageViewClosed: (compilerId: number) => void;
|
||||
stackUsageViewOpened: (compilerId: number) => void;
|
||||
outputClosed: (compilerId: number) => void;
|
||||
outputOpened: (compilerId: number) => void;
|
||||
panesLinkLine: (
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
IR_VIEW_COMPONENT_NAME,
|
||||
LLVM_OPT_PIPELINE_VIEW_COMPONENT_NAME,
|
||||
OPT_VIEW_COMPONENT_NAME,
|
||||
STACK_USAGE_VIEW_COMPONENT_NAME,
|
||||
OUTPUT_COMPONENT_NAME,
|
||||
PP_VIEW_COMPONENT_NAME,
|
||||
RUST_HIR_VIEW_COMPONENT_NAME,
|
||||
@@ -63,6 +64,7 @@ import {Tool} from './panes/tool.js';
|
||||
import {Diff} from './panes/diff.js';
|
||||
import {ToolInputView} from './panes/tool-input-view.js';
|
||||
import {Opt as OptView} from './panes/opt-view.js';
|
||||
import {StackUsage as StackUsageView} from './panes/stack-usage-view.js';
|
||||
import {Flags as FlagsView} from './panes/flags-view.js';
|
||||
import {PP as PreProcessorView} from './panes/pp-view.js';
|
||||
import {Ast as AstView} from './panes/ast-view.js';
|
||||
@@ -121,6 +123,7 @@ export class Hub {
|
||||
layout.registerComponent(TOOL_INPUT_VIEW_COMPONENT_NAME, (c, s) => this.toolInputViewFactory(c, s));
|
||||
layout.registerComponent(DIFF_VIEW_COMPONENT_NAME, (c, s) => this.diffFactory(c, s));
|
||||
layout.registerComponent(OPT_VIEW_COMPONENT_NAME, (c, s) => this.optViewFactory(c, s));
|
||||
layout.registerComponent(STACK_USAGE_VIEW_COMPONENT_NAME, (c, s) => this.stackUsageViewFactory(c, s));
|
||||
layout.registerComponent(FLAGS_VIEW_COMPONENT_NAME, (c, s) => this.flagsViewFactory(c, s));
|
||||
layout.registerComponent(PP_VIEW_COMPONENT_NAME, (c, s) => this.ppViewFactory(c, s));
|
||||
layout.registerComponent(AST_VIEW_COMPONENT_NAME, (c, s) => this.astViewFactory(c, s));
|
||||
@@ -426,6 +429,11 @@ export class Hub {
|
||||
return new OptView(this, container, state);
|
||||
}
|
||||
|
||||
public stackUsageViewFactory(container: GoldenLayout.Container,
|
||||
state: ConstructorParameters<typeof StackUsageView>[2]): StackUsageView {
|
||||
return new StackUsageView(this, container, state);
|
||||
}
|
||||
|
||||
public flagsViewFactory(
|
||||
container: GoldenLayout.Container,
|
||||
state: ConstructorParameters<typeof FlagsView>[2],
|
||||
|
||||
@@ -179,6 +179,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
private nextCMakeRequest: CompilationRequest | null;
|
||||
private flagsViewOpen: boolean;
|
||||
private optViewOpen: boolean;
|
||||
private stackUsageViewOpen: boolean;
|
||||
private cfgViewOpen: boolean;
|
||||
private wantOptInfo?: boolean;
|
||||
private readonly decorations: Decorations;
|
||||
@@ -195,6 +196,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
private currentLangId: string | null;
|
||||
private filters: Toggles;
|
||||
private optButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
private stackUsageButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
private flagsButton?: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
private ppButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
private astButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>;
|
||||
@@ -475,6 +477,16 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
this.sourceTreeId ?? 0,
|
||||
);
|
||||
};
|
||||
const createStackUsageView = () => {
|
||||
return Components.getStackUsageViewWith(
|
||||
this.id,
|
||||
this.source,
|
||||
this.lastResult?.optOutput,
|
||||
this.getCompilerName(),
|
||||
this.sourceEditorId ?? 0,
|
||||
this.sourceTreeId ?? 0,
|
||||
);
|
||||
};
|
||||
|
||||
const createFlagsView = () => {
|
||||
return Components.getFlagsViewWith(this.id, this.getCompilerName(), this.optionsField.val());
|
||||
@@ -696,6 +708,17 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
insertPoint.addChild(createOptView());
|
||||
});
|
||||
|
||||
(this.container.layoutManager
|
||||
.createDragSource(this.stackUsageButton, createStackUsageView as any) as any)
|
||||
._dragListener.on('dragStart', togglePannerAdder);
|
||||
|
||||
this.stackUsageButton.on('click', () => {
|
||||
const insertPoint =
|
||||
this.hub.findParentRowOrColumn(this.container.parent) ||
|
||||
this.container.layoutManager.root.contentItems[0];
|
||||
insertPoint.addChild(createStackUsageView());
|
||||
});
|
||||
|
||||
const popularArgumentsMenu = this.domRoot.find('div.populararguments div.dropdown-menu');
|
||||
if (this.flagsButton) {
|
||||
this.container.layoutManager
|
||||
@@ -1217,6 +1240,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
dumpFlags: this.dumpFlags,
|
||||
},
|
||||
produceOptInfo: this.wantOptInfo ?? false,
|
||||
produceStackUsageInfo: this.stackUsageViewOpen,
|
||||
produceCfg: this.cfgViewOpen,
|
||||
produceGnatDebugTree: this.gnatDebugTreeViewOpen,
|
||||
produceGnatDebug: this.gnatDebugViewOpen,
|
||||
@@ -1972,6 +1996,12 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
this.optButton.prop('disabled', this.optViewOpen);
|
||||
}
|
||||
}
|
||||
onStackUsageViewClosed(id: number): void {
|
||||
if (this.id === id) {
|
||||
this.stackUsageViewOpen = false;
|
||||
this.stackUsageButton.prop('disabled', this.stackUsageViewOpen);
|
||||
}
|
||||
}
|
||||
|
||||
onFlagsViewClosed(id: number, compilerFlags: string): void {
|
||||
if (this.id === id) {
|
||||
@@ -2297,6 +2327,13 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
this.compile();
|
||||
}
|
||||
}
|
||||
onStackUsageViewOpened(id: number): void {
|
||||
if (this.id === id) {
|
||||
this.stackUsageViewOpen = true;
|
||||
this.stackUsageButton.prop('disabled', this.stackUsageViewOpen);
|
||||
this.compile();
|
||||
}
|
||||
}
|
||||
|
||||
onFlagsViewOpened(id: number): void {
|
||||
if (this.id === id) {
|
||||
@@ -2368,6 +2405,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
this.filters = new Toggles(this.domRoot.find('.filters'), patchOldFilters(state.filters));
|
||||
|
||||
this.optButton = this.domRoot.find('.btn.view-optimization');
|
||||
this.stackUsageButton = this.domRoot.find('.btn.view-stack-usage');
|
||||
this.flagsButton = this.domRoot.find('div.populararguments div.dropdown-menu button');
|
||||
this.ppButton = this.domRoot.find('.btn.view-pp');
|
||||
this.astButton = this.domRoot.find('.btn.view-ast');
|
||||
@@ -2665,6 +2703,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
// many executors as you want.
|
||||
|
||||
this.optButton.toggle(!!this.compiler.supportsOptOutput);
|
||||
this.stackUsageButton.toggle(!!this.compiler.supportsStackUsageOutput);
|
||||
this.ppButton.toggle(!!this.compiler.supportsPpView);
|
||||
this.astButton.toggle(!!this.compiler.supportsAstView);
|
||||
this.irButton.toggle(!!this.compiler.supportsIrView);
|
||||
@@ -2819,6 +2858,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
||||
|
||||
this.eventHub.on('optViewOpened', this.onOptViewOpened, this);
|
||||
this.eventHub.on('optViewClosed', this.onOptViewClosed, this);
|
||||
this.eventHub.on('stackUsageViewOpened', this.onStackUsageViewOpened, this);
|
||||
this.eventHub.on('stackUsageViewClosed', this.onStackUsageViewClosed, this);
|
||||
this.eventHub.on('flagsViewOpened', this.onFlagsViewOpened, this);
|
||||
this.eventHub.on('flagsViewClosed', this.onFlagsViewClosed, this);
|
||||
this.eventHub.on('ppViewOpened', this.onPpViewOpened, this);
|
||||
|
||||
43
static/panes/stack-usage-view.interfaces.ts
Normal file
43
static/panes/stack-usage-view.interfaces.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
export interface StackUsageState {
|
||||
suOutput?: suCodeEntry[];
|
||||
source: any; // TODO
|
||||
}
|
||||
|
||||
type SourceLocation = {
|
||||
File: string;
|
||||
Line: number;
|
||||
Column: number;
|
||||
};
|
||||
|
||||
export type suCodeEntry = {
|
||||
DebugLoc: SourceLocation;
|
||||
Function: string;
|
||||
// https://github.com/gcc-mirror/gcc/blob/master/gcc/toplev.cc#L773-L775
|
||||
Qualifier: 'static' | 'dynamic' | 'dynamic,bounded';
|
||||
BytesUsed: number;
|
||||
displayString: string;
|
||||
};
|
||||
171
static/panes/stack-usage-view.ts
Normal file
171
static/panes/stack-usage-view.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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 'jquery';
|
||||
import _ from 'underscore';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import {Container} from 'golden-layout';
|
||||
|
||||
import {MonacoPane} from './pane.js';
|
||||
import {StackUsageState, suCodeEntry} from './stack-usage-view.interfaces.js';
|
||||
import {MonacoPaneState} from './pane.interfaces.js';
|
||||
|
||||
import {ga} from '../analytics.js';
|
||||
import {extendConfig} from '../monaco-config.js';
|
||||
import {Hub} from '../hub.js';
|
||||
|
||||
export class StackUsage extends MonacoPane<monaco.editor.IStandaloneCodeEditor, StackUsageState> {
|
||||
currentDecorations: string[] = [];
|
||||
// Note: bool | undef here instead of just bool because of an issue with field initialization order
|
||||
isCompilerSupported?: boolean;
|
||||
|
||||
constructor(hub: Hub, container: Container, state: StackUsageState & MonacoPaneState) {
|
||||
super(hub, container, state);
|
||||
if (state.suOutput) {
|
||||
this.showStackUsageResults(state.suOutput);
|
||||
}
|
||||
this.eventHub.emit('stackUsageViewOpened', this.compilerInfo.compilerId);
|
||||
}
|
||||
|
||||
override getInitialHTML(): string {
|
||||
return $('#stackusage').html();
|
||||
}
|
||||
|
||||
override createEditor(editorRoot: HTMLElement): monaco.editor.IStandaloneCodeEditor {
|
||||
return monaco.editor.create(
|
||||
editorRoot,
|
||||
extendConfig({
|
||||
language: 'plaintext',
|
||||
readOnly: true,
|
||||
glyphMargin: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
override registerOpeningAnalyticsEvent() {
|
||||
ga.proxy('send', {
|
||||
hitType: 'event',
|
||||
eventCategory: 'OpenViewPane',
|
||||
eventAction: 'StackUsage',
|
||||
});
|
||||
}
|
||||
|
||||
override registerCallbacks() {
|
||||
this.eventHub.emit('requestSettings');
|
||||
this.eventHub.emit('findCompilers');
|
||||
|
||||
this.container.on('shown', this.resize, this);
|
||||
|
||||
const cursorSelectionThrottledFunction = _.throttle(this.onDidChangeCursorSelection.bind(this), 500);
|
||||
this.editor.onDidChangeCursorSelection(e => {
|
||||
cursorSelectionThrottledFunction(e);
|
||||
});
|
||||
}
|
||||
|
||||
override onCompileResult(id: number, compiler, result) {
|
||||
if (this.compilerInfo.compilerId !== id || !this.isCompilerSupported) return;
|
||||
this.editor.setValue(result.source);
|
||||
if (result.hasStackUsageOutput) {
|
||||
this.showStackUsageResults(result.stackUsageOutput);
|
||||
}
|
||||
// TODO: This is inelegant again. Previously took advantage of fourth argument for the compileResult event.
|
||||
const lang = compiler.lang === 'c++' ? 'cpp' : compiler.lang;
|
||||
const model = this.editor.getModel();
|
||||
if (model != null && this.getCurrentEditorLanguage() !== lang) {
|
||||
monaco.editor.setModelLanguage(model, lang);
|
||||
}
|
||||
|
||||
if (!this.isAwaitingInitialResults) {
|
||||
if (this.selection) {
|
||||
this.editor.setSelection(this.selection);
|
||||
this.editor.revealLinesInCenter(this.selection.startLineNumber, this.selection.endLineNumber);
|
||||
}
|
||||
this.isAwaitingInitialResults = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Monaco language id of the current editor
|
||||
getCurrentEditorLanguage() {
|
||||
return this.editor.getModel()?.getLanguageId();
|
||||
}
|
||||
|
||||
override getDefaultPaneName() {
|
||||
return 'Stack Usage Viewer';
|
||||
}
|
||||
|
||||
getDisplayableOpt(optResult: suCodeEntry) {
|
||||
return {
|
||||
value: optResult.displayString,
|
||||
isTrusted: false,
|
||||
};
|
||||
}
|
||||
|
||||
showStackUsageResults(results: suCodeEntry[]) {
|
||||
const su: monaco.editor.IModelDeltaDecoration[] = [];
|
||||
|
||||
const groupedResults = _.groupBy(results, x => x.DebugLoc.Line);
|
||||
|
||||
for (const [key, value] of Object.entries(groupedResults)) {
|
||||
const linenumber = Number(key);
|
||||
const className = value.reduce((acc, x) => {
|
||||
// reuse CSS in opt-view.ts
|
||||
if (x.Qualifier === 'static' || acc === 'static') {
|
||||
return 'Missed';
|
||||
} else if (x.Qualifier === 'dynamic' || acc === 'dynamic') {
|
||||
return 'Passed';
|
||||
}
|
||||
return 'Mixed';
|
||||
}, '');
|
||||
const contents = value.map(this.getDisplayableOpt);
|
||||
su.push({
|
||||
range: new monaco.Range(linenumber, 1, linenumber, Infinity),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
glyphMarginClassName: 'opt-decoration.' + className.toLowerCase(),
|
||||
hoverMessage: contents,
|
||||
glyphMarginHoverMessage: contents,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.currentDecorations = this.editor.deltaDecorations(this.currentDecorations, su);
|
||||
}
|
||||
|
||||
override onCompiler(id: number, compiler) {
|
||||
if (id === this.compilerInfo.compilerId) {
|
||||
this.compilerInfo.compilerName = compiler ? compiler.name : '';
|
||||
this.updateTitle();
|
||||
this.isCompilerSupported = compiler ? compiler.supportsStackUsageOutput : false;
|
||||
if (!this.isCompilerSupported) {
|
||||
this.editor.setValue('<Stack usage output is not supported for this compiler>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override close() {
|
||||
this.eventHub.unsubscribe();
|
||||
this.eventHub.emit('stackUsageViewClosed', this.compilerInfo.compilerId);
|
||||
this.editor.dispose();
|
||||
}
|
||||
}
|
||||
65
test/stack-usage-transformer-test.ts
Normal file
65
test/stack-usage-transformer-test.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2023, Compiler Explorer Authors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import {parse} from '../lib/stack-usage-transformer.js';
|
||||
|
||||
describe('stack usage transformer', () => {
|
||||
it('should work', async () => {
|
||||
const doc = `example.cpp:2:5:int square(int)\t16\tstatic
|
||||
example.cpp:6:5:int f()\t32\tdynamic
|
||||
example.cpp:7:5:int h()\t64\tdynamic,bounded
|
||||
`;
|
||||
const output = parse(doc);
|
||||
output.should.deep.equal([
|
||||
{
|
||||
BytesUsed: 16,
|
||||
DebugLoc: {
|
||||
File: 'example.cpp',
|
||||
Line: 2,
|
||||
Column: 0,
|
||||
},
|
||||
Function: 'int square(int)',
|
||||
Qualifier: 'static',
|
||||
displayString: '16 bytes, static',
|
||||
},
|
||||
{
|
||||
BytesUsed: 32,
|
||||
DebugLoc: {
|
||||
File: 'example.cpp',
|
||||
Line: 6,
|
||||
Column: 0,
|
||||
},
|
||||
Function: 'int f()',
|
||||
Qualifier: 'dynamic',
|
||||
displayString: '32 bytes, dynamic',
|
||||
},
|
||||
{
|
||||
BytesUsed: 64,
|
||||
DebugLoc: {
|
||||
File: 'example.cpp',
|
||||
Line: 7,
|
||||
Column: 0,
|
||||
},
|
||||
Function: 'int h()',
|
||||
Qualifier: 'dynamic,bounded',
|
||||
displayString: '64 bytes, dynamic,bounded',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -26,6 +26,7 @@ import {BuildEnvDownloadInfo} from '../../lib/buildenvsetup/buildenv.interfaces.
|
||||
import {IAsmParser} from '../../lib/parsers/asm-parser.interfaces.js';
|
||||
import type {GccDumpViewSelectedPass} from '../../static/panes/gccdump-view.interfaces.js';
|
||||
import type {PPOptions} from '../../static/panes/pp-view.interfaces.js';
|
||||
import {suCodeEntry} from '../../static/panes/stack-usage-view.interfaces.js';
|
||||
import {CompilerInfo} from '../compiler.interfaces.js';
|
||||
import {BasicExecutionResult} from '../execution/execution.interfaces.js';
|
||||
import {ParseFiltersAndOutputOptions} from '../features/filters.interfaces.js';
|
||||
@@ -68,6 +69,7 @@ export type CompilationRequestOptions = {
|
||||
dumpFlags: any;
|
||||
};
|
||||
produceOptInfo?: boolean;
|
||||
produceStackUsageInfo?: boolean;
|
||||
produceCfg?: boolean;
|
||||
produceGnatDebugTree?: boolean;
|
||||
produceGnatDebug?: boolean;
|
||||
@@ -155,6 +157,10 @@ export type CompilationResult = {
|
||||
optOutput?: any;
|
||||
optPath?: string;
|
||||
|
||||
hasStackUsageOutput?: boolean;
|
||||
stackUsageOutput?: suCodeEntry[];
|
||||
stackUsagePath?: string;
|
||||
|
||||
hasAstOutput?: boolean;
|
||||
astOutput?: any;
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ export type CompilerInfo = {
|
||||
supportsGccDump?: boolean;
|
||||
supportsFiltersInBinary?: boolean;
|
||||
supportsOptOutput?: boolean;
|
||||
supportsStackUsageOutput?: boolean;
|
||||
supportsPpView?: boolean;
|
||||
supportsAstView?: boolean;
|
||||
supportsIrView?: boolean;
|
||||
@@ -123,6 +124,7 @@ export type CompilerInfo = {
|
||||
possibleOverrides?: AllCompilerOverrideOptions;
|
||||
disabledFilters: string[];
|
||||
optArg?: string;
|
||||
stackUsageArg?: string;
|
||||
externalparser: any;
|
||||
removeEmptyGccDump?: boolean;
|
||||
irArg?: string[];
|
||||
|
||||
@@ -55,6 +55,7 @@ mixin newPaneButton(classId, text, title, icon)
|
||||
+newPaneButton("add-compiler", "Clone Compiler", "Clone this compiler window (click or drag)", "far fa-clone")
|
||||
+newPaneButton("create-executor", "Executor From This", "Create executor from this compiler", "fas fa-microchip")
|
||||
+newPaneButton("view-optimization", "Optimization", "Show optimization output", "fas fa-weight")
|
||||
+newPaneButton("view-stack-usage", "Stack Usage", "Show stack usage", "fas fa-layer-group")
|
||||
+newPaneButton("view-pp", "Preprocessor", "Show preprocessor output", "fas fa-hashtag")
|
||||
+newPaneButton("view-ast", "AST", "Show AST output", "fas fa-leaf")
|
||||
+newPaneButton("view-ir", "LLVM IR", "Show LLVM Intermediate Representation", "fas fa-align-center")
|
||||
|
||||
@@ -54,6 +54,8 @@ mixin monacopane(id)
|
||||
|
||||
include widgets/overrides-favorite-tpl
|
||||
|
||||
+monacopane("stackusage")
|
||||
|
||||
+monacopane("opt")
|
||||
|
||||
+monacopane("flags")
|
||||
|
||||
Reference in New Issue
Block a user