Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating point cost values with some heuristics for ExhaustiveSynthesis #399

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 77 additions & 15 deletions lib/Infer/ExhaustiveSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ namespace {
static cl::opt<bool> EnableBigQuery("souper-exhaustive-synthesis-enable-big-query",
cl::desc("Enable big query in exhaustive synthesis (default=false)"),
cl::init(false));
static cl::opt<std::string> CostModel("souper-cost-model", cl::desc("Cost Model"),
cl::init("default"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the description you should list the possible choices

}

// TODO
Expand Down Expand Up @@ -86,6 +88,60 @@ namespace {
// experiment with synthesizing at reduced bitwidth, then expanding the result
// aggressively avoid calling into the solver

using CostFunctionType = std::function<float(Inst *, bool)>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use double instead of float everywhere


float defaultCostFunction(Inst* I, bool IgnoreDepsWithExternalUses) {
return souper::cost(I, IgnoreDepsWithExternalUses);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might as well move the code over here, instead of calling out to somewhere else

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old synthesis still relies on this.
And having it support new cost models is tricky.
(eg; #395)

}

// TODO : Derive these randomly to maximize infer rate with test-infer.sh
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does "derive these randomly" mean?

Copy link
Collaborator Author

@manasij7479 manasij7479 Jan 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pick a plausible cost range and a sample frequency for each instruction.
For all possible cost combinations, run test-infer.sh on the solver/alive directory and pick the combinations with the best infer success rate.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't explain it here, explain it in the code

float getCost(Inst::Kind K) {
switch (K) {
case souper::Inst::Var:
case souper::Inst::Const:
case souper::Inst::Phi:
return 0;
case souper::Inst::BSwap:
case souper::Inst::CtPop:
case souper::Inst::Cttz:
case souper::Inst::Ctlz:
case souper::Inst::SDiv:
case souper::Inst::UDiv:
case souper::Inst::SRem:
case souper::Inst::URem:
return 5;
case souper::Inst::Select:
return 3;
case souper::Inst::Mul:
return 2.5f;
case souper::Inst::Add:
case souper::Inst::Sub:
return 1.5f;
default:
return 1;
}
}

// TODO: Recognize simple patterns
static float costHelper(Inst *I, Inst *Root, std::set<Inst *> &Visited,
bool IgnoreDepsWithExternalUses) {
if (!Visited.insert(I).second)
return 0;
if (IgnoreDepsWithExternalUses && I != Root &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should leave a comment explaining the rationale for counting things with external uses are zero cost

Root->DepsWithExternalUses.find(I) != Root->DepsWithExternalUses.end()) {
return 0;
}
float Cost = getCost(I->K);
for (auto Op : I->Ops)
Cost += costHelper(Op, Root, Visited, IgnoreDepsWithExternalUses);
return Cost;
}

float simpleHeuristicsCostFunction(Inst* I, bool IgnoreDepsWithExternalUses) {
std::set<Inst *> Visited;
return costHelper(I, I, Visited, IgnoreDepsWithExternalUses);
}

void hasConstantHelper(Inst *I, std::set<Inst *> &Visited,
std::vector<Inst *> &ConstList) {
// FIXME this only works for one constant and keying by name is bad
Expand Down Expand Up @@ -119,9 +175,9 @@ std::vector<Inst *> matchWidth(Inst *I, unsigned NewW, InstContext &IC) {
return { I };
}

void addGuess(Inst *RHS, int MaxCost, std::vector<Inst *> &Guesses,
int &TooExpensive) {
if (souper::cost(RHS) < MaxCost)
void addGuess(Inst *RHS, float MaxCost, std::vector<Inst *> &Guesses,
float &TooExpensive, CostFunctionType &Cost) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than passing the cost function around a lot, investigate whether there's a better place to stash this pointer, for example in one of the context objects

if (Cost(RHS, false) < MaxCost)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when passing a boolean constant, follow the convention of including the name of the flag as a comment

Guesses.push_back(RHS);
else
TooExpensive++;
Expand All @@ -138,9 +194,9 @@ bool prune (Inst *I, std::vector<Inst *> &ReservedInsts) {

void getGuesses(std::vector<Inst *> &Guesses,
const std::vector<Inst *> &Inputs,
int Width, int LHSCost,
int Width, float LHSCost,
InstContext &IC, Inst *PrevInst, Inst *PrevSlot,
int &TooExpensive) {
float &TooExpensive, CostFunctionType &Cost) {

std::vector<Inst *> PartialGuesses;

Expand Down Expand Up @@ -177,7 +233,7 @@ void getGuesses(std::vector<Inst *> &Guesses,

for (auto V : matchWidth(Comp, Width, IC)) {
auto N = IC.getInst(K, Width, { V });
addGuess(N, LHSCost, PartialGuesses, TooExpensive);
addGuess(N, LHSCost, PartialGuesses, TooExpensive, Cost);
}
}
}
Expand Down Expand Up @@ -264,7 +320,7 @@ void getGuesses(std::vector<Inst *> &Guesses,
continue;
auto N = IC.getInst(K, Inst::isCmp(K) ? 1 : OpWidth, { V1i, V2i });
for (auto MatchedWidthN : matchWidth(N, Width, IC)) {
addGuess(MatchedWidthN, LHSCost, PartialGuesses, TooExpensive);
addGuess(MatchedWidthN, LHSCost, PartialGuesses, TooExpensive, Cost);
}
}
}
Expand Down Expand Up @@ -318,7 +374,7 @@ void getGuesses(std::vector<Inst *> &Guesses,
auto MatchedWidthL = matchWidth(L, 1, IC);
auto SelectInst = IC.getInst(Inst::Select,
Width, { MatchedWidthL[0], V1i, V2i });
addGuess(SelectInst, LHSCost, PartialGuesses, TooExpensive);
addGuess(SelectInst, LHSCost, PartialGuesses, TooExpensive, Cost);
}
}
}
Expand Down Expand Up @@ -351,7 +407,7 @@ void getGuesses(std::vector<Inst *> &Guesses,
if (prune(JoinedGuess, CurrSlots)) {
for (auto S : CurrSlots)
getGuesses(Guesses, Inputs, S->Width,
LHSCost, IC, JoinedGuess, S, TooExpensive);
LHSCost, IC, JoinedGuess, S, TooExpensive, Cost);
}
}
}
Expand Down Expand Up @@ -445,18 +501,24 @@ ExhaustiveSynthesis::synthesize(SMTLIBSolver *SMTSolver,
if (DebugLevel > 1)
llvm::errs() << "got " << Inputs.size() << " candidates from LHS\n";

CostFunctionType Cost;
if (CostModel == "default") {
Cost = defaultCostFunction;
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the else clause needs to be an error

Cost = simpleHeuristicsCostFunction;
}

int LHSCost = souper::cost(LHS, /*IgnoreDepsWithExternalUses=*/true);
float LHSCost = Cost(LHS, /*IgnoreDepsWithExternalUses=*/true);

int TooExpensive = 0;
float TooExpensive = 0;
std::vector<Inst *> Guesses;
getGuesses(Guesses, Inputs, LHS->Width,
LHSCost, IC, nullptr, nullptr, TooExpensive);
LHSCost, IC, nullptr, nullptr, TooExpensive, Cost);

// add nops guesses separately
for (auto I : Inputs) {
for (auto V : matchWidth(I, LHS->Width, IC))
addGuess(V, LHSCost, Guesses, TooExpensive);
addGuess(V, LHSCost, Guesses, TooExpensive, Cost);
}

std::error_code EC;
Expand All @@ -467,8 +529,8 @@ ExhaustiveSynthesis::synthesize(SMTLIBSolver *SMTSolver,
// CEGIS is that we can synthesize in precisely increasing cost
// order, and not try to somehow teach the solver how to do that
std::stable_sort(Guesses.begin(), Guesses.end(),
[](Inst *a, Inst *b) -> bool {
return souper::cost(a) < souper::cost(b);
[&Cost](Inst *a, Inst *b) -> bool {
return Cost(a, false) < Cost(b, false);
});

if (DebugLevel > 1)
Expand Down
11 changes: 11 additions & 0 deletions test/Infer/syn-shl-from-add.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
; REQUIRES: solver, solver-model

; RUN: %souper-check %solver -reinfer-rhs -souper-exhaustive-synthesis -souper-exhaustive-synthesis-num-instructions=1 -souper-cost-model=heuristics %s > %t
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't a great cost model test, since many different cost models would allow this optimization to go.
please try to find a place in our unit tests where you can test the cost model directly, including whether it properly deals with the costs of external uses.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default one (without ignoring cost) does not infer this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not a good test case and wants to be improved

; RUN: %FileCheck %s < %t

%b:i64 = var
%a = add %b, %b
infer %a
%r2 = shl %b, 1
result %r2
; CHECK: RHS inferred successfully, no cost regression