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

vla5924-practice / compiler-project / 13883569707

16 Mar 2025 12:54PM UTC coverage: 77.233% (-0.04%) from 77.273%
13883569707

Pull #223

github

web-flow
Merge 28bde1312 into 21e8f12a8
Pull Request #223: Refactor ast::Node (add numChildren method, use in64_t)

79 of 85 new or added lines in 7 files covered. (92.94%)

1 existing line in 1 file now uncovered.

4366 of 5653 relevant lines covered (77.23%)

250.61 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/utils/language.hpp"
8

9
#include "optimizer/optimizer_context.hpp"
10

11
using namespace ast;
12
using namespace optimizer;
13

14
namespace language = utils::language;
15

16
namespace {
17

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

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

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

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

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

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

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

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

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

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

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

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

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

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

82
} // namespace
83

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

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

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

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

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

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

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

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

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

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

251
void processExpression(Node::Ptr &node, OptimizerContext &ctx);
252

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

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

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

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

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

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

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

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

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

435
        processExpression(child, ctx);
134✔
436
    }
437
}
229✔
438

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

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

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

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

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

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

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

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

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

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

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

603
void Optimizer::process(SyntaxTree &tree, const OptimizerOptions &options) {
30✔
604
    if (options == OptimizerOptions::none())
30✔
605
        return;
×
606

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

617
    if (ctx.options.has(OptimizerOptions::RemoveUnusedFunctions))
30✔
618
        removeUnusedFunctions(tree);
30✔
619

620
    removeEmptyBranchRoots(tree.root);
30✔
621
}
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