@specs-feup/clava
Version:
A C/C++ source-to-source compiler written in Typescript
230 lines • 8.73 kB
JavaScript
import PrintOnce from "@specs-feup/lara/api/lara/util/PrintOnce.js";
import Query from "@specs-feup/lara/api/weaver/Query.js";
import { BinaryOp, BuiltinType, Call, FunctionJp, Loop, Op, Param, Varref, } from "../../Joinpoints.js";
import OpsBlock from "./OpsBlock.js";
export default class StaticOpsCounter {
// Whitelist of ops
static validOps = new Set([
"mul",
"div",
"rem",
"add",
"sub",
"shl",
"shr",
"cmp",
"and",
"xor",
"or",
"l_and",
"l_or",
"mul_assign",
"div_assign",
"rem_assign",
"add_assign",
"sub_assign",
"shl_assign",
"shr_assign",
"and_assign",
"xor_assign",
"or_assign",
"post_inc",
"post_dec",
"pre_inc",
"pre_dec",
]);
instrumentedFunctions = new Set();
filterFunction;
constructor(filterFunction = ($op) => true) {
this.filterFunction = filterFunction;
}
count($fn, opsBlock, includeOpKind = false) {
const $function = $fn instanceof FunctionJp
? $fn
: $fn.getAncestor("function");
if ($function === undefined) {
PrintOnce.message(`StaticOpsCounter.count: Could not find function corresponding to the join point ${$fn.location}`);
return;
}
const functionId = `${$function.name}@${$function.location}`;
// Check if it is already instrumented
if (this.instrumentedFunctions.has(functionId)) {
// TODO: Support recursive function calls
return;
}
this.instrumentedFunctions.add(functionId);
console.log("StaticOpsCounter.count: Estimating ops of function " + functionId);
opsBlock ??= new OpsBlock(functionId);
// Go statement-by-statement
$function.body.children.forEach(($stmt) => {
this.countOpStatic($stmt, opsBlock, includeOpKind);
});
return opsBlock;
}
countOpStatic($stmt, opsBlock, includeOpKind) {
// If stmt is a loop, count new block, recursively
if ($stmt == undefined) {
return;
}
if ($stmt instanceof Loop) {
if ($stmt.kind !== "for") {
console.log(`Ignoring loops that are not 'fors' (location ${$stmt.location}) for now`);
return;
}
const rank = $stmt.rank;
const nestedId = `${opsBlock.id} => ${rank[rank.length - 1]}`;
// Create block for loop
const nestedOpsBlock = new OpsBlock(nestedId);
this.countOpStatic($stmt.init, opsBlock, includeOpKind);
this.countOpStatic($stmt.cond, nestedOpsBlock, includeOpKind);
this.countOpStatic($stmt.step, nestedOpsBlock, includeOpKind);
// Extract iterations
const iter = $stmt.iterationsExpr;
let replacementsMap = {};
do {
replacementsMap = this.analyseIterationsExpr(iter, $stmt);
for (const rep in replacementsMap) {
for (const $jp of iter.descendants) {
if ($jp.code === rep) {
$jp.replaceWith(replacementsMap[rep]); // TODO: Do calculation without altering the source code.
}
}
}
} while (Object.keys(replacementsMap).length > 0);
nestedOpsBlock.repetitions = iter.code;
// Add to nested blocks
opsBlock.nestedOpsBlocks.push(nestedOpsBlock);
// Go statement-by-statement
$stmt.body.children.forEach(($nestedStmt) => {
this.countOpStatic($nestedStmt, nestedOpsBlock, includeOpKind);
});
return;
}
// If stmt is not a loop, count ops
// Apply to all ops found in the stmt
for (const $op of Query.searchFrom($stmt, Op)) {
// If not a valid op, continue
if (!this.isValidOp($op)) {
continue;
}
// Calculate type and bitwidth
const $builtinType = this.toBuiltinType($op.type);
const counterType = this.getCounterType($builtinType);
const bitwidth = $builtinType !== undefined ? String($op.bitWidth) : undefined;
// Increment counter
let opsId = `${counterType}-${bitwidth}`;
if (includeOpKind) {
opsId += `-${$op.kind}`;
}
opsBlock.add(opsId);
}
// Call function recursively when function calls are found
for (const $call of Query.searchFrom($stmt, Call)) {
const $funcDef = $call.definition;
if ($funcDef === undefined) {
continue;
}
this.count($funcDef, opsBlock, includeOpKind);
}
}
getCounterType($builtinType) {
if ($builtinType === undefined) {
return undefined;
}
if ($builtinType.isFloat) {
return "flops";
}
else if ($builtinType.isInteger) {
return "iops";
}
else {
PrintOnce.message(`StaticOpsCounter: could not determine if builtinType ${$builtinType.kind} is integer or float`);
return undefined;
}
}
toBuiltinType($type) {
if ($type instanceof BuiltinType) {
return $type;
}
PrintOnce.message(`StaticOpsCounter: could not determine builtinType of ${$type.joinPointType}`);
return undefined;
}
isValidOp($op) {
const isValid = StaticOpsCounter.validOps.has($op.kind);
if (!isValid) {
return false;
}
if (!this.filterFunction($op)) {
return false;
}
return true;
}
analyseIterationsExpr($expr, $source) {
const result = {};
for (const $varref of Query.searchFromInclusive($expr, Varref)) {
if (result[$varref.name] !== undefined) {
continue;
}
if ($varref.decl instanceof Param) {
console.log(`Var ${$varref.name} is a parameter`);
continue;
}
console.log(`REFS of ${$varref.name}`);
const $lastWrite = this.getLastWrite($source, $varref.vardecl);
if ($lastWrite === undefined) {
console.log("Could not find last write");
continue;
}
console.log(`Last write of ${$varref.vardecl.name}: ${$lastWrite.code}`);
result[$varref.name] = $lastWrite;
}
return result;
}
getLastWrite($currentJp, $vardecl) {
if ($currentJp === undefined) {
console.log("Could not find declaration");
return undefined;
}
// Get siblings on the left
const siblLeft = $currentJp.siblingsLeft;
// Go back until the variable declaration/parameter is found
for (let i = siblLeft.length - 1; i >= 0; i--) {
const sibl = siblLeft[i];
// For each sibling, find write references to the variable
const refs = sibl.getDescendantsAndSelf("varref").filter((varref) => varref.name === $vardecl.name);
for (const $ref of refs) {
// Ignore
if ($ref.use === "read") {
continue;
}
// Not supported yet
if ($ref.use === "readwrite") {
console.log("Readwrite not supported yet");
return undefined;
}
// Check if assignment
const $refParent = $ref.parent;
if ($refParent.kind !== "assign") {
console.log("Not supported when not an assignment");
return undefined;
}
if ($refParent instanceof BinaryOp) {
return $refParent.right;
}
}
// Check vardecl
const decls = sibl.getDescendantsAndSelf("vardecl").filter((vardecl) => vardecl.equals($vardecl));
for (const $decl of decls) {
// Found decl
if (!$decl.hasInit) {
console.log(`Variable declaration for ${$decl.name} has no initialization`);
return undefined;
}
return $decl.init;
}
}
// Did not find declaration yet, call on parent
return this.getLastWrite($currentJp.parent, $vardecl);
}
}
//# sourceMappingURL=StaticOpsCounter.js.map