<<>> <> <> <> <> <> DIRECTORY Basics32 USING [BITXOR], IntCodeDefs USING [ApplyNode, AssignNode, BlockNode, CaseList, CommentNode, CompositeLocation, ConstNode, DeclNode, DerefLocation, EnableNode, EscapeLocation, FieldLocation, GlobalVarLocation, GotoNode, Handler, HandlerRep, IndexedLocation, Label, LabelNode, LabelRep, LambdaNode, LocalVarLocation, Location, LocationRep, LogicalId, MachineCodeNode, ModuleNode, Node, NodeList, NodeRep, nullVariableId, OperNode, OperRep, ReturnNode, SourceNode, Var, VariableFlags, VarList, WordConstNode], IntCodeEnables USING [GenLabel, GenTemp, RewriteEnables], IntCodeOpt USING [CleanupLambda, GenAnonLocal, SimplifyValueBlocks], IntCodeStuff USING [addrOperNode, allocOperNode, BitsForArgList, constant0, constant1, constant2, CopyVar, defaultNIL, emptyReturn, freeOperNode, GenAddr, GenAnonVar, GenApply, GenAssign, GenBlock, GenComment, GenComposite, GenConst, GenDecl, GenDeref, GenDummy, GenField, GenFieldLoc, GenXFieldLoc, GenFieldLocOfVar, GenFieldOfDeref, GenFree, GenGoTo, GenLabelAddress, GenLargeReturn, GenReturn, GenUpLevel, globalLinkInit, MarkAddressed, MarkAssigned, MarkUsed, NodeContains, NodeListCons2, NodeListCons3, NodeListCons5, PadComposite, StripNilCheck, subOperNode], IntCodeTarget USING [bitsPerAU, bitsPerGlobal, bitsPerLink, bitsPerLocal, directGlobals, firstGlobalOffset, lastRegister, lastStack, logBitsPerGlobal, logBitsPerLocal, logMinBitsPerArgument, logMinBitsPerReturn, maxBitsArgumentRecord, maxBitsReturnRecord, minBitsPerArgument, minBitsPerReturn, ToBits, ToUnits], IntCodeTwig USING [BaseModel, BaseModelRep, DeclsFetch, DeclsSize, DeclsStore, Duplicate, LabelsFetch, LabelsSize, LabelsStore, LambdaModel, LambdaModelRep, ModelsFetch, ModelsStore, Switches], IntCodeUtils USING [IdTab, LabelVisitor, MapNode, MapNodeList, NewIdTab, NodeListCons, NodeListTail, SideEffectFree, SimplyEqual, VarListCons, VarListTail, VisitLabels, Visitor, WordToCard, WordToInt, zone], IO USING [PutF1, PutFR, PutFR1, STREAM], ProcessProps USING [GetProp], Rope USING [ROPE], Target: TYPE MachineParms USING [AlignmentIndex, Alignments, bitsPerProc, bitsPerWord, bitsPerProcess]; IntCodeTwigImpl: CEDAR PROGRAM IMPORTS Basics32, IntCodeEnables, IntCodeOpt, IntCodeStuff, IntCodeTarget, IntCodeTwig, IntCodeUtils, IO, ProcessProps EXPORTS IntCodeTwig = BEGIN OPEN IntCodeDefs, IntCodeEnables, IntCodeStuff, IntCodeTarget, IntCodeTwig, IntCodeUtils, Rope; IdTab: TYPE = IntCodeUtils.IdTab; <> smallExceptions: BOOL ¬ FALSE; < use small exception raising>> < use "normal" exception raising>> <<>> simplifyValueBlocks: BOOL ¬ TRUE; < perform some simplifications of value-returning blocks>> < no transformation of value-returning blocks>> <<>> useMemoryFromHandlers: BOOL = TRUE; < uplevel use from catch phrase handlers force the target variable into memory>> < uplevel use from catch phrase handlers can use registers>> <<>> heapAllocFX: BOOL = FALSE; < use heap allocation for frame extensions>> < declare the frame extension as a simple (addressed) variable>> <<>> cleanupCode: BOOL ¬ TRUE; < call CleanupLambda to remove junk>> < don't call CleanupLambda>> <<>> <> indexedImpliesAddressedLimit: INT ¬ LAST[INT]; <> localVarRegisterLimit: INT ¬ LAST[INT]; <> firstMappedOffset: INT ¬ 8*LONG[1024]*IntCodeTarget.bitsPerAU; <> worst: NAT = Target.Alignments[Target.AlignmentIndex.LAST]; <<>> <> staticLinkOffset: NAT = 0; <> firstLocalOffsetLinks: NAT = 4; <> firstLocalOffset: NAT = firstLocalOffsetLinks*bitsPerLink; <> <> z: ZONE ¬ IntCodeUtils.zone; <> rewriteEnables: PROC [node: Node, genTemp: GenTemp, genLabel: GenLabel] RETURNS [Node] ¬ NIL; <> Duplicate: PUBLIC SIGNAL = CODE; <> CantHappen: SIGNAL = CODE; <> NotYetImplemented: SIGNAL = CODE; <> <> DoModule: PUBLIC PROC [module: Node, switches: Switches] RETURNS [BaseModel] = { ENABLE CantHappen => IF switches['i] THEN { <> RESUME; }; RETURN [DoModuleInner[module, switches]]; }; DoModuleInner: PROC [module: Node, switches: Switches] RETURNS [BaseModel] = { base: BaseModel ¬ z.NEW[BaseModelRep ¬ [ module: module, labels: IntCodeUtils.NewIdTab[], decls: IntCodeUtils.NewIdTab[], models: IntCodeUtils.NewIdTab[] ]]; IF switches['h] OR rewriteEnables # NIL THEN { genTemp: GenTemp = { RETURN [IntCodeOpt.GenAnonLocal[base, parent, bits]]; }; genLabel: GenLabel = { RETURN [GenAnonLabelNode[base, node]]; }; IF rewriteEnables # NIL THEN module ¬ rewriteEnables[module, genTemp, genLabel] ELSE module ¬ IntCodeEnables.RewriteEnables[module, genTemp, genLabel]; }; InitModels[base, module, NIL]; <> CanonVars[base, module, NIL]; <> <<[[ This pass sets the upLevel and addressed (=> notRegister) variable flags ]]>> <> <<[[ This pass sets the notRegister variable flag ]]>> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO MarkUplevelLocals[base, m, m.lambda]; ENDLOOP; IF simplifyValueBlocks THEN <> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO IntCodeOpt.SimplifyValueBlocks[base, m, m.lambda]; ENDLOOP; IF lastStack # 0 THEN <> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO CheckStackDepth[base, m, m.lambda]; ENDLOOP; <> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO AllocMemLocals[base, m, m.lambda]; ENDLOOP; <> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO SubstUplevelLocals[base, m, m.lambda]; ENDLOOP; <> <<[[ This pass sets the used and assigned bits for variables ]]>> FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO SubstLocals[base, m, m.lambda]; ENDLOOP; <> <<[[ At this point we can rely on the used & assigned flags ]]>> IF cleanupCode THEN FOR m: LambdaModel ¬ base.first, m.next WHILE m # NIL DO rtnPtr: Var ¬ NIL; IF m.returnVar # NIL THEN WITH m.returnVar.location SELECT FROM deref: REF LocationRep.deref => WITH deref.addr SELECT FROM v: Var => rtnPtr ¬ v; ENDCASE; ENDCASE; IntCodeOpt.CleanupLambda[base, m, m.lambda, rtnPtr]; ENDLOOP; <> WITH module SELECT FROM m: ModuleNode => { tail: NodeList ¬ m.procs ¬ NIL; FOR lm: LambdaModel ¬ base.first, lm.next WHILE lm # NIL DO labelNode: LabelNode ¬ z.NEW[NodeRep.label ¬ [0, label[lm.label]]]; list: NodeList ¬ NodeListCons[labelNode]; IF tail = NIL THEN m.procs ¬ list ELSE tail.rest ¬ list; tail ¬ list; ENDLOOP; }; ENDCASE; RETURN [base]; }; <> InitModels: PROC [base: BaseModel, node: Node, model: LambdaModel] = { inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM const: ConstNode => RETURN [node]; < no map>> var: Var => WITH var.location SELECT FROM local: LocalVarLocation => RETURN [node]; < no map>> global: GlobalVarLocation => RETURN [node]; < no map>> ENDCASE; decl: DeclNode => AddVar[decl.var, model]; label: LabelNode => { lab: Label ¬ label.label; IF lab # NIL THEN { id: LogicalId ¬ lab.id; IF LabelsFetch[base, id] # lab THEN LabelsStore[base, id, lab]; WITH lab.node SELECT FROM lambda: LambdaNode => { newModel: LambdaModel ¬ z.NEW[LambdaModelRep ¬ [ label: lab, lambda: lambda, parentModel: NIL, parentLabel: lambda.parent, nesting: 0, forceLong: id = 0]]; <> IF lambda.parent # NIL AND lambda.descBody = NIL AND lambda.formalArgs # NIL THEN newModel.isCatch ¬ TRUE; ModelsStore[base, id, newModel]; IF base.tail = NIL THEN base.first ¬ newModel ELSE base.tail.next ¬ newModel; base.tail ¬ newModel; InitModels[base, lambda, newModel]; RETURN [node]; }; ENDCASE; }; }; lambda: LambdaNode => { units: INT ¬ 0; model.returnBits ¬ lambda.bitsOut; <> FOR each: VarList ¬ lambda.formalArgs, each.rest WHILE each # NIL DO arg: Var ¬ each.first; IF arg # NIL THEN units ¬ units + ToUnits[arg.bits, minBitsPerArgument, logMinBitsPerArgument]; ENDLOOP; model.argumentBits ¬ ToBits[units, logMinBitsPerArgument]; <> IF model.forceLong OR model.argumentBits > maxBitsArgumentRecord THEN { <> oldArgList: VarList ¬ lambda.formalArgs; argPtr: Var ¬ GenAnonVar[bitsPerLink]; argTemp: Var ¬ GenDeref[argPtr, MAX[model.argumentBits, bitsPerLink], worst]; units: INT ¬ 0; model.argumentBits ¬ bitsPerLink; FOR each: VarList ¬ oldArgList, each.rest WHILE each # NIL DO <> arg: Var ¬ each.first; IF arg # NIL THEN { bits: INT ¬ ToBits[units, logMinBitsPerArgument]; arg.location ¬ GenFieldLocOfVar[argTemp, bits]; AddVar[arg, model]; units ¬ units + ToUnits[arg.bits, minBitsPerArgument, logMinBitsPerArgument]; }; ENDLOOP; lambda.formalArgs ¬ VarListCons[argPtr]; model.argVar ¬ argTemp; AddVar[argPtr, model]; } ELSE { <> FOR each: VarList ¬ lambda.formalArgs, each.rest WHILE each # NIL DO AddVar[each.first, model]; ENDLOOP; }; <> IntCodeUtils.MapNodeList[lambda.body, inner]; <> IF model.returnVar # NIL OR HasLongReturnVar[model, model.returnBits] THEN <> WITH model.returnVar.location SELECT FROM deref: REF LocationRep.deref => WITH deref.addr SELECT FROM rtnPtr: Var => { AddVar[rtnPtr, model]; lambda.formalArgs ¬ VarListCons[rtnPtr, lambda.formalArgs]; lambda.bitsOut ¬ 0; }; ENDCASE => SIGNAL CantHappen; ENDCASE => SIGNAL CantHappen; RETURN [node]; }; goto: GotoNode => { label: Label = goto.dest; SELECT TRUE FROM label = NIL => {}; NOT label.used OR NOT label.jumpedTo => {}; goto.backwards AND NOT label.backTarget => {}; ENDCASE => RETURN [node]; SIGNAL CantHappen; RETURN [node]; }; apply: ApplyNode => { <> retBits: INT ¬ apply.bits; argBits: INT ¬ BitsForArgList[apply.args]; units: INT ¬ ToUnits[argBits, minBitsPerArgument, logMinBitsPerArgument]; tail: NodeList ¬ NIL; useLargeArgs: BOOL ¬ argBits > maxBitsArgumentRecord; useLargeRets: BOOL ¬ retBits > minBitsPerReturn; IntCodeUtils.MapNode[apply, inner]; <> WITH apply.proc SELECT FROM mc: MachineCodeNode => RETURN [node]; <> oper: OperNode => { args: NodeList ¬ apply.args; WITH oper.oper SELECT FROM code: REF OperRep.code => { IF code.label = NIL OR NOT code.label.used THEN SIGNAL CantHappen; }; mesa: REF OperRep.mesa => { SELECT mesa.mesa FROM signal, error => IF apply.args = NIL THEN SIGNAL CantHappen ELSE { <> first: Node ¬ args.first; rest: NodeList ¬ args.rest; nArgs: INT ¬ 0; FOR each: NodeList ¬ rest, each.rest WHILE each # NIL DO nArgs ¬ nArgs + 1; ENDLOOP; IF smallExceptions THEN { name: ROPE ¬ NIL; SELECT mesa.mesa FROM error => SELECT nArgs FROM 0 => name ¬ "XR_Error0"; 1 => name ¬ "XR_Error1"; 2 => name ¬ "XR_Error2"; ENDCASE => GO TO notSmall; signal => { IF retBits # 0 THEN GO TO notSmall; SELECT nArgs FROM 0 => name ¬ "XR_Signal0"; 1 => name ¬ "XR_Signal1"; 2 => name ¬ "XR_Signal2"; ENDCASE => GO TO notSmall; }; ENDCASE => ERROR; apply.proc ¬ z.NEW[NodeRep.machineCode ¬ [bits: 0, details: machineCode[name]]]; GO TO ret; EXITS notSmall => {}; }; IF rest = NIL THEN rest ¬ NodeListCons[defaultNIL] ELSE { apply.args ¬ rest; node ¬ GenLargeArgs[base, apply, model]; rest ¬ apply.args; }; SELECT TRUE FROM mesa.mesa = error => {}; <> <> retBits = 0 => <> rest ¬ NodeListCons[defaultNIL, rest]; ENDCASE => { <> addr: Node ¬ NIL; [new: node, addr: addr] ¬ GenLargeRets[base, model, node, retBits]; rest ¬ NodeListCons[addr, rest]; }; apply.args ¬ NodeListCons[first, rest]; GO TO ret; }; fork => { node ¬ TransformFork[base, model, apply]; GO TO ret; }; join => { <> rtnBits: INT ¬ node.bits; node.bits ¬ bitsPerLink; <> IF rtnBits # 0 THEN { result: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, rtnBits]; rtnPtr: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, bitsPerLink]; rtnSrc: Node ¬ GenDeref[rtnPtr, rtnBits, worst]; head: NodeList ¬ NodeListCons[GenDecl[result, NIL]]; <> tail: NodeList ¬ head; tail ¬ tail.rest ¬ NodeListCons[GenDecl[rtnPtr, node]]; <> tail ¬ tail.rest ¬ NodeListCons[GenAssign[result, rtnSrc]]; <> tail ¬ tail.rest ¬ NodeListCons[GenFree[rtnPtr]]; <> tail ¬ tail.rest ¬ NodeListCons[result]; <> RETURN [GenBlock[head, rtnBits]]; }; RETURN [GenAssign[GenDummy[bitsPerLink], node]]; <> }; resume, reject, unwind => { <> [] ¬ HasLongReturnVar[model, minBitsPerArgument*2]; GO TO checkArgs; }; startGlobal => { <> prog: Node ¬ apply.args.first; rest: NodeList ¬ apply.args ¬ apply.args.rest; IF rest # NIL THEN node ¬ GenLargeArgs[base, apply, model] ELSE apply.args ¬ NodeListCons[defaultNIL]; apply.args ¬ NodeListCons[prog, apply.args]; IF retBits = 0 THEN apply.args ¬ NodeListCons[defaultNIL, apply.args] ELSE { addr: Node ¬ NIL; [node, addr] ¬ GenLargeRets[base, model, node, retBits]; apply.args ¬ NodeListCons[addr, apply.args]; }; GO TO ret; }; addr => apply.proc ¬ addrOperNode; free => apply.proc ¬ freeOperNode; alloc => apply.proc ¬ allocOperNode; ENDCASE; GO TO ret; }; ENDCASE => GO TO ret; EXITS checkArgs => {}; }; ENDCASE; IF useLargeArgs THEN <> node ¬ GenLargeArgs[base, apply, model]; <> IF useLargeRets THEN { <> addr: Node ¬ NIL; [node, addr] ¬ GenLargeRets[base, model, node, retBits]; apply.args ¬ NodeListCons[addr, apply.args]; }; GO TO ret; EXITS ret => RETURN [node]; }; return: ReturnNode => { IF return.rets = NIL THEN RETURN [emptyReturn]; IntCodeUtils.MapNode[node, inner]; node ¬ CanonReturn[return, model]; RETURN [node]; }; module: ModuleNode => { <> offset: INT ¬ IntCodeTarget.firstGlobalOffset; FOR each: VarList ¬ module.vars, each.rest WHILE each # NIL DO var: Var ¬ each.first; IF var # NIL THEN { bits: INT ¬ var.bits; units: INT ¬ ToUnits[bits, IntCodeTarget.bitsPerGlobal, IntCodeTarget.logBitsPerGlobal]; round: INT ¬ ToBits[units, IntCodeTarget.logMinBitsPerArgument]; loc: Location ¬ z.NEW[LocationRep.globalVar ¬ [globalVar[offset]]]; var.location ¬ loc; DeclsStore[base, var.id, var]; offset ¬ offset + round; }; ENDLOOP; }; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; AddVar: PROC [var: Var, model: LambdaModel] = { IF var # NIL AND model # NIL AND DeclsFetch[base, var.id] = NIL THEN { IF var.id = nullVariableId THEN <> var.id ¬ -DeclsSize[base]-1; IF var.location = NIL THEN var.location ¬ z.NEW[LocationRep.localVar ¬ [localVar[var.id, model.label]]]; DeclsStore[base, var.id, var]; }; }; canonLabels: IntCodeUtils.LabelVisitor = { <<[label: Label, node: Node, define: BOOL _ FALSE] RETURNS [Label]>> IF NOT define THEN { new: Label ¬ LabelsFetch[base, label.id]; IF new # NIL THEN RETURN [new]; }; RETURN [label]; }; [] ¬ inner[node]; IF model = NIL THEN <> IntCodeUtils.VisitLabels[node: node, visitor: canonLabels, fullTree: TRUE, visitNIL: FALSE]; }; CanonVars: PROC [base: BaseModel, node: Node, model: LambdaModel] = { inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM var: Var => { <> canon: Var ¬ DeclsFetch[base, var.id]; IF canon # NIL THEN { WITH canon.location SELECT FROM local: LocalVarLocation => { <> parent: Label ¬ IF model = NIL THEN NIL ELSE model.label; IF local.parent # parent THEN canon.flags[upLevel] ¬ TRUE; }; global: GlobalVarLocation => IF NOT IntCodeTarget.directGlobals THEN { <> IF model # NIL AND model.globalLink = NIL THEN <> model.globalLink ¬ IntCodeOpt.GenAnonLocal[base, model.label, bitsPerLink]; }; ENDCASE; var ¬ canon; }; WITH var.location SELECT FROM local: LocalVarLocation => { <> IF var.bits > localVarRegisterLimit THEN <> MarkAddressed[var]; }; indexed: IndexedLocation => { base: Node ¬ indexed.base ¬ inner[indexed.base]; index: Node ¬ indexed.index ¬ inner[indexed.index]; WITH index SELECT FROM const: WordConstNode => { <> val: INT ¬ IntCodeUtils.WordToInt[const.word]; var.location ¬ GenFieldLoc[base, val*var.bits]; }; ENDCASE => IF base # NIL AND base.bits > indexedImpliesAddressedLimit THEN <> MarkAddressed[base]; }; ENDCASE => IntCodeUtils.MapNode[var, inner]; RETURN [var]; }; decl: DeclNode => { var: Var ¬ DeclsFetch[base, decl.var.id]; <> decl.init ¬ inner[decl.init]; <> RETURN [node]; }; labelNode: LabelNode => { label: Label = labelNode.label; IF label # NIL THEN { id: LogicalId ¬ label.id; WITH label.node SELECT FROM lambda: LambdaNode => { newModel: LambdaModel ¬ ModelsFetch[base, id]; lambda.descBody ¬ NARROW[inner[lambda.descBody]]; CanonLambda[base, newModel, lambda]; IF newModel.isCatch THEN [] ¬ GetHandlerArgs[newModel]; <> RETURN [node]; }; ENDCASE; }; }; enable: EnableNode => { handler: Handler ¬ enable.handle; IF handler # NIL THEN { proc: Node ¬ handler.proc; handler.proc ¬ MarkCatch[base, proc]; [] ¬ inner[proc]; }; }; assign: AssignNode => { IntCodeUtils.MapNode[node, inner]; IF assign.lhs # NIL AND assign.lhs.id # nullVariableId THEN assign.lhs.flags[assigned] ¬ TRUE; RETURN [node]; }; apply: ApplyNode => { handler: Handler ¬ apply.handler; IF handler # NIL THEN { proc: Node ¬ handler.proc; handler.proc ¬ MarkCatch[base, proc]; [] ¬ inner[proc]; }; IntCodeUtils.MapNode[node, inner]; WITH apply.proc SELECT FROM oper: OperNode => { WITH oper.oper SELECT FROM mesa: REF OperRep.mesa => SELECT mesa.mesa FROM addr => { args: NodeList ¬ apply.args; IF args # NIL THEN MarkAddressed[args.first]; }; ENDCASE; ENDCASE; }; ENDCASE; RETURN [node]; }; block: BlockNode => { <> list: NodeList ¬ block.nodes; count: INT ¬ 0; decl0: DeclNode ¬ NIL; assign1: AssignNode ¬ NIL; var1: Var ¬ NIL; var2: Var ¬ NIL; FOR each: NodeList ¬ list, each.rest WHILE each # NIL DO WITH each.first SELECT FROM decl: DeclNode => IF count = 0 THEN decl0 ¬ decl ELSE decl0 ¬ NIL; assign: AssignNode => IF count = 1 THEN assign1 ¬ assign ELSE assign1 ¬ NIL; var: Var => { IF count = 1 THEN var1 ¬ var ELSE var1 ¬ NIL; IF count = 2 THEN var2 ¬ var ELSE var2 ¬ NIL; }; ENDCASE; count ¬ count + 1; IF count > 3 THEN EXIT; ENDLOOP; SELECT count FROM 1 => IF decl0 = NIL THEN <> RETURN [inner[list.first]]; 2 => IF decl0 # NIL AND decl0.var = var1 THEN <> RETURN [inner[decl0.init]]; 3 => IF decl0 # NIL AND decl0.var = var2 AND decl0.init = NIL AND assign1 # NIL AND assign1.lhs = var2 THEN <> IF NOT NodeContains[assign1.rhs, var2] THEN <> RETURN [inner[assign1.rhs]]; ENDCASE; }; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; [] ¬ inner[node]; }; DetermineNesting: PROC [base: BaseModel, model: LambdaModel] = { parent: Label ¬ model.parentLabel; IF parent # NIL AND model.nesting = 0 THEN { parentModel: LambdaModel ¬ ModelsFetch[base, parent.id]; IF parentModel # NIL THEN { DetermineNesting[base, parentModel]; model.parentModel ¬ parentModel; model.parentLabel ¬ parentModel.label; model.nesting ¬ parentModel.nesting + 1; }; }; }; CanonLambda: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { IF model # NIL AND model.parentLabel # NIL AND model.parentModel = NIL THEN <> DetermineNesting[base, model]; CanonVars[base, lambda, model]; SELECT TRUE FROM model.isCatch => { <> model.staticLink ¬ GetHandlerArgs[model].regsPtr; model.returnBits ¬ 2*bitsPerLink; }; lambda.parent # NIL => { <> static: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, bitsPerLink]; staticList: VarList ¬ VarListCons[static]; model.staticLink ¬ static; IF lambda.formalArgs = NIL THEN lambda.formalArgs ¬ staticList ELSE VarListTail[lambda.formalArgs].rest ¬ staticList; }; ENDCASE; IF model.globalLink # NIL THEN { <> decl: Node ¬ GenDecl[model.globalLink, globalLinkInit]; model.globalLink.flags[frequent] ¬ TRUE; lambda.body ¬ NodeListCons[decl, lambda.body]; }; }; CheckStackDepth: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> oldDepth: INT ¬ depth; IF depth > lastStack THEN ERROR CantHappen; <> IF node = NIL OR CanSimplyPush[node, depth] THEN RETURN [node]; <> WITH node SELECT FROM decl: DeclNode => { <> var: Var ¬ decl.var; IF var # NIL AND NOT var.flags[notRegister] THEN WITH var.location SELECT FROM local: LocalVarLocation => { units: INT ¬ ToUnits[var.bits, bitsPerLocal, logBitsPerLocal]; next: INT ¬ depth+units; IF next > lastRegister THEN { <> var.flags[notRegister] ¬ TRUE; } ELSE { <> depth ¬ next; IF depth > max THEN max ¬ depth; }; }; ENDCASE; decl.init ¬ inner[decl.init]; RETURN [node]; }; apply: ApplyNode => { <> args: NodeList ¬ apply.args; argsUnits: INT ¬ ToUnits[BitsForArgList[args], bitsPerLocal, logBitsPerLocal]; tail: NodeList ¬ NIL; lastTemp: INT ¬ lastStack-maxBitsArgumentRecord/bitsPerLocal; PutArgInMemory: PROC [arg: Node] RETURNS [Var] = { var: Var ¬ GenMemTemp[base, arg.bits, model]; new: NodeList ¬ NodeListCons[GenDecl[var, inner[arg]]]; var.flags[constant] ¬ TRUE; IF tail = NIL THEN { <> new.rest ¬ NodeListCons[apply]; node ¬ GenBlock[new, apply.bits]; } ELSE { <> new.rest ¬ tail.rest; tail.rest ¬ new; }; tail ¬ new; RETURN [var]; }; IF NOT CanSimplyPush[apply.proc, depth+argsUnits] THEN <> apply.proc ¬ PutArgInMemory[apply.proc]; <> SELECT TRUE FROM args = NIL => GO TO commonExit; args.rest = NIL => { <> args.first ¬ inner[args.first]; GO TO commonExit; }; ENDCASE; WITH apply.proc SELECT FROM oper: OperNode => { WITH oper.oper SELECT FROM code: REF OperRep.code => GO TO useProcApply; <> mesa: REF OperRep.mesa => SELECT mesa.mesa FROM addr, equal, notEqual => GO TO useAddrs; ENDCASE; ENDCASE; <> FOR each: NodeList ¬ args, each.rest WHILE each # NIL DO arg: Node ¬ each.first; units: INT ¬ ToUnits[arg.bits, minBitsPerArgument, logMinBitsPerArgument]; SELECT TRUE FROM depth <= lastTemp, CanSimplyPush[arg, depth+argsUnits] => <> each.first ¬ inner[arg]; ENDCASE => <> each.first ¬ PutArgInMemory[arg]; ENDLOOP; GO TO commonExit; EXITS useProcApply => {}; useAddrs => { <> localsPerLink: NAT = bitsPerLink/bitsPerLocal; <> FOR each: NodeList ¬ args, each.rest WHILE each # NIL DO depth ¬ depth + localsPerLink; ENDLOOP; FOR each: NodeList ¬ args, each.rest WHILE each # NIL DO each.first ¬ inner[each.first]; ENDLOOP; GO TO commonExit; }; }; ENDCASE; FOR each: NodeList ¬ args, each.rest WHILE each # NIL DO arg: Node ¬ each.first; units: INT ¬ ToUnits[arg.bits, minBitsPerArgument, logMinBitsPerArgument]; next: INT ¬ units+depth; SELECT TRUE FROM next <= lastTemp => { <> depth ¬ next; each.first ¬ inner[arg]; }; CanSimplyPush[arg, depth] => <> each.first ¬ inner[arg]; ENDCASE => <> each.first ¬ PutArgInMemory[arg]; ENDLOOP; GO TO commonExit; EXITS commonExit => { depth ¬ oldDepth; RETURN [node]; }; }; lambda: LambdaNode => RETURN [node]; source: SourceNode => { <> FOR each: NodeList ¬ source.nodes, each.rest WHILE each # NIL DO each.first ¬ inner[each.first]; ENDLOOP; RETURN [node]; }; ENDCASE; IntCodeUtils.MapNode[node, inner]; depth ¬ oldDepth; RETURN [node]; }; depth: INT ¬ ToUnits[model.argumentBits, minBitsPerArgument, logMinBitsPerArgument]; max: INT ¬ depth; localsPerLink: NAT = bitsPerLink/bitsPerLocal; IF model.returnVar # NIL THEN depth ¬ depth+localsPerLink; IF model.globalLink # NIL THEN depth ¬ depth+localsPerLink; IntCodeUtils.MapNode[lambda, inner]; }; MarkUplevelLocals: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { <> <> inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM var: Var => { WITH var.location SELECT FROM local: LocalVarLocation => { IF model # NIL AND var.flags[upLevel] THEN { target: Label = local.parent; IF target # NIL AND target # model.label THEN { <> notRegister: BOOL ¬ var.flags[notRegister]; FOR mod: LambdaModel ¬ model, mod.parentModel DO SELECT TRUE FROM mod = NIL => { <> VarCantHappen[var, "ref to non-enclosing lambda"]; EXIT}; mod.label = target => { var.flags[notRegister] ¬ notRegister; RETURN [CopyVar[var]]; }; mod.staticLink = NIL => { <> VarCantHappen[var, "can't find static link"]; EXIT}; ENDCASE; IF useMemoryFromHandlers OR NOT mod.isCatch THEN <> notRegister ¬ TRUE; ENDLOOP; }; }; RETURN [node]; }; deref: DerefLocation => { old: Node ¬ deref.addr; IF old # NIL THEN { new: Node ¬ inner[old]; IF new # old THEN { nVar: Var ¬ CopyVar[var]; nVar.location ¬ z.NEW[LocationRep.deref ¬ [deref[new, deref.align]]]; RETURN [nVar]; }; }; RETURN [var]; }; field: FieldLocation => { old: Node ¬ field.base; IF old # NIL THEN { new: Node ¬ inner[old]; IF new # old THEN { nVar: Var ¬ CopyVar[var]; IF field.cross THEN nVar.location ¬ GenXFieldLoc[new, field.start] --ChJ, May 4, 1993 ELSE nVar.location ¬ GenFieldLoc[new, field.start]; RETURN [nVar]; }; }; RETURN [var]; }; indexed: IndexedLocation => { base: Node ¬ indexed.base; index: Node ¬ indexed.index; IF base # NIL THEN base ¬ inner[base]; IF index # NIL THEN index ¬ inner[index]; IF base # indexed.base OR index # indexed.index THEN { nVar: Var ¬ CopyVar[var]; nVar.location ¬ z.NEW[LocationRep.indexed ¬ [indexed[base, index]]]; RETURN [nVar]; }; RETURN [var]; }; composite: CompositeLocation => { FOR each: NodeList ¬ composite.parts, each.rest WHILE each # NIL DO old: Node ¬ each.first; IF old # NIL THEN { new: Node ¬ inner[old]; IF old # new THEN { <> nVar: Var ¬ CopyVar[var]; head: NodeList ¬ NIL; tail: NodeList ¬ NIL; nLoc: REF LocationRep.composite ¬ z.NEW[LocationRep.composite ¬ [composite[NIL]]]; <> FOR lead: NodeList ¬ composite.parts, lead.rest WHILE lead # each DO copy: NodeList ¬ NodeListCons[lead.first]; IF tail = NIL THEN nLoc.parts ¬ copy ELSE tail.rest ¬ copy; tail ¬ copy; ENDLOOP; <> IF tail = NIL THEN tail ¬ nLoc.parts ¬ NodeListCons[new] ELSE tail ¬ tail.rest ¬ NodeListCons[new]; <> FOR lead: NodeList ¬ each.rest, lead.rest WHILE lead # NIL DO copy: NodeList ¬ NodeListCons[old ¬ lead.first]; IF old # NIL THEN copy.first ¬ inner[old]; tail ¬ tail.rest ¬ copy; ENDLOOP; nVar.location ¬ nLoc; RETURN [nVar]; }; }; ENDLOOP; RETURN [var]; }; escape: EscapeLocation => { base: Node ¬ escape.base; IF base # NIL THEN { base ¬ inner[base]; IF base # escape.base THEN { nVar: Var ¬ CopyVar[var]; nVar.location ¬ z.NEW[LocationRep.escape ¬ [escape[id: escape.id, base: base, offset: escape.offset]]]; RETURN [nVar]; }; }; RETURN [var]; }; ENDCASE; }; return: ReturnNode => <> node ¬ CanonReturn[return, model]; apply: ApplyNode => { <> WITH apply.proc SELECT FROM oper: OperNode => { args: NodeList ¬ apply.args; WITH oper.oper SELECT FROM mesa: REF OperRep.mesa => { SELECT mesa.mesa FROM reject => { <> rets: NodeList ¬ NodeListCons[constant0, NodeListCons[constant0]]; node ¬ GenReturn[rets]; GO TO fixReturn; }; resume => { <> <> rets: NodeList ¬ NodeListCons[constant1, NodeListCons[constant0]]; node ¬ GenReturn[rets]; IF args # NIL THEN { comp: Node ¬ PadComposite[args, logMinBitsPerReturn]; dest: Var ¬ GenDeref[ GetHandlerArgs[model].rtnPtr, comp.bits, worst]; assign: Node ¬ GenAssign[dest, comp]; node ¬ GenBlock[NodeListCons2[assign, node]]; }; GO TO fixReturn; }; unwind => { <> rets: NodeList ¬ NodeListCons[constant2, args]; node ¬ GenReturn[rets]; GO TO fixReturn; }; ENDCASE; EXITS fixReturn => RETURN [inner[node]]; }; ENDCASE; }; ENDCASE; }; lambda: LambdaNode => RETURN [node]; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; IntCodeUtils.MapNode[lambda, inner]; }; AllocMemLocals: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { <> inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM var: Var => WITH var.location SELECT FROM stack: REF LocationRep.stack => forceExtension ¬ TRUE; <> ENDCASE; decl: DeclNode => CountVar[decl.var]; block: BlockNode => { oldDepth: INT ¬ model.memDepth; IntCodeUtils.MapNode[node, inner]; model.memDepth ¬ oldDepth; RETURN [node]; }; enable: EnableNode => IF enable.handle # NIL THEN forceExtension ¬ TRUE; apply: ApplyNode => IF apply.handler # NIL THEN forceExtension ¬ TRUE; lambda: LambdaNode => RETURN [node]; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; CountVar: PROC [var: Var] = { WITH var.location SELECT FROM local: LocalVarLocation => SELECT TRUE FROM NOT var.flags[notRegister] => {}; <> NOT heapAllocFX AND NOT var.flags[upLevel] => {}; <> ENDCASE => { <> units: INT ¬ ToUnits[var.bits, bitsPerLocal, logBitsPerLocal]; depth: INT ¬ model.memDepth; IF local.parent # model.label THEN { varId: Rope.ROPE = IF local.parent = NIL THEN "??" ELSE IO.PutFR1["%g", [integer[local.parent.id]] ]; modelId: Rope.ROPE = IF model.label = NIL THEN "??" ELSE IO.PutFR1["%g", [integer[model.label.id]] ]; VarCantHappen[var, IO.PutFR["%g # %g", [rope[varId]], [rope[modelId]] ]]; }; local.id ¬ depth; IF (model.memDepth ¬ depth + units) > model.memMax THEN model.memMax ¬ model.memDepth; }; stack: REF LocationRep.stack => forceExtension ¬ TRUE; <> ENDCASE => VarCantHappen[var, "location not stack, not local"]; }; forceExtension: BOOL ¬ FALSE; model.memDepth ¬ model.memMax ¬ firstLocalOffsetLinks; FOR each: VarList ¬ lambda.formalArgs, each.rest WHILE each # NIL DO CountVar[each.first]; ENDLOOP; IntCodeUtils.MapNode[lambda, inner]; IF model.memMax = firstLocalOffsetLinks AND NOT forceExtension THEN <> model.memDepth ¬ model.memMax ¬ 0 ELSE { <> memBits: INT ¬ ToBits[model.memMax, logBitsPerLocal]; fxLink: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, bitsPerLink]; fxSpace: Var ¬ model.frameExtension ¬ IF heapAllocFX THEN GenDeref[fxLink, memBits, worst] ELSE IntCodeOpt.GenAnonLocal[base, model.label, memBits]; fxLink.flags[frequent] ¬ TRUE; <> IF model.staticLink # NIL THEN <> lambda.body ¬ NodeListCons[ GenAssign[ lhs: GenField[fxSpace, staticLinkOffset, bitsPerLink], rhs: model.staticLink], lambda.body]; IF heapAllocFX THEN { model.memoryLink ¬ fxLink; AddFrameExtension[base, model, lambda]; } ELSE { <> IF lambda.body = NIL THEN <> lambda.body ¬ NodeListCons[GenComment["entry point"]]; model.entryPoint ¬ lambda.body; IF NOT useMemoryFromHandlers THEN { <> model.memoryLink ¬ fxLink; lambda.body ¬ NodeListCons[ GenDecl[var: fxLink, init: GenAddr[fxSpace]], lambda.body]; }; lambda.body ¬ NodeListCons[ GenDecl[var: fxSpace, init: NIL], lambda.body]; fxSpace.flags[notRegister] ¬ FALSE; <> }; <> }; }; AddFrameExtension: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { <> link: Var ¬ model.memoryLink; ext: Var ¬ model.frameExtension; <> <<{decl {var link} {apply {oper mesa alloc} {const word size}}}>> <<{enable {} body}>> <> <<{apply {oper mesa free} {var link}}>> bits: INT ¬ ext.bits; units: INT ¬ ToUnits[bits, minBitsPerArgument, logMinBitsPerArgument]; <> alloc: Node ¬ GenDecl[link, GenApply[allocOperNode, NodeListCons[GenConst[units, minBitsPerArgument]]]]; free: Node ¬ GenFree[link]; freeExit: LabelNode ¬ SELECT model.returnBits FROM 0, > maxBitsReturnRecord => GenAnonLabelNode[base, free], ENDCASE => NIL; inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM return: ReturnNode => { <> IF freeExit # NIL THEN <> RETURN [GenGoTo[freeExit.label]]; IF return.rets = NIL THEN SIGNAL CantHappen ELSE { <> clean: NodeList ¬ NodeListCons[free]; NodeListTail[return.rets].rest ¬ clean; }; RETURN [node]; }; lambda: LambdaNode => RETURN [node]; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; lambda.body ¬ NodeListCons[ alloc, model.entryPoint ¬ NodeListCons[ z.NEW[NodeRep.enable ¬ [0, enable[ handle: z.NEW[HandlerRep ¬ [context: link, proc: NIL]], scope: lambda.body]]], IF freeExit # NIL THEN NodeListCons[freeExit] ELSE NIL ]]; IntCodeUtils.MapNode[lambda, inner]; }; SubstUplevelLocals: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { <> inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM var: Var => { WITH var.location SELECT FROM local: LocalVarLocation => { target: Label = local.parent; SELECT target FROM model.label => { <> }; # NIL => { <> link: Var ¬ model.staticLink; mod: LambdaModel ¬ model; fromCatch: BOOL ¬ mod.isCatch; DO parentModel: LambdaModel ¬ mod.parentModel; SELECT TRUE FROM target = mod.parentLabel => { <> inRegister: BOOL ¬ NOT var.flags[notRegister]; offset: INT ¬ ToBits[local.id, logBitsPerLocal]; bits: INT ¬ var.bits; SELECT TRUE FROM fromCatch AND NOT useMemoryFromHandlers => { IF inRegister THEN RETURN [GenUpLevel[link, var]]; <> link ¬ GenUpLevel[link, mod.parentModel.memoryLink]; <> }; parentModel # NIL AND parentModel.staticLink = var => { <> link ¬ mod.staticLink; offset ¬ staticLinkOffset; }; inRegister => VarCantHappen[var, "SubstUplevelLocals"]; ENDCASE; IF link = NIL THEN VarCantHappen[var, "SubstUplevelLocals"] ELSE RETURN [GenFieldOfDeref[link, offset, bits]]; }; ENDCASE => { next: LambdaModel ¬ mod.parentModel; IF next = NIL OR next.staticLink = NIL THEN { VarCantHappen[var, "SubstUplevelLocals"]; EXIT; }; SELECT TRUE FROM useMemoryFromHandlers OR NOT fromCatch => <> link ¬ GenFieldOfDeref[link, staticLinkOffset, bitsPerLink]; ENDCASE => <> link ¬ GenUpLevel[link, next.staticLink]; fromCatch ¬ mod.isCatch; mod ¬ next; }; ENDLOOP; }; ENDCASE => VarCantHappen[var, "SubstUplevelLocals"]; RETURN [node]; }; global: GlobalVarLocation => IF NOT IntCodeTarget.directGlobals THEN IF model.globalLink # NIL THEN { <> gVar: Var ¬ GenFieldOfDeref[model.globalLink, global.id, var.bits]; gVar.id ¬ var.id; RETURN [gVar]; }; stack: REF LocationRep.stack => { <> ext: Var = model.frameExtension; new: Node ¬ ext; IF ext = NIL THEN ERROR CantHappen; new ¬ GenField[new, stack.offset*IntCodeTarget.bitsPerAU, node.bits]; RETURN [new]; }; ENDCASE; }; lambda: LambdaNode => RETURN [node]; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; IntCodeUtils.MapNode[lambda, inner]; }; SubstLocals: PROC [base: BaseModel, model: LambdaModel, lambda: LambdaNode] = { <> innerVar: PROC [var: Var] RETURNS [Var] = { IF var # NIL THEN { loc: Location ¬ var.location; WITH loc SELECT FROM local: LocalVarLocation => { SELECT TRUE FROM NOT var.flags[notRegister] => {}; <> NOT heapAllocFX AND NOT var.flags[upLevel] => {}; <> var = model.frameExtension => {}; <> ENDCASE => { target: Label = local.parent; SELECT target FROM model.label => { <> ext: Var ¬ model.frameExtension; IF ext = NIL THEN VarCantHappen[var, "SubstLocals"] ELSE var.location ¬ GenFieldLocOfVar[ext, ToBits[local.id, logBitsPerLocal]]; }; ENDCASE => SIGNAL CantHappen; <> }; }; field: FieldLocation => { <> base: Node ¬ field.base ¬ inner[field.base]; WITH base SELECT FROM fv: Var => WITH fv.location SELECT FROM ff: FieldLocation => { <> IF ff.cross#field.cross THEN { <<--Lifted from little endian compiler without understanding, ChJ, May 4, 1993>> IF fv.bits >= Target.bitsPerWord THEN { -- we should test if the basefield.start is a multiple of words LAI field.start _ field.start + ff.start; } ELSE { field.start _ field.start + Basics32.BITXOR[ff.start + fv.bits-1, Target.bitsPerWord-1] }; } ELSE { field.start ¬ field.start + ff.start; }; field.base ¬ ff.base; }; fd: DerefLocation => IF NOT var.flags[addressed] THEN { addr: Node ¬ StripNilCheck[fd.addr]; IF addr # fd.addr THEN { <> IF field.start < firstMappedOffset THEN fd.addr ¬ addr ELSE addr ¬ fd.addr; }; }; ENDCASE; ENDCASE; }; indexed: IndexedLocation => { derefLoc: DerefLocation ¬ NIL; WITH indexed.base SELECT FROM var: Var => WITH var.location SELECT FROM field: FieldLocation => IF field.start < firstMappedOffset THEN WITH field.base SELECT FROM fv: Var => WITH fv.location SELECT FROM deref: DerefLocation => derefLoc ¬ deref; ENDCASE; ENDCASE; deref: DerefLocation => derefLoc ¬ deref; ENDCASE; ENDCASE; IF derefLoc # NIL THEN { derefAddr: Node ¬ derefLoc.addr; stripped: Node ¬ StripNilCheck[derefAddr]; index: Node ¬ indexed.index; IF stripped # derefAddr AND IntCodeUtils.SideEffectFree[stripped, FALSE] AND IntCodeUtils.SideEffectFree[index, FALSE] THEN { <> scanner: IntCodeUtils.Visitor = { IF derefAddr # NIL THEN { WITH node SELECT FROM var: Var => WITH var.location SELECT FROM deref: DerefLocation => { ds: Node ¬ StripNilCheck[deref.addr]; IF ds = stripped THEN GO TO noMap; IF IntCodeUtils.SimplyEqual[ds, stripped] THEN GO TO noMap; }; ENDCASE; ENDCASE; IntCodeUtils.MapNode[node, scanner]; EXITS noMap => {derefAddr ¬ NIL}; }; RETURN [node]; }; WITH index SELECT FROM wc: WordConstNode => { <> x: CARD ¬ IntCodeUtils.WordToCard[wc.word]; sz: CARD ¬ var.bits; IF x < CARD[firstMappedOffset]/sz THEN derefAddr ¬ NIL; }; ENDCASE => { <> [] ¬ scanner[index]; }; IF derefAddr = NIL THEN <> derefLoc.addr ¬ stripped; }; }; }; ENDCASE => IF loc = NIL THEN VarCantHappen[var, "SubstLocals"]; }; RETURN [var]; }; inner: IntCodeUtils.Visitor = { <<[node: IntCodeDefs.Node] RETURNS [IntCodeDefs.Node]>> WITH node SELECT FROM var: Var => { var ¬ innerVar[var]; MarkUsed[var]; }; decl: DeclNode => { decl.var ¬ innerVar[decl.var]; decl.init ¬ inner[decl.init]; RETURN [node]; }; assign: AssignNode => { MarkAssigned[assign.lhs]; }; lambda: LambdaNode => RETURN [node]; apply: ApplyNode => IF apply.handler # NIL THEN IF apply.handler.context = NIL AND model.frameExtension # NIL THEN apply.handler.context ¬ GenAddr[model.frameExtension]; enable: EnableNode => IF enable.handle # NIL THEN IF enable.handle.context = NIL AND model.frameExtension # NIL THEN enable.handle.context ¬ GenAddr[model.frameExtension]; ENDCASE; IntCodeUtils.MapNode[node, inner]; RETURN [node]; }; IntCodeUtils.MapNode[lambda, inner]; <> IF model.frameExtension # NIL AND lambda.formalArgs # NIL THEN { <> tail: NodeList ¬ NIL; head: NodeList ¬ NIL; lag: NodeList ¬ NIL; FOR each: VarList ¬ lambda.formalArgs, each.rest WHILE each # NIL DO var: Var ¬ each.first; WITH var.location SELECT FROM local: LocalVarLocation => {}; register: REF LocationRep.register => {}; ENDCASE => { <> anon: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, var.bits]; assign: NodeList ¬ NodeListCons[GenDecl[var, anon]]; each.first ¬ anon; IF tail = NIL THEN head ¬ assign ELSE tail.rest ¬ assign; tail ¬ assign; }; ENDLOOP; IF tail # NIL THEN { FOR each: NodeList ¬ lambda.body, each.rest WHILE each # NIL DO IF each = model.entryPoint THEN { tail.rest ¬ each; IF lag = NIL THEN SIGNAL CantHappen; <> lag.rest ¬ head; EXIT; }; lag ¬ each; ENDLOOP; }; }; { <> parent: Label ¬ lambda.parent; descBody: Var ¬ lambda.descBody; lambda.descBody ¬ NIL; IF descBody # NIL AND NOT model.isCatch THEN { staticLink: Var ¬ model.staticLink; IF staticLink # NIL THEN { WITH descBody.location SELECT FROM field: FieldLocation => { units: INT ¬ field.start / bitsPerAU; IF field.cross THEN ERROR; -- cross record here??? LAI IF units # 0 THEN { <> args: NodeList ¬ NodeListCons[staticLink, NodeListCons[GenConst[units, minBitsPerArgument]]]; assign: Node ¬ GenAssign[staticLink, GenApply[subOperNode, args]]; lambda.body ¬ NodeListCons[assign, lambda.body]; }; }; ENDCASE; }; }; }; }; <> TransformFork: PROC [base: BaseModel, model: LambdaModel, apply: ApplyNode] RETURNS [Node] = { <> args: NodeList ¬ apply.args; procToFork: Node = args.first; retBits: INT ¬ IntCodeUtils.WordToInt[ NARROW[args.rest.first, IntCodeDefs.WordConstNode].word]; nRets: INT ¬ ToUnits[retBits, minBitsPerArgument, logMinBitsPerArgument]; argsToCallWith: NodeList = args.rest.rest; argBits: INT ¬ BitsForArgList[argsToCallWith]; nArgs: INT ¬ ToUnits[argBits, minBitsPerArgument, logMinBitsPerArgument]; helperNode: LabelNode ¬ GenAnonLabelNode[base, NIL]; helperLabel: Label ¬ helperNode.label; helperAddr: Node ¬ GenLabelAddress[helperLabel, FALSE]; tempVar: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, bitsPerLink]; newArgs: NodeList ¬ NodeListCons[helperAddr, NodeListCons[constant0, args]]; <> <<[pc, 0, proc, nRets, args...]>> bogusBits: INT ¬ BitsForArgList[newArgs]; nBogus: INT ¬ ToUnits[bogusBits, minBitsPerArgument, logMinBitsPerArgument]; rtnVar: Var ¬ IntCodeOpt.GenAnonLocal[base, model.label, Target.bitsPerProcess]; rtnDecl: DeclNode ¬ GenDecl[rtnVar, NIL]; tempDecl: DeclNode ¬ GenDecl[tempVar, GenApply[allocOperNode, NodeListCons[GenConst[nBogus, minBitsPerArgument]]]]; assignNode: Node ¬ GenAssign[ GenDeref[tempVar, bogusBits, worst], GenComposite[newArgs, bogusBits]]; apply.args ¬ NodeListCons2[GenAddr[rtnVar], tempVar]; <> apply.bits ¬ 0; <> { <> formal: Var ¬ IntCodeOpt.GenAnonLocal[base, helperLabel, bitsPerLink]; actualProc: Var ¬ IntCodeOpt.GenAnonLocal[base, helperLabel, Target.bitsPerProc]; actualProcDecl: Node ¬ GenDecl[actualProc, GenFieldOfDeref[formal, 2*minBitsPerArgument, Target.bitsPerProc]]; applyNode: ApplyNode ¬ GenApply[actualProc, NIL, retBits]; applyListNode: NodeList ¬ NodeListCons[applyNode]; tail: NodeList ¬ applyListNode; head: NodeList ¬ NodeListCons[ actualProcDecl, NodeListCons[GenFree[formal], applyListNode]]; <> result: Node ¬ defaultNIL; newProc: LambdaNode ¬ z.NEW[NodeRep.lambda ¬ [0, lambda[ parent: NIL, descBody: NIL, kind: fork, bitsOut: bitsPerLink, formalArgs: VarListCons[formal], body: NIL]]]; IF argBits # 0 THEN { <> actualArgs: Var ¬ IntCodeOpt.GenAnonLocal[base, helperLabel, argBits]; actualArgsDecl: Node ¬ GenDecl[ actualArgs, GenFieldOfDeref[formal, 4*minBitsPerArgument, argBits]]; argsTail: NodeList ¬ NIL; offset: INT ¬ 0; FOR each: NodeList ¬ argsToCallWith, each.rest WHILE each # NIL DO bits: INT ¬ IF each.first = NIL THEN 0 ELSE each.first.bits; IF bits # 0 THEN { new: NodeList ¬ NodeListCons[GenField[actualArgs, offset, bits]]; IF argsTail = NIL THEN applyNode.args ¬ new ELSE argsTail.rest ¬ new; argsTail ¬ new; }; offset ¬ offset + bits; ENDLOOP; head ¬ NodeListCons[actualArgsDecl, head]; }; IF nRets # 0 THEN { <> retTemp: Var ¬ IntCodeOpt.GenAnonLocal[base, helperLabel, bitsPerLink]; retDecl: DeclNode ¬ GenDecl[retTemp, GenApply[allocOperNode, NodeListCons[GenConst[nRets, minBitsPerArgument]]]]; applyListNode.first ¬ GenAssign[GenDeref[retTemp, retBits, worst], applyNode]; head ¬ NodeListCons[retDecl, head]; result ¬ retTemp; }; tail ¬ tail.rest ¬ NodeListCons[GenReturn[NodeListCons[result]]]; <> head ¬ NodeListCons[ GenComment[IO.PutFR[ "intermediary proc for a FORK, nArgs: %g, nRets: %g", [integer[nArgs]], [integer[nRets]] ]], head]; newProc.body ¬ head; helperLabel.node ¬ newProc; WITH base.module SELECT FROM module: ModuleNode => <> NodeListTail[module.procs].rest ¬ NodeListCons[helperNode]; ENDCASE => ERROR; }; RETURN [GenBlock[ NodeListCons5[rtnDecl, tempDecl, assignNode, apply, rtnVar], Target.bitsPerProcess]]; }; <> CanonReturn: PROC [return: ReturnNode, model: LambdaModel] RETURNS [node: Node] = { <> IF return.rets # NIL THEN { units: INT ¬ 0; bits: INT ¬ 0; FOR each: NodeList ¬ return.rets, each.rest WHILE each # NIL DO ret: Node ¬ each.first; IF ret # NIL THEN units ¬ units + ToUnits[ret.bits, minBitsPerReturn, logMinBitsPerReturn]; ENDLOOP; bits ¬ ToBits[units, logMinBitsPerReturn]; SELECT TRUE FROM bits # model.returnBits => SIGNAL CantHappen; HasLongReturnVar[model, bits] => { <> rets: NodeList ¬ return.rets; <> IF bits = 0 THEN RETURN [emptyReturn]; RETURN [GenLargeReturn[rets, model.returnVar]]; }; ENDCASE; }; RETURN [return]; }; HasLongReturnVar: PROC [model: LambdaModel, bits: INT] RETURNS [BOOL] = { IF model.forceLong OR bits > maxBitsReturnRecord THEN { <> rtnVar: Var ¬ model.returnVar; IF bits = 0 THEN bits ¬ bitsPerLink; IF rtnVar = NIL THEN { <