• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vla5924-practice / compiler-project / 13883670119

16 Mar 2025 01:05PM UTC coverage: 77.261% (-0.01%) from 77.273%
13883670119

push

github

web-flow
Refactor ast::Node (add numChildren method, use in64_t) (#223)

* Add numChildren method to be called instead of `node->children.size()`
* Use int64_t instead of long int everywhere
* Related refactoring (includes, unsigned suffixes, const &)

90 of 99 new or added lines in 7 files covered. (90.91%)

1 existing line in 1 file now uncovered.

4373 of 5660 relevant lines covered (77.26%)

250.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.71
/compiler/lib/backend/ast/optimizer/optimizer.cpp
1
#include "optimizer/optimizer.hpp"
2

3
#include <algorithm>
4
#include <cstdint>
5
#include <variant>
6

7
#include "compiler/ast/node.hpp"
8
#include "compiler/ast/node_type.hpp"
9
#include "compiler/utils/language.hpp"
10

11
#include "optimizer/optimizer_context.hpp"
12

13
using namespace ast;
14
using namespace optimizer;
15

16
namespace language = utils::language;
17

18
namespace {
19

20
bool isLiteral(const Node::Ptr &node) {
×
21
    switch (node->type) {
×
22
    case NodeType::IntegerLiteralValue:
×
23
    case NodeType::FloatingPointLiteralValue:
24
    case NodeType::StringLiteralValue:
25
        return true;
×
26
    default:
×
27
        return false;
×
28
    }
29
}
30

31
bool isAssignment(const Node::Ptr &node) {
68✔
32
    return node->type == NodeType::BinaryOperation && node->binOp() == BinaryOperation::Assign;
68✔
33
}
34

35
bool isZeroIntLiteral(const Node::Ptr &node) {
2✔
36
    return node->type == NodeType::IntegerLiteralValue && node->intNum() == 0;
2✔
37
}
38

39
bool isNonZeroIntLiteral(const Node::Ptr &node) {
16✔
40
    return node->type == NodeType::IntegerLiteralValue && node->intNum() != 0;
16✔
41
}
42

43
bool isZeroFloatLiteral(const Node::Ptr &node) {
1✔
44
    return node->type == NodeType::FloatingPointLiteralValue && node->fpNum() == 0.0;
1✔
45
}
46

47
bool isNonZeroFloatLiteral(const Node::Ptr &node) {
9✔
48
    return node->type == NodeType::FloatingPointLiteralValue && node->fpNum() != 0.0;
9✔
49
}
50

51
bool isTruthyLiteral(const Node::Ptr &node) {
16✔
52
    return isNonZeroIntLiteral(node) || isNonZeroFloatLiteral(node);
16✔
53
}
54

55
bool isFalsyLiteral(const Node::Ptr &node) {
2✔
56
    return isZeroIntLiteral(node) || isZeroFloatLiteral(node);
2✔
57
}
58

59
bool isNumericLiteral(const Node::Ptr &node) {
223✔
60
    return node->type == NodeType::IntegerLiteralValue || node->type == NodeType::FloatingPointLiteralValue;
223✔
61
}
62

63
bool isModifiedVariable(const Node::Ptr &node, OptimizerContext &ctx) {
23✔
64
    return node->type == NodeType::VariableName && ctx.findVariable(node).attributes.modified;
23✔
65
}
66

67
bool isNonModifiedVariable(const Node::Ptr &node, OptimizerContext &ctx) {
164✔
68
    return node->type == NodeType::VariableName && !ctx.findVariable(node).attributes.modified &&
222✔
69
           node->parent->type != NodeType::VariableDeclaration;
222✔
70
}
71

72
bool isVariableWithType(const Node::Ptr &node, TypeId typeId, OptimizerContext &ctx) {
17✔
73
    return node->type == NodeType::VariableName && ctx.findVariable(node).type == typeId;
17✔
74
}
75

76
bool canBeConstantInt(const Node::Ptr &node, OptimizerContext &ctx) {
15✔
77
    return node->type == NodeType::IntegerLiteralValue || isVariableWithType(node, BuiltInTypes::IntType, ctx);
15✔
78
}
79

80
bool canBeConstantFloat(const Node::Ptr &node, OptimizerContext &ctx) {
7✔
81
    return node->type == NodeType::FloatingPointLiteralValue || isVariableWithType(node, BuiltInTypes::FloatType, ctx);
7✔
82
}
83

84
} // namespace
85

86
int64_t calculateIntOperation(Node::Ptr &first, Node::Ptr &second, BinaryOperation operation, OptimizerContext &ctx) {
13✔
87
    int64_t lhs = first->type == NodeType::VariableName ? std::get<int64_t>(ctx.findVariableValue(first->str()))
13✔
88
                                                        : first->intNum();
8✔
89
    int64_t rhs = second->type == NodeType::VariableName ? std::get<int64_t>(ctx.findVariableValue(second->str()))
13✔
90
                                                         : second->intNum();
12✔
91

92
    switch (operation) {
13✔
93
    case BinaryOperation::Add:
13✔
94
        return lhs + rhs;
13✔
95
    case BinaryOperation::Sub:
×
96
        return lhs - rhs;
×
97
    case BinaryOperation::Mult:
×
98
        return lhs * rhs;
×
99
    case BinaryOperation::Div:
×
100
        return lhs / rhs;
×
101
    case BinaryOperation::Equal:
×
102
        return lhs == rhs;
×
103
    case BinaryOperation::And:
×
104
        return lhs && rhs;
×
105
    case BinaryOperation::Or:
×
106
        return lhs || rhs;
×
107
    case BinaryOperation::Greater:
×
108
        return lhs > rhs;
×
109
    case BinaryOperation::GreaterEqual:
×
110
        return lhs >= rhs;
×
111
    case BinaryOperation::Less:
×
112
        return lhs < rhs;
×
113
    case BinaryOperation::LessEqual:
×
114
        return lhs <= rhs;
×
115
    case BinaryOperation::NotEqual:
×
116
        return lhs != rhs;
×
117
    default:
×
118
        return 0;
×
119
    }
120
}
121

122
double calculateFloatOperation(Node::Ptr &first, Node::Ptr &second, BinaryOperation operation, OptimizerContext &ctx) {
3✔
123
    double lhs =
124
        first->type == NodeType::VariableName ? std::get<double>(ctx.findVariableValue(first->str())) : first->fpNum();
3✔
125
    double rhs = second->type == NodeType::VariableName ? std::get<double>(ctx.findVariableValue(second->str()))
3✔
126
                                                        : second->fpNum();
2✔
127

128
    switch (operation) {
3✔
129
    case BinaryOperation::FAdd:
3✔
130
        return lhs + rhs;
3✔
131
    case BinaryOperation::FSub:
×
132
        return lhs - rhs;
×
133
    case BinaryOperation::FMult:
×
134
        return lhs * rhs;
×
135
    case BinaryOperation::FDiv:
×
136
        return lhs / rhs;
×
137
    case BinaryOperation::FEqual:
×
138
        return lhs == rhs;
×
139
    case BinaryOperation::FAnd:
×
140
        return lhs && rhs;
×
141
    case BinaryOperation::FOr:
×
142
        return lhs || rhs;
×
143
    case BinaryOperation::FGreater:
×
144
        return lhs > rhs;
×
145
    case BinaryOperation::FGreaterEqual:
×
146
        return lhs >= rhs;
×
147
    case BinaryOperation::FLess:
×
148
        return lhs < rhs;
×
149
    case BinaryOperation::FLessEqual:
×
150
        return lhs <= rhs;
×
151
    case BinaryOperation::FNotEqual:
×
152
        return lhs != rhs;
×
153
    default:
×
154
        return 0.0;
×
155
    }
156
}
157

158
bool constantPropagation(Node::Ptr &first, Node::Ptr &second, OptimizerContext &ctx) {
34✔
159
    auto parent = first->parent;
34✔
160
    if (isAssignment(parent) || isModifiedVariable(first, ctx) || isModifiedVariable(second, ctx))
34✔
161
        return false;
24✔
162
    if (canBeConstantInt(first, ctx) && canBeConstantInt(second, ctx)) {
10✔
163
        parent->type = NodeType::IntegerLiteralValue;
5✔
164
        parent->value = calculateIntOperation(first, second, parent->binOp(), ctx);
5✔
165
        parent->children.clear();
5✔
166
        return true;
5✔
167
    }
168
    if (canBeConstantFloat(first, ctx) && canBeConstantFloat(second, ctx)) {
5✔
169
        parent->type = NodeType::FloatingPointLiteralValue;
2✔
170
        parent->value = calculateFloatOperation(first, second, parent->binOp(), ctx);
2✔
171
        parent->children.clear();
2✔
172
        return true;
2✔
173
    }
174
    return false;
3✔
175
}
34✔
176

177
void processTypeConversion(Node::Ptr &node, OptimizerContext &ctx) {
10✔
178
    Node::Ptr &operand = node->lastChild();
10✔
179
    if (isNumericLiteral(operand)) {
10✔
180
        if (node->firstChild()->typeId() == BuiltInTypes::FloatType) {
6✔
181
            node->type = NodeType::FloatingPointLiteralValue;
5✔
182
            node->value = static_cast<double>(operand->intNum());
5✔
183
        } else {
184
            node->type = NodeType::IntegerLiteralValue;
1✔
185
            node->value = static_cast<int64_t>(operand->fpNum());
1✔
186
        }
187
        node->children.clear();
6✔
188
        return;
6✔
189
    }
190

191
    if (isNonModifiedVariable(operand, ctx)) {
4✔
192
        const std::string &varName = operand->str();
4✔
193
        if (node->firstChild()->typeId() == BuiltInTypes::FloatType) {
4✔
194
            node->type = NodeType::FloatingPointLiteralValue;
1✔
195
            node->value = static_cast<double>(std::get<int64_t>(ctx.findVariableValue(varName)));
1✔
196
        } else {
197
            node->type = NodeType::IntegerLiteralValue;
3✔
198
            node->value = static_cast<int64_t>(std::get<double>(ctx.findVariableValue(varName)));
3✔
199
        }
200
        node->children.clear();
4✔
201
    }
202
}
203

204
bool constantFolding(Node::Ptr &first, Node::Ptr &second, OptimizerContext &ctx) {
43✔
205
    auto parent = first->parent;
43✔
206
    if (first->type == NodeType::IntegerLiteralValue && second->type == NodeType::IntegerLiteralValue) {
43✔
207
        parent->type = NodeType::IntegerLiteralValue;
8✔
208
        parent->value = calculateIntOperation(first, second, parent->binOp(), ctx);
8✔
209
        parent->children.clear();
8✔
210
        return true;
8✔
211
    }
212
    if (first->type == NodeType::FloatingPointLiteralValue && second->type == NodeType::FloatingPointLiteralValue) {
35✔
213
        parent->type = NodeType::FloatingPointLiteralValue;
1✔
214
        parent->value = calculateFloatOperation(first, second, parent->binOp(), ctx);
1✔
215
        parent->children.clear();
1✔
216
        return true;
1✔
217
    }
218
    return false;
34✔
219
}
43✔
220

221
void variablePropagation(Node::Ptr &node, OptimizerContext &ctx) {
7✔
222
    const std::string &varName = node->str();
7✔
223
    if (!ctx.hasVariable(varName))
7✔
224
        return;
1✔
225
    auto variableIter = ctx.findVariableValue(varName);
6✔
226
    if (ctx.findVariable(node).type == BuiltInTypes::FloatType) {
6✔
227
        node->type = NodeType::FloatingPointLiteralValue;
1✔
228
        node->value = std::get<double>(variableIter);
1✔
229
    } else {
230
        node->type = NodeType::IntegerLiteralValue;
5✔
231
        node->value = std::get<int64_t>(variableIter);
5✔
232
    }
233
}
234

235
void pushVariableAttribute(Node::Ptr &node, Node::Ptr &child, OptimizerContext &ctx) {
62✔
236
    TypeId type;
237
    auto parent = node->parent;
62✔
238
    for (auto &iter : parent->children) {
228✔
239
        if (iter->type == NodeType::TypeName) {
166✔
240
            type = iter->typeId();
36✔
241
            continue;
36✔
242
        }
243
        if (iter->type == NodeType::VariableName) {
130✔
244
            const std::string &varName = iter->str();
42✔
245
            if (type == BuiltInTypes::IntType)
42✔
246
                ctx.values.front().emplace(varName, child->intNum()); // fix
24✔
247
            else if (type == BuiltInTypes::FloatType)
18✔
248
                ctx.values.front().emplace(varName, child->fpNum());
12✔
249
        }
250
    }
251
}
62✔
252

253
void processExpression(Node::Ptr &node, OptimizerContext &ctx);
254

255
void copyExpression(const Node::Ptr &node, Node::Ptr &newExpr, std::unordered_map<std::string, Node::Ptr> &map) {
8✔
256
    for (const auto &child : node->children) {
20✔
257
        auto type = child->type;
12✔
258
        switch (type) {
12✔
259
        case NodeType::BinaryOperation:
4✔
260
            newExpr->children.emplace_back(new Node(child->binOp(), newExpr));
4✔
261
            copyExpression(child, newExpr->children.back(), map);
4✔
262
            break;
4✔
263
        case NodeType::TypeConversion:
×
264
            newExpr->children.emplace_back(new Node(NodeType::TypeConversion, newExpr));
×
265
            copyExpression(child, newExpr->children.back(), map);
×
266
            break;
×
267
        case NodeType::IntegerLiteralValue:
×
268
            newExpr->children.emplace_back(new Node(child->intNum(), newExpr));
×
269
            break;
×
270
        case NodeType::FloatingPointLiteralValue:
×
271
            newExpr->children.emplace_back(new Node(child->fpNum(), newExpr));
×
272
            break;
×
273
        case NodeType::VariableName:
8✔
274
            newExpr->children.emplace_back(map[child->str()]);
8✔
275
            newExpr->children.back()->parent = newExpr;
8✔
276
            break;
8✔
277
        case NodeType::TypeName:
×
278
            newExpr->children.emplace_back(new Node(child->typeId(), newExpr));
×
279
            break;
×
280
        default:
×
281
            break;
×
282
        }
283
    }
284
}
8✔
285

286
void processFunctionCall(Node::Ptr &node, Node functionRoot) {
6✔
287
    if (functionRoot.numChildren() != 1U)
6✔
UNCOV
288
        return;
×
289
    auto returnExpr = functionRoot.lastChild()->lastChild();
6✔
290
    if (isNumericLiteral(returnExpr->firstChild())) {
6✔
291
        Node::Ptr newExpr;
2✔
292
        auto literal = returnExpr->firstChild();
2✔
293
        if (literal->type == NodeType::IntegerLiteralValue)
2✔
294
            newExpr = std::make_shared<Node>(literal->intNum(), node->parent);
2✔
295
        if (literal->type == NodeType::FloatingPointLiteralValue)
2✔
296
            newExpr = std::make_shared<Node>(literal->fpNum(), node->parent);
×
297
        node = newExpr;
2✔
298
    }
2✔
299

300
    if (node->numChildren() > 1U) {
6✔
301
        std::unordered_map<std::string, Node::Ptr> map;
4✔
302
        auto nodeArgsIter = node->secondChild()->children.begin();
4✔
303
        for (auto &argsIter : functionRoot.parent->secondChild()->children) {
16✔
304
            map[argsIter->lastChild()->str()] = (*nodeArgsIter)->firstChild();
12✔
305
            nodeArgsIter++;
12✔
306
        }
307
        Node::Ptr newExpr = std::make_shared<Node>(NodeType::Expression, node->parent);
4✔
308
        copyExpression(returnExpr, newExpr, map);
4✔
309
        node = newExpr->firstChild();
4✔
310
        node->parent = newExpr->parent;
4✔
311
    }
4✔
312
}
6✔
313

314
bool processBinaryOperation(Node::Ptr &node, OptimizerContext &ctx) {
10✔
315
    auto first = node->firstChild();
10✔
316
    auto second = node->lastChild();
10✔
317
    bool haveFunctionCall = false;
10✔
318
    if (first->type == NodeType::BinaryOperation)
10✔
319
        haveFunctionCall = processBinaryOperation(first, ctx);
1✔
320
    if (second->type == NodeType::BinaryOperation)
10✔
321
        haveFunctionCall = processBinaryOperation(second, ctx);
×
322
    if (first->type == NodeType::TypeConversion)
10✔
323
        processTypeConversion(first, ctx);
×
324
    if (second->type == NodeType::TypeConversion)
10✔
325
        processTypeConversion(second, ctx);
1✔
326
    bool isConsExpr = constantFolding(first, second, ctx);
10✔
327
    bool isNotModifiedExpr = false;
10✔
328
    if (!isConsExpr)
10✔
329
        isNotModifiedExpr = constantPropagation(first, second, ctx);
6✔
330
    if (first->type == NodeType::FunctionCall) {
10✔
331
        haveFunctionCall = true;
3✔
332
        auto funct = ctx.functions.find(first->firstChild()->str());
3✔
333
        funct->second.useCount++;
3✔
334
        if (funct->second.returnType != BuiltInTypes::NoneType) {
3✔
335
            for (auto &rootIter : ctx.root->children) {
3✔
336
                if (rootIter->firstChild()->str() == funct->first) {
3✔
337
                    processFunctionCall(node->firstChild(), *(rootIter->lastChild()));
3✔
338
                    break;
3✔
339
                }
340
            }
341
        }
342
    }
343
    if (second->type == NodeType::FunctionCall) {
10✔
344
        haveFunctionCall = true;
1✔
345
        auto funct = ctx.functions.find(second->firstChild()->str());
1✔
346
        funct->second.useCount++;
1✔
347
        if (funct->second.returnType != BuiltInTypes::NoneType) {
1✔
348
            for (auto &rootIter : ctx.root->children) {
1✔
349
                if (rootIter->firstChild()->str() == funct->first) {
1✔
350
                    processFunctionCall(node->lastChild(), *(rootIter->lastChild()));
1✔
351
                    break;
1✔
352
                }
353
            }
354
        }
355
    }
356
    if (haveFunctionCall)
10✔
357
        processExpression(node, ctx);
3✔
358
    return haveFunctionCall;
10✔
359
}
10✔
360

361
void processExpression(Node::Ptr &node, OptimizerContext &ctx) {
229✔
362
    for (auto &child : node->children) {
454✔
363
        if (child->type == NodeType::BinaryOperation) {
225✔
364
            auto first = child->firstChild();
33✔
365
            auto second = child->lastChild();
33✔
366
            bool haveFunctionCall = false;
33✔
367
            if (first->type == NodeType::BinaryOperation)
33✔
368
                haveFunctionCall = processBinaryOperation(first, ctx);
2✔
369
            if (second->type == NodeType::BinaryOperation)
33✔
370
                haveFunctionCall = processBinaryOperation(second, ctx);
7✔
371
            if (haveFunctionCall)
33✔
372
                processExpression(node, ctx);
3✔
373
            if (child->binOp() == BinaryOperation::Assign) {
33✔
374
                if (isNonModifiedVariable(second, ctx)) {
21✔
375
                    variablePropagation(second, ctx);
2✔
376
                }
377
            }
378
            if (first->type == NodeType::TypeConversion)
33✔
379
                processTypeConversion(first, ctx);
1✔
380
            if (second->type == NodeType::TypeConversion)
33✔
381
                processTypeConversion(second, ctx);
4✔
382
            bool isConsExpr = constantFolding(first, second, ctx);
33✔
383
            bool isNotModifiedExpr = false;
33✔
384
            if (!isConsExpr)
33✔
385
                isNotModifiedExpr = constantPropagation(first, second, ctx);
28✔
386
            if (isConsExpr || isNotModifiedExpr) {
33✔
387
                pushVariableAttribute(node, child, ctx);
9✔
388
            }
389
            if (first->type == NodeType::FunctionCall) {
33✔
390
                ctx.functions.find(first->firstChild()->str())->second.useCount++;
×
391
                haveFunctionCall = true;
×
392
            }
393
            if (second->type == NodeType::FunctionCall) {
33✔
394
                haveFunctionCall = true;
2✔
395
                auto funct = ctx.functions.find(second->firstChild()->str());
2✔
396
                funct->second.useCount++;
2✔
397
                if (funct->second.returnType != BuiltInTypes::NoneType) {
2✔
398
                    for (auto &rootIter : ctx.root->children) {
2✔
399
                        if (rootIter->firstChild()->str() == funct->first) {
2✔
400
                            processFunctionCall(child->lastChild(), *(rootIter->lastChild()));
2✔
401
                            break;
2✔
402
                        }
403
                    }
404
                }
405
                processExpression(node, ctx);
2✔
406
            }
407
            if (isAssignment(child) && haveFunctionCall) {
33✔
408
                ctx.findVariable(child->firstChild()).attributes.modified = true;
5✔
409
            }
410
            continue;
33✔
411
        }
33✔
412

413
        if (child->type == NodeType::FunctionCall) {
192✔
NEW
414
            auto lastChild = child->lastChild();
×
NEW
415
            processExpression(lastChild, ctx);
×
416
            ctx.functions.find(child->firstChild()->str())->second.useCount++;
×
NEW
417
            if (lastChild->type == NodeType::FunctionName)
×
418
                continue;
×
419
        }
×
420

421
        if (child->type == NodeType::TypeConversion) {
192✔
422
            processTypeConversion(child, ctx);
4✔
423
            pushVariableAttribute(node, child, ctx);
4✔
424
            continue;
4✔
425
        }
426

427
        if (isNumericLiteral(child)) {
188✔
428
            pushVariableAttribute(node, child, ctx);
49✔
429
            continue;
49✔
430
        }
431

432
        if (isNonModifiedVariable(child, ctx)) {
139✔
433
            variablePropagation(child, ctx);
5✔
434
            continue;
5✔
435
        }
436

437
        processExpression(child, ctx);
134✔
438
    }
439
}
229✔
440

441
void changeVariablesAttributes(Node::Ptr &node, OptimizerContext &ctx) {
1✔
442
    for (auto &child : node->children) {
3✔
443
        if (child->type == NodeType::Expression) {
2✔
444
            auto &exprNode = child->firstChild();
1✔
445
            if (isAssignment(exprNode)) {
1✔
446
                ctx.findVariable(exprNode->firstChild()).attributes.modified = true;
1✔
447
            }
448
        }
449
    }
450
}
1✔
451

452
bool isUnusedVariable(const std::list<ast::Node::Ptr>::iterator &nodeIter, const std::string &name,
140✔
453
                      bool isFirstCall = true) {
454
    Node::Ptr &node = *nodeIter;
140✔
455
    auto &children = node->parent->children;
140✔
456
    auto endIter = children.end();
140✔
457
    if (node->parent->type == NodeType::BranchRoot && !isFirstCall) {
140✔
458
        endIter = std::find_if(children.begin(), children.end(), [&name](const Node::Ptr &node) {
14✔
459
            return node->type == NodeType::VariableDeclaration && node->secondChild()->str() == name;
15✔
460
        });
461
    }
462
    for (auto childIter = nodeIter; childIter != endIter; childIter++) {
207✔
463
        Node::Ptr &child = *childIter;
177✔
464
        if (child->type != NodeType::VariableDeclaration && !child->children.empty()) {
177✔
465
            bool unused = isUnusedVariable(child->children.begin(), name, false);
113✔
466
            if (child->type != NodeType::BranchRoot && unused)
113✔
467
                continue;
28✔
468
            return unused;
110✔
469
        }
470
        if (child->type == NodeType::VariableName && child->str() == name)
64✔
471
            return false;
25✔
472
    }
473
    return true;
30✔
474
}
475

476
void processBranchRoot(Node::Ptr &node, OptimizerContext &ctx) {
45✔
477
    ctx.variables.push_front(&node->variables());
45✔
478
    ctx.values.emplace_front();
45✔
479
    for (auto childIter = node->children.begin(); childIter != node->children.end(); childIter++) {
120✔
480
        Node::Ptr &child = *childIter;
82✔
481
        if (child->type == NodeType::Expression || child->type == NodeType::VariableDeclaration) {
82✔
482
            processExpression(child, ctx);
63✔
483
        }
484

485
        if (child->type == NodeType::IfStatement) {
82✔
486
            processExpression(child->firstChild(), ctx);
11✔
487
            auto &exprResult = child->firstChild()->firstChild();
11✔
488
            if (isNumericLiteral(exprResult)) {
11✔
489
                child->children.pop_front();
10✔
490
                if (isTruthyLiteral(exprResult)) {
10✔
491
                    processBranchRoot(child->children.front(), ctx);
3✔
492
                    child = child->children.front();
3✔
493
                } else {
494
                    child->children.pop_front();
7✔
495
                    for (auto &ifChild : child->children) {
9✔
496
                        if (ifChild->type == NodeType::ElifStatement) {
7✔
497
                            processExpression(ifChild->firstChild(), ctx);
5✔
498
                        } else {
499
                            child = ifChild->firstChild();
2✔
500
                            processBranchRoot(child, ctx);
2✔
501
                            break;
2✔
502
                        }
503
                        auto &ifExprResult = ifChild->firstChild()->firstChild();
5✔
504
                        if (isNumericLiteral(ifExprResult)) {
5✔
505
                            ifChild->children.pop_front();
5✔
506
                            if (isTruthyLiteral(ifExprResult)) {
5✔
507
                                child = ifChild->children.front();
3✔
508
                                processBranchRoot(child, ctx);
3✔
509
                                break;
3✔
510
                            } else {
511
                                ifChild->children.clear();
2✔
512
                            }
513
                        }
514
                    }
515

516
                    child->children.remove_if([](const Node::Ptr &node) {
7✔
517
                        return node->type == NodeType::ElifStatement && node->children.empty();
6✔
518
                    });
519

520
                    if (child->children.empty()) {
7✔
521
                        child->type = NodeType::BranchRoot;
2✔
522
                    }
523
                }
524
            } else {
525
                changeVariablesAttributes(child->secondChild(), ctx);
1✔
526
                processBranchRoot(child->secondChild(), ctx);
1✔
527
            }
528
        }
529

530
        if (child->type == NodeType::WhileStatement) {
82✔
531
            processExpression(child->firstChild(), ctx);
2✔
532
            auto &exprResult = child->firstChild()->firstChild();
2✔
533
            if (isNumericLiteral(exprResult)) {
2✔
534
                if (isFalsyLiteral(exprResult)) {
2✔
535
                    child->children.clear();
1✔
536
                    child->type = NodeType::BranchRoot;
1✔
537
                } else if (isTruthyLiteral(exprResult)) {
1✔
538
                    auto currNode = child->parent;
1✔
539
                    auto prevNode = child;
1✔
540
                    while (currNode->parent->type != NodeType::FunctionDefinition) {
3✔
541
                        if ((currNode->type == NodeType::IfStatement || currNode->type == NodeType::ElifStatement ||
3✔
542
                             currNode->type == NodeType::WhileStatement) &&
2✔
543
                            isNumericLiteral(currNode->firstChild()->firstChild()) &&
4✔
544
                            !isTruthyLiteral(currNode->firstChild()->firstChild()))
×
545
                            break;
×
546
                        prevNode = currNode;
2✔
547
                        currNode = currNode->parent;
2✔
548
                    }
549
                    if (currNode->parent->type == NodeType::FunctionDefinition) {
1✔
550
                        auto iter =
551
                            std::find_if(currNode->children.begin(), currNode->children.end(),
1✔
552
                                         [&prevNode](const Node::Ptr &node) { return node.get() == prevNode.get(); });
2✔
553
                        currNode->children.erase(std::next(iter), currNode->children.end());
1✔
554
                        break;
1✔
555
                    }
556
                }
2✔
557
            } else {
558
                changeVariablesAttributes(child->secondChild(), ctx);
×
559
                processBranchRoot(child->secondChild(), ctx);
×
560
            }
561
        }
562

563
        if (child->type == NodeType::ReturnStatement) {
81✔
564
            processExpression(child->firstChild(), ctx);
6✔
565
            if (node->parent->type == NodeType::FunctionDefinition) {
6✔
566
                node->children.erase(std::next(childIter), node->children.end());
6✔
567
                break;
6✔
568
            }
569
        }
570

571
        if (child->type == NodeType::VariableDeclaration && ctx.options.has(OptimizerOptions::RemoveUnusedVariables)) {
75✔
572
            auto name = child->secondChild()->str();
27✔
573
            auto iter = std::next(childIter);
27✔
574
            if (iter == node->children.end() || isUnusedVariable(iter, name)) {
27✔
575
                child->children.clear();
2✔
576
                child->type = NodeType::BranchRoot;
2✔
577
            }
578
        }
27✔
579
    }
580
    ctx.variables.pop_front();
45✔
581
    ctx.values.pop_front();
45✔
582
}
45✔
583

584
void removeEmptyBranchRoots(const Node::Ptr &node) {
258✔
585
    for (auto &child : node->children) {
783✔
586
        if (!child->children.empty())
525✔
587
            removeEmptyBranchRoots(child);
228✔
588
        if (child->numChildren() == 1U && child->firstChild()->type == NodeType::BranchRoot &&
611✔
589
            child->type != NodeType::ElseStatement && child->type != NodeType::FunctionDefinition)
611✔
590
            child = child->firstChild();
×
591
        child->children.remove_if([](const Node::Ptr &node) {
525✔
592
            return node->type == NodeType::BranchRoot && node->children.empty() ||
540✔
593
                   node->type == NodeType::WhileStatement && node->numChildren() == 1U ||
1,025✔
594
                   node->type == NodeType::IfStatement && node->numChildren() == 1U;
975✔
595
        });
596
    }
597
}
258✔
598

599
void removeUnusedFunctions(SyntaxTree &tree) {
30✔
600
    tree.root->children.remove_if([&functions = tree.functions](const Node::Ptr &node) {
30✔
601
        const std::string &funcName = node->firstChild()->str();
36✔
602
        return functions[funcName].useCount == 0 && funcName != language::funcMain;
36✔
603
    });
604
}
30✔
605

606
void Optimizer::process(SyntaxTree &tree, const OptimizerOptions &options) {
30✔
607
    if (options == OptimizerOptions::none())
30✔
608
        return;
×
609

610
    OptimizerContext ctx(tree.functions, options);
30✔
611
    ctx.root = tree.root;
30✔
612
    for (auto &node : tree.root->children) {
66✔
613
        if (node->type == NodeType::FunctionDefinition) {
36✔
614
            auto child = node->children.begin();
36✔
615
            std::advance(child, 3);
36✔
616
            processBranchRoot(*child, ctx);
36✔
617
        }
618
    }
619

620
    if (ctx.options.has(OptimizerOptions::RemoveUnusedFunctions))
30✔
621
        removeUnusedFunctions(tree);
30✔
622

623
    removeEmptyBranchRoots(tree.root);
30✔
624
}
30✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc