DIRECTORY Alloc USING [AddNotify, Base, BaseSeq, DropNotify, Notifier], Basics USING [LowHalf], BasicTime USING [Now], CardTab USING [Create, Ref, Store], CompilerUtil USING [], ConstArith USING [ToInt], ConvertUnsafe USING [ToRope, SubString, SubStringToRope], IntCodeDefs USING [BlockNode, ByteSequence, CaseList, Label, LabelNode, LambdaKind, LambdaNode, Location, LocationRep, MesaSelector, ModuleNode, Node, NodeList, NodeRep, RefLitKind, SourceNode, Var, VariableFlags, VarList], IntCodeGen USING [CodeGenerator, GetCodeGenerator], IntCodeStuff USING [NodeContains], IntCodeTwig USING [BaseModel, DoModule, LambdaModel], IntCodeUtils USING [MapNode, Visitor, zone], IO USING [PutChar, PutF1, PutFR, PutRope, RopeFromROS, ROS, STREAM], List USING [CompareProc, UniqueSort], LiteralOps USING [StringValue], Literals USING [Base, STIndex, stType], MimBodyCorrect USING [FixBodies], MimCommandUtil USING [GetRootName, SetExtension], MimCode USING [BitCount, CodeList, StoreOptions], MimData USING [bodyIndex, idATOM, idTEXT, mainCtx, nSigCodes, objectVersion, source, switches, table, textIndex, worstAlignment], MimosaLog USING [ErrorRope], MimP5 USING [DeclList, Exp, ExpList, StatementList, StatementTree, VarForSei, VisibalContextArray, WrapSource, WrapSourceBlock], MimP5Install USING [GenInstallationProc], MimP5S USING [ComAssign, ExtendValue, Temporize, WillEvalToConst], MimP5U USING [Address, AllocLabel, AppendNodeList, ApplyOp, Assign, BitsForOperand, BitsForType, CgenUtilInit, CreateTemp, Declare, Deref, ExtractList, InsertLabel, Jump, LabelAddress, MakeArgList, MakeArgList2, MakeBlock, MakeComposite, MakeConstCard, MakeGoTo, MakeNodeList, MakeNodeList2, MakeReturn, MakeTemp, MakeVarList, MaybeBlock, MesaOpNode, MoreCode, NewCodeList, NextVar, OperandType, PadArgList, ProcessSafens, TakeField, TakeFieldVar, TreeLiteralValue, TypeForTree], MimSysOps USING [Close, Open], MobDefs USING [Link, ModuleIndex], ParseIntCode USING [ToStream], Rope USING [Concat, FromProc, ROPE], SourceMap USING [Loc, nullLoc, Up], SymbolOps USING [DecodeLink, EncodeBitAddr, EncodeInt, EnumerateBodies, MakeCtxSe, NameForSe, NextSe, own, ParentBti, RCType, SetCtxLevel, SubStringForName, TransferTypes, XferMode], Symbols USING [Base, bodyType, BTIndex, BTNull, CBTIndex, CBTNull, ContextLevel, CSEIndex, CSENull, CTXIndex, CTXNull, ctxType, HTNull, ISEIndex, ISENull, lG, lL, Name, nullType, RecordSEIndex, RecordSENull, RootBti, SEIndex, SENull, seType, SpecialVarKind, Type, typeANY, VariableFlags], TargetConversions USING [NewWriter, PutCard, PutChar, Writer, WriterContents], Target: TYPE MachineParms USING [bitsPerAU, bitsPerByte, bitsPerChar, bitsPerLongWord, bitsPerRef, bitsPerSignal, bitsPerStringBound, bitsPerWord], Tree USING [Base, Index, Link, LinkRep, Map, NodePtr, NodeName, Null, treeType], TreeOps USING [GetTag, OpName, SearchList, UpdateLeaves]; MimDriver: PROGRAM IMPORTS Alloc, Basics, BasicTime, CardTab, ConstArith, ConvertUnsafe, IntCodeGen, IntCodeStuff, IntCodeTwig, IntCodeUtils, IO, List, LiteralOps, MimBodyCorrect, MimCommandUtil, MimData, MimosaLog, MimP5, MimP5Install, MimP5S, MimP5U, MimSysOps, ParseIntCode, Rope, SourceMap, SymbolOps, TargetConversions, TreeOps EXPORTS CompilerUtil, MimCode, MimP5, MimP5S = { OPEN IntCodeDefs, MimCode, Target; enableTypesFile: BOOL ฌ FALSE; enableIntCodeTransforms: BOOL ฌ TRUE; collectConstants: BOOL ฌ TRUE; minCollectibleWords: NAT ฌ 4; noCollectConstAssigns: BOOL ฌ TRUE; maxMemoBits: INT ฌ LAST[INT]; minMemoBits: INT ฌ 3*LONG[bitsPerLongWord]+1; LORA: TYPE = LIST OF REF ANY; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; BTIndex: TYPE = Symbols.BTIndex; CBTIndex: TYPE = Symbols.CBTIndex; CTXIndex: TYPE = Symbols.CTXIndex; CTXNull: CTXIndex = Symbols.CTXNull; CSEIndex: TYPE = Symbols.CSEIndex; CSENull: CSEIndex = Symbols.CSENull; ISEIndex: TYPE = Symbols.ISEIndex; ISENull: ISEIndex = Symbols.ISENull; RecordSEIndex: TYPE = Symbols.RecordSEIndex; RecordSENull: RecordSEIndex = Symbols.RecordSENull; Type: TYPE = Symbols.Type; nullType: Type = Symbols.nullType; typeANY: Type = Symbols.typeANY; bytesPerWord: NAT = Target.bitsPerWord / Target.bitsPerByte; bitsPerPtr: NAT = Target.bitsPerRef; bitsPerWord: NAT = Target.bitsPerWord; bitsPerProcDesc: NAT = bitsPerPtr*2; curctxlvl: PUBLIC Symbols.ContextLevel ฌ Symbols.lG; bodyRetLabel, bodyComRetLabel: PUBLIC Label ฌ NIL; bodyInRecord, bodyOutRecord: PUBLIC Symbols.RecordSEIndex ฌ RecordSENull; mainBody: PUBLIC BOOL ฌ FALSE; tailJumpOK: PUBLIC BOOL ฌ FALSE; caseCV: PUBLIC Node ฌ NIL; caseType: PUBLIC Symbols.Type ฌ nullType; fileLoc, inlineFileLoc: PUBLIC SourceMap.Loc ฌ SourceMap.nullLoc; catchcount: PUBLIC CARDINAL ฌ 0; catchoutrecord: PUBLIC Symbols.RecordSEIndex ฌ RecordSENull; tempcontext: PUBLIC Symbols.CTXIndex ฌ CTXNull; xtracting: PUBLIC BOOL ฌ FALSE; xtractNode: PUBLIC Node ฌ NIL; xtractsei: PUBLIC Symbols.ISEIndex ฌ ISENull; nC0, nC1: PUBLIC Node ฌ NIL; trueNode, falseNode: PUBLIC Node ฌ NIL; CodeNotImplemented: PUBLIC SIGNAL = CODE; CodePassInconsistency: PUBLIC SIGNAL = CODE; myBaseSeq: REF Alloc.BaseSeq ฌ NIL; tb: Tree.Base; -- tree base (local copy) seb: Symbols.Base; -- semantic entry base (local copy) ctxb: Symbols.Base; -- context entry base (local copy) bb: Symbols.Base; -- body entry base (local copy) stb: Literals.Base; -- string base (local copy) DriverNotify: Alloc.Notifier = { myBaseSeq ฌ base; seb ฌ base[Symbols.seType]; ctxb ฌ base[Symbols.ctxType]; bb ฌ base[Symbols.bodyType]; stb ฌ base[Literals.stType]; tb ฌ base[Tree.treeType]; FOR i: NAT IN [0..notifiers) DO notifier: Alloc.Notifier ฌ notifierArray[i]; notifier[base]; ENDLOOP; }; z: PUBLIC ZONE ฌ IntCodeUtils.zone; notifiers: NAT ฌ 0; notifierArray: REF NotifierArray ฌ z.NEW[NotifierArray ฌ ALL[NIL]]; NotifierArray: TYPE = ARRAY [0..32) OF Alloc.Notifier; inInline: PUBLIC BOOL ฌ FALSE; localProcCodeList: CodeList ฌ NIL; substState: REF SubstState ฌ NIL; SubstState: TYPE = RECORD [ cl: CodeList ฌ NIL, prefixCL: CodeList ฌ NIL, postfixCL: CodeList ฌ NIL, resultType: Type ฌ nullType, resultVar: Var ฌ NIL, exitLabel: IntCodeDefs.Label ฌ NIL, lock: Tree.Link ฌ Tree.Null, lastResult: Node ฌ NIL, lastResultExpr: Node ฌ NIL, lastResultGoTo: Node ฌ NIL, nResults: INT ฌ 0 ]; mLock: Tree.Link ฌ Tree.Null; signalsVar: Var ฌ NIL; procDescRoot: REF ProcDescEntry ฌ NIL; ProcDescEntry: TYPE = RECORD [ rest: REF ProcDescEntry ฌ NIL, -- the next sibling (if any) parent: REF ProcDescEntry ฌ NIL, -- the parent (if any) child: REF ProcDescEntry ฌ NIL, -- the first child (if any) name: ROPE ฌ NIL, -- the name of the proc label: Label ฌ NIL, -- the label for the proc used: BOOL ฌ FALSE, -- TRUE if used as a proc desc bti: CBTIndex ฌ Symbols.CBTNull, -- the bti for the proc indirectEntry: Node ฌ NIL, -- the indirect entry point directEntry: Node ฌ NIL, -- the direct entry point body: Var ฌ NIL -- the variable for the proc desc body ]; maxBti: CBTIndex ฌ Symbols.RootBti; modNode: ModuleNode ฌ NIL; modVarsTail: VarList ฌ NIL; maxGlobalVarId: INT ฌ 0; ModuleIndex: TYPE = MobDefs.ModuleIndex; linkToVarSeq: LinkVarSeq ฌ NIL; LinkVarSeq: TYPE = REF LinkVarSeqRep; LinkVarSeqRep: TYPE = RECORD [ length: ModuleIndex, entries: SEQUENCE max: ModuleIndex OF Var ]; linkOverhead: NAT ฌ 4; extraLinkDeref: BOOL ฌ FALSE; P5module: PUBLIC PROC = { moduleNode: Node = Module[]; nodeList: NodeList ฌ MimP5U.MakeNodeList[moduleNode]; root: ROPE ฌ MimCommandUtil.GetRootName[MimData.source.locator]; id: Symbols.Name ฌ seb[bb[Symbols.RootBti].id].hash; ss: ConvertUnsafe.SubString = SymbolOps.SubStringForName[SymbolOps.own, id]; moduleName: ROPE ฌ ConvertUnsafe.SubStringToRope[ss]; namesFileName: ROPE ฌ MimCommandUtil.SetExtension[root, "names"]; nameStream: STREAM ฌ NIL; err: ROPE ฌ NIL; cg: IntCodeGen.CodeGenerator ฌ NIL; cgd: REF ฌ NIL; [cg: cg, data: cgd] ฌ IntCodeGen.GetCodeGenerator[]; IF MimData.switches['m] THEN cg ฌ NIL; IF MimData.switches['i] THEN { [nameStream, err, ] ฌ MimSysOps.Open[namesFileName, $write]; IF err # NIL THEN {MimosaLog.ErrorRope[other, err]; RETURN}; IO.PutF1[nameStream, "-- %g \n", [rope[namesFileName]] ]; }; MimP5Install.GenInstallationProc[ FindProcDesc[Symbols.RootBti].name, bb[Symbols.RootBti].type, NARROW[modNode]]; IF enableIntCodeTransforms THEN { model: IntCodeTwig.BaseModel ฌ IntCodeTwig.DoModule[modNode, MimData.switches]; lambda: IntCodeTwig.LambdaModel ฌ model.first; nodeList ฌ MimP5U.MakeNodeList[model.module]; RewriteSymbols[model]; WHILE lambda # NIL DO next: IntCodeTwig.LambdaModel ฌ lambda.next; lambdaญ ฌ []; -- clobber the fields to the ground state lambda ฌ next; ENDLOOP; modelญ ฌ []; -- clobber the base model }; { cr: ROPE ฌ IO.PutFR["file: %g, module: %g, compiled at: %g", [rope[root]], [rope[moduleName]], [time[BasicTime.Now[]]]]; cn: Node ฌ z.NEW[NodeRep.comment ฌ [bits: 0, details: comment[cr]]]; modNode.procs ฌ MimP5U.MakeNodeList[cn, modNode.procs]; }; MimBodyCorrect.FixBodies[modNode.procs]; IF MimData.switches['i] THEN { tName: ROPE ฌ MimCommandUtil.SetExtension[root, "icd"]; st: STREAM ฌ NIL; [st, err, ] ฌ MimSysOps.Open[tName, $write]; IF err # NIL THEN {MimosaLog.ErrorRope[other, err]; RETURN}; ParseIntCode.ToStream[st, nodeList]; [] ฌ MimSysOps.Close[st]; }; IF nameStream # NIL OR cg # NIL THEN { head: LORA ฌ NIL; tail: LORA ฌ NIL; inner: IntCodeUtils.Visitor = TRUSTED { add: BOOL ฌ FALSE; WITH node SELECT FROM var: Var => add ฌ var.flags[named]; labelNode: LabelNode => WITH labelNode.label.node SELECT FROM lambda: LambdaNode => add ฌ TRUE; ENDCASE; ENDCASE; IF add THEN { new: LORA ฌ LIST[node]; IF tail = NIL THEN head ฌ new ELSE tail.rest ฌ new; tail ฌ new; }; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; compare: List.CompareProc = TRUSTED { delta: INT; WITH ref1 SELECT FROM var1: Var => WITH ref2 SELECT FROM var2: Var => delta ฌ var1.id - var2.id; labelNode2: LabelNode => RETURN [greater]; ENDCASE; labelNode1: LabelNode => WITH ref2 SELECT FROM var2: Var => RETURN [less]; labelNode2: LabelNode => delta ฌ labelNode1.label.id - labelNode2.label.id; ENDCASE; ENDCASE; SELECT delta FROM < 0 => RETURN [less]; > 0 => RETURN [greater]; ENDCASE => RETURN [equal]; }; table: CardTab.Ref ฌ NIL; IntCodeUtils.MapNode[modNode, inner]; head ฌ List.UniqueSort[head, compare]; IF nameStream # NIL THEN { FOR each: LORA ฌ head, each.rest WHILE each # NIL DO WITH each.first SELECT FROM var: Var => IF var.flags[named] THEN { sei: Symbols.ISEIndex; index: Tree.LinkRep ฌ LOOPHOLE[var.id]; index.tag ฌ VAL[0]; IO.PutF1[nameStream, "\n %g: ", [integer[LOOPHOLE[index]]] ]; index.tag ฌ symbol; sei ฌ LOOPHOLE[index]; PrintSei[nameStream, sei]; }; labelNode: LabelNode => IF labelNode.label.id IN [0..100000) THEN { bti: Symbols.BTIndex = LOOPHOLE[labelNode.label.id]; IO.PutF1[nameStream, "\n %%%g: ", [integer[LOOPHOLE[bti]]] ]; WITH b: bb[bti] SELECT FROM Callable => PrintSei[nameStream, b.id]; ENDCASE => IO.PutRope[nameStream, "??"]; }; ENDCASE; ENDLOOP; IO.PutRope[nameStream, "\n\n"]; [] ฌ MimSysOps.Close[nameStream]; }; IF cg # NIL THEN { msg: ROPE ฌ NIL; namesTable: CardTab.Ref ฌ CardTab.Create[]; labelsTable: CardTab.Ref ฌ CardTab.Create[]; FOR each: LORA ฌ head, each.rest WHILE each # NIL DO WITH each.first SELECT FROM var: Var => IF var.flags[named] THEN { sei: Symbols.ISEIndex; index: Tree.LinkRep ฌ LOOPHOLE[var.id]; index.tag ฌ symbol; sei ฌ LOOPHOLE[index]; [] ฌ CardTab.Store[namesTable, LOOPHOLE[var.id, CARD], RopeForSei[sei]]; }; labelNode: LabelNode => IF labelNode.label.id IN [0..100000) THEN { bti: Symbols.BTIndex = LOOPHOLE[labelNode.label.id]; WITH b: bb[bti] SELECT FROM Callable => [] ฌ CardTab.Store[labelsTable, LOOPHOLE[bti, CARD], RopeForSei[b.id]]; ENDCASE; }; ENDCASE; ENDLOOP; msg ฌ cg[ fileName: root, moduleName: moduleName, versionStamp: IO.PutFR["[%g,%g]", [cardinal[MimData.objectVersion[0]]], [cardinal[MimData.objectVersion[1]]] ], root: modNode, names: namesTable, labels: labelsTable, data: cgd, switches: MimData.switches]; IF msg # NIL THEN MimosaLog.ErrorRope[other, msg]; }; }; z.FREE[@linkToVarSeq]; (MimData.table).DropNotify[DriverNotify]; myBaseSeq ฌ NIL; caseCV ฌ NIL; xtractNode ฌ NIL; mLock ฌ Tree.Null; signalsVar ฌ NIL; ClearProcDesc[procDescRoot]; procDescRoot ฌ NIL; MimP5U.CgenUtilInit[NIL]; IntCodeUtils.MapNode[modNode, ClearNodes]; modNode ฌ NIL; modVarsTail ฌ NIL; }; RegisterNotifier: PUBLIC PROC [notifier: Alloc.Notifier] = { notifierArray[notifiers] ฌ notifier; notifiers ฌ notifiers + 1; IF myBaseSeq # NIL THEN notifier[myBaseSeq]; }; visibleContext: PUBLIC REF MimP5.VisibalContextArray ฌ NIL; MakeGlobal: PUBLIC PROC [bits: INT, type: Type ฌ typeANY] RETURNS [v: Var, sei: ISEIndex] = { oldTempCtx: Symbols.CTXIndex ฌ tempcontext; new: VarList; tempcontext ฌ MimData.mainCtx; [v, sei] ฌ MimP5U.CreateTemp[bits: bits, type: type]; new ฌ MimP5U.MakeVarList[v]; SELECT TRUE FROM modVarsTail # NIL => modVarsTail.rest ฌ new; modNode.vars = NIL => modNode.vars ฌ new; ENDCASE => ERROR; modVarsTail ฌ new; tempcontext ฌ oldTempCtx; RETURN [v, sei]; }; P5Error: PUBLIC PROC [n: CARDINAL] = { ERROR CodePassError[n]; }; ProcDescForBti: PUBLIC PROC [bti: CBTIndex, body: BOOL] RETURNS [Node] = { new: REF ProcDescEntry ฌ FindProcDesc[bti]; node: Node ฌ new.body; new.used ฌ TRUE; IF node = NIL THEN ERROR; IF NOT body THEN node ฌ MimP5U.Address[node]; RETURN [node]; }; ProcLabelForBti: PUBLIC PROC [bti: CBTIndex, direct: BOOL] RETURNS [Node] = { new: REF ProcDescEntry ฌ FindProcDesc[bti]; node: Node ฌ IF direct THEN new.directEntry ELSE new.indirectEntry; new.used ฌ TRUE; RETURN [node]; }; SignalForSei: PUBLIC PROC [sei: ISEIndex] RETURNS [Node] = { SELECT SymbolOps.XferMode[SymbolOps.own, seb[sei].idType] FROM signal, error => { link: MobDefs.Link = SymbolOps.DecodeLink[seb[sei].idValue]; index: NAT = link.offset; IF link.modIndex = 0 THEN { sel: IntCodeDefs.MesaSelector = SELECT index FROM 0 => unnamedError, 1 => unwindError, 2 => abortedError, 3 => uncaughtError, 4 => boundsError, ENDCASE => ERROR; RETURN [MimP5U.MesaOpNode[op: sel, bits: bitsPerSignal]]; }; RETURN [MimP5U.Address[MimP5U.TakeField[ signalsVar, index*bitsPerWord, bitsPerWord]]]; }; ENDCASE => ERROR; }; VarForInterface: PUBLIC PROC [mod: MobDefs.ModuleIndex] RETURNS [Var] = { oldLen: ModuleIndex ฌ IF linkToVarSeq = NIL THEN 0 ELSE linkToVarSeq.length; linkVar: Var ฌ NIL; IF oldLen <= mod THEN { newLen: ModuleIndex = MIN[MAX[mod+1, oldLen + oldLen/2 + 1], ModuleIndex.LAST]; newSeq: LinkVarSeq ฌ z.NEW[LinkVarSeqRep[newLen]]; newSeq.length ฌ mod+1; IF linkToVarSeq # NIL THEN { FOR i: ModuleIndex IN [0..oldLen) DO newSeq[i] ฌ linkToVarSeq[i]; linkToVarSeq[i] ฌ NIL; ENDLOOP; z.FREE[@linkToVarSeq]; }; linkToVarSeq ฌ newSeq; }; linkVar ฌ linkToVarSeq[mod]; IF linkVar = NIL THEN linkToVarSeq[mod] ฌ linkVar ฌ MakeGlobal[bitsPerPtr].v; RETURN [linkVar]; }; VarForLink: PUBLIC PROC [link: MobDefs.Link, bits: INT] RETURNS [v: Var] = { offset: CARD = Target.bitsPerRef * (link.offset + linkOverhead); linkVar: Var ฌ VarForInterface[link.modIndex]; v ฌ MimP5U.TakeFieldVar[ MimP5U.Deref[linkVar, offset+bits, MimData.worstAlignment], offset, bits]; }; Lock: PUBLIC PROC [node: Tree.Index] RETURNS [n: Node ฌ NIL] = { saveLock: Tree.Link = mLock; cl: CodeList ฌ MimP5U.NewCodeList[]; mLock ฌ tb[node].son[2]; substState.lock ฌ mLock; SetLock[cl, mLock]; n ฌ MimP5U.MaybeBlock[cl, MimP5.StatementTree[tb[node].son[1]]]; mLock ฌ saveLock; }; Result: PUBLIC PROC [node: Tree.Index] RETURNS [Node ฌ NIL] = { cl: CodeList ฌ MimP5U.NewCodeList[]; resultVar: Var ฌ substState.resultVar; substState.lastResultExpr ฌ NIL; substState.nResults ฌ substState.nResults + 1; IF resultVar # NIL THEN { returnOfAnotherCall: BOOL ฌ tb[node].attr3; t1: Tree.Link ฌ tb[node].son[1]; dstType: Type = substState.resultType; result: Node ฌ NIL; IF returnOfAnotherCall THEN result ฌ MimP5.Exp[t1] ELSE { list: NodeList ฌ MimP5.ExpList[t1, TRUE].head; IF list.rest = NIL THEN result ฌ list.first ELSE result ฌ MimP5U.MakeComposite[list]; }; IF result # NIL AND result.bits # resultVar.bits THEN { lbits: INT ฌ resultVar.bits; rbits: INT ฌ result.bits; SELECT lbits FROM < rbits => { start: INT ฌ IF rbits <= bitsPerWord THEN rbits-lbits ELSE 0; result ฌ MimP5U.TakeField[result, start, lbits]; }; > rbits => { srcType: Type = SELECT TreeOps.OpName[t1] FROM none, list => dstType, ENDCASE => MimP5U.OperandType[t1]; result ฌ MimP5S.ExtendValue[result, dstType, srcType, lbits]; }; ENDCASE; }; substState.lastResultExpr ฌ result; MimP5U.MoreCode[cl, MimP5U.Assign[lhs: resultVar, rhs: result]]; }; IF substState.exitLabel = NIL THEN substState.exitLabel ฌ MimP5U.AllocLabel[]; IF substState.lastResultGoTo = NIL THEN substState.lastResultGoTo ฌ MimP5U.MakeGoTo[substState.exitLabel]; substState.lastResult ฌ MimP5U.MaybeBlock[cl, substState.lastResultGoTo]; RETURN [substState.lastResult]; }; Resume: PUBLIC PROC [node: Tree.Index] RETURNS [Node ฌ NIL] = { returnOfAnotherCall: BOOL ฌ tb[node].attr3; retvals: NodeList; t1: Tree.Link ฌ tb[node].son[1]; totalBits: BitCount ฌ MimP5U.BitsForType[catchoutrecord]; IF returnOfAnotherCall THEN retvals ฌ MimP5U.MakeNodeList[MimP5.Exp[t1]] ELSE retvals ฌ MimP5.ExpList[t1, TRUE].head; RETURN [MimP5U.ApplyOp[oper: MimP5U.MesaOpNode[resume], args: MimP5U.PadArgList[retvals]]]; }; Return: PUBLIC PROC [node: Tree.Index] RETURNS [l: Node] = { cl: CodeList ฌ MimP5U.NewCodeList[]; monitored: BOOL ฌ tb[node].attr1; returnOfAnotherCall: BOOL ฌ tb[node].attr3; retvals: NodeList; t1: Tree.Link ฌ tb[node].son[1]; safend: Tree.Link ฌ t1; totalBits: BitCount ฌ MimP5U.BitsForType[bodyOutRecord]; IF (Basics.LowHalf[totalBits] MOD bitsPerWord) # 0 THEN ERROR; IF CommonRet[t1] THEN { outCtx: CTXIndex = IF bodyOutRecord = CSENull THEN CTXNull ELSE seb[bodyOutRecord].fieldCtx; IF substState.exitLabel # NIL THEN { MimP5U.Jump[cl, substState.exitLabel]; RETURN [MimP5U.MakeBlock[cl]]; }; IF monitored THEN LocalReleaseLock[cl, mLock]; RETURN [MimP5U.MaybeBlock[cl, MimP5U.MakeReturn[NodesForCtx[outCtx]]]]; }; IF monitored THEN safend ฌ MimP5U.ProcessSafens[cl: cl, t: t1]; IF returnOfAnotherCall THEN retvals ฌ MimP5U.MakeNodeList[MimP5.Exp[safend]] ELSE retvals ฌ MimP5.ExpList[safend, TRUE].head; IF substState.exitLabel # NIL THEN { IF ListNeedsTemp[retvals] THEN MakeListNice[cl, retvals]; IF bodyOutRecord # RecordSENull THEN { ctx: Symbols.CTXIndex = seb[bodyOutRecord].fieldCtx; sei: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; UNTIL sei = ISENull DO var: Var ฌ MimP5.VarForSei[sei]; src: Node ฌ retvals.first; IF returnOfAnotherCall THEN { retvals.first ฌ MimP5U.TakeField[src, var.bits, src.bits-var.bits]; src ฌ MimP5U.TakeField[src, 0, var.bits]; } ELSE retvals ฌ retvals.rest; MimP5U.MoreCode[cl, MimP5U.Assign[var, src]]; sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; ENDLOOP; }; MimP5U.Jump[cl, substState.exitLabel]; RETURN [MimP5U.MakeBlock[cl]]; }; IF monitored THEN { IF ListNeedsTemp[retvals] THEN MakeListNice[cl, retvals]; LocalReleaseLock[cl, mLock]; }; l ฌ MimP5U.MaybeBlock[cl, MimP5U.MakeReturn[retvals]]; }; RetWithError: PUBLIC PROC [node: Tree.Index] RETURNS [Node] = { cl: CodeList ฌ MimP5U.NewCodeList[]; psei: CSEIndex = MimP5U.OperandType[tb[node].son[1]]; sig: Node ฌ MimP5.Exp[tb[node].son[1]]; t2: Tree.Link ฌ tb[node].son[2]; monitored: BOOL ฌ tb[node].attr1; exitLabel: Label ฌ MimP5U.AllocLabel[]; sigTemp: Var ฌ NIL; argsList: NodeList ฌ NIL; argsTemp: Var ฌ NIL; prefixCL: CodeList ฌ substState.prefixCL; postfixCL: CodeList ฌ substState.postfixCL; IF prefixCL = NIL THEN prefixCL ฌ substState.prefixCL ฌ MimP5U.NewCodeList[]; IF postfixCL = NIL THEN postfixCL ฌ substState.postfixCL ฌ MimP5U.NewCodeList[]; t2 ฌ MimP5U.ProcessSafens[cl: cl, t: t2]; sigTemp ฌ MimP5U.MakeTemp[prefixCL, sig.bits].var; MimP5U.MoreCode[cl, MimP5U.Assign[sigTemp, sig]]; argsList ฌ MimP5.ExpList[t2, TRUE].head; IF argsList # NIL THEN { argsVar: Var ฌ MimP5U.MakeComposite[MimP5U.PadArgList[argsList]]; argsTemp ฌ MimP5U.MakeTemp[prefixCL, argsVar.bits].var; MimP5U.MoreCode[cl, MimP5U.Assign[argsTemp, argsVar]]; }; IF monitored THEN LocalReleaseLock[cl, mLock]; MimP5U.Jump[cl, exitLabel]; MimP5U.InsertLabel[postfixCL, exitLabel]; MimP5U.MoreCode[postfixCL, MimP5.WrapSource[ node: MimP5U.ApplyOp[ oper: MimP5U.MesaOpNode[error], args: IF argsTemp = NIL THEN MimP5U.MakeArgList[sigTemp] ELSE MimP5U.MakeArgList2[sigTemp, argsTemp]], loc: LOOPHOLE[tb[node].info, SourceMap.Loc] ] ]; RETURN [MimP5U.MakeBlock[cl]]; }; MakeString: PUBLIC PROC [t: Tree.Link] RETURNS [Node] = { WITH e: t SELECT TreeOps.GetTag[t] FROM string => { sti: Literals.STIndex ฌ e.index; string: LONG STRING ฌ LiteralOps.StringValue[sti]; local: BOOL ฌ FALSE; DO WITH s: stb[sti] SELECT FROM heap => { kind: IntCodeDefs.RefLitKind ฌ rope; SELECT s.type FROM MimData.idATOM => kind ฌ atom; MimData.idTEXT => kind ฌ refText; ENDCASE => kind ฌ rope; RETURN [z.NEW[NodeRep.const.refLiteral ฌ [ bits: bitsPerPtr, details: const[data: refLiteral[ litKind: kind, contents: ConvertUnsafe.ToRope[string]]]]]]; }; copy => {sti ฌ s.link; local ฌ TRUE}; master => { align: NAT ฌ Target.bitsPerWord; nchars: INT ฌ string.length; extras: NAT ฌ bytesPerWord - (nchars MOD bytesPerWord); bits: INT ฌ (nchars+extras)*bitsPerChar + 2*bitsPerStringBound; init: Node ฌ z.NEW[NodeRep.const.bytes ฌ [ bits: bits, details: const[bytes[align, RopeHoldingStringRep[string]]]]]; IF local THEN init ฌ MimP5U.MakeTemp[cl: localProcCodeList, bits: bits, init: init].var; RETURN [MimP5U.Address[init]]; }; ENDCASE => ERROR; ENDLOOP; }; ENDCASE => ERROR; }; RopeHoldingStringRep: PROC [string: LONG STRING] RETURNS [ByteSequence] = { nchars: CARDINAL ฌ string.length; writer: TargetConversions.Writer ฌ TargetConversions.NewWriter[]; extras: NAT ฌ bytesPerWord - (nchars MOD bytesPerWord); TargetConversions.PutCard[writer, nchars, bitsPerStringBound]; TargetConversions.PutCard[writer, nchars+extras, bitsPerStringBound]; FOR i: CARDINAL IN [0..nchars) DO TargetConversions.PutChar[writer, string[i]]; ENDLOOP; THROUGH [0..extras) DO TargetConversions.PutChar[writer, 0C]; ENDLOOP; RETURN [TargetConversions.WriterContents[writer]]; }; StringInit: PUBLIC PROC [node: Tree.Index] RETURNS [Node] = { cl: CodeList ฌ substState.cl; nchars: INT = ConstArith.ToInt[MimP5U.TreeLiteralValue[tb[node].son[2]]]; bits: INT ฌ bitsPerChar*nchars + bitsPerStringBound*2; bodyVar: Var ฌ IF mainBody THEN MakeGlobal[bits].v ELSE MimP5U.MakeTemp[cl, bits].var; field: Var ฌ MimP5U.TakeFieldVar[bodyVar, 0, bitsPerStringBound*2]; list: NodeList ฌ MimP5U.MakeNodeList2[ MimP5U.MakeConstCard[0, bitsPerStringBound], -- length MimP5U.MakeConstCard[nchars, bitsPerStringBound] -- max length ]; init: Node ฌ MimP5U.MakeComposite[list, bitsPerStringBound*2]; MimP5U.MoreCode[cl, MimP5U.Assign[lhs: field, rhs: init]]; RETURN [MimP5U.ApplyOp[ MimP5U.MesaOpNode[addr], MimP5U.MakeNodeList[bodyVar], bitsPerPtr]]; }; Subst: PUBLIC PROC [node: Tree.Index, resultType: Type] RETURNS [result: Node ฌ NIL] = { oldInInline: BOOL = inInline; oldSubstState: SubstState = substStateญ; declCL: CodeList ฌ MimP5U.NewCodeList[]; cl: CodeList ฌ MimP5U.NewCodeList[]; bits: INT = MimP5U.BitsForType[resultType]; resultVar: Var = IF resultType = nullType THEN NIL ELSE MimP5U.MakeTemp[declCL, bits].var; argType: RecordSEIndex = SymbolOps.TransferTypes[SymbolOps.own, MimP5U.OperandType[tb[node].son[1]]].typeIn; stmtList: Tree.Link = tb[node].son[2]; inInline ฌ TRUE; substStateญ ฌ [resultType: resultType, resultVar: resultVar, cl: cl]; { stmtNode: Node ฌ MimP5.StatementTree[stmtList]; IF substState.lastResultGoTo # NIL THEN IF NOT IntCodeStuff.NodeContains[stmtNode, substState.lastResultGoTo] THEN substState.exitLabel ฌ NIL; WITH stmtNode SELECT FROM block: BlockNode => MimP5U.AppendNodeList[cl, block.nodes]; ENDCASE => MimP5U.MoreCode[cl, stmtNode]; IF substState.exitLabel # NIL THEN MimP5U.InsertLabel[cl, substState.exitLabel]; IF substState.lock # Tree.Null THEN LocalReleaseLock[cl, substState.lock]; IF substState.postfixCL # NIL THEN cl ฌ ApplyPrefixAndPostfix[MimP5U.ExtractList[cl]]; IF resultType # nullType THEN { list: NodeList ฌ MimP5U.ExtractList[cl]; IF list # NIL AND list.rest = NIL THEN WITH StripSource[list.first] SELECT FROM assign: REF NodeRep.assign => IF assign.lhs = resultVar THEN { result ฌ assign.rhs; GO TO simple; }; ENDCASE; MimP5U.AppendNodeList[declCL, list]; cl ฌ declCL; MimP5U.MoreCode[cl, resultVar]; }; result ฌ MimP5U.MakeBlock[cl, bits]; EXITS simple => {}; }; inInline ฌ oldInInline; substStateญ ฌ oldSubstState; RETURN [result]; }; PushContext: PUBLIC PROC [label: Label, cl: CodeList, inner: PROC] = { saveCaseCV: Node = caseCV; saveCaseType: Symbols.Type = caseType; oldInInline: BOOL = inInline; oldProcCodeList: CodeList = localProcCodeList; oldSubstState: SubstState = substStateญ; enclosingContext: Label ฌ visibleContext[curctxlvl]; curctxlvl ฌ curctxlvl + 1; catchcount ฌ catchcount + 1; visibleContext[curctxlvl] ฌ label; substStateญ ฌ [resultType: nullType, resultVar: NIL, cl: cl]; localProcCodeList ฌ cl; inner[]; catchcount ฌ catchcount - 1; curctxlvl ฌ curctxlvl - 1; inInline ฌ oldInInline; substStateญ ฌ oldSubstState; localProcCodeList ฌ oldProcCodeList; caseCV ฌ saveCaseCV; caseType ฌ saveCaseType; }; ApplyPrefixAndPostfix: PROC [list: NodeList] RETURNS [CodeList] = { prefixCL: CodeList = substState.prefixCL; postfixCL: CodeList = substState.postfixCL; afterLabel: Label = MimP5U.AllocLabel[]; MimP5U.AppendNodeList[prefixCL, list]; MimP5U.Jump[prefixCL, afterLabel]; MimP5U.AppendNodeList[prefixCL, MimP5U.ExtractList[postfixCL]]; MimP5U.InsertLabel[prefixCL, afterLabel]; substState.prefixCL ฌ NIL; substState.postfixCL ฌ NIL; RETURN [prefixCL]; }; BlockTail: PROC [node: Node] RETURNS [NodeList] = { nodes: NodeList ฌ NIL; WITH node SELECT FROM block: BlockNode => nodes ฌ block.nodes; source: SourceNode => nodes ฌ source.nodes; ENDCASE => RETURN [NIL]; WHILE nodes # NIL DO next: NodeList ฌ nodes.rest; IF next = NIL THEN WITH nodes.first SELECT FROM block: BlockNode => next ฌ block.nodes; source: SourceNode => next ฌ source.nodes; ENDCASE => RETURN [nodes]; nodes ฌ next; ENDLOOP; RETURN [NIL]; }; CodePassError: ERROR [n: CARDINAL] = CODE; CollectConstants: PROC [cl: CodeList] = { EachBody: PROC [bti: Symbols.BTIndex] RETURNS [stop: BOOL ฌ FALSE] = { const: BOOL ฌ TRUE; inAssign: BOOL ฌ FALSE; Mapper: Tree.Map = { v ฌ t; WITH e: t SELECT TreeOps.GetTag[t] FROM subtree => IF t # Tree.Null THEN { node: Tree.Index = e.index; name: Tree.NodeName = tb[e.index].name; oldConst: BOOL ฌ const; needsMap: BOOL ฌ FALSE; const ฌ TRUE; SELECT name FROM mwconst, construct, rowcons, all, union, cast, pad, lengthen, shorten => { const ฌ MimP5S.WillEvalToConst[t, TRUE]; IF const THEN { bits: INT ฌ MimP5U.BitsForOperand[t]; IF inAssign AND noCollectConstAssigns THEN GO TO noCollect; IF bits < minMemoBits OR bits > maxMemoBits THEN GO TO noCollect; FOR each: ConstList ฌ constListHead, each.rest WHILE each # NIL DO IF each.bits # bits THEN LOOP; IF each.name # name THEN LOOP; IF TreeSame[t, each.tree] THEN { v ฌ each.var; each.usesฌ each.uses + 1; const ฌ oldConst; GO TO done; }; ENDLOOP; { node: Node ฌ MimP5.Exp[t]; lcl: CodeList = MimP5U.NewCodeList[]; type: Type ฌ MimP5U.TypeForTree[t]; temp: Var; sei: ISEIndex; [temp, sei] ฌ MakeGlobal[bits, MimP5U.TypeForTree[t]]; v ฌ [symbol[sei]]; constListHead ฌ z.NEW[ConstEntry ฌ [ rest: constListHead, var: v, bits: bits, uses: 1, name: name, tree: t ]]; node ฌ MimP5S.ComAssign[v, t, [ init: TRUE, counted: SymbolOps.RCType[SymbolOps.own, type] # none, skipZeros: TRUE]]; MimP5U.MoreCode[lcl, node]; MimP5U.MoreCode[cl, MimP5U.MakeBlock[lcl]]; seb[sei].immutable ฌ TRUE; seb[sei].idDecl ฌ 2; }; const ฌ oldConst; GO TO done; }; }; ENDCASE; SELECT name FROM assign, assignx => { inAssign ฌ FALSE; tb[node].son[1] ฌ Mapper[tb[node].son[1]]; inAssign ฌ TRUE; tb[node].son[2] ฌ Mapper[tb[node].son[2]]; inAssign ฌ FALSE; }; decl => { init: Tree.Link = tb[node].son[3]; IF init # Tree.Null THEN { inAssign ฌ TRUE; tb[node].son[3] ฌ Mapper[tb[node].son[3]]; inAssign ฌ FALSE; }; }; new => { inAssign ฌ FALSE; tb[node].son[1] ฌ Mapper[tb[node].son[1]]; tb[node].son[2] ฌ Mapper[tb[node].son[2]]; inAssign ฌ TRUE; tb[node].son[3] ฌ Mapper[tb[node].son[3]]; inAssign ฌ FALSE; }; construct, rowcons => { tb[node].son[2] ฌ Mapper[tb[node].son[2]]; }; all, union, cast, pad, list, lengthen, shorten => { [] ฌ TreeOps.UpdateLeaves[e, Mapper]; }; ENDCASE => { inAssign ฌ FALSE; [] ฌ TreeOps.UpdateLeaves[e, Mapper]; }; GO TO noCollect; EXITS done => {}; noCollect => const ฌ FALSE; }; symbol => { const ฌ FALSE; FOR each: ConstList ฌ constListHead, each.rest WHILE each # NIL DO IF v = t THEN {const ฌ TRUE; EXIT}; ENDLOOP; }; ENDCASE; }; WITH body: bb[bti] SELECT FROM Callable => IF ~body.inline AND body.hints.pad = 0 THEN WITH bi: bb[bti].info SELECT FROM Internal => { bodyNode: Tree.Index ฌ bi.bodyTree; [] ฌ TreeOps.UpdateLeaves[[subtree[bodyNode]], Mapper]; }; ENDCASE; ENDCASE; }; TreeSame: PROC [t1, t2: Tree.Link] RETURNS [BOOL] = { IF t1 = t2 THEN RETURN [TRUE]; WITH e1: t1 SELECT TreeOps.GetTag[t1] FROM subtree => { tp1: Tree.NodePtr = @tb[e1.index]; WITH e2: t2 SELECT TreeOps.GetTag[t2] FROM subtree => { tp2: Tree.NodePtr = @tb[e2.index]; SELECT TRUE FROM tp1.name # tp2.name => {}; tp1.nSons # tp2.nSons => {}; tp1.info # tp2.info => {}; tp1.subInfo # tp2.subInfo => {}; tp1.attr1 # tp2.attr1 => {}; tp1.attr2 # tp2.attr2 => {}; tp1.attr3 # tp2.attr3 => {}; ENDCASE => { FOR i: NAT IN [1..tp1.nSons] DO IF NOT TreeSame[tp1.son[i], tp2.son[i]] THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE]; }; }; ENDCASE; }; ENDCASE; RETURN [FALSE]; }; ConstList: TYPE = REF ConstEntry; ConstEntry: TYPE = RECORD [ rest: ConstList, var: Tree.Link, bits: INT, uses: INT, name: Tree.NodeName, tree: Tree.Link]; constListHead: ConstList ฌ NIL; [] ฌ SymbolOps.EnumerateBodies[SymbolOps.own, Symbols.RootBti, EachBody]; WHILE constListHead # NIL DO next: ConstList ฌ constListHead.rest; z.FREE[@constListHead]; constListHead ฌ next; ENDLOOP; }; CommonRet: PROC [t: Tree.Link] RETURNS [common: BOOL ฌ TRUE] = { sei: ISEIndex; Item: PROC [t: Tree.Link] RETURNS [BOOL] = { WITH t SELECT TreeOps.GetTag[t] FROM symbol => common ฌ (sei = index); literal, subtree => common ฌ FALSE; ENDCASE; IF sei # ISENull THEN sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; RETURN [~common] }; IF t = Tree.Null THEN RETURN; IF bodyOutRecord # CSENull THEN sei ฌ MimP5U.NextVar[ctxb[seb[bodyOutRecord].fieldCtx].seList] ELSE RETURN [FALSE]; TreeOps.SearchList[t, Item]; }; FindProcDesc: PROC [bti: CBTIndex] RETURNS [new: REF ProcDescEntry ฌ NIL] = { SELECT bb[bti].kind FROM Outer => { new ฌ procDescRoot; }; Inner => { pBti: Symbols.BTIndex ฌ bti; DO pBti ฌ SymbolOps.ParentBti[SymbolOps.own, pBti]; WITH body: bb[pBti] SELECT FROM Callable => IF NOT body.inline AND body.hints.pad = 0 THEN { new ฌ MakeProcDesc[pBti].child; EXIT; }; ENDCASE; ENDLOOP; }; ENDCASE => ERROR; WHILE new # NIL DO IF new.bti = bti THEN RETURN; new ฌ new.rest; ENDLOOP; }; GetFormals: PROC [irecord: RecordSEIndex] RETURNS [VarList] = { IF irecord = CSENull THEN RETURN [NIL]; RETURN [VarsForCtx[seb[irecord].fieldCtx]]; }; IsVarInCtx: PROC [sei: ISEIndex, ctx: CTXIndex] RETURNS [BOOL] = { IF ctx # CTXNull THEN { each: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; WHILE each # ISENull DO IF sei = each THEN RETURN [TRUE]; each ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, each]]; ENDLOOP; }; RETURN [FALSE]; }; ListNeedsTemp: PROC [nodeList: NodeList] RETURNS [BOOL] = { FOR each: NodeList ฌ nodeList, each.rest WHILE each # NIL DO IF NeedsTemp[each.first] THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; LocalReleaseLock: PROC [cl: CodeList, lock: Tree.Link] = { node: Node = MimP5.Exp[lock]; rel: Node = MimP5U.ApplyOp[ oper: MimP5U.MesaOpNode[monitorExit], args: MimP5U.MakeArgList[MimP5U.Address[node]], bits: 0]; MimP5U.MoreCode[cl, rel]; }; MakeListNice: PROC [cl: CodeList, nodeList: NodeList] = { FOR each: NodeList ฌ nodeList, each.rest WHILE each # NIL DO n: Node = each.first; IF NeedsTemp[n] THEN each.first ฌ MimP5S.Temporize[cl, n]; ENDLOOP; }; MakeProcDesc: PROC [bti: BTIndex] RETURNS [new: REF ProcDescEntry ฌ NIL] = { parent: REF ProcDescEntry ฌ NIL; IF bti = Symbols.BTNull THEN ERROR; WITH body: bb[bti] SELECT FROM Callable => { IF body.inline THEN ERROR; SELECT body.kind FROM Outer => new ฌ procDescRoot; Inner => { pBti: Symbols.BTIndex ฌ bti; DO pBti ฌ SymbolOps.ParentBti[SymbolOps.own, pBti]; WITH bb[pBti] SELECT FROM Callable => { parent ฌ MakeProcDesc[pBti]; new ฌ parent.child; EXIT; }; ENDCASE; ENDLOOP; }; ENDCASE => ERROR; }; ENDCASE => ERROR; WHILE new # NIL DO IF new.bti = bti THEN RETURN; new ฌ new.rest; ENDLOOP; IF new = NIL THEN { label: Label = MimP5U.AllocLabel[id: LOOPHOLE[bti]]; directEntry: Node ฌ MimP5U.LabelAddress[label, TRUE]; indirectEntry: Node ฌ MimP5U.LabelAddress[label, FALSE]; new ฌ z.NEW[ProcDescEntry ฌ [ bti: LOOPHOLE[bti], parent: parent, directEntry: directEntry, indirectEntry: indirectEntry, label: label]]; WITH b: bb[bti] SELECT FROM Callable => { out: STREAM ฌ IO.ROS[]; PrintSei[out, b.id]; new.name ฌ IO.RopeFromROS[out]; IF LOOPHOLE[bti, CARD] > LOOPHOLE[maxBti, CARD] THEN { maxBti ฌ LOOPHOLE[bti]; }; }; ENDCASE; IF parent = NIL THEN { new.rest ฌ procDescRoot; procDescRoot ฌ new; } ELSE { new.rest ฌ parent.child; parent.child ฌ new; }; }; }; Module: PROC RETURNS [Node] = { bodies: CodeList ฌ MimP5U.NewCodeList[]; Body1: PROC [bti: Symbols.BTIndex] RETURNS [stop: BOOL ฌ FALSE] = { WITH body: bb[bti] SELECT FROM Callable => IF ~body.inline AND body.hints.pad = 0 THEN [] ฌ MakeProcDesc[bti]; ENDCASE; }; Body2: PROC [bti: Symbols.BTIndex] RETURNS [stop: BOOL ฌ FALSE] = { WITH body: bb[bti] SELECT FROM Callable => IF ~body.inline AND body.hints.pad = 0 THEN MimP5U.MoreCode[bodies, ProcBody[LOOPHOLE[bti]]]; ENDCASE; }; (MimData.table).AddNotify[DriverNotify]; maxBti ฌ Symbols.RootBti; procDescRoot ฌ NIL; linkToVarSeq ฌ NIL; inInline ฌ FALSE; visibleContext ฌ z.NEW[MimP5.VisibalContextArray ฌ ALL[NIL]]; substState ฌ z.NEW[SubstState ฌ []]; { modNode ฌ z.NEW[module NodeRep ฌ [details: module[vars: VarsForCtx[MimData.mainCtx], procs: NIL]]]; maxGlobalVarId ฌ 0; FOR each: VarList ฌ modNode.vars, each.rest WHILE each # NIL DO id: INT ฌ each.first.id; IF id > maxGlobalVarId THEN maxGlobalVarId ฌ id; ENDLOOP; modVarsTail ฌ modNode.vars; IF modVarsTail # NIL THEN WHILE modVarsTail.rest # NIL DO modVarsTail ฌ modVarsTail.rest; ENDLOOP; bodyInRecord ฌ bodyOutRecord ฌ RecordSENull; MimP5U.CgenUtilInit[MimData.table]; inlineFileLoc ฌ SourceMap.nullLoc; xtracting ฌ FALSE; caseCV ฌ NIL; catchoutrecord ฌ RecordSENull; [] ฌ SymbolOps.EnumerateBodies[SymbolOps.own, Symbols.RootBti, Body1]; IF MimData.nSigCodes # 0 THEN signalsVar ฌ MakeGlobal[MimData.nSigCodes*bitsPerWord].v; [] ฌ SymbolOps.EnumerateBodies[SymbolOps.own, Symbols.RootBti, Body2]; modNode.procs ฌ bodies.head; }; z.FREE[@visibleContext]; z.FREE[@substState]; RETURN [modNode] }; NeedsTemp: PROC [node: Node] RETURNS [BOOL] = { n: Node ฌ node; WHILE n # NIL DO list: NodeList ฌ NIL; WITH n SELECT FROM v: REF NodeRep.var => { IF v.flags[constant] THEN EXIT; WITH v.location SELECT FROM local: REF LocationRep.localVar => RETURN [FALSE]; dummy: REF LocationRep.dummy => RETURN [FALSE]; field: REF LocationRep.field => {n ฌ field.base; LOOP}; indexed: REF LocationRep.indexed => IF NeedsTemp[indexed.base] THEN RETURN [TRUE] ELSE {n ฌ indexed.index; LOOP}; comp: REF LocationRep.composite => list ฌ comp.parts; ENDCASE; RETURN [TRUE]; }; c: REF NodeRep.const => EXIT; block: REF NodeRep.block => list ฌ block.nodes; decl: REF NodeRep.decl => {n ฌ decl.init; LOOP}; assign: REF NodeRep.assign => IF NeedsTemp[assign.lhs] THEN RETURN [TRUE] ELSE {n ฌ assign.rhs; LOOP}; cond: REF NodeRep.cond => { FOR each: CaseList ฌ cond.cases, each.rest WHILE each # NIL DO IF ListNeedsTemp[each.tests] THEN RETURN [TRUE]; IF NeedsTemp[each.body] THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; label: REF NodeRep.label => {n ฌ label.label.node; LOOP}; apply: REF NodeRep.apply => { WITH apply.proc SELECT FROM oper: REF NodeRep.oper => SELECT oper.oper.kind FROM arith, boolean, convert, check, compare, mesa, cedar => { IF apply.handler # NIL THEN RETURN [TRUE]; list ฌ apply.args; }; ENDCASE => RETURN [TRUE]; ENDCASE => RETURN [TRUE]; }; source: REF NodeRep.source => list ฌ source.nodes; ENDCASE => RETURN [TRUE]; WHILE list # NIL DO IF NeedsTemp[list.first] THEN RETURN [TRUE]; list ฌ list.rest; IF list = NIL THEN RETURN [FALSE]; ENDLOOP; EXIT; ENDLOOP; RETURN [FALSE]; }; NodesForCtx: PROC [ctx: CTXIndex] RETURNS [vl: NodeList ฌ NIL] = { IF ctx # CTXNull THEN { tail: NodeList ฌ NIL; sei: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; UNTIL sei = ISENull DO var: Var ฌ MimP5.VarForSei[sei]; this: NodeList ฌ MimP5U.MakeNodeList[var]; IF tail = NIL THEN vl ฌ this ELSE tail.rest ฌ this; tail ฌ this; sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; ENDLOOP; }; }; PrintSei: PROC [st: STREAM, sei: Symbols.ISEIndex] = TRUSTED { name: Symbols.Name = SymbolOps.NameForSe[SymbolOps.own, sei]; s: ConvertUnsafe.SubString ฌ SymbolOps.SubStringForName[SymbolOps.own, name]; FOR i: CARDINAL IN [s.offset..s.offset+s.length) DO IO.PutChar[st, s.base[i]]; ENDLOOP; }; RopeForSei: PROC [sei: Symbols.ISEIndex] RETURNS [ROPE] = TRUSTED { name: Symbols.Name = SymbolOps.NameForSe[SymbolOps.own, sei]; s: ConvertUnsafe.SubString ฌ SymbolOps.SubStringForName[SymbolOps.own, name]; i: CARDINAL ฌ s.offset; eachChar: SAFE PROC RETURNS [c: CHAR] = TRUSTED { c ฌ s.base[i]; i ฌ i + 1; }; RETURN [Rope.FromProc[len: s.length, p: eachChar]]; }; ProcBody: PROC [bti: Symbols.CBTIndex] RETURNS [Node] = { oldSubstState: SubstState ฌ substStateญ; oldLocalProcList: CodeList ฌ localProcCodeList; cl: CodeList ฌ MimP5U.NewCodeList[]; desc: REF ProcDescEntry ฌ FindProcDesc[bti]; procLabel: Label ฌ desc.label; lambda: LambdaNode ฌ NIL; enclosingContext: Label ฌ NIL; substStateญ ฌ [cl: cl, prefixCL: NIL, postfixCL: NIL]; localProcCodeList ฌ cl; mainBody ฌ (bti = Symbols.RootBti); MimData.bodyIndex ฌ bti; MimData.textIndex ฌ SourceMap.Up[bb[bti].sourceIndex]; WITH bi: bb[bti].info SELECT FROM Internal => { bodyNode: Tree.Index ฌ bi.bodyTree; kind: IntCodeDefs.LambdaKind ฌ outer; curctxlvl ฌ bb[bti].level; FOR pd: REF ProcDescEntry ฌ desc, pd.parent WHILE pd # NIL DO pBti: CBTIndex = pd.bti; level: Symbols.ContextLevel ฌ bb[pBti].level; visibleContext[level] ฌ pd.label; ENDLOOP; IF curctxlvl >= Symbols.lL THEN { enclosingContext ฌ visibleContext[curctxlvl.PRED]; IF curctxlvl > Symbols.lL THEN kind ฌ inner; }; IF mainBody THEN kind ฌ init; [bodyInRecord, bodyOutRecord] ฌ SymbolOps.TransferTypes[SymbolOps.own, bb[bti].ioType]; fileLoc ฌ SourceMap.Up[bb[bti].sourceIndex]; tailJumpOK ฌ TRUE; SymbolOps.SetCtxLevel[tempcontext, curctxlvl]; lambda ฌ z.NEW[NodeRep.lambda ฌ [details: lambda[ parent: enclosingContext, kind: kind, descBody: NIL, bitsOut: MimP5U.BitsForType[bodyOutRecord], formalArgs: GetFormals[bodyInRecord], body: NIL]]]; substState.resultType ฌ bodyOutRecord; IF bodyOutRecord # RecordSENull THEN { ctx: Symbols.CTXIndex = seb[bodyOutRecord].fieldCtx; sei: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; IF MimP5U.BitsForType[bodyOutRecord] > bitsPerWord THEN { substState.exitLabel ฌ MimP5U.AllocLabel[]; }; UNTIL sei = ISENull DO MimP5U.Declare[cl: cl, var: MimP5.VarForSei[sei]]; sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; ENDLOOP; }; IF bti = Symbols.RootBti THEN { FOR each: REF ProcDescEntry ฌ procDescRoot, each.rest WHILE each # NIL DO bodyVar: Var ฌ MakeGlobal[bitsPerProcDesc].v; WITH bodyVar.location SELECT FROM glob: REF LocationRep.globalVar => bb[each.bti].frameOffset ฌ glob.id / Target.bitsPerAU; ENDCASE => ERROR; each.body ฌ bodyVar; bodyVar.flags[constant] ฌ TRUE; ENDLOOP; IF collectConstants THEN CollectConstants[cl]; }; FOR each: REF ProcDescEntry ฌ desc.child, each.rest WHILE each # NIL DO init: Node ฌ MimP5U.MakeComposite[ MimP5U.MakeNodeList2[each.indirectEntry, MimP5U.MakeConstCard[1]], bitsPerProcDesc]; each.body ฌ MimP5S.Temporize[cl, init]; ENDLOOP; mLock ฌ Tree.Null; IF bb[bti].entry THEN { IF substState.exitLabel # NIL THEN substState.exitLabel ฌ MimP5U.AllocLabel[]; mLock ฌ tb[bodyNode].son[4]; SetLock[cl, mLock]; }; MimP5.DeclList[cl, tb[bodyNode].son[2]]; MimP5.StatementList[cl, tb[bodyNode].son[3]]; IF substState.exitLabel # NIL THEN { MimP5U.InsertLabel[cl, substState.exitLabel]; IF mLock # Tree.Null THEN LocalReleaseLock[cl, mLock]; IF bodyOutRecord # RecordSENull THEN { ctx: Symbols.CTXIndex = seb[bodyOutRecord].fieldCtx; sei: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; head: NodeList ฌ NIL; tail: NodeList ฌ NIL; UNTIL sei = ISENull DO new: NodeList ฌ MimP5U.MakeArgList[MimP5.VarForSei[sei]]; IF tail = NIL THEN head ฌ new ELSE tail.rest ฌ new; tail ฌ new; sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; ENDLOOP; MimP5U.MoreCode[cl, MimP5U.MakeReturn[head]]; }; }; }; ENDCASE; lambda.body ฌ MimP5U.MakeNodeList[MimP5.WrapSourceBlock[cl, bti, FALSE]]; IF substState.postfixCL # NIL THEN lambda.body ฌ MimP5U.ExtractList[ApplyPrefixAndPostfix[lambda.body]]; lambda.body ฌ MimP5U.MakeNodeList[ z.NEW[NodeRep.comment ฌ [details: comment[Rope.Concat[desc.name, ":"]]]], lambda.body]; IF desc.parent # NIL THEN lambda.descBody ฌ desc.body; procLabel.node ฌ lambda; substStateญ ฌ oldSubstState; localProcCodeList ฌ oldLocalProcList; RETURN [z.NEW[NodeRep.label ฌ [details: label[procLabel]]]]; }; SetLock: PROC [cl: CodeList, lock: Tree.Link] = { node: Node ฌ MimP5.Exp[lock]; set: Node = MimP5U.ApplyOp[ oper: MimP5U.MesaOpNode[monitorEntry], args: MimP5U.MakeArgList[MimP5U.Address[node]], bits: 0]; MimP5U.MoreCode[cl, set]; }; StripExtraDecl: PROC [node: Node] RETURNS [Node] = { WITH StripSource[node] SELECT FROM block: REF NodeRep.block => { list: NodeList ฌ block.nodes; IF list # NIL THEN WITH StripSource[list.first] SELECT FROM decl: REF NodeRep.decl => IF decl.init = NIL THEN { r1: NodeList ฌ list.rest; IF r1 # NIL THEN { var: Var ฌ decl.var; IF var # NIL AND NOT var.flags[named] THEN { n2: Node ฌ StripSource[r1.first]; WITH StripSource[r1.first] SELECT FROM assign: REF NodeRep.assign => IF assign.lhs = var THEN { r2: NodeList ฌ r1.rest; IF r2 # NIL AND r2.rest = NIL THEN WITH StripSource[r2.first] SELECT FROM rvar: Var => IF rvar = var THEN RETURN [assign.rhs]; ENDCASE; }; ENDCASE; }; }; }; ENDCASE; }; ENDCASE; RETURN [node]; }; StripSource: PROC [node: Node] RETURNS [Node] = { DO WITH node SELECT FROM source: SourceNode => { nodes: NodeList ฌ source.nodes; IF nodes # NIL AND nodes.rest = NIL THEN {node ฌ nodes.first; LOOP}; }; ENDCASE; RETURN [node]; ENDLOOP; }; ClearProcDesc: PROC [pd: REF ProcDescEntry] = { WHILE pd # NIL DO next: REF ProcDescEntry ฌ pd.rest; ClearProcDesc[pd.child]; pdญ ฌ []; pd ฌ next; ENDLOOP; }; ClearNodes: IntCodeUtils.Visitor = CHECKED { IF node # NIL THEN IntCodeUtils.MapNode[node, ClearNodes]; RETURN [NIL]; }; VarsForCtx: PROC [ctx: CTXIndex] RETURNS [vl: VarList ฌ NIL] = { IF ctx # CTXNull THEN { tail: VarList ฌ NIL; sei: ISEIndex ฌ MimP5U.NextVar[ctxb[ctx].seList]; UNTIL sei = ISENull DO var: Var ฌ MimP5.VarForSei[sei]; this: VarList ฌ MimP5U.MakeVarList[var]; IF tail = NIL THEN vl ฌ this ELSE tail.rest ฌ this; tail ฌ this; sei ฌ MimP5U.NextVar[SymbolOps.NextSe[SymbolOps.own, sei]]; ENDLOOP; }; }; RewriteSymbols: PROC [baseModel: IntCodeTwig.BaseModel] = { modNode: ModuleNode = NARROW[baseModel.module]; FOR each: VarList ฌ modNode.vars, each.rest WHILE each # NIL DO var: Var ฌ each.first; IF var # NIL THEN WITH var.location SELECT FROM glob: REF LocationRep.globalVar => { id: INT ฌ var.id; IF id > 0 AND id <= maxGlobalVarId THEN { sei: Symbols.SEIndex ฌ Symbols.SENull + id; WITH se: seb[sei] SELECT FROM id => IF se.idCtx = MimData.mainCtx THEN se.idValue ฌ SymbolOps.EncodeBitAddr[[glob.id]]; ENDCASE; CopyVarFlags[var]; }; }; ENDCASE; ENDLOOP; FOR lambda: IntCodeTwig.LambdaModel ฌ baseModel.first, lambda.next WHILE lambda # NIL DO label: Label = lambda.label; IF label # NIL AND LOOPHOLE[label.id, CARD] <= LOOPHOLE[maxBti, CARD] THEN { bti: CBTIndex = LOOPHOLE[label.id]; MakeSpecialVar: PROC [var: Var, kind: Symbols.SpecialVarKind] = { IF var # NIL THEN WITH var.location SELECT FROM local: REF LocationRep.localVar => { bits: INT ฌ var.bits; sei: ISEIndex ฌ SymbolOps.MakeCtxSe[Symbols.HTNull, Symbols.CTXNull]; ctx: CTXIndex = bb[bti].localCtx; WITH se: seb[sei] SELECT FROM linked => se.link ฌ ctxb[ctx].seList; ENDCASE => ERROR; seb[sei].idInfo ฌ SymbolOps.EncodeInt[bits]; seb[sei].idType ฌ typeANY; seb[sei].special ฌ kind; var.id ฌ LOOPHOLE[sei - Symbols.ISENull]; ctxb[ctx].seList ฌ sei; }; ENDCASE; }; MakeSpecialVar[lambda.frameExtension, frameExtension]; MakeSpecialVar[lambda.globalLink, globalLink]; MakeSpecialVar[lambda.staticLink, staticLink]; MakeSpecialVar[lambda.memoryLink, memoryLink]; MakeSpecialVar[lambda.returnVar, returnLink]; }; [] ฌ CopyFlags[lambda.lambda]; ENDLOOP; }; CopyFlags: IntCodeUtils.Visitor = TRUSTED { WITH node SELECT FROM decl: REF NodeRep.decl => CopyVarFlags[decl.var]; lambda: REF NodeRep.lambda => FOR each: VarList ฌ lambda.formalArgs, each.rest WHILE each # NIL DO CopyVarFlags[each.first]; ENDLOOP; ENDCASE; IntCodeUtils.MapNode[node, CopyFlags]; RETURN [node]; }; CopyVarFlags: PROC [var: Var] = { IF var # NIL THEN { flags: IntCodeDefs.VariableFlags = var.flags; IF flags[named] THEN { sei: ISEIndex = Symbols.ISENull + CARD[var.id]; new: Symbols.VariableFlags ฌ seb[sei].flags; IF flags[upLevel] THEN { offset: INT ฌ 0; new.upLevel ฌ new.valid ฌ TRUE; WITH var.location SELECT FROM field: REF LocationRep.field => offset ฌ field.start; ENDCASE; seb[sei].idValue ฌ SymbolOps.EncodeBitAddr[[offset]]; }; IF flags[addressed] THEN new.addressed ฌ new.valid ฌ TRUE; IF flags[assigned] THEN new.assigned ฌ new.valid ฌ TRUE; IF flags[used] THEN new.used ฌ new.valid ฌ TRUE; IF flags[constant] THEN seb[sei].immutable ฌ TRUE; seb[sei].flags ฌ new; }; }; }; }. #่ MimDriver.mesa Copyright ำ 1985, 1986, 1987, 1988, 1989, 1990, 1991 by Xerox Corporation. All rights reserved. Sweet, June 1, 1986 9:01:50 pm PDT Satterthwaite, November 26, 1985 1:40:30 pm PST Russ Atkinson (RRA) February 21, 1990 1:02:54 pm PST Willie-s, September 24, 1991 4:48 pm PDT Options sometimes set from the interpreter Is this a good idea? Is there reason to have less? Pretty arbitrary, really Imported definitions Stuff exported to MimCode initialized to 0 and 1 constants by CGenUtil initialized to TRUE and FALSE constants by CGenUtil Bases & notifier called by allocator whenever table area is repacked Variables & local types The maximum bti seen (used for ease of verification) The tail of the module variables list. Useful for adding global vars. The maximum global variable id (generated from MimData.mainCtx) Number of refs in the overhead for interface records. (This needs to be parameterized) TRUE if a link needs extra deref for vars. (This needs to be parameterized) Procedures exported to CompilerUtil starts the code generation pass Make a names stream and (optionally) a types stream There are simplifications necessary Now break open the cycles left in the models make sure that there are BodyRecord objects for all procedures Output to a simple stream (and icd extension) There is either an external code generator or we need a names file [node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node] We have been asked to generate a names file There is an external code generator to call Cleanup Procedures exported to MimCode Procedures exported to MimP5 Don't cache this address, since it will cause trouble when transforming the nodes later on. This is a built-in signal or error like UNWIND or ABORTED. Return a variable for the interface record Need to create a new link base This comes to us from an interface or some other link Procedures exported to MimP5 generate code for RETURN inside of INLINE procs monitored: BOOL _ tb[node].attr1; returningNoGlobals: BOOL _ tb[node].attr2; A compound variable Similar code to that in ComAssign. Make it a common routine some day? We have to take a field of the value for the destination. The destination is larger, so extend the value. If arithmetic or address extension, then use the conversion operators. We need a place to go to when we calculate a result produce code for RESUME generate code for RETURN returningNoGlobals: BOOL _ tb[node].attr2; This catches some layout errors The default return (or an explict return equivalent to the default return) There is a common return point that handles unlocking Make the world safe (this may go away when we finish rethinking the whole safen issue). There is a common return point that handles unlocking and returning We need to assign to the return values We have to evaluate these values into temporaries (sigh) generates code for procedure signal/error statement In this procedure we do not try to generate optimal code. After all, this construct is used when semantics is paramount, rather than efficiency. Therefore, all exceptions & their arguments use temporaries. The tricky (and important) part is to place the code that actually raisses the exception outside of the scope of any UNWIND that gets rid of the monitor lock. Make sure that we have both prefix & postfix code lists We need a prefix code list We need a postfix code list Process the safens first Evaluate the signal into a temporary Evaluate the arguments into a temporary (if there are any arguments) Monitored, so have to emit code to release the lock Finally, emit the jump to get to the handler Emit the code that actually raises the error (we emit this in the postfix to be in the right scope, which is outside the unwind for the monitor lock) See Pass4Xb.Exp for this convention Must make a local copy of the string For C we want strings to be null-terminated and padded out to a word This generates the address of anonymous string bodies. Currently we always generate them in the local frame unless we are generating code for the initialization procedure, in which case the bodies are placed in the global frame. RRA: note that if a lock was declared in this block (due to an INLINE ENTRY PROC) that the node structure needs flattening so we can put the unlock within the scope of the declaration (sigh). In other cases the flattening certainly does not hurt. There is a label from some inner result This is an INLINE ENTRY PROC We have to wrap up this "procedure" due to RETURN WITH ERROR There is a result variable, which should be outermost! The body was a single node The body was a single assignment The result of the INLINE is a simple expression! Private procedures & signals There is code that we must wrap around the procedure body to handle returning with error. The caller must ensure that substState.postfixCL # NIL. Emit the code we want wrapped In case control flows through to this point we must jump around the next code Exceptional code is emitted here Normal control resumes here The first pass is just to setup the proc desc structure and collect constants that were not processed previously. [t: Tree.Link] RETURNS [v: Tree.Link] These are the collectible constants Too small or too large to worry about memoizing The constant is already collected At this point we need to construct a new constant Indicate that this is a collected constant (see MimStore.WillEvalToConst & MimExpr.VarForSei) We are in an assignment node We are in an declaration node (only map the init) We are in a NEW node Don't change the value of inAssign, only map the second son Don't change the value of inAssign, map all sons Indicate that we are NOT in an assignment test if the returns list duplicats the returns declaration This routine finds the existing proc desc data for the given bti, returning NIL if not such structure is present. An outer level procedure (=> descriptor is in global frame) A nested procedure (we fill in the variable later) A catch phrase !?!? If proc desc data exists for the given bti, this routine returns it (just like FindProcDesc). Otherwise this routine makes up new proc desc data for the given bti, properly linking it into the tree structure, but not initializing the descriptor variables. An outer level procedure (=> descriptor is in global frame) A nested procedure (=> descriptor is in local frame) A catch phrase !?!? I don't understand! There was no previous entry, so make a new one. For each nested procedure we need to reserve a descriptor body. This consists of two words, where the first word contains the starting PC, and the second word contains 0 (for global variables) or 1 (for local variables). A procedure descriptor is then the address of the descriptor body. Maintain the maximum bti for bti verification An outer level procedure (=> descriptor is in global frame) A nested procedure (we fill in the variable later) main driver for code generation The first pass is just to setup the proc desc structure and collect constants that were not processed previously. The second pass actually generates the code. fill in procs below This routine tests for the node involving some quantity that could be monitor protected. As a basis, constants and constant variables are unprotected. Most compositions of unprotected nodes are also unprotected. These operations do not have side effects on data that are monitor protected unless their arguments can be monitor protected. produces code for body Fill in the context stack set up input and output contexts will fill in body field soon do type table and string literals declare return variable(s) initialize the proc descriptors Emit declarations for the descriptors for the main body procs. For each procedure we need to reserve a descriptor body and the descriptor. Collect constants into the global frame For each nested procedure we need to reserve a descriptor body and the descriptor. do initialization code and main body generate code for declaration initializations and statements There is a common unlock point and a common return point Note that the lambda node takes care of scoping. WrapSourceBlock can be told to avoid emitting a block node. We have to wrap up this procedure due to RETURN WITH ERROR Put the name as a comment in the front For blocks of the form {decl V; V _ E; V} Transform them into simply E A decl of an unnamed variable Followed by an assignment to that variable Followed by a return of that variable [node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node] Symbol Table Rewrite This module takes the result of IntCodeTwig processing and rewrites portions of the symbol table to accomodate the derived information. First take the offsets of the global variables and put them into the symbol table entries. This variable is (probably) from mainCtx Definitely from mainCtx, so fill in the offset Now make up variables for each "special" variable Since there should only be one instance of each special variable, shared from all places, this assignment should rename the variable globally. Remember to put this var into the context or all is for naught These flags need copying into the symbol table Copy not only the flags, but the offset ส;ฌ–(cedarcode) style•NewlineDelimiter ™head™Icodešœ ฯeœU™`Lšฯy"™"Lšž/™/L™4L™(L™šฯk ˜ LšœŸœ2˜=LšœŸœ ˜Lšœ Ÿœ˜LšœŸœ˜#Lšœ Ÿœ˜Lšœ Ÿœ ˜LšœŸœ&˜9Lšœ Ÿœฮ˜฿Lšœ Ÿœ#˜3Lšœ Ÿœ˜"Lšœ Ÿœ$˜5Lšœ Ÿœ˜,LšŸœŸœ/ŸœŸœ˜DLšœŸœ˜%Lšœ Ÿœ˜Lšœ Ÿœ˜'Lšะboัbko  ˜!LšœŸœ˜1LšœŸœ$˜1LšœŸœt˜Lšœ Ÿœ ˜LšœŸœu˜€Lšœ Ÿœ˜)LšœŸœ6˜BLšœŸœำ˜฿Lšœ Ÿœ˜LšœŸœ˜"Lšœ Ÿœ ˜LšœŸœŸœ˜$Lšœ Ÿœ˜#Lšœ Ÿœง˜ถLšœŸœ“˜ LšœŸœ7˜NLšœŸœŸœt˜“LšœŸœF˜PLšœŸœ,˜9——šฯn œŸ˜LšŸœtŸœ œš˜นLšŸœ)˜0LšŸœ˜"—™*LšœŸœŸœ˜LšœŸœŸœ˜%LšœŸœŸœ˜LšœŸœ˜LšœŸœŸœ˜#šœ ŸœŸœŸœ˜L™3—šœ ŸœŸœ˜-L™——™Lš ŸœŸœŸœŸœŸœŸœ˜LšŸœŸœŸœ˜LšŸœŸœŸœŸœ˜Lšœ Ÿœ˜ Lšœ Ÿœ˜"Lšœ Ÿœ˜"L˜$Lšœ Ÿœ˜"L˜$Lšœ Ÿœ˜"L˜$LšœŸœ˜,L˜3šœŸœ˜L˜"L˜ —L˜LšœŸœ+˜—L˜šŸœŸœ˜L™-LšœŸœ,˜7LšœŸœŸœ˜Lšœ,˜,LšŸœŸœŸœ#Ÿœ˜ -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]šœŸœ˜%LšœŸœ˜ šŸœŸœŸ˜šœ ŸœŸœŸ˜"Lšœ'˜'LšœŸœ ˜*LšŸœ˜—šœŸœŸœŸ˜.Lšœ Ÿœ˜LšœK˜KLšŸœ˜—LšŸœ˜—šŸœŸ˜LšœŸœ˜LšœŸœ ˜LšŸœŸœ ˜—L˜—LšœŸœ˜Lšœ%˜%Lšœ&˜&šŸœŸœŸœ˜L™+š ŸœŸœŸœŸœŸ˜4šŸœ ŸœŸ˜šœ ŸœŸœ˜&Lšœ˜LšœŸœ ˜'Lšœ Ÿœ˜LšŸœ(Ÿœ ˜>Lšœ˜LšœŸœ˜Lšœ˜L˜—šœŸœŸœ Ÿœ˜CLšœŸœ˜4LšŸœ*Ÿœ ˜>šŸœ ŸœŸ˜Lšœ'˜'LšŸœŸœ˜(—L˜—LšŸœ˜—LšŸœ˜—LšŸœ˜Lšœ!˜!L˜L˜—šŸœŸœŸœ˜L™+LšœŸœŸœ˜Lšœ+˜+Lšœ,˜,L˜š ŸœŸœŸœŸœŸ˜4šŸœ ŸœŸ˜šœ ŸœŸœ˜&Lšœ˜LšœŸœ ˜'Lšœ˜LšœŸœ˜LšœŸœ Ÿœ˜HL˜—šœŸœŸœ Ÿœ˜CLšœŸœ˜4šŸœ ŸœŸ˜šœ ˜ Lšœ ŸœŸœ˜G—LšŸœ˜—L˜—LšŸœ˜—LšŸœ˜—šœ ˜ Lšœ˜Lšœ˜LšœŸœn˜~Lšœ˜Lšœ˜Lšœ ˜ Lšœ˜—LšŸœŸœŸœ!˜2L˜—L˜—L˜™LšœŸœ˜L˜)Lšœ Ÿœ˜Lšœ Ÿœ˜ Lšœ Ÿœ˜Lšœ˜Lšœ Ÿœ˜Lšœ˜LšœŸœ˜LšœŸœ˜Lšœ*˜*Lšœ Ÿœ˜LšœŸœ˜—L˜L˜——™šขœŸ œ˜˜L˜L™—L˜šŸœŸœ˜L™J˜LšŸœŸœ Ÿœ˜I—šŸœŸœŸœ˜$Lšœ5™5Lšœ&˜&LšŸœ˜L˜—LšŸœ Ÿœ˜.LšŸœA˜GL˜—L˜šŸœ Ÿœ-˜?L™W—L˜šŸœ˜LšŸœ1˜5LšŸœ!Ÿœ˜0—L˜šŸœŸœŸœ˜$LšœC™CLšŸœŸœ˜9šŸœŸœ˜&L™&L˜4L˜1šŸœŸ˜Lšœ ˜ L˜šŸœ˜šŸœ˜L˜CL˜)L˜—LšŸœ˜—Lšœ-˜-L˜;LšŸœ˜—L˜—Lšœ&˜&LšŸœ˜L˜L˜—šŸœ Ÿœ˜L™8LšŸœŸœ˜9L˜L˜—L˜Lšœ6˜6L˜L˜—šข œŸœŸœŸœ ˜?L™3L™L™ฯL™žL˜L˜$L˜5L˜'L˜ Lšœ Ÿœ˜!Lšœ'˜'LšœŸœ˜LšœŸœ˜LšœŸœ˜Lšœ)˜)Lšœ+˜+L˜L™7šŸœ ŸœŸ˜L™Lšœ6˜6—šŸœ ŸœŸ˜L™Lšœ8˜8—L˜L™L˜)L˜L™$Lšœ2˜2Lšœ1˜1L˜L™DLšœŸœ˜(šŸœ ŸœŸœ˜LšœA˜ALšœ7˜7Lšœ6˜6L˜—L˜šŸœ Ÿœ˜.L™3—L˜L™,Lšœ˜L˜L™•Lšœ)˜)šœ˜šœ˜šœ˜Lšœ˜šœŸœ Ÿ˜LšŸœ˜ LšŸœ)˜-——LšœŸœ˜+Lšœ˜—Lšœ˜—L˜LšŸœ˜L˜L˜—šข œŸœŸœŸœ ˜9šŸœŸœŸ˜'šœ ˜ L˜ LšœŸœŸœ˜2LšœŸœŸœ˜šŸ˜šŸœ ŸœŸ˜˜ Lšœ$˜$šŸœŸ˜Lšœ˜Lšœ!˜!LšŸœ˜—šŸœŸœ˜*L˜˜ Lšœ˜L˜,——L˜—šœŸœ˜%L™#—˜ LšœŸœ˜ LšœŸœ˜LšœŸœŸœ˜7LšœŸœ6˜?šœŸœ˜*L˜ L˜=—šŸœŸ˜ L™$LšœJ˜J—LšŸœ˜L˜—LšŸœŸœ˜—LšŸœ˜—L˜—LšŸœŸœ˜—L˜L˜—š ขœŸœ ŸœŸœŸœ˜KLšœŸœ˜!L˜ALšœŸœŸœ˜7L˜>LšœE˜EšŸœŸœŸœ Ÿ˜!L˜-LšŸœ˜—šŸœ Ÿœ(Ÿœ˜FLšœD™D—LšŸœ,˜2L˜L˜—šข œŸœŸœŸœ ˜=L™ๅL˜LšœŸœ>˜ILšœŸœ-˜6LšœŸœ ŸœŸœ˜VL˜Cšœ&˜&Lšœ-ฃ ˜6Lšœ1ฃ ˜>L˜—Lšœ>˜>L˜:LšŸœV˜\L˜L˜—š ขœŸœŸœ&ŸœŸœ˜XLšœ Ÿœ ˜L˜(L˜(L˜$LšœŸœ"˜+Lš œŸœŸœŸœŸœ#˜ZLšœl˜lLšœ&˜&Lšœ Ÿœ˜LšœE˜E˜Lšœ/˜/šŸœŸœŸ˜'šŸœŸœ@Ÿ˜JLšœŸœ˜——šŸœ ŸœŸ˜šœ;˜;LšŸœ<ŸœŸœŸœง™๗—LšŸœ"˜)—šŸœŸœŸ˜"L™'L˜-—šŸœŸ˜#Lšœ ŸœŸœŸ™Lšœ&˜&—šŸœŸœŸ˜"Lšœ+ŸœŸœŸ™LšŸœŸœŸœŸœ˜0LšŸœŸœŸœŸœ˜+LšŸœ˜—LšŸœŸœ˜L˜—LšœŸœ)Ÿœ˜9šœŸœ˜šŸœ ŸœŸ˜šœŸœ˜šŸœŸ˜˜9L™}Lš ŸœŸœŸœŸœŸœ˜*Lšœ˜L˜—LšŸœŸœŸœ˜——LšŸœŸœŸœ˜—L˜—LšœŸœ'˜2LšŸœŸœŸœ˜—šŸœŸœŸ˜LšŸœŸœŸœŸœ˜-L˜Lš ŸœŸœŸœŸœŸœ˜"LšŸœ˜—LšŸœ˜LšŸœ˜—LšŸœŸœ˜L˜L˜—šข œŸœŸœŸœ˜BšŸœŸœ˜LšœŸœ˜L˜1šŸœŸ˜L˜ L˜*LšŸœŸœŸœ Ÿœ˜3L˜ L˜;LšŸœ˜—L˜—L˜L˜—šขœŸœŸœŸœ˜>Lšœ=˜=LšœM˜MšŸœŸœŸœŸ˜3LšŸœ˜LšŸœ˜—L˜L˜—š ข œŸœŸœŸœŸœ˜CLšœ=˜=LšœM˜MLšœŸœ ˜š œ ŸœŸœŸœŸœŸœ˜1Lšœ˜L˜ L˜—L–[len: INT, p: PROCšŸœ-˜3L˜L˜—šขœŸœŸœ ˜9L™L˜(L˜/L˜$LšœŸœ#˜,L˜LšœŸœ˜LšœŸœ˜L˜Lšœ!Ÿœ Ÿœ˜6Lšœ˜L˜L˜#L˜L˜6šŸœŸœŸ˜!˜ L˜#L˜%L˜š ŸœŸœ!ŸœŸœŸ˜=L™Lšœ˜Lšœ-˜-Lšœ!˜!LšŸœ˜—šŸœŸœ˜!Lšœ,Ÿœ˜2LšŸœŸœ˜,L˜—LšŸœ Ÿœ ˜L˜L™ L˜WL˜L˜,Lšœ Ÿœ˜L˜L˜.šœ ŸœTŸœZŸœ˜ฦL™—L˜L™!L˜Lšœ&˜&šŸœŸœ˜&L™L˜4L˜1šœ9˜9Lšœ+˜+L˜—šŸœŸ˜L˜2L˜;LšŸœ˜—L˜L˜—L™L˜šŸœŸœ˜L™>L˜š ŸœŸœ)ŸœŸœŸ˜IL™KL˜-šŸœŸœŸ˜!šœŸœ˜"L˜6—LšŸœŸœ˜—L˜LšœŸœ˜LšŸœ˜—L˜L™'LšŸœŸœ˜.L˜L˜—š ŸœŸœ'ŸœŸœŸ˜GL™R˜"LšœB˜BL˜—Lšœ'˜'LšŸœ˜—L˜L™$L˜Lšœ˜šŸœŸœ˜LšŸœŸœŸœ,˜NLšœ˜Lšœ˜L˜—L˜L™—L˜—LšŸœ˜—L˜—Lšœ6˜6Lšœ.˜.Lšœ.˜.Lšœ.˜.Lšœ-˜-L˜—Lšœ˜LšŸœ˜—L˜L˜—šข œŸœ˜+šŸœŸœŸ˜šœŸœ˜Lšœ˜—šœŸœ˜šŸœ.ŸœŸœŸ˜DLšœ˜LšŸœ˜——LšŸœ˜—Lšœ&˜&LšŸœ˜L˜L˜—šข œŸœ˜!šŸœŸœŸœ˜L™.L˜-šŸœŸœ˜Lšœ"Ÿœ ˜/Lšœ,˜,šŸœŸœ˜L™'LšœŸœ˜LšœŸœ˜šŸœŸœŸ˜LšœŸœ+˜5LšŸœ˜—Lšœ5˜5L˜—LšŸœŸœŸœ˜:LšŸœŸœŸœ˜8LšŸœ ŸœŸœ˜0LšŸœŸœŸœ˜2Lšœ˜L˜—L˜—L˜——˜L˜L˜——…—ณ$