Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 76.1% 286 / 0 / 376
Functions: -% 0 / 1 / 1
Branches: 61.6% 175 / 0 / 284

OMCompiler/Compiler/NBackEnd/Modules/2_Pre/NBInline.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 uniontype NBInline<T>
37 " file: NBInline.mo
38 package: NBInline
39 description: This file contains functions for inlining operations.
40 "
41
42 protected
43 import Inline = NBInline;
44
45 // OF imports
46 import Absyn;
47 import AbsynUtil;
48 import DAE;
49 import DAEDump;
50 import DAEUtil;
51
52 // NF imports
53 import BackendExtension = NFBackendExtension;
54 import Binding = NFBinding;
55 import Call = NFCall;
56 import Class = NFClass;
57 import Component = NFComponent;
58 import ComponentRef = NFComponentRef;
59 import Dimension = NFDimension;
60 import Expression = NFExpression;
61 import NFFunction.Function;
62 import NFFlatten.FunctionTree;
63 import InstNode = NFInstNode.InstNode;
64 import MutableWeak;
65 import NFModifier.Modifier;
66 import Operator = NFOperator;
67 import Statement = NFStatement;
68 import Subscript = NFSubscript;
69 import Type = NFType;
70 import Variable = NFVariable;
71
72 // NB imports
73 import Module = NBModule;
74 import BackendDAE = NBackendDAE;
75 import BEquation = NBEquation;
76 import NBEquation.{Equation, IfEquationBody, EquationPointers, EqData, EquationAttributes, Iterator};
77 import Replacements = NBReplacements;
78 import BVariable = NBVariable;
79 import NBVariable.{VariablePointer, VariablePointers, VarData};
80
81 // Util
82 import Slice = NBSlice;
83 import StringUtil;
84
85 // =========================================================================
86 // MAIN ROUTINE, PLEASE DO NOT CHANGE
87 // =========================================================================
88 public
89 function main
90 "Wrapper function for any inlining function. This will be
91 called during simulation and gets the corresponding subfunction from
92 the given input."
93 extends Module.wrapper;
94 input list<DAE.InlineType> inline_types;
95 input Boolean init;
96 algorithm
97 bdae := match bdae
98 local
99 EqData eqData;
100 VarData varData;
101 case BackendDAE.MAIN()
102 algorithm
103
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 569 times.
569 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
104 ✗ print(StringUtil.headline_4("[dumpBackendInline] Inlining operatations for: "
105 + List.toString(inline_types, DAEDump.dumpInlineTypeBackendStr)));
106 end if;
107 569 (eqData, varData) := inline(bdae.eqData, bdae.varData, bdae.funcMap, inline_types, init);
108 569 bdae.eqData := eqData;
109 569 bdae.varData := varData;
110
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 569 times.
569 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
111 ✗ print("\n");
112 end if;
113 then bdae;
114
115 else algorithm
116 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed."});
117 ✗ then fail();
118 end match;
119 end main;
120
121 // =========================================================================
122 // TYPES, UNIONTYPES AND MEMBER FUNCTIONS
123 // =========================================================================
124 function inlineForEquation
125 "inlines for-equations of size 1 to its body equation by replacing
126 the iterators by the only values they are ever going to be."
127 input output Equation eqn;
128 algorithm
129 eqn := match eqn
130 local
131 Equation new_eqn;
132 UnorderedMap<ComponentRef, Expression> replacements "replacement map for iterator crefs";
133 list<ComponentRef> names;
134 list<Expression> ranges;
135 ComponentRef name;
136 Expression range;
137 Integer start;
138
139 case Equation.FOR_EQUATION(body = {new_eqn}) guard(Iterator.size(eqn.iter) == 1 and not Iterator.isResizable(eqn.iter)) algorithm
140 13 replacements := UnorderedMap.new<Expression>(ComponentRef.hash, ComponentRef.isEqual);
141 13 (names, ranges) := Iterator.getFrames(eqn.iter);
142
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 13 times.
26 for tpl in List.zip(names, ranges) loop
143 13 (name, range) := tpl;
144 13 (start, _, _) := Expression.getIntegerRange(range, true);
145 13 UnorderedMap.add(name, Expression.INTEGER(start), replacements);
146 end for;
147 13 new_eqn := Equation.map(new_eqn, function Replacements.applySimpleExp(replacements = replacements));
148
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
149 ✗ print("[" + getInstanceName() + "] Inlining: " + Equation.toString(eqn) + "\n");
150 ✗ print("-- Result: " + Equation.toString(new_eqn) + "\n");
151 end if;
152 then new_eqn;
153
154 else eqn;
155 end match;
156 end inlineForEquation;
157
158 function functionInlineable
159 "returns true if the function can be inlined"
160 input Function fn;
161 output Boolean b = false;
162 algorithm
163 // currently we only inline single assignments
164 // also check for single output?
165
2/2
✓ Branch 1 taken 814 times.
✓ Branch 2 taken 112 times.
926 if Function.hasSingleOrEmptyBody(fn) then
166 b := match Function.getBody(fn)
167 case {Statement.ASSIGNMENT()} then true;
168 else false;
169 end match;
170 end if;
171 end functionInlineable;
172
173 function inlineRecordSliceEquation
174 input Slice<Pointer<Equation>> slice;
175 input VariablePointers variables;
176 input UnorderedSet<VariablePointer> set "new iterators";
177 input Pointer<Integer> index;
178 input Boolean inlineSimple;
179 output list<Slice<Pointer<Equation>>> slices;
180 protected
181 Pointer<list<Pointer<Equation>>> record_eqns = Pointer.create({});
182 algorithm
183 slices := match Pointer.access(Slice.getT(slice))
184 local
185 Equation eqn;
186 // only some rows of a record equation are part of the slice, keep only the fields with those rows
187 case eqn as Equation.RECORD_EQUATION(ty = Type.COMPLEX()) guard(not listEmpty(slice.indices))
188 9 then inlineRecordSliceFields(eqn, slice.indices, variables, set, index);
189 else algorithm
190 1222 inlineRecordTupleArrayEquation(Pointer.access(Slice.getT(slice)), Iterator.EMPTY(), variables, record_eqns, set, index, inlineSimple);
191
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1222 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1222 times.
1222 then list(Slice.SLICE(eqn, {}) for eqn in Pointer.access(record_eqns));
192 end match;
193
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1222 times.
1231 if listEmpty(slices) then
194 slices := {slice};
195 end if;
196 end inlineRecordSliceEquation;
197
198 function inlineRecordSliceFields
199 "inlines the fields of a sliced record equation. A field equation gets the slice rows that are
200 within its own rows, fields without any rows of the slice are dropped."
201 input Equation eqn "has to be a RECORD_EQUATION";
202 input list<Integer> indices "zero based rows of the record equation";
203 input VariablePointers variables;
204 input UnorderedSet<VariablePointer> set "new iterators";
205 input Pointer<Integer> index;
206 output list<Slice<Pointer<Equation>>> slices = {};
207 protected
208 Expression lhs, rhs;
209 EquationAttributes attr;
210 Integer recordSize, offset = 0, size;
211 list<Pointer<Equation>> field_eqns;
212 list<Integer> field_indices;
213 algorithm
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 Equation.RECORD_EQUATION(lhs = lhs, rhs = rhs, attr = attr, recordSize = recordSize) := eqn;
215
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 for i in 1:recordSize loop
216 27 field_eqns := createInlinedEquation({}, inlineRecordConstructorExp(lhs, i, variables), inlineRecordConstructorExp(rhs, i, variables),
217 attr, Iterator.EMPTY(), variables, set, index);
218
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 27 times.
54 size := sum(Equation.size(e) for e in field_eqns);
219
8/8
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 45 times.
✓ Branch 4 taken 135 times.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 45 times.
✓ Branch 7 taken 27 times.
162 field_indices := list(k - offset for k guard(k >= offset and k < offset + size) in indices);
220
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 12 times.
27 if not listEmpty(field_indices) then
221
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if List.hasOneElement(field_eqns) and listLength(field_indices) < size then
222 ✗ slices := Slice.SLICE(listHead(field_eqns), field_indices) :: slices;
223 else
224 // nested records are kept as a whole
225
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 15 times.
30 slices := listAppend(list(Slice.SLICE(e, {}) for e in field_eqns), slices);
226 end if;
227 end if;
228 27 offset := offset + size;
229 end for;
230 9 slices := listReverse(slices);
231 end inlineRecordSliceFields;
232
233 function inlineArrayConstructorSingle
234 input output Equation eqn;
235 input Iterator iter;
236 input VariablePointers variables;
237 input UnorderedSet<VariablePointer> set "new iterators";
238 input Pointer<Integer> index;
239 input Pointer<list<Pointer<Equation>>> new_eqns = Pointer.create({});
240 output Boolean changed;
241 algorithm
242 try
243 (eqn, changed) := match eqn
244 local
245 Equation new_eqn, body;
246 Expression lhs, rhs;
247 Call call;
248
249 // CREF = {... for i in []} array constructor equation
250 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.CREF(), rhs=Expression.CALL(call = call as Call.TYPED_ARRAY_CONSTRUCTOR())) algorithm
251 22 then (inlineArrayConstructor(eqn, lhs.cref, call.exp, call.iters, eqn.attr, iter, variables, new_eqns, set, index), true);
252
253 // {... for i in []} = CREF array constructor equation
254 case Equation.ARRAY_EQUATION(lhs=Expression.CALL(call = call as Call.TYPED_ARRAY_CONSTRUCTOR()), rhs = rhs as Expression.CREF()) algorithm
255 ✗ then (inlineArrayConstructor(eqn, rhs.cref, call.exp, call.iters, eqn.attr, iter, variables, new_eqns, set, index), true);
256
257 // apply on for-equation. assumed to be split up
258 case Equation.FOR_EQUATION(body = {body}) algorithm
259 252 (new_eqn, changed) := inlineArrayConstructorSingle(body, eqn.iter, variables, set, index, new_eqns);
260
2/2
✓ Branch 0 taken 250 times.
✓ Branch 1 taken 2 times.
252 new_eqn := if changed then new_eqn else eqn;
261 then (new_eqn, changed);
262
263 // nothing happens
264 4475 else (eqn, false);
265 end match;
266 // unpack the equation
267
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 4727 times.
4749 eqn := if Equation.isDummy(eqn) then Pointer.access(listHead(Pointer.access(new_eqns))) else eqn;
268 else
269 ✗ changed := false;
270 ✗ if Flags.isSet(Flags.FAILTRACE) then
271 ✗ Error.addCompilerWarning("Failed to inline following equation:\n" + Equation.toString(eqn));
272 end if;
273 end try;
274 end inlineArrayConstructorSingle;
275
276 protected
277 function inline extends Module.inlineInterface;
278 protected
279 UnorderedMap<Absyn.Path, Function> replacements "rules for replacements are stored inside here";
280 UnorderedSet<VariablePointer> set "new iterators from function bodies";
281 VariablePointers variables = VarData.getVariables(varData);
282 Absyn.Path key;
283 Function value;
284 // this map should probably be saved somewhere so its not done again for the initial system
285 UnorderedMap<Function, InlineRating> func_map = UnorderedMap.new<InlineRating>(Function.nameHash, Function.nameEqual);
286 algorithm
287 // collect functions
288 569 replacements := UnorderedMap.new<Function>(AbsynUtil.pathHash, AbsynUtil.pathEqual);
289
2/2
✓ Branch 1 taken 1222 times.
✓ Branch 2 taken 569 times.
1791 for tpl in UnorderedMap.toList(funcMap) loop
290 1222 (key, value) := tpl;
291 // only add to the map if the function has one of the inline types and is inlineable
292 // if its inline type = default check if its reasonable to inline
293
2/2
✓ Branch 1 taken 410 times.
✓ Branch 2 taken 812 times.
1222 if checkInline(value, inline_types, func_map) then
294 410 UnorderedMap.add(key, value, replacements);
295 end if;
296 end for;
297
298
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 569 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
569 if Flags.isSet(Flags.DUMPBACKENDINLINE_VERBOSE) and List.contains(inline_types, DAE.InlineType.DEFAULT_INLINE(), DAEUtil.inlineTypeEqual) and not init then
299 ✗ print(StringUtil.headline_2("Heuristic results for Inline=default functions. Threshold = " + intString(HEURISTIC_THRESHOLD)));
300 ✗ print(UnorderedMap.toString(func_map, function Function.signatureString(printTypes = false), InlineRating.toString) + "\n\n");
301 end if;
302
303 // carry the attributes (min, max, nominal, unit, ...) declared on the
304 // function inputs/outputs onto the model variables bound to them before the
305 // call is replaced by the function body, so they are not lost (#15947).
306 569 eqData := propagateAttributes(eqData, variables, replacements);
307
308 // apply replacements
309 569 eqData := Replacements.replaceFunctions(eqData, variables, replacements);
310
311 // do not inline records tuples and arrays after causalization
312 // ToDo: do it properly on strong components, cannot remove equations here
313 569 set := UnorderedSet.new(BVariable.hash, BVariable.equalName);
314
2/2
✓ Branch 3 taken 381 times.
✓ Branch 4 taken 188 times.
569 if not List.any(inline_types, function DAEUtil.inlineTypeEqual(it2 = DAE.AFTER_INDEX_RED_INLINE())) then
315 // replace record constucters after functions because record operator
316 // functions will produce record constructors once inlined
317 381 eqData := inlineRecordsTuplesArrays(eqData, variables, set, init);
318 end if;
319
320 // collect new iterators from replaced function bodies
321 569 eqData := EqData.map(eqData, function BackendDAE.lowerEquationIterators(variables = variables, set = set));
322 569 varData := VarData.addTypedList(varData, UnorderedSet.toList(set), NBVariable.VarData.VarType.ITERATOR);
323 1138 eqData := EqData.mapExp(eqData, function BackendDAE.lowerComponentReferenceExp(variables = variables, complete = true),
324 SOME(function BackendDAE.lowerComponentReference(variables = variables, complete = true)));
325
326 // constants are not assigned at runtime, replace them in function arguments by their bindings
327 569 eqData := EqData.mapExp(eqData, replaceConstantArguments);
328 end inline;
329
330 function replaceConstantArguments
331 "replaces constant crefs in function call arguments by their binding. Records
332 that have constant children are replaced by record expressions."
333 input output Expression exp;
334 algorithm
335 exp := match exp
336 local
337 Call call;
338 case Expression.CALL(call = call as Call.TYPED_CALL()) algorithm
339
4/4
✓ Branch 0 taken 13427 times.
✓ Branch 1 taken 7548 times.
✓ Branch 2 taken 13427 times.
✓ Branch 3 taken 7548 times.
28523 call.arguments := list(replaceConstantArgument(arg) for arg in call.arguments);
340 7548 then Expression.CALL(call);
341 else exp;
342 end match;
343 end replaceConstantArguments;
344
345 function replaceConstantArgument
346 input output Expression exp;
347 protected
348 Pointer<Variable> var_ptr;
349 list<Pointer<Variable>> children;
350 list<Expression> elements;
351 InstNode cls;
352 Expression new_exp;
353 algorithm
354 exp := match exp
355 case Expression.CREF(ty = Type.COMPLEX()) guard(Type.isRecord(exp.ty) and isVarCref(exp.cref) and BVariable.checkCref(exp.cref, BVariable.isRecord, sourceInfo())) algorithm
356 64 var_ptr := BVariable.getVarPointer(exp.cref, sourceInfo());
357 64 children := BVariable.getRecordChildren(var_ptr);
358
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
64 if List.any(children, BVariable.isConst) and List.compareLength(children, Type.recordFields(exp.ty)) == 0 then
359 ✗ elements := list(Replacements.recordChildArg(child) for child in BVariable.getRecordChildrenCref(exp.cref));
360 ✗ new_exp := Expression.makeRecord(InstNode.fullPath(Type.complexNode(exp.ty)), exp.ty, elements);
361 else
362 new_exp := exp;
363 end if;
364 then new_exp;
365 case Expression.CREF() guard(isVarCref(exp.cref) and BVariable.checkCref(exp.cref, BVariable.isConst, sourceInfo()))
366 ✗ then Replacements.recordChildArg(exp.cref);
367 else exp;
368 end match;
369 end replaceConstantArgument;
370
371 function isVarCref
372 input ComponentRef cref;
373 output Boolean b = ComponentRef.isCref(cref) and InstNode.isVar(ComponentRef.node(cref));
374 end isVarCref;
375
376 // =========================================================================
377 // ATTRIBUTE PROPAGATION (min/max/nominal/unit/... see #15947)
378 // =========================================================================
379 function propagateAttributes
380 "Carries the attributes (min, max, nominal, unit, start, fixed, ...) declared
381 on the inputs and outputs of the functions that are about to be inlined onto
382 the model variables that are bound to them, so the information is not lost
383 when the call is replaced by the function body (#15947)."
384 input output EqData eqData;
385 input VariablePointers variables;
386 input UnorderedMap<Absyn.Path, Function> replacements;
387 protected
388 UnorderedMap<ComponentRef, ComponentRef> alias_map;
389 algorithm
390
2/2
✓ Branch 1 taken 514 times.
✓ Branch 2 taken 55 times.
569 if UnorderedMap.isEmpty(replacements) then return; end if;
391 // The frontend extracts function call outputs into auxiliary '$FUN_x'
392 // variables ('$FUN_x = fn(...)' plus 'realVar = $FUN_x'). Build a map from
393 // such auxiliary variables to the real variable they are bound to so the
394 // output attributes end up on the real variable.
395 55 alias_map := UnorderedMap.new<ComponentRef>(ComponentRef.hash, ComponentRef.isEqual);
396 55 eqData := EqData.map(eqData, function collectFunctionAlias(variables = variables, alias_map = alias_map));
397 55 eqData := EqData.map(eqData, function propagateEquationAttributes(variables = variables, replacements = replacements, alias_map = alias_map));
398 end propagateAttributes;
399
400 function collectFunctionAlias
401 "Collects 'realVar = $FUN_x' (or the reverse) equations into a map from the
402 auxiliary function-alias variable to the real variable bound to it."
403 input output Equation eqn;
404 input VariablePointers variables;
405 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
406 algorithm
407 () := match eqn
408 local
409 ComponentRef cr1, cr2;
410 case Equation.SCALAR_EQUATION(lhs = Expression.CREF(cref = cr1), rhs = Expression.CREF(cref = cr2)) algorithm
411 2444 addFunctionAlias(cr1, cr2, variables, alias_map);
412 then ();
413 case Equation.RECORD_EQUATION(lhs = Expression.CREF(cref = cr1), rhs = Expression.CREF(cref = cr2)) algorithm
414 147 addFunctionAlias(cr1, cr2, variables, alias_map);
415 then ();
416 else ();
417 end match;
418 end collectFunctionAlias;
419
420 function addFunctionAlias
421 "Adds a mapping aux -> real if exactly one of the two crefs is a function
422 alias variable."
423 input ComponentRef cr1;
424 input ComponentRef cr2;
425 input VariablePointers variables;
426 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
427 protected
428 ComponentRef n1 = ComponentRef.stripSubscriptsAll(cr1);
429 ComponentRef n2 = ComponentRef.stripSubscriptsAll(cr2);
430 Boolean f1, f2;
431 algorithm
432
3/4
✓ Branch 1 taken 2575 times.
✓ Branch 2 taken 16 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2575 times.
2591 if not (VariablePointers.containsCref(n1, variables) and VariablePointers.containsCref(n2, variables)) then
433 16 return;
434 end if;
435 2575 f1 := BVariable.isFunctionAlias(VariablePointers.getVarSafe(variables, n1));
436 2575 f2 := BVariable.isFunctionAlias(VariablePointers.getVarSafe(variables, n2));
437
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 2461 times.
2575 if f1 and not f2 then
438 114 UnorderedMap.add(n1, n2, alias_map);
439 elseif f2 and not f1 then
440 323 UnorderedMap.add(n2, n1, alias_map);
441 end if;
442 end addFunctionAlias;
443
444 function resolveAlias
445 "Follows the function-alias chain to the real variable bound to it. The chain
446 is expected to be acyclic; Floyd's tortoise-and-hare detects an (illegal)
447 cycle and reports an internal error instead of looping forever."
448 input output ComponentRef name;
449 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
450 protected
451 ComponentRef tortoise = name;
452 Option<ComponentRef> next;
453 algorithm
454 // 'name' is the hare and advances two steps per iteration, 'tortoise' one;
455 // the end of the chain (no successor) is the resolved real variable.
456 while true loop
457 2092 next := UnorderedMap.get(name, alias_map);
458
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2092 times.
✓ Branch 2 taken 1848 times.
✓ Branch 3 taken 244 times.
2092 if isNone(next) then return; end if;
459 244 SOME(name) := next;
460 244 next := UnorderedMap.get(name, alias_map);
461
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
✓ Branch 2 taken 244 times.
✗ Branch 3 not taken.
244 if isNone(next) then return; end if;
462 ✗ SOME(name) := next;
463 // the tortoise trails the hare, so it is guaranteed to have a successor
464 ✗ tortoise := UnorderedMap.getOrFail(tortoise, alias_map);
465 ✗ if ComponentRef.isEqual(tortoise, name) then
466 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + ": detected a cycle in the function-alias map while resolving '" + ComponentRef.toString(name) + "'."});
467 ✗ fail();
468 end if;
469 end while;
470 end resolveAlias;
471
472 function propagateEquationAttributes
473 "Applied on each equation. Does not change the equation, it only mutates the
474 attributes of the variables bound to the inlined function inputs/outputs."
475 input output Equation eqn;
476 input VariablePointers variables;
477 input UnorderedMap<Absyn.Path, Function> replacements;
478 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
479 algorithm
480 // output side: carry the output attributes onto the result variable of a
481 // simple 'cref = fn(...)' (or 'fn(...) = cref') equation.
482 () := match eqn
483 6648 case Equation.SCALAR_EQUATION() algorithm propagateOutput(eqn.lhs, eqn.rhs, variables, replacements, alias_map); then ();
484 1636 case Equation.ARRAY_EQUATION() algorithm propagateOutput(eqn.lhs, eqn.rhs, variables, replacements, alias_map); then ();
485 282 case Equation.RECORD_EQUATION() algorithm propagateOutput(eqn.lhs, eqn.rhs, variables, replacements, alias_map); then ();
486 else ();
487 end match;
488 // input side: carry the input attributes onto every cref argument of any
489 // inlinable call found anywhere in the equation.
490 9288 eqn := Equation.map(eqn, function propagateInputExp(variables = variables, replacements = replacements, alias_map = alias_map));
491 end propagateEquationAttributes;
492
493 function propagateOutput
494 "Carries the (single) output's attributes onto the result variable of a
495 'cref = fn(...)' or 'fn(...) = cref' equation."
496 input Expression lhs;
497 input Expression rhs;
498 input VariablePointers variables;
499 input UnorderedMap<Absyn.Path, Function> replacements;
500 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
501 protected
502 Expression cref_exp, call_exp;
503 Call call;
504 Function fn;
505 algorithm
506 // figure out which side is the result cref and which is the inlinable call
507
4/4
✓ Branch 1 taken 8084 times.
✓ Branch 2 taken 482 times.
✓ Branch 4 taken 7610 times.
✓ Branch 5 taken 474 times.
8566 if Expression.isCref(lhs) and isInlinableCall(rhs, replacements) then
508 cref_exp := lhs;
509 call_exp := rhs;
510 elseif Expression.isCref(rhs) and isInlinableCall(lhs, replacements) then
511 cref_exp := rhs;
512 call_exp := lhs;
513 else
514 8092 return;
515 end if;
516
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 474 times.
474 Expression.CALL(call = call) := call_exp;
518 474 fn := Call.typedFunction(call);
519 474 fn := UnorderedMap.getOrFail(fn.path, replacements);
520 // only single-output functions have a well defined result variable
521
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 474 times.
474 if listLength(fn.outputs) == 1 then
522 474 mergeNodeOntoArg(InstNode.fromHandle(listHead(fn.outputs)), cref_exp, variables, alias_map);
523 end if;
524 end propagateOutput;
525
526 function propagateInputExp
527 "Needs to be mapped with Expression.map(). Merges the declared input
528 attributes onto the cref arguments of every inlinable call."
529 input output Expression exp;
530 input VariablePointers variables;
531 input UnorderedMap<Absyn.Path, Function> replacements;
532 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
533 protected
534 Call call;
535 Function fn;
536 list<Expression> args;
537 algorithm
538 () := match exp
539 case Expression.CALL(call = call as Call.TYPED_CALL(fn = fn, arguments = args))
540 guard UnorderedMap.contains(fn.path, replacements) algorithm
541 515 fn := UnorderedMap.getOrFail(fn.path, replacements);
542
1/2
✓ Branch 2 taken 515 times.
✗ Branch 3 not taken.
515 if listLength(fn.inputs) == listLength(args) then
543
2/2
✓ Branch 1 taken 917 times.
✓ Branch 2 taken 515 times.
1432 for tpl in List.zip(fn.inputs, args) loop
544 917 mergeNodeOntoArg(Util.tuple21(tpl), Util.tuple22(tpl), variables, alias_map);
545 end for;
546 end if;
547 then ();
548 else ();
549 end match;
550 end propagateInputExp;
551
552 function isInlinableCall
553 "True if the expression is a call to a function that will be inlined."
554 input Expression exp;
555 input UnorderedMap<Absyn.Path, Function> replacements;
556 output Boolean b;
557 algorithm
558 b := match exp
559 local
560 Function fn;
561 1152 case Expression.CALL(call = Call.TYPED_CALL(fn = fn)) then UnorderedMap.contains(fn.path, replacements);
562 else false;
563 end match;
564 end isInlinableCall;
565
566 function mergeNodeOntoArg
567 "Merges the attributes declared on an input/output node onto the model
568 variable bound to the given argument expression. Handles records both as
569 crefs and as record constructors/literals."
570 input InstNode node;
571 input Expression arg;
572 input VariablePointers variables;
573 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
574 protected
575 list<InstNode> node_children;
576 list<Expression> elems;
577 algorithm
578 () := match arg
579 case Expression.CREF() algorithm
580 1108 mergeNodeOntoCref(node, arg.cref, variables, alias_map);
581 then ();
582
583 // record constructor / record literal argument: recurse element-wise
584 else algorithm
585 1105 node_children := nodeRecordChildren(node);
586
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 1033 times.
1105 if not listEmpty(node_children) then
587 try
588 72 elems := Expression.getRecordElements(arg);
589 else
590 elems := {};
591 end try;
592
1/2
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
72 if listLength(node_children) == listLength(elems) then
593
2/2
✓ Branch 1 taken 822 times.
✓ Branch 2 taken 72 times.
966 for tpl in List.zip(node_children, elems) loop
594 822 mergeNodeOntoArg(Util.tuple21(tpl), Util.tuple22(tpl), variables, alias_map);
595 end for;
596 end if;
597 end if;
598 then ();
599 end match;
600 end mergeNodeOntoArg;
601
602 function mergeNodeOntoCref
603 "Merges the attributes declared on an input/output node onto the variable
604 referenced by cref. For record variables it recurses onto the children."
605 input InstNode node;
606 input ComponentRef cref;
607 input VariablePointers variables;
608 input UnorderedMap<ComponentRef, ComponentRef> alias_map;
609 protected
610 ComponentRef name;
611 Pointer<Variable> var_ptr;
612 Variable var;
613 BackendExtension.BackendInfo binfo;
614 list<Pointer<Variable>> rec_children;
615 list<InstNode> node_children;
616 list<ComponentRef> cref_children;
617 BackendExtension.VariableAttributes src_attrs;
618 algorithm
619 2092 name := ComponentRef.stripSubscriptsAll(cref);
620 // redirect auxiliary function-alias variables to the real variable
621 2092 name := resolveAlias(name, alias_map);
622
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 2074 times.
2092 if not VariablePointers.containsCref(name, variables) then
623 18 return;
624 end if;
625 2074 var_ptr := VariablePointers.getVarSafe(variables, name);
626
627 // records: recurse onto the children variables
628 2074 rec_children := BVariable.getRecordChildren(var_ptr);
629
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 1682 times.
2074 if not listEmpty(rec_children) then
630 392 node_children := nodeRecordChildren(node);
631 392 cref_children := BVariable.getRecordChildrenCref(name);
632
1/2
✓ Branch 2 taken 392 times.
✗ Branch 3 not taken.
392 if listLength(node_children) == listLength(cref_children) then
633
2/2
✓ Branch 1 taken 984 times.
✓ Branch 2 taken 392 times.
1376 for tpl in List.zip(node_children, cref_children) loop
634 984 mergeNodeOntoCref(Util.tuple21(tpl), Util.tuple22(tpl), variables, alias_map);
635 end for;
636 end if;
637 392 return;
638 end if;
639
640 // scalar leaf: merge the declared attributes onto the variable
641 try
642 1682 src_attrs := nodeVariableAttributes(node);
643 1682 var := Pointer.access(var_ptr);
644 1682 binfo := var.backendinfo;
645 1682 binfo.attributes := BackendExtension.VariableAttributes.merge(binfo.attributes, src_attrs);
646 1682 var.backendinfo := binfo;
647 1682 Pointer.update(var_ptr, var);
648 else
649 end try;
650 end mergeNodeOntoCref;
651
652 function nodeRecordChildren
653 "Returns the record field nodes of a node, or {} if it is not a record."
654 input InstNode node;
655 output list<InstNode> children;
656 protected
657 Type elem_ty;
658 algorithm
659 children := match Type.arrayElementType(InstNode.getType(node))
660 case elem_ty as Type.COMPLEX()
661 464 then arrayList(Class.getComponents(InstNode.getClass(Type.complexNode(elem_ty))));
662 else {};
663 end match;
664 end nodeRecordChildren;
665
666 function nodeVariableAttributes
667 "Builds the backend VariableAttributes declared on a (scalar) function
668 input/output node. Only constant attribute values are kept so that no
669 function-local references leak into the model variable's attributes."
670 input InstNode node;
671 output BackendExtension.VariableAttributes attrs;
672 protected
673 Component comp = InstNode.component(node);
674 list<tuple<String, Binding>> ty_attrs;
675 algorithm
676
4/4
✓ Branch 3 taken 7120 times.
✓ Branch 4 taken 1682 times.
✓ Branch 5 taken 7120 times.
✓ Branch 6 taken 1682 times.
8802 ty_attrs := list((Modifier.name(m), Modifier.binding(m)) for m in
677 Class.getTypeAttributes(InstNode.getClass(Component.classInstance(comp))));
678 1682 ty_attrs := List.filterOnTrue(ty_attrs, attrIsConst);
679 1682 attrs := BackendExtension.VariableAttributes.create(ty_attrs, InstNode.getType(node),
680 Component.getAttributes(comp), {}, Component.comment(comp));
681 end nodeVariableAttributes;
682
683 function attrIsConst
684 "True if the attribute binding is typed and contains no component references."
685 input tuple<String, Binding> attr;
686 output Boolean b;
687 protected
688 Expression exp;
689 algorithm
690 try
691 7120 exp := Binding.getTypedExp(Util.tuple22(attr));
692 7120 b := not Expression.contains(exp, Expression.isCref);
693 else
694 b := false;
695 end try;
696 end attrIsConst;
697
698 function inlineRecordsTuplesArrays
699 "does not inline simple record equalities"
700 input output EqData eqData;
701 input VariablePointers variables;
702 input UnorderedSet<VariablePointer> set "new iterators";
703 input Boolean init;
704 protected
705 Pointer<Integer> index = EqData.getUniqueIndex(eqData);
706 Pointer<list<Pointer<Equation>>> new_eqns = Pointer.create({});
707 algorithm
708
2/2
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 193 times.
381 if init then
709 eqData := match eqData
710 case EqData.EQ_DATA_SIM() algorithm
711 376 eqData.initials := EquationPointers.map(eqData.initials, function inlineRecordTupleArrayEquation(iter = Iterator.EMPTY(), variables = variables, new_eqns = new_eqns, set = set, index = index, inlineSimple = false));
712 188 eqData.initials := EquationPointers.addList(Pointer.access(new_eqns), eqData.initials);
713 188 eqData.initials := EquationPointers.compress(eqData.initials);
714 then eqData;
715
716 else eqData;
717 end match;
718 else
719 193 eqData := EqData.map(eqData, function inlineRecordTupleArrayEquation(iter = Iterator.EMPTY(), variables = variables, new_eqns = new_eqns, set = set, index = index, inlineSimple = false));
720 193 eqData := EqData.addUntypedList(eqData, Pointer.access(new_eqns), false);
721 193 eqData := EqData.compress(eqData);
722 end if;
723 end inlineRecordsTuplesArrays;
724
725 public
726 function inlineRecordTupleArrayEquation
727 input output Equation eqn;
728 input Iterator iter;
729 input VariablePointers variables;
730 input Pointer<list<Pointer<Equation>>> new_eqns;
731 input UnorderedSet<VariablePointer> set "new iterators";
732 input Pointer<Integer> index;
733 input Boolean inlineSimple;
734 algorithm
735 try
736 eqn := match eqn
737 local
738 Equation new_eqn, body;
739 Expression lhs, rhs;
740 Call call;
741 Dimension dim;
742 list<Expression> elements;
743 Integer size;
744
745 // don't inline simple cref equalities
746 148 case Equation.RECORD_EQUATION(lhs = Expression.CREF(), rhs = Expression.CREF()) guard(not inlineSimple) then eqn;
747 554 case Equation.ARRAY_EQUATION(lhs = Expression.CREF(), rhs = Expression.CREF()) guard(not inlineSimple) then eqn;
748
749 // try to inline other record equations. try catch to be sure to not discard
750 71 case Equation.RECORD_EQUATION(ty = Type.COMPLEX()) then inlineRecordEquation(eqn, eqn.lhs, eqn.rhs, iter, eqn.attr, eqn.recordSize, variables, new_eqns, set, index, inlineSimple);
751
752 // only if record size is not NONE()
753 ✗ case Equation.ARRAY_EQUATION(recordSize = SOME(size)) then inlineRecordEquation(eqn, eqn.lhs, eqn.rhs, iter, eqn.attr, size, variables, new_eqns, set, index, inlineSimple);
754
755 // inlining potential tuple equations
756 4 case Equation.RECORD_EQUATION() then inlineTupleEquation(eqn, eqn.lhs, eqn.rhs, eqn.attr, iter, variables, new_eqns, set, index);
757
758 // {...} = {...} array equation
759 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.ARRAY(), rhs = rhs as Expression.ARRAY())
760 ✗ then inlineArrayEquation(eqn, lhs.elements, rhs.elements, eqn.attr, iter, variables, new_eqns, set, index);
761
762 // CREF = {...} array equation
763 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.CREF(), rhs = rhs as Expression.ARRAY()) algorithm
764 311 dim := listHead(Type.arrayDims(lhs.ty));
765
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 311 times.
✓ Branch 2 taken 577 times.
✓ Branch 3 taken 311 times.
1776 elements := list(NFExpression.applySubscripts({Subscript.nth(dim, i)}, lhs, true) for i in 1:arrayLength(rhs.elements));
766 311 then inlineArrayEquation(eqn, listArray(elements), rhs.elements, eqn.attr, iter, variables, new_eqns, set, index);
767
768 // {...} = CREF array equation
769 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.ARRAY(), rhs = rhs as Expression.CREF()) algorithm
770 ✗ dim := listHead(Type.arrayDims(rhs.ty));
771 ✗ elements := list(NFExpression.applySubscripts({Subscript.nth(dim, i)}, rhs, true) for i in 1:arrayLength(lhs.elements));
772 ✗ then inlineArrayEquation(eqn, lhs.elements, listArray(elements), eqn.attr, iter, variables, new_eqns, set, index);
773
774 // {...} = exp array equation, if exp can be subscripted (e.g. if-expression of arrays)
775 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.ARRAY(), rhs = rhs) guard(not Expression.isCref(rhs))
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
14 then inlineArrayEquation(eqn, lhs.elements, subscriptElements(rhs, arrayLength(lhs.elements)), eqn.attr, iter, variables, new_eqns, set, index);
777
778 // exp = {...} array equation
779 case Equation.ARRAY_EQUATION(lhs = lhs, rhs = rhs as Expression.ARRAY()) guard(not Expression.isCref(lhs))
780
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
36 then inlineArrayEquation(eqn, subscriptElements(lhs, arrayLength(rhs.elements)), rhs.elements, eqn.attr, iter, variables, new_eqns, set, index);
781
782 // CREF = {... for i in []} array constructor equation
783 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.CREF(), rhs=Expression.CALL(call = call as Call.TYPED_ARRAY_CONSTRUCTOR()))
784 5 then inlineArrayConstructor(eqn, lhs.cref, call.exp, call.iters, eqn.attr, iter, variables, new_eqns, set, index);
785
786 // {... for i in []} = CREF array constructor equation
787 case Equation.ARRAY_EQUATION(lhs=Expression.CALL(call = call as Call.TYPED_ARRAY_CONSTRUCTOR()), rhs = rhs as Expression.CREF())
788 ✗ then inlineArrayConstructor(eqn, rhs.cref, call.exp, call.iters, eqn.attr, iter, variables, new_eqns, set, index);
789
790 // CREF = cat()
791 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.CREF(), rhs = Expression.CALL(call = call))
792 guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(Call.typedFunction(call))) == "cat")
793 74 then inlineCatCall(eqn, lhs.cref, Call.arguments(call), eqn.attr, iter, variables, new_eqns, set, index);
794
795 // cat() = CREF
796 case Equation.ARRAY_EQUATION(lhs = Expression.CALL(call = call), rhs = rhs as Expression.CREF())
797 guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(Call.typedFunction(call))) == "cat")
798 ✗ then inlineCatCall(eqn, rhs.cref, Call.arguments(call), eqn.attr, iter, variables, new_eqns, set, index);
799
800 // CREF = promote()
801 case Equation.ARRAY_EQUATION(lhs = lhs as Expression.CREF(), rhs = Expression.CALL(call = call))
802 guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(Call.typedFunction(call))) == "promote")
803 2 then inlinePromoteCall(eqn, lhs.cref, Call.arguments(call), eqn.attr, iter, variables, new_eqns, set, index);
804
805 // promote() = CREF
806 case Equation.ARRAY_EQUATION(lhs = Expression.CALL(call = call), rhs = rhs as Expression.CREF())
807 guard(AbsynUtil.pathString(Function.nameConsiderBuiltin(Call.typedFunction(call))) == "promote")
808 ✗ then inlinePromoteCall(eqn, rhs.cref, Call.arguments(call), eqn.attr, iter, variables, new_eqns, set, index);
809
810 // apply on for-equation. assumed to be split up
811 case Equation.FOR_EQUATION(body = {body}) algorithm
812 973 new_eqn := inlineRecordTupleArrayEquation(body, eqn.iter, variables, new_eqns, set, index, true);
813
2/2
✓ Branch 1 taken 969 times.
✓ Branch 2 taken 4 times.
973 new_eqn := if Equation.isDummy(new_eqn) then new_eqn else eqn;
814 then new_eqn;
815
816 // apply on if-equation body equations
817 case Equation.IF_EQUATION() guard IfEquationBody.isRecordOrTupleEquation(eqn.body) algorithm
818 ✗ new_eqn := inlineRecordTupleArrayIfEquation(eqn, eqn.body, iter, variables, new_eqns, set, index, inlineSimple);
819 ✗ new_eqn := if Equation.isDummy(new_eqn) then new_eqn else eqn;
820 then new_eqn;
821
822 // nothing happens
823 14259 else eqn;
824 end match;
825 else
826
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if Flags.isSet(Flags.FAILTRACE) then
827 ✗ Error.addCompilerWarning("Failed to inline following equation:\n" + Equation.toString(eqn));
828 end if;
829 end try;
830 end inlineRecordTupleArrayEquation;
831
832 protected
833 function inlineRecordTupleArrayIfEquation
834 "Documentation"
835 input output Equation eqn;
836 input IfEquationBody body;
837 input Iterator iter;
838 input VariablePointers variables;
839 input Pointer<list<Pointer<Equation>>> new_eqns;
840 input UnorderedSet<VariablePointer> set "new iterators";
841 input Pointer<Integer> index;
842 input Boolean inlineSimple;
843 protected
844 list<Pointer<Equation>> eqns;
845 IfEquationBody new_body;
846 Pointer<Equation> new_eqn;
847 algorithm
848 ✗ eqns := Pointer.access(new_eqns);
849 ✗ new_body := inlineRecordTupleArrayIfBody(body, iter, variables, set, index, inlineSimple);
850 ✗ for b in IfEquationBody.split(new_body) loop
851 ✗ new_eqn := IfEquationBody.makeIfEquation(b, index, NBEquation.SIMULATION_STR, iter, Equation.getSource(eqn), Equation.getAttributes(eqn));
852 eqns := new_eqn :: eqns;
853 end for;
854 ✗ Pointer.update(new_eqns, eqns);
855 eqn := Equation.DUMMY_EQUATION();
856 end inlineRecordTupleArrayIfEquation;
857
858 function inlineRecordTupleArrayIfBody
859 "Documentation"
860 input output IfEquationBody body;
861 input Iterator iter;
862 input VariablePointers variables;
863 input UnorderedSet<VariablePointer> set "new iterators";
864 input Pointer<Integer> index;
865 input Boolean inlineSimple;
866 protected
867 Pointer<list<Pointer<Equation>>> new_eqns = Pointer.create({});
868 algorithm
869 ✗ body.then_eqns := List.flatten(list(
870 match inlineRecordTupleArrayEquation(Pointer.access(e), iter, variables, new_eqns, set, index, inlineSimple)
871 ✗ case Equation.DUMMY_EQUATION() then Pointer.access(new_eqns);
872 else {e};
873 end match for e in body.then_eqns));
874 body.else_if := Util.applyOption(body.else_if, function inlineRecordTupleArrayIfBody(iter = iter, variables = variables, set = set, index = index, inlineSimple = inlineSimple));
875 end inlineRecordTupleArrayIfBody;
876
877 function inlineRecordEquation
878 "tries to inline a record equation. Removes the old equation by making it a dummy
879 and appends new equations to the mutable list.
880 EquationPointers.compress() should be used afterwards to remove the dummy equations."
881 input output Equation eqn;
882 input Expression lhs;
883 input Expression rhs;
884 input Iterator iter;
885 input EquationAttributes attr;
886 input Integer recordSize;
887 input VariablePointers variables;
888 input Pointer<list<Pointer<Equation>>> new_eqns;
889 input UnorderedSet<VariablePointer> set "new iterators";
890 input Pointer<Integer> index;
891 input Boolean inlineSimple;
892 protected
893 Expression new_lhs, new_rhs;
894 list<Pointer<Equation>> eqns;
895 algorithm
896
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 71 times.
71 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
897 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
898 ✗ if not Iterator.isEmpty(iter) then
899 ✗ print("{" + Iterator.toString(iter) + "} ");
900 end if;
901 ✗ print(Equation.toString(eqn) + "\n");
902 end if;
903 71 eqns := Pointer.access(new_eqns);
904
1/2
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
236 for i in 1:recordSize loop
905 165 new_lhs := inlineRecordConstructorExp(lhs, i, variables);
906 165 new_rhs := inlineRecordConstructorExp(rhs, i, variables);
907 165 eqns := createInlinedEquation(eqns, new_lhs, new_rhs, attr, iter, variables, set, index);
908 end for;
909 71 Pointer.update(new_eqns, eqns);
910 eqn := Equation.DUMMY_EQUATION();
911 end inlineRecordEquation;
912
913 function inlineTupleEquation
914 "online inlines of LHS and RHS are of type Tuple"
915 input output Equation eqn;
916 input Expression LHS;
917 input Expression RHS;
918 input EquationAttributes attr;
919 input Iterator iter;
920 input VariablePointers variables;
921 input Pointer<list<Pointer<Equation>>> new_eqns;
922 input UnorderedSet<VariablePointer> set "new iterators";
923 input Pointer<Integer> index;
924 protected
925 list<Pointer<Equation>> eqns;
926 list<Expression> lhs_elems, rhs_elems;
927 Expression lhs, rhs;
928 algorithm
929 4 lhs_elems := getElementList(LHS);
930 4 rhs_elems := getElementList(RHS);
931
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 time.
4 if not listEmpty(lhs_elems) and List.compareLength(lhs_elems, rhs_elems) == 0 then
932
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
933 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
934 ✗ if not Iterator.isEmpty(iter) then
935 ✗ print("{" + Iterator.toString(iter) + "} ");
936 end if;
937 ✗ print(Equation.toString(eqn) + "\n");
938 end if;
939 1 eqns := Pointer.access(new_eqns);
940
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 for tpl in List.zip(lhs_elems, rhs_elems) loop
941 2 (lhs, rhs) := tpl;
942 // skip wild cref assignments
943
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 if not (Expression.isWildCref(lhs) or Expression.isWildCref(rhs)) then
944 2 eqns := createInlinedEquation(eqns, lhs, rhs, attr, iter, variables, set, index);
945 end if;
946 end for;
947 1 Pointer.update(new_eqns, eqns);
948 eqn := Equation.DUMMY_EQUATION();
949 end if;
950 end inlineTupleEquation;
951
952 function inlineArrayEquation
953 "inlines array equations of the form {a, b, c, ...} = {d, e, f, ...}
954 to a = d; and so on. Also inlines them if inside for-equation or nested arrays.
955 ToDo: inside When/If"
956 input output Equation eqn;
957 input array<Expression> lhs_elements;
958 input array<Expression> rhs_elements;
959 input EquationAttributes attr;
960 input Iterator iter;
961 input VariablePointers variables;
962 input Pointer<list<Pointer<Equation>>> new_eqns;
963 input UnorderedSet<VariablePointer> set "new iterators";
964 input Pointer<Integer> index;
965 protected
966 list<Pointer<Equation>> eqns;
967 algorithm
968
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 321 times.
321 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
969 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
970 ✗ if not Iterator.isEmpty(iter) then
971 ✗ print("{" + Iterator.toString(iter) + "} ");
972 end if;
973 ✗ print(Equation.toString(eqn) + "\n");
974 end if;
975 321 eqns := Pointer.access(new_eqns);
976
1/2
✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
918 for i in 1: arrayLength(lhs_elements) loop
977 597 eqns := createInlinedEquation(eqns, lhs_elements[i], rhs_elements[i], attr, iter, variables, set, index);
978 end for;
979 321 Pointer.update(new_eqns, eqns);
980 eqn := Equation.DUMMY_EQUATION();
981 end inlineArrayEquation;
982
983 function subscriptElements
984 "subscripts the outermost dimension of an array expression element by element.
985 fails if a subscript cannot be pushed into the expression."
986 input Expression exp;
987 input Integer n;
988 output array<Expression> elements;
989 protected
990 Dimension dim = listHead(Type.arrayDims(Expression.typeOf(exp)));
991 algorithm
992
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 25 times.
143 elements := listArray(list(Expression.applySubscripts({Subscript.nth(dim, i)}, exp, true) for i in 1:n));
993
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 10 times.
45 for e in elements loop
994
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 20 times.
35 if Expression.contains(e, isSubscriptedExp) then
995 15 fail();
996 end if;
997 end for;
998 end subscriptElements;
999
1000 function isSubscriptedExp
1001 input Expression exp;
1002 output Boolean b;
1003 algorithm
1004 b := match exp
1005 case Expression.SUBSCRIPTED_EXP() then true;
1006 else false;
1007 end match;
1008 end isSubscriptedExp;
1009
1010 function inlineArrayConstructor
1011 input output Equation eqn;
1012 input ComponentRef cref;
1013 input Expression rhs;
1014 input list<tuple<InstNode, Expression>> iters;
1015 input EquationAttributes attr;
1016 input Iterator iter;
1017 input VariablePointers variables;
1018 input Pointer<list<Pointer<Equation>>> new_eqns;
1019 input UnorderedSet<VariablePointer> set "new iterators";
1020 input Pointer<Integer> index;
1021 protected
1022 list<tuple<ComponentRef, Expression, Option<Iterator>>> frames;
1023 list<Subscript> subs;
1024 Expression cref_exp, new_rhs;
1025 UnorderedSet<VariablePointer> local_set = UnorderedSet.new(BVariable.hash, BVariable.equalName);
1026 VariablePointers local_it;
1027 list<Pointer<Equation>> eqns;
1028 algorithm
1029
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1030 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
1031 ✗ if not Iterator.isEmpty(iter) then
1032 ✗ print("{" + Iterator.toString(iter) + "} ");
1033 end if;
1034 ✗ print(Equation.toString(eqn) + "\n");
1035 end if;
1036 27 eqns := Pointer.access(new_eqns);
1037
1038 // inline the iterators
1039
4/4
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 27 times.
56 frames := list(Iterator.createFrame(iter, local_set) for iter in iters);
1040 27 UnorderedSet.merge(set, local_set);
1041
1042 // add the iterators to the cref, the last iterator is the outermost dimension
1043 27 subs := listReverse(Iterator.normalizedSubscripts(Iterator.fromFrames(frames)));
1044 27 cref_exp := Expression.fromCref(ComponentRef.mergeSubscripts(subs, cref, true));
1045
1046 // lower the potentiall new iterators
1047 27 local_it := VariablePointers.fromList(UnorderedSet.toList(local_set));
1048 27 cref_exp := Expression.map(cref_exp, function BackendDAE.lowerComponentReferenceExp(variables = local_it, complete = false));
1049 27 new_rhs := Expression.map(rhs, function BackendDAE.lowerComponentReferenceExp(variables = local_it, complete = false));
1050
1051 27 eqns := createInlinedEquation(eqns, cref_exp, new_rhs, attr, Iterator.addFrames(iter, frames), variables, set, index);
1052 27 Pointer.update(new_eqns, eqns);
1053 eqn := Equation.DUMMY_EQUATION();
1054 end inlineArrayConstructor;
1055
1056 function inlinePromoteCall
1057 "inlines a promote() call by creating a new equation for the argument. needs prior handling in function alias
1058 where both the promote() and its argument have been replaced by alias variables such that we can always expect
1059 the structure:
1060 FUN_2 = promote(FUN_1, DIM)
1061 the result will be:
1062 FUN_2[:,:,:,1,1,1,1] = FUN_1;
1063 where the amount of ':' is equal to the number of dimensions in FUN_1 and
1064 the amount of '1' is equal to DIM minus that number.
1065 "
1066 input output Equation eqn;
1067 input ComponentRef cref;
1068 input list<Expression> args;
1069 input EquationAttributes attr;
1070 input Iterator iter;
1071 input VariablePointers variables;
1072 input Pointer<list<Pointer<Equation>>> new_eqns;
1073 input UnorderedSet<VariablePointer> set "new iterators";
1074 input Pointer<Integer> index;
1075 protected
1076 Expression arg;
1077 Integer n, dim_count;
1078 list<Subscript> subs;
1079 Expression lhs;
1080 Pointer<Equation> new_eqn;
1081 algorithm
1082
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1083 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
1084 ✗ if not Iterator.isEmpty(iter) then
1085 ✗ print("{" + Iterator.toString(iter) + "} ");
1086 end if;
1087 ✗ print(Equation.toString(eqn) + "\n");
1088 end if;
1089
1090
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
2 {arg, Expression.INTEGER(n)} := args;
1091
1092 eqn := match arg
1093 case Expression.CREF() algorithm
1094 2 dim_count := Type.dimensionCount(ComponentRef.getSubscriptedType(arg.cref));
1095
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if n == dim_count then
1096 ✗ lhs := Expression.fromCref(cref);
1097 else
1098 2 subs := Subscript.fillWithWholeLeft(List.fill(Subscript.INDEX(Expression.INTEGER(1)), n - dim_count), n);
1099 2 lhs := Expression.fromCref(ComponentRef.mergeSubscripts(subs, cref));
1100 end if;
1101 // create the new equation
1102 2 new_eqn := Equation.makeAssignment(lhs, arg, index, NBEquation.SIMULATION_STR, iter, attr);
1103
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1104 ✗ print("-- Result: " + Equation.pointerToString(new_eqn) + "\n");
1105 end if;
1106 2 then Pointer.access(new_eqn);
1107 else eqn;
1108 end match;
1109 end inlinePromoteCall;
1110
1111 function inlineCatCall
1112 "inlines a cat() call by creating a new equation each of the arguments. needs prior handling in function alias
1113 where both the cat() and all its arguments have been replaced by alias variables such that we can always expect
1114 the structure:
1115 FUN_X = cat(DIM, FUN_1, FUN_2, FUN_3, ....)
1116 the result will be for each scalar argument:
1117 FUN_X[shift + 1] = FUN_1;
1118 and for array argument:
1119 for $i0 in 1:size(FUN_2) loop
1120 FUN_X[shift + i0] = FUN_2[i0];
1121 end for;
1122 shift always takes the size of the sum of the previous sizes so that all replacements in total make up the size of FUN_X."
1123 input output Equation eqn;
1124 input ComponentRef cref;
1125 input list<Expression> args;
1126 input EquationAttributes attr;
1127 input Iterator iter;
1128 input VariablePointers variables;
1129 input Pointer<list<Pointer<Equation>>> new_eqns;
1130 input UnorderedSet<VariablePointer> set "new iterators";
1131 input Pointer<Integer> index;
1132 protected
1133 Integer n, sz;
1134 list<Expression> rest;
1135 list<Pointer<Equation>> eqns;
1136 Type ty;
1137 Dimension dim;
1138 ComponentRef iterator_name, lhs, rhs;
1139 Pointer<Variable> iterator_var;
1140 VariablePointers update_vars;
1141 Expression range, subscript_exp, lhs_sub, lhs_exp, rhs_exp, shift, new_size;
1142 Iterator local_iter;
1143 Pointer<Equation> new_eqn;
1144 Boolean failed = false;
1145 algorithm
1146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
74 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1147 ✗ print("\n[" + getInstanceName() + "] Inlining: ");
1148 ✗ if not Iterator.isEmpty(iter) then
1149 ✗ print("{" + Iterator.toString(iter) + "} ");
1150 end if;
1151 ✗ print(Equation.toString(eqn) + "\n");
1152 end if;
1153 74 eqns := Pointer.access(new_eqns);
1154
1155 // split of the first argument as it is the dimension indicator
1156
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 74 times.
74 Expression.INTEGER(n) :: rest := args;
1157
1158 // create an iterator that can be used multiple times
1159 74 iterator_name := ComponentRef.makeIterator(InstNode.newUniqueIterator(), Type.INTEGER());
1160 74 iterator_var := BackendDAE.lowerIterator(iterator_name);
1161 74 iterator_name := BVariable.getVarName(iterator_var);
1162 // create a variable array that is used to properly lower the variable nodes of iterators
1163 74 update_vars := VariablePointers.fromList({iterator_var});
1164 74 UnorderedSet.add(iterator_var, set);
1165 // create an expression of the iterator that can be used for subscripting
1166 74 subscript_exp := Expression.fromCref(iterator_name);
1167
1168 // initialize the shift at 0
1169 74 shift := Expression.INTEGER(0);
1170
1171
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 74 times.
288 for arg in rest loop
1172 failed := match arg
1173 case Expression.CREF(cref = rhs) guard(not failed) algorithm
1174 165 ty := Expression.typeOf(arg);
1175
1/2
✓ Branch 1 taken 165 times.
✗ Branch 2 not taken.
165 if Type.isArray(ty) then
1176 165 dim := Type.nthDimension(ty, n);
1177 165 sz := Dimension.size(dim);
1178 // if its size one, create scalar assignment, otherwise create for-loop
1179
3/4
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 159 times.
165 if sz <> 1 or Dimension.isResizable(dim) then
1180 // ARRAY
1181 // make a range of proper size to the rhs
1182 6 new_size := Dimension.sizeExp(dim);
1183 6 range := Expression.makeRange(Expression.INTEGER(1), NONE(), new_size);
1184 // add the new iterator
1185 12 local_iter := Iterator.addFrames(iter, {(iterator_name, range, NONE())});
1186 // subscript the LHS with the shift+iterator
1187
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 lhs_sub := if Expression.isZero(shift) then subscript_exp else Expression.MULTARY({shift, subscript_exp}, {}, Operator.makeAdd(Type.INTEGER()));
1188 12 lhs := ComponentRef.mergeSubscripts(Subscript.fillWithWholeLeft({Subscript.INDEX(lhs_sub)}, n), cref);
1189 // subscript the LHS only with iterator
1190 12 rhs := ComponentRef.mergeSubscripts(Subscript.fillWithWholeLeft({Subscript.INDEX(subscript_exp)}, n), rhs);
1191 // lower the iterators to add proper variable nodes
1192 6 lhs_exp := Expression.map(Expression.fromCref(lhs), function BackendDAE.lowerComponentReferenceExp(variables = update_vars, complete = false));
1193 6 rhs_exp := Expression.map(Expression.fromCref(rhs), function BackendDAE.lowerComponentReferenceExp(variables = update_vars, complete = false));
1194 else
1195 // SCALAR.
1196 // properly subscript LHS with shift
1197 new_size := Expression.INTEGER(1);
1198 159 lhs_sub := bumpShift(shift, new_size);
1199 318 lhs := ComponentRef.mergeSubscripts(Subscript.fillWithWholeLeft({Subscript.INDEX(lhs_sub)}, n), cref);
1200 159 lhs_exp := Expression.fromCref(lhs);
1201 // if its an array type RHS needs to be subscripted even though its of size 1
1202 159 rhs := ComponentRef.mergeSubscripts(Subscript.fillWithWholeLeft({Subscript.INDEX(Expression.INTEGER(1))}, n), rhs);
1203 159 rhs_exp := Expression.fromCref(rhs);
1204 // the local iterator does not add anything, just take surrounding iterator
1205 local_iter := iter;
1206 end if;
1207 else
1208 // SCALAR
1209 // properly subscript LHS with shift
1210 new_size := Expression.INTEGER(1);
1211 ✗ lhs_sub := bumpShift(shift, new_size);
1212 ✗ lhs := ComponentRef.mergeSubscripts(Subscript.fillWithWholeLeft({Subscript.INDEX(lhs_sub)}, n), cref);
1213 ✗ lhs_exp := Expression.fromCref(lhs);
1214 ✗ rhs_exp := Expression.fromCref(rhs);
1215 // the local iterator does not add anything, just take surrounding iterator
1216 local_iter := iter;
1217 end if;
1218
1219 // create the new equation
1220 165 new_eqn := Equation.makeAssignment(lhs_exp, rhs_exp, index, NBEquation.SIMULATION_STR, local_iter, attr);
1221
1222 // bump the shift adding the size of this last equation
1223 165 shift := bumpShift(shift, new_size);
1224
1225 eqns := new_eqn :: eqns;
1226
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 165 times.
165 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1227 ✗ print("-- Result: " + Equation.pointerToString(new_eqn) + "\n");
1228 end if;
1229 then false;
1230
1231 // inline for literals down to element, nested arrays possible
1232 case Expression.ARRAY() guard(not failed and Expression.isLiteral(arg)) algorithm
1233 49 (eqns, shift) := inlineCatCallLiterals(arg, cref, iter, attr, n, index, eqns, shift);
1234 then false;
1235
1236 else true;
1237 end match;
1238 end for;
1239
1240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 if not failed then
1241 74 Pointer.update(new_eqns, eqns);
1242 eqn := Equation.DUMMY_EQUATION();
1243 end if;
1244 end inlineCatCall;
1245
1246 function inlineCatCallLiterals
1247 "recursively inlines arrays of literal expressions that were an argument to a cat() call"
1248 input Expression exp;
1249 input ComponentRef cref;
1250 input Iterator iter;
1251 input EquationAttributes attr;
1252 input Integer n;
1253 input Pointer<Integer> index;
1254 input output list<Pointer<Equation>> eqns;
1255 input output Expression shift;
1256 input list<Subscript> subs = {};
1257 algorithm
1258 () := match exp
1259 local
1260 Expression sub_idx;
1261 Boolean is_cat_dim;
1262 Subscript sub;
1263 ComponentRef lhs;
1264 Expression lhs_exp;
1265 Pointer<Equation> new_eqn;
1266
1267 case Expression.ARRAY() algorithm
1268 96 is_cat_dim := n == listLength(subs) + 1;
1269
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 47 times.
96 sub_idx := if is_cat_dim then bumpShift(shift, Expression.INTEGER(1)) else Expression.INTEGER(1);
1270
1271
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
✓ Branch 3 taken 126 times.
✓ Branch 4 taken 96 times.
318 for elem in exp.elements loop
1272 126 sub := Subscript.INDEX(sub_idx);
1273 126 (eqns, shift) := inlineCatCallLiterals(elem, cref, iter, attr, n, index, eqns, shift, sub :: subs);
1274 126 sub_idx := bumpShift(sub_idx, Expression.INTEGER(1));
1275 end for;
1276
1277
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 47 times.
96 if is_cat_dim then
1278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
98 shift := bumpShift(shift, Expression.INTEGER(arrayLength(exp.elements)));
1279 end if;
1280 then ();
1281
1282 else algorithm
1283 // properly subscript LHS with shift
1284 79 lhs := ComponentRef.mergeSubscripts(listReverse(subs), cref);
1285 79 lhs_exp := Expression.fromCref(lhs);
1286
1287 // create the new equation
1288 79 new_eqn := Equation.makeAssignment(lhs_exp, exp, index, NBEquation.SIMULATION_STR, iter, attr);
1289
1290 eqns := new_eqn :: eqns;
1291
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 79 times.
79 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1292 ✗ print("-- Result: " + Equation.pointerToString(new_eqn) + "\n");
1293 end if;
1294 then ();
1295 end match;
1296 end inlineCatCallLiterals;
1297
1298 function bumpShift
1299 input output Expression shift;
1300 input Expression new_size;
1301 algorithm
1302 shift := match(shift, new_size)
1303 local
1304 Integer value;
1305 Expression arg;
1306 list<Expression> args;
1307 // two integers, just add them
1308 548 case (Expression.INTEGER(), Expression.INTEGER()) then Expression.INTEGER(shift.value + new_size.value);
1309 // add integer to multary. first argument of multary is the integer (the algorithm here makes sure of it)
1310 case (Expression.MULTARY(arguments = Expression.INTEGER(value) :: args), Expression.INTEGER())
1311 guard(Operator.getMathClassification(shift.operator) == NFOperator.MathClassification.ADDITION) algorithm
1312 ✗ shift.arguments := Expression.INTEGER(value + new_size.value) :: args;
1313 then shift;
1314 // add anything to multary. make sure the first argument is untouched
1315 case (Expression.MULTARY(arguments = arg :: args), _)
1316 guard(Operator.getMathClassification(shift.operator) == NFOperator.MathClassification.ADDITION) algorithm
1317 ✗ shift.arguments := arg :: new_size :: args;
1318 then shift;
1319 // add anything else, just create multary
1320 ✗ else Expression.MULTARY({shift, new_size}, {}, Operator.makeAdd(Type.INTEGER()));
1321 end match;
1322 end bumpShift;
1323
1324 function createInlinedEquation
1325 "used for inlining record, tuple and array equations.
1326 tries to create new equation from lhs and rhs and applying
1327 the inlining methods on the results"
1328 input output list<Pointer<Equation>> eqns;
1329 input Expression lhs;
1330 input Expression rhs;
1331 input EquationAttributes attr;
1332 input Iterator iter;
1333 input VariablePointers variables;
1334 input UnorderedSet<VariablePointer> set "new iterators";
1335 input Pointer<Integer> index;
1336 protected
1337 Pointer<list<Pointer<Equation>>> tmp_eqns = Pointer.create({});
1338 Equation inlined;
1339 Pointer<Equation> new_eqn;
1340 algorithm
1341 818 new_eqn := Equation.makeAssignment(lhs, rhs, index, NBEquation.SIMULATION_STR, iter, attr);
1342 818 inlined := inlineRecordTupleArrayEquation(Pointer.access(new_eqn), iter, variables, tmp_eqns, set, index, false);
1343 eqns := match inlined
1344 126 case Equation.DUMMY_EQUATION() then listAppend(eqns, Pointer.access(tmp_eqns));
1345 else algorithm
1346
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 692 times.
692 if Flags.isSet(Flags.DUMPBACKENDINLINE) then
1347 ✗ print("-- Result: " + Equation.toString(inlined) + "\n");
1348 end if;
1349 then new_eqn :: eqns;
1350 end match;
1351 end createInlinedEquation;
1352
1353 function inlineRecordConstructorExp
1354 "inlines record constructors in a single expression"
1355 input output Expression exp;
1356 input Integer index;
1357 input VariablePointers variables;
1358 algorithm
1359 384 exp := Expression.nthRecordElement(index, exp);
1360 // lower indexed record constructor elements
1361 384 exp := Expression.map(exp, inlineRecordConstructorElements);
1362 // lower the new component references of record attributes
1363 384 exp := Expression.map(exp, function BackendDAE.lowerComponentReferenceExp(variables = variables, complete = true));
1364 end inlineRecordConstructorExp;
1365
1366 function inlineRecordConstructorElements
1367 "removes indexed constructor element calls
1368 Constructor(a,b,c)[2] --> b"
1369 input output Expression exp;
1370 algorithm
1371 exp := match exp
1372 local
1373 Expression new_exp;
1374 Call call;
1375 Function fn;
1376
1377 case Expression.RECORD_ELEMENT(recordExp = Expression.CALL(call = call as Call.TYPED_CALL(fn = fn))) algorithm
1378
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if Function.isDefaultRecordConstructor(fn) then
1379 ✗ new_exp := listGet(call.arguments, exp.index);
1380 elseif Function.isNonDefaultRecordConstructor(fn) then
1381 // ToDo: this has to be mapped correctly with the body.
1382 // for non default record constructors its not always the
1383 // case that inputs map 1:1 to attributes
1384 56 new_exp := listGet(call.arguments, exp.index);
1385 else
1386 new_exp := exp;
1387 end if;
1388 then new_exp;
1389
1390 else exp;
1391 end match;
1392 end inlineRecordConstructorElements;
1393
1394 function getElementList
1395 "used for inlining tuple equations
1396 returns the tuple elements of an expression"
1397 input Expression exp;
1398 output list<Expression> elements;
1399 algorithm
1400 elements := match exp
1401 local
1402 Expression sub_exp, elem;
1403
1404 5 case Expression.TUPLE() then exp.elements;
1405
1406 case Expression.TUPLE_ELEMENT(tupleExp = sub_exp as Expression.TUPLE()) algorithm
1407 ✗ if exp.index > listLength(sub_exp.elements) then
1408 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName() + " failed to get subscripted tuple element: " + Expression.toString(exp)});
1409 ✗ fail();
1410 else
1411 ✗ elem := listGet(sub_exp.elements, exp.index);
1412 end if;
1413 then {elem};
1414
1415 else {};
1416 end match;
1417 end getElementList;
1418
1419 function checkInline
1420 "checks if the function should be inlined. three properties are checked:
1421 A. is the inline type in the list of current stages?
1422 B. is it inlineable?
1423 C. if its default inline type: heuristic to check if its reasonable to do so
1424 Inline if (A and B and C). ordered for cheapest checks first"
1425 input Function func;
1426 input list<DAE.InlineType> inline_types;
1427 input UnorderedMap<Function, InlineRating> func_map;
1428 output Boolean b;
1429 protected
1430 DAE.InlineType it = Function.inlineBuiltin(func);
1431 algorithm
1432 // A and B
1433
4/4
✓ Branch 1 taken 744 times.
✓ Branch 2 taken 478 times.
✓ Branch 4 taken 426 times.
✓ Branch 5 taken 318 times.
1222 b := List.contains(inline_types, it, DAEUtil.inlineTypeEqual) and functionInlineable(func);
1434 // C: heuristic check for default
1435
2/2
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 174 times.
426 if b and DAEUtil.inlineTypeEqual(it, DAE.InlineType.DEFAULT_INLINE()) then
1436 174 b := defaultHeuristic(func, func_map);
1437 end if;
1438 end checkInline;
1439
1440 constant Integer HEURISTIC_THRESHOLD = 10;
1441
1442 function defaultHeuristic
1443 "heuristically determines if a function should be inlined.
1444 only apply to functions with inline type default."
1445 input Function fn;
1446 input UnorderedMap<Function, InlineRating> func_map;
1447 output Boolean b;
1448 algorithm
1449 174 b := InlineRating.resolve(InlineRating.fromFunction(fn, func_map)) < HEURISTIC_THRESHOLD;
1450 end defaultHeuristic;
1451
1452 uniontype InlineRating
1453 "used to rate a function by how much it grows when inlining.
1454 collects data about how often the inputs will occur and how much constant bloating inlining would cause."
1455 record INLINE_RATING
1456 "factors for each input with an additional constant overhead."
1457 array<Integer> input_rating;
1458 Integer constant_rating;
1459 end INLINE_RATING;
1460
1461 function toString
1462 input InlineRating ir;
1463 output String str;
1464 algorithm
1465 ✗ str := "{resolved: " + realString(resolve(ir)) + " | input: " + Array.toString(ir.input_rating, intString) + " | constant: " + intString(ir.constant_rating) + "}";
1466 end toString;
1467
1468 function resolve
1469 "resolve the rating to a final single rational number"
1470 input InlineRating ir;
1471 output Real r = sum(v for v in ir.input_rating)/arrayLength(ir.input_rating) + intReal(ir.constant_rating);
1472 end resolve;
1473
1474 function add
1475 "adds the rating of src to dst"
1476 input output InlineRating dst;
1477 input InlineRating src;
1478 algorithm
1479
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 508 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 508 times.
✓ Branch 4 taken 508 times.
✗ Branch 5 not taken.
1524 if arrayLength(dst.input_rating) == arrayLength(src.input_rating) then
1480
1/2
✓ Branch 0 taken 508 times.
✗ Branch 1 not taken.
2170 for i in 1:arrayLength(dst.input_rating) loop
1481 1662 dst.input_rating[i] := dst.input_rating[i] + src.input_rating[i];
1482 end for;
1483 508 dst.constant_rating := dst.constant_rating + src.constant_rating;
1484 else
1485 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed because dst and src input arrays are of different length.\n"
1486 + "dst: " + toString(dst) + "\nsrc: " + toString(src)});
1487 ✗ fail();
1488 end if;
1489 end add;
1490
1491 function multiply
1492 input output InlineRating ir;
1493 input Integer i;
1494 algorithm
1495 ✗ for i in 1:arrayLength(ir.input_rating) loop
1496 ✗ ir.input_rating[i] := i * ir.input_rating[i];
1497 end for;
1498 ✗ ir.constant_rating := i * ir.constant_rating;
1499 end multiply;
1500
1501 function addConst
1502 "bumps the constant cost"
1503 input output InlineRating ir;
1504 algorithm
1505 370 ir.constant_rating := ir.constant_rating + 1;
1506 end addConst;
1507
1508 function addMapped
1509 "adds the rating of src to dst mapping the interfaces correctly."
1510 input output InlineRating dst;
1511 input InlineRating src;
1512 input array<Expression> args;
1513 input UnorderedMap<ComponentRef, InlineRating> local_map;
1514 protected
1515 Pointer<InlineRating> irp = Pointer.create(InlineRating.INLINE_RATING(arrayCreate(arrayLength(dst.input_rating), 0), src.constant_rating));
1516 algorithm
1517
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
76 if arrayLength(src.input_rating) == arrayLength(args) then
1518
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
96 for i in 1:arrayLength(src.input_rating) loop
1519
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 18 times.
58 if src.input_rating[i] <> 0 then
1520 40 Expression.map(args[i], function addMappedExp(i = src.input_rating[i], irp = irp, local_map = local_map));
1521 end if;
1522 end for;
1523 38 dst := add(dst, Pointer.access(irp));
1524 else
1525 ✗ Error.addMessage(Error.INTERNAL_ERROR, {getInstanceName() + " failed because src input array and arguments are of different length.\n"
1526 + "src: " + toString(src) + "\nargs: " + Array.toString(args, Expression.toString)});
1527 ✗ fail();
1528 end if;
1529 end addMapped;
1530
1531 function addMappedExp
1532 "checks if a cref has a rating already and multiplies it by the local bloating"
1533 input output Expression exp;
1534 input Integer i;
1535 input Pointer<InlineRating> irp;
1536 input UnorderedMap<ComponentRef, InlineRating> local_map;
1537 algorithm
1538 () := match exp
1539 local
1540 Option<InlineRating> iro;
1541 case Expression.CREF() algorithm
1542 40 iro := UnorderedMap.get(exp.cref, local_map);
1543
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
40 if isSome(iro) then
1544 ✗ Pointer.update(irp, add(Pointer.access(irp), multiply(Util.getOption(iro), i)));
1545 end if;
1546 then ();
1547 else ();
1548 end match;
1549 end addMappedExp;
1550
1551 function fromFunction
1552 "rates a function by analyzing the body and the local variables.
1553 also adds the rating to a map so each function is only rated once."
1554 input Function fn;
1555 input UnorderedMap<Function, InlineRating> func_map;
1556 output InlineRating ir;
1557 protected
1558 Pointer<InlineRating> irp;
1559 InlineRating lir;
1560 Integer idx=1, num_inp = listLength(fn.inputs);
1561 InlineRating tmp;
1562 UnorderedMap<ComponentRef, InlineRating> local_map = UnorderedMap.new<InlineRating>(ComponentRef.hash, ComponentRef.isEqual);
1563 algorithm
1564 // add the trivial input mappings as ratings
1565
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 180 times.
522 for inp in fn.inputs loop
1566 342 tmp := InlineRating.INLINE_RATING(arrayCreate(num_inp, 0), 0);
1567 342 tmp.input_rating[idx] := 1;
1568 342 idx := idx + 1;
1569 342 UnorderedMap.add(ComponentRef.fromNode(inp, InstNode.getType(inp)), tmp, local_map);
1570 end for;
1571
1572 // add the local variable ratings
1573
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 180 times.
186 for loc in fn.locals loop
1574 6 irp := Pointer.create(InlineRating.INLINE_RATING(arrayCreate(num_inp, 0), 0));
1575 lir := match InstNode.getBindingExpOpt(loc)
1576 local
1577 Expression bind;
1578 case SOME(bind) algorithm
1579 6 Expression.fakeMap(bind, function rateExpression(func_map = func_map, local_map = local_map, irp = irp));
1580 6 then Pointer.access(irp);
1581 ✗ else Pointer.access(irp);
1582 end match;
1583 6 UnorderedMap.add(ComponentRef.fromNode(loc, InstNode.getType(loc)), lir, local_map);
1584 end for;
1585
1586 // apply rating mapping to body
1587 180 irp := Pointer.create(InlineRating.INLINE_RATING(arrayCreate(num_inp, 0), 0));
1588
1589 180 Expression.fakeMap(Function.getSingleBodyExp(fn), function rateExpression(func_map = func_map, local_map = local_map, irp = irp));
1590 180 ir := Pointer.access(irp);
1591
1592 // also add rating to the map so it will not be rated again
1593 180 UnorderedMap.add(fn, ir, func_map);
1594 end fromFunction;
1595
1596 function rateExpression
1597 "rates an expression and updates the rating pointer.
1598 functions are rated with the function map, component references with the local map.
1599 Furthermore, constants are counted."
1600 input output Expression exp;
1601 input UnorderedMap<Function, InlineRating> func_map;
1602 input UnorderedMap<ComponentRef, InlineRating> local_map;
1603 input Pointer<InlineRating> irp;
1604 protected
1605 Boolean cont;
1606 algorithm
1607
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 1322 times.
1546 if Expression.isLiteral(exp) then
1608 // just count literals as constants
1609 224 Pointer.update(irp, addConst(Pointer.access(irp)));
1610 else
1611 cont := match exp
1612 local
1613 Function fn;
1614 Option<InlineRating> lir;
1615
1616 // check if the call already has a rating. if not only rate inline type default functions
1617 // no-inline has no bloating (traverse the args so just put NONE())
1618 // if inlined assumed to not scale as well (traverse the args so just put NONE())
1619 case Expression.CALL() guard(functionInlineable(Call.typedFunction(exp.call))) algorithm
1620 84 fn := Call.typedFunction(exp.call);
1621 84 lir := UnorderedMap.get(fn, func_map);
1622
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 32 times.
84 if isSome(lir) then
1623 // add the found rating to the overall rating
1624 32 Pointer.update(irp, addMapped(Pointer.access(irp), Util.getOption(lir), listArray(Call.arguments(exp.call)), local_map));
1625 cont := false;
1626 elseif DAEUtil.inlineTypeEqual(Function.inlineBuiltin(fn), DAE.InlineType.DEFAULT_INLINE()) then
1627 // determine rating and add it to the overall rating
1628 6 Pointer.update(irp, addMapped(Pointer.access(irp), fromFunction(fn, func_map), listArray(Call.arguments(exp.call)), local_map));
1629 cont := false;
1630 else
1631 cont := true;
1632 end if;
1633 then cont;
1634
1635 // check if the cref has a rating, otherwise count as constant
1636 case Expression.CREF() then match UnorderedMap.get(ComponentRef.stripSubscriptsAll(exp.cref), local_map)
1637 case lir as SOME(_) algorithm
1638 470 Pointer.update(irp, add(Pointer.access(irp), Util.getOption(lir)));
1639 then false;
1640 else algorithm
1641 146 Pointer.update(irp, addConst(Pointer.access(irp)));
1642 then false;
1643 end match;
1644
1645 // no relevant case, traverse deeper later
1646 else true;
1647 end match;
1648
1649 if cont then
1650 // traverse deeper if no rating was found here
1651 668 exp := Expression.mapShallow(exp, function rateExpression(func_map = func_map, local_map = local_map, irp = irp));
1652 end if;
1653 end if;
1654 end rateExpression;
1655 end InlineRating;
1656
1657 annotation(__OpenModelica_Interface="nbackend");
1658 end NBInline;
1659