Add option for how parents are placed in control flow layout (#7850)

This PR implements this part of the cutter graph layout algorithm:

![image](https://github.com/user-attachments/assets/134e1717-ba24-4e22-bd83-b64b5333fb96)


![image](https://github.com/user-attachments/assets/27c35f4f-73c8-487d-bb2c-cf9ac57787e8)


![image](https://github.com/user-attachments/assets/661a1afe-4e1e-42fd-8b9d-5ca38a2e8a18)
This commit is contained in:
Jeremy Rifkin
2025-06-22 13:55:53 -05:00
committed by GitHub
parent 37ae297d95
commit bb08a6042a
5 changed files with 42 additions and 5 deletions

View File

@@ -160,7 +160,10 @@ export class GraphLayoutCore {
edgeRows: (RowDescriptor & EdgeRowMetadata)[];
readonly layoutTime: number;
constructor(cfg: AnnotatedCfgDescriptor) {
constructor(
cfg: AnnotatedCfgDescriptor,
readonly centerParents: boolean,
) {
this.populate_graph(cfg);
const start = performance.now();
@@ -358,9 +361,14 @@ export class GraphLayoutCore {
boundingBox.rows++;
block.boundingBox = boundingBox;
block.row = 0;
// between immediate children
const [left, right] = [this.blocks[block.treeEdges[0]], this.blocks[block.treeEdges[1]]];
block.col = Math.floor((left.col + right.col) / 2); // TODO
if (this.centerParents) {
// center of bounding box
block.col = Math.floor(Math.max(boundingBox.cols - 2, 0) / 2);
} else {
// center between immediate children
const [left, right] = [this.blocks[block.treeEdges[0]], this.blocks[block.treeEdges[1]]];
block.col = Math.floor((left.col + right.col) / 2);
}
}
}

View File

@@ -25,6 +25,7 @@
export interface CfgState {
selectedFunction: string | null;
isircfg?: boolean;
centerparents?: boolean;
}
/*

View File

@@ -48,6 +48,7 @@ import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {assert, unwrap} from '../assert.js';
import {GraphLayoutCore} from '../graph-layout-core.js';
import * as MonacoConfig from '../monaco-config.js';
import {Toggles} from '../widgets/toggles.js';
const ColorTable = {
red: '#FE5D5D',
@@ -107,6 +108,7 @@ export class Cfg extends Pane<CfgState> {
graphContainer: HTMLElement;
graphElement: HTMLElement;
infoElement: HTMLElement;
centerParentsButton: JQuery<HTMLElement>;
exportPNGButton: JQuery;
estimatedPNGSize: Element;
exportSVGButton: JQuery;
@@ -118,6 +120,8 @@ export class Cfg extends Pane<CfgState> {
functionSelector: TomSelect;
resetViewButton: JQuery;
zoomOutButton: JQuery;
toggles: Toggles;
results: CFGResult;
state: CfgState & PaneState;
layout: GraphLayoutCore;
@@ -201,10 +205,13 @@ export class Cfg extends Pane<CfgState> {
this.graphContainer = this.domRoot.find('.graph-container')[0];
this.graphElement = this.domRoot.find('.graph')[0];
this.infoElement = this.domRoot.find('.cfg-info')[0];
this.centerParentsButton = this.domRoot.find('.center-parents');
this.exportPNGButton = this.domRoot.find('.export-png').first();
this.estimatedPNGSize = unwrap(this.exportPNGButton[0].querySelector('.estimated-export-size'));
this.exportSVGButton = this.domRoot.find('.export-svg').first();
this.setupFictitiousGraphContainer();
this.toggles = new Toggles(this.domRoot.find('.options'), state as unknown as Record<string, boolean>);
}
setupFictitiousGraphContainer() {
@@ -271,6 +278,7 @@ export class Cfg extends Pane<CfgState> {
}
e.preventDefault();
});
this.toggles.on('change', this.onToggleChange.bind(this));
this.exportPNGButton.on('click', () => {
this.exportPNG();
});
@@ -290,6 +298,12 @@ export class Cfg extends Pane<CfgState> {
});
}
onToggleChange() {
this.state = this.getCurrentState();
this.updateState();
this.selectFunction(this.state.selectedFunction);
}
async exportPNG() {
fileSaver.saveAs(await this.createPNG(), 'cfg.png');
}
@@ -519,7 +533,7 @@ export class Cfg extends Pane<CfgState> {
const fn = this.results[name];
this.bbMap = {};
await this.createBasicBlocks(fn);
this.layout = new GraphLayoutCore(fn as AnnotatedCfgDescriptor);
this.layout = new GraphLayoutCore(fn as AnnotatedCfgDescriptor, !!this.state.centerparents);
this.applyLayout();
this.drawEdges();
this.infoElement.innerHTML = `Layout time: ${Math.round(this.layout.layoutTime)}ms<br/>Basic blocks: ${
@@ -695,6 +709,7 @@ export class Cfg extends Pane<CfgState> {
treeid: this.compilerInfo.treeId,
selectedFunction: this.state.selectedFunction,
isircfg: this.state.isircfg,
centerparents: this.toggles.get().centerparents,
};
this.paneRenaming.addState(state);
return state;

View File

@@ -504,6 +504,10 @@ pre.content.wrap * {
font-size: x-small;
font-style: italic;
}
.options button {
height: 100%;
}
}
.change-language {

View File

@@ -2,6 +2,15 @@
.top-bar.btn-toolbar.bg-light.cfg-toolbar(role="toolbar")
.btn-group.btn-group-sm(role="group")
select.function-selector
.btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Control flow graph options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Change how the control flow graph is rendered")
span.fas.fa-cog
span.hideable Layout Options
.dropdown-menu.options
.button-checkbox
button.dropdown-item.btn.btn-sm.btn-light.center-parents(type="button" title="Center Parents" data-bind="centerparents" aria-pressed="false" aria-label="Center Parents")
span Center Parents
input.d-none(type="checkbox" checked=false)
.btn-group.btn-group-sm(role="group" aria-label="CFG Export")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options")
span.fas.fa-arrow-down