35 using ConstStack = std::vector<std::optional<double>>;
39 const auto& cfg_blocks = block_result.cfg_blocks;
47 if (!cfg_blocks.empty()) {
54 for (
size_t i = 0; i < cfg_blocks.size(); ++i) {
57 if (i > 0 && !cfg_blocks[i].predecessors.empty()) {
59 for (
size_t j = 1; j < cfg_blocks[i].predecessors.size(); ++j) {
60 int p_idx = cfg_blocks[i].predecessors[j];
63 if (new_in.size() != other_stack.size()) {
65 std::format(
"Stack depth mismatch during constant "
66 "propagation at block {}: {} vs {}",
67 i, new_in.size(), other_stack.size()));
71 for (
size_t k = 0; k < new_in.size(); ++k) {
72 if (new_in[k] != other_stack[k]) {
73 new_in[k] = std::nullopt;
78 new_in = ConstStack{};
87 for (
int j = cfg_blocks[i].start_token_idx;
88 j < cfg_blocks[i].end_token_idx; ++j) {
89 const auto& token = tokens[j];
92 if (current_stack.size() <
93 static_cast<size_t>(behavior.arity)) {
94 for (
size_t k = 0; k < static_cast<size_t>(behavior.arity) -
97 current_stack.insert(current_stack.begin(),
102 std::vector<std::optional<double>> args;
103 for (
int k = 0; k < behavior.arity; ++k) {
104 args.push_back(current_stack.back());
105 current_stack.pop_back();
107 std::ranges::reverse(args);
109 auto all_const = [&](
const auto& a) {
return a.has_value(); };
110 bool can_compute = std::ranges::all_of(args, all_const);
112 auto push_result = [&](std::optional<double> res) {
113 current_stack.push_back(res);
116 auto push_n = [&](
int n, std::optional<double> val) {
117 for (
int k = 0; k < n; ++k) {
118 current_stack.push_back(val);
123 switch (token.type) {
126 std::get<TokenPayloadNumber>(token.payload).value);
129 push_result(std::numbers::pi_v<double>);
133 push_result(can_compute ? std::optional(args[0].value() +
138 push_result(can_compute ? std::optional(args[0].value() -
143 push_result(can_compute ? std::optional(args[0].value() *
148 push_result(can_compute ? std::optional(args[0].value() /
153 push_result(can_compute
154 ? std::optional(std::fmod(args[0].value(),
163 args[0].value() > args[1].value() ? 1.0 : 0.0)
170 args[0].value() < args[1].value() ? 1.0 : 0.0)
176 ? std::optional(args[0].value() == args[1].value()
184 ? std::optional(args[0].value() >= args[1].value()
192 ? std::optional(args[0].value() <= args[1].value()
200 push_result(can_compute
201 ? std::optional((args[0].value() > 0 &&
208 push_result(can_compute
209 ? std::optional((args[0].value() > 0 ||
216 push_result(can_compute
217 ? std::optional(((args[0].value() > 0) !=
218 (args[1].value() > 0))
226 ? std::optional(args[0].value() > 0 ? 0.0 : 1.0)
231 push_result(can_compute
232 ? std::optional(std::pow(args[0].value(),
237 push_result(can_compute
238 ? std::optional(std::sqrt(args[0].value()))
243 push_result(can_compute
244 ? std::optional(std::exp(args[0].value()))
248 push_result(can_compute
249 ? std::optional(std::exp2(args[0].value()))
253 push_result(can_compute
254 ? std::optional(std::log(args[0].value()))
258 push_result(can_compute
259 ? std::optional(std::log2(args[0].value()))
263 push_result(can_compute
264 ? std::optional(std::log10(args[0].value()))
269 push_result(can_compute
270 ? std::optional(std::sin(args[0].value()))
274 push_result(can_compute
275 ? std::optional(std::cos(args[0].value()))
279 push_result(can_compute
280 ? std::optional(std::tan(args[0].value()))
284 push_result(can_compute
285 ? std::optional(std::asin(args[0].value()))
289 push_result(can_compute
290 ? std::optional(std::acos(args[0].value()))
294 push_result(can_compute
295 ? std::optional(std::atan(args[0].value()))
299 push_result(can_compute
300 ? std::optional(std::atan2(args[0].value(),
306 push_result(can_compute
307 ? std::optional(std::sinh(args[0].value()))
311 push_result(can_compute
312 ? std::optional(std::cosh(args[0].value()))
316 push_result(can_compute
317 ? std::optional(std::tanh(args[0].value()))
322 push_result(can_compute
323 ? std::optional(std::floor(args[0].value()))
327 push_result(can_compute
328 ? std::optional(std::ceil(args[0].value()))
332 push_result(can_compute
333 ? std::optional(std::round(args[0].value()))
337 push_result(can_compute
338 ? std::optional(std::trunc(args[0].value()))
343 push_result(can_compute
344 ? std::optional(std::abs(args[0].value()))
348 push_result(can_compute ? std::optional(-args[0].value())
352 push_result(can_compute ? std::optional([&]() ->
double {
353 double val = args[0].value();
365 push_result(can_compute
366 ? std::optional(std::copysign(
367 args[0].value(), args[1].value()))
371 push_result(can_compute
372 ? std::optional(std::fma(args[0].value(),
379 push_result(can_compute
380 ? std::optional(std::max(args[0].value(),
385 push_result(can_compute
386 ? std::optional(std::min(args[0].value(),
391 push_result(can_compute
392 ? std::optional(std::clamp(args[0].value(),
399 push_result(can_compute
400 ? std::optional(args[0].value() > 0
407 push_result(can_compute
408 ? std::optional(
static_cast<double>(
409 static_cast<int64_t
>(
410 std::nearbyint(args[0].value())) &
411 static_cast<int64_t
>(
412 std::nearbyint(args[1].value()))))
416 push_result(can_compute
417 ? std::optional(
static_cast<double>(
418 static_cast<int64_t
>(
419 std::nearbyint(args[0].value())) |
420 static_cast<int64_t
>(
421 std::nearbyint(args[1].value()))))
425 push_result(can_compute
426 ? std::optional(
static_cast<double>(
427 static_cast<int64_t
>(
428 std::nearbyint(args[0].value())) ^
429 static_cast<int64_t
>(
430 std::nearbyint(args[1].value()))))
434 push_result(can_compute
435 ? std::optional(
static_cast<double>(
436 ~
static_cast<int64_t
>(
437 std::nearbyint(args[0].value()))))
442 push_n(behavior.arity + behavior.stack_effect,