mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Add support for Yul intermediate view when compiling Solidity (#8219)
## What Adds support for seeing Yul (Solidity IR) as intermediate output when compiling Solidity. This PR also enables that view for the Resolc compiler. ### Main Additions - [x] Support viewing Yul in a supplementary view - Solidity compilers can enable this by setting `this.compiler.supportsYulView = true` in the compiler's constructor - If custom processing of the Yul output or the Yul output filename is needed, the compiler can override `processYulOutput()` or `getYulOutputFilename()` - [x] Enable the Yul view for Resolc - [x] Implement a Yul backend option for filtering out debug info from the output ### Notes Source mappings are currently not handled for Yul -> Solidity. ## Overall Usage ### Steps * Choose Solidity as the language * Choose a Resolc compiler * View intermediate results: * Yul * (Hide/show debug info by toggling "Hide Debug Info" in the Yul view filters) ## Screenshots <img width="1502" height="903" alt="ce-yul-view" src="https://github.com/user-attachments/assets/ccc897e2-cd8d-4c33-962c-522d60b63134" />
This commit is contained in:
@@ -15,6 +15,7 @@ const PANE_DATA_MAP = {
|
|||||||
core: {name: 'Core', selector: 'view-haskellCore'},
|
core: {name: 'Core', selector: 'view-haskellCore'},
|
||||||
stg: {name: 'STG', selector: 'view-haskellStg'},
|
stg: {name: 'STG', selector: 'view-haskellStg'},
|
||||||
cmm: {name: 'Cmm', selector: 'view-haskellCmm'},
|
cmm: {name: 'Cmm', selector: 'view-haskellCmm'},
|
||||||
|
yul: {name: 'Yul', selector: 'view-yul'},
|
||||||
// TODO find a way to properly hack the state URL to test this pane like the rust
|
// TODO find a way to properly hack the state URL to test this pane like the rust
|
||||||
// ones seem to be able to do.
|
// ones seem to be able to do.
|
||||||
// clojure_macro: {name: 'Clojure Macro', selector: 'view-clojuremacroexp'},
|
// clojure_macro: {name: 'Clojure Macro', selector: 'view-clojuremacroexp'},
|
||||||
@@ -68,6 +69,7 @@ describe('Individual pane testing', () => {
|
|||||||
addPaneOpenTest(PANE_DATA_MAP.core);
|
addPaneOpenTest(PANE_DATA_MAP.core);
|
||||||
addPaneOpenTest(PANE_DATA_MAP.stg);
|
addPaneOpenTest(PANE_DATA_MAP.stg);
|
||||||
addPaneOpenTest(PANE_DATA_MAP.cmm);
|
addPaneOpenTest(PANE_DATA_MAP.cmm);
|
||||||
|
addPaneOpenTest(PANE_DATA_MAP.yul);
|
||||||
addPaneOpenTest(PANE_DATA_MAP.dump);
|
addPaneOpenTest(PANE_DATA_MAP.dump);
|
||||||
addPaneOpenTest(PANE_DATA_MAP.tree);
|
addPaneOpenTest(PANE_DATA_MAP.tree);
|
||||||
addPaneOpenTest(PANE_DATA_MAP.debug);
|
addPaneOpenTest(PANE_DATA_MAP.debug);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import type {
|
|||||||
OptPipelineBackendOptions,
|
OptPipelineBackendOptions,
|
||||||
OptPipelineOutput,
|
OptPipelineOutput,
|
||||||
} from '../types/compilation/opt-pipeline-output.interfaces.js';
|
} from '../types/compilation/opt-pipeline-output.interfaces.js';
|
||||||
|
import type {YulBackendOptions} from '../types/compilation/yul.interfaces.js';
|
||||||
import type {CompilerInfo, PreliminaryCompilerInfo} from '../types/compiler.interfaces.js';
|
import type {CompilerInfo, PreliminaryCompilerInfo} from '../types/compiler.interfaces.js';
|
||||||
import {
|
import {
|
||||||
BasicExecutionResult,
|
BasicExecutionResult,
|
||||||
@@ -1628,6 +1629,10 @@ export class BaseCompiler {
|
|||||||
return utils.changeExtension(inputFilename, '.dump-cmm');
|
return utils.changeExtension(inputFilename, '.dump-cmm');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getYulOutputFilename(defaultOutputFilename: string) {
|
||||||
|
return utils.changeExtension(defaultOutputFilename, '.yul');
|
||||||
|
}
|
||||||
|
|
||||||
// Currently called for getting macro expansion and HIR.
|
// Currently called for getting macro expansion and HIR.
|
||||||
// It returns the content of the output file created after using -Z unpretty=<unprettyOpt>.
|
// It returns the content of the output file created after using -Z unpretty=<unprettyOpt>.
|
||||||
// The outputFriendlyName is a free form string used in case of error.
|
// The outputFriendlyName is a free form string used in case of error.
|
||||||
@@ -1706,6 +1711,32 @@ export class BaseCompiler {
|
|||||||
return [{text: 'Internal error; unable to open output path'}];
|
return [{text: 'Internal error; unable to open output path'}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async processYulOutput(
|
||||||
|
defaultOutputFilename: string,
|
||||||
|
result: CompilationResult,
|
||||||
|
yulOptions: YulBackendOptions,
|
||||||
|
): Promise<ResultLine[]> {
|
||||||
|
if (result.code !== 0) {
|
||||||
|
return [{text: 'Failed to run compiler to get Yul intermediary output'}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputFilename = this.getYulOutputFilename(defaultOutputFilename);
|
||||||
|
if (await utils.fileExists(outputFilename)) {
|
||||||
|
const content = await fs.readFile(outputFilename, 'utf8');
|
||||||
|
const result: ResultLine[] = content.split('\n').map(line => ({text: line}));
|
||||||
|
const filters: RegExp[] = [];
|
||||||
|
|
||||||
|
if (yulOptions.filterDebugInfo) {
|
||||||
|
const debugInfoRe = /^\s*\/\/\/ @(use-src|src|ast-id)/;
|
||||||
|
filters.push(debugInfoRe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.filter(line => filters.every(re => !line.text.match(re)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{text: 'Internal error: Unable to open output path'}];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the LLVM IR output filename.
|
* Get the LLVM IR output filename.
|
||||||
*
|
*
|
||||||
@@ -2447,6 +2478,7 @@ export class BaseCompiler {
|
|||||||
const makeHaskellStg = backendOptions.produceHaskellStg && this.compiler.supportsHaskellStgView;
|
const makeHaskellStg = backendOptions.produceHaskellStg && this.compiler.supportsHaskellStgView;
|
||||||
const makeHaskellCmm = backendOptions.produceHaskellCmm && this.compiler.supportsHaskellCmmView;
|
const makeHaskellCmm = backendOptions.produceHaskellCmm && this.compiler.supportsHaskellCmmView;
|
||||||
const makeGccDump = backendOptions.produceGccDump?.opened && this.compiler.supportsGccDump;
|
const makeGccDump = backendOptions.produceGccDump?.opened && this.compiler.supportsGccDump;
|
||||||
|
const makeYul = backendOptions.produceYul && this.compiler.supportsYulView;
|
||||||
|
|
||||||
const [
|
const [
|
||||||
asmResult,
|
asmResult,
|
||||||
@@ -2513,6 +2545,10 @@ export class BaseCompiler {
|
|||||||
? await this.processHaskellExtraOutput(this.getHaskellCmmOutputFilename(inputFilename), asmResult)
|
? await this.processHaskellExtraOutput(this.getHaskellCmmOutputFilename(inputFilename), asmResult)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const yulResult = makeYul
|
||||||
|
? await this.processYulOutput(outputFilename, asmResult, backendOptions.produceYul)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
asmResult.dirPath = dirPath;
|
asmResult.dirPath = dirPath;
|
||||||
if (!asmResult.compilationOptions) asmResult.compilationOptions = options;
|
if (!asmResult.compilationOptions) asmResult.compilationOptions = options;
|
||||||
asmResult.downloads = downloads;
|
asmResult.downloads = downloads;
|
||||||
@@ -2565,6 +2601,8 @@ export class BaseCompiler {
|
|||||||
|
|
||||||
asmResult.clojureMacroExpOutput = clojureMacroExpResult;
|
asmResult.clojureMacroExpOutput = clojureMacroExpResult;
|
||||||
|
|
||||||
|
asmResult.yulOutput = yulResult;
|
||||||
|
|
||||||
if (asmResult.code !== 0) {
|
if (asmResult.code !== 0) {
|
||||||
return [{...asmResult, asm: '<Compilation failed>'}, [], []];
|
return [{...asmResult, asm: '<Compilation failed>'}, [], []];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export class ResolcCompiler extends BaseCompiler {
|
|||||||
this.compiler.irArg = [];
|
this.compiler.irArg = [];
|
||||||
this.compiler.supportsIrView = true;
|
this.compiler.supportsIrView = true;
|
||||||
this.compiler.supportsIrViewOptToggleOption = true;
|
this.compiler.supportsIrViewOptToggleOption = true;
|
||||||
|
this.compiler.supportsYulView = this.inputIs(InputKind.Solidity);
|
||||||
}
|
}
|
||||||
|
|
||||||
override getSharedLibraryPathsAsArguments(): string[] {
|
override getSharedLibraryPathsAsArguments(): string[] {
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const RUST_HIR_VIEW_COMPONENT_NAME = 'rusthir' as const;
|
|||||||
export const CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME = 'clojuremacroexp' as const;
|
export const CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME = 'clojuremacroexp' as const;
|
||||||
export const DEVICE_VIEW_COMPONENT_NAME = 'device' as const;
|
export const DEVICE_VIEW_COMPONENT_NAME = 'device' as const;
|
||||||
export const EXPLAIN_VIEW_COMPONENT_NAME = 'explain' as const;
|
export const EXPLAIN_VIEW_COMPONENT_NAME = 'explain' as const;
|
||||||
|
export const YUL_VIEW_COMPONENT_NAME = 'yul' as const;
|
||||||
|
|
||||||
export type StateWithLanguage = {lang: string};
|
export type StateWithLanguage = {lang: string};
|
||||||
// TODO(#7808): Normalize state types to reduce duplication (see #4490)
|
// TODO(#7808): Normalize state types to reduce duplication (see #4490)
|
||||||
@@ -356,6 +357,15 @@ export type PopulatedExplainViewState = StateWithId & {
|
|||||||
treeid: number;
|
treeid: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EmptyYulViewState = EmptyState;
|
||||||
|
export type PopulatedYulViewState = StateWithId & {
|
||||||
|
source: string;
|
||||||
|
yulOutput: unknown;
|
||||||
|
compilerName: string;
|
||||||
|
editorid: number;
|
||||||
|
treeid: number;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping of component names to their expected state types. This provides compile-time type safety for component
|
* Mapping of component names to their expected state types. This provides compile-time type safety for component
|
||||||
* states. Components can have either empty (default) or populated states.
|
* states. Components can have either empty (default) or populated states.
|
||||||
@@ -392,6 +402,7 @@ export interface ComponentStateMap {
|
|||||||
[CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyClojureMacroExpViewState | PopulatedClojureMacroExpViewState;
|
[CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME]: EmptyClojureMacroExpViewState | PopulatedClojureMacroExpViewState;
|
||||||
[DEVICE_VIEW_COMPONENT_NAME]: EmptyDeviceViewState | PopulatedDeviceViewState;
|
[DEVICE_VIEW_COMPONENT_NAME]: EmptyDeviceViewState | PopulatedDeviceViewState;
|
||||||
[EXPLAIN_VIEW_COMPONENT_NAME]: EmptyExplainViewState | PopulatedExplainViewState;
|
[EXPLAIN_VIEW_COMPONENT_NAME]: EmptyExplainViewState | PopulatedExplainViewState;
|
||||||
|
[YUL_VIEW_COMPONENT_NAME]: EmptyYulViewState | PopulatedYulViewState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import {
|
|||||||
TOOL_COMPONENT_NAME,
|
TOOL_COMPONENT_NAME,
|
||||||
TOOL_INPUT_VIEW_COMPONENT_NAME,
|
TOOL_INPUT_VIEW_COMPONENT_NAME,
|
||||||
TREE_COMPONENT_NAME,
|
TREE_COMPONENT_NAME,
|
||||||
|
YUL_VIEW_COMPONENT_NAME,
|
||||||
} from './components.interfaces.js';
|
} from './components.interfaces.js';
|
||||||
import {GccDumpViewState} from './panes/gccdump-view.interfaces.js';
|
import {GccDumpViewState} from './panes/gccdump-view.interfaces.js';
|
||||||
import {SentryCapture} from './sentry.js';
|
import {SentryCapture} from './sentry.js';
|
||||||
@@ -782,6 +783,38 @@ export function getHaskellCmmViewWith(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get an empty Yul view component. */
|
||||||
|
export function getYulView(): ComponentConfig<typeof YUL_VIEW_COMPONENT_NAME> {
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
componentName: YUL_VIEW_COMPONENT_NAME,
|
||||||
|
componentState: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a Yul view with the given configuration. */
|
||||||
|
export function getYulViewWith(
|
||||||
|
id: number,
|
||||||
|
source: string,
|
||||||
|
yulOutput: unknown,
|
||||||
|
compilerName: string,
|
||||||
|
editorid: number,
|
||||||
|
treeid: number,
|
||||||
|
): ComponentConfig<typeof YUL_VIEW_COMPONENT_NAME> {
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
componentName: YUL_VIEW_COMPONENT_NAME,
|
||||||
|
componentState: {
|
||||||
|
id,
|
||||||
|
source,
|
||||||
|
yulOutput,
|
||||||
|
compilerName,
|
||||||
|
editorid,
|
||||||
|
treeid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Get an empty gnat debug tree view component. */
|
/** Get an empty gnat debug tree view component. */
|
||||||
export function getGnatDebugTreeView(): ComponentConfig<typeof GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME> {
|
export function getGnatDebugTreeView(): ComponentConfig<typeof GNAT_DEBUG_TREE_VIEW_COMPONENT_NAME> {
|
||||||
return {
|
return {
|
||||||
@@ -1233,6 +1266,7 @@ function validateComponentState(componentName: string, state: any): boolean {
|
|||||||
case RUST_HIR_VIEW_COMPONENT_NAME:
|
case RUST_HIR_VIEW_COMPONENT_NAME:
|
||||||
case CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME:
|
case CLOJURE_MACRO_EXP_VIEW_COMPONENT_NAME:
|
||||||
case DEVICE_VIEW_COMPONENT_NAME:
|
case DEVICE_VIEW_COMPONENT_NAME:
|
||||||
|
case YUL_VIEW_COMPONENT_NAME:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {ClangirBackendOptions} from '../types/compilation/clangir.interfaces.js'
|
|||||||
import {CompilationResult} from '../types/compilation/compilation.interfaces.js';
|
import {CompilationResult} from '../types/compilation/compilation.interfaces.js';
|
||||||
import {LLVMIrBackendOptions} from '../types/compilation/ir.interfaces.js';
|
import {LLVMIrBackendOptions} from '../types/compilation/ir.interfaces.js';
|
||||||
import {OptPipelineBackendOptions} from '../types/compilation/opt-pipeline-output.interfaces.js';
|
import {OptPipelineBackendOptions} from '../types/compilation/opt-pipeline-output.interfaces.js';
|
||||||
|
import {YulBackendOptions} from '../types/compilation/yul.interfaces.js';
|
||||||
import {CompilerInfo} from '../types/compiler.interfaces.js';
|
import {CompilerInfo} from '../types/compiler.interfaces.js';
|
||||||
import {Language, LanguageKey} from '../types/languages.interfaces.js';
|
import {Language, LanguageKey} from '../types/languages.interfaces.js';
|
||||||
import {MessageWithLocation} from '../types/resultline/resultline.interfaces.js';
|
import {MessageWithLocation} from '../types/resultline/resultline.interfaces.js';
|
||||||
@@ -160,6 +161,9 @@ export type EventMap = {
|
|||||||
rustMirViewOpened: (compilerId: number) => void;
|
rustMirViewOpened: (compilerId: number) => void;
|
||||||
clojureMacroExpViewClosed: (compilerId: number) => void;
|
clojureMacroExpViewClosed: (compilerId: number) => void;
|
||||||
clojureMacroExpViewOpened: (compilerId: number) => void;
|
clojureMacroExpViewOpened: (compilerId: number) => void;
|
||||||
|
yulViewClosed: (compilerId: number) => void;
|
||||||
|
yulViewOpened: (compilerId: number) => void;
|
||||||
|
yulViewOptionsUpdated: (compilerId: number, options: YulBackendOptions, recompile: boolean) => void;
|
||||||
// TODO: There are no emitters for this event
|
// TODO: There are no emitters for this event
|
||||||
selectLine: (editorId: number, lineNumber: number) => void;
|
selectLine: (editorId: number, lineNumber: number) => void;
|
||||||
settingsChange: (newSettings: SiteSettings) => void;
|
settingsChange: (newSettings: SiteSettings) => void;
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import {
|
|||||||
TOOL_COMPONENT_NAME,
|
TOOL_COMPONENT_NAME,
|
||||||
TOOL_INPUT_VIEW_COMPONENT_NAME,
|
TOOL_INPUT_VIEW_COMPONENT_NAME,
|
||||||
TREE_COMPONENT_NAME,
|
TREE_COMPONENT_NAME,
|
||||||
|
YUL_VIEW_COMPONENT_NAME,
|
||||||
} from './components.interfaces.js';
|
} from './components.interfaces.js';
|
||||||
import {EventHub} from './event-hub.js';
|
import {EventHub} from './event-hub.js';
|
||||||
import {EventMap} from './event-map.js';
|
import {EventMap} from './event-map.js';
|
||||||
@@ -93,6 +94,7 @@ import {StackUsage as StackUsageView} from './panes/stack-usage-view.js';
|
|||||||
import {Tool} from './panes/tool.js';
|
import {Tool} from './panes/tool.js';
|
||||||
import {ToolInputView} from './panes/tool-input-view.js';
|
import {ToolInputView} from './panes/tool-input-view.js';
|
||||||
import {Tree, TreeState} from './panes/tree.js';
|
import {Tree, TreeState} from './panes/tree.js';
|
||||||
|
import {Yul as YulView} from './panes/yul-view.js';
|
||||||
|
|
||||||
type GLC = GoldenLayout.Container;
|
type GLC = GoldenLayout.Container;
|
||||||
|
|
||||||
@@ -173,6 +175,7 @@ export class Hub {
|
|||||||
this.conformanceViewFactory(c, s),
|
this.conformanceViewFactory(c, s),
|
||||||
);
|
);
|
||||||
layout.registerComponent(EXPLAIN_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.explainViewFactory(c, s));
|
layout.registerComponent(EXPLAIN_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.explainViewFactory(c, s));
|
||||||
|
layout.registerComponent(YUL_VIEW_COMPONENT_NAME, (c: GLC, s: any) => this.yulViewFactory(c, s));
|
||||||
|
|
||||||
layout.eventHub.on(
|
layout.eventHub.on(
|
||||||
'editorOpen',
|
'editorOpen',
|
||||||
@@ -604,4 +607,8 @@ export class Hub {
|
|||||||
public explainViewFactory(container: GoldenLayout.Container, state: any): ExplainView {
|
public explainViewFactory(container: GoldenLayout.Container, state: any): ExplainView {
|
||||||
return new ExplainView(this, container, state);
|
return new ExplainView(this, container, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public yulViewFactory(container: GoldenLayout.Container, state: InferComponentState<YulView>): YulView {
|
||||||
|
return new YulView(this, container, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import fileSaver from 'file-saver';
|
|||||||
import {escapeHTML, splitArguments} from '../../shared/common-utils.js';
|
import {escapeHTML, splitArguments} from '../../shared/common-utils.js';
|
||||||
import {ClangirBackendOptions} from '../../types/compilation/clangir.interfaces.js';
|
import {ClangirBackendOptions} from '../../types/compilation/clangir.interfaces.js';
|
||||||
import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js';
|
import {LLVMIrBackendOptions} from '../../types/compilation/ir.interfaces.js';
|
||||||
|
import {YulBackendOptions} from '../../types/compilation/yul.interfaces.js';
|
||||||
import {CompilerOutputOptions} from '../../types/features/filters.interfaces.js';
|
import {CompilerOutputOptions} from '../../types/features/filters.interfaces.js';
|
||||||
import {InstructionSet} from '../../types/instructionsets.js';
|
import {InstructionSet} from '../../types/instructionsets.js';
|
||||||
import {LanguageKey} from '../../types/languages.interfaces.js';
|
import {LanguageKey} from '../../types/languages.interfaces.js';
|
||||||
@@ -194,6 +195,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
private haskellStgButton: JQuery<HTMLButtonElement>;
|
private haskellStgButton: JQuery<HTMLButtonElement>;
|
||||||
private haskellCmmButton: JQuery<HTMLButtonElement>;
|
private haskellCmmButton: JQuery<HTMLButtonElement>;
|
||||||
private clojureMacroExpButton: JQuery<HTMLButtonElement>;
|
private clojureMacroExpButton: JQuery<HTMLButtonElement>;
|
||||||
|
private yulButton: JQuery<HTMLButtonElement>;
|
||||||
private gccDumpButton: JQuery<HTMLButtonElement>;
|
private gccDumpButton: JQuery<HTMLButtonElement>;
|
||||||
private cfgButton: JQuery<HTMLButtonElement>;
|
private cfgButton: JQuery<HTMLButtonElement>;
|
||||||
private explainButton: JQuery<HTMLButtonElement>;
|
private explainButton: JQuery<HTMLButtonElement>;
|
||||||
@@ -271,9 +273,11 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
private haskellStgViewOpen: boolean;
|
private haskellStgViewOpen: boolean;
|
||||||
private haskellCmmViewOpen: boolean;
|
private haskellCmmViewOpen: boolean;
|
||||||
private clojureMacroExpViewOpen: boolean;
|
private clojureMacroExpViewOpen: boolean;
|
||||||
|
private yulViewOpen: boolean;
|
||||||
private ppOptions: PPOptions;
|
private ppOptions: PPOptions;
|
||||||
private llvmIrOptions: LLVMIrBackendOptions;
|
private llvmIrOptions: LLVMIrBackendOptions;
|
||||||
private clangirOptions: ClangirBackendOptions;
|
private clangirOptions: ClangirBackendOptions;
|
||||||
|
private yulOptions: YulBackendOptions;
|
||||||
private optPipelineOptions: OptPipelineBackendOptions;
|
private optPipelineOptions: OptPipelineBackendOptions;
|
||||||
private isOutputOpened: boolean;
|
private isOutputOpened: boolean;
|
||||||
private mouseMoveThrottledFunction?: ((e: monaco.editor.IEditorMouseEvent) => void) & _.Cancelable;
|
private mouseMoveThrottledFunction?: ((e: monaco.editor.IEditorMouseEvent) => void) & _.Cancelable;
|
||||||
@@ -636,6 +640,17 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createYulView = () => {
|
||||||
|
return Components.getYulViewWith(
|
||||||
|
this.id,
|
||||||
|
this.source,
|
||||||
|
this.lastResult?.yulOutput,
|
||||||
|
this.getCompilerName(),
|
||||||
|
this.sourceEditorId ?? 0,
|
||||||
|
this.sourceTreeId ?? 0,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const createGccDumpView = () => {
|
const createGccDumpView = () => {
|
||||||
return Components.getGccDumpViewWith(
|
return Components.getGccDumpViewWith(
|
||||||
this.id,
|
this.id,
|
||||||
@@ -920,6 +935,18 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
insertPoint.addChild(createClojureMacroExpView());
|
insertPoint.addChild(createClojureMacroExpView());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createDragSource(this.container.layoutManager, this.yulButton, () => createYulView()).on(
|
||||||
|
'dragStart',
|
||||||
|
hidePaneAdder,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.yulButton.on('click', () => {
|
||||||
|
const insertPoint =
|
||||||
|
this.hub.findParentRowOrColumn(this.container.parent) ||
|
||||||
|
this.container.layoutManager.root.contentItems[0];
|
||||||
|
insertPoint.addChild(createYulView());
|
||||||
|
});
|
||||||
|
|
||||||
createDragSource(this.container.layoutManager, this.gccDumpButton, () => createGccDumpView()).on(
|
createDragSource(this.container.layoutManager, this.gccDumpButton, () => createGccDumpView()).on(
|
||||||
'dragStart',
|
'dragStart',
|
||||||
hidePaneAdder,
|
hidePaneAdder,
|
||||||
@@ -1316,6 +1343,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
produceHaskellStg: this.haskellStgViewOpen,
|
produceHaskellStg: this.haskellStgViewOpen,
|
||||||
produceHaskellCmm: this.haskellCmmViewOpen,
|
produceHaskellCmm: this.haskellCmmViewOpen,
|
||||||
produceClojureMacroExp: this.clojureMacroExpViewOpen,
|
produceClojureMacroExp: this.clojureMacroExpViewOpen,
|
||||||
|
produceYul: this.yulViewOpen ? this.yulOptions : null,
|
||||||
overrides: this.getCurrentState().overrides,
|
overrides: this.getCurrentState().overrides,
|
||||||
},
|
},
|
||||||
filters: this.getEffectiveFilters(),
|
filters: this.getEffectiveFilters(),
|
||||||
@@ -2226,6 +2254,30 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onYulViewOpened(id: number): void {
|
||||||
|
if (this.id === id) {
|
||||||
|
this.yulButton.prop('disabled', true);
|
||||||
|
this.yulViewOpen = true;
|
||||||
|
this.compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onYulViewClosed(id: number): void {
|
||||||
|
if (this.id === id) {
|
||||||
|
this.yulButton.prop('disabled', false);
|
||||||
|
this.yulViewOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onYulViewOptionsUpdated(id: number, options: YulBackendOptions, recompile: boolean): void {
|
||||||
|
if (this.id === id) {
|
||||||
|
this.yulOptions = options;
|
||||||
|
if (recompile) {
|
||||||
|
this.compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onGccDumpUIInit(id: number): void {
|
onGccDumpUIInit(id: number): void {
|
||||||
if (this.id === id) {
|
if (this.id === id) {
|
||||||
this.compile();
|
this.compile();
|
||||||
@@ -2415,6 +2467,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg');
|
this.haskellStgButton = this.domRoot.find('.btn.view-haskellStg');
|
||||||
this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm');
|
this.haskellCmmButton = this.domRoot.find('.btn.view-haskellCmm');
|
||||||
this.clojureMacroExpButton = this.domRoot.find('.btn.view-clojuremacroexp');
|
this.clojureMacroExpButton = this.domRoot.find('.btn.view-clojuremacroexp');
|
||||||
|
this.yulButton = this.domRoot.find('.btn.view-yul');
|
||||||
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
|
this.gccDumpButton = this.domRoot.find('.btn.view-gccdump');
|
||||||
this.cfgButton = this.domRoot.find('.btn.view-cfg');
|
this.cfgButton = this.domRoot.find('.btn.view-cfg');
|
||||||
this.explainButton = this.domRoot.find('.btn.view-explain');
|
this.explainButton = this.domRoot.find('.btn.view-explain');
|
||||||
@@ -2699,6 +2752,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen);
|
this.rustMacroExpButton.prop('disabled', this.rustMacroExpViewOpen);
|
||||||
this.rustHirButton.prop('disabled', this.rustHirViewOpen);
|
this.rustHirButton.prop('disabled', this.rustHirViewOpen);
|
||||||
this.clojureMacroExpButton.prop('disabled', this.clojureMacroExpViewOpen);
|
this.clojureMacroExpButton.prop('disabled', this.clojureMacroExpViewOpen);
|
||||||
|
this.yulButton.prop('disabled', this.yulViewOpen);
|
||||||
this.gccDumpButton.prop('disabled', this.gccDumpViewOpen);
|
this.gccDumpButton.prop('disabled', this.gccDumpViewOpen);
|
||||||
this.gnatDebugTreeButton.prop('disabled', this.gnatDebugTreeViewOpen);
|
this.gnatDebugTreeButton.prop('disabled', this.gnatDebugTreeViewOpen);
|
||||||
this.gnatDebugButton.prop('disabled', this.gnatDebugViewOpen);
|
this.gnatDebugButton.prop('disabled', this.gnatDebugViewOpen);
|
||||||
@@ -2720,6 +2774,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView);
|
this.haskellStgButton.toggle(!!this.compiler.supportsHaskellStgView);
|
||||||
this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView);
|
this.haskellCmmButton.toggle(!!this.compiler.supportsHaskellCmmView);
|
||||||
this.clojureMacroExpButton.toggle(!!this.compiler.supportsClojureMacroExpView);
|
this.clojureMacroExpButton.toggle(!!this.compiler.supportsClojureMacroExpView);
|
||||||
|
this.yulButton.toggle(!!this.compiler.supportsYulView);
|
||||||
// TODO(jeremy-rifkin): Disable cfg button when binary mode is set?
|
// TODO(jeremy-rifkin): Disable cfg button when binary mode is set?
|
||||||
this.cfgButton.toggle(!!this.compiler.supportsCfg);
|
this.cfgButton.toggle(!!this.compiler.supportsCfg);
|
||||||
this.gccDumpButton.toggle(!!this.compiler.supportsGccDump);
|
this.gccDumpButton.toggle(!!this.compiler.supportsGccDump);
|
||||||
@@ -2898,6 +2953,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
|
|||||||
this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this);
|
this.eventHub.on('haskellCmmViewClosed', this.onHaskellCmmViewClosed, this);
|
||||||
this.eventHub.on('clojureMacroExpViewOpened', this.onClojureMacroExpViewOpened, this);
|
this.eventHub.on('clojureMacroExpViewOpened', this.onClojureMacroExpViewOpened, this);
|
||||||
this.eventHub.on('clojureMacroExpViewClosed', this.onClojureMacroExpViewClosed, this);
|
this.eventHub.on('clojureMacroExpViewClosed', this.onClojureMacroExpViewClosed, this);
|
||||||
|
this.eventHub.on('yulViewOpened', this.onYulViewOpened, this);
|
||||||
|
this.eventHub.on('yulViewClosed', this.onYulViewClosed, this);
|
||||||
|
this.eventHub.on('yulViewOptionsUpdated', this.onYulViewOptionsUpdated, this);
|
||||||
this.eventHub.on('outputOpened', this.onOutputOpened, this);
|
this.eventHub.on('outputOpened', this.onOutputOpened, this);
|
||||||
this.eventHub.on('outputClosed', this.onOutputClosed, this);
|
this.eventHub.on('outputClosed', this.onOutputClosed, this);
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export enum DiffType {
|
|||||||
RustMacroExpOutput = 11,
|
RustMacroExpOutput = 11,
|
||||||
RustHirOutput = 12,
|
RustHirOutput = 12,
|
||||||
ClojureMacroExpOutput = 13,
|
ClojureMacroExpOutput = 13,
|
||||||
|
YulOutput = 14,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DiffState = {
|
export type DiffState = {
|
||||||
|
|||||||
@@ -175,6 +175,11 @@ class DiffStateObject {
|
|||||||
{text: "<select 'Add new...' → 'Clojure Macro Expansion' in this compiler's pane>"},
|
{text: "<select 'Add new...' → 'Clojure Macro Expansion' in this compiler's pane>"},
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
case DiffType.YulOutput:
|
||||||
|
output = this.result.yulOutput || [
|
||||||
|
{text: "<select 'Add new...' → 'Yul (Solidity IR)' in this compiler's pane>"},
|
||||||
|
];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.model.setValue(output.map(x => x.text).join('\n'));
|
this.model.setValue(output.map(x => x.text).join('\n'));
|
||||||
@@ -481,6 +486,9 @@ export class Diff extends MonacoPane<monaco.editor.IStandaloneDiffEditor, DiffSt
|
|||||||
if (compiler.supportsClojureMacroExpView) {
|
if (compiler.supportsClojureMacroExpView) {
|
||||||
options.push({id: DiffType.ClojureMacroExpOutput.toString(), name: 'Clojure Macro Expansion'});
|
options.push({id: DiffType.ClojureMacroExpOutput.toString(), name: 'Clojure Macro Expansion'});
|
||||||
}
|
}
|
||||||
|
if (compiler.supportsYulView) {
|
||||||
|
options.push({id: DiffType.YulOutput.toString(), name: 'Yul (Solidity IR)'});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lhsoptions = this.getDiffableOptions(this.selectize.lhs, lhsextraoptions);
|
const lhsoptions = this.getDiffableOptions(this.selectize.lhs, lhsextraoptions);
|
||||||
|
|||||||
30
static/panes/yul-view.interfaces.ts
Normal file
30
static/panes/yul-view.interfaces.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2025, 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 YulState {
|
||||||
|
yulOutput: any;
|
||||||
|
|
||||||
|
// Options and filters.
|
||||||
|
'filter-debug-info'?: boolean;
|
||||||
|
}
|
||||||
175
static/panes/yul-view.ts
Normal file
175
static/panes/yul-view.ts
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
// Copyright (c) 2025, 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 {Container} from 'golden-layout';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import _ from 'underscore';
|
||||||
|
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
|
||||||
|
import {YulBackendOptions} from '../../types/compilation/yul.interfaces.js';
|
||||||
|
import {CompilerInfo} from '../../types/compiler.interfaces.js';
|
||||||
|
import {Hub} from '../hub.js';
|
||||||
|
import {extendConfig} from '../monaco-config.js';
|
||||||
|
import {Toggles} from '../widgets/toggles.js';
|
||||||
|
import {MonacoPaneState} from './pane.interfaces.js';
|
||||||
|
import {MonacoPane} from './pane.js';
|
||||||
|
import {YulState} from './yul-view.interfaces.js';
|
||||||
|
|
||||||
|
export class Yul extends MonacoPane<monaco.editor.IStandaloneCodeEditor, YulState> {
|
||||||
|
private filters: Toggles;
|
||||||
|
private lastOptions: YulBackendOptions = {
|
||||||
|
filterDebugInfo: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(hub: Hub, container: Container, state: YulState & MonacoPaneState) {
|
||||||
|
super(hub, container, state);
|
||||||
|
if (state.yulOutput) {
|
||||||
|
this.showYulResults(state.yulOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onOptionsChange(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getInitialHTML(): string {
|
||||||
|
return $('#yul').html();
|
||||||
|
}
|
||||||
|
|
||||||
|
override createEditor(editorRoot: HTMLElement): void {
|
||||||
|
this.editor = monaco.editor.create(
|
||||||
|
editorRoot,
|
||||||
|
extendConfig({
|
||||||
|
language: 'yul',
|
||||||
|
readOnly: true,
|
||||||
|
glyphMargin: true,
|
||||||
|
lineNumbersMinChars: 3,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getPrintName() {
|
||||||
|
return 'Yul Output';
|
||||||
|
}
|
||||||
|
|
||||||
|
override getDefaultPaneName(): string {
|
||||||
|
return 'Yul (Solidity IR) Viewer';
|
||||||
|
}
|
||||||
|
|
||||||
|
override registerButtons(state: YulState): void {
|
||||||
|
super.registerButtons(state);
|
||||||
|
this.filters = new Toggles(this.domRoot.find('.filters'), state as unknown as Record<string, boolean>);
|
||||||
|
this.filters.on('change', () => this.onOptionsChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtons(compiler: CompilerInfo | null): void {
|
||||||
|
this.filters.enableToggle('filter-debug-info', !!compiler?.supportsYulView);
|
||||||
|
}
|
||||||
|
|
||||||
|
override registerCallbacks(): void {
|
||||||
|
const throttleFunction = _.throttle(
|
||||||
|
(event: monaco.editor.ICursorSelectionChangedEvent) => this.onDidChangeCursorSelection(event),
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
this.editor.onDidChangeCursorSelection(event => throttleFunction(event));
|
||||||
|
this.eventHub.emit('yulViewOpened', this.compilerInfo.compilerId);
|
||||||
|
this.eventHub.emit('requestSettings');
|
||||||
|
}
|
||||||
|
|
||||||
|
override getCurrentState(): MonacoPaneState {
|
||||||
|
return {
|
||||||
|
...super.getCurrentState(),
|
||||||
|
...this.filters.get(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onOptionsChange(force = false): void {
|
||||||
|
const filters = this.filters.get();
|
||||||
|
const newOptions: YulBackendOptions = {
|
||||||
|
filterDebugInfo: filters['filter-debug-info'],
|
||||||
|
};
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
for (const key in newOptions) {
|
||||||
|
if (newOptions[key as keyof YulBackendOptions] !== this.lastOptions[key as keyof Yul]) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastOptions = newOptions;
|
||||||
|
if (changed || force) {
|
||||||
|
this.eventHub.emit('yulViewOptionsUpdated', this.compilerInfo.compilerId, newOptions, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override onCompileResult(compilerId: number, compiler: CompilerInfo, result: CompilationResult): void {
|
||||||
|
if (this.compilerInfo.compilerId !== compilerId) return;
|
||||||
|
|
||||||
|
if (result.yulOutput) {
|
||||||
|
this.showYulResults(result.yulOutput);
|
||||||
|
} else if (compiler.supportsYulView) {
|
||||||
|
this.showYulResults([{text: '<No output>'}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override onCompiler(
|
||||||
|
compilerId: number,
|
||||||
|
compiler: CompilerInfo | null,
|
||||||
|
options: string,
|
||||||
|
editorId?: number,
|
||||||
|
treeId?: number,
|
||||||
|
): void {
|
||||||
|
if (this.compilerInfo.compilerId === compilerId) {
|
||||||
|
this.compilerInfo.compilerName = compiler?.name || '';
|
||||||
|
this.compilerInfo.editorId = editorId;
|
||||||
|
this.compilerInfo.treeId = treeId;
|
||||||
|
this.updateTitle();
|
||||||
|
this.updateButtons(compiler);
|
||||||
|
if (!compiler?.supportsYulView) {
|
||||||
|
const text = compiler?.name.toLowerCase().includes('resolc')
|
||||||
|
? '<Yul output is only supported for this compiler when the input language is Solidity>'
|
||||||
|
: '<Yul output is not supported for this compiler>';
|
||||||
|
this.showYulResults([{text}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showYulResults(result: any[]): void {
|
||||||
|
const newValue = result.length ? _.pluck(result, 'text').join('\n') : '<No Yul generated>';
|
||||||
|
this.editor.getModel()?.setValue(newValue);
|
||||||
|
|
||||||
|
if (!this.isAwaitingInitialResults) {
|
||||||
|
if (this.selection) {
|
||||||
|
this.editor.setSelection(this.selection);
|
||||||
|
this.editor.revealLinesInCenter(this.selection.selectionStartLineNumber, this.selection.endLineNumber);
|
||||||
|
}
|
||||||
|
this.isAwaitingInitialResults = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override close(): void {
|
||||||
|
this.eventHub.unsubscribe();
|
||||||
|
this.eventHub.emit('yulViewClosed', this.compilerInfo.compilerId);
|
||||||
|
this.editor.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ describe('Resolc', () => {
|
|||||||
expectedFilenameWithoutExtension: string,
|
expectedFilenameWithoutExtension: string,
|
||||||
): void {
|
): void {
|
||||||
const defaultOutputFilename = `${expectedFilenameWithoutExtension}.pvmasm`;
|
const defaultOutputFilename = `${expectedFilenameWithoutExtension}.pvmasm`;
|
||||||
expect(compiler.getOutputFilename(path.normalize('test/resolc'))).toEqual(defaultOutputFilename);
|
expect(compiler.getOutputFilename(path.normalize(path.dirname(inputFilename)))).toEqual(defaultOutputFilename);
|
||||||
|
|
||||||
let llvmIrBackendOptions = makeFakeLlvmIrBackendOptions({showOptimized: true});
|
let llvmIrBackendOptions = makeFakeLlvmIrBackendOptions({showOptimized: true});
|
||||||
expect(compiler.getIrOutputFilename(inputFilename, undefined, llvmIrBackendOptions)).toEqual(
|
expect(compiler.getIrOutputFilename(inputFilename, undefined, llvmIrBackendOptions)).toEqual(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import {ClangirBackendOptions} from './clangir.interfaces.js';
|
|||||||
import {ConfiguredOverrides} from './compiler-overrides.interfaces.js';
|
import {ConfiguredOverrides} from './compiler-overrides.interfaces.js';
|
||||||
import {LLVMIrBackendOptions} from './ir.interfaces.js';
|
import {LLVMIrBackendOptions} from './ir.interfaces.js';
|
||||||
import {OptPipelineBackendOptions, OptPipelineOutput} from './opt-pipeline-output.interfaces.js';
|
import {OptPipelineBackendOptions, OptPipelineOutput} from './opt-pipeline-output.interfaces.js';
|
||||||
|
import {YulBackendOptions} from './yul.interfaces.js';
|
||||||
|
|
||||||
export type ActiveTool = {
|
export type ActiveTool = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -117,6 +118,7 @@ export type CompilationRequestOptions = {
|
|||||||
produceHaskellStg?: boolean;
|
produceHaskellStg?: boolean;
|
||||||
produceHaskellCmm?: boolean;
|
produceHaskellCmm?: boolean;
|
||||||
produceClojureMacroExp?: boolean;
|
produceClojureMacroExp?: boolean;
|
||||||
|
produceYul?: YulBackendOptions | null;
|
||||||
cmakeArgs?: string;
|
cmakeArgs?: string;
|
||||||
customOutputFilename?: string;
|
customOutputFilename?: string;
|
||||||
overrides?: ConfiguredOverrides;
|
overrides?: ConfiguredOverrides;
|
||||||
@@ -216,6 +218,8 @@ export type CompilationResult = {
|
|||||||
|
|
||||||
clojureMacroExpOutput?: ResultLine[];
|
clojureMacroExpOutput?: ResultLine[];
|
||||||
|
|
||||||
|
yulOutput?: ResultLine[];
|
||||||
|
|
||||||
forceBinaryView?: boolean;
|
forceBinaryView?: boolean;
|
||||||
|
|
||||||
artifacts?: Artifact[];
|
artifacts?: Artifact[];
|
||||||
|
|||||||
27
types/compilation/yul.interfaces.ts
Normal file
27
types/compilation/yul.interfaces.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2025, 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 type YulBackendOptions = {
|
||||||
|
filterDebugInfo: boolean;
|
||||||
|
};
|
||||||
@@ -91,6 +91,7 @@ export type CompilerInfo = {
|
|||||||
supportsHaskellStgView?: boolean;
|
supportsHaskellStgView?: boolean;
|
||||||
supportsHaskellCmmView?: boolean;
|
supportsHaskellCmmView?: boolean;
|
||||||
supportsClojureMacroExpView?: boolean;
|
supportsClojureMacroExpView?: boolean;
|
||||||
|
supportsYulView?: boolean;
|
||||||
supportsCfg?: boolean;
|
supportsCfg?: boolean;
|
||||||
supportsGnatDebugViews?: boolean;
|
supportsGnatDebugViews?: boolean;
|
||||||
supportsLibraryCodeFilter?: boolean;
|
supportsLibraryCodeFilter?: boolean;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ mixin newPaneButton(classId, text, title, icon)
|
|||||||
+newPaneButton("view-haskellStg", "GHC STG", "Show GHC STG Intermediate Representation", "fas fa-water")
|
+newPaneButton("view-haskellStg", "GHC STG", "Show GHC STG Intermediate Representation", "fas fa-water")
|
||||||
+newPaneButton("view-haskellCmm", "GHC Cmm", "Show GHC Cmm Intermediate Representation", "fas fa-water")
|
+newPaneButton("view-haskellCmm", "GHC Cmm", "Show GHC Cmm Intermediate Representation", "fas fa-water")
|
||||||
+newPaneButton("view-clojuremacroexp", "Clojure Macro Expansion", "Show Clojure macro expansion", "fas fa-arrows-alt")
|
+newPaneButton("view-clojuremacroexp", "Clojure Macro Expansion", "Show Clojure macro expansion", "fas fa-arrows-alt")
|
||||||
|
+newPaneButton("view-yul", "Yul (Solidity IR)", "Show Solidity Intermediate Representation", "fas fa-align-center")
|
||||||
+newPaneButton("view-gccdump", "GCC Tree/RTL", "Show GCC Tree/RTL dump", "fas fa-tree")
|
+newPaneButton("view-gccdump", "GCC Tree/RTL", "Show GCC Tree/RTL dump", "fas fa-tree")
|
||||||
+newPaneButton("view-gnatdebugtree", "GNAT Debug Tree", "Show GNAT debug tree", "fas fa-tree")
|
+newPaneButton("view-gnatdebugtree", "GNAT Debug Tree", "Show GNAT debug tree", "fas fa-tree")
|
||||||
+newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree")
|
+newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree")
|
||||||
|
|||||||
17
views/templates/panes/yul.pug
Normal file
17
views/templates/panes/yul.pug
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
mixin optionButton(bind, isActive, text, title)
|
||||||
|
.button-checkbox
|
||||||
|
button(type="button" class="dropdown-item btn btn-sm btn-light" + (isActive ? " active" : "") title=title data-bind=bind aria-pressed=isActive ? "true" : "false")
|
||||||
|
span #{text}
|
||||||
|
input.d-none(type="checkbox" checked=isActive)
|
||||||
|
|
||||||
|
#yul
|
||||||
|
.top-bar.btn-toolbar.bg-light(role="toolbar")
|
||||||
|
include ../../font-size
|
||||||
|
.btn-group.btn-group-sm.filters(role="group")
|
||||||
|
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Yul Output Filters" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters")
|
||||||
|
span.fas.fa-filter
|
||||||
|
span.hideable Filters
|
||||||
|
.dropdown-menu
|
||||||
|
+optionButton("filter-debug-info", true, "Hide Debug Info", "Filter debug info intrinsics")
|
||||||
|
div.yul-body
|
||||||
|
.monaco-placeholder
|
||||||
@@ -40,6 +40,8 @@ mixin monacopane(id)
|
|||||||
|
|
||||||
include panes/explain
|
include panes/explain
|
||||||
|
|
||||||
|
include panes/yul
|
||||||
|
|
||||||
include widgets/compiler-selector
|
include widgets/compiler-selector
|
||||||
|
|
||||||
include widgets/libs-dropdown
|
include widgets/libs-dropdown
|
||||||
@@ -87,3 +89,5 @@ mixin monacopane(id)
|
|||||||
+monacopane("rusthir")
|
+monacopane("rusthir")
|
||||||
|
|
||||||
+monacopane("clojuremacroexp")
|
+monacopane("clojuremacroexp")
|
||||||
|
|
||||||
|
+monacopane("yul")
|
||||||
|
|||||||
Reference in New Issue
Block a user