libexpr: turn debug frames into exprs

this way we don't have to even check whether we need a debug frame when
the debugger isn't enabled. not doing this gives us an eval performance
improvement of roughly 7% on nixos system eval and 2% for `nix search`.

Change-Id: I1cdad3de61f865ea54d6e09d63a281688e828768
This commit is contained in:
eldritch horrors
2025-05-01 14:28:05 +00:00
parent dd2c54d9ad
commit 6bd6a2416b
3 changed files with 65 additions and 46 deletions
+7 -42
View File
@@ -1205,19 +1205,6 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
*i.second.chooseByKind(&env2, &env, inheritEnv));
}
auto dts = state.ctx.debug
? makeDebugTraceStacker(
state,
*this,
env2,
getPos()
? std::make_shared<Pos>(state.ctx.positions[getPos()])
: nullptr,
"while evaluating a '%1%' expression",
"let"
)
: nullptr;
body->eval(state, env2, v);
}
@@ -1308,16 +1295,6 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
try {
auto dts = state.ctx.debug
? makeDebugTraceStacker(
state,
*this,
env,
state.ctx.positions[getPos()],
"while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath))
: nullptr;
for (auto const & [partIdx, currentAttrName] : enumerate(attrPath)) {
state.ctx.stats.nrLookups++;
@@ -1619,13 +1596,6 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Evaluate the body. */
try {
auto dts = ctx.debug
? makeDebugTraceStacker(
*this, *lambda.body, env2, ctx.positions[lambda.pos],
"while calling %s",
lambda.getQuotedName(ctx.symbols))
: nullptr;
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
@@ -1758,18 +1728,6 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
void ExprCall::eval(EvalState & state, Env & env, Value & v)
{
auto dts = state.ctx.debug
? makeDebugTraceStacker(
state,
*this,
env,
getPos()
? std::make_shared<Pos>(state.ctx.positions[getPos()])
: nullptr,
"while calling a function"
)
: nullptr;
Value vFun;
fun->eval(state, env, vFun);
@@ -2116,6 +2074,13 @@ void ExprBlackHole::eval(EvalState & state, Env & env, Value & v)
.debugThrow();
}
void ExprDebugFrame::eval(EvalState & state, Env & env, Value & v)
{
auto dts =
makeDebugTraceStacker(state, *inner, env, state.ctx.positions[pos], message);
inner->eval(state, env, v);
}
// always force this to be separate, otherwise forceValue may inline it and take
// a massive perf hit
[[gnu::noinline]]
+36 -4
View File
@@ -330,6 +330,10 @@ struct VarBinder : ExprVisitor
using ExprVisitor::visit;
void visit(ExprDebugFrame & e, std::unique_ptr<Expr> & ptr) override
{
visit(e.inner);
}
void visit(ExprLiteral & e, std::unique_ptr<Expr> & ptr) override;
void visit(ExprVar & e, std::unique_ptr<Expr> & ptr) override;
void visit(ExprInheritFrom & e, std::unique_ptr<Expr> & ptr) override;
@@ -380,13 +384,41 @@ struct DebugVarBinder : VarBinder
OVERRIDE(ExprLiteral)
OVERRIDE(ExprVar)
OVERRIDE(ExprInheritFrom)
OVERRIDE(ExprSelect)
void visit(ExprSelect & e, std::unique_ptr<Expr> & ptr) override
{
es.debug->exprEnvs.insert(std::make_pair(&e, env));
VarBinder::visit(e, ptr);
ptr = std::make_unique<ExprDebugFrame>(e.pos, std::move(ptr), "while evaluating an attribute");
}
OVERRIDE(ExprOpHasAttr)
OVERRIDE(ExprSet)
OVERRIDE(ExprList)
OVERRIDE(ExprLambda)
OVERRIDE(ExprCall)
OVERRIDE(ExprLet)
void visit(ExprLambda & e, std::unique_ptr<Expr> & ptr) override
{
es.debug->exprEnvs.insert(std::make_pair(&e, env));
VarBinder::visit(e, ptr);
e.body = std::make_unique<ExprDebugFrame>(
e.pos, std::move(e.body), HintFmt("while calling %s", e.getQuotedName(es.symbols)).str()
);
}
void visit(ExprCall & e, std::unique_ptr<Expr> & ptr) override
{
es.debug->exprEnvs.insert(std::make_pair(&e, env));
VarBinder::visit(e, ptr);
ptr = std::make_unique<ExprDebugFrame>(e.pos, std::move(ptr), "while calling a function");
}
void visit(ExprLet & e, std::unique_ptr<Expr> & ptr) override
{
es.debug->exprEnvs.insert(std::make_pair(&e, env));
VarBinder::visit(e, ptr);
e.body = std::make_unique<ExprDebugFrame>(
e.pos, std::move(e.body), HintFmt("while evaluating a '%1%' expression", "let").str()
);
}
OVERRIDE(ExprWith)
OVERRIDE(ExprIf)
OVERRIDE(ExprAssert)
+22
View File
@@ -41,6 +41,7 @@ JSON printAttrPathToJson(const SymbolTable & symbols, const AttrPath & attrPath)
/* Abstract syntax of Nix expressions. */
struct ExprDebugFrame;
struct ExprLiteral;
struct ExprString;
struct ExprPath;
@@ -70,6 +71,7 @@ struct ExprBlackHole;
struct ExprVisitor
{
virtual void visit(ExprDebugFrame & e, std::unique_ptr<Expr> & ptr) = 0;
virtual void visit(ExprLiteral & e, std::unique_ptr<Expr> & ptr) = 0;
virtual void visit(ExprVar & e, std::unique_ptr<Expr> & ptr) = 0;
virtual void visit(ExprInheritFrom & e, std::unique_ptr<Expr> & ptr) = 0;
@@ -146,6 +148,26 @@ inline void ExprVisitor::visit(std::unique_ptr<Expr> & ptr)
ptr->accept(*this, ptr);
}
struct ExprDebugFrame : Expr
{
std::unique_ptr<Expr> inner;
std::string message;
ExprDebugFrame(PosIdx pos, std::unique_ptr<Expr> inner, std::string message)
: Expr(pos)
, inner(std::move(inner))
, message(std::move(message))
{
}
JSON toJSON(const SymbolTable & symbols) const override
{
return inner->toJSON(symbols);
}
void eval(EvalState & state, Env & env, Value & v) override;
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
};
struct ExprLiteral : Expr
{
protected: