From 6e92c1b6c1cdea002c61627cddb9edd8b508f40a Mon Sep 17 00:00:00 2001 From: perost Date: Tue, 1 Feb 2022 16:18:59 +0100 Subject: [PATCH] Implement automatic component merging (#8490) - Implemented automatic merging of similar components into arrays, enabled by the flag `-d=mergeComponents`. --- OMCompiler/Compiler/FrontEnd/AbsynUtil.mo | 111 ++++ OMCompiler/Compiler/FrontEnd/SCodeUtil.mo | 428 +++++++++++++ OMCompiler/Compiler/NFFrontEnd/NFInst.mo | 1 + OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo | 568 ++++++++++++++++++ OMCompiler/Compiler/Util/Flags.mo | 2 + OMCompiler/Compiler/Util/FlagsUtil.mo | 3 +- OMCompiler/Compiler/Util/UnorderedMap.mo | 30 + OMCompiler/Compiler/Util/Util.mo | 36 ++ .../flattening/modelica/scodeinst/Makefile | 6 + .../modelica/scodeinst/MergeComponents1.mo | 25 + .../modelica/scodeinst/MergeComponents2.mo | 25 + .../modelica/scodeinst/MergeComponents3.mo | 28 + .../modelica/scodeinst/MergeComponents4.mo | 33 + .../modelica/scodeinst/MergeComponents5.mo | 25 + .../modelica/scodeinst/MergeComponents6.mo | 26 + 15 files changed, 1346 insertions(+), 1 deletion(-) create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents1.mo create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents2.mo create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents3.mo create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents4.mo create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents5.mo create mode 100644 testsuite/flattening/modelica/scodeinst/MergeComponents6.mo diff --git a/OMCompiler/Compiler/FrontEnd/AbsynUtil.mo b/OMCompiler/Compiler/FrontEnd/AbsynUtil.mo index 63796cfd069..df928de4337 100644 --- a/OMCompiler/Compiler/FrontEnd/AbsynUtil.mo +++ b/OMCompiler/Compiler/FrontEnd/AbsynUtil.mo @@ -1621,6 +1621,21 @@ algorithm end match; end addSubscriptsLast; +public function crefReplaceFirst + "Replaces the first part of a cref with another cref." + input Absyn.ComponentRef cref; + input Absyn.ComponentRef replacement; + output Absyn.ComponentRef outCref; +algorithm + outCref := match cref + case Absyn.ComponentRef.CREF_IDENT() then replacement; + case Absyn.ComponentRef.CREF_QUAL() + then joinCrefs(replacement, crefStripFirst(cref)); + case Absyn.ComponentRef.CREF_FULLYQUALIFIED() + then Absyn.ComponentRef.CREF_FULLYQUALIFIED(crefReplaceFirst(cref.componentRef, replacement)); + end match; +end crefReplaceFirst; + public function crefReplaceFirstIdent " Replaces the first part of a cref with a replacement path: (a[4].b.c[3], d.e) => d.e[4].b.c[3] @@ -2273,6 +2288,18 @@ algorithm end match; end crefIsQual; +public function crefFirstSubs + "Returns the subscripts on the first part of an Absyn.ComponentRef." + input Absyn.ComponentRef cref; + output list subscripts; +algorithm + subscripts := match cref + case Absyn.ComponentRef.CREF_IDENT() then cref.subscripts; + case Absyn.ComponentRef.CREF_QUAL() then cref.subscripts; + case Absyn.ComponentRef.CREF_FULLYQUALIFIED() then crefFirstSubs(cref.componentRef); + end match; +end crefFirstSubs; + public function crefLastSubs "Return the last subscripts of an Absyn.ComponentRef" input Absyn.ComponentRef cref; output list subscripts; @@ -2284,6 +2311,32 @@ algorithm end match; end crefLastSubs; +public function crefSetFirstSubs + "Sets the subscripts of the first part of an Absyn.ComponentRef." + input output Absyn.ComponentRef cref; + input list subscripts; +algorithm + () := match cref + case Absyn.ComponentRef.CREF_IDENT() + algorithm + cref.subscripts := subscripts; + then + (); + + case Absyn.ComponentRef.CREF_QUAL() + algorithm + cref.subscripts := subscripts; + then + (); + + case Absyn.ComponentRef.CREF_FULLYQUALIFIED() + algorithm + cref.componentRef := crefSetFirstSubs(cref.componentRef, subscripts); + then + (); + end match; +end crefSetFirstSubs; + public function crefSetLastSubs input output Absyn.ComponentRef cref; input list inSubscripts; @@ -4536,6 +4589,13 @@ algorithm outSubscript := Absyn.SUBSCRIPT(inExp); end makeSubscript; +public function makeIntegerSubscript + input Integer n; + output Absyn.Subscript sub; +algorithm + sub := Absyn.SUBSCRIPT(Absyn.INTEGER(n)); +end makeIntegerSubscript; + public function crefExplode "Splits a cref into parts." input Absyn.ComponentRef inCref; @@ -5820,5 +5880,56 @@ algorithm end match; end createChoiceArray; +function mapCrefExps + "Applies a function to the expressions in an Absyn.ComponentRef, i.e. in its subscripts." + input output Absyn.ComponentRef cref; + input Func func; + + partial function Func + input output Absyn.Exp exp; + end Func; +algorithm + () := match cref + case Absyn.ComponentRef.CREF_IDENT() + algorithm + cref.subscripts := list(mapSubscriptExp(s, func) for s in cref.subscripts); + then + (); + + case Absyn.ComponentRef.CREF_QUAL() + algorithm + cref.subscripts := list(mapSubscriptExp(s, func) for s in cref.subscripts); + then + (); + + case Absyn.ComponentRef.CREF_FULLYQUALIFIED() + algorithm + cref.componentRef := mapCrefExps(cref.componentRef, func); + then + (); + + else (); + end match; +end mapCrefExps; + +function mapSubscriptExp + input output Absyn.Subscript sub; + input Func func; + + partial function Func + input output Absyn.Exp exp; + end Func; +algorithm + () := match sub + case Absyn.Subscript.SUBSCRIPT() + algorithm + sub.subscript := func(sub.subscript); + then + (); + + else (); + end match; +end mapSubscriptExp; + annotation(__OpenModelica_Interface="frontend"); end AbsynUtil; diff --git a/OMCompiler/Compiler/FrontEnd/SCodeUtil.mo b/OMCompiler/Compiler/FrontEnd/SCodeUtil.mo index 418eb6cdeb4..32245bae2cb 100644 --- a/OMCompiler/Compiler/FrontEnd/SCodeUtil.mo +++ b/OMCompiler/Compiler/FrontEnd/SCodeUtil.mo @@ -5721,5 +5721,433 @@ algorithm end match; end classDefHasSections; +function mapElements + "Applies a function to all elements in a list of elements, and recursively to + all elements in those elements." + input output list elements; + input Func func; + + partial function Func + input output SCode.Element element; + end Func; +algorithm + elements := list(mapElement(e, func) for e in elements); +end mapElements; + +function mapElement + input output SCode.Element element; + input Func func; + + partial function Func + input output SCode.Element element; + end Func; +protected + SCode.ClassDef def; +algorithm + () := match element + case SCode.Element.CLASS() + algorithm + def := mapElementsClassDef(element.classDef, func); + + if not referenceEq(def, element.classDef) then + element.classDef := def; + end if; + then + (); + + else (); + end match; + + element := func(element); +end mapElement; + +function mapElementsClassDef + input output SCode.ClassDef classDef; + input Func func; + + partial function Func + input output SCode.Element element; + end Func; +protected + SCode.ClassDef def; +algorithm + () := match classDef + case SCode.ClassDef.PARTS() + algorithm + classDef.elementLst := list(mapElement(e, func) for e in classDef.elementLst); + then + (); + + case SCode.ClassDef.CLASS_EXTENDS() + algorithm + def := mapElementsClassDef(classDef.composition, func); + + if not referenceEq(def, classDef.composition) then + classDef.composition := def; + end if; + then + (); + + else (); + end match; +end mapElementsClassDef; + +function mapEquationsList + "Applies a function to all equations in a list of equations, and recursively + to all equations in those equations." + input output list eql; + input Func func; + + partial function Func + input output SCode.EEquation eq; + end Func; +algorithm + eql := list(mapEquations(e, func) for e in eql); +end mapEquationsList; + +function mapEquations + input output SCode.Equation eq; + input Func func; + + partial function Func + input output SCode.EEquation eq; + end Func; +algorithm + eq.eEquation := mapEEquations(eq.eEquation, func); +end mapEquations; + +function mapEEquationsList + input output list eql; + input Func func; + + partial function Func + input output SCode.EEquation eq; + end Func; +algorithm + eql := list(mapEEquations(e, func) for e in eql); +end mapEEquationsList; + +function mapEEquations + input output SCode.EEquation eq; + input Func func; + + partial function Func + input output SCode.EEquation eq; + end Func; +algorithm + () := match eq + case SCode.EEquation.EQ_IF() + algorithm + eq.thenBranch := list(mapEEquationsList(b, func) for b in eq.thenBranch); + eq.elseBranch := mapEEquationsList(eq.elseBranch, func); + then + (); + + case SCode.EEquation.EQ_FOR() + algorithm + eq.eEquationLst := mapEEquationsList(eq.eEquationLst, func); + then + (); + + case SCode.EEquation.EQ_WHEN() + algorithm + eq.eEquationLst := mapEEquationsList(eq.eEquationLst, func); + eq.elseBranches := list( + (Util.tuple21(b), mapEEquationsList(Util.tuple22(b), func)) for b in eq.elseBranches); + then + (); + + else (); + end match; + + eq := func(eq); +end mapEEquations; + +function mapEquationExps + "Applies a function to all expressions in an equation." + input output SCode.Equation eq; + input Func func; + + partial function Func + input output Absyn.Exp exp; + end Func; +algorithm + eq.eEquation := mapEEquationExps(eq.eEquation, func); +end mapEquationExps; + +function mapEEquationExps + input output SCode.EEquation eq; + input Func func; + + partial function Func + input output Absyn.Exp exp; + end Func; +algorithm + () := match eq + case SCode.EEquation.EQ_IF() + algorithm + eq.condition := list(func(e) for e in eq.condition); + then + (); + + case SCode.EEquation.EQ_EQUALS() + algorithm + eq.expLeft := func(eq.expLeft); + eq.expRight := func(eq.expRight); + then + (); + + case SCode.EEquation.EQ_PDE() + algorithm + eq.expLeft := func(eq.expLeft); + eq.expRight := func(eq.expRight); + eq.domain := AbsynUtil.mapCrefExps(eq.domain, func); + then + (); + + case SCode.EEquation.EQ_CONNECT() + algorithm + eq.crefLeft := AbsynUtil.mapCrefExps(eq.crefLeft, func); + eq.crefRight := AbsynUtil.mapCrefExps(eq.crefRight, func); + then + (); + + case SCode.EEquation.EQ_FOR() + algorithm + if isSome(eq.range) then + eq.range := SOME(func(Util.getOption(eq.range))); + end if; + then + (); + + case SCode.EEquation.EQ_WHEN() + algorithm + eq.condition := func(eq.condition); + eq.elseBranches := list(Util.applyTuple21(b, func) for b in eq.elseBranches); + then + (); + + case SCode.EEquation.EQ_ASSERT() + algorithm + eq.condition := func(eq.condition); + eq.message := func(eq.message); + eq.level := func(eq.level); + then + (); + + case SCode.EEquation.EQ_TERMINATE() + algorithm + eq.message := func(eq.message); + then + (); + + case SCode.EEquation.EQ_REINIT() + algorithm + eq.cref := func(eq.cref); + eq.expReinit := func(eq.expReinit); + then + (); + + case SCode.EEquation.EQ_NORETCALL() + algorithm + eq.exp := func(eq.exp); + then + (); + + end match; +end mapEEquationExps; + +function mapAlgorithmStatements + "Applies a function to all statements in algorithm section, and recursively + to all statements in those statements." + input output SCode.AlgorithmSection alg; + input Func func; + + partial function Func + input output SCode.Statement stmt; + end Func; +algorithm + alg.statements := mapStatementsList(alg.statements, func); +end mapAlgorithmStatements; + +function mapStatementsList + input output list statements; + input Func func; + + partial function Func + input output SCode.Statement stmt; + end Func; +algorithm + statements := list(mapStatements(s, func) for s in statements); +end mapStatementsList; + +function mapStatements + input output SCode.Statement stmt; + input Func func; + + partial function Func + input output SCode.Statement stmt; + end Func; +algorithm + () := match stmt + case SCode.Statement.ALG_IF() + algorithm + stmt.trueBranch := mapStatementsList(stmt.trueBranch, func); + stmt.elseIfBranch := + list((Util.tuple21(b), mapStatementsList(Util.tuple22(b), func)) for b in stmt.elseIfBranch); + stmt.elseBranch := mapStatementsList(stmt.elseBranch, func); + then + (); + + case SCode.Statement.ALG_FOR() + algorithm + stmt.forBody := mapStatementsList(stmt.forBody, func); + then + (); + + case SCode.Statement.ALG_PARFOR() + algorithm + stmt.parforBody := mapStatementsList(stmt.parforBody, func); + then + (); + + case SCode.Statement.ALG_WHILE() + algorithm + stmt.whileBody := mapStatementsList(stmt.whileBody, func); + then + (); + + case SCode.Statement.ALG_WHEN_A() + algorithm + stmt.branches := + list((Util.tuple21(b), mapStatementsList(Util.tuple22(b), func)) for b in stmt.branches); + then + (); + + case SCode.Statement.ALG_FAILURE() + algorithm + stmt.stmts := mapStatementsList(stmt.stmts, func); + then + (); + + case SCode.Statement.ALG_TRY() + algorithm + stmt.body := mapStatementsList(stmt.body, func); + stmt.elseBody := mapStatementsList(stmt.body, func); + then + (); + + else (); + end match; + + stmt := func(stmt); +end mapStatements; + +function mapStatementExps + "Applies a function to all expressions in a statement." + input output SCode.Statement stmt; + input Func func; + + partial function Func + input output Absyn.Exp exp; + end Func; +algorithm + () := match stmt + case SCode.Statement.ALG_ASSIGN() + algorithm + stmt.assignComponent := func(stmt.assignComponent); + stmt.value := func(stmt.value); + then + (); + + case SCode.Statement.ALG_IF() + algorithm + stmt.boolExpr := func(stmt.boolExpr); + stmt.elseIfBranch := list((func(Util.tuple21(b)), Util.tuple22(b)) for b in stmt.elseIfBranch); + then + (); + + case SCode.Statement.ALG_FOR() + algorithm + if isSome(stmt.range) then + stmt.range := SOME(func(Util.getOption(stmt.range))); + end if; + then + (); + + case SCode.Statement.ALG_PARFOR() + algorithm + if isSome(stmt.range) then + stmt.range := SOME(func(Util.getOption(stmt.range))); + end if; + then + (); + + case SCode.Statement.ALG_WHILE() + algorithm + stmt.boolExpr := func(stmt.boolExpr); + then + (); + + case SCode.Statement.ALG_WHEN_A() + algorithm + stmt.branches := list((func(Util.tuple21(b)), Util.tuple22(b)) for b in stmt.branches); + then + (); + + case SCode.Statement.ALG_ASSERT() + algorithm + stmt.condition := func(stmt.condition); + stmt.message := func(stmt.message); + stmt.level := func(stmt.level); + then + (); + + case SCode.Statement.ALG_TERMINATE() + algorithm + stmt.message := func(stmt.message); + then + (); + + case SCode.Statement.ALG_REINIT() + algorithm + stmt.cref := func(stmt.cref); + stmt.newValue := func(stmt.newValue); + then + (); + + case SCode.Statement.ALG_NORETCALL() + algorithm + stmt.exp := func(stmt.exp); + then + (); + + else (); + end match; +end mapStatementExps; + +function lookupModInMod + "Looks up a modifier with the given name in the given modifier, or returns + NOMOD() if no modifier is found." + input String name; + input SCode.Mod mod; + output SCode.Mod outMod; +algorithm + outMod := match mod + case SCode.Mod.MOD() + algorithm + for m in mod.subModLst loop + if m.ident == name then + outMod := m.mod; + return; + end if; + end for; + then + SCode.Mod.NOMOD(); + + else SCode.Mod.NOMOD(); + end match; +end lookupModInMod; + annotation(__OpenModelica_Interface="frontend"); end SCodeUtil; diff --git a/OMCompiler/Compiler/NFFrontEnd/NFInst.mo b/OMCompiler/Compiler/NFFrontEnd/NFInst.mo index 897c04cf33a..0fa0cecd086 100644 --- a/OMCompiler/Compiler/NFFrontEnd/NFInst.mo +++ b/OMCompiler/Compiler/NFFrontEnd/NFInst.mo @@ -145,6 +145,7 @@ algorithm // Look up the class to instantiate and mark it as the root class. cls := Lookup.lookupClassName(classPath, top, NFInstContext.RELAXED, AbsynUtil.dummyInfo, checkAccessViolations = false); + cls := InstUtil.mergeScalars(cls); checkInstanceRestriction(cls, classPath, context); cls := InstNode.setNodeType(InstNodeType.ROOT_CLASS(InstNode.EMPTY_NODE()), cls); diff --git a/OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo b/OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo index ca9acefd6b2..4a81d274f7a 100644 --- a/OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo +++ b/OMCompiler/Compiler/NFFrontEnd/NFInstUtil.mo @@ -43,9 +43,18 @@ encapsulated package NFInstUtil import Algorithm = NFAlgorithm; import Statement = NFStatement; import Equation = NFEquation; + import SCode; + import Absyn; protected + import AbsynUtil; + import SCodeUtil; + import Dump; import Flags; + import UnorderedMap; + import MetaModelica.Dangerous.listReverseInPlace; + import SCodeDump; + import ExecStat.execStat; public function dumpFlatModelDebug @@ -290,5 +299,564 @@ public fn := Function.mapBody(fn, expandSlicedCrefsAlg); end expandSlicedCrefsFunction; + function mergeScalars + "Tries to merge components inside the given class into arrays. Components + can be merged if they have e.g. the same type, same prefixes, same + modifiers, etc." + input output InstNode node; + protected + SCode.Element elem; + algorithm + if not Flags.isSet(Flags.MERGE_COMPONENTS) then + return; + end if; + + elem := InstNode.definition(node); + elem := mergeScalars2(elem); + node := InstNode.setDefinition(elem, node); + execStat(getInstanceName()); + end mergeScalars; + + function mergeScalars2 + "Helper function to mergeScalars, does the actual merging." + input output SCode.Element cls; + protected + SCode.ClassDef cdef; + list elems; + UnorderedMap name_map; + algorithm + () := match cls + case SCode.Element.CLASS(classDef = cdef as SCode.ClassDef.PARTS()) + algorithm + // Merge components. + (elems, name_map) := mergeScalars3(cdef.elementLst); + cdef.elementLst := elems; + // Replace references to merged components with their new names. + cdef.normalEquationLst := mergeScalarsEql(cdef.normalEquationLst, name_map); + cdef.initialEquationLst := mergeScalarsEql(cdef.initialEquationLst, name_map); + cdef.normalAlgorithmLst := mergeScalarsAlgs(cdef.normalAlgorithmLst, name_map); + cdef.initialAlgorithmLst := mergeScalarsAlgs(cdef.initialAlgorithmLst, name_map); + cls.classDef := cdef; + then + (); + + else (); + end match; + end mergeScalars2; + + function mergeScalars3 + "Helper function to mergeScalars2. Takes a list of elements and returns a + new list with components merged, as well as a map of the merged components' + old names to their new names." + input list elements; + output list outElements; + output UnorderedMap outNameMap; + protected + list> mergeable; + SCode.Element merged_e; + Integer i = 1; + Absyn.TypeSpec ty; + String prefix; + algorithm + // Find the groups of mergeable component. + (mergeable, outElements) := makeMergeMap(elements); + outNameMap := UnorderedMap.new(stringHashDjb2Mod, stringEq); + + // Merge each group of mergeable components. + for el in mergeable loop + // The name of the merged component will be $LastIdentInTypeOfComponent + + // an index to ensure the name is unique. + ty := SCodeUtil.getComponentTypeSpec(listHead(el)); + prefix := "$" + AbsynUtil.pathLastIdent(AbsynUtil.typeSpecPath(ty)); + merged_e := mergeComponents(el, prefix + String(i), outNameMap); + i := i + 1; + outElements := merged_e :: outElements; + end for; + + outElements := listReverseInPlace(outElements); + end mergeScalars3; + + function makeMergeMap + "Takes a list of elements and returns a list of mergeable component groups + and a list of all other unmergeable elements." + input list elements; + output list> mergeable = {}; + output list unmergeable = {}; + protected + type ElementList = list; + UnorderedMap merge_map; + list> grouped_elems; + + function append_merge + input Option> oldValue; + input SCode.Element elem; + output list newValue; + algorithm + if isSome(oldValue) then + SOME(newValue) := oldValue; + else + newValue := {}; + end if; + + newValue := elem :: newValue; + end append_merge; + algorithm + merge_map := UnorderedMap.new(stringHashDjb2Mod, stringEq); + + // Group the components by their signature if they fulfill the requirements + // for being considered mergeable. + for e in elements loop + () := match e + case SCode.Element.COMPONENT() + guard isMergeableComponent(e) + algorithm + UnorderedMap.addUpdate(getComponentSignature(e), + function append_merge(elem = e), merge_map); + then + (); + + else + algorithm + unmergeable := e :: unmergeable; + then + (); + end match; + end for; + + grouped_elems := UnorderedMap.valueList(merge_map); + + // Move single mergeable components to the list of unmergeables. + for el in grouped_elems loop + if listLength(el) == 1 then + unmergeable := listHead(el) :: unmergeable; + else + mergeable := listReverseInPlace(el) :: mergeable; + end if; + end for; + end makeMergeMap; + + function isMergeableComponent + "Returns true if an element is a component that is considered to be + mergeable, otherwise false. A component is considered to be not mergeable + if it is e.g. a redeclare or inner/outer." + input SCode.Element element; + output Boolean isMergeable; + algorithm + isMergeable := match element + case SCode.Element.COMPONENT(attributes = SCode.Attributes.ATTR(arrayDims = {}), + prefixes = SCode.Prefixes.PREFIXES( + redeclarePrefix = SCode.Redeclare.NOT_REDECLARE(), + innerOuter = Absyn.InnerOuter.NOT_INNER_OUTER(), + replaceablePrefix = SCode.Replaceable.NOT_REPLACEABLE()), + condition = NONE()) + then isMergeableType(element.typeSpec) and isMergeableMod(element.modifications); + + else false; + end match; + end isMergeableComponent; + + function isMergeableMod + input SCode.Mod mod; + output Boolean mergeable; + algorithm + mergeable := match mod + case SCode.MOD(eachPrefix = SCode.Each.NOT_EACH()) + algorithm + for m in mod.subModLst loop + if not isMergeableMod(m.mod) then + mergeable := false; + return; + end if; + end for; + then + true; + + case SCode.NOMOD() then true; + else false; + end match; + end isMergeableMod; + + function isMergeableType + input Absyn.TypeSpec ty; + output Boolean mergeable; + algorithm + mergeable := match ty + case Absyn.TPATH(arrayDim = NONE()) then true; + else false; + end match; + end isMergeableType; + + function getComponentSignature + "Creates a signature string for a component which consists of the components + attributes, type, and modifiers, that can be used to group similar + components together." + input SCode.Element element; + output String signature; + protected + SCode.Prefixes prefs; + SCode.Attributes attrs; + Absyn.TypeSpec ty; + SCode.Mod mod; + algorithm + SCode.Element.COMPONENT(prefixes = prefs, attributes = attrs, typeSpec = ty, + modifications = mod) := element; + + signature := stringAppendList({ + SCodeDump.visibilityStr(prefs.visibility), + SCodeDump.finalStr(prefs.finalPrefix), + SCodeDump.connectorTypeStr(attrs.connectorType), + SCodeDump.variabilityString(attrs.variability), + Dump.unparseDirectionSymbolStr(attrs.direction), + Dump.unparseTypeSpec(ty), + getModSignature(mod) + }); + end getComponentSignature; + + function getModSignature + "Creates a signature string for a modifier that can be hashed in order to + group similar modifiers. The signature is similar to the string + representation of the modifier with the binding expressions removed, e.g.: + + (x=3, y(start=1)=4, m(a=1)) => '(m(a=,),x=,y(start=,)=,)' + + Submodifiers are also sorted by their names, so (x=3, y=4) and (y=4, x=3) + both have the signature '(x=,y=,)'." + input SCode.Mod mod; + input String name = ""; + output String signature; + protected + function sub_mod_lt + input SCode.SubMod m1; + input SCode.SubMod m2; + output Boolean res = m1.ident < m2.ident; + end sub_mod_lt; + + list strl = {}; + Boolean has_binding, has_submods; + algorithm + signature := match mod + case SCode.Mod.MOD() + algorithm + has_binding := isSome(mod.binding); + has_submods := not listEmpty(mod.subModLst); + + if has_binding then + strl := "=" :: strl; + end if; + + if has_submods then + strl := ")" :: strl; + + for m in List.sort(mod.subModLst, sub_mod_lt) loop + strl := "," :: strl; + strl := getModSignature(m.mod, m.ident) :: strl; + end for; + + strl := "(" :: strl; + end if; + + if has_binding or has_submods then + strl := name :: strl; + end if; + + if SCodeUtil.finalBool(mod.finalPrefix) then + strl := "final " :: strl; + end if; + + if SCodeUtil.eachBool(mod.eachPrefix) then + strl := "each " :: strl; + end if; + then + stringAppendList(strl); + + else ""; + end match; + end getModSignature; + + function mergeComponents + "Merges a list of components into a single component." + input list components; + input String prefix; + input UnorderedMap nameMap; + output SCode.Element mergedComponent; + protected + Absyn.TypeSpec ty; + SCode.Prefixes prefs; + SCode.Attributes attrs; + SCode.Mod mod; + Integer i = 1; + String name; + Absyn.ComponentRef cref; + list mods; + algorithm + // All components should have the same type and attributes, so take them + // from the first component in the list. + SCode.Element.COMPONENT(typeSpec = ty, + prefixes = prefs, + attributes = attrs) := listHead(components); + + // Add a dimension equal to the number of components. + attrs.arrayDims := {AbsynUtil.makeIntegerSubscript(listLength(components))}; + + // Merge the modifiers into a single modifier. + mods := list(SCodeUtil.componentMod(c) for c in components); + mod := mergeMods(mods); + + mergedComponent := SCode.Element.COMPONENT( + prefix, + prefs, + attrs, + ty, + mod, + SCode.noComment, + NONE(), + AbsynUtil.dummyInfo + ); + + // Add a mapping from old name to new name for each of the merged components. + for c in components loop + SCode.Element.COMPONENT(name = name) := c; + cref := Absyn.ComponentRef.CREF_IDENT(prefix, {AbsynUtil.makeIntegerSubscript(i)}); + i := i + 1; + UnorderedMap.addUnique(name, cref, nameMap); + end for; + end mergeComponents; + + function mergeMods + "Merges a list of modifiers into one modifier. All modifiers are assumed to + be equal with exception for binding expressions." + input list mods; + output SCode.Mod mod; + protected + list names; + list> bindings; + UnorderedMap binding_map; + algorithm + if listEmpty(mods) then + mod := SCode.Mod.NOMOD(); + return; + end if; + + // Get the paths of all the modified elements. + mod := listHead(mods); + names := getModNames(mod); + bindings := List.fill({}, listLength(names)); + + // Collect the bindings from all modifiers. + for m in listReverse(mods) loop + bindings := getModBindings(m, names, bindings); + end for; + + // Make a name => binding map. + binding_map := UnorderedMap.fromLists(names, + listReverse(Absyn.Exp.ARRAY(b) for b in bindings), + AbsynUtil.pathHashMod, AbsynUtil.pathEqual); + + // Use one the modifiers as a template and replace the bindings in it with + // the merged bindings, in order to preserve 'each' and 'final'. + mod := mergeMods2(mod, binding_map); + end mergeMods; + + function getModNames + "Returns a list of the modified names in a modifier, e.g.: + (x = 4, y(start = 1) = 3, m(a = 2)) => {x, y, y.start, m.a}" + input SCode.Mod mod; + input list name = {}; + input output list names = {}; + algorithm + names := match mod + case SCode.Mod.MOD() + algorithm + if isSome(mod.binding) then + names := AbsynUtil.stringListPathReversed(name) :: names; + end if; + + for m in mod.subModLst loop + names := getModNames(m.mod, m.ident :: name, names); + end for; + then + names; + + else names; + end match; + end getModNames; + + function mergeMods2 + "Helper function to mergeMods, replaces bindings in the given modifier with + the merged bindings." + input output SCode.Mod mod; + input UnorderedMap bindingMap; + input list name = {}; + protected + Absyn.Exp new_binding; + list submods = {}; + algorithm + () := match mod + case SCode.Mod.MOD() + algorithm + // If the modifier has a binding expression, look up the new binding + // in the map and replace it. + if isSome(mod.binding) then + new_binding := UnorderedMap.getOrFail(AbsynUtil.stringListPathReversed(name), bindingMap); + mod.binding := SOME(new_binding); + end if; + + // Recursively do the same to the submodifiers. + if not listEmpty(mod.subModLst) then + for m in mod.subModLst loop + m.mod := mergeMods2(m.mod, bindingMap, m.ident :: name); + submods := m :: submods; + end for; + + mod.subModLst := listReverseInPlace(submods); + end if; + then + (); + + else (); + end match; + end mergeMods2; + + function getModBindings + "Looks up the named bindings in a modifier and appends them to the binding + expression lists." + input SCode.Mod mod; + input list names; + input output list> bindings; + protected + list mod_bindings = {}; + algorithm + for name in names loop + mod_bindings := lookupModBinding(name, mod) :: mod_bindings; + end for; + + bindings := List.threadMap(mod_bindings, bindings, cons); + end getModBindings; + + function lookupModBinding + "Looks up the binding expression for the modifier given by the path." + input Absyn.Path name; + input SCode.Mod mod; + output Absyn.Exp binding; + protected + SCode.Mod m; + algorithm + SCode.Mod.MOD(binding = SOME(binding)) := lookupMod(name, mod); + end lookupModBinding; + + function lookupMod + "Looks up the modifier given by the path." + input Absyn.Path name; + input SCode.Mod mod; + output SCode.Mod outMod; + algorithm + outMod := match name + case Absyn.Path.IDENT() + then SCodeUtil.lookupModInMod(name.name, mod); + + case Absyn.Path.QUALIFIED() + algorithm + outMod := SCodeUtil.lookupModInMod(name.name, mod); + then + lookupMod(name.path, outMod); + end match; + end lookupMod; + + function mergeScalarsEql + "Updates the names of merged components in a list of equations." + input output list eql; + input UnorderedMap nameMap; + algorithm + eql := SCodeUtil.mapEquationsList(eql, function mergeScalarsEq(nameMap = nameMap)); + end mergeScalarsEql; + + function mergeScalarsEq + "Updates the names of merged components in an equation." + input output SCode.EEquation eq; + input UnorderedMap nameMap; + algorithm + eq := SCodeUtil.mapEEquationExps(eq, function mergeScalarsExps(nameMap = nameMap)); + + () := match eq + case SCode.EEquation.EQ_CONNECT() + algorithm + eq.crefLeft := mergeScalarsCref(eq.crefLeft, nameMap); + eq.crefRight := mergeScalarsCref(eq.crefRight, nameMap); + then + (); + + else (); + end match; + end mergeScalarsEq; + + function mergeScalarsExps + "Updates the names of merged components in an expression." + input output Absyn.Exp exp; + input UnorderedMap nameMap; + algorithm + exp := AbsynUtil.traverseExp(exp, mergeScalarsExp, nameMap); + end mergeScalarsExps; + + function mergeScalarsExp + input output Absyn.Exp exp; + input output UnorderedMap nameMap; + algorithm + () := match exp + case Absyn.Exp.CREF() + guard not AbsynUtil.crefIsWild(exp.componentRef) + algorithm + exp.componentRef := mergeScalarsCref(exp.componentRef, nameMap); + then + (); + + else (); + end match; + end mergeScalarsExp; + + function mergeScalarsCref + "Updates the names of a component reference if it refers to a merged component." + input output Absyn.ComponentRef cref; + input UnorderedMap nameMap; + protected + Option repl_ocr; + Absyn.ComponentRef repl_cr; + list subs; + algorithm + // Look up the first part of the cref in the name map. + repl_ocr := UnorderedMap.get(AbsynUtil.crefFirstIdent(cref), nameMap); + + // If a replacement was found... + if isSome(repl_ocr) then + SOME(repl_cr) := repl_ocr; + // The new name has a subscript that needs to be merged with any existing subscripts. + subs := AbsynUtil.crefFirstSubs(cref); + + if not listEmpty(subs) then + subs := listAppend(AbsynUtil.crefFirstSubs(repl_cr), subs); + repl_cr := AbsynUtil.crefSetLastSubs(repl_cr, subs); + end if; + + // Replace the first part of the cref with the new part. + cref := AbsynUtil.crefReplaceFirst(cref, repl_cr); + end if; + end mergeScalarsCref; + + function mergeScalarsAlgs + "Updates the names of merged components in a list of algorithm sections." + input output list algs; + input UnorderedMap nameMap; + algorithm + algs := list(SCodeUtil.mapAlgorithmStatements(a, + function mergeScalarsStmt(nameMap = nameMap)) for a in algs); + end mergeScalarsAlgs; + + function mergeScalarsStmt + "Updates the names of merged components in a statement." + input output SCode.Statement stmt; + input UnorderedMap nameMap; + algorithm + stmt := SCodeUtil.mapStatementExps(stmt, function mergeScalarsExps(nameMap = nameMap)); + end mergeScalarsStmt; + annotation(__OpenModelica_Interface="frontend"); end NFInstUtil; diff --git a/OMCompiler/Compiler/Util/Flags.mo b/OMCompiler/Compiler/Util/Flags.mo index 4f7aa5e21c2..96e63d52bc3 100644 --- a/OMCompiler/Compiler/Util/Flags.mo +++ b/OMCompiler/Compiler/Util/Flags.mo @@ -545,6 +545,8 @@ constant DebugFlag DUMP_CONVERSION_RULES = DEBUG_FLAG(186, "dumpConversionRules" Gettext.gettext("Dumps the rules when converting a package using a conversion script.")); constant DebugFlag PRINT_RECORD_TYPES = DEBUG_FLAG(187, "printRecordTypes", false, Gettext.gettext("Prints out record types as part of the flat code.")); +constant DebugFlag MERGE_COMPONENTS = DEBUG_FLAG(188, "mergeComponents", false, + Gettext.gettext("Enables automatic merging of components into arrays.")); public // CONFIGURATION FLAGS diff --git a/OMCompiler/Compiler/Util/FlagsUtil.mo b/OMCompiler/Compiler/Util/FlagsUtil.mo index f593a48c3a2..b70ad2d6f8a 100644 --- a/OMCompiler/Compiler/Util/FlagsUtil.mo +++ b/OMCompiler/Compiler/Util/FlagsUtil.mo @@ -243,7 +243,8 @@ constant list allDebugFlags = { Flags.COMBINE_SUBSCRIPTS, Flags.ZMQ_LISTEN_TO_ALL, Flags.DUMP_CONVERSION_RULES, - Flags.PRINT_RECORD_TYPES + Flags.PRINT_RECORD_TYPES, + Flags.MERGE_COMPONENTS }; protected diff --git a/OMCompiler/Compiler/Util/UnorderedMap.mo b/OMCompiler/Compiler/Util/UnorderedMap.mo index c29b2bf1fe7..4620e64800f 100644 --- a/OMCompiler/Compiler/Util/UnorderedMap.mo +++ b/OMCompiler/Compiler/Util/UnorderedMap.mo @@ -83,6 +83,36 @@ public ); end new; + function fromLists + "Creates a new map from a list of keys and a corresponding list of values. + Fails if the two lists do not have the same size." + input list keys; + input list values; + input Hash hash; + input KeyEq keyEq; + output UnorderedMap map; + protected + Integer key_count, bucket_count; + V v; + list rest_v = values; + algorithm + key_count := listLength(keys); + bucket_count := Util.nextPrime(key_count); + + map := UNORDERED_MAP( + Vector.newFill(bucket_count, {}), + Vector.new(key_count), + Vector.new(key_count), + hash, + keyEq + ); + + for k in keys loop + v :: rest_v := rest_v; + add(k, v, map); + end for; + end fromLists; + function copy "Returns a copy of the map." input UnorderedMap map; diff --git a/OMCompiler/Compiler/Util/Util.mo b/OMCompiler/Compiler/Util/Util.mo index 19c6c28b209..1db62879404 100644 --- a/OMCompiler/Compiler/Util/Util.mo +++ b/OMCompiler/Compiler/Util/Util.mo @@ -1642,6 +1642,42 @@ algorithm t := System.realtimeTock(ClockIndexes.RT_PROFILER2); end profilertock2; +function applyTuple21 + "Applies a function to the first element of a tuple." + input tuple inTuple; + input FuncT func; + output tuple outTuple; + + partial function FuncT + input output T1 e; + end FuncT; +protected + T1 e1_1, e1_2; + T2 e2; +algorithm + (e1_1, e2) := inTuple; + e1_2 := func(e1_1); + outTuple := if referenceEq(e1_1, e1_2) then inTuple else (e1_2, e2); +end applyTuple21; + +function applyTuple22 + "Applies a function to the second element of a tuple." + input tuple inTuple; + input FuncT func; + output tuple outTuple; + + partial function FuncT + input output T2 e; + end FuncT; +protected + T1 e1; + T2 e2_1, e2_2; +algorithm + (e1, e2_1) := inTuple; + e2_2 := func(e2_1); + outTuple := if referenceEq(e2_1, e2_2) then inTuple else (e1, e2_2); +end applyTuple22; + function applyTuple31 input tuple inTuple; input FuncT func; diff --git a/testsuite/flattening/modelica/scodeinst/Makefile b/testsuite/flattening/modelica/scodeinst/Makefile index 660291780d0..eb5d4fa7a39 100644 --- a/testsuite/flattening/modelica/scodeinst/Makefile +++ b/testsuite/flattening/modelica/scodeinst/Makefile @@ -770,6 +770,12 @@ LookupLibrary1.mo \ loop1.mo \ loop2.mo \ loop3.mo \ +MergeComponents1.mo \ +MergeComponents2.mo \ +MergeComponents3.mo \ +MergeComponents4.mo \ +MergeComponents5.mo \ +MergeComponents6.mo \ MissingRedeclare1.mo \ mod1.mo \ mod10.mo \ diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents1.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents1.mo new file mode 100644 index 00000000000..749e2d5e8f5 --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents1.mo @@ -0,0 +1,25 @@ +// name: MergeComponents1 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; + Real z; +end A; + +model MergeComponents1 + A a1(x = 1, y = 2, z = 3); + A a2(x = 4, y = 5, z = 6); + A a3(x = 7, y = 8, z = 9); +end MergeComponents1; + +// Result: +// class MergeComponents1 +// Real[3] $A1.z = {3.0, 6.0, 9.0}; +// Real[3] $A1.y = {2.0, 5.0, 8.0}; +// Real[3] $A1.x = {1.0, 4.0, 7.0}; +// end MergeComponents1; +// endResult diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents2.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents2.mo new file mode 100644 index 00000000000..3f49b2725ba --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents2.mo @@ -0,0 +1,25 @@ +// name: MergeComponents2 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; + Real z; +end A; + +model MergeComponents2 + A a1(x = 1, y = 2, z = 3); + A a2(z = 4, y = 5, x = 6); + A a3(y = 7, x = 8, z = 9); +end MergeComponents2; + +// Result: +// class MergeComponents2 +// Real[3] $A1.z = {3.0, 4.0, 9.0}; +// Real[3] $A1.y = {2.0, 5.0, 7.0}; +// Real[3] $A1.x = {1.0, 6.0, 8.0}; +// end MergeComponents2; +// endResult diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents3.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents3.mo new file mode 100644 index 00000000000..2dc8526127a --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents3.mo @@ -0,0 +1,28 @@ +// name: MergeComponents3 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; + Real z; +end A; + +model MergeComponents3 + A a1(x = 1, y(start = 5) = 2, z = 3); + A a2(x = 4, y = 5, z = 6); + A a3(x = 7, y(start = 3) = 8, z = 9); +end MergeComponents3; + +// Result: +// class MergeComponents3 +// Real a2.x = 4.0; +// Real a2.y = 5.0; +// Real a2.z = 6.0; +// Real[2] $A1.z = {3.0, 9.0}; +// Real[2] $A1.y(start = {5.0, 3.0}) = {2.0, 8.0}; +// Real[2] $A1.x = {1.0, 7.0}; +// end MergeComponents3; +// endResult diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents4.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents4.mo new file mode 100644 index 00000000000..cc626907a17 --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents4.mo @@ -0,0 +1,33 @@ +// name: MergeComponents4 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; + Real z; +end A; + +model MergeComponents4 + A a1(x = 1, y = 2, z = 3); + A a2(x = 4, y = 5, z = 6); + A a3(x = 7, y = 8, z = 9); +equation + a1.x = a2.y + a3.z; +algorithm + a2.z := a3.x + a1.y; +end MergeComponents4; + +// Result: +// class MergeComponents4 +// Real[3] $A1.z = {3.0, 6.0, 9.0}; +// Real[3] $A1.y = {2.0, 5.0, 8.0}; +// Real[3] $A1.x = {1.0, 4.0, 7.0}; +// equation +// $A1[1].x = $A1[2].y + $A1[3].z; +// algorithm +// $A1[2].z := $A1[3].x + $A1[1].y; +// end MergeComponents4; +// endResult diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents5.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents5.mo new file mode 100644 index 00000000000..cba3406514d --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents5.mo @@ -0,0 +1,25 @@ +// name: MergeComponents5 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; +end A; + +type A3 = A[3](x = {1, 2, 3}); + +model MergeComponents5 + A3 a1(y = {1, 2, 3}); + A3 a2(y = {4, 5, 6}); + A3 a3(y = {7, 8, 9}); +end MergeComponents5; + +// Result: +// class MergeComponents5 +// Real[3, 3] $A31.y = {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}}; +// Real[3, 3] $A31.x = {{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}; +// end MergeComponents5; +// endResult diff --git a/testsuite/flattening/modelica/scodeinst/MergeComponents6.mo b/testsuite/flattening/modelica/scodeinst/MergeComponents6.mo new file mode 100644 index 00000000000..c33c226a8f7 --- /dev/null +++ b/testsuite/flattening/modelica/scodeinst/MergeComponents6.mo @@ -0,0 +1,26 @@ +// name: MergeComponents6 +// keywords: +// status: correct +// cflags: -d=newInst,mergeComponents,-nfScalarize +// + +model A + Real x; + Real y; +end A; + +model MergeComponents6 + A a1(x = 1, y = 2); + parameter A a2(x = 3, y = 4); + A a3(x = 5, y = 6); + parameter A a4(x = 7, y = 8); +end MergeComponents6; + +// Result: +// class MergeComponents6 +// parameter Real[2] $A1.y = {4.0, 8.0}; +// parameter Real[2] $A1.x = {3.0, 7.0}; +// Real[2] $A2.y = {2.0, 6.0}; +// Real[2] $A2.x = {1.0, 5.0}; +// end MergeComponents6; +// endResult