style
diff --git a/src/support/permutations.h b/src/support/permutations.h
index 4e6cc18..601057b 100644
--- a/src/support/permutations.h
+++ b/src/support/permutations.h
@@ -55,10 +55,14 @@
std::vector<std::vector<Index>> ret;
std::vector<Index> curr;
curr.resize(size);
- for (auto& x : curr) x = 0;
+ for (auto& x : curr) {
+ x = 0;
+ }
while (1) {
std::set<Index> set;
- for (auto x : curr) set.insert(x);
+ for (auto x : curr) {
+ set.insert(x);
+ }
if (set.size() == size) {
ret.push_back(curr); // this is indeed a permutation
}
@@ -66,9 +70,13 @@
Index toBump = size - 1;
while (1) {
curr[toBump]++;
- if (curr[toBump] < size) break;
+ if (curr[toBump] < size) {
+ break;
+ }
// an overflow
- if (toBump == 0) return ret; // all done
+ if (toBump == 0) {
+ return ret; // all done
+ }
curr[toBump] = 0;
toBump--;
}
diff --git a/src/tools/wasm-analyze.cpp b/src/tools/wasm-analyze.cpp
index db7f0bd..3278218 100644
--- a/src/tools/wasm-analyze.cpp
+++ b/src/tools/wasm-analyze.cpp
@@ -17,21 +17,22 @@
//
// Expression analyzer utility
//
-// Superoptimization is based on Bansal, Sorav; Aiken, Alex (21–25 October 2006): "Automatic Generation of Peephole Superoptimizers".
+// Superoptimization is based on Bansal, Sorav; Aiken, Alex (21–25 October
+// 2006): "Automatic Generation of Peephole Superoptimizers".
//
+#include "ast/cost.h"
+#include "ast_utils.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
#include "support/hash.h"
#include "support/permutation.h"
-#include "wasm-s-parser.h"
-#include "wasm-traversal.h"
-#include "wasm-printing.h"
#include "wasm-interpreter.h"
#include "wasm-io.h"
-#include "ast_utils.h"
-#include "ast/cost.h"
+#include "wasm-printing.h"
+#include "wasm-s-parser.h"
+#include "wasm-traversal.h"
using namespace cashew;
using namespace wasm;
@@ -42,13 +43,37 @@
// special values to make sure to consider in execution hashing
#define NUM_LIMITS 6
-static int32_t LIMIT_I32S[NUM_LIMITS] = { std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), int32_t(std::numeric_limits<uint32_t>::min()), int32_t(std::numeric_limits<uint32_t>::max()), 0xfffff, -0xfffff };
-static int64_t LIMIT_I64S[NUM_LIMITS] = { std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), int64_t(std::numeric_limits<uint64_t>::min()), int64_t(std::numeric_limits<uint64_t>::max()), 0xfffffLL, -0xfffffLL };
-static float LIMIT_F32S[NUM_LIMITS] = { std::numeric_limits<float>::min(), std::numeric_limits<float>::max(), std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::infinity(), float(0xfffff), float(-0xfffff) };
-static double LIMIT_F64S[NUM_LIMITS] = { std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::infinity(), double(0xfffff), double(-0xfffff) };
+static int32_t LIMIT_I32S[NUM_LIMITS] = {
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max(),
+ int32_t(std::numeric_limits<uint32_t>::min()),
+ int32_t(std::numeric_limits<uint32_t>::max()),
+ 0xfffff,
+ -0xfffff};
+static int64_t LIMIT_I64S[NUM_LIMITS] = {
+ std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max(),
+ int64_t(std::numeric_limits<uint64_t>::min()),
+ int64_t(std::numeric_limits<uint64_t>::max()),
+ 0xfffffLL,
+ -0xfffffLL};
+static float LIMIT_F32S[NUM_LIMITS] = {std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::quiet_NaN(),
+ std::numeric_limits<float>::infinity(),
+ float(0xfffff),
+ float(-0xfffff)};
+static double LIMIT_F64S[NUM_LIMITS] = {
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::quiet_NaN(),
+ std::numeric_limits<double>::infinity(),
+ double(0xfffff),
+ double(-0xfffff)};
#define MAX_SMALL 260
-#define NUM_SMALLS (MAX_SMALL + MAX_SMALL + 1) /* negatives, positives, and zero */
+#define NUM_SMALLS \
+ (MAX_SMALL + MAX_SMALL + 1) /* negatives, positives, and zero */
#define NUM_SPECIALS (NUM_LIMITS + NUM_SMALLS)
@@ -67,27 +92,30 @@
}
}
- HashedExpression(const HashedExpression& other) : expr(other.expr), hash(other.hash) {}
+ HashedExpression(const HashedExpression& other)
+ : expr(other.expr), hash(other.hash) {}
};
struct ExpressionHasher {
- size_t operator()(const HashedExpression value) const {
- return value.hash;
- }
+ size_t operator()(const HashedExpression value) const { return value.hash; }
};
struct ExpressionComparer {
bool operator()(const HashedExpression a, const HashedExpression b) const {
- if (a.hash != b.hash) return false;
+ if (a.hash != b.hash)
+ return false;
return ExpressionAnalyzer::equal(a.expr, b.expr);
}
};
// expression -> a count
-class ExpressionIntMap : public std::unordered_map<HashedExpression, size_t, ExpressionHasher, ExpressionComparer> {};
+class ExpressionIntMap : public std::unordered_map<HashedExpression,
+ size_t,
+ ExpressionHasher,
+ ExpressionComparer> {};
// global expression state
-Module global; // a module that persists til the end
+Module global; // a module that persists til the end
ExpressionIntMap freqs; // expression -> its frequency
// Normalize an expression, replacing irrelevant bits with
@@ -103,11 +131,13 @@
Normalizer(Module& wasm) : wasm(wasm), builder(wasm) {}
Expression* parentCopy(Expression* curr) {
- return ExpressionManipulator::flexibleCopy(curr, wasm, [&](Expression* curr) { return this->copy(curr); });
+ return ExpressionManipulator::flexibleCopy(
+ curr, wasm, [&](Expression* curr) { return this->copy(curr); });
}
Expression* copy(Expression* curr) {
- // For now, we only handle math-type expressions: having a return value and no side effects
+ // For now, we only handle math-type expressions: having a return value
+ // and no side effects
// TODO: do more stuff, modeling side effects etc.
if (!isConcreteWasmType(curr->type)) {
return builder.makeUnreachable();
@@ -132,23 +162,29 @@
// consider the general case of an arbitrary expression here
return builder.makeGetLocal(nextLocal++, curr->type);
}
- if (curr->is<Host>() || curr->is<Call>() || curr->is<CallImport>() || curr->is<CallIndirect>() || curr->is<GetGlobal>() || curr->is<Load>() || curr->is<Return>() || curr->is<Break>() || curr->is<Switch>()) {
+ if (curr->is<Host>() || curr->is<Call>() || curr->is<CallImport>() ||
+ curr->is<CallIndirect>() || curr->is<GetGlobal>() ||
+ curr->is<Load>() || curr->is<Return>() || curr->is<Break>() ||
+ curr->is<Switch>()) {
return builder.makeUnreachable();
}
return nullptr; // allow the default copy to proceed
}
} normalizer(wasm);
- auto* ret = ExpressionManipulator::flexibleCopy(expr, wasm, [&](Expression* curr) { return normalizer.copy(curr); });
+ auto* ret = ExpressionManipulator::flexibleCopy(
+ expr, wasm, [&](Expression* curr) { return normalizer.copy(curr); });
- if (!isConcreteWasmType(ret->type) || normalizer.nextLocal >= MAX_LOCAL || Measurer::measure(ret) > MAX_EXPRESSION_SIZE) {
+ if (!isConcreteWasmType(ret->type) || normalizer.nextLocal >= MAX_LOCAL ||
+ Measurer::measure(ret) > MAX_EXPRESSION_SIZE) {
return nullptr;
}
return ret;
}
// Scan an expression for local types. Assumes it has MAX_LOCAL locals at most
-struct ScanLocals : public WalkerPass<PostWalker<ScanLocals, Visitor<ScanLocals>>> {
+struct ScanLocals
+ : public WalkerPass<PostWalker<ScanLocals, Visitor<ScanLocals>>> {
WasmType localTypes[MAX_LOCAL];
ScanLocals(Expression* expr) {
@@ -165,10 +201,12 @@
};
// Remap locals
-struct RemapLocals : public WalkerPass<PostWalker<RemapLocals, Visitor<RemapLocals>>> {
+struct RemapLocals
+ : public WalkerPass<PostWalker<RemapLocals, Visitor<RemapLocals>>> {
std::vector<Index>& mapping;
- RemapLocals(Expression* expr, std::vector<Index>& mapping) : mapping(mapping) {
+ RemapLocals(Expression* expr, std::vector<Index>& mapping)
+ : mapping(mapping) {
walk(expr);
}
@@ -186,17 +224,19 @@
struct ScanSettings {
Index* totalExpressions;
bool adviseOnly;
- ScanSettings(Index* totalExpressions, bool adviseOnly) : totalExpressions(totalExpressions), adviseOnly(adviseOnly) {}
+ ScanSettings(Index* totalExpressions, bool adviseOnly)
+ : totalExpressions(totalExpressions), adviseOnly(adviseOnly) {}
};
// Scan a module for expressions
-struct Scan : public WalkerPass<PostWalker<Scan, UnifiedExpressionVisitor<Scan>>> {
+struct Scan
+ : public WalkerPass<PostWalker<Scan, UnifiedExpressionVisitor<Scan>>> {
ScanSettings settings;
Scan(ScanSettings settings) : settings(settings) {}
void doWalkFunction(Function* func) {
- //std::cout << " [" << func->name << ']' << '\n';
+ // std::cout << " [" << func->name << ']' << '\n';
walk(func->body);
}
@@ -205,7 +245,8 @@
// which is ephemeral TODO: avoid keeping them alive til the end of
// module processing to reduce peak mem usage?
auto* normalized = normalize(curr, *getModule());
- if (!normalized) return;
+ if (!normalized)
+ return;
if (!settings.adviseOnly) {
(*settings.totalExpressions)++; // this is relevant, count it
}
@@ -216,16 +257,19 @@
iter->second++; // just increment it
}
} else {
- // create a persistent copy in the global module TODO: avoid the rehash here
+ // create a persistent copy in the global module TODO: avoid the rehash
+ // here
auto* copy = ExpressionManipulator::copy(normalized, global);
freqs[HashedExpression(copy)] = settings.adviseOnly ? 0 : 1;
#if 1
- // add the permutations on the locals as well, with freq 0, as we just want to use them as optimization targets,
- // we don't need to optimize them, we optimize the canonical first form.
+ // add the permutations on the locals as well, with freq 0, as we just
+ // want to use them as optimization targets, we don't need to optimize
+ // them, we optimize the canonical first form.
ScanLocals scanner(copy);
if (scanner.localTypes[1] != none) {
struct PermutationsLister {
- std::vector<std::vector<std::vector<Index>>> list; // index => list of permutations of that size
+ std::vector<std::vector<std::vector<Index>>>
+ list; // index => list of permutations of that size
PermutationsLister() {
list.resize(MAX_LOCAL + 1);
for (size_t i = 1; i < MAX_LOCAL + 1; i++) {
@@ -258,24 +302,31 @@
// Generate local values deterministically, using a seed
class LocalGenerator {
Index seed;
+
public:
LocalGenerator(Index seed) : seed(seed) {}
Literal get(Index index, WasmType type) {
// use low indexes to ensure we get representation of a few special values
// TODO: get each of the MAX_LOCALS to all of its NUM_SPECIALS values
- int64_t special = seed; // start with 0-NS having them all taking the same value
+ int64_t special =
+ seed; // start with 0-NS having them all taking the same value
if (special >= NUM_SPECIALS) { // then give each a range for itself
special = int64_t(seed) - int64_t(NUM_SPECIALS * (index + 1));
}
if (special >= 0 && special < NUM_SPECIALS) {
if (special < NUM_LIMITS) {
switch (type) {
- case i32: return Literal(LIMIT_I32S[special]);
- case i64: return Literal(LIMIT_I64S[special]);
- case f32: return Literal(LIMIT_F32S[special]);
- case f64: return Literal(LIMIT_F64S[special]);
- default: WASM_UNREACHABLE();
+ case i32:
+ return Literal(LIMIT_I32S[special]);
+ case i64:
+ return Literal(LIMIT_I64S[special]);
+ case f32:
+ return Literal(LIMIT_F32S[special]);
+ case f64:
+ return Literal(LIMIT_F64S[special]);
+ default:
+ WASM_UNREACHABLE();
}
} else {
special -= NUM_LIMITS;
@@ -283,11 +334,16 @@
special -= MAX_SMALL;
assert(special >= -MAX_SMALL && special <= MAX_SMALL);
switch (type) {
- case i32: return Literal(int32_t(special));
- case i64: return Literal(int64_t(special));
- case f32: return Literal(float(special));
- case f64: return Literal(double(special));
- default: WASM_UNREACHABLE();
+ case i32:
+ return Literal(int32_t(special));
+ case i64:
+ return Literal(int64_t(special));
+ case f32:
+ return Literal(float(special));
+ case f64:
+ return Literal(double(special));
+ default:
+ WASM_UNREACHABLE();
}
}
}
@@ -297,16 +353,20 @@
case i32:
case f32: {
auto ret = Literal(rehash(base, Index(type)));
- if (type == f32) ret = ret.castToF32();
+ if (type == f32)
+ ret = ret.castToF32();
return ret;
}
case i64:
case f64: {
- auto ret = Literal(rehash(base, Index(type)) | (int64_t(rehash(base, Index(type + 1000))) << 32));
- if (type == f64) ret = ret.castToF64();
+ auto ret = Literal(rehash(base, Index(type)) |
+ (int64_t(rehash(base, Index(type + 1000))) << 32));
+ if (type == f64)
+ ret = ret.castToF64();
return ret;
}
- default: WASM_UNREACHABLE();
+ default:
+ WASM_UNREACHABLE();
}
}
};
@@ -316,12 +376,14 @@
// Execute the expression over a set of local values
class Runner : public ExpressionRunner<Runner> {
LocalGenerator& localGenerator;
+
public:
Runner(LocalGenerator& localGenerator) : localGenerator(localGenerator) {}
Flow visitLoop(Loop* curr) {
// loops might be infinite, so must be careful
- // but we can't tell if non-infinite, since we don't have state, so loops are just impossible to optimize for now
+ // but we can't tell if non-infinite, since we don't have state, so loops
+ // are just impossible to optimize for now
trap("loop");
WASM_UNREACHABLE();
}
@@ -357,24 +419,26 @@
abort(); // we should not see this
}
- void trap(const char* why) override {
- throw TrapException();
- }
+ void trap(const char* why) override { throw TrapException(); }
};
// Calculate a hash value based on executing an expression
struct ExecutionHasher {
- std::unordered_map<size_t, std::vector<Expression*>> hashClasses; // hash value => list of expressions that have it, so they may be equal
+ std::unordered_map<size_t, std::vector<Expression*>>
+ hashClasses; // hash value => list of expressions that have it, so they may
+ // be equal
void note(Expression* expr) {
size_t hash;
try {
hash = doHash(expr);
} catch (TrapException& e) {
- // we don't bother trying to handle things that trap TODO: maybe abort the whole thing, move try out, for speed?
+ // we don't bother trying to handle things that trap TODO: maybe abort the
+ // whole thing, move try out, for speed?
return;
}
- hashClasses[hash].push_back(expr); // we depend on expr being unique, so the classes are mathematical sets
+ hashClasses[hash].push_back(expr); // we depend on expr being unique, so the
+ // classes are mathematical sets
}
size_t doHash(Expression* expr) {
@@ -392,15 +456,30 @@
hash = rehash(hash, 4);
hash = rehash(hash, flow.value.type);
switch (flow.value.type) {
- case f32: flow.value = flow.value.castToI32(); break;
- case f64: flow.value = flow.value.castToI64(); break;
- default: {}
+ case f32:
+ flow.value = flow.value.castToI32();
+ break;
+ case f64:
+ flow.value = flow.value.castToI64();
+ break;
+ default: {
+ }
}
switch (flow.value.type) {
- case none: hash = rehash(hash, 5); hash = rehash(hash, 6); break;
- case i32: hash = rehash(hash, flow.value.geti32()); hash = rehash(hash, 7); break;
- case i64: hash = rehash(hash, flow.value.geti64()); hash = rehash(hash, flow.value.geti64() >> 32); break;
- default: WASM_UNREACHABLE();
+ case none:
+ hash = rehash(hash, 5);
+ hash = rehash(hash, 6);
+ break;
+ case i32:
+ hash = rehash(hash, flow.value.geti32());
+ hash = rehash(hash, 7);
+ break;
+ case i64:
+ hash = rehash(hash, flow.value.geti64());
+ hash = rehash(hash, flow.value.geti64() >> 32);
+ break;
+ default:
+ WASM_UNREACHABLE();
}
}
}
@@ -414,9 +493,12 @@
}
// can our optimizer do better on a than b?
-static bool alreadyOptimizable(Expression* input, WasmType localTypes[MAX_LOCAL], Expression* output) {
+static bool alreadyOptimizable(Expression* input,
+ WasmType localTypes[MAX_LOCAL],
+ Expression* output) {
Module temp;
- // make a single function that receives the expressions locals and returns its output
+ // make a single function that receives the expressions locals and returns its
+ // output
auto* func = new Function();
func->name = Name("temp");
func->result = input->type;
@@ -442,7 +524,8 @@
// Given two expressions that hashing suggests might be the same, try
// harder directly on the two to prove or disprove equivalence
bool looksValid(Expression* a, Expression* b) {
- if (a->type != b->type) return false; // hash collision, these are not even the same type
+ if (a->type != b->type)
+ return false; // hash collision, these are not even the same type
// local types must be identical, otherwise the rule isn't even valid to apply
ScanLocals aScanner(a), bScanner(b);
for (Index i = 0; i < MAX_LOCAL; i++) {
@@ -458,12 +541,14 @@
Flow aFlow = Runner(localGenerator).visit(a);
Flow bFlow = Runner(localGenerator).visit(b);
// TODO: breaking
- if (aFlow.value != bFlow.value) return false;
+ if (aFlow.value != bFlow.value)
+ return false;
}
// let's see if this possible optimization is already something our
// optimizer can do: if we optimize the input, do we get something
// as good or better than the output?
- if (alreadyOptimizable(a, aScanner.localTypes, b)) return false;
+ if (alreadyOptimizable(a, aScanner.localTypes, b))
+ return false;
// we see no reason these two should not be joined together in holy optimony
return true;
}
@@ -487,15 +572,21 @@
}
} generalizer(wasm);
- return ExpressionManipulator::flexibleCopy(expr, wasm, [&](Expression* curr) { return generalizer.copy(curr); });
+ return ExpressionManipulator::flexibleCopy(
+ expr, wasm, [&](Expression* curr) { return generalizer.copy(curr); });
}
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
// receive arguments
std::vector<std::string> filenames;
- Options options("wasm-analyze", "Analyze a set of wasm modules. Provide a set of input files, optionally split by 'advice:' (in which case files afterwards are just advice, used to find optimization outputs but not inputs we focus on optimizing)");
- options.add_positional("INFILES", Options::Arguments::N,
- [&](Options *o, const std::string &argument) {
+ Options options(
+ "wasm-analyze",
+ "Analyze a set of wasm modules. Provide a set of input files, optionally "
+ "split by 'advice:' (in which case files afterwards are just advice, used "
+ "to find optimization outputs but not inputs we focus on optimizing)");
+ options.add_positional("INFILES",
+ Options::Arguments::N,
+ [&](Options* o, const std::string& argument) {
filenames.push_back(argument);
});
options.parse(argc, argv);
@@ -550,8 +641,8 @@
}
#endif
- // perform execution hashing, looking for expressions that are functionally equivalent,
- // so one can be optimized to the other
+ // perform execution hashing, looking for expressions that are functionally
+ // equivalent, so one can be optimized to the other
std::cerr << "[hashing executions]\n";
@@ -574,7 +665,8 @@
}
std::cout << " num relevant expressions: " << total << '\n';
}
- std::cout << " num execution classes: " << executionHasher.hashClasses.size() << '\n';
+ std::cout << " num execution classes: " << executionHasher.hashClasses.size()
+ << '\n';
{
size_t max = 0;
for (auto& pair : executionHasher.hashClasses) {
@@ -585,15 +677,17 @@
// Detailed output
{
- // a rule is a connection between one pattern and another, which we think may be equivalent to it,
- // and which may provide a measured benefit
- // TODO: test rules on more random inputs, trying to prove they are not equivalent?
+ // a rule is a connection between one pattern and another, which we think
+ // may be equivalent to it, and which may provide a measured benefit
+ // TODO: test rules on more random inputs, trying to prove they are not
+ // equivalent?
struct Rule {
Expression* from;
Expression* to;
size_t benefit;
- Rule(Expression* from, Expression* to, size_t benefit) : from(from), to(to), benefit(benefit) {}
+ Rule(Expression* from, Expression* to, size_t benefit)
+ : from(from), to(to), benefit(benefit) {}
};
std::vector<Rule> rules;
@@ -603,23 +697,32 @@
for (auto& pair : executionHasher.hashClasses) {
auto& clazz = pair.second;
Index size = clazz.size();
- if (size < 2) continue;
+ if (size < 2)
+ continue;
// consider all pairs, since some may be spurious hash collisions
for (Index i = 0; i < size; i++) {
auto* iExpr = clazz[i];
auto iFreq = freqs[iExpr];
- if (iFreq == 0) continue; // no frequency means no benefit to optimize it; this expression is just a target of optimization, not an origin
+ if (iFreq == 0)
+ continue; // no frequency means no benefit to optimize it; this
+ // expression is just a target of optimization, not an
+ // origin
Index iSize = calcWeight(iExpr);
Expression* best = nullptr;
Index bestSize = -1;
for (Index j = 0; j < size; j++) {
- if (i == j) continue;
+ if (i == j)
+ continue;
auto* jExpr = clazz[j];
Index jSize = calcWeight(jExpr);
// we are looking for a rule where i => j, so we need j to be smaller
- if (iSize <= jSize) continue; // TODO: for equality, look not just at size, but cost etc.
- // a likely candidate, if direct attempts to prove they differ fail, this is worth reporting to the user
- if (best && jSize >= bestSize) continue; // we can't do better
+ if (iSize <= jSize)
+ continue; // TODO: for equality, look not just at size, but cost
+ // etc.
+ // a likely candidate, if direct attempts to prove they differ fail,
+ // this is worth reporting to the user
+ if (best && jSize >= bestSize)
+ continue; // we can't do better
if (looksValid(iExpr, jExpr)) {
best = jExpr;
bestSize = jSize;
@@ -631,15 +734,16 @@
}
}
- // Many rules are part of a more general pattern, for example x + 1 + 1 === x + 2 is
- // closely related to x + 1 + 2 === x + 3. The generalized rule is what the human would
- // write in the optimizer, so to assess the benefit of rules, we must generalize in
- // our output.
+ // Many rules are part of a more general pattern, for example x + 1 + 1 ===
+ // x + 2 is closely related to x + 1 + 2 === x + 3. The generalized rule is
+ // what the human would write in the optimizer, so to assess the benefit of
+ // rules, we must generalize in our output.
std::cerr << "[generalizing]\n";
struct GeneralizedRule : public Rule {
- std::vector<Rule*> rules; // the specific rules underlying this generalization
+ std::vector<Rule*>
+ rules; // the specific rules underlying this generalization
GeneralizedRule(Expression* from, Rule* rule) : Rule(from, nullptr, 0) {
addRule(rule);
@@ -652,13 +756,20 @@
};
// hashed from expression => the generalized rules for that expression
- std::unordered_map<HashedExpression, GeneralizedRule, ExpressionHasher, ExpressionComparer> generalizedRules;
+ std::unordered_map<HashedExpression,
+ GeneralizedRule,
+ ExpressionHasher,
+ ExpressionComparer>
+ generalizedRules;
for (auto& rule : rules) {
- auto generalizedFrom = HashedExpression(generalize(rule.from, global)); // TODO: save memory, don't use global unless needed?
+ auto generalizedFrom = HashedExpression(generalize(
+ rule.from,
+ global)); // TODO: save memory, don't use global unless needed?
auto iter = generalizedRules.find(generalizedFrom);
if (iter == generalizedRules.end()) {
- generalizedRules.emplace(generalizedFrom, GeneralizedRule(generalizedFrom.expr, &rule));
+ generalizedRules.emplace(generalizedFrom,
+ GeneralizedRule(generalizedFrom.expr, &rule));
} else {
iter->second.addRule(&rule);
}
@@ -676,14 +787,19 @@
auto ruleSorter = [](const Rule* a, const Rule* b) {
// primary sorting criteria is the size benefit
auto diff = int64_t(a->benefit) - int64_t(b->benefit);
- if (diff > 0) return true;
- if (diff < 0) return false;
+ if (diff > 0)
+ return true;
+ if (diff < 0)
+ return false;
return size_t(a->from) < size_t(b->from);
};
- std::sort(sortedGeneralizedRules.begin(), sortedGeneralizedRules.end(), [&ruleSorter](const GeneralizedRule* a, const GeneralizedRule* b) {
- return ruleSorter(a, b);
- });
+ std::sort(
+ sortedGeneralizedRules.begin(),
+ sortedGeneralizedRules.end(),
+ [&ruleSorter](const GeneralizedRule* a, const GeneralizedRule* b) {
+ return ruleSorter(a, b);
+ });
std::cout << "sorted possible optimization rules:\n";
@@ -691,15 +807,23 @@
size_t i = 0;
for (auto* item : sortedGeneralizedRules) {
- std::cout << "\n[generalized rule " << (i++) << ": benefit: " << item->benefit << ", (" << (100*double(item->benefit)/totalWeight) << "%)], input pattern:\n" << item->from << '\n';
+ std::cout << "\n[generalized rule " << (i++)
+ << ": benefit: " << item->benefit << ", ("
+ << (100 * double(item->benefit) / totalWeight)
+ << "%)], input pattern:\n"
+ << item->from << '\n';
// show the specific rules underlying the generalized one
std::sort(item->rules.begin(), item->rules.end(), ruleSorter);
for (auto* rule : item->rules) {
- std::cout << "\n[child specific rule benefit: " << rule->benefit << ", (" << (100*double(rule->benefit)/totalWeight) << "%)], possible rule:\n" << rule->from << "\n =->\n" << rule->to << '\n';
+ std::cout << "\n[child specific rule benefit: " << rule->benefit
+ << ", (" << (100 * double(rule->benefit) / totalWeight)
+ << "%)], possible rule:\n"
+ << rule->from << "\n =->\n"
+ << rule->to << '\n';
}
}
}
- // TODO TODO: if all execution hashes of expr are the same, it might be constant (avoids needing to have all constants hashed)
+ // TODO TODO: if all execution hashes of expr are the same, it might be
+ // constant (avoids needing to have all constants hashed)
}
-