Skip to content

Commit

Permalink
speculate in promises (except when inlining them)
Browse files Browse the repository at this point in the history
  • Loading branch information
JanJecmen committed Jun 15, 2022
1 parent 90e0880 commit c572a49
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 62 deletions.
23 changes: 5 additions & 18 deletions rir/src/compiler/analysis/verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TheVerifier {
std::unordered_set<BB*> seenPreds;

void operator()() {
Visitor::run(f->entry, [&](BB* bb) { return verify(bb, false); });
Visitor::run(f->entry, [&](BB* bb) { return verify(bb); });
Visitor::run(f->entry, [&](BB* bb) { seenPreds.erase(bb); });
if (!seenPreds.empty()) {
std::cerr << "The following preds are not reachable from entry: ";
Expand Down Expand Up @@ -98,7 +98,7 @@ class TheVerifier {
return doms.at(c);
}

void verify(BB* bb, bool inPromise) {
void verify(BB* bb) {
if (bb->id >= bb->owner->nextBBId) {
std::cout << "BB" << bb->id << " id is bigger than max ("
<< bb->owner->nextBBId << ")\n";
Expand All @@ -121,7 +121,7 @@ class TheVerifier {
}

for (auto i : *bb) {
verify(i, bb, inPromise);
verify(i, bb);
}
/* This check verifies that our graph is in edge-split format.
Currently we do not rely on this property, however we should
Expand Down Expand Up @@ -196,10 +196,10 @@ class TheVerifier {
}

void verify(Promise* p) {
Visitor::run(p->entry, [&](BB* bb) { verify(bb, true); });
Visitor::run(p->entry, [&](BB* bb) { verify(bb); });
}

void verify(Instruction* i, BB* bb, bool inPromise) {
void verify(Instruction* i, BB* bb) {
if (i->bb() != bb) {
std::cerr << "Error: instruction '";
i->print(std::cerr);
Expand Down Expand Up @@ -269,19 +269,6 @@ class TheVerifier {
});
}

if (i->frameState()) {
if (!inPromise) {
auto fs = i->frameState();
while (fs->next())
fs = fs->next();
if (fs->inPromise) {
std::cerr << "Error: instruction '";
i->print(std::cerr);
std::cerr << "' outermost fs inPromis in body code\n";
ok = false;
}
}
}
if (auto assume = Assume::Cast(i)) {
if (IsType::Cast(assume->arg(0).val())) {
if (!assume->reason.pc()) {
Expand Down
2 changes: 1 addition & 1 deletion rir/src/compiler/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void Compiler::compileClosure(Closure* closure, rir::Function* optFunction,
auto arg = closure->formals().defaultArgs()[idx];
assert(rir::Code::check(arg) && "Default arg not compiled");
auto code = rir::Code::unpack(arg);
auto res = rir2pir.tryCreateArg(code, builder, false);
auto res = rir2pir.tryCreateArg(code, builder);
if (!res) {
failedToCompileDefaultArgs = true;
return;
Expand Down
2 changes: 0 additions & 2 deletions rir/src/compiler/opt/pass_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ PASS(DelayEnv, false, false)
* passes will do the smart parts.
*/
PASS(Inline, false, false)
// PASS(Inline, true, false)

/*
* Goes through every operation that for the general case needs an environment
Expand Down Expand Up @@ -145,7 +144,6 @@ PASS(LoadElision, false, false)
PASS(TypeInference, true, false)

PASS(TypeSpeculation, false, false)
// PASS(TypeSpeculation, true, false)

PASS(PromiseSplitter, false, false)

Expand Down
79 changes: 41 additions & 38 deletions rir/src/compiler/rir2pir/rir2pir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ Checkpoint* Rir2Pir::addCheckpoint(rir::Code* srcCode, Opcode* pos,
return insert.emitCheckpoint(srcCode, pos, stack, inPromise());
}

Value* Rir2Pir::tryCreateArg(rir::Code* promiseCode, Builder& insert,
bool eager) {
Value* Rir2Pir::tryCreateArg(rir::Code* promiseCode, Builder& insert) {
Promise* prom = insert.function->createProm(promiseCode);
{
Builder promiseBuilder(insert.function, prom);
Expand All @@ -175,19 +174,14 @@ Value* Rir2Pir::tryCreateArg(rir::Code* promiseCode, Builder& insert,
}

Value* eagerVal = UnboundValue::instance();
if (eager || Query::pureExceptDeopt(prom)) {
if (Query::pureExceptDeopt(prom)) {
eagerVal = tryInlinePromise(promiseCode, insert);
if (!eagerVal) {
log.warn("Failed to inline a promise");
return nullptr;
}
}

if (eager) {
assert(eagerVal != UnboundValue::instance());
return eagerVal;
}

return insert(new MkArg(prom, eagerVal, insert.env));
}

Expand Down Expand Up @@ -437,7 +431,7 @@ bool Rir2Pir::compileBC(const BC& bc, Opcode* pos, Opcode* nextPos,

// If this call was never executed we might as well compile an
// unconditional deopt.
if (!inPromise() && !inlining() && feedback.taken == 0 &&
if (!inlining() && feedback.taken == 0 &&
insert.function->optFunction->invocationCount() > 1 &&
srcCode->function()->deadCallReached() < 3) {
auto sp =
Expand Down Expand Up @@ -669,8 +663,8 @@ bool Rir2Pir::compileBC(const BC& bc, Opcode* pos, Opcode* nextPos,
// Insert a guard if we want to speculate
if (!staticMonomorphicBuiltin &&
(monomorphicBuiltin || monomorphicClosure || monomorphicSpecial)) {
// Can't speculate in promises
if (inPromise()) {
// Can't speculate while inlining
if (inlining()) {
monomorphicBuiltin = monomorphicClosure = monomorphicSpecial =
false;
} else {
Expand All @@ -683,12 +677,12 @@ bool Rir2Pir::compileBC(const BC& bc, Opcode* pos, Opcode* nextPos,
auto bb = cp->nextBB();
auto dummyPos = bb->begin();

Value* expection = nullptr;
Value* expectation = nullptr;
if (ldfun && localFuns.count(ldfun->varName)) {
auto mk = localFuns.at(ldfun->varName);
if (mk && mk->originalBody->container() ==
BODY(ti.monomorphic)) {
expection = mk;
expectation = mk;
// Even though we statically know the env, we must
// compile an Env::unclosed() closure here, since we
// cannot pass the pir env from the host function to
Expand All @@ -701,37 +695,46 @@ bool Rir2Pir::compileBC(const BC& bc, Opcode* pos, Opcode* nextPos,
guardedCallee = BBTransform::insertCalleeGuard(
compiler, ti,
DeoptReason(ti.feedbackOrigin, DeoptReason::CallTarget),
callee, stableEnv || expection, expection, cp, bb,
callee, stableEnv || expectation, expectation, cp, bb,
dummyPos);
}
}
}

auto eagerEval = [&](Value*& arg, size_t i, bool promiseWrapped) {
if (auto mk = MkArg::Cast(arg)) {
if (mk->isEager()) {
arg = mk->eagerArg();
} else {
auto original = arg;
arg = tryCreateArg(mk->prom()->rirSrc(), insert, true);
if (promiseWrapped)
arg = insert(new MkArg(mk->prom(), arg, mk->env()));
if (!arg) {
log.warn("Failed to compile a promise");
return false;
}
if (i != (size_t)-1 && at(nargs - 1 - i) == original) {
// Inlined argument evaluation might have side effects.
// Let's have a checkpoint here. This checkpoint needs
// to capture the so far evaluated promises.
stack.at(nargs - 1 - i) =
promiseWrapped
? arg
: insert(new MkArg(mk->prom(), arg, mk->env()));
addCheckpoint(srcCode, pos, stack, insert);
}
}
if (!MkArg::Cast(arg)) {
return true;
}

auto mk = MkArg::Cast(arg);
if (mk->isEager()) {
arg = mk->eagerArg();
return true;
}

assert(!inlining());
auto original = arg;
arg = tryInlinePromise(mk->prom()->rirSrc(), insert);
if (!arg) {
log.warn("Failed to inline a promise");
return false;
}

if (promiseWrapped) {
arg = insert(new MkArg(mk->prom(), arg, mk->env()));
}

if (i != (size_t)-1 && at(nargs - 1 - i) == original) {
// Inlined argument evaluation might have side effects.
// Let's have a checkpoint here. This checkpoint needs
// to capture the so far evaluated promises.
stack.at(nargs - 1 - i) =
promiseWrapped
? arg
: insert(new MkArg(mk->prom(), arg, mk->env()));
addCheckpoint(srcCode, pos, stack, insert);
}

return true;
};

Expand Down Expand Up @@ -1453,7 +1456,7 @@ Value* Rir2Pir::tryTranslate(rir::Code* srcCode, Builder& insert, Opcode* start,
auto asBool = insert(
new Identical(branchCondition, branchReason, PirType::val()));

if (!inPromise()) {
if (!inlining()) {
if (auto c = Instruction::Cast(branchCondition)) {
auto likely = c->typeFeedback().value;
if (likely == True::instance() ||
Expand Down
2 changes: 1 addition & 1 deletion rir/src/compiler/rir2pir/rir2pir.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Rir2Pir {
const std::vector<PirType>& initialStack)
__attribute__((warn_unused_result));

Value* tryCreateArg(rir::Code* prom, Builder& insert, bool eager)
Value* tryCreateArg(rir::Code* prom, Builder& insert)
__attribute__((warn_unused_result));

typedef std::unordered_map<Value*, Checkpoint*> CallTargetCheckpoints;
Expand Down
2 changes: 0 additions & 2 deletions rir/src/interpreter/interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1670,8 +1670,6 @@ void deoptFramesWithContext(const CallContext* callCtxt,
// case we leave the result on the top of the stack and the native
// backend knows that if the deopt returns, it should pop the result
// from the stack and return it as the promise's result.

// assert(false);
}
}

Expand Down

0 comments on commit c572a49

Please sign in to comment.