Skip to content

Commit

Permalink
Merge pull request #128 from specs-feup/cfg-improvements
Browse files Browse the repository at this point in the history
CFG improvements: goto and label connection and constructor update
  • Loading branch information
joaobispo authored Jul 29, 2023
2 parents 7b45c5d + 8e4c015 commit da1ad83
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ public enum LaraApiResource implements LaraResourceProvider {

CFG_CASE_DATA("graphs/cfg/nodedata/CaseData.js"),
CFG_DATA_FACTORY("graphs/cfg/nodedata/DataFactory.js"),
CFG_GOTO_DATA("graphs/cfg/nodedata/GotoData.js"),
CFG_HEADER_DATA("graphs/cfg/nodedata/HeaderData.js"),
CFG_IF_DATA("graphs/cfg/nodedata/IfData.js"),
CFG_INST_LIST_NODE_DATA("graphs/cfg/nodedata/InstListNodeData.js"),
CFG_LABEL_DATA("graphs/cfg/nodedata/LabelData.js"),
CFG_LOOP_DATA("graphs/cfg/nodedata/LoopData.js"),
CFG_SCOPE_NODE_DATA("graphs/cfg/nodedata/ScopeNodeData.js"),
CFG_SWITCH_DATA("graphs/cfg/nodedata/SwitchData.js"),
Expand Down
16 changes: 14 additions & 2 deletions ClavaLaraApi/src-lara-clava/clava/clava/graphs/ControlFlowGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,20 @@ class ControlFlowGraph extends Graph {
this.#endNode = endNode;
}

static build($jp, deterministicIds = false, splitInstList = false) {
const builderResult = new CfgBuilder($jp, deterministicIds, splitInstList).build();
/**
* Builds the control flow graph
*
* @param {joinpoint} $jp
* @param {boolean} [deterministicIds = false] If true, uses deterministic ids for the graph ids (e.g. id_0, id_1...). Otherwise, uses $jp.astId whenever possible
* @param {Object} [options = {}] An object containing configuration options for the cfg
* @param {boolean} [options.splitInstList = false] If true, statements of each instruction list must be split
* @param {boolean} [options.removeGotoNodes = false] If true, the nodes that correspond to goto statements will be excluded from the resulting graph
* @param {boolean} [options.removeLabelNodes = false] If true, the nodes that correspond to label statements will be excluded from the resulting graph
*
* @returns {ControlFlowGraph} a new instance of the ControlFlowGraph class
*/
static build($jp, deterministicIds = false, options = {}) {
const builderResult = new CfgBuilder($jp, deterministicIds, options).build();
return new ControlFlowGraph(...builderResult);
}

Expand Down
98 changes: 82 additions & 16 deletions ClavaLaraApi/src-lara-clava/clava/clava/graphs/cfg/CfgBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,35 @@ class CfgBuilder {
#nextNodes;

/**
* Indicates whether an instruction list should be split
* {boolean} If true, each instruction list should be split
*/
#splitInstList;

/**
* {boolean} If true, the goto nodes should be excluded from the graph
*/
#removeGotoNodes;

/**
* {boolean} If true, the label nodes should be excluded from the graph
*/
#removeLabelNodes;

/**
* Creates a new instance of the CfgBuilder class
* @param {joinpoint} $jp
* @param {boolean} [splitInstList = false] If true, statements of each instruction list must be split
* @param {boolean} [deterministicIds = false] If true, uses deterministic ids for the graph ids (e.g. id_0, id_1...). Otherwise, uses $jp.astId whenever possible
*/
constructor($jp, deterministicIds = false, splitInstList = false) {
* @param {Object} [options = {}] An object containing configuration options for the cfg
* @param {boolean} [options.splitInstList = false] If true, statements of each instruction list must be split
* @param {boolean} [options.removeGotoNodes = false] If true, the nodes that correspond to goto statements will be excluded from the resulting graph
* @param {boolean} [options.removeLabelNodes = false] If true, the nodes that correspond to label statements will be excluded from the resulting graph
*/
constructor($jp, deterministicIds = false, options = {}) {
this.#jp = $jp;
this.#splitInstList = splitInstList;
this.#deterministicIds = deterministicIds;
this.#splitInstList = options.splitInstList || false;
this.#removeGotoNodes = options.removeGotoNodes || false;
this.#removeLabelNodes = options.removeLabelNodes || false;
this.#currentId = 0;
this.#dataFactory = new DataFactory(this.#jp);
this.#graph = Graphs.newGraph();
Expand Down Expand Up @@ -278,18 +293,9 @@ class CfgBuilder {
*/
#connectBreakNode(node) {
const $breakStmt = node.data().nodeStmt;
const $loop = $breakStmt.ancestor("loop");
const $switch = $breakStmt.ancestor("switch");
const loopDepth = $loop !== undefined ? $loop.depth : -1;
const switchDepth = $switch !== undefined ? $switch.depth : -1;
let afterNode = undefined;

if (loopDepth > switchDepth)
// Statement is used to terminate a loop
afterNode = this.#nextNodes.nextExecutedNode($loop);
// Statement is used to exit a switch block
else afterNode = this.#nextNodes.nextExecutedNode($switch);
const $enclosingStmt = $breakStmt.enclosingStmt;

const afterNode = this.#nextNodes.nextExecutedNode($enclosingStmt);
this.#addEdge(node, afterNode, CfgEdgeType.UNCONDITIONAL);
}

Expand Down Expand Up @@ -424,6 +430,28 @@ class CfgBuilder {
this.#addEdge(node, afterNode, CfgEdgeType.UNCONDITIONAL);
}

/**
* @param {Cytoscape.node} node node whose type is "GOTO"
*/
#connectGotoNode(node) {
const $gotoStmt = node.data().nodeStmt;
const labelName = $gotoStmt.label.name;
const $labelStmt = Query.searchFromInclusive(this.#jp, "labelStmt", {decl: decl => decl.name == labelName}).first();

const afterNode = this.#nodes.get($labelStmt.astId);
this.#addEdge(node, afterNode, CfgEdgeType.UNCONDITIONAL);
}

/**
* @param {Cytoscape.node} node node whose type is "LABEL"
*/
#connectLabelNode(node) {
const $labelStmt = node.data().nodeStmt;

const afterNode = this.#nextNodes.nextExecutedNode($labelStmt);
this.#addEdge(node, afterNode, CfgEdgeType.UNCONDITIONAL);
}

/**
* Connects a node associated with a statement that is an instance of a "return" statement.
* @param {Cytoscape.node} node node whose type is "RETURN"
Expand Down Expand Up @@ -510,6 +538,12 @@ class CfgBuilder {
case CfgNodeType.INST_LIST:
this.#connectInstListNode(node);
break;
case CfgNodeType.GOTO:
this.#connectGotoNode(node);
break;
case CfgNodeType.LABEL:
this.#connectLabelNode(node);
break;
case CfgNodeType.RETURN:
this.#connectReturnNode(node);
break;
Expand Down Expand Up @@ -585,6 +619,38 @@ class CfgBuilder {
);
}

// Remove label nodes
if (this.#removeLabelNodes) {
for (const node of this.#graph.nodes()) {
// Only nodes whose type is "LABEL"
if (node.data().type !== CfgNodeType.LABEL)
continue;

Graphs.removeNode(
this.#graph,
node,
(incoming, outgoing) => new CfgEdge(incoming.data().type)
);
this.#nodes.delete(node.data().nodeStmt.astId);
}
}

// Remove goto nodes
if (this.#removeGotoNodes) {
for (const node of this.#graph.nodes()) {
// Only nodes whose type is "GOTO"
if (node.data().type !== CfgNodeType.GOTO)
continue;

Graphs.removeNode(
this.#graph,
node,
(incoming, outgoing) => new CfgEdge(incoming.data().type)
);
this.#nodes.delete(node.data().nodeStmt.astId);
}
}

// Remove nodes that have no incoming edge and are not start
for (const node of this.#graph.nodes()) {
// Only nodes that are not start
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class CfgNodeType {
static CONTINUE = new CfgNodeType("CONTINUE");
static SWITCH = new CfgNodeType("SWITCH");
static CASE = new CfgNodeType("CASE");
static GOTO = new CfgNodeType("GOTO");
static LABEL = new CfgNodeType("LABEL");
static RETURN = new CfgNodeType("RETURN");
// To add: WHILE, DOWHILE?

Expand Down
12 changes: 11 additions & 1 deletion ClavaLaraApi/src-lara-clava/clava/clava/graphs/cfg/CfgUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,21 @@ class CfgUtils {
return CfgNodeType.SWITCH;
}

//Case stmt
// Case stmt
if ($stmt.instanceOf("case")) {
return CfgNodeType.CASE;
}

// Goto stmt
if ($stmt.instanceOf("gotoStmt")) {
return CfgNodeType.GOTO;
}

// Label stmt
if ($stmt.instanceOf("labelStmt")) {
return CfgNodeType.LABEL;
}

// Return stmt
if ($stmt.instanceOf("returnStmt")) {
return CfgNodeType.RETURN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ class CaseData extends CfgNodeData {
}

toString() {
if (this.case.isDefault)
return "default";
return "case " + this.case.values.map(value => value.code).join(" || ");
return this.case.code;
}

isBranch() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ laraImport("clava.graphs.cfg.nodedata.HeaderData");
laraImport("clava.graphs.cfg.nodedata.IfData");
laraImport("clava.graphs.cfg.nodedata.SwitchData");
laraImport("clava.graphs.cfg.nodedata.CaseData");
laraImport("clava.graphs.cfg.nodedata.GotoData");
laraImport("clava.graphs.cfg.nodedata.LabelData");
laraImport("clava.graphs.cfg.nodedata.ReturnData");

class DataFactory {
Expand Down Expand Up @@ -38,6 +40,10 @@ class DataFactory {
return new SwitchData($stmt, id);
case CfgNodeType.CASE:
return new CaseData($stmt, id);
case CfgNodeType.GOTO:
return new GotoData($stmt, id);
case CfgNodeType.LABEL:
return new LabelData($stmt, id);
case CfgNodeType.RETURN:
return new ReturnData($stmt, id);
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
laraImport("clava.graphs.cfg.CfgNodeData");
laraImport("clava.graphs.cfg.CfgNodeType");

class GotoData extends CfgNodeData {
//#stmt

constructor($stmt, id) {
super(CfgNodeType.GOTO, $stmt, id);

//this.#stmt = $stmt
}

toString() {
return this.nodeStmt.code;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
laraImport("clava.graphs.cfg.CfgNodeData");
laraImport("clava.graphs.cfg.CfgNodeType");

class LabelData extends CfgNodeData {
//#stmt

constructor($stmt, id) {
super(CfgNodeType.LABEL, $stmt, id);

//this.#stmt = $stmt
}

toString() {
return this.nodeStmt.code;
}

}

0 comments on commit da1ad83

Please sign in to comment.