<<>> <> <> <> <> <> DIRECTORY IntCodeDefs, IntCodeStuff, IntCodeUtils, C2CBasics, C2CIntCodeUtils, C2CPreprocessing, C2CTarget, Rope; C2CPreprocessingImpl: CEDAR PROGRAM IMPORTS C2CBasics, IntCodeStuff, C2CIntCodeUtils, IntCodeUtils EXPORTS C2CPreprocessing = BEGIN OPEN IntCodeDefs; debugDontChangeIntCode: BOOL ¬ FALSE; -- while debugging plain generation we might want to make sure that the int code tree is correct z: ZONE ¬ IntCodeUtils.zone; defaultFlags: VariableFlags ~ DefaultFlags[]; DefaultFlags: PROC [] RETURNS [dummyParameterFlags: VariableFlags] = { dummyParameterFlags[used] ¬ TRUE; dummyParameterFlags[constant] ¬ FALSE; dummyParameterFlags[addressed] ¬ TRUE; dummyParameterFlags[upLevel] ¬ FALSE; dummyParameterFlags[notRegister] ¬ TRUE; dummyParameterFlags[named] ¬ FALSE; }; dummyParameterFlags: VariableFlags ~ DummyParameterFlags[]; DummyParameterFlags: PROC [] RETURNS [dummyParameterFlags: VariableFlags] = { dummyParameterFlags[used] ¬ TRUE; dummyParameterFlags[constant] ¬ TRUE; dummyParameterFlags[addressed] ¬ FALSE; dummyParameterFlags[upLevel] ¬ FALSE; dummyParameterFlags[notRegister] ¬ TRUE; dummyParameterFlags[named] ¬ FALSE; }; addition: Node ¬ IntCodeStuff.GenOperNode[operRep: [arith[class: [unsigned, FALSE, C2CTarget.bitsPerWord], select: add]], bits: C2CTarget.bitsPerWord]; multiplication: Node ¬ IntCodeStuff.GenOperNode[operRep: [arith[class: [unsigned, FALSE, C2CTarget.bitsPerWord], select: mul]], bits: C2CTarget.bitsPerWord]; AppendNode: PROC [nodes: NodeList, node: Node] RETURNS [NodeList] = { IF node=NIL THEN RETURN [nodes]; IF nodes=NIL THEN RETURN [IntCodeUtils.NodeListCons[first: node]]; FOR nl: NodeList ¬ nodes, nl.rest DO IF nl.rest=NIL THEN {nl.rest ¬ IntCodeUtils.NodeListCons[first: node]; RETURN [nodes]} ENDLOOP; }; GenNullSource: PROC [nodes: NodeList, bits: INT ¬ 0] RETURNS [SourceNode] = { RETURN [z.NEW[NodeRep.source ¬ [bits, source[nullSourceRange, nodes]]]]; }; HasFreeDecl: PROC [node: Node] RETURNS [has: BOOL ¬ FALSE] = { <<--returns whether node has declarations not inside local blocks>> Inner: IntCodeUtils.Visitor = { WITH node SELECT FROM block: BlockNode => RETURN [node]; --stop recursion decl: DeclNode => {has ¬ TRUE; RETURN [node]}; ENDCASE => {}; IntCodeUtils.MapNode[node, Inner]; RETURN [node] }; [] ¬ Inner[node]; }; GenWordConst: PROC [val: Word, bits: INT] RETURNS [ConstNode] = { RETURN [ IntCodeStuff.GenConst[int: IntCodeUtils.WordToInt[val], bits: bits] ]; }; GenLocalLocation: PROC [id: VariableId, parent: Label ¬ NIL] RETURNS [loc: Location] = { loc ¬ z.NEW[LocationRep.localVar ¬ [localVar[id: id, parent: parent]]]; }; GenIndexedLocation: PROC [base: Node, index: Node] RETURNS [loc: IndexedLocation] = { loc ¬ z.NEW[LocationRep.indexed ¬ [indexed[base: base, index: index]]]; }; ProcDescMayRaiseErrors: PROC [applyNode: ApplyNode] RETURNS [BOOL] = { <<--this does NOT tell whether the procedure raises errors, but,>> <<--whether finding the procedure descriptor may raise errors >> WITH applyNode.proc SELECT FROM oper: OperNode => RETURN [FALSE]; mc: MachineCodeNode => RETURN [FALSE]; ENDCASE => RETURN [~IntCodeUtils.SideEffectFree[applyNode.proc, TRUE]]; }; ArgsMayRaiseErrors: PROC [applyNode: ApplyNode] RETURNS [BOOL] = { RETURN [~IntCodeUtils.SideEffectFreeList[applyNode.args, TRUE]] }; ConsideredConstant: PROC [node: Node] RETURNS [isConst: BOOL ¬ TRUE] = { isConst ¬ WITH node SELECT FROM var: Var => var.flags[constant], const: ConstNode => TRUE, decl: DeclNode => ConsideredConstant[decl.var] AND IntCodeUtils.SideEffectFree[decl.init, TRUE], comment: CommentNode => TRUE, ENDCASE => FALSE; }; MoveFrontParametersWhichMayRaiseErrors: PROC [node: Node] = { DoIt: PROC [outer: Node, parent: Label] = { Inner: IntCodeUtils.Visitor = { DoIt[node, parent]; WITH node SELECT FROM applyNode: ApplyNode => { IF applyNode.handler#NIL THEN { descDanger: BOOL ¬ ProcDescMayRaiseErrors[applyNode: applyNode]; argDanger: BOOL ¬ ArgsMayRaiseErrors[applyNode: applyNode]; blockNodes: NodeList ¬ NIL; Move: PROC [node: Node] RETURNS [substitute: Var] = { <<--side effect: changes blockNode (also uses C2CIntCodeUtils.NewVariableId, parent)>> GenLocalVariable: PROC [parent: Label, bits: INT, flags: VariableFlags ¬ defaultFlags] RETURNS [var: Var] = { <<--generates new variable, but not yet declared>> id: VariableId ¬ C2CIntCodeUtils.NewVariableId[]; loc: Location ¬ GenLocalLocation[id, parent]; var ¬ IntCodeStuff.GenAnonVar[bits, loc]; var.id ¬ id; var.flags ¬ flags; }; var: Var ¬ GenLocalVariable[parent, node.bits, dummyParameterFlags]; declNode: Node ¬ IntCodeStuff.GenDecl[var: var, init: node]; blockNodes ¬ AppendNode[blockNodes, declNode]; RETURN [var]; }; IF descDanger THEN { substitute: Var ¬ Move[applyNode.proc]; applyNode.proc ¬ substitute; }; IF argDanger THEN { FOR args: NodeList ¬ applyNode.args, args.rest WHILE args#NIL DO IF ~ConsideredConstant[args.first] THEN { <<--constancy required, not error-freeness (evaluation order!)>> substitute: Var ¬ Move[args.first]; args.first ¬ substitute; } ENDLOOP; }; IF blockNodes#NIL THEN { blockNodes ¬ AppendNode[blockNodes, applyNode]; IF HasFreeDecl[applyNode] THEN RETURN [GenNullSource[nodes: blockNodes, bits: applyNode.bits]] ELSE RETURN [IntCodeStuff.GenBlock[nodes: blockNodes, bits: applyNode.bits]]; }; }; }; ENDCASE => {}; RETURN [node]; }; <<--DoIt>> WITH outer SELECT FROM labelNode: LabelNode => WITH labelNode.label.node SELECT FROM labeledLambda: LambdaNode => parent ¬ labelNode.label; ENDCASE => {}; ENDCASE => {}; IntCodeUtils.MapNode[outer, Inner]; }; <<--don't clobber IntCode tree while debugging other parts>> IF debugDontChangeIntCode THEN RETURN; <<--check to make sure the recursion starts right at this point>> IF ISTYPE[node, ApplyNode] THEN ERROR; --recursion starts wrong DoIt[node, NIL]; }; EliminateSideEffectsOfErrors: PUBLIC PROC [] = { CleanupFields[]; CleanupLabels[]; MoveFrontParametersWhichMayRaiseErrors[C2CBasics.rootNode]; C2CBasics.Report[]; }; CleanupLabels: PROC [] = { usedlabels: IntCodeUtils.IdTab ¬ IntCodeUtils.NewIdTab[]; FindGotos: IntCodeUtils.Visitor = { WITH node SELECT FROM goto: GotoNode => [] ¬ IntCodeUtils.Store[usedlabels, goto.dest.id, $used]; oper: OperNode => WITH oper.oper SELECT FROM codeOp: CodeOper => [] ¬ IntCodeUtils.Store[usedlabels, codeOp.label.id, $used]; ENDCASE => {}; ENDCASE => {}; IntCodeUtils.MapNode[node, FindGotos]; RETURN [node]; }; VisitLambdas: IntCodeUtils.Visitor = { <<--for each lambda call VisitLabels>> WITH node SELECT FROM lambda: LambdaNode => { IntCodeUtils.MapNode[node, VisitLabels]; RETURN [node]; --dont recurse }; ENDCASE => {}; IntCodeUtils.MapNode[node, VisitLambdas]; RETURN [node]; }; VisitLabels: IntCodeUtils.Visitor = { <<--and replace id of unused labels>> WITH node SELECT FROM label: LabelNode => { IF label.label.id#nullLogicalId AND IntCodeUtils.Fetch[usedlabels, label.label.id]=IntCodeUtils.NullValue THEN { label.label.id ¬ nullLogicalId }; }; ENDCASE => {}; IntCodeUtils.MapNode[node, VisitLabels]; RETURN [node]; }; [] ¬ FindGotos[C2CBasics.rootNode]; [] ¬ VisitLambdas[C2CBasics.rootNode]; }; MaskOut: PROC [use: Word, from, bits: INT] RETURNS [Word] = { <<--returns "bits" rightadjusted bits from location "from">> ShiftRight: PROC [use: Word, bits: INT] RETURNS [Word] = { IF bits#0 THEN { FOR b: INT DECREASING IN [bits..IntCodeDefs.bitsPerWord) DO use[b] ¬ use[b-bits] ENDLOOP; }; RETURN [use]; }; RestrictTo: PROC [use: Word, bits: INT] RETURNS [Word] = { FOR b: INT IN [0..IntCodeDefs.bitsPerWord-bits) DO use[b] ¬ FALSE; ENDLOOP; RETURN [use]; }; use ¬ ShiftRight[use, C2CTarget.bitsPerWord-(from+bits)]; RETURN [RestrictTo[use, bits]]; }; HasLeftBitSet: PROC [limWord: Word, leftBits: INT] RETURNS [BOOL ¬ FALSE] = { FOR b: INT IN [0..leftBits+IntCodeDefs.bitsPerWord-C2CTarget.bitsPerWord) DO IF limWord[b] THEN RETURN [TRUE]; ENDLOOP }; SimplifyBoundsChecks: PROC [oBckApply: ApplyNode] RETURNS [replaceBy: Node] = { <<--oCkApply = outer bounds checking apply>> <<--When called, oBckApply oper most be a check operation, lt, unsigned, <= one word>> replaceBy ¬ oBckApply; IF oBckApply.handler=NIL THEN { oBCkSimple: BOOL; oBCkLimit: CARD; [oBCkSimple, oBCkLimit] ¬ C2CIntCodeUtils.IsSimpleConst[--limit--oBckApply.args.rest.first]; IF oBCkSimple THEN { oArgument: Node ¬ oBckApply.args.first; WITH oArgument SELECT FROM nApply: ApplyNode => IF nApply.handler=NIL THEN { WITH nApply.proc SELECT FROM nOper: OperNode => { WITH nOper.oper SELECT FROM nCheckOp: CheckOper => { IF CheckOpSimple[nCheckOp] THEN { nBCkSimple: BOOL; nBCkLimit: CARD; [nBCkSimple, nBCkLimit] ¬ C2CIntCodeUtils.IsSimpleConst[--limit--nApply.args.rest.first]; IF nBCkSimple THEN { IF oBCkLimit<=nBCkLimit THEN {oBckApply.args.first ¬ nApply.args.first} ELSE {replaceBy ¬ nApply} }; }; }; nArithOp: ArithOper => { --precision ok without test IF nArithOp.class.kind=unsigned AND nArithOp.select=sub THEN { WITH nApply.args.first --base of sub-- SELECT FROM bApply: ApplyNode => IF bApply.handler=NIL THEN { WITH bApply.proc SELECT FROM bOper: OperNode => WITH bOper.oper SELECT FROM bCheckOp: CheckOper => { IF CheckOpSimple[bCheckOp] THEN { nBCkSimple: BOOL; nBCkLimit: CARD; [nBCkSimple, nBCkLimit] ¬ C2CIntCodeUtils.IsSimpleConst[--limit--bApply.args.rest.first]; IF nBCkSimple THEN { subSimple: BOOL; subVal: CARD; [subSimple, subVal] ¬ C2CIntCodeUtils.IsSimpleConst[--sub--nApply.args.rest.first]; IF nBCkSimple AND subSimple AND oBCkLimit<=nBCkLimit AND CARD[nBCkLimit-oBCkLimit]>=subVal THEN { nApply.args.first --base of sub-- ¬ bApply.args.first }; }; }; }; ENDCASE --bOper.oper-- => {}; ENDCASE --bApply.proc-- => {}; }; ENDCASE --nApply.args.first-- => {}; }; }; ENDCASE --nOper.oper-- => {}; }; ENDCASE --nApply.proc-- => {}; }; ENDCASE --oArgument-- => {}; }; }; }; CheckOpSimple: PROC [checkOp: CheckOper] RETURNS [BOOL] = { RETURN [checkOp.sense=lt AND checkOp.class.kind=unsigned AND checkOp.class.precision<=C2CTarget.bitsPerWord] }; CleanupFields: PROC [] = { <<--eliminate masking constants and bound checks>> <<--reduce frequent flags where problems are to be expected>> <<--also compute size of largest dummy>> largestDummy: INT ¬ 0; idsForPtrTab: IntCodeUtils.IdTab ¬ IntCodeUtils.NewIdTab[]; lhs: BOOL ¬ FALSE; --used as hint only FindFields: IntCodeUtils.Visitor = { oldLhs: BOOL ¬ lhs; --set on every RETURN WITH node SELECT FROM apply: ApplyNode => { <<--fix converts of mesa errors from 0 bits to 1 bit>> WITH apply.proc SELECT FROM oper: OperNode => WITH oper.oper SELECT FROM checkOp: CheckOper => { IF CheckOpSimple[checkOp] THEN node ¬ SimplifyBoundsChecks[apply] }; arithOp: ArithOper => { <> IF arithOp.class.kind=unsigned AND arithOp.class.precision=BITS[CARD] AND apply.args#NIL AND apply.args.rest#NIL THEN { arg1: Node ¬ apply.args.first; arg2: Node ¬ apply.args.rest.first; WITH arg1 SELECT FROM carg1: IntCodeDefs.WordConstNode => { WITH arg2 SELECT FROM carg2: IntCodeDefs.WordConstNode => { fold: BOOL ¬ FALSE; value: CARD; c1: CARD ¬ IntCodeUtils.WordToCard[carg1.word]; c2: CARD ¬ IntCodeUtils.WordToCard[carg2.word]; SELECT arithOp.select FROM add => {value ¬ c1+c2; fold ¬ TRUE}; sub => {value ¬ c1-c2; fold ¬ TRUE}; mul => {value ¬ c1*c2; fold ¬ TRUE}; div => IF c2#0 THEN {value ¬ c1/c2; fold ¬ TRUE}; mod => IF c2#0 THEN {value ¬ c1 MOD c2; fold ¬ TRUE}; ENDCASE; IF fold THEN node ¬ IntCodeStuff.GenConst[LOOPHOLE[value], C2CTarget.bitsPerWord]; }; ENDCASE; }; ENDCASE; }; }; convert: ConvertOper => IF convert.from.precision=0 AND apply.args.first.bits=0 AND convert.to.precision>0 THEN { WITH apply.args.first SELECT FROM apply2: ApplyNode => WITH apply2.proc SELECT FROM oper2: OperNode => WITH oper2.oper SELECT FROM mesa2: MesaOper => SELECT mesa2.mesa FROM error => { apply2.bits ¬ 1; convert.from.precision ¬ 1; }; ENDCASE => {--?--}; ENDCASE => {--?--}; ENDCASE => {--?--}; ENDCASE => {--?--}; }; ENDCASE => {}; ENDCASE => {}; }; var: Var => { IF var.flags[frequent] THEN { IF var.bits#C2CTarget.bitsPerWord THEN var.flags[frequent] ¬ FALSE; IF var.flags[addressed] THEN var.flags[frequent] ¬ FALSE; IF var.flags[notRegister] THEN var.flags[frequent] ¬ FALSE; IF var.flags[upLevel] THEN var.flags[frequent] ¬ FALSE; IF var.flags[frequent] THEN [] ¬ IntCodeUtils.Store[idsForPtrTab, var.id, $yes]; --provisionally }; WITH var.location SELECT FROM field: FieldLocation => { <<--don't do that, size may be wrong>> <> <<--field is complete record>> <> <<};>> IF var.bits>0 AND field.base.bits<=C2CTarget.bitsPerWord THEN { WITH field.base SELECT FROM var: Var => var.flags[frequent] ¬ FALSE; --some code to take fields uses the address const: ConstNode => <<--return new, modified constant if possible>> WITH const SELECT FROM wordConst: WordConstNode => { <<--we've got it!>> replacementNode: Node ¬ GenWordConst[MaskOut[wordConst.word, field.start, var.bits], var.bits]; lhs ¬ oldLhs; RETURN [replacementNode]; --instead of field-var! }; ENDCASE => {}; apply: ApplyNode => <<--eliminate masking over bound checks if possible>> WITH apply.proc SELECT FROM oper: OperNode => WITH oper.oper SELECT FROM checkOp: CheckOper => { IF checkOp.sense=lt AND field.start+var.bits=C2CTarget.bitsPerWord THEN { isConst: BOOL; p: INT ¬0; limWord: Word; constVal: CARD ¬ 0; limit: Node ¬ apply.args.rest.first; [isConst, constVal] ¬ C2CIntCodeUtils.IsSimpleConst[limit]; IF isConst AND constVal>0 THEN { limWord ¬ IntCodeUtils.CardToWord[constVal-1]; IF ~HasLeftBitSet[limWord, field.start] THEN { <<--we've got it!>> replacementNode: Node ¬ z.NEW[apply NodeRep ¬ apply­]; replacementNode.bits ¬ var.bits; lhs ¬ oldLhs; RETURN [replacementNode]; --instead of field-var! }; }; }; }; ENDCASE => {}; ENDCASE => {}; ENDCASE => {}; }; }; indexed: IndexedLocation => { <<--try to get rid of multi dimensional packed arrays>> IF var.bits WITH bvar.location SELECT FROM bindexed: IndexedLocation => IF bvar.bits IF bfield.start MOD C2CTarget.bitsPerWord # 0 THEN { IF bfield.start MOD var.bits = 0 THEN { <<--now field can be removed or changed, but>> <<--check if removing field actually helps...>> doReplace: BOOL ¬ FALSE; IF bfield.base.bits<=C2CTarget.bitsPerWord AND C2CIntCodeUtils.SizeIsProvableOk[bfield.base] THEN { <> doReplace ¬ TRUE; }; IF doReplace THEN { offset: Node ¬ IntCodeStuff.GenConst[bfield.start/var.bits, C2CTarget.bitsPerWord]; summands: NodeList ¬ IntCodeStuff.NodeListCons2[indexed.index, offset]; newIndex: ApplyNode ¬ IntCodeStuff.GenApply[addition, summands, C2CTarget.bitsPerWord]; newLocation: Location ¬ GenIndexedLocation[base: bfield.base, index: newIndex]; var.location ¬ newLocation; lhs ¬ oldLhs; RETURN [FindFields[node]]; --recurse on same node }; }; }; ENDCASE => {}; ENDCASE => {} }; indexed.base ¬ FindFields[indexed.base]; lhs ¬ FALSE; indexed.index ¬ FindFields[indexed.index]; lhs ¬ oldLhs; RETURN [node]; }; dummy: DummyLocation => largestDummy ¬ MAX[largestDummy, var.bits]; deref: DerefLocation => { lhs ¬ FALSE; deref.addr ¬ FindFields[deref.addr]; lhs ¬ oldLhs; RETURN [node]; }; ENDCASE => {}; }; assign: AssignNode => { lhs ¬ TRUE; assign.lhs ¬ NARROW[FindFields[assign.lhs]]; lhs ¬ FALSE; assign.rhs ¬ FindFields[assign.rhs]; lhs ¬ oldLhs; RETURN [node]; }; ENDCASE => {}; IntCodeUtils.MapNode[node, FindFields]; lhs ¬ oldLhs; RETURN [node]; }; EliminateVar: PROC [var: Var] = { IF var.flags[frequent] AND var.bits=C2CTarget.bitsPerWord THEN [] ¬ IntCodeUtils.Store[idsForPtrTab, var.id, NIL]; }; EliminatePointerUsage: IntCodeUtils.Visitor = { WITH node SELECT FROM var: Var => { EliminateVar[var]; WITH var.location SELECT FROM deref: DerefLocation => { <<--avoids direct visit of addr>> IntCodeUtils.MapNode[deref.addr, EliminatePointerUsage]; RETURN [node]; }; ENDCASE => {}; }; decl: DeclNode => { <<--don't recurse on the var itself!>> [] ¬ EliminatePointerUsage[decl.init]; WITH decl.init SELECT FROM oper: OperNode => WITH oper.oper SELECT FROM mesa: MesaOper => SELECT mesa.mesa FROM globalFrame => {}; --that is ok usage ENDCASE => EliminateVar[decl.var]; ENDCASE => EliminateVar[decl.var]; ENDCASE => IF decl.init#NIL THEN EliminateVar[decl.var]; RETURN [node]; --avoids direct visit of var }; ENDCASE => {}; IntCodeUtils.MapNode[node, EliminatePointerUsage]; RETURN [node]; }; C2CBasics.PutProp[$MayBeDeclaredAsPtrTab, idsForPtrTab]; C2CBasics.PutProp[$LargestDummy, NEW[INT ¬ largestDummy]]; [] ¬ FindFields[C2CBasics.rootNode]; [] ¬ EliminatePointerUsage[C2CBasics.rootNode]; }; MayBeDeclaredAsPtr: PUBLIC PROC [var: IntCodeDefs.Var] RETURNS [BOOL ¬ FALSE] = { IF var.bits#C2CTarget.bitsPerWord THEN RETURN [FALSE]; IF ~var.flags[frequent] THEN RETURN [FALSE]; WITH C2CBasics.GetProp[$MayBeDeclaredAsPtrTab] SELECT FROM tab: IntCodeUtils.IdTab => RETURN [IntCodeUtils.Fetch[tab, var.id]=$yes] ENDCASE => RETURN [FALSE]; }; END.