From 0e62fe0548d18d7afbd6b8a02b8871729524790c Mon Sep 17 00:00:00 2001 From: Simon Eriksson Date: Thu, 15 Mar 2018 11:52:44 +0100 Subject: [PATCH] Initial MidCode implementation Co-authored-by: Simon Eriksson Co-authored-by: Patrik Andersson Belonging to [master]: - OpenModelica/OMCompiler#2324 - OpenModelica/OpenModelica-testsuite#903 --- Compiler/MidCode/.gitignore | 4 + Compiler/MidCode/DAEToMid.mo | 1643 +++++++++++++++++++++++ Compiler/MidCode/HashTableMidVar.mo | 99 ++ Compiler/MidCode/MidCode.mo | 269 ++++ Compiler/MidCode/MidToMid.mo | 220 +++ Compiler/Script/CevalScript.mo | 2 + Compiler/SimCode/SimCodeFunction.mo | 31 +- Compiler/Template/CodegenCFunctions.tpl | 16 + Compiler/Template/CodegenMidToC.tpl | 739 ++++++++++ Compiler/Template/Makefile.common | 7 +- Compiler/Template/MidCodeTV.mo | 256 ++++ Compiler/Util/Flags.mo | 2 +- Compiler/boot/LoadCompilerSources.mos | 9 +- 13 files changed, 3288 insertions(+), 9 deletions(-) create mode 100644 Compiler/MidCode/.gitignore create mode 100644 Compiler/MidCode/DAEToMid.mo create mode 100644 Compiler/MidCode/HashTableMidVar.mo create mode 100644 Compiler/MidCode/MidCode.mo create mode 100644 Compiler/MidCode/MidToMid.mo create mode 100644 Compiler/Template/CodegenMidToC.tpl create mode 100644 Compiler/Template/MidCodeTV.mo diff --git a/Compiler/MidCode/.gitignore b/Compiler/MidCode/.gitignore new file mode 100644 index 0000000000..13eb6deb62 --- /dev/null +++ b/Compiler/MidCode/.gitignore @@ -0,0 +1,4 @@ +*.c +*.h +*.libs +*.makefile diff --git a/Compiler/MidCode/DAEToMid.mo b/Compiler/MidCode/DAEToMid.mo new file mode 100644 index 0000000000..aa1e1899b0 --- /dev/null +++ b/Compiler/MidCode/DAEToMid.mo @@ -0,0 +1,1643 @@ +/* + * This file is part of OpenModelica. + * + * Copyright (c) 1998-2018, Open Source Modelica Consortium (OSMC), + * c/o Linköpings universitet, Department of Computer and Information Science, + * SE-58183 Linköping, Sweden. + * + * All rights reserved. + * + * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR + * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2. + * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES + * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, + * ACCORDING TO RECIPIENTS CHOICE. + * + * The OpenModelica software and the Open Source Modelica + * Consortium (OSMC) Public License (OSMC-PL) are obtained + * from OSMC, either from the above address, + * from the URLs: http://www.ida.liu.se/projects/OpenModelica or + * http://www.openmodelica.org, and in the OpenModelica distribution. + * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html. + * + * This program is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH + * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. + * + * See the full OSMC Public License conditions for more details. + * + */ + +encapsulated package DAEToMid + +public +import MidCode; +import SimCodeFunction; + +function DAEFunctionsToMid + input list simfuncs; + output list midfuncs; +algorithm + midfuncs := list(DAEFunctionToMid(simfunc) for simfunc in simfuncs); +end DAEFunctionsToMid; + +protected +import DAE; +import DAEDump; +import MidToMid; +import SimCode; +import Expression; +import ExpressionDump; +import ComponentReference; +import System; +import DoubleEndedList; +import Mutable; +import BaseHashTable; +import HashTableMidVar; +import List; +import Error; + +uniontype State + record STATE + DoubleEndedList locals; + DoubleEndedList localBufs; + DoubleEndedList localBufPtrs; + DoubleEndedList blocks; + DoubleEndedList stmts; + Mutable.Mutable blockid; + Mutable.Mutable> continuejumps; + Mutable.Mutable> breakjumps; + Mutable.Mutable vars; + end STATE; +end State; + +function listZip + "List.threadTuple fails for lists of unequal length + but truncating is the more common semantics." + input list xs; + input list ys; + output list> zs; +protected + list xs_; + list ys_; + X x; + Y y; +algorithm + zs := match (xs,ys) + case ({} , _) then {}; + case (_ , {}) then {}; + case (x::xs_, y::ys_) then (x,y) :: listZip(xs_,ys_); + end match; +end listZip; + +function GenTmpVar + input DAE.Type ty; + input State state; + output MidCode.Var var; +algorithm + var := MidCode.VAR("_tmp_" + intString(System.tmpTickIndex(46)), ty, false); + DoubleEndedList.push_back(state.locals, var); +end GenTmpVar; + +function GenTmpVarVolatile + input DAE.Type ty; + input State state; + output MidCode.Var var; +algorithm + var := MidCode.VAR("_tmp_" + intString(System.tmpTickIndex(46)), ty, true); + DoubleEndedList.push_back(state.locals, var); +end GenTmpVarVolatile; + +function GenTmpVarBuf + /* + Uses same naming scheme as variables. + Doesn't have to as long as they don't collide. + */ + input State state; + output MidCode.VarBuf var; +algorithm + var := MidCode.VARBUF("_jmpbuf_" + intString(System.tmpTickIndex(47))); + DoubleEndedList.push_back(state.localBufs, var); +end GenTmpVarBuf; + +function GenTmpVarBufPtr + /* + Uses same naming scheme as variables. + Doesn't have to as long as they don't collide. + */ + input State state; + output MidCode.VarBufPtr var; +algorithm + var := MidCode.VARBUFPTR("_tmp_" + intString(System.tmpTickIndex(46))); + DoubleEndedList.push_back(state.localBufPtrs, var); +end GenTmpVarBufPtr; + +function GenBlockId + output Integer id; +algorithm + id := System.tmpTickIndex(45); +end GenBlockId; + +function ConvertSimCodeVars + input SimCodeFunction.Variable simcodevar; + input State state; + output MidCode.Var var; +algorithm + var := match simcodevar + local + MidCode.Var midcodevar; + case SimCodeFunction.VARIABLE(__) + algorithm + midcodevar := CrefToMidVar(simcodevar.name, state); + () := match simcodevar.value + local + DAE.Exp exp; + case NONE() then (); + case SOME(exp) + algorithm + stateAddStmt(MidCode.ASSIGN(midcodevar, ExpToMid(exp, state)), state); + then (); + end match; + then midcodevar; + end match; +end ConvertSimCodeVars; + +function GetCrefIndexVar + input DAE.ComponentRef cref; + input State state; + output Option var; +protected + list subscripts; +algorithm + subscripts := ComponentReference.crefLastSubs(cref); + + var := match subscripts + local + DAE.Subscript subscript; + MidCode.Var indexvar; + case {} then NONE(); + case {subscript as DAE.INDEX(__)} + algorithm + indexvar := RValueToVar(ExpToMid(subscript.exp, state), state); + then SOME(indexvar); + end match; +end GetCrefIndexVar; + +function CrefToMidVar + //TODO: handle scopes better + input DAE.ComponentRef cref; + input State state; + output MidCode.Var var; +protected + String ident; + DAE.Type ty; +algorithm + if not BaseHashTable.hasKey(cref, Mutable.access(state.vars)) then + (ident, ty) := match cref + local + String ident_; + DAE.Type ty_; + case DAE.CREF_IDENT(ident_, ty_, _) then (ident_, ty_); + else + algorithm + Error.addInternalError("CrefToMidVar error", sourceInfo()); + then fail(); + end match; + Mutable.update(state.vars, BaseHashTable.add((cref, MidCode.VAR(ident, Types.complicateType(ty), false)), Mutable.access(state.vars))); + end if; + var := BaseHashTable.get(cref, Mutable.access(state.vars)); +end CrefToMidVar; + +function RValueType + input MidCode.RValue rvalue; + output DAE.Type ty; +algorithm + ty := match rvalue + case MidCode.VARIABLE(__) then rvalue.src.ty; + //TODO: move comparisons to separate? + case MidCode.BINARYOP(__) then match rvalue.op + case MidCode.LESS() then DAE.T_BOOL_DEFAULT; + case MidCode.LESSEQ() then DAE.T_BOOL_DEFAULT; + case MidCode.GREATER() then DAE.T_BOOL_DEFAULT; + case MidCode.GREATEREQ() then DAE.T_BOOL_DEFAULT; + case MidCode.EQUAL() then DAE.T_BOOL_DEFAULT; + case MidCode.NEQUAL() then DAE.T_BOOL_DEFAULT; + else then rvalue.lsrc.ty; + end match; + case MidCode.UNARYOP(MidCode.BOX(),_) then Types.boxIfUnboxedType(rvalue.src.ty); + case MidCode.UNARYOP(MidCode.UNBOX(),_) then Types.unboxedType(rvalue.src.ty); + //TODO: separate CAST? since has new type + case MidCode.UNARYOP(__) then rvalue.src.ty; + case MidCode.LITERALINTEGER(__) then DAE.T_INTEGER_DEFAULT; + case MidCode.LITERALREAL(__) then DAE.T_REAL_DEFAULT; + case MidCode.LITERALBOOLEAN(__) then DAE.T_BOOL_DEFAULT; + case MidCode.LITERALSTRING(__) then DAE.T_STRING_DEFAULT; + case MidCode.LITERALMETATYPE(__) then rvalue.ty; + case MidCode.METAFIELD(__) then rvalue.ty; + case MidCode.UNIONTYPEVARIANT(__) then DAE.T_INTEGER_DEFAULT; + case MidCode.ISCONS(__) then DAE.T_BOOL_DEFAULT; + case MidCode.ISSOME(__) then DAE.T_BOOL_DEFAULT; + else + algorithm + Error.addInternalError("Could not find the correct type of an RValue.\n", sourceInfo()); + then fail(); + end match; +end RValueType; + +function RValueToVar + input MidCode.RValue rvalue; + input State state; + output MidCode.Var var; +algorithm +var := match rvalue + local + MidCode.Var tmpvar; + case MidCode.VARIABLE(__) then rvalue.src; + else + algorithm + tmpvar := GenTmpVar(Types.complicateType(RValueType(rvalue)),state); + DoubleEndedList.push_back(state.stmts, MidCode.ASSIGN(tmpvar, rvalue)); + then tmpvar; +end match; +end RValueToVar; + +function DAEFunctionToMid + input SimCodeFunction.Function simfunc; + output MidCode.Function midfunc; +protected + State state; + DoubleEndedList inputs; + DoubleEndedList outputs; + MidCode.Block block_; + Absyn.Path path; + Integer labelFirst; + +algorithm + System.tmpTickReset(47); //jump buffers + System.tmpTickReset(46); //variables + System.tmpTickReset(45); //block ids + + () := match simfunc + local + Absyn.Path name; + list outVars; + list functionArguments; + list variableDeclarations; + list body; + SCode.Visibility visibility; + SourceInfo info; + + case SimCodeFunction.FUNCTION(name, outVars, functionArguments, variableDeclarations, body, visibility, info) + algorithm + labelFirst := GenBlockId(); + path := name; + inputs := DoubleEndedList.fromList({}); + outputs := DoubleEndedList.fromList({}); + state := STATE(DoubleEndedList.fromList({}), + DoubleEndedList.fromList({}), + DoubleEndedList.fromList({}), + DoubleEndedList.fromList({}), + DoubleEndedList.fromList({}), + Mutable.create(labelFirst), + Mutable.create({}), + Mutable.create({}), + Mutable.create(HashTableMidVar.emptyHashTable())); + for simcodeVar in variableDeclarations loop + DoubleEndedList.push_back(state.locals, ConvertSimCodeVars(simcodeVar, state)); + end for; + for simcodeVar in outVars loop + DoubleEndedList.push_back(outputs, ConvertSimCodeVars(simcodeVar, state)); + end for; + for simcodeVar in functionArguments loop + DoubleEndedList.push_back(inputs, ConvertSimCodeVars(simcodeVar, state)); + end for; + + StmtsToMid(body, state); + then (); + else + algorithm + Error.addInternalError("Unsupported SimCodeFunction.Function type\n", sourceInfo()); + fail(); + then (); + end match; + + stateTerminate(-1, MidCode.RETURN(), state); + + midfunc := MidCode.FUNCTION(name=path, + locals=DoubleEndedList.toListAndClear(state.locals), + localBufs=DoubleEndedList.toListAndClear(state.localBufs), + localBufPtrs=DoubleEndedList.toListAndClear(state.localBufPtrs), + inputs=DoubleEndedList.toListAndClear(inputs), + outputs=DoubleEndedList.toListAndClear(outputs), + body=DoubleEndedList.toListAndClear(state.blocks), + entryId=labelFirst, + exitId=GenBlockId()); + midfunc := MidToMid.longJmpGoto(midfunc); +end DAEFunctionToMid; + +function StmtsToMid + input list daestmts; + input State state; +algorithm + () := match daestmts + local + DAE.Statement stmt; + list tail; + case ({}) then (); + case (stmt::tail) + algorithm + () := match stmt + local + DAE.Type ty; + DAE.Exp exp1; + DAE.Exp exp; + DAE.ComponentRef cref; + DAE.Pattern pattern; + list daestmtLst; + list expLst; + list indexesLst; + MidCode.Var varCref; + MidCode.Var varArray; + MidCode.Var varIndex; + MidCode.Var varValue; + MidCode.Var varCondition; + MidCode.Var varIter; + MidCode.Var varLast; + MidCode.Var varStep; + MidCode.Var varMessage; + MidCode.Var varLevel; + MidCode.Var varRHS; + MidCode.OutVar outvar; + MidCode.Block block_; + Integer labelBody; + Integer labelNext; + Integer labelCondition; + Integer labelStep; + DAE.Else else_; + DoubleEndedList outvars; + String iter; + Integer index; + list subscripts; + MidCode.Stmt midstmt; + MidCode.RValue rvalue; + array> assignBlock; + case DAE.STMT_ASSIGN(_, exp1 as DAE.CREF(__), exp, _) + algorithm + cref := ComponentReference.crefLastCref(exp1.componentRef); //gå runt CREF_QUAL tills vidare + varCref := CrefToMidVar(cref,state); + + stateAddStmt(MidCode.ASSIGN(varCref, ExpToMid(exp, state)), state); + then (); + case DAE.STMT_ASSIGN(_, exp1 as DAE.ASUB(__), exp, _) + algorithm + varArray := RValueToVar(ExpToMid(exp1.exp, state), state); + varIndex := match exp1.sub + local + DAE.Exp indexexp; + case {indexexp} then RValueToVar(ExpToMid(indexexp, state), state); + end match; + varValue := RValueToVar(ExpToMid(exp, state), state); + + labelNext := GenBlockId(); + stateTerminate(labelNext, MidCode.CALL(Absyn.IDENT("arrayUpdate"), true, {varArray, varIndex, varValue}, {}, labelNext), state); + then (); + case DAE.STMT_ASSIGN(_, DAE.PATTERN(pattern), exp, _) + algorithm + varRHS := RValueToVar(ExpToMid(exp,state),state); + patternToMidCode(matches={(varRHS,pattern)},labelNoMatch=1,state=state); // pattern match + then (); + case DAE.STMT_ASSIGN(__) + algorithm + Error.addInternalError("DAE.STMT_ASSIGN to Mid conversion failed " + ExpressionDump.dumpExpStr(stmt.exp1,0) + "\n", sourceInfo()); + then fail(); + case DAE.STMT_TUPLE_ASSIGN(_, expLst, exp, _) + algorithm + outvars := DoubleEndedList.fromList({}); + for exp1 in expLst loop + () := match exp1 + case DAE.CREF(DAE.WILD()) + algorithm + DoubleEndedList.push_back(outvars, MidCode.OUT_WILD()); + then (); + case DAE.CREF(__) + algorithm + varCref := CrefToMidVar(exp1.componentRef, state); + DoubleEndedList.push_back(outvars, MidCode.OUT_VAR(varCref)); + then (); + else + algorithm + Error.addInternalError("outvars convertion failed " + ExpressionDump.dumpExpStr(exp1,0) + "\n", sourceInfo()); + then fail(); + end match; + end for; + () := match exp + case DAE.CALL(__) + algorithm + CallToMid(exp, DoubleEndedList.toListAndClear(outvars), state); + then (); + case DAE.MATCHEXPRESSION(__) + algorithm + MatchExpressionToMid(exp, DoubleEndedList.toListAndClear(outvars), state); + then (); + end match; + then (); + case DAE.STMT_IF(__) + algorithm + IfToMid(stmt.exp, stmt.statementLst, stmt.else_, state); + then (); + case DAE.STMT_WHILE(__) + algorithm + labelCondition := GenBlockId(); + labelBody := GenBlockId(); + labelNext := GenBlockId(); + + Mutable.update(state.continuejumps, labelCondition :: Mutable.access(state.continuejumps)); + Mutable.update(state.breakjumps, labelNext :: Mutable.access(state.breakjumps)); + + stateTerminate(labelCondition, MidCode.GOTO(labelCondition), state); + + varCondition := RValueToVar(ExpToMid(stmt.exp, state), state); + stateTerminate(labelBody, MidCode.BRANCH(varCondition, labelBody, labelNext), state); + + StmtsToMid(stmt.statementLst, state); + stateTerminate(labelNext, MidCode.GOTO(labelCondition), state); + + Mutable.update(state.continuejumps, listRest(Mutable.access(state.continuejumps))); + Mutable.update(state.breakjumps, listRest(Mutable.access(state.breakjumps))); + + then (); + case DAE.STMT_FOR(__) + algorithm + ForToMid(stmt.type_, stmt.iter, stmt.range, stmt.statementLst, state); + then (); + case DAE.STMT_BREAK(_) + algorithm + labelNext := GenBlockId(); + stateTerminate(labelNext, MidCode.GOTO(listHead(Mutable.access(state.breakjumps))), state); + then (); + case DAE.STMT_CONTINUE(_) + algorithm + labelNext := GenBlockId(); + stateTerminate(labelNext, MidCode.GOTO(listHead(Mutable.access(state.continuejumps))), state); + then (); + case DAE.STMT_RETURN(_) + algorithm + labelNext := GenBlockId(); + stateTerminate(labelNext, MidCode.RETURN(), state); + then (); + case DAE.STMT_NORETCALL(__) + algorithm + () := match stmt.exp + case DAE.CALL(__) + algorithm + CallToMid(stmt.exp, {}, state); + then (); + case DAE.MATCHEXPRESSION(__) + algorithm + MatchExpressionToMid(stmt.exp, {}, state); + then (); + end match; + then (); + case DAE.STMT_ASSERT(__) + algorithm + varCondition := RValueToVar(ExpToMid(stmt.cond, state), state); + varMessage := RValueToVar(ExpToMid(stmt.msg, state), state); + varLevel := RValueToVar(ExpToMid(stmt.level, state), state); + + labelNext := GenBlockId(); + + stateTerminate(labelNext, MidCode.ASSERT(varCondition, varMessage, varLevel, labelNext), state); + then (); + case DAE.STMT_TERMINATE(__) + algorithm + varMessage := RValueToVar(ExpToMid(stmt.msg, state), state); + + labelNext := GenBlockId(); + + stateTerminate(labelNext, MidCode.TERMINATE(varMessage), state); + then (); + else + algorithm + Error.addInternalError("DAE.Statement to Mid conversion failed " + DAEDump.ppStatementStr(stmt), sourceInfo()); + then fail(); + + end match; + + StmtsToMid(tail, state); + then (); + end match; +end StmtsToMid; + +function ExpToMid + input DAE.Exp exp; + input State state; + output MidCode.RValue rval; +algorithm + rval := match exp + local + MidCode.Var varExp; + MidCode.Var varExp2; + MidCode.Var varCref; + MidCode.Var varCar; + MidCode.Var varCdr; + MidCode.Var varTmp; + MidCode.BinaryOp binop; + MidCode.UnaryOp unop; + DAE.Exp exp1; + DAE.Exp exp2; + DAE.Exp exp3; + DAE.Operator operator; + DAE.Type ty; + DAE.ComponentRef cref; + Integer labelBody; + Integer labelElse; + Integer labelNext; + Integer index; + Integer length; + Integer numTailTypes; + MidCode.Block block_; + MidCode.Terminator terminator; + Absyn.Path path; + list expLst; + DoubleEndedList values; + list outvars; + Option option; + DAE.CallAttributes callattrs; + list subscripts; + MidCode.RValue rvalue; + case DAE.ICONST(__) then MidCode.LITERALINTEGER(exp.integer); + case DAE.ENUM_LITERAL(__) then MidCode.LITERALINTEGER(exp.index); + case DAE.RCONST(__) then MidCode.LITERALREAL(exp.real); + case DAE.SCONST(__) then MidCode.LITERALSTRING(exp.string); + case DAE.SHARED_LITERAL(__) then ExpToMid(exp.exp, state); //don't bother with shared support yet + case DAE.BOX(__) + algorithm + varExp := RValueToVar(ExpToMid(exp.exp, state), state); + then MidCode.UNARYOP(MidCode.BOX(), varExp); + case DAE.UNBOX(__) + algorithm + varExp := RValueToVar(ExpToMid(exp.exp, state), state); + then MidCode.UNARYOP(MidCode.UNBOX(), varExp); + case DAE.BCONST(__) then MidCode.LITERALBOOLEAN(exp.bool); + case DAE.META_OPTION(SOME(exp1)) + algorithm + varExp := RValueToVar(ExpToMid(exp1, state), state); + then MidCode.LITERALMETATYPE({varExp}, Types.complicateType(DAE.T_METAOPTION(varExp.ty))); + case DAE.META_OPTION(NONE()) + then MidCode.LITERALMETATYPE({}, Types.complicateType(DAE.T_NONE_DEFAULT)); + case DAE.META_TUPLE(expLst) + algorithm + values := DoubleEndedList.fromList({}); + for exp in expLst loop + varExp := RValueToVar(ExpToMid(exp, state), state); + DoubleEndedList.push_back(values, varExp); + end for; + then MidCode.LITERALMETATYPE(DoubleEndedList.toListAndClear(values), Types.complicateType(Expression.typeof(exp))); + case DAE.METARECORDCALL(_, expLst, _, _, _) + algorithm + values := DoubleEndedList.fromList({}); + for exp in expLst loop + varExp := RValueToVar(ExpToMid(exp, state), state); + DoubleEndedList.push_back(values, varExp); + end for; + then MidCode.LITERALMETATYPE(DoubleEndedList.toListAndClear(values), Types.complicateType(Expression.typeof(exp))); + case DAE.CONS(__) + algorithm + varCar := RValueToVar(ExpToMid(exp.car, state), state); + varCdr := RValueToVar(ExpToMid(exp.cdr, state), state); + then MidCode.LITERALMETATYPE({varCar, varCdr}, Types.complicateType(DAE.T_METALIST(varCar.ty))); + case DAE.LIST(expLst) + algorithm + expLst := listReverse(expLst); + + varCdr := GenTmpVar(DAE.T_METALIST_DEFAULT,state); + DoubleEndedList.push_back(state.stmts, MidCode.ASSIGN(varCdr, MidCode.LITERALMETATYPE({}, DAE.T_METALIST_DEFAULT))); + for exp in expLst loop + varCar := RValueToVar(ExpToMid(exp, state), state); + varTmp := GenTmpVar(DAE.T_METALIST(Types.complicateType(varCar.ty)),state); + DoubleEndedList.push_back(state.stmts, MidCode.ASSIGN(varTmp, MidCode.LITERALMETATYPE({varCar, varCdr}, Types.complicateType(DAE.T_METALIST(varCar.ty))))); + varCdr := varTmp; + end for; + then MidCode.VARIABLE(varCdr); + case DAE.CREF(cref, _) + algorithm + varCref := CrefToMidVar(cref, state); + + rvalue := match GetCrefIndexVar(cref, state) + local + MidCode.Var indexvar; + case NONE() then MidCode.VARIABLE(varCref); + case SOME(indexvar) + algorithm + labelNext := GenBlockId(); + + varTmp := GenTmpVar(Types.complicateType(Expression.typeof(exp)),state); + + stateTerminate(labelNext, + MidCode.CALL(Absyn.IDENT("arrayGet"), true, {varCref,indexvar}, {MidCode.OUT_VAR(varTmp)}, labelNext), + state); + then MidCode.VARIABLE(varTmp); + end match; + then rvalue; + case DAE.ASUB(exp1, expLst) + algorithm + varExp := RValueToVar(ExpToMid(exp1, state), state); + varExp2 := match expLst + local + DAE.Exp indexexp; + case {indexexp} then RValueToVar(ExpToMid(indexexp, state), state); + end match; + + varTmp := GenTmpVar(Types.complicateType(Expression.typeof(exp)),state); + + labelNext := GenBlockId(); + + stateTerminate(labelNext, + MidCode.CALL(Absyn.IDENT("arrayGet"), true, {varExp, varExp2}, {MidCode.OUT_VAR(varTmp)}, labelNext), + state); + then MidCode.VARIABLE(varTmp); + case DAE.TSUB(exp1 as DAE.CALL(_,_,callattrs), 1, _) + algorithm + /* stupid special case */ + (ty,numTailTypes) := match callattrs.ty + local + DAE.Type actualType; + list tailTypes; + case DAE.T_TUPLE(actualType::tailTypes) then (actualType, listLength(tailTypes)); + else fail(); + end match; + + varTmp := GenTmpVar(Types.complicateType(ty),state); + + outvars := {}; + for i in 1:numTailTypes loop + outvars := MidCode.OUT_WILD() :: outvars; + end for; + outvars := MidCode.OUT_VAR(varTmp) :: outvars; + CallToMid(exp1, outvars, state); + then MidCode.VARIABLE(varTmp); + case DAE.TSUB(__) + algorithm + varExp := RValueToVar(ExpToMid(exp.exp, state), state); + then MidCode.METAFIELD(varExp, exp.ix, Types.complicateType(exp.ty)); + case DAE.RSUB(__) + algorithm + varExp := RValueToVar(ExpToMid(exp.exp, state), state); + then MidCode.METAFIELD(varExp, exp.ix, Types.complicateType(exp.ty)); + case DAE.CAST(ty, exp1) + algorithm + varExp := RValueToVar(ExpToMid(exp1, state), state); + then MidCode.UNARYOP(MidCode.MOVE(), varExp); //TODO: return type? + case DAE.LUNARY(operator, exp1) + algorithm + varExp := RValueToVar(ExpToMid(exp1, state), state); + then MidCode.UNARYOP(MidCode.NOT(), varExp); + case DAE.LBINARY(exp1, operator, exp2) + algorithm + labelElse := GenBlockId(); + labelNext := GenBlockId(); + + ty := match operator + case DAE.AND(__) then operator.ty; + case DAE.OR(__) then operator.ty; + end match; + varTmp := GenTmpVar(ty,state); + + terminator := match operator + case DAE.AND(_) then MidCode.BRANCH(varTmp, labelElse, labelNext); + case DAE.OR(_) then MidCode.BRANCH(varTmp, labelNext, labelElse); + end match; + + stateAddStmt(MidCode.ASSIGN(varTmp, ExpToMid(exp1, state)), state); + stateTerminate(labelElse, terminator, state); + + stateAddStmt(MidCode.ASSIGN(varTmp, ExpToMid(exp2, state)), state); + stateTerminate(labelNext, MidCode.GOTO(labelNext), state); + + then MidCode.VARIABLE(varTmp); + case DAE.UNARY(operator,exp1) + algorithm + unop := match operator + case DAE.UMINUS(__) then MidCode.UMINUS(); + end match; + + varExp := RValueToVar(ExpToMid(exp1, state), state); + then MidCode.UNARYOP(unop, varExp); + case DAE.BINARY(exp1, operator, exp2) + algorithm + binop := match operator + case DAE.ADD(__) then MidCode.ADD(); + case DAE.SUB(__) then MidCode.SUB(); + case DAE.MUL(__) then MidCode.MUL(); + case DAE.DIV(__) then MidCode.DIV(); + case DAE.POW(__) then MidCode.POW(); + end match; + + varExp := RValueToVar(ExpToMid(exp1, state), state); + varExp2 := RValueToVar(ExpToMid(exp2, state), state); + then MidCode.BINARYOP(binop, varExp, varExp2); + case DAE.RELATION(exp1, operator, exp2, _, _) + algorithm + binop := match operator + case DAE.LESS(__) then MidCode.LESS(); + case DAE.LESSEQ(__) then MidCode.LESSEQ(); + case DAE.GREATER(__) then MidCode.GREATER(); + case DAE.GREATEREQ(__) then MidCode.GREATEREQ(); + case DAE.EQUAL(__) then MidCode.EQUAL(); + case DAE.NEQUAL(__) then MidCode.NEQUAL(); + end match; + + varExp := RValueToVar(ExpToMid(exp1, state), state); + varExp2 := RValueToVar(ExpToMid(exp2, state), state); + then MidCode.BINARYOP(binop, varExp, varExp2); + case DAE.IFEXP(exp1, exp2, exp3) + algorithm + + labelBody := GenBlockId(); + labelElse := GenBlockId(); + labelNext := GenBlockId(); + + varExp := RValueToVar(ExpToMid(exp1, state), state); + + varTmp := GenTmpVar(Types.complicateType(Expression.typeof(exp2)),state); + + stateTerminate(labelBody, MidCode.BRANCH(varExp, labelBody, labelElse), state); + + stateAddStmt(MidCode.ASSIGN(varTmp, ExpToMid(exp2, state)), state); + stateTerminate(labelElse, MidCode.GOTO(labelNext), state); + + stateAddStmt(MidCode.ASSIGN(varTmp, ExpToMid(exp3, state)), state); + stateTerminate(labelNext, MidCode.GOTO(labelNext), state); + then MidCode.VARIABLE(varTmp); + case DAE.CALL(_, _, callattrs) + algorithm + varTmp := GenTmpVar(Types.complicateType(callattrs.ty),state); + CallToMid(exp, {MidCode.OUT_VAR(varTmp)}, state); + then MidCode.VARIABLE(varTmp); + case DAE.MATCHEXPRESSION(et=ty) + algorithm + varTmp := GenTmpVar(Types.complicateType(ty),state); + () := match Types.complicateType(ty) + case DAE.T_TUPLE(__) + algorithm + Error.addInternalError("Not supposed to get tuple here.\n", sourceInfo()); + then fail(); + else then (); + end match; + MatchExpressionToMid(exp,{MidCode.OUT_VAR(varTmp)},state); + then MidCode.VARIABLE(varTmp); + else + algorithm + Error.addInternalError("DAE.Exp to Mid conversion failed:\n" + ExpressionDump.dumpExpStr(exp,0) + "\n", sourceInfo()); + then fail(); + end match; +end ExpToMid; + +function CallToMid + input DAE.Exp call; + input list outvars; + input State state; +algorithm + //TODO: maybe handle isFunctionPointerCall/isImpure + () := match call + local + Absyn.Path path; + list expLst; + DAE.CallAttributes callattr; + Integer labelNext; + DoubleEndedList inputs; + MidCode.Var var1; + MidCode.Block block_; + case DAE.CALL(path, expLst, callattr) + algorithm + labelNext := GenBlockId(); + + inputs := DoubleEndedList.fromList({}); + for exp1 in expLst loop + var1 := RValueToVar(ExpToMid(exp1, state), state); + DoubleEndedList.push_back(inputs, var1); + end for; + + stateTerminate(labelNext, + MidCode.CALL(path,callattr.builtin,DoubleEndedList.toListAndClear(inputs),outvars,labelNext), + state); + + then (); + end match; +end CallToMid; + +function ForToMid + input DAE.Type type_; + input String iter; + input DAE.Exp range; + input list daestmtLst; + input State state; +protected + MidCode.Var varCref; + MidCode.Var varCondition; + Integer labelCondition; + Integer labelStep; + Integer labelBody; + Integer labelNext; +algorithm + varCref := CrefToMidVar(DAE.CREF_IDENT(iter, type_, {}), state); + DoubleEndedList.push_back(state.locals, varCref); + + labelCondition := GenBlockId(); + labelStep := GenBlockId(); + labelBody := GenBlockId(); + labelNext := GenBlockId(); + + Mutable.update(state.continuejumps, labelStep :: Mutable.access(state.continuejumps)); + Mutable.update(state.breakjumps, labelNext :: Mutable.access(state.breakjumps)); + + varCondition := GenTmpVar(DAE.T_BOOL_DEFAULT,state); + + () := match range + local + DAE.Exp start; + Option step; + DAE.Exp stop; + MidCode.Var varRange; + MidCode.Var varFirst; + MidCode.Var varIter; + MidCode.Var varLast; + MidCode.Var varStep; + Integer labelBody2; + Integer labelCondition2; + MidCode.RValue rvalueStep; + case DAE.RANGE(_, start, step, stop) + algorithm + labelCondition2 := GenBlockId(); + + varFirst := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + varIter := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + varLast := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + varStep := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + + stateAddStmt(MidCode.ASSIGN(varFirst, ExpToMid(start, state)), state); + stateAddStmt(MidCode.ASSIGN(varIter, ExpToMid(start, state)), state); + stateAddStmt(MidCode.ASSIGN(varLast, ExpToMid(stop, state)), state); + + rvalueStep := match step + local + DAE.Exp stepexp; + case NONE() then MidCode.LITERALINTEGER(1); + case SOME(stepexp) then ExpToMid(stepexp, state); + end match; + + stateAddStmt(MidCode.ASSIGN(varStep, rvalueStep), state); + stateTerminate(labelCondition, MidCode.GOTO(labelCondition), state); + + stateTerminate(labelCondition2, + MidCode.CALL(Absyn.IDENT("in_range_integer"), true, {varIter, varFirst, varLast}, {MidCode.OUT_VAR(varCondition)}, labelCondition2), + state); + + stateTerminate(labelBody, MidCode.BRANCH(varCondition, labelBody, labelNext), state); + + stateAddStmt(MidCode.ASSIGN(varCref, MidCode.VARIABLE(varIter)), state); + StmtsToMid(daestmtLst, state); + stateTerminate(labelStep, MidCode.GOTO(labelStep), state); + + stateAddStmt(MidCode.ASSIGN(varIter, MidCode.BINARYOP(MidCode.ADD(), varIter, varStep)), state); + stateTerminate(labelNext, MidCode.GOTO(labelCondition), state); + then (); + else + algorithm + varRange := RValueToVar(ExpToMid(range, state), state); + () := match varRange.ty + case DAE.T_METATYPE(_) + algorithm + Error.addInternalError("metatype error", sourceInfo()); + then fail(); + case DAE.T_METAARRAY(_) + algorithm + labelBody2 := GenBlockId(); + + varIter := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + varLast := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + varStep := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + + stateAddStmt(MidCode.ASSIGN(varIter, MidCode.LITERALINTEGER(1)), state); + stateAddStmt(MidCode.ASSIGN(varStep, MidCode.LITERALINTEGER(1)), state); + stateTerminate(labelCondition, + MidCode.CALL(Absyn.IDENT("arrayLength"), true, {varRange}, {MidCode.OUT_VAR(varLast)}, labelCondition), + state); + + stateAddStmt(MidCode.ASSIGN(varCondition, MidCode.BINARYOP(MidCode.LESSEQ(), varIter, varLast)), state); + stateTerminate(labelBody, MidCode.BRANCH(varCondition, labelBody, labelNext), state); + + stateTerminate(labelBody2, + MidCode.CALL(Absyn.IDENT("arrayGet"), true, {varRange, varIter}, {MidCode.OUT_VAR(varCref)}, labelBody2), + state); + + StmtsToMid(daestmtLst, state); + stateTerminate(labelStep, MidCode.GOTO(labelStep), state); + + stateAddStmt(MidCode.ASSIGN(varIter, MidCode.BINARYOP(MidCode.ADD(), varIter, varStep)), state); + stateTerminate(labelNext, MidCode.GOTO(labelCondition), state); + then (); + case DAE.T_METALIST(_) + algorithm + labelBody2 := GenBlockId(); + + varIter := varRange; + stateTerminate(labelCondition, MidCode.GOTO(labelCondition), state); + + stateAddStmt(MidCode.ASSIGN(varCondition, MidCode.ISCONS(varIter)), state); + stateTerminate(labelBody, MidCode.BRANCH(varCondition, labelBody, labelNext), state); + + stateTerminate(labelBody2, + MidCode.CALL(Absyn.IDENT("listHead"), true, {varIter}, {MidCode.OUT_VAR(varCref)}, labelBody2), + state); + + StmtsToMid(daestmtLst, state); + stateTerminate(labelStep, MidCode.GOTO(labelStep), state); + + stateTerminate(labelNext, + MidCode.CALL(Absyn.IDENT("listRest"), true, {varIter}, {MidCode.OUT_VAR(varIter)}, labelCondition), + state); + then (); + else + algorithm + Error.addInternalError("unknown for type " + DAEDump.daeTypeStr(varRange.ty) + "\n", sourceInfo()); + then fail(); + end match; + then (); + end match; + + Mutable.update(state.continuejumps, listRest(Mutable.access(state.continuejumps))); + Mutable.update(state.breakjumps, listRest(Mutable.access(state.breakjumps))); +end ForToMid; + +function IfToMid + input DAE.Exp exp; + input list daestmtLst; + input DAE.Else else_; + input State state; +protected + Integer labelBody; + Integer labelElse; + Integer labelNext; + MidCode.Var var1; + MidCode.Block block_; +algorithm + labelBody := GenBlockId(); + labelElse := GenBlockId(); + labelNext := GenBlockId(); + + var1 := RValueToVar(ExpToMid(exp, state), state); + + stateTerminate(labelBody, MidCode.BRANCH(var1, labelBody, labelElse), state); + + StmtsToMid(daestmtLst, state); + stateTerminate(labelElse, MidCode.GOTO(labelNext), state); + + () := match else_ + local + DAE.Exp subexp; + list subdaestmtLst; + DAE.Else subelse; + case DAE.NOELSE() then (); + case DAE.ELSEIF(subexp, subdaestmtLst, subelse) + algorithm + IfToMid(subexp, subdaestmtLst, subelse, state); + then (); + case DAE.ELSE(subdaestmtLst) + algorithm + StmtsToMid(subdaestmtLst, state); + then (); + end match; + + stateTerminate(labelNext, MidCode.GOTO(labelNext), state); +end IfToMid; + + +function stateGetCurrentLabel + input State state; + output Integer label; +algorithm + label := Mutable.access(state.blockid); +end stateGetCurrentLabel; + +function stateSetCurrentLabel + input Integer label; + input State state; +algorithm + Mutable.update(state.blockid, label); +end stateSetCurrentLabel; + +function stateAddStmt + input MidCode.Stmt stmt; + input State state; +algorithm + DoubleEndedList.push_back(state.stmts, stmt); +end stateAddStmt; + +function stateTerminate + input Integer newLabel; + input MidCode.Terminator terminator; + input State state; +protected + MidCode.Block block_; +algorithm + block_ := MidCode.BLOCK(stateGetCurrentLabel(state), + DoubleEndedList.toListAndClear(state.stmts), + terminator); + DoubleEndedList.push_back(state.blocks, block_); + + stateSetCurrentLabel(newLabel, state); +end stateTerminate; + +// helper +function stateAddBailOnFalse + input MidCode.Var var; + input Integer labelBail; + input State state; +protected + Integer labelTmp; +algorithm + labelTmp := GenBlockId(); + stateTerminate(labelTmp,MidCode.BRANCH(var,onFalse=labelBail,onTrue=labelTmp), state); +end stateAddBailOnFalse; + +function unpackCrefFromExp + input DAE.Exp exp; + output DAE.ComponentRef cref; +algorithm + cref := match exp + case DAE.CREF(cref) + then cref; + end match; +end unpackCrefFromExp; + + +//TODO: stuff needs to be volatile for setjmp. +//TODO: could handle match separately from matchcontinue and add more simplifications +/* +The term matchexpression is used to include both matchcontinue and match. +*/ +function MatchExpressionToMid + input DAE.Exp matchexpression; + input list outvars; + input State state; +protected + Integer labelFin, labelMux, labelInit, labelFail, labelFin2, labelOut, caseLabel; + list caseLabels; + MidCode.Var muxState, one, midvar,midvar2; + MidCode.VarBufPtr muxOldBuf; + MidCode.VarBuf muxNewBuf; + MidCode.OutVar outvar; + Boolean matchContinue; + DAE.MatchType matchType; + list cases; + list inputsCref; + list> aliases; // list of (list of alias) where each outer list corresponds to a input + MidCode.Var srcVar, aliasVar; + list aliasList; + DAE.Type ty; + DAE.ComponentRef cref; + list inputsMidVar; + DAE.Exp daeExp; + list caseLabelIterator; +algorithm + /* + I assume the Else case is a case with top level wild patterns (_,_,_). + */ + + // match just to get match elements + () := match matchexpression + case DAE.MATCHEXPRESSION(matchType=matchType, cases=cases, inputs=inputsCref, aliases=aliases) + algorithm + labelInit := stateGetCurrentLabel(state); + labelMux := GenBlockId(); + labelFin := GenBlockId(); + + matchContinue := match matchType + case DAE.MATCHCONTINUE() then true; + case DAE.MATCH() then false; + end match; + + // caseLabels <- sequence $ repeat (length cases) genBlockId + // can write with list comprehension if I can make a range + caseLabels := {}; + for i in 1:listLength(cases) loop + caseLabels := GenBlockId() :: caseLabels; + end for; + + /* + First we evaluate all inputs to the matchcontinue. + We must also bind the aliases that were sent. + If an input does not have an alias we must create a MidCode.Var for it to use. + + zip inputs aliases : [(input,[alias])] + where + length inputs = length aliases + + */ + + assert( listLength(inputsCref) == listLength(aliases), "MatchExpressionToMid: incorrect input: listLength(inputs) != listLength(aliases)" ); + inputsMidVar := {}; + for daeExp_aliasList in List.threadTuple(inputsCref,aliases) loop + + (daeExp,aliasList) := daeExp_aliasList; + srcVar := RValueToVar(ExpToMid(daeExp, state), state); + ty := RValueType(MidCode.VARIABLE(srcVar)); + inputsMidVar := srcVar :: inputsMidVar; + for alias in aliasList loop + aliasVar := MidCode.VAR(name=alias, ty=ty, volatile=false); + DoubleEndedList.push_back(state.locals, aliasVar); + stateAddStmt( MidCode.ASSIGN(aliasVar, MidCode.VARIABLE(srcVar) ), state ); + end for; + end for; + + // inputsMidVar := list(CrefToMidVar(unpackCrefFromExp(expCref),state) for expCref in inputsCref); + + /* + init: + state = 0 + #IF MATCHCONTINUE + PUSHJMP(J_old,J_new) + goto mux + */ + + muxState := GenTmpVarVolatile(DAE.T_INTEGER_DEFAULT,state); // volatile since we mutate it after setjmp + stateAddStmt(MidCode.ASSIGN(muxState, MidCode.LITERALINTEGER(0)), state); + + if matchContinue + then + muxOldBuf := GenTmpVarBufPtr(state); + muxNewBuf := GenTmpVarBuf(state); + stateTerminate(labelMux, MidCode.PUSHJMP(muxOldBuf,muxNewBuf,labelMux),state); + else + stateTerminate(labelMux, MidCode.GOTO(labelMux),state); + end if; + + /* + mux: + #IF MATCHCONTINUE + state+=1 + switch (state) {1:case1, 2:case2, ...,n:case_n,n+1:fin} + #IF MATCH + goto first case or fail if no case + */ + if matchContinue + then + one := GenTmpVar(DAE.T_INTEGER_DEFAULT,state); + stateAddStmt(MidCode.ASSIGN(one, MidCode.LITERALINTEGER(1)) ,state); + stateAddStmt(MidCode.ASSIGN(muxState, MidCode.BINARYOP(MidCode.ADD(),muxState,one)),state); + stateTerminate(labelFin, MidCode.SWITCH( muxState, List.threadTuple( List.intRange(listLength(cases)+1), listAppend(caseLabels,{labelFin}) ) ), state); + else + stateTerminate(labelFin, MidCode.GOTO(if not listEmpty(caseLabels) then listHead(caseLabels) else labelFin), state); + end if; + /* + fin: + #IF MATCHCONTINUE + POPJMP(J_old) + if state == nr_cases+1 + longjmp + else + goto next + */ + + /* + We make the label for the next thing we generate after the match expression. + We replace this in the case loop as we add more cases. + */ + labelFail := GenBlockId(); + labelFin2 := GenBlockId(); + labelOut := GenBlockId(); + + if matchContinue + then + stateTerminate(labelFin2, MidCode.POPJMP( muxOldBuf, labelFin2 ), state); + else + stateTerminate(labelFin2, MidCode.GOTO(labelFin2), state); + end if; + + midvar := RValueToVar(MidCode.LITERALINTEGER(listLength(cases)+1),state); + midvar2 := RValueToVar(MidCode.BINARYOP(MidCode.EQUAL(),muxState, midvar),state); + stateTerminate(labelFail, MidCode.BRANCH(midvar2, labelFail, labelOut),state); + + stateTerminate(labelOut, MidCode.LONGJMP(),state); + + caseLabelIterator := caseLabels; + + // for each case + while not listEmpty(caseLabelIterator) loop + caseLabel := listHead(caseLabelIterator); + caseLabelIterator := listRest(caseLabelIterator); + stateSetCurrentLabel(caseLabel, state); + // left to right - depth first - through all patterns in the case + () := match cases + local + list patterns; + list daeBody; + Option patternGuard; + Option caseResult; + case {} + algorithm + // No more cases. + then (); + case (DAE.CASE(patterns=patterns,body=daeBody,patternGuard=patternGuard,result=caseResult)::cases) // note: modifies cases + algorithm + // first do checks and assignments + // NOTE: If the guard fails we will have made pattern assignments for a failing case. This is how it was done before as far as I can tell. + if matchContinue + then + patternToMidCode(state=state, matches=List.threadTuple(inputsMidVar,patterns), labelNoMatch=labelMux); + else + patternToMidCode(state=state, matches=List.threadTuple(inputsMidVar,patterns) + ,labelNoMatch= if not listEmpty(caseLabelIterator) then listHead(caseLabelIterator) else labelFail); + end if; + // then guard + () := match patternGuard + case (NONE()) + algorithm + // No guard. + then (); + case (SOME(daeExp)) + algorithm + midvar := RValueToVar(ExpToMid(daeExp,state),state); + if matchContinue + then + stateAddBailOnFalse(midvar,labelMux,state); + else + stateAddBailOnFalse(midvar, + if not listEmpty(caseLabelIterator) then listHead(caseLabelIterator) else labelFail, + state); + end if; + then (); + end match; + // followed by body + StmtsToMid(daeBody,state); + /* + instead of caseResult being a list of exps there are 3 cases. + - No result. + NONE() + + - One result. + SOME(result) + + - More results. + SOME(TUPLE(result0,result1,...)) + + Also outvars is unexpectedly removed of trailing wildcards and can be shorter than expList, + including tuples of length 1 (probably 0 too, but who knows). + So we define and use listZip instead of threadTuple. + + TODO: Document the unintuitive undocumented interface somewhere. + */ + () := match (caseResult, outvars) + local + list expList; + case (SOME(DAE.TUPLE(expList)),_) + algorithm + for outvarDaeExp in listZip(outvars, expList) loop + (outvar, daeExp) := outvarDaeExp; + () := match outvar + local + MidCode.Var var; + case MidCode.OUT_VAR(var) + algorithm + stateAddStmt(MidCode.ASSIGN(var, ExpToMid(daeExp,state)),state); + then (); + case MidCode.OUT_WILD() then (); + end match; + + end for; + + then (); + case (SOME(daeExp as DAE.CALL(__)), _) + algorithm + CallToMid(daeExp, outvars, state); + then (); + case (SOME(daeExp as DAE.MATCHEXPRESSION(__)), _) + algorithm + MatchExpressionToMid(daeExp, outvars, state); + then (); + case (SOME(daeExp), {MidCode.OUT_VAR(midvar)}) + algorithm + stateAddStmt(MidCode.ASSIGN(midvar, ExpToMid(daeExp,state)),state); + then (); + case (SOME(daeExp), _) + algorithm + Error.addInternalError("Match expression output to Mid conversion failed:\n" + ExpressionDump.dumpExpStr(daeExp,0) + "\n", sourceInfo()); + then (); + case (NONE(), {}) + algorithm + // No result. + then (); + case (NONE(), _) + algorithm + Error.addInternalError("case fail", sourceInfo()); + then fail(); + end match; + // finally go to end + stateTerminate(labelOut, MidCode.GOTO(labelFin),state); + then (); + end match; + end while; + then (); + end match; +end MatchExpressionToMid; + +function patternToMidCode + " + Performs pattern matching. + + The state will be left so that if the + matching was successful then we + continue in the active block. And + variables in pattern will be bound. + But failures will have jumped to + labelNoMatch. And nothing will be bound. + + For example in a match a failure means handling + the next case. Except for the last case where + failure is a longjmp. + " + input list> matches "List of variables and their corresponding patterns"; + input Integer labelNoMatch "where to go on a failed match"; + input State state; + output array> assignBlock "A block of assignments to perform for a pattern."; +algorithm + assignBlock := arrayCreate(1,{}); + + patternToMidCode2(state=state,matches=matches,labelNoMatch=labelNoMatch,assignBlock=assignBlock); + + for stmt in listReverse(arrayGet(assignBlock,1)) loop + stateAddStmt(stmt,state); + end for; +end patternToMidCode; + +function patternToMidCode2 + " + Recursive worker function for + patternToMidCode handling. + " + input State state; + input list> matches; + input Integer labelNoMatch; /* where to go on a failed match*/ + input array> assignBlock; /* A block of assignments to perform for a pattern. */ +protected + Absyn.Path name; + Integer index; + list morePatterns, iterator; + list fields; + list typeVars; + Boolean knownSingleton; + Integer fieldNr; +algorithm + /* + case0: + check some pattern + if not match then goto mux (e.g. METACONSTRUCTOR) + extract scrutinees for sub-patterns (e.g. METAFIELD) + note down if there is a binding to be done later (assignBlock) + check another part of pattern + ... + if not guard expression + goto mux + else goto body0 + + guard and body is handled in the caller, not here + + */ + + () := match matches + local + list> restMatches, moreMatches; + list listTypes; + MidCode.Var ok; /* Just a MidCode boolean variable */ + MidCode.Var scrutinee, midvar, headVar, restVar; + String id; + DAE.Pattern pattern, headPattern, restPattern; + DAE.Exp exp; + DAE.Type ty; + MidCode.Var scrutineeCompareVar; + MidCode.Var patCompareVar; + Option optType; + + Boolean bool; + Integer integer; + Real real; + String string; + + case {} + algorithm + // All patterns have been matched. Fall through to what happens on succesful match. + then (); + + case (scrutinee,DAE.PAT_WILD()) :: restMatches + algorithm + patternToMidCode2(matches = restMatches, state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (scrutinee,DAE.PAT_AS(id=id,ty=NONE(),attr=_,pat=pattern)) :: restMatches + algorithm + ty := RValueType(MidCode.VARIABLE(scrutinee)); + midvar := MidCode.VAR(id, ty, false); + arrayUpdate(assignBlock, 1, MidCode.ASSIGN(midvar, MidCode.VARIABLE(scrutinee))::arrayGet(assignBlock,1)); + patternToMidCode2(matches = (scrutinee, pattern) :: restMatches, state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (scrutinee,DAE.PAT_AS(id=id,ty=SOME(ty),attr=_,pat=pattern)) :: restMatches + algorithm + // ty=SOME(_) means that the contained value needs unboxing + midvar := MidCode.VAR(id, ty, false); + arrayUpdate(assignBlock, 1, MidCode.ASSIGN(midvar, MidCode.UNARYOP(MidCode.UNBOX(),scrutinee))::arrayGet(assignBlock,1)); + patternToMidCode2(matches = (scrutinee, pattern) :: restMatches, state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (scrutinee,DAE.PAT_CONSTANT(ty=optType,exp=exp)) :: restMatches // TODO: what to do about optType + algorithm + //remove shared literal + exp := match exp + case DAE.SHARED_LITERAL(exp=exp) then exp; + else then exp; + end match; + + //unbox + scrutinee := match optType + case NONE() then scrutinee; + case SOME(_) then RValueToVar(MidCode.UNARYOP(MidCode.UNBOX(),scrutinee),state); + end match; + + // test + () := match exp + case DAE.BCONST(bool=bool) + algorithm + scrutineeCompareVar := scrutinee; + patCompareVar := RValueToVar(MidCode.LITERALBOOLEAN(bool), state); + then (); + case DAE.ICONST(integer=integer) + algorithm + scrutineeCompareVar := scrutinee; + patCompareVar := RValueToVar(MidCode.LITERALINTEGER(integer), state); + then (); + case DAE.RCONST(real=real) + algorithm + scrutineeCompareVar := scrutinee; + patCompareVar := RValueToVar(MidCode.LITERALREAL(real), state); + then (); + case DAE.ENUM_LITERAL(index=integer) + algorithm + scrutineeCompareVar := scrutinee; + patCompareVar := RValueToVar(MidCode.LITERALINTEGER(integer), state); + then (); + case DAE.LIST(valList = {}) + algorithm + scrutineeCompareVar := RValueToVar(MidCode.ISCONS(scrutinee), state); + patCompareVar := RValueToVar(MidCode.LITERALBOOLEAN(false), state); + then (); + case DAE.META_OPTION(exp = NONE()) + algorithm + scrutineeCompareVar := RValueToVar(MidCode.ISSOME(scrutinee), state); + patCompareVar := RValueToVar(MidCode.LITERALBOOLEAN(false), state); + then (); + case DAE.SCONST(string=string) + algorithm + scrutineeCompareVar := scrutinee; + patCompareVar := RValueToVar(MidCode.LITERALSTRING(string), state); + then (); + else + algorithm + Error.addInternalError("DAE.Exp to Mid conversion failed for pattern constant. Exp:" + ExpressionDump.dumpExpStr(exp,0) + ".\n", sourceInfo()); + then fail(); + end match; + + // generic part of test + ok := GenTmpVar(DAE.T_BOOL_DEFAULT,state); + + stateAddStmt(MidCode.ASSIGN(ok, MidCode.BINARYOP(MidCode.EQUAL(), scrutineeCompareVar, patCompareVar )), state); + stateAddBailOnFalse(ok, labelNoMatch, state); + patternToMidCode2(matches = restMatches, state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (scrutinee,DAE.PAT_META_TUPLE(morePatterns)) :: restMatches + algorithm + listTypes := match scrutinee.ty + case DAE.T_METATUPLE(listTypes) then listTypes; + else algorithm Error.addInternalError("Wrong type of midvar in tuple pattern: " + DAEDump.daeTypeStr(scrutinee.ty) + ".\n", sourceInfo()); then fail(); + end match; + + moreMatches := {}; + iterator := morePatterns; + fieldNr := 0; + while not listEmpty(iterator) loop + midvar := RValueToVar(MidCode.METAFIELD(scrutinee,fieldNr,listHead(listTypes)),state); + moreMatches := (midvar, listHead(iterator)) :: moreMatches; + fieldNr := fieldNr + 1; + iterator := List.rest(iterator); + listTypes := List.rest(listTypes); + end while; + moreMatches := listReverse(moreMatches); + patternToMidCode2(matches = listAppend(moreMatches, restMatches), state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (scrutinee,DAE.PAT_SOME(pattern)) :: restMatches + algorithm + ok := GenTmpVar(DAE.T_BOOL_DEFAULT,state); + scrutineeCompareVar := RValueToVar(MidCode.ISSOME(scrutinee), state); + patCompareVar := RValueToVar(MidCode.LITERALBOOLEAN(true), state); + stateAddStmt(MidCode.ASSIGN(ok, MidCode.BINARYOP(MidCode.EQUAL(),scrutineeCompareVar, patCompareVar )), state); + stateAddBailOnFalse(ok, labelNoMatch, state); + + ty := match scrutinee.ty + case DAE.T_METAOPTION(ty=ty) + then ty; + else algorithm Error.addInternalError("Wrong type of midvar in option pattern.\n", sourceInfo()); then fail(); + end match; + + midvar := RValueToVar(MidCode.METAFIELD(scrutinee,0,ty),state); + patternToMidCode2( + matches = (midvar,pattern)::restMatches, + state=state, + assignBlock=assignBlock, + labelNoMatch=labelNoMatch + ); + then (); + + case (scrutinee,DAE.PAT_CONS(head=headPattern,tail=restPattern)) :: restMatches + algorithm + scrutineeCompareVar := RValueToVar(MidCode.ISCONS(scrutinee), state); + patCompareVar := RValueToVar(MidCode.LITERALBOOLEAN(true), state); + ok := GenTmpVar(DAE.T_BOOL_DEFAULT,state); + stateAddStmt(MidCode.ASSIGN(ok, MidCode.BINARYOP(MidCode.EQUAL(),scrutineeCompareVar, patCompareVar )), state); + stateAddBailOnFalse(ok, labelNoMatch, state); + + ty := match scrutinee.ty + case DAE.T_METALIST(ty=DAE.T_UNKNOWN()) + algorithm Error.addInternalError("Found list of unknown in cons pattern: " + DAEDump.daeTypeStr(scrutinee.ty) +".\n", sourceInfo()); then fail(); + case DAE.T_METALIST(ty=ty) + then ty; + else algorithm Error.addInternalError("Wrong type of midvar in option pattern.\n", sourceInfo()); then fail(); + end match; + + headVar := RValueToVar(MidCode.METAFIELD(scrutinee,0,ty),state); + restVar := RValueToVar(MidCode.METAFIELD(scrutinee,1,scrutinee.ty),state); + + patternToMidCode2( + matches=(headVar,headPattern)::(restVar,restPattern)::restMatches, + state=state, + assignBlock=assignBlock, + labelNoMatch=labelNoMatch + ); + + then (); + + case (scrutinee,DAE.PAT_CALL(name,index,morePatterns,fields,typeVars,knownSingleton)) :: restMatches + algorithm + // TODO: Is this correct usage of knownSingleton? + + if not knownSingleton + then + ok := GenTmpVar(DAE.T_BOOL_DEFAULT,state); + scrutineeCompareVar := RValueToVar(MidCode.UNIONTYPEVARIANT(scrutinee) , state); + patCompareVar := RValueToVar(MidCode.LITERALINTEGER(index) , state); + stateAddStmt(MidCode.ASSIGN(ok, MidCode.BINARYOP(MidCode.EQUAL(),scrutineeCompareVar, patCompareVar )), state); + stateAddBailOnFalse(ok, labelNoMatch, state); + end if; + + listTypes := list(v.ty for v in fields); + + moreMatches := {}; + iterator := morePatterns; + fieldNr := 1; + while not listEmpty(iterator) loop + midvar := RValueToVar(MidCode.METAFIELD(scrutinee,fieldNr,listHead(listTypes)),state); + moreMatches := (midvar, listHead(iterator)) :: moreMatches; + fieldNr := fieldNr + 1; + iterator := List.rest(iterator); + listTypes := List.rest(listTypes); + end while; + moreMatches := listReverse(moreMatches); + + patternToMidCode2(matches = listAppend(moreMatches, restMatches), state=state, assignBlock=assignBlock, labelNoMatch=labelNoMatch); + then (); + + case (_,DAE.PAT_AS_FUNC_PTR())::_ + algorithm + Error.addInternalError("DAE.Pattern to Mid conversion failed. Unimplemented pattern: PAT_AS_FUNC_PTR.\n", sourceInfo()); + then fail(); + case (_,DAE.PAT_CALL_TUPLE())::_ + algorithm + Error.addInternalError("DAE.Pattern to Mid conversion failed. Unimplemented pattern: PAT_CALL_TUPLE.\n", sourceInfo()); + then fail(); + case (_,DAE.PAT_CALL_NAMED())::_ + algorithm + Error.addInternalError("DAE.Pattern to Mid conversion failed. Unimplemented pattern: PAT_CALL_NAMED.\n", sourceInfo()); + then fail(); + else + algorithm + Error.addInternalError("DAE.Pattern to Mid conversion failed\n", sourceInfo()); + then fail(); + end match; +end patternToMidCode2; + +annotation(__OpenModelica_Interface="backend"); + +end DAEToMid; + diff --git a/Compiler/MidCode/HashTableMidVar.mo b/Compiler/MidCode/HashTableMidVar.mo new file mode 100644 index 0000000000..d00fd1e445 --- /dev/null +++ b/Compiler/MidCode/HashTableMidVar.mo @@ -0,0 +1,99 @@ +/* + * This file is part of OpenModelica. + * + * Copyright (c) 1998-2014, Open Source Modelica Consortium (OSMC), + * c/o Linköpings universitet, Department of Computer and Information Science, + * SE-58183 Linköping, Sweden. + * + * All rights reserved. + * + * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR + * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2. + * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES + * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, + * ACCORDING TO RECIPIENTS CHOICE. + * + * The OpenModelica software and the Open Source Modelica + * Consortium (OSMC) Public License (OSMC-PL) are obtained + * from OSMC, either from the above address, + * from the URLs: http://www.ida.liu.se/projects/OpenModelica or + * http://www.openmodelica.org, and in the OpenModelica distribution. + * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html. + * + * This program is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH + * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. + * + * See the full OSMC Public License conditions for more details. + * + */ + +encapsulated package HashTableMidVar + +/* Below is the instance specific code. For each hashtable the user must define: +Key - The key used to uniquely define elements in a hashtable +Value - The data to associate with each key +hashFunc - A function that maps a key to a positive integer. +keyEqual - A comparison function between two keys, returns true if equal. +*/ + +/* HashTable instance specific code */ + +public import BaseHashTable; +public import DAE; +public import MidCode; +protected import ComponentReference; + +public type Key = DAE.ComponentRef; +public type Value = MidCode.Var; + +public type HashTableCrefFunctionsType = tuple; +public type HashTable = tuple>>, + tuple>>>, + Integer, + HashTableCrefFunctionsType>; + +partial function FuncHashCref + input Key cr; + input Integer mod; + output Integer res; +end FuncHashCref; + +partial function FuncCrefEqual + input Key cr1; + input Key cr2; + output Boolean res; +end FuncCrefEqual; + +partial function FuncCrefStr + input Key cr; + output String res; +end FuncCrefStr; + +partial function FuncExpStr + input Value exp; + output String res; +end FuncExpStr; + +public function emptyHashTable +" + Returns an empty HashTable. + Using the default bucketsize.. +" + output HashTable hashTable; +algorithm + hashTable := emptyHashTableSized(BaseHashTable.defaultBucketSize); +end emptyHashTable; + +public function emptyHashTableSized +"Returns an empty HashTable. + Using the bucketsize size" + input Integer size; + output HashTable hashTable; +algorithm + hashTable := BaseHashTable.emptyHashTableWork(size,(ComponentReference.hashComponentRefMod,ComponentReference.crefEqual,ComponentReference.printComponentRefStr,MidCode.varString)); +end emptyHashTableSized; + +annotation(__OpenModelica_Interface="backend"); +end HashTableMidVar; diff --git a/Compiler/MidCode/MidCode.mo b/Compiler/MidCode/MidCode.mo new file mode 100644 index 0000000000..b208bfb65c --- /dev/null +++ b/Compiler/MidCode/MidCode.mo @@ -0,0 +1,269 @@ +/* + * This file is part of OpenModelica. + * + * Copyright (c) 1998-2014, Open Source Modelica Consortium (OSMC), + * c/o Linköpings universitet, Department of Computer and Information Science, + * SE-58183 Linköping, Sweden. + * + * All rights reserved. + * + * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR + * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2. + * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES + * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, + * ACCORDING TO RECIPIENTS CHOICE. + * + * The OpenModelica software and the Open Source Modelica + * Consortium (OSMC) Public License (OSMC-PL) are obtained + * from OSMC, either from the above address, + * from the URLs: http://www.ida.liu.se/projects/OpenModelica or + * http://www.openmodelica.org, and in the OpenModelica distribution. + * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html. + * + * This program is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH + * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. + * + * See the full OSMC Public License conditions for more details. + * + */ + +encapsulated package MidCode + +import DAE; +import DAEDump; + +uniontype Program + record PROGRAM + String name; + list functions; + end PROGRAM; +end Program; + +uniontype Var + record VAR + String name; + DAE.Type ty; + Boolean volatile "Used for setjmp semantics in C."; // Codegen detail that doesn't really belong in midcode. + end VAR; +end Var; + +uniontype VarBuf + /* + A MidCode variable containing a jmp_buf for longjmp. + */ + record VARBUF + String name; + end VARBUF; +end VarBuf; + +uniontype VarBufPtr + /* + A MidCode variable containing a jmp_buf pointer for longjmp. + */ + record VARBUFPTR + String name; + end VARBUFPTR; +end VarBufPtr; + +uniontype OutVar + record OUT_VAR + Var var; + end OUT_VAR; + record OUT_WILD end OUT_WILD; +end OutVar; + +public function varString + input Var var; + output String str; +algorithm + str := "(" + DAEDump.daeTypeStr(var.ty) + ") " + var.name; +end varString; + +uniontype Function + record FUNCTION + Absyn.Path name; + list locals; + list localBufs; // jmb_buf for longjmp + list localBufPtrs; // jmb_buf pointers for longjmp + list inputs; + list outputs; + list body; + Integer entryId; + Integer exitId; + end FUNCTION; +end Function; + +uniontype Block + record BLOCK + "Basic block. + No control flow within block. + Can branch or jump on exit, called the block's terminator." + Integer id; + list stmts; + Terminator terminator; + end BLOCK; +end Block; + +uniontype Terminator + record GOTO + Integer next; + end GOTO; + + record BRANCH + Var condition; + Integer onTrue; + Integer onFalse; + end BRANCH; + + record CALL + Absyn.Path func; + Boolean builtin; //vilka övriga CallAttributes relevanta? + list inputs; + list outputs; + Integer next; + end CALL; + + record RETURN + end RETURN; + + record SWITCH + Var condition; + list> cases; + end SWITCH; + + record LONGJMP "used for fail() stmts" + end LONGJMP; + + record PUSHJMP "used for match-continue fail() handling" + VarBufPtr old_buf "where to save old jmp_buf"; + VarBuf new_buf "what to use as new jmp_buf"; + Integer next "where to goto next and the setjmp target"; + end PUSHJMP; + + /* POPJMP does not cause control flow but + if it is a terminator to simplify matching + with their respective PUSHJMPS. + */ + record POPJMP "used for match-continue fail() handling" + VarBufPtr old_buf "what to reset to"; + Integer next; + end POPJMP; + + record ASSERT + Var condition; + Var message; + Var level; + Integer next; + end ASSERT; + + record TERMINATE + Var message; + end TERMINATE; + +end Terminator; + +uniontype Stmt + record NOP + end NOP; + + record ASSIGN + Var dest; + RValue src; + end ASSIGN; +end Stmt; + +uniontype RValue + record VARIABLE + Var src; + end VARIABLE; + + record UNARYOP + UnaryOp op; + Var src; + end UNARYOP; + + record BINARYOP + BinaryOp op; + Var lsrc; + Var rsrc; + end BINARYOP; + + record LITERALINTEGER + Integer value; + end LITERALINTEGER; + + record LITERALREAL + Real value; + end LITERALREAL; + + record LITERALBOOLEAN + Boolean value; + end LITERALBOOLEAN; + + record LITERALSTRING + String value; + end LITERALSTRING; + + record LITERALMETATYPE + list elements; + DAE.Type ty; + end LITERALMETATYPE; + + + +/* +CTOR SLOTS +0 0 nil för list +0 1+ tuple +1 0 none för option +1 1 some för option +1 2 cons för list +2 0+ array +3+ 0+ record +*/ + + record UNIONTYPEVARIANT + Var src; + end UNIONTYPEVARIANT; + + record ISSOME + Var src; + end ISSOME; + + record ISCONS + Var src; + end ISCONS; + + record METAFIELD "get value from metamodelica object" + Var src; + Integer index; + DAE.Type ty "type of value"; + end METAFIELD; +end RValue; + +uniontype UnaryOp + record MOVE end MOVE; + record UMINUS end UMINUS; + record NOT end NOT; + record UNBOX end UNBOX; + record BOX end BOX; +end UnaryOp; + +uniontype BinaryOp + record ADD end ADD; + record SUB end SUB; + record MUL end MUL; + record DIV end DIV; + record POW end POW; + record LESS end LESS; + record LESSEQ end LESSEQ; + record GREATER end GREATER; + record GREATEREQ end GREATEREQ; + record EQUAL end EQUAL; + record NEQUAL end NEQUAL; +end BinaryOp; + +annotation(__OpenModelica_Interface="backend"); +end MidCode; diff --git a/Compiler/MidCode/MidToMid.mo b/Compiler/MidCode/MidToMid.mo new file mode 100644 index 0000000000..db422ad31a --- /dev/null +++ b/Compiler/MidCode/MidToMid.mo @@ -0,0 +1,220 @@ +/* + * This file is part of OpenModelica. + * + * Copyright (c) 1998-2018, Open Source Modelica Consortium (OSMC), + * c/o Linköpings universitet, Department of Computer and Information Science, + * SE-58183 Linköping, Sweden. + * + * All rights reserved. + * + * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR + * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2. + * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES + * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, + * ACCORDING TO RECIPIENTS CHOICE. + * + * The OpenModelica software and the Open Source Modelica + * Consortium (OSMC) Public License (OSMC-PL) are obtained + * from OSMC, either from the above address, + * from the URLs: http://www.ida.liu.se/projects/OpenModelica or + * http://www.openmodelica.org, and in the OpenModelica distribution. + * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html. + * + * This program is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH + * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. + * + * See the full OSMC Public License conditions for more details. + * + */ + +encapsulated package MidToMid + +public +import MidCode; + +/* +Longjmps are not allowed to land in the same function. +This is handled in midtomid. +Handling it here allows other tranformations to +deal with goto instead of longjmp, which might enable +further transformation. + +pushpopjmp possible. +can remove push-pop -jmp pairs if there is no possible longjmp in between. + +Typechecking possible. +Useful for correctness of midcode transformations. + +Normalisation possble. (AKA canonicalisation) +Probably essential to simplify other transformations. +Remove greater than comparisons and similar. + +Inlining possible. +Important catalyst for other optimisations. + +Common subexpression elimination possible. +But requires some data flow and side effect analysis. +Some SSA variables and purity marked functions perhaps. + +*/ + + +function longJmpGoto + " + Replace a longjmp within a function call with goto. + longjmp within function calls isn't allowed so + this is necessary to form correct c form midcode. + " + input MidCode.Function oldFunction; + output MidCode.Function newFunction; +protected + list newBody, oldBody; + MidCode.Block newBlock, oldBlock; + Integer node,jump; + list jumps; + list nodes_tmp, checkedNodes; + list,Integer>> tasks, tasks_tmp; // [ ([Int], Int) ] +algorithm + /* + do depth first search + keep a stack of jmp_targets + when encoutering a PUSHJMP: push the target label onto jmp_targets + when encountering a LONGJMP and jmp_targets in't empty: replace with goto the top of jmp_targets + when encountering a POPJMP: pop jmp_targets + */ + /* + How to depth first + maybe not use program stack ~1000 blocks might cause stack overflow + so jmp_targets is a list + while loop with empty check on nodes + start wih entryId-block + keep list of used ids, alreadyChecked + need to look up blockId in Function structure + find the successors of current node, add all that are not in alreadyChecked + build a new list of blocks with terminators maybe replaced + replace the body part of function + */ + /* + Leave the buffer variables in the function still declared. + */ + oldBody := oldFunction.body; + newBody := {}; + checkedNodes := {oldFunction.entryId}; + tasks := {({},oldFunction.entryId)}; + + while not listEmpty(tasks) loop + ((jumps,node) :: tasks) := tasks; // pop + oldBlock := lookupId(oldBody,node); // O(length(oldBody)) + newBlock := oldBlock; // don't change the block by defualt + if isPushJmp(oldBlock.terminator) then + jumps := (listHead(getSuccessors(oldBlock)) :: jumps); // push + elseif isLongJmp(oldBlock.terminator) and not listEmpty(jumps) then + (jump :: _) := jumps; // peek + newBlock := MidCode.BLOCK(id=oldBlock.id,stmts=oldBlock.stmts, terminator=MidCode.GOTO(jump)); + elseif isPopJmp(oldBlock.terminator) then + (_ :: jumps) := jumps; // pop + end if; + newBody := newBlock :: newBody; + + nodes_tmp := List.setDifference(getSuccessors(oldBlock), checkedNodes); + checkedNodes := listAppend(nodes_tmp, checkedNodes); + tasks_tmp := list( (jumps,node_tmp) for node_tmp in nodes_tmp ); + tasks := listAppend(tasks_tmp, tasks); + end while; + newBody := listReverse(newBody); + + newFunction := MidCode.FUNCTION( + name=oldFunction.name + ,locals=oldFunction.locals + ,localBufs=oldFunction.localBufs + ,localBufPtrs=oldFunction.localBufPtrs + ,inputs=oldFunction.inputs + ,outputs=oldFunction.outputs + ,body=newBody + ,entryId=oldFunction.entryId + ,exitId=oldFunction.exitId + ); +end longJmpGoto; + +function lookupId + input list blocks; + input Integer id; + output MidCode.Block block_; +protected + list blocks_local; + MidCode.Block block_local; +algorithm + block_ := match blocks + case (block_local :: _) guard (block_local.id == id) then block_local; + case (_ :: blocks_local) then lookupId(blocks_local, id); + //else listHead(blocks); + end match; +end lookupId; + +protected +import SimCode; +import SimCodeFunction; +import DAE; +import List; + +function getSuccessors + input MidCode.Block block_; + output list neighbours; +protected + Integer l0,l1; + list> switchList; +algorithm + neighbours := match block_.terminator + case MidCode.GOTO(l0) then {l0}; + case MidCode.BRANCH(_,l0,l1) then {l0,l1}; + case MidCode.CALL(_,_,_,_,l0) then {l0}; + case MidCode.RETURN() then {}; + case MidCode.SWITCH(_,switchList) then list(tupleSnd(x) for x in switchList); + case MidCode.LONGJMP() then {}; + case MidCode.PUSHJMP(_,_,l0) then {l0}; + case MidCode.POPJMP(_,l0) then {l0}; + end match; +end getSuccessors; + +function tupleSnd + input tuple t; + output Integer i; +algorithm + (_,i) := t; +end tupleSnd; + +function isLongJmp + input MidCode.Terminator t; + output Boolean b; +algorithm + b := match t + case MidCode.LONGJMP() then true; + else false; + end match; +end isLongJmp; + +function isPushJmp + input MidCode.Terminator t; + output Boolean b; +algorithm + b := match t + case MidCode.PUSHJMP(_,_,_) then true; + else false; + end match; +end isPushJmp; + +function isPopJmp + input MidCode.Terminator t; + output Boolean b; +algorithm + b := match t + case MidCode.POPJMP(_,_) then true; + else false; + end match; +end isPopJmp; + +annotation(__OpenModelica_Interface="backend"); + +end MidToMid; diff --git a/Compiler/Script/CevalScript.mo b/Compiler/Script/CevalScript.mo index 7c692b6e4d..55069ae925 100644 --- a/Compiler/Script/CevalScript.mo +++ b/Compiler/Script/CevalScript.mo @@ -2420,7 +2420,9 @@ algorithm print_debug := Flags.isSet(Flags.DYN_LOAD); libHandle := System.loadLibrary(fileName, print_debug); funcHandle := System.lookupFunction(libHandle, stringAppend("in_", funcstr)); + execStatReset(); newval := DynLoad.executeFunction(funcHandle, vallst, print_debug); + execStat("executeFunction("+Absyn.pathString(funcpath)+")"); System.freeLibrary(libHandle, print_debug); // update the build time in the class! diff --git a/Compiler/SimCode/SimCodeFunction.mo b/Compiler/SimCode/SimCodeFunction.mo index faa5dd5ae4..e41f1a6ad5 100644 --- a/Compiler/SimCode/SimCodeFunction.mo +++ b/Compiler/SimCode/SimCodeFunction.mo @@ -248,8 +248,11 @@ constant list boxedRecordOutVars = VARIABLE(DAE.CREF_IDENT("",DAE.T_CO protected import BaseHashTable; import CodegenCFunctions; +import CodegenMidToC; import Global; import SimCodeFunctionUtil; +import MidCode; +import DAEToMid; public function translateFunctions " Entry point to translate Modelica/MetaModelica functions to C functions. @@ -262,6 +265,7 @@ public function translateFunctions " input list inIncludes; algorithm setGlobalRoot(Global.optionSimCode, NONE()); + _ := match (program, name, optMainFunction, idaeElements, metarecordTypes, inIncludes) local DAE.Function daeMainFunction; @@ -273,7 +277,8 @@ algorithm list extraRecordDecls; list literals; list daeElements; - + Tpl.Text midCode; + list midfuncs; case (_, _, SOME(daeMainFunction), daeElements, _, includes) equation // Create FunctionCode @@ -282,8 +287,15 @@ algorithm SimCodeFunctionUtil.checkValidMainFunction(name, mainFunction); makefileParams = SimCodeFunctionUtil.createMakefileParams(includeDirs, libs, libPaths, true); fnCode = FUNCTIONCODE(name, SOME(mainFunction), fns, literals, includes, makefileParams, extraRecordDecls); - // Generate code - _ = Tpl.tplString(CodegenCFunctions.translateFunctions, fnCode); + + if Config.simCodeTarget() == "MidC" then + _ = Tpl.tplString(CodegenCFunctions.translateFunctionHeaderFiles, fnCode); + midfuncs = DAEToMid.DAEFunctionsToMid(mainFunction::fns); + midCode = Tpl.tplCallWithFailError(CodegenMidToC.genProgram, MidCode.PROGRAM(name, midfuncs)); + _ = Tpl.textFileConvertLines(midCode, name + ".c"); + else + _ = Tpl.tplString(CodegenCFunctions.translateFunctions, fnCode); + end if; then (); case (_, _, NONE(), daeElements, _, includes) @@ -295,12 +307,19 @@ algorithm // remove OpenModelica.threadData.ThreadData fns = removeThreadDataFunction(fns, {}); extraRecordDecls = removeThreadDataRecord(extraRecordDecls, {}); - fnCode = FUNCTIONCODE(name, NONE(), fns, literals, includes, makefileParams, extraRecordDecls); - // Generate code - _ = Tpl.tplString(CodegenCFunctions.translateFunctions, fnCode); + + if Config.simCodeTarget() == "MidC" then + _ = Tpl.tplString(CodegenCFunctions.translateFunctionHeaderFiles, fnCode); + midfuncs = DAEToMid.DAEFunctionsToMid(fns); + midCode = Tpl.tplCallWithFailError(CodegenMidToC.genProgram, MidCode.PROGRAM(name, midfuncs)); + _ = Tpl.textFileConvertLines(midCode, name + ".c"); + else + _ = Tpl.tplString(CodegenCFunctions.translateFunctions, fnCode); + end if; then (); + end match; end translateFunctions; diff --git a/Compiler/Template/CodegenCFunctions.tpl b/Compiler/Template/CodegenCFunctions.tpl index 54d260df92..c309399d87 100644 --- a/Compiler/Template/CodegenCFunctions.tpl +++ b/Compiler/Template/CodegenCFunctions.tpl @@ -142,6 +142,22 @@ end mainTop; end match end translateFunctions; +template translateFunctionHeaderFiles(FunctionCode functionCode) +::= + match functionCode + case fc as FUNCTIONCODE(__) then + let()= System.tmpTickResetIndex(0,2) /* auxFunction index */ + let()= System.tmpTickResetIndex(0,20) /*parfor index*/ + let &staticPrototypes = buffer "" + let filePrefix = name + let _= (if mainFunction then textFile(functionsMakefile(functionCode), '<%filePrefix%>.makefile')) + let()= textFile(functionsHeaderFile(filePrefix, mainFunction, functions, extraRecordDecls, staticPrototypes), '<%filePrefix%>.h') + let()= textFile(externalFunctionIncludes(fc.externalFunctionIncludes), '<%filePrefix%>_includes.h') + let()= textFile(recordsFile(filePrefix, extraRecordDecls), '<%filePrefix%>_records.c') + "" // Return empty result since result written to files directly + end match +end translateFunctionHeaderFiles; + template functionsFile(String filePrefix, Option mainFunction, list functions, diff --git a/Compiler/Template/CodegenMidToC.tpl b/Compiler/Template/CodegenMidToC.tpl new file mode 100644 index 0000000000..62dac54ad1 --- /dev/null +++ b/Compiler/Template/CodegenMidToC.tpl @@ -0,0 +1,739 @@ +package CodegenMidToC + +// import CodegenUtil.*; +import interface MidCodeTV; +import interface SimCodeTV; + +/* +maybe do +- make a genGoto that generates empty statement if target is next block +*/ + +template genProgram(MidCode.Program p) +::= + match p + case MidCode.PROGRAM(__) then + << + // number of functions: <% listLength(functions) %> + #include "<%name%>.h" + #include "util/modelica.h" + + #include "<%name%>_includes.h" + + <% functions |> fn => genFunction(fn) ; separator="\n\n"%> + >> + end match +end genProgram; + +/* +template genGlobalDef(Literal lit) +::= + match lit + case LITSTRING(var=var,value_=value_) then + let name = genVarName(var) + let dataName = <<_OMC_LIT_DATA_<%name%>>> //<<_OMC_LIT<%name%>_data>> + let structName = <<_OMC_LIT_STRUCT_<%name%>>>//<<_OMC_LIT_STRUCT<%name%>>> + let escaped = Util.escapeModelicaStringToCString(value_) + let escapedLength = System.unescapedStringLength(escaped) // =listLength(value_)? + << + #define <%dataName%> "<%escaped%>" + static const MMC_DEFSTRINGLIT(<%structName%>,<%escapedLength%>,<%dataName%>); + #define <%name%> MMC_REFSTRINGLIT(<%structName%>) + + >> + case LITRECORD(var=var,args=args) then + match var case MidCode.VAR(name=_,ty =ty as DAE.T_METARECORD(__)) then + let name = genVarName(var) + let dataName = <<_OMC_LIT_DATA_<%name%>>> + let structName = <<_OMC_LIT_STRUCT_<%name%>>> + << + static const MMC_DEFSTRUCTLIT(<%structName%>,<%ty.index%>,<%listLength(args)%>) {<%args |> arg => genVarName(arg) ; separator=","%>}}; + #define <%name%> MMC_REFSTRUCTLIT(<%structName%>) + + >> + end match + else "notimplemented" + end match +end genGlobalDef; +*/ + +template genFunction(MidCode.Function fn) + "Generates the body for a Modelica/MetaModelica function." +::= + /* + The function should accept a thread_data argument. + The inputs are normal arguments. + The first output is the return value of the function. + The rest of the outputs are passed as pointers to the funtion. + If there is no output return void? + */ + match fn + case MidCode.FUNCTION(__) then + let returnType = if listEmpty(outputs) then "void" else genVarType(listHead(outputs)) + + let arguments = { + ("threadData_t *threadData"), + (inputs |> i => <<<%genVarType(i)%> <%genVarName(i)%>>> ; separator=", "), + (List.restOrEmpty(outputs) |> o => <<<%genVarType(o)%> *outPtr_<%genVarName(o)%>>> ; separator=", ") // TODO: this might not be a great way to pointerify a type + } ; separator=",\n " + + << + <%returnType%> omc_<%underscorePath(name)%>(<%arguments%>) + { + <% genLocalDecls(fn, locals, localBufs, localBufPtrs)%> + <% genEntry(fn) %> + <% genBlocks(fn,body) %> + <% genExit(fn) %> + } + + <%genInFunction(fn)%> + <%genBoxPtrFunction(fn)%> + + >> + + end match +end genFunction; + +template genInFunction(MidCode.Function fn) +::= + match fn + case MidCode.FUNCTION(__) then + let inputDefs = (inputs |> i => <<<%genVarType(i)%> <%genVarName(i)%>;>>; separator="\n") + let outputDefs = (outputs |> o => <<<%genVarType(o)%> <%genVarName(o)%>;>>; separator="\n") + let inputLines = (inputs |> i => <) return 1;>>; separator="\n") + let outputLines = (outputs |> o => <<<%varModelicaWrite(o)%>;>>; separator="\n") + let callretval = if not listEmpty(outputs) then <<<%genVarName(listHead(outputs))%> = >> + let callargs = { + ("threadData"), + (inputs |> i => <<<%genVarName(i)%>>> ; separator=", "), + (List.restOrEmpty(outputs) |> o => <<&<%genVarName(o)%>>> ; separator=", ") + } ; separator=",\n " + << + int in_<%underscorePath(name)%>(threadData_t *threadData, type_description *inArgs, type_description *outVar) + { + <%inputDefs%> + <%outputDefs%> + <%inputLines%> + MMC_TRY_TOP_INTERNAL() + <%callretval%>omc_<%underscorePath(name)%>(<%callargs%>); + MMC_CATCH_TOP(return 1) + <%outputLines%> + <%if listEmpty(outputs) then "write_noretcall(outVar);"%> + fflush(NULL); + return 0; + } + >> + end match +end genInFunction; + +template genBoxPtrFunction(MidCode.Function fn) +::= + match fn + case MidCode.FUNCTION(__) then + let returnType = if listEmpty(outputs) then "void" else "modelica_metatype" + + let arguments = { + ("threadData_t *threadData"), + (inputs |> i => <>> ; separator=", "), + (List.restOrEmpty(outputs) |> o => <>> ; separator=", ") + } ; separator=",\n " + + let unboxDefs = ( inputs |> i => + if varBoxType(i) then <<<%genVarType(i)%> unbox_<%genVarName(i)%>;>> ; separator="\n") + + let callOutDefs = ( outputs |> o => + if varBoxType(o) then <<<%genVarType(o)%> <%genVarName(o)%>;>> ; separator="\n") + + let boxDefs = if not listEmpty(outputs) then <;<%"\n"%>>> + + let unboxes = ( inputs |> i => + if varBoxType(i) then < = <%varUnbox2(i)%>;>> ; separator="\n") + + let callretval = if not listEmpty(outputs) then <<<%if not varBoxType(listHead(outputs)) then "out_"%><%genVarName(listHead(outputs))%> = >> + let callvars = { + ("threadData"), + (inputs |> i => <<<%if varBoxType(i) then "unbox_"%><%genVarName(i)%>>> ; separator=", "), + (List.restOrEmpty(outputs) |> o => <<<%if varBoxType(o) then "&" else "out_"%><%genVarName(o)%>>> ; separator=", ") + } ; separator=",\n " + + let boxes = { + (if not listEmpty(outputs) then (if varBoxType(listHead(outputs)) then < = <%varBox(listHead(outputs))%>;>>)), + (List.restOrEmpty(outputs) |> o => + if varBoxType(o) then <) *out_<%genVarName(o)%> = <%varBox(o)%>;>> ) + } ; separator="\n" + + << + #undef boxptr_<%underscorePath(name)%> + <%returnType%> boxptr_<%underscorePath(name)%>(<%arguments%>) + { + <%unboxDefs%> + <%callOutDefs%> + <%boxDefs%> + <%unboxes%> + <%callretval%>omc_<%underscorePath(name)%>(<%callvars%>); + <%boxes%> + <% if listEmpty(outputs) then "return;" else "return out_" + genVarName(listHead(outputs)) + ";" %> + } + >> + end match +end genBoxPtrFunction; + +template genLocalDecls(MidCode.Function fn, list locals, list localBufs, list localBufPtrs) +::= + << + <% locals |> local => genLocalDecl(fn,local) ; separator="\n" %> + <% localBufs |> local => genLocalBufDecl(fn,local) ; separator="\n" %> + <% localBufPtrs |> local => genLocalBufPtrDecl(fn,local) ; separator="\n" %> + >> +end genLocalDecls; + +template genLocalDecl(MidCode.Function fn, MidCode.Var var) +::= + << + <% match var case MidCode.VAR(volatile=true) then "volatile " end match %><% genVarType(var) %> <% genVarName(var) %>; + >> +end genLocalDecl; + +template genLocalBufDecl(MidCode.Function fn, MidCode.VarBuf var) +::= + << + jmp_buf <% genVarBufName(var) %>; + >> +end genLocalBufDecl; + +template genLocalBufPtrDecl(MidCode.Function fn, MidCode.VarBufPtr var) +::= + << + jmp_buf *<% genVarBufPtrName(var) %>; + >> +end genLocalBufPtrDecl; + +template genEntry(MidCode.Function fn) + "" +::= + match fn + case FUNCTION(__) then + << + goto <% genLabel(entryId) %>; + >> + end match +end genEntry; + +template genExit(MidCode.Function fn) + "" +::= + match fn + case FUNCTION(__) then + let returnString = if listEmpty(outputs) then "" else genVarName(listHead(outputs)) + << + <% genLabel(exitId) %>: // exit block + <% List.restOrEmpty(outputs) |> v => + let outPtrName = "outPtr_"+ genVarName(v) + << + if (<%outPtrName%> != NULL) + *<%outPtrName%> = <%genVarName(v)%>; + >> ; separator="\n" + %> + return <%returnString%>; + >> + end match +end genExit; + +template genBlocks(MidCode.Function fn, list body) + "" +::= + body |> block => genBlock(fn, block) ; separator="\n" +end genBlocks; + +template genBlock(MidCode.Function fn, MidCode.Block block) + "" +::= + match block + case BLOCK(__) then + << + <% genLabel(id) %>: + <% stmts |> stmt => genStmt(fn, stmt) ; separator="\n" %> + <% genTerminator(fn, terminator) %> + >> + end match +end genBlock; + +template genLabel(Integer i) +::= + << + label_<% i %> + >> +end genLabel; + +template genVarName(MidCode.Var v) +::= + match v + case MidCode.VAR(__) + then name +end genVarName; + +template genVarBufName(MidCode.VarBuf v) +::= + match v + case MidCode.VARBUF(__) + then name +end genVarBufName; + +template genVarBufPtrName(MidCode.VarBufPtr v) +::= + match v + case MidCode.VARBUFPTR(__) + then name +end genVarBufPtrName; + +template genStmt(MidCode.Function fn, MidCode.Stmt stmt) + "" +::= + match stmt + case MidCode.NOP(__) then + << + ; // NOP + >> + case MidCode.ASSIGN( + dest=MidCode.VAR(name=dest_name,ty=_) + ,src=rvalue + ) then + << + <%dest_name%> = <%genRValue(rvalue)%>; + >> + end match +end genStmt; + +template genRValue(MidCode.RValue rvalue) + "" +::= + match rvalue + case MidCode.VARIABLE(src=src as MidCode.VAR(name=src_name, ty=_)) then + << + <%src_name%> + >> + case MidCode.BINARYOP( + op=op + ,lsrc=lsrc as MidCode.VAR(name=lsrc_name,ty=DAE.T_STRING(__)) + ,rsrc=rsrc as MidCode.VAR(name=rsrc_name,ty=DAE.T_STRING(__)) + ) then + genStringBinaryop(op,lsrc,rsrc) + case MidCode.BINARYOP( + op=MidCode.POW() + ,lsrc=MidCode.VAR(name=lsrc_name, ty=_) + ,rsrc=MidCode.VAR(name=rsrc_name, ty=_)) then + // TODO: use custom pow for modelica semantics CodegenCFunctions.tpl:4889 + << + pow(<%lsrc_name%>, <%rsrc_name%>) + >> + case MidCode.BINARYOP( // c binary ops + op=op + ,lsrc=MidCode.VAR(name=lsrc_name, ty=_) + ,rsrc=MidCode.VAR(name=rsrc_name, ty=_)) then + << + <%lsrc_name%> <%binaryopToString(op)%> <%rsrc_name%> + >> + case MidCode.UNARYOP(op=MidCode.BOX(), src=src) then + varBox(src) + case MidCode.UNARYOP(op=MidCode.UNBOX(), src=src) then + varUnbox(src) + case MidCode.UNARYOP(op=op, src=src) then + << + (<%genVarType(src)%>) <%unaryopToString(op)%> <%genVarName(src)%> + >> + case MidCode.LITERALINTEGER(value=value) then + << + <%value%> + >> + case MidCode.LITERALBOOLEAN(value=value) then + << + <%if value then 1 else 0%> + >> + case MidCode.LITERALREAL(value=value) then + << + <%value%> + >> + case MidCode.LITERALSTRING(value=value) then + << + mmc_mk_scon("<%Util.escapeModelicaStringToCString(value)%>") + >> + case MidCode.LITERALMETATYPE(elements=elements,ty=ty) then + let metatypeSlots = listLength(elements) + let metatypeCtor = genTypeCtorIndex(elements, ty) + let elementargs = (elements |> element => <<<%genVarName(element)%>>> ; separator=", ") + + match ty + case DAE.T_METARECORD(__) then + let arguments = { + (<<<%metatypeSlots%>+1>>), + (metatypeCtor), + (<<&<%genTypeUnderscorePath(ty)%>__desc>>), + elementargs + } ; separator=", " + <)>> + else + let arguments = { + (<<<%metatypeSlots%>>>), + (metatypeCtor), + elementargs + } ; separator=", " + <)>> + + case MidCode.METAFIELD(src=src, index=index, ty=ty) then + <),<%intAdd(index,1)%>))>> + case MidCode.UNIONTYPEVARIANT() then + <<(MMC_HDRCTOR(MMC_GETHDR(<%genVarName(src)%>)) - 3)>> + case MidCode.ISSOME(src=src) then + <<(0==MMC_HDRSLOTS(MMC_GETHDR(<%genVarName(src)%>)) ? 0 : 1)>> + case MidCode.ISCONS(src=src) then + <<(MMC_GETHDR(<%genVarName(src)%>) == MMC_CONSHDR)>> + else "notimplemented" + end match +end genRValue; + +template binaryopToString(MidCode.BinaryOp op) +::= + match op + case MidCode.ADD() then "+" + case MidCode.SUB() then "-" + case MidCode.MUL() then "*" + case MidCode.DIV() then "/" + // pow + case MidCode.LESS() then "<" + case MidCode.LESSEQ() then "<=" + case MidCode.GREATER() then ">" + case MidCode.GREATEREQ() then ">=" + case MidCode.EQUAL() then "==" + case MidCode.NEQUAL() then "!=" + else "notimplemented" + end match +end binaryopToString; + +template unaryopToString(MidCode.UnaryOp op) +::= + match op + case MidCode.MOVE() then "" + case MidCode.UMINUS() then "-" + case MidCode.NOT() then "!" + else "notimplemented" + end match +end unaryopToString; + +template genStringBinaryop(MidCode.BinaryOp op, MidCode.Var lsrc, MidCode.Var rsrc) +::= + match op + case MidCode.ADD() then + << + stringAppend(<%genVarName(lsrc)%>, <%genVarName(rsrc)%>) + >> + else + // TODO: + // error checking. sub,mul + // could optimise equality? CodegenCFunctions.tpl:5163 + << + 0 <%binaryopToString(op)%> stringCompare(<%genVarName(lsrc)%>, <%genVarName(rsrc)%>) + >> + end match +end genStringBinaryop; + + +template genTerminator(MidCode.Function fn, MidCode.Terminator terminator) + "" +::= + match fn case FUNCTION(locals=_,inputs=_,outputs=_,body=_,entryId=_,exitId=exitId) then + // big match expression I guess + match terminator + case MidCode.RETURN() then + << + goto <% genLabel(exitId) %>; // exit label + >> + case MidCode.GOTO(next=label) then + << + goto <% genLabel(label) %>; + >> + case MidCode.BRANCH(condition=condition,onTrue=labelTrue, onFalse=labelFalse) then + << + if (<%genVarName(condition)%>) + goto <% genLabel(labelTrue) %>; + else + goto <% genLabel(labelFalse) %>; + >> + case MidCode.SWITCH(condition=condition, cases=cases) then + << + switch (<%genVarName(condition)%>){ + <% cases |> (from,to) => + << + case <%from%>: goto <%genLabel(to)%>; + >> + ; separator="\n" %> + } + >> + case MidCode.CALL(func=func,builtin=builtin,inputs=inputs,outputs=outputs,next=next) then + let returnAssignment = match outputs + case {} then "" + case MidCode.OUT_WILD() :: _ then "" + case MidCode.OUT_VAR(var=var) :: _ then <<<%genVarName(var)%> = >> + + let arguments = { + (if not builtin then "threadData"), + (inputs |> i => genVarName(i) ; separator=", "), + (List.restOrEmpty(outputs) |> o => match o case MidCode.OUT_VAR(var=var) then <<&<%genVarName(var)%>>> else "NULL" ; separator=", ") + } ; separator=",\n " + + << + <%returnAssignment%><%if not builtin then ("omc_" + underscorePath(func)) else identBuiltinCall(func)%>(<%arguments%>); + goto <%genLabel(next)%>; + >> + /* SimulationRuntime/c/meta/meta_modelica_data.h + */ + case MidCode.LONGJMP() then + // old codegen does fail-goto substitution here + // INFO: not allowed to throw to same function + << + longjmp(*threadData->mmc_jumper,1); + >> + case MidCode.PUSHJMP(__) then + << + // PUSHJMP + <% genVarBufPtrName(old_buf) %> = threadData->mmc_jumper; + threadData->mmc_jumper = &<% genVarBufName(new_buf) %>; + setjmp(&<% genVarBufName(new_buf) %>); + goto <%genLabel(next)%>; + >> + case MidCode.POPJMP(__) then + << + // POPJMP + threadData->mmc_jumper = <% genVarBufPtrName(old_buf) %>; + goto <%genLabel(next)%>; + >> + else "notimplemented" + end match +end genTerminator; + +template genVarType(Var var) + "Generate the c type for a variable." +::= + match var case VAR(name=_,ty=ty) then + match ty + case DAE.T_INTEGER(__) + case DAE.T_ENUMERATION(__) then + "modelica_integer" + case DAE.T_BOOL(__) then + "modelica_boolean" + case DAE.T_REAL(__) then + "modelica_real" + case DAE.T_STRING(__) then + "modelica_string" + case DAE.T_METABOXED(__) + case DAE.T_METARECORD(__) + case DAE.T_METATYPE(__) + case DAE.T_METAOPTION(__) + case DAE.T_METAARRAY(__) + case DAE.T_METATUPLE(__) + case DAE.T_METAUNIONTYPE(__) + case DAE.T_METALIST(__) then + "modelica_metatype" + case DAE.T_UNKNOWN() then + "unknown" //TODO: fail? + case DAE.T_METAPOLYMORPHIC(__) then + "error_ metapolymorphic" + case DAE.T_METAUNIONTYPE(__) then + "error_ metauniontype" + case DAE.T_ANYTYPE(__) then + "error_anytype" + case DAE.T_TUPLE(__) then + "error_tuple" + else "notimplemented" + end match + end match +end genVarType; + +template genTypeCtorIndex(list elements, DAE.Type ty) + "Generate the c-tag that indicates which record of a uniontype we have." +::= + match ty + + case DAE.T_METARECORD(__) then intAdd(index,3) + case DAE.T_METAARRAY(__) then 2 + case DAE.T_METATUPLE(__) then 0 + case DAE.T_METAOPTION(__) then 1 + case DAE.T_METALIST(__) then (if listLength(elements) then 1 else 0) + else 0 + end match +end genTypeCtorIndex; + + + +template genTypeUnderscorePath(DAE.Type ty) + "generate underscored path from type" +::= + // + match ty + case T_METARECORD(path=path) + then underscorePath(path) + case T_COMPLEX(complexClassType = RECORD(path = path), varLst = _) + then underscorePath(path) + else "error: genTypeUnderscorePath" + end match +end genTypeUnderscorePath; + +template varBoxType(MidCode.Var var) +::= + match var case VAR(name=_,ty=ty) then + match ty + case T_INTEGER(__) + case T_ENUMERATION(__) + case T_BOOL(__) + case T_REAL(__) then 'modelica_metatype' + end match + end match +end varBoxType; + +template varBox(MidCode.Var var) +::= + match var case VAR(name=name,ty=ty) then + match ty + case T_INTEGER(__) + case T_ENUMERATION(__) then 'mmc_mk_icon(<%name%>)' + case T_BOOL(__) then 'mmc_mk_icon(<%name%>)' + case T_REAL(__) then 'mmc_mk_rcon(<%name%>)' + case T_STRING(__) then 'mmc_mk_string(<%name%>)' + case T_COMPLEX(__) then 'mmc_mk_box(<%name%>)' //? + else name + end match + end match +end varBox; + + +//TODO: split into two functions +//non-metaboxed case only necessary for boxptr/in functions +//other should be used for normal code +template varUnbox(MidCode.Var var) +::= + match var case VAR(name=name,ty=ty) then + match ty + case T_METABOXED(ty=T_INTEGER(__)) + case T_METABOXED(ty=T_ENUMERATION(__))then 'mmc_unbox_integer(<%name%>)' + case T_METABOXED(ty=T_BOOL(__)) then 'mmc_unbox_integer(<%name%>)' + case T_METABOXED(ty=T_REAL(__)) then 'mmc_unbox_real(<%name%>)' + case T_STRING(__) then 'mmc_unbox_string(<%name%>)' + else name + end match + end match +end varUnbox; + +template varUnbox2(MidCode.Var var) +::= + match var case VAR(name=name,ty=ty) then + match ty + case T_INTEGER(__) + case T_ENUMERATION(__) then 'mmc_unbox_integer(<%name%>)' + case T_BOOL(__) then 'mmc_unbox_integer(<%name%>)' + case T_REAL(__) then 'mmc_unbox_real(<%name%>)' + case T_STRING(__) then 'mmc_unbox_string(<%name%>)' + else name + end match + end match +end varUnbox2; + +template varModelicaRead(MidCode.Var var) +::= + match var case VAR(name=name,ty=ty) then + match ty + case T_INTEGER(__) then 'read_modelica_integer(&inArgs, &<%name%>)' + case T_BOOL(__) then 'read_modelica_integer(&inArgs, &<%name%>)' + case T_REAL(__) then 'read_modelica_real(&inArgs, &<%name%>)' + case T_STRING(__) then 'read_modelica_string(&inArgs, &<%name%>)' + case T_ENUMERATION(__) then 'read_modelica_integer(&inArgs, &<%name%>)' + case T_COMPLEX(__) then 'read_modelica_metatype(&inArgs, &<%name%>)' //? + case T_METAUNIONTYPE(__) + case T_METALIST(__) + case T_METAARRAY(__) + case T_METAOPTION(__) + case T_METATUPLE(__) then 'read_modelica_metatype(&inArgs, &<%name%>)' + end match + end match +end varModelicaRead; + +template varModelicaWrite(MidCode.Var var) +::= + match var case VAR(name=name,ty=ty) then + match ty + case T_INTEGER(__) then 'write_modelica_integer(outVar, &<%name%>)' + case T_BOOL(__) then 'write_modelica_integer(outVar, &<%name%>)' + case T_REAL(__) then 'write_modelica_real(outVar, &<%name%>)' + case T_STRING(__) then 'write_modelica_string(outVar, &<%name%>)' + case T_ENUMERATION(__) then 'write_modelica_integer(outVar, &<%name%>)' + case T_COMPLEX(__) then 'write_modelica_metatype(outVar, &<%name%>)' //? + case T_METAUNIONTYPE(__) + case T_METALIST(__) + case T_METAARRAY(__) + case T_METAOPTION(__) + case T_METATUPLE(__) then 'write_modelica_metatype(outVar, &<%name%>)' + end match + end match +end varModelicaWrite; + +template identName(Absyn.Path path) +::= + match path + case Absyn.IDENT(__) then name +end identName; + +template identBuiltinCall(Absyn.Path path) +::= + match path + case Absyn.IDENT(name="clock") then "mmc_clock" + case Absyn.IDENT(name="anyString") then "mmc_anyString" + case Absyn.IDENT(name="fail") then "MMC_THROW_INTERNAL" + case Absyn.IDENT(name="intMod") then "modelica_mod_integer" + //TODO: print -> puts(MMC_STRINGDATA(...)) + //TODO: mmc_get_field (usual macro solution) + //TODO: bitwise operators (could be done in DAEToMid instead) + //TODO: mod, div, max, min? + //needs some more advanced expression generation with arguments as input + + case Absyn.IDENT(__) then name + +end identBuiltinCall; + + + +/* + ++++++++++++++++++++++++++++++++++++++++ +Copied from +CodegenUtil.tpl + +Don't know how to import across directories. +*/ + +template replaceDotAndUnderscore(String str) + "Replace _ with __ and dot in identifiers with _" +::= + match str + case name then + let str_dots = System.stringReplace(name,".", "_") + let str_underscores = System.stringReplace(str_dots, "_", "__") + System.unquoteIdentifier(str_underscores) +end replaceDotAndUnderscore; + + +template underscorePath(Absyn.Path path) + "Generate paths with components separated by underscores. + Replaces also the . in identifiers with _. + The dot might happen for world.gravityAccleration" +::= + match path + case Absyn.QUALIFIED(__) then + '<%replaceDotAndUnderscore(name)%>_<%underscorePath(path)%>' + case Absyn.IDENT(__) then + replaceDotAndUnderscore(name) + case Absyn.FULLYQUALIFIED(__) then + underscorePath(path) +end underscorePath; + + +//++++++++++++++++++++++++++++++++++++++++ + +annotation(__OpenModelica_Interface="backend"); +end CodegenMidToC; diff --git a/Compiler/Template/Makefile.common b/Compiler/Template/Makefile.common index fad6a9aab1..58d9296a0f 100644 --- a/Compiler/Template/Makefile.common +++ b/Compiler/Template/Makefile.common @@ -1,7 +1,7 @@ .PHONY : all check_tabs GENERATED_FILES_BOOT_STAGE2=AbsynDumpTpl.mo CodegenUtil.mo DAEDumpTpl.mo ExpressionDumpTpl.mo Unparsing.mo SCodeDumpTpl.mo GenerateAPIFunctionsTpl.mo CodegenCFunctions.mo -GENERATED_FILES=$(GENERATED_FILES_BOOT_STAGE2) CodegenC.mo CodegenUtilSimulation.mo CodegenEmbeddedC.mo CodegenFMUCommon.mo CodegenFMU.mo CodegenFMU1.mo CodegenFMU2.mo CodegenCSharp.mo CodegenCppCommon.mo CodegenCpp.mo CodegenCppHpcom.mo CodegenFMUCpp.mo CodegenFMUCppHpcom.mo CodegenCppInit.mo CodegenModelica.mo GraphvizDump.mo GraphMLDumpTpl.mo NFInstDumpTpl.mo SimCodeDump.mo CodegenAdevs.mo CodegenSparseFMI.mo CodegenXML.mo CodegenJava.mo CodegenJS.mo TaskSystemDump.mo VisualXMLTpl.mo +GENERATED_FILES=$(GENERATED_FILES_BOOT_STAGE2) CodegenC.mo CodegenUtilSimulation.mo CodegenEmbeddedC.mo CodegenFMUCommon.mo CodegenFMU.mo CodegenFMU1.mo CodegenFMU2.mo CodegenCSharp.mo CodegenCppCommon.mo CodegenCpp.mo CodegenCppHpcom.mo CodegenFMUCpp.mo CodegenFMUCppHpcom.mo CodegenCppInit.mo CodegenMidToC.mo CodegenModelica.mo GraphvizDump.mo GraphMLDumpTpl.mo NFInstDumpTpl.mo SimCodeDump.mo CodegenAdevs.mo CodegenSparseFMI.mo CodegenXML.mo CodegenJava.mo CodegenJS.mo TaskSystemDump.mo VisualXMLTpl.mo all : $(GENERATED_FILES) TplCodegen.mo check_tabs templates-bootstrap : $(GENERATED_FILES_BOOT_STAGE2) TplCodegen.mo check_tabs @@ -49,6 +49,11 @@ CodegenCFunctions.mo : CodegenCFunctions.tpl SimCodeTV.mo CodegenUtil.tpl $(OMC) $< > $@.log || (cat $@.log && false) @echo " " +CodegenMidToC.mo : CodegenMidToC.tpl SimCodeTV.mo MidCodeTV.mo + @echo " ** CodegenMidToC template compilation ** " + $(OMC) $< > $@.log || (cat $@.log && false) + @echo " " + CodegenCSharp.mo : CodegenCSharp.tpl SimCodeTV.mo @echo " ** CodegenCSharp template compilation ** " $(OMC) $< > $@.log || (cat $@.log && false) diff --git a/Compiler/Template/MidCodeTV.mo b/Compiler/Template/MidCodeTV.mo new file mode 100644 index 0000000000..25b2838b0f --- /dev/null +++ b/Compiler/Template/MidCodeTV.mo @@ -0,0 +1,256 @@ +/* + * This file is part of OpenModelica. + * + * Copyright (c) 1998-2014, Open Source Modelica Consortium (OSMC), + * c/o Linköpings universitet, Department of Computer and Information Science, + * SE-58183 Linköping, Sweden. + * + * All rights reserved. + * + * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR + * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.2. + * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES + * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, + * ACCORDING TO RECIPIENTS CHOICE. + * + * The OpenModelica software and the Open Source Modelica + * Consortium (OSMC) Public License (OSMC-PL) are obtained + * from OSMC, either from the above address, + * from the URLs: http://www.ida.liu.se/projects/OpenModelica or + * http://www.openmodelica.org, and in the OpenModelica distribution. + * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html. + * + * This program is distributed WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH + * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. + * + * See the full OSMC Public License conditions for more details. + * + */ + +interface package MidCodeTV + +package MidCode + +uniontype Program + record PROGRAM + String name; + list functions; + end PROGRAM; +end Program; + +uniontype Var + record VAR + String name; + DAE.Type ty; + Boolean volatile; + end VAR; +end Var; + +uniontype VarBuf + /* + A MidCode variable containing a jmp_buf for longjmp. + */ + record VARBUF + String name; + end VARBUF; +end VarBuf; + +uniontype VarBufPtr + /* + A MidCode variable containing a jmp_buf pointer for longjmp. + */ + record VARBUFPTR + String name; + end VARBUFPTR; +end VarBufPtr; + +uniontype OutVar + record OUT_VAR + Var var; + end OUT_VAR; + record OUT_WILD end OUT_WILD; +end OutVar; + + +uniontype Function + record FUNCTION + Absyn.Path name; + list locals; + list localBufs; // jmb_buf for longjmp + list localBufPtrs; // jmb_buf pointers for longjmp + list inputs; + list outputs; + list body; + Integer entryId; + Integer exitId; + end FUNCTION; +end Function; + +uniontype Block + record BLOCK + "Basic block. + No control flow within block. + Can branch or jump on exit, called the block's terminator." + Integer id; + list stmts; + Terminator terminator; + end BLOCK; +end Block; + +uniontype Terminator + record GOTO + Integer next; + end GOTO; + + record BRANCH + Var condition; + Integer onTrue; + Integer onFalse; + end BRANCH; + + record CALL + Absyn.Path func; + Boolean builtin; + list inputs; + list outputs; + Integer next; + end CALL; + + record RETURN + end RETURN; + + record SWITCH + Var condition; + list> cases; + end SWITCH; + + record LONGJMP "used for fail() stmts" + end LONGJMP; + + record PUSHJMP "used for match-continue fail() handling" + VarBufPtr old_buf "where to save old jmp_buf"; + VarBuf new_buf "what to use as new jmp_buf"; + Integer next "where to goto next and the setjmp target"; + end PUSHJMP; + + /* POPJMP does not cause control flow but + if it is a terminator to simplify matching + with their respective PUSHJMPS. + */ + record POPJMP "used for match-continue fail() handling" + VarBufPtr old_buf "what to reset to"; + Integer next; + end POPJMP; + + record ASSERT + Var condition; + Var message; + Var level; + Integer next; + end ASSERT; + + record TERMINATE + Var message; + end TERMINATE; + +end Terminator; + +uniontype Stmt + record NOP + end NOP; + + record ASSIGN + Var dest; + RValue src; + end ASSIGN; + + record ASSIGN_INDEX + Var dest; + Var index; + RValue src; + end ASSIGN_INDEX; +end Stmt; + +uniontype RValue + record VARIABLE + Var src; + end VARIABLE; + + record UNARYOP + UnaryOp op; + Var src; + end UNARYOP; + + record BINARYOP + BinaryOp op; + Var lsrc; + Var rsrc; + end BINARYOP; + + record LITERALINTEGER + Integer value; + end LITERALINTEGER; + + record LITERALREAL + Real value; + end LITERALREAL; + + record LITERALBOOLEAN + Boolean value; + end LITERALBOOLEAN; + + record LITERALSTRING + String value; + end LITERALSTRING; + + record LITERALMETATYPE + list elements; + DAE.Type ty; + end LITERALMETATYPE; + + record UNIONTYPEVARIANT + Var src; + end UNIONTYPEVARIANT; + + record ISSOME + Var src; + end ISSOME; + + record ISCONS + Var src; + end ISCONS; + + record METAFIELD "get value from metamodelica object" + Var src; + Integer index; + DAE.Type ty "type of value"; + end METAFIELD; +end RValue; + +uniontype UnaryOp + record MOVE end MOVE; + record UMINUS end UMINUS; + record NOT end NOT; + record UNBOX end UNBOX; + record BOX end BOX; +end UnaryOp; + +uniontype BinaryOp + record ADD end ADD; + record SUB end SUB; + record MUL end MUL; + record DIV end DIV; + record POW end POW; + record LESS end LESS; + record LESSEQ end LESSEQ; + record GREATER end GREATER; + record GREATEREQ end GREATEREQ; + record EQUAL end EQUAL; + record NEQUAL end NEQUAL; +end BinaryOp; + +end MidCode; + +end MidCodeTV; diff --git a/Compiler/Util/Flags.mo b/Compiler/Util/Flags.mo index 99f37831b4..c0c12a2002 100644 --- a/Compiler/Util/Flags.mo +++ b/Compiler/Util/Flags.mo @@ -929,7 +929,7 @@ constant ConfigFlag POST_OPT_MODULES = CONFIG_FLAG(16, "postOptModules", constant ConfigFlag SIMCODE_TARGET = CONFIG_FLAG(17, "simCodeTarget", NONE(), EXTERNAL(), STRING_FLAG("C"), - SOME(STRING_OPTION({"None", "Adevs", "C", "Cpp", "CSharp", "ExperimentalEmbeddedC", "Java", "JavaScript", "sfmi", "XML"})), + SOME(STRING_OPTION({"None", "Adevs", "C", "Cpp", "CSharp", "ExperimentalEmbeddedC", "Java", "JavaScript", "sfmi", "XML", "MidC"})), Util.gettext("Sets the target language for the code generation.")); constant ConfigFlag ORDER_CONNECTIONS = CONFIG_FLAG(18, "orderConnections", diff --git a/Compiler/boot/LoadCompilerSources.mos b/Compiler/boot/LoadCompilerSources.mos index a434bf50bb..c8b086dc97 100644 --- a/Compiler/boot/LoadCompilerSources.mos +++ b/Compiler/boot/LoadCompilerSources.mos @@ -243,7 +243,13 @@ if true then /* Suppress output */ "../Util/System.mo", "../Util/Util.mo", "../Util/VarTransform.mo", - "../Util/ZeroMQ.mo" + "../Util/ZeroMQ.mo", + + // "MidCode"; + "../MidCode/MidCode.mo", + "../MidCode/DAEToMid.mo", + "../MidCode/MidToMid.mo", + "../MidCode/HashTableMidVar.mo" }; backendfiles := if OpenModelica.Scripting.getEnvironmentVar("OPENMODELICA_BACKEND_STUBS")<>"" then { @@ -365,6 +371,7 @@ if true then /* Suppress output */ "../Template/CodegenFMUCppHpcom.mo", "../Template/CodegenJava.mo", "../Template/CodegenJS.mo", + "../Template/CodegenMidToC.mo", "../Template/CodegenModelica.mo", "../Template/CodegenSparseFMI.mo", "../Template/CodegenUtilSimulation.mo",