diff --git a/eval/eval/evaluator_core.cc b/eval/eval/evaluator_core.cc index 0aa6000a4..458a56d0b 100644 --- a/eval/eval/evaluator_core.cc +++ b/eval/eval/evaluator_core.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/log/absl_check.h" @@ -39,6 +40,23 @@ void FlatExpressionEvaluatorState::Reset() { } const ExpressionStep* ExecutionFrame::Next() { + if (ABSL_PREDICT_TRUE(pc_ < execution_path_.size())) { + const ExpressionStep* step = &execution_path_[pc_++]; + ABSL_ASSUME(step != nullptr); + return step; + } + // Normal completion of the top-level program. Handled inline since every + // evaluation ends here. + if (ABSL_PREDICT_TRUE(pc_ == execution_path_.size() && call_stack_.empty())) { + return nullptr; + } + return ReturnFromSubexpression(); +} + +// Kept out of line so that `Next()` stays small enough to inline into the +// `Evaluate` loop. +ABSL_ATTRIBUTE_NOINLINE const ExpressionStep* +ExecutionFrame::ReturnFromSubexpression() { while (true) { const size_t end_pos = execution_path_.size(); diff --git a/eval/eval/evaluator_core.h b/eval/eval/evaluator_core.h index f25be8448..fab393b8e 100644 --- a/eval/eval/evaluator_core.h +++ b/eval/eval/evaluator_core.h @@ -495,6 +495,12 @@ class ExecutionFrame : public ExecutionFrameBase { size_t expected_stack_size; }; + // Called by `Next()` when `pc_` reaches the end of the current execution + // path. If a subexpression is being evaluated, returns to the caller (storing + // the result in the caller's comprehension slot) and returns the caller's + // next step. Returns nullptr once the top-level expression is complete. + const ExpressionStep* ReturnFromSubexpression(); + size_t pc_; // pc_ - Program Counter. Current position on execution path. ExecutionPathView execution_path_; EvaluatorStack* absl_nonnull const value_stack_;