Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 75.7% 56 / 0 / 74
Functions: -% 0 / 1 / 1
Branches: 62.5% 45 / 0 / 72

OMCompiler/Compiler/NFFrontEnd/NFInline.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NFInline
37
38 import Call = NFCall;
39 import Expression = NFExpression;
40
41 protected
42 import Binding = NFBinding;
43 import Class = NFClass;
44 import Component = NFComponent;
45 import ComponentRef = NFComponentRef;
46 import DAE;
47 import DAE.InlineType;
48 import Dimension = NFDimension;
49 import Flags;
50 import NFFunction.Function;
51 import NFInstNode.InstNode;
52 import Statement = NFStatement;
53 import Subscript = NFSubscript;
54 import Type = NFType;
55
56 public
57 function inlineCallExp
58 "Inlines a call if it has an EarlyInline=true annotation, or always if
59 forceInline is set to true."
60 input Expression callExp;
61 input Boolean forceInline = false;
62 output Expression result;
63 algorithm
64 result := match callExp
65 local
66 Call call;
67 Boolean shouldInline;
68
69 case Expression.CALL(call = call as Call.TYPED_CALL())
70 algorithm
71 shouldInline := match Call.inlineType(call)
72 case DAE.InlineType.BUILTIN_EARLY_INLINE() then true;
73 case DAE.InlineType.EARLY_INLINE()
74 guard Flags.isSet(Flags.INLINE_FUNCTIONS) then true;
75
3/4
✓ Branch 0 taken 28176 times.
✓ Branch 1 taken 19153 times.
✓ Branch 3 taken 28176 times.
✗ Branch 4 not taken.
47329 case DAE.InlineType.NORM_INLINE() then forceInline or Flags.getConfigBool(Flags.FRONTEND_INLINE);
76 else forceInline;
77 end match;
78
2/2
✓ Branch 0 taken 51171 times.
✓ Branch 1 taken 36586 times.
114915 then
79 if shouldInline then inlineCall(callExp, forceInline) else callExp;
80
81 else callExp;
82 end match;
83 end inlineCallExp;
84
85 function inlineCall
86 input Expression callExp;
87 input Boolean forceInline = false;
88 output Expression exp;
89 protected
90 Call call;
91 Function fn;
92 Expression arg;
93 list<Expression> args;
94 list<InstNode> inputs, locals;
95 list<NFInstNode.NodeHandle> outputs;
96 list<Statement> body;
97 Statement stmt;
98 Binding binding;
99 algorithm
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78329 times.
78329 Expression.CALL(call = call) := callExp;
101
102 exp := match call
103 // Record constructor
104 case Call.TYPED_CALL(fn = fn, arguments = args)
105 guard not InstNode.isEmpty(InstNode.fromHandle(fn.node)) and InstNode.isNamed(InstNode.parentScope(InstNode.fromHandle(fn.node)), "'constructor'")
106 algorithm
107 690 body := Function.getBody(fn);
108
109
2/4
✓ Branch 0 taken 690 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 690 times.
690 if not (listEmpty(body) and listEmpty(fn.locals)) then
110 exp := callExp;
111 ✗ return;
112 end if;
113
114 690 binding := Component.getBinding(InstNode.component(InstNode.fromHandle(listHead(fn.outputs))));
115
116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 690 times.
690 if Binding.hasExp(binding) then
117 ✗ exp := Binding.getExp(binding);
118 ✗ true := Expression.isRecord(exp);
119 else
120 690 exp := Class.makeRecordExp(InstNode.fromHandle(listHead(fn.outputs)), InstNode.fromHandle(fn.node), typed = true);
121 end if;
122
123
2/2
✓ Branch 0 taken 1380 times.
✓ Branch 1 taken 690 times.
2070 for i in fn.inputs loop
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1380 times.
1380 arg :: args := args;
125 1380 arg := inlineCallExp(arg, forceInline);
126 1380 exp := Expression.map(exp, func = function replaceCrefNode(node = i, value = arg));
127 end for;
128 then
129 exp;
130
131 // Normal function
132 case Call.TYPED_CALL(fn = fn as Function.FUNCTION(inputs = inputs, outputs = outputs, locals = locals),
133 arguments = args)
134 guard Function.hasSingleOrEmptyBody(fn)
135 algorithm
136 26527 body := Function.getBody(fn);
137 26527 body := removeDeadCode(body);
138
139 // This function can so far only handle functions with at most one
140 // statement and output and no local variables.
141
5/6
✓ Branch 1 taken 26509 times.
✓ Branch 2 taken 18 times.
✓ Branch 4 taken 26509 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✓ Branch 7 taken 26490 times.
26527 if listLength(body) > 1 or listLength(outputs) <> 1 or not listEmpty(locals) then
142 exp := callExp;
143 37 return;
144 end if;
145
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26490 times.
26490 if listEmpty(body) then
147 ✗ stmt := makeOutputStatement(InstNode.fromHandle(listHead(outputs)));
148 else
149 26490 stmt := convertToAssignment(listHead(body));
150 end if;
151
152
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26490 times.
26490 if not Statement.isAssignment(stmt) then
153 exp := callExp;
154 ✗ return;
155 end if;
156
157 26490 Error.assertion(listLength(inputs) == listLength(args),
158 getInstanceName() + " got wrong number of arguments for " +
159 AbsynUtil.pathString(Function.name(fn)), sourceInfo());
160
161 try
162 // TODO: Instead of repeating this for each input we should probably
163 // just build a lookup tree or hash table and go through the
164 // statement once.
165
2/2
✓ Branch 0 taken 36877 times.
✓ Branch 1 taken 26490 times.
63367 for i in inputs loop
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36877 times.
36877 arg :: args := args;
167 36877 arg := inlineCallExp(arg, forceInline);
168 36877 stmt := Statement.mapExp(stmt,
169 function Expression.map(func = function replaceCrefNode(node = i, value = arg)));
170 end for;
171
172 26490 exp := getOutputExp(stmt, InstNode.fromHandle(listHead(outputs)), call);
173
2/2
✓ Branch 0 taken 8005 times.
✓ Branch 1 taken 18485 times.
34495 exp := Expression.map(exp, function inlineCallExp(forceInline = forceInline));
174 else
175 exp := callExp;
176 end try;
177 then
178 exp;
179
180 else callExp;
181 end match;
182 end inlineCall;
183
184 protected
185 function replaceCrefNode
186 input output Expression exp;
187 input InstNode node;
188 input Expression value;
189 protected
190 Type ty, repl_ty;
191 algorithm
192 exp := match exp
193 case Expression.CREF()
194 guard InstNode.refEqual(ComponentRef.node(ComponentRef.firstNonScope(exp.cref)), node)
195 136764 then replaceCrefNode2(exp.cref, node, value);
196
197 else exp;
198 end match;
199
200 // Replace expressions in dimensions too.
201 1132882 ty := Expression.typeOf(exp);
202 1132882 repl_ty := Type.mapDims(ty, function replaceDimExp(node = node, value = value));
203
204
2/2
✓ Branch 0 taken 846430 times.
✓ Branch 1 taken 286452 times.
1132882 if not referenceEq(ty, repl_ty) then
205 286452 exp := Expression.setType(repl_ty, exp);
206 end if;
207 end replaceCrefNode;
208
209 function replaceCrefNode2
210 input ComponentRef cref;
211 input InstNode node;
212 input output Expression value;
213 protected
214 algorithm
215
2/2
✓ Branch 2 taken 2730 times.
✓ Branch 3 taken 136764 times.
139494 if not InstNode.refEqual(node, ComponentRef.node(cref)) then
216 2730 value := replaceCrefNode2(ComponentRef.rest(cref), node, value);
217 2730 value := Expression.recordElement(ComponentRef.nodeName(cref), value);
218 end if;
219
220 139494 value := Expression.applySubscripts(ComponentRef.getSubscripts(cref), value);
221 end replaceCrefNode2;
222
223 function replaceDimExp
224 input output Dimension dim;
225 input InstNode node;
226 input Expression value;
227 algorithm
228 dim := match dim
229 local
230 Expression exp;
231
232 case Dimension.EXP()
233 algorithm
234 1182 exp := Expression.map(dim.exp, function replaceCrefNode(node = node, value = value));
235 1182 then
236 Dimension.fromExp(exp, dim.var);
237
238 else dim;
239 end match;
240 end replaceDimExp;
241
242 function removeDeadCode
243 input output list<Statement> body;
244 algorithm
245 // Everything after a 'return' can be removed, but for inlining we only care
246 // if we can remove everything after the first statement.
247
3/4
✓ Branch 1 taken 26509 times.
✓ Branch 2 taken 18 times.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
26527 if listLength(body) > 1 and Statement.isReturn(listGet(body, 2)) then
248 ✗ body := {listHead(body)};
249 end if;
250 end removeDeadCode;
251
252 function convertToAssignment
253 "Converts a statement into an assignment statement."
254 input Statement stmt;
255 output Statement outStmt;
256 algorithm
257 outStmt := match stmt
258 2 case Statement.IF() then convertIfToAssignment(stmt);
259 else stmt;
260 end match;
261 end convertToAssignment;
262
263 function convertIfToAssignment
264 "Converts an if-statement where all branches assign the same variable into an
265 assignment with an if-expression. Ex:
266 if x > 1 then
267 y := 1;
268 else
269 y := 2;
270 end if;
271 =>
272 y := if x > 1 then 1 else 2;
273 "
274 input output Statement stmt;
275 protected
276 list<tuple<Expression, list<Statement>>> branches;
277 Expression cond, if_exp, output_exp, lhs, rhs;
278 Type ty = Type.UNKNOWN();
279 list<Statement> body;
280 Statement s;
281 DAE.ElementSource source;
282 algorithm
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 Statement.IF(branches = branches, source = source) := stmt;
284
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 (cond, body) :: branches := listReverse(branches);
285
286 // The if-statement must have an else-branch.
287
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if not listEmpty(branches) and not Expression.isTrue(cond) then
288 ✗ return;
289 end if;
290
291 // The body of the else branch must have exactly one statement.
292
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if listLength(body) <> 1 then
293 ✗ return;
294 end if;
295
296 // The statement must be, or be convertible to, an assignment.
297 2 s := convertToAssignment(listHead(body));
298
299
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if not Statement.isAssignment(s) then
300 ✗ return;
301 end if;
302
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 Statement.ASSIGNMENT(lhs = output_exp, rhs = if_exp) := s;
304
305
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for b in branches loop
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 (cond, body) :: branches := branches;
307
308 // Each branch must be a single assignment.
309
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if listLength(body) <> 1 then
310 ✗ return;
311 end if;
312
313 2 s := convertToAssignment(listHead(body));
314
315
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if not Statement.isAssignment(s) then
316 ✗ return;
317 end if;
318
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 Statement.ASSIGNMENT(lhs = lhs, rhs = rhs, ty = ty) := s;
320
321 // Check that all branches have the same lhs.
322
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if not Expression.isEqual(lhs, output_exp) then
323 ✗ return;
324 end if;
325
326 2 if_exp := Expression.IF(ty, cond, rhs, if_exp);
327 end for;
328
329 2 stmt := Statement.ASSIGNMENT(output_exp, if_exp, ty, source);
330 end convertIfToAssignment;
331
332 function makeOutputStatement
333 input InstNode outputNode;
334 output Statement stmt;
335 protected
336 Binding binding;
337 Expression cref_exp, binding_exp;
338 algorithm
339 ✗ binding := Component.getImplicitBinding(InstNode.component(outputNode), InstNode.instanceParent(outputNode));
340
341 ✗ if Binding.isBound(binding) then
342 ✗ cref_exp := Expression.fromCref(ComponentRef.fromNode(outputNode, Type.UNKNOWN()));
343 ✗ binding_exp := Binding.getExp(binding);
344 ✗ stmt := Statement.makeAssignment(cref_exp, binding_exp, Type.UNKNOWN(), DAE.emptyElementSource);
345 else
346 stmt := Statement.FAILURE({}, DAE.emptyElementSource);
347 end if;
348 end makeOutputStatement;
349
350 function getOutputExp
351 input Statement stmt;
352 input InstNode outputNode;
353 input Call call;
354 output Expression exp;
355 algorithm
356 exp := match stmt
357 local
358 ComponentRef cr, rest_cr;
359
360 case Statement.ASSIGNMENT(lhs = Expression.CREF(
361 cref = cr as ComponentRef.CREF(subscripts = {}, restCref = rest_cr)))
362 guard InstNode.refEqual(outputNode, ComponentRef.node(cr)) and
363 not ComponentRef.isFromCref(rest_cr)
364 26490 then stmt.rhs;
365
366 ✗ else Expression.CALL(call);
367 end match;
368 end getOutputExp;
369
370 annotation(__OpenModelica_Interface="nf_frontend");
371 end NFInline;
372