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

polserver / polserver / 16205400460

10 Jul 2025 08:32PM UTC coverage: 59.527% (+0.2%) from 59.304%
16205400460

push

github

web-flow
Escript Optimizer more types and operator (#794)

* compile time optimization:
int with doubles and strings
doubles with ints and strings
strings with ints and doubles

* bool with other types, more unary ops, float branch optimizer

* more tests
fixed bool to dbl compare

* output cleanup
more tests

* optimize string values in if statements, optimize ternary operator

* optimize elvis, addes missing files, code cleanup

* use array to keep unoptimized if branch in funcexpr tests

* missing include

* better readable testdata

* removed file

* optimize while and dowhile loops if predicate is a compile time known
value

* cleaner variant of loop optimization?

* added ConstantPredicateLoop Node used by the optimizer for constant loop
predicates
optimize repeat until loop
change tests to run the loops more then once to be sure they work
correctly

* test break/continue with label for constant-loop

* docs

392 of 435 new or added lines in 16 files covered. (90.11%)

6 existing lines in 2 files now uncovered.

43324 of 72780 relevant lines covered (59.53%)

448442.97 hits per line

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

93.67
/pol-core/bscript/compiler/optimizer/Optimizer.cpp
1
#include "Optimizer.h"
2

3
#include <optional>
4
#include <utility>
5

6
#include "bscript/compiler/Report.h"
7
#include "bscript/compiler/analyzer/Constants.h"
8
#include "bscript/compiler/ast/BinaryOperator.h"
9
#include "bscript/compiler/ast/Block.h"
10
#include "bscript/compiler/ast/BooleanValue.h"
11
#include "bscript/compiler/ast/BranchSelector.h"
12
#include "bscript/compiler/ast/ClassDeclaration.h"
13
#include "bscript/compiler/ast/ConditionalOperator.h"
14
#include "bscript/compiler/ast/ConstDeclaration.h"
15
#include "bscript/compiler/ast/ConstantPredicateLoop.h"
16
#include "bscript/compiler/ast/DoWhileLoop.h"
17
#include "bscript/compiler/ast/ElvisOperator.h"
18
#include "bscript/compiler/ast/FloatValue.h"
19
#include "bscript/compiler/ast/Identifier.h"
20
#include "bscript/compiler/ast/IfThenElseStatement.h"
21
#include "bscript/compiler/ast/IntegerValue.h"
22
#include "bscript/compiler/ast/Program.h"
23
#include "bscript/compiler/ast/RepeatUntilLoop.h"
24
#include "bscript/compiler/ast/Statement.h"
25
#include "bscript/compiler/ast/StringValue.h"
26
#include "bscript/compiler/ast/TopLevelStatements.h"
27
#include "bscript/compiler/ast/UnaryOperator.h"
28
#include "bscript/compiler/ast/UninitializedValue.h"
29
#include "bscript/compiler/ast/UserFunction.h"
30
#include "bscript/compiler/ast/ValueConsumer.h"
31
#include "bscript/compiler/ast/WhileLoop.h"
32
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
33
#include "bscript/compiler/model/CompilerWorkspace.h"
34
#include "bscript/compiler/optimizer/BinaryOperatorOptimizer.h"
35
#include "bscript/compiler/optimizer/ConstantValidator.h"
36
#include "bscript/compiler/optimizer/ReferencedFunctionGatherer.h"
37
#include "bscript/compiler/optimizer/UnaryOperatorOptimizer.h"
38
#include "bscript/compiler/optimizer/ValueConsumerOptimizer.h"
39

40
namespace Pol::Bscript::Compiler
41
{
42
Optimizer::Optimizer( Constants& constants, Report& report )
1,602✔
43
    : constants( constants ), report( report )
1,602✔
44
{
45
}
1,602✔
46

47
void Optimizer::optimize( CompilerWorkspace& workspace,
1,602✔
48
                          UserFunctionInclusion user_function_inclusion )
49
{
50
  workspace.accept( *this );
1,602✔
51

52
  std::vector<UserFunction*> all_user_functions;
1,602✔
53
  for ( auto& user_function : workspace.user_functions )
4,064✔
54
  {
55
    if ( user_function_inclusion == UserFunctionInclusion::All ||
4,924✔
56
         user_function->type == UserFunctionType::Method ||
4,768✔
57
         user_function->type == UserFunctionType::Constructor )
2,306✔
58
    {
59
      all_user_functions.push_back( user_function.get() );
440✔
60
    }
61
  }
62

63
  std::vector<UserFunction*> exported_functions;
1,602✔
64
  for ( auto& user_function : workspace.user_functions )
4,064✔
65
  {
66
    if ( user_function->exported )
2,462✔
67
      exported_functions.push_back( user_function.get() );
554✔
68
  }
69
  ReferencedFunctionGatherer gatherer( workspace.module_function_declarations,
1,602✔
70
                                       std::move( workspace.user_functions ) );
1,602✔
71
  workspace.top_level_statements->accept( gatherer );
1,602✔
72
  if ( auto program = workspace.program.get() )
1,602✔
73
  {
74
    program->accept( gatherer );
377✔
75
  }
76
  for ( auto uf : exported_functions )
2,156✔
77
  {
78
    gatherer.reference( uf );
554✔
79
  }
80
  for ( auto& uf : all_user_functions )
2,042✔
81
  {
82
    if ( user_function_inclusion == UserFunctionInclusion::All ||
440✔
83
         uf->type == UserFunctionType::Method || uf->type == UserFunctionType::Constructor )
440✔
84
    {
85
      gatherer.reference( uf );
440✔
86
    }
87
  }
88

89
  for ( auto& cd : workspace.class_declarations )
1,938✔
90
  {
91
    cd->accept( gatherer );
336✔
92
  }
93

94
  workspace.referenced_module_function_declarations =
95
      gatherer.take_referenced_module_function_declarations();
1,602✔
96

97
  workspace.user_functions = gatherer.take_referenced_user_functions();
1,602✔
98
}
1,602✔
99

100
void Optimizer::visit_children( Node& node )
662,690✔
101
{
102
  unsigned i = 0;
662,690✔
103
  for ( auto& child : node.children )
1,337,172✔
104
  {
105
    child->accept( *this );
674,482✔
106

107
    if ( optimized_replacement )
674,482✔
108
    {
109
      node.children[i] = std::move( optimized_replacement );
29,417✔
110
    }
111

112
    ++i;
674,482✔
113
  }
114
}
662,690✔
115

116
void Optimizer::visit_binary_operator( BinaryOperator& binary_operator )
8,216✔
117
{
118
  visit_children( binary_operator );
8,216✔
119

120
  optimized_replacement = BinaryOperatorOptimizer( binary_operator, report ).optimize();
8,216✔
121
}
8,216✔
122

123
void Optimizer::visit_branch_selector( BranchSelector& selector )
3,618✔
124
{
125
  visit_children( selector );
3,618✔
126

127
  auto predicate = selector.predicate();
3,618✔
128
  if ( auto unary_operator = dynamic_cast<UnaryOperator*>( predicate ) )
3,618✔
129
  {
130
    if ( unary_operator->token_id == TOK_LOG_NOT )
1,614✔
131
    {
132
      BranchSelector::BranchType branch_type;
133
      switch ( selector.branch_type )
1,604✔
134
      {
135
      case BranchSelector::IfTrue:
×
136
        branch_type = BranchSelector::IfFalse;
×
137
        break;
×
138
      case BranchSelector::IfFalse:
1,604✔
139
        branch_type = BranchSelector::IfTrue;
1,604✔
140
        break;
1,604✔
141
      default:
×
142
        selector.internal_error( "Expected conditional branch with predicate" );
×
143
      }
144
      optimized_replacement = std::make_unique<BranchSelector>(
1,604✔
145
          selector.source_location, branch_type, unary_operator->take_operand() );
3,208✔
146
    }
147
  }
148
  else if ( auto decision = branch_decision( predicate ); decision.has_value() )
2,004✔
149
  {
150
    BranchSelector::BranchType branch_type;
151
    switch ( selector.branch_type )
194✔
152
    {
153
    case BranchSelector::IfTrue:
×
NEW
154
      branch_type = decision.value() ? BranchSelector::Always : BranchSelector::Never;
×
155
      break;
×
156
    case BranchSelector::IfFalse:
194✔
157
      branch_type = !decision.value() ? BranchSelector::Always : BranchSelector::Never;
194✔
158
      break;
194✔
159
    default:
×
160
      selector.internal_error( "Expected conditional branch with predicate" );
×
161
    }
162
    optimized_replacement =
163
        std::make_unique<BranchSelector>( selector.source_location, branch_type );
194✔
164
  }
165
}
3,618✔
166

167
void Optimizer::visit_const_declaration( ConstDeclaration& constant )
121,503✔
168
{
169
  visit_children( constant );
121,503✔
170
  if ( !ConstantValidator().validate( constant.expression() ) )
121,503✔
171
  {
172
    report.error( constant,
10✔
173
                  "Const expression must be optimizable.\n"
174
                  "{}",
175
                  constant );
176
  }
177
}
121,503✔
178

179
void Optimizer::visit_identifier( Identifier& identifier )
43,230✔
180
{
181
  // We don't have scoped constants, so only global identifiers can be optimized.
182
  if ( identifier.scoped_name.scope.global() )
43,230✔
183
  {
184
    auto name = identifier.string();
43,052✔
185
    if ( auto constant = constants.find( name ) )
43,052✔
186
    {
187
      SimpleValueCloner cloner( report, identifier.source_location );
18,968✔
188
      optimized_replacement = cloner.clone( constant->expression() );
18,968✔
189
    }
18,968✔
190
  }
43,052✔
191
}
43,230✔
192

193
void Optimizer::visit_if_then_else_statement( IfThenElseStatement& if_then_else )
3,618✔
194
{
195
  visit_children( if_then_else );
3,618✔
196

197
  if ( auto else_block = dynamic_cast<Block*>( if_then_else.alternative() ) )
3,618✔
198
  {
199
    if ( else_block->children.empty() )
392✔
200
      if_then_else.children.erase( if_then_else.children.begin() + 2 );
4✔
201
  }
202

203
  auto& branch_selector = if_then_else.branch_selector();
3,618✔
204
  if ( branch_selector.branch_type == BranchSelector::Never )
3,618✔
205
  {
206
    optimized_replacement = if_then_else.take_consequent();
132✔
207
  }
208
  else if ( branch_selector.branch_type == BranchSelector::Always )
3,486✔
209
  {
210
    if ( auto alternative = if_then_else.take_alternative() )
62✔
211
    {
212
      optimized_replacement = std::move( alternative );
48✔
213
    }
214
    else
215
    {
216
      std::vector<std::unique_ptr<Statement>> empty;
14✔
217
      optimized_replacement =
218
          std::make_unique<Block>( if_then_else.source_location, std::move( empty ) );
14✔
219
    }
76✔
220
  }
221
}
3,618✔
222

223
void Optimizer::visit_unary_operator( UnaryOperator& unary_operator )
7,877✔
224
{
225
  visit_children( unary_operator );
7,877✔
226

227
  optimized_replacement = UnaryOperatorOptimizer( unary_operator ).optimize();
7,877✔
228
}
7,877✔
229

230
void Optimizer::visit_value_consumer( ValueConsumer& consume_value )
11,455✔
231
{
232
  visit_children( consume_value );
11,455✔
233

234
  optimized_replacement = ValueConsumerOptimizer().optimize( consume_value );
11,455✔
235
}
11,455✔
236

237
void Optimizer::visit_conditional_operator( ConditionalOperator& conditional )
110✔
238
{
239
  visit_children( conditional );
110✔
240

241
  auto optimize_branch = branch_decision( &conditional.conditional() );
110✔
242
  if ( optimize_branch.has_value() )
110✔
243
  {
244
    if ( optimize_branch.value() )
40✔
245
      optimized_replacement = conditional.take_consequent();
16✔
246
    else
247
      optimized_replacement = conditional.take_alternate();
24✔
248
  }
249
}
110✔
250

251
void Optimizer::visit_elvis_operator( ElvisOperator& elvisop )
166✔
252
{
253
  visit_children( elvisop );
166✔
254

255
  auto optimize_branch = branch_decision( &elvisop.lhs() );
166✔
256
  if ( optimize_branch.has_value() )
166✔
257
  {
258
    if ( optimize_branch.value() )
26✔
259
      optimized_replacement = elvisop.take_lhs();
8✔
260
    else
261
      optimized_replacement = elvisop.take_rhs();
18✔
262
  }
263
}
166✔
264

265
std::optional<bool> Optimizer::branch_decision( Expression* exp ) const
2,682✔
266
{
267
  std::optional<bool> optimize_branch;
2,682✔
268
  if ( auto iv = dynamic_cast<IntegerValue*>( exp ) )
2,682✔
269
    optimize_branch = iv->value;
384✔
270
  else if ( auto fv = dynamic_cast<FloatValue*>( exp ) )
2,298✔
271
    optimize_branch = fv->value != 0.0;
14✔
272
  else if ( auto bv = dynamic_cast<BooleanValue*>( exp ) )
2,284✔
273
    optimize_branch = bv->value;
28✔
274
  else if ( auto sv = dynamic_cast<StringValue*>( exp ) )
2,256✔
275
    optimize_branch = !sv->value.empty();
30✔
276
  else if ( dynamic_cast<UninitializedValue*>( exp ) )
2,226✔
277
    optimize_branch = false;
8✔
278
  return optimize_branch;
2,682✔
279
}
280

281
void Optimizer::visit_while_loop( WhileLoop& loop )
336✔
282
{
283
  visit_children( loop );
336✔
284
  if ( auto optimize_branch = branch_decision( &loop.predicate() ) )
336✔
285
  {
286
    if ( optimize_branch.value() )
172✔
287
      optimized_replacement = std::make_unique<ConstantPredicateLoop>(
170✔
288
          loop.source_location, loop.get_label(), loop.take_block(), true );
340✔
289
    else
290
      optimized_replacement = std::make_unique<Block>( loop.source_location,
4✔
291
                                                       std::vector<std::unique_ptr<Statement>>{} );
6✔
292
  }
293
}
336✔
294

295
void Optimizer::visit_do_while_loop( DoWhileLoop& loop )
34✔
296
{
297
  visit_children( loop );
34✔
298
  if ( auto optimize_branch = branch_decision( &loop.predicate() ) )
34✔
299
  {
300
    optimized_replacement = std::make_unique<ConstantPredicateLoop>(
52✔
301
        loop.source_location, loop.get_label(), loop.take_block(), optimize_branch.value() );
52✔
302
  }
303
}
34✔
304

305
void Optimizer::visit_repeat_until_loop( RepeatUntilLoop& loop )
32✔
306
{
307
  visit_children( loop );
32✔
308
  if ( auto optimize_branch = branch_decision( &loop.expression() ) )
32✔
309
  {
310
    // inverted logic if the predicate is true its not an endleas loop
311
    optimized_replacement = std::make_unique<ConstantPredicateLoop>(
6✔
312
        loop.source_location, loop.get_label(), loop.take_block(), !optimize_branch.value() );
12✔
313
  }
314
}
32✔
315
}  // namespace Pol::Bscript::Compiler
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