! File: FINAL.BLI ! Module FINAL = Begin ! FINAL MODULE ! ------------ ! Require 'Bliss'; Own CHANGE : Boolean, PUSHPOPFLAG : Integer; Forward Routine ADRLEN, ADRSIMP, BACKEQ, BACKOV, BACKSET, BRIMPLY, CELLLENGTH, CLEARTOLAB, COMBLAB, CONDJMPSRC, DELADR, DELETE, DELBACKREF : Novalue, DELTST, DOWNDATE : Novalue, DUPADR, EQUIVADR, EQUIVFWD : Novalue, EQUIVIND, FINAL : Novalue, FINAL1 : Novalue, FINAL2 : Novalue, FINAL3 : Novalue, FIXJMPIND, LABREFED, LABREFP, MAKEADR : Novalue, MAKEINFLOOP : Novalue, MAKELABREF, MAKEJMP, MAYPOP, MAY_REFER, MAY_USE_ADDR, MVLABREF, OPT_ADDSUB, OPT_BIC, OPT_BIS, OPT_BIT, OPT_BOOLEAN, OPT_CLC, OPT_CLR, OPT_CLVC, OPT_CMP, OPT_DEC, OPT_INC, OPT_JSR, OPT_MOV, OPT_TST, OPT_CASE, OPT_COND_JUMP, OPT_LABEL, OPT_UNCOND_JUMP, REACH, IS_SAME_ADR, IS_SAME_CMP, IS_SAME_IND, SETSCC, TESTCC, TESTEQL, TESTPOP, TESTPP, TESTPUSH, UPDATE : Novalue; Macro SETCHANGE = (CHANGE = TRUE) %, REVCOND(I) = (I Xor 1) %, CCSHIFT(X) = ((X)^9) %, REDUCEADR(X) = Begin DELADR(Block[X,cel_src],X); COPYADR(Block[X,cel_src],Block[x,cel_dst]) End %; Require 'final_tables.req'; ! DEFINITIONS FOR "BASE" ADDRESSES Bind PCADR = ADRPLIT( GENREG, PC,UNUSED, NAME_NORMAL, 0) : ADRVARSTR, IMMEDZERO = ADRPLIT( AUTOINCR,PC,ADDR_IMMED, NAME_NORMAL, 0) : ADRVARSTR, IMMEDONE = ADR1PLIT(AUTOINCR,PC,ADDR_IMMED, NAME_NORMAL, 0) : ADRVARSTR, MEMADR = ADRPLIT( INDEXED, PC,ADDR_MEMORY,NAME_LABEL, 0) : ADRVARSTR, REGADR = ADRPLIT( GENREG, 0,ADDR_REG, NAME_NORMAL,0) : ADRVARSTR, SPADR = ADRPLIT( GENREG, SP,ADDR_REG, NAME_NORMAL,0) : ADRVARSTR; ! THIS SECTION CONTAINS THE REAL WORK ! OF FINAL -- CROSS-JUMPS, ETC. ! ! FUNCTION: ! RETURN THE LENGTH IN WORDS (EITHER 1 OR 0) OF THE PDP-11 ! REPRESENTATION OF ADDRESS 'ADR'. HAS AN OPTIMIZATION SIDE EFFECT. ! Routine ADRLEN(ADR : Ref ADRVARSTR) = Begin ! change 0(reg) to @reg If .ADR[adr_mode] Eql INDEXED And .ADR[adr_reg] Neq PC And .ADR[adr_name] Leq 1 And .ADR[adr_disp] Eql 0 Then ADR[adr_mode] = GENREG + DEFERRED; ! indexed modes and auto-inc PC modes take an extra word If (.ADR[adr_mode] And 6) EQL INDEXED Then Return TRUE; If .ADR[adr_reg] Eql PC And (.ADR[adr_mode] And 6) Eql AUTOINCR Then Return TRUE Else Return FALSE End; ! ! MEASURES THE SIMPLICITY OF AN ADDRESS. ! USED BY EQUIVADR AND EQUIVIND. ! Routine ADRSIMP(A : Ref ADRVARSTR) = Begin Local A1 : Integer, A2 : Integer, A4 : Integer, A12 : Integer; ! classification of addressing mode complexities: ! ! 0 -impossible- ! 1 #0 ! 2 R ! 3 @SP ! 4 @R, (R)+, -(R) ! 5 #n ! 6 #k ! 7 n ! 8 k(SP) ! 9 n(R) ! 10 @#k ! 11 k(R) ! 12 @0(SP) ! 13 @n ! 14 @k(SP) ! 15 @n(R) ! 16 @k(R), @(R)+, @-(R) ! 17 @k Bind SIMPLICITY = Uplit Byte ( 0, ! zero immed -impossible- 1, ! PC zero immed #0 0, ! immed -impossible- 6, ! PC immed #k 0, ! zero SP immed -impossible- 0, ! PC zero SP immed -impossible- 0, ! SP immed -impossible- 0, ! PC SP immed -impossible- 0, ! zero name immed -impossible- 5, ! PC zero name immed #n 0, ! name immed -impossible- 5, ! PC name immed #n 16, ! zero indir @(R)+ \ @-(R) \ @0(R) 17, ! PC zero indir @k 16, ! indir @k(R) 17, ! PC indir @k 12, ! zero SP indir @0(SP) 0, ! PC zero SP indir -impossible- 14, ! SP indir @k(SP) 0, ! PC SP indir -impossible- 15, ! zero name indir @n(R) 13, ! PC zero name indir @n 15, ! name indir @n(R) 13, ! PC name indir @n 4, ! zero @R \ (R)+ \ -(R) 10, ! PC zero @#0 11, ! k(R) 10, ! PC k(PC), @#k 3, ! zero SP 0(SP) 0, ! PC zero SP -impossible- 8, ! SP k(SP) 0, ! PC SP -impossible- 9, ! zero name n(R) 7, ! PC zero name n 9, ! name n(R) 7) ! PC name n : Vector[,Byte]; If .A[adr_type] Eql ADDR_REG Then Return 2; If Not ONEOF(.A[adr_type],ADDR_IMMED,ADDR_MEMORY,ADDR_INDIRECT) Then Return 0; A1 = 1*(.A[adr_reg] Eql PC); A2 = 2*(.A[adr_disp] Neq 0); A4 = 4*(If .A[adr_name] Eql 0 Then 0 Else If .A[adr_name] Eql 1 Then 1 Else 2); A12= 12*(If .A[adr_type] Eql ADDR_IMMED Then 0 Else If .A[adr_type] Eql ADDR_INDIRECT Then 1 Else 2); Return .SIMPLICITY[.A12 + .A4 + .A2 + .A1] End; ! ! TRUE IF ADDRESSES A AND B ARE ! EQUIVALENT ENOUGH TO BE CROSS-JUMPED OVER. ! DA AND DB ARE STACK ADJUSTS (OR ADJUSTS TO OTHER REGISTERS), ! AND THERE MAY BE SIDE-EFFECTS ON THESE. ! Routine BACKEQ(A : Ref ADRVARSTR,B : Ref ADRVARSTR,DA,DB) = Begin ! if 'A' is a label then 'B' had better be a label too and reference ! the same cell. If .A[adr_name_type] Eql NAME_LABEL Then If .B[adr_name_type] Eql NAME_LABEL Then Return .Block[.A[adr_name],cel_ref_ef] Eql .Block[.B[adr_name],cel_ref_ef] Else Return FALSE; ! if NAME_FORMAL then no match If .A[adr_name_type] Neq NAME_NORMAL Then Return FALSE; ! the two addresses must use the same register ! and name and the operand must actually exist. If .A[adr_reg] Neq .B[adr_reg] Or .A[adr_name] Neq .B[adr_name] Or .A[adr_type] Eql UNUSED Then Return FALSE; ! if not complex, need only to compare displacements If .A[adr_reg] Neq SP Then Return .A[adr_disp] Eql .B[adr_disp] And .A[adr_delta] Eql .B[adr_delta]; ! stack modes. check displacements, taking into account stack adjustments If .A[adr_disp] + ..DA Neq .B[adr_disp] + ..DB Then Return FALSE; ! if the value of SP itself, the adjustments must be the same for them ! to be equal If .A[adr_type] Eql ADDR_REG Then Return ..DA Eql 0; ! if no need to change adjustments If ..DA Eql 0 And ..DB Eql 0 And .A[adr_delta] Eql .B[adr_delta] Then Return TRUE; ! MUST CHANGE STACK ADJUSTS, OFFSETS, DELTAS, OR EVEN MODES. ! compute the new displacements and modify the adjustments ! according to any auto-increments/decrements A[adr_disp] = .A[adr_disp] + ..DA; B[adr_disp] = .B[adr_disp] + ..DB; .DA = ..DA - .A[adr_delta]; .DB = ..DB - .B[adr_delta]; A[adr_delta] = 0; B[adr_delta] = 0; ! re-compute the addressing modes A[adr_mode] = (If .A[adr_type] Eql ADDR_INDIRECT Then INDEXED+DEFERRED Else If .A[adr_disp] Eql 0 Then GENREG+DEFERRED Else INDEXED); B[adr_mode] = (If .B[adr_type] Eql ADDR_INDIRECT Then INDEXED+DEFERRED Else If .B[adr_disp] Eql 0 Then GENREG+DEFERRED Else INDEXED); Return TRUE End; ! ! FUNCTION: ! THIS ROUTINE PERFORMS CROSS-JUMPING. F AND T ARE POINTERS TO ! THE ENDS OF TWO BLOCKS OF CODE (THE "FROM" BLOCK AND THE "TO" BLOCK), ! WHICH JOIN EACH OTHER BY BRANCHING OF SOME SORT. ! ! IDENTIFIERS: ! DF,DT -- (DELTA F, DELTA T) CONTAIN THE AMOUNT THE STACK MUST BE ! ADJUSTED AT THE TOP OF EACH BLOCK. ! SDF,SDT -- BEFORE ATTEMPTING TO BACK OVER A CODE CELL, ! BACKOV SAVES DF & DT IN THESE TWO. ! ADJUST -- THE AMOUNT THE STACK MUST BE ADJUSTED AT THE ! BOTTOM OF THE "TO" BLOCK. ! FLAG -- INITIALLY 1; SET TO 0 AS SOON AS A CELL IS SUCCESSFULLY ! BACKED OVER. ! SKIPFLAG -- IS SET FOR ONE OF TWO REASONS. THE FIRST IS WHEN ! A TWO-WORD STACK ADJUSTMENT IS NECESSARY IF THE ! CURRENT CELL IS TO BE BACKED OVER; THIS PREVENTS ! CROSS-JUMPING FROM LOSING BY INSERTING TWO WORDS ! OF STACK-ADJUSTING CODE TO SAVE ONE WORD OF CROSS- ! JUMPED CODE. ! Routine BACKOV(F : Ref CELL,T : Ref CELL) = Begin Local ADJUST : Integer, DF : Integer, DT : Integer, SDF : Integer, SDT : Integer, FINIT : Ref CELL, TINIT : Ref CELL, FLAG : Boolean, SKIPFLAG : Boolean, DSTF : ADRVARSTR, DSTT : ADRVARSTR; FINIT = .F; TINIT = PRVCC(.T); SKIPFLAG = FALSE; FLAG = TRUE; ! skip over any stack adjustments and get the adjustment value F = PRVCC(BACKSET(.F,DF)); T = BACKSET(.T,DT); T = .T[cel_prev]; ! compute the difference in stack adjustments ADJUST = .DT - .DF; If .ADJUST Neq 0 Then If .ADJUST Lss 0 Then Begin ! set skipflag if more than two words of adjustment are needed If .DF Eql 0 And .ADJUST Lss -4 Then SKIPFLAG = TRUE; DF = -.ADJUST; ADJUST = 0; DT = 0 End Else Begin ! set skipflag if more than two words of adjustment are needed If .DT Eql 0 And .ADJUST Gtr 4 Then SKIPFLAG = TRUE; DT = .ADJUST; DF = 0 End Else If .DF Neq 0 Then Begin F = NXTCC(.F); T = NXTCC(.T); ADJUST = 0; DF = 0; DT = 0 End; While TRUE Do Begin ! save the stack depths SDF = .DF; SDT = .DT; ! if not at a code cell then move to the previous one, making sure ! we don't move over non-cells when we have a stack adjustment If .T[cel_type] Neq CELL_CODE Then If .DT Neq 0 Then Exitloop Else T = PRVCC(.T); ! ditto for 'F' If .F[cel_type] Neq CELL_CODE Then If .DF Neq 0 Then Exitloop Else F = PRVCC(.F); ! must both be the same instruction code If .T[cel_code] Neq .F[cel_code] Then Exitloop; ! now compare operands Case .OPERTYPE[.T[cel_code]] From LO_OPTYPE To HI_OPTYPE Of Set [ OPTYPE_NOP ]: Begin ! cannot cross-jump over INLINE code If .T[cel_code] Eql PINLINE Then Exitloop; ! cannot cross-jump over unconditional jumps If .T[cel_code] Eql PRTI Then Exitloop; ! cannot cross-jump over any instructions which implicitly use ! the stack pointer. If .T[cel_code] Eql PIOT Then If .DF Neq 0 Or .DT Neq 0 Then Exitloop End; [ OPTYPE_ONE ]: ! one-operand - compare the single operands If Not BACKEQ(F[cel_src],T[cel_src],DF,DT) Then Exitloop; [ OPTYPE_TWO ]: Begin ! save the destination operands in case the BACKEQ for the source ! fails and the destination was modified. COPYADR(DSTF,F[cel_dst]); COPYADR(DSTT,T[cel_dst]); ! compare destination operands If Not BACKEQ(F[cel_dst],T[cel_dst],DF,DT) Then Exitloop; ! compare source operands, restoring the destination in case it ! was destroyed. If Not BACKEQ(F[cel_src],T[cel_src],DF,DT) Then Begin COPYADR(F[cel_dst],DSTF); COPYADR(T[cel_dst],DSTT); Exitloop End End; [ OPTYPE_BR ]: Begin ! cannot cross-jump over unconditional jumps If .T[cel_code] Eql PJMP Or .T[cel_code] Eql PBR Then Exitloop; ! no stack adjustments allowed at fork points If .DF Neq 0 Or .DT Neq 0 Then Exitloop; ! compare the branch destinations If Not BACKEQ(F[cel_src],T[cel_src],DF,DT) Then Exitloop End; [ OPTYPE_JSR ]: Begin ! JSR implicitly used the stack so no adjustments allowed. If .DF Neq 0 Or .DT Neq 0 Then Exitloop; ! quick form of BACKEQ since the source may only be a register If .F[cel_src_reg] Neq .T[cel_src_reg] Then Exitloop; ! compare destinations If Not BACKEQ(F[cel_dst],T[cel_dst],DF,DT) Then Exitloop End; [ OPTYPE_RTS ]: ! cannot cross-jump over unconditional jumps Exitloop; [ OPTYPE_TRAP ]: Begin ! TRAP/EMT instructions implicitly use the stack so no adjustments ! are allowed. If .DF Neq 0 Or .DT Neq 0 Then Exitloop; ! compare TRAP/EMT codes If .F[cel_src_disp] Neq .T[cel_src_disp] Then Exitloop End; [ OPTYPE_WORD ]: Exitloop; [ OPTYPE_CASE ]: ! case jumps are maybe a little too hairy to cross-jump Exitloop Tes; ! if a final stack adjustment is needed and what was gained from ! cross-jumping was a single instruction then don't bother with ! doing cross-jumping since the stack adjustment generates more ! code than gained from cross-jumping. If Not .SKIPFLAG Or CELLLENGTH(.F) Neq 1 Then Begin ! cross-jumping succeeded. insert a JMP from 'F' to 'T' SETCHANGE; FLAG = FALSE; F = BEFORE(.F,NEWCODECELL()); MAKEJMP(.F,.T); T = .T[cel_prev] End; ! now setup to try the next cell. SKIPFLAG = FALSE; T = .T[cel_prev]; F = .F[cel_prev] End; ! return if nothing gained from cross-jumping If .FLAG Then Return .FINIT[cel_next]; ! generate any common stack adjustment If .ADJUST Neq 0 Then Begin TINIT = AFTER(.TINIT,NEWCODECELL()); TINIT[cel_code] = PADD; TINIT[cel_class] = INST_ADD_IMMED; COPYADR(TINIT[cel_dst],SPADR); COPYADR(TINIT[cel_src],IMMEDZERO); TINIT[cel_src_disp] = .ADJUST End; ! generate any stack adjustment for 'F' F = .F[cel_next]; If .SDF Neq 0 Then Begin F = BEFORE(.F,NEWCODECELL()); F[cel_code] = PSUB; F[cel_class] = INST_ADD_IMMED; COPYADR(F[cel_dst],SPADR); COPYADR(F[cel_src],IMMEDZERO); F[cel_src_disp] = .SDF; End; ! ditto for 'T' If .SDT Neq 0 Then Begin T = AFTER(.T,NEWCODECELL()); T[cel_code] = PSUB; T[cel_class] = INST_ADD_IMMED; COPYADR(T[cel_dst],SPADR); COPYADR(T[cel_src],IMMEDZERO); T[cel_src_disp] = .SDT End; Return .F End; ! ! FUNCTION: ! BACK OVER ANY STACK ADJUSTING INSTRUCTIONS, UPDATING BOTH ! CODE CELL POINTER "A" AND DELTA-VALUE "DA". THIS IS ESSENTIALLY ! A SETTING-UP ROUTINE FOR "BACKOV". ! Routine BACKSET(A : Ref CELL,DA) = Begin Local B : Ref CELL; B = PRVCC(.A); .DA = 0; ! look for 'op #n,SP' where we know 'op' can only be ADD or SUB If .B[cel_src_type] Neq ADDR_IMMED Or .B[cel_src_name] Neq 0 Or .B[cel_dst_type] Neq ADDR_REG Or .B[cel_dst_reg] Neq SP Then Return .A; ! fetch the stack adjust If .B[cel_code] Eql PADD Then .DA = -.B[cel_src_disp] Else If .B[cel_code] Eql PSUB Then .DA = .B[cel_src_disp] Else Return .A; ! if 'ADD #0,SP' then delete it and try again If ..DA Eql 0 Then B = BACKSET(DELETE(.B)); Return .B End; ! ! F AND T ARE OPF VALUES OF TWO ! CONDITIONAL BRANCH INSTRUCTIONS. ! ! VALUE: ! 2 - WHENEVER F CAUSES A BRANCH, T WILL NOT. ! 1 - WHENEVER F CAUSES A BRANCH, T WILL ALSO. ! 0 - NEITHER OF THE ABOVE. ! Routine BRIMPLY(F : Integer,T : Integer) = Begin ! if 'T' is unconditional then it doesn't matter what 'F' does If .T Eql PBR Or .T Eql PJMP Then Return 1; ! if the same operation then always. If .T Eql .F Then Return 1; ! if opposites then never If .T Eql REVCOND(.F) Then Return 2; ! BEQ implies BLE, BGE, BLOS, BHIS, BPL ! BEQ denies BGT, BLT, BLO, BHI, BMI If .F Eql PBEQ Then If .T Eql PBLE Or .T Eql PBLOS Then Return 1 Else If .swit_final And .T Eql PBPL Then Return 1 Else If .T Eql PBGT Or .T Eql PBHI Then Return 2 Else If .swit_final And .T Eql PBMI Then Return 2 Else Return 0; ! BLO implies BLOS ! BLO denies BHI If .F Eql PBLO Then If .T Eql PBLOS Then Return 1 Else If .T Eql PBHI Then Return 2 Else Return 0; ! BHI,BGT,BMI implies BNE ! BHI,BGT,BMI denies BEQ If .F Eql PBHI Or .F Eql PBGT Or (.swit_final And .F Eql PBMI) Then If .T Eql PBNE Then Return 1 Else If .T Eql PBEQ Then Return 2 Else Return 0; ! no other implications Return 0 End; ! returns the length of an instruction in words ! ! notes: ! the PDP-11 is quite simple. one word for the instruction opcode ! and one word for each extra word needed for each operand. Routine CELLLENGTH(CURS : Ref CELL) = Begin Case .OPERTYPE[.CURS[cel_code]] From LO_OPTYPE To HI_OPTYPE Of Set ! no-ops are normally 1 single word except for inline which we give a wild guess to. ! the guess is actually the distance a branch instruction may span. since we ! do not know how much code an in-line will generate, we give it a size that ! will guarantee that no short branches will pass over it. [ OPTYPE_NOP ]: If .CURS[cel_code] Eql PINLINE Then Return If .CURS[cel_inl_comment] Then 0 Else %o'201' Else Return 1; ! single operand [ OPTYPE_ONE ]: Return 1 + ADRLEN(CURS[cel_src]); ! double operand [ OPTYPE_TWO ]: Return 1 + ADRLEN(CURS[cel_src]) + ADRLEN(CURS[cel_dst]); [ OPTYPE_BR ]: ! short branch If LABREFP(.CURS) And .CURS[cel_code] Neq PJMP Then Return 1 ! unconditional branch Else If .CURS[cel_code] Eql PBR Or .CURS[cel_code] Eql PJMP Then Return 1 + ADRLEN(CURS[cel_src]) ! reversed branch and a jump Else Return 2 + ADRLEN(CURS[cel_src]); [ OPTYPE_JSR ]: Return 1 + ADRLEN(CURS[cel_dst]); [ OPTYPE_RTS,OPTYPE_TRAP,OPTYPE_WORD,OPTYPE_CASE ]: Return 1 Tes End; ! delete instruction up to the next label ! ! notes: ! normally used after an unconditional jump Routine CLEARTOLAB(IND : Ref CELL) = Begin Local NEXT : Ref CELL; NEXT = NXTLCC(.IND); Until .NEXT[cel_type] Eql CELL_LABEL Or .NEXT Eql .BRAK2 Do Begin DELETE(.NEXT); NEXT = NXTLCC(.IND) End; Return .NEXT End; ! combine multiple labels into a single label Routine COMBLAB(LAB : Ref CELL) = Begin Local CURS : Ref CELL, LABCURS : Ref CELL; ! find the start of a sequence of labels While .Block[(LABCURS = PRVLCC(.LAB)),cel_type] Eql CELL_LABEL Do LAB = .LABCURS; ! loop for each label in the sequence, moving all references to the ! first one While .Block[(LABCURS = NXTLCC(.LAB)),cel_type] Eql CELL_LABEL Do Begin ! we found two consecutive labels SETCHANGE; ! loop, moving all references CURS = .LABCURS[cel_top]; Until .CURS Eql .LABCURS Do CURS = MVLABREF(.CURS,.LAB); ! if we just merged in a user label or a procedure entry label then ! make the first label that user/procedure entry label If .LABCURS[cel_lab_type] Eql LAB_USER Or .LABCURS[cel_lab_type] Eql LAB_ROUTINE Then Begin LAB[cel_lab_type] = .LABCURS[cel_lab_type]; LAB[cel_lab_name] = .LABCURS[cel_lab_name] End; ! delete the label just merged in ERASE(.LABCURS) End; Return .LAB End; ! ! FUNCTION: ! THIS ROUTINE RETURNS THE ULTIMATE DESTINATION OF ! THE BRANCHING CELL "CONDJ". "LAB" IS THE LABEL THAT IT ! REFERENCES (NOT THE SAME ON TWO DIFFERENT RECURSIONS). ! Routine CONDJMPSRC(LAB : Ref CELL,CONDJ : Ref CELL) = Begin Local NEXT : Ref CELL; ! this avoids endless recursion If .LAB[cel_lab_seen] Then Return .LAB; ! next was a label cell. we need the code cell after it. NEXT = NXTCC(.LAB); If .NEXT[cel_code] Eql INFLOOPOP Then Return .LAB; ! only consider branches to jumps of some sort If .NEXT[cel_class] Neq INST_COND_JUMP And .NEXT[cel_class] Neq INST_UNCOND_JUMP Then Return .LAB; ! return if not a branch to a label (e.g. a branch outside the routine). If Not LABREFP(.NEXT) Then Return .LAB; Case BRIMPLY(.CONDJ[cel_code],.NEXT[cel_code]) From 0 To 2 Of Set ! if the target is another branch with no implications [0]: Return .LAB; ! if the target is another branch which will always branch on the ! condition [1]: Begin LAB[cel_lab_seen] = TRUE; NEXT = CONDJMPSRC(LABREFED(.NEXT),.CONDJ); LAB[cel_lab_seen] = FALSE; Return .NEXT End; ! if the target is another branch which never branches on the ! given condition [2]: Return .NEXT[cel_next] Tes End; ! delete an addressing descriptor ! ! notes: ! not as easy as it would seem. if the mode uses auto-increment/decrement ! then we must simulate it by inserting an ADD/SUB instruction. ! ! Q: the insertion of an ADD will cause the C-bit to be modified. what ! if there are instructions down-stream depending on the C-bit? Routine DELADR(ADR : Ref ADRVARSTR,IND : Ref CELL) = Begin Local PCELL : Ref CELL; ! if there is a register adjustment (e.g. from AUTO-INCREMENT) then ! we need to insert an ADD/SUB to adjust the register If .ADR[adr_delta] Neq 0 Then Begin PCELL = NEWCODECELL(); PCELL[cel_code] = (If .ADR[adr_delta] Lss 0 Then PSUB Else PADD); PCELL[cel_class] = INST_ADD_IMMED; COPYADR(PCELL[cel_src],IMMEDZERO); COPYADR(PCELL[cel_dst],REGADR); PCELL[cel_src_disp] = Abs(.ADR[adr_delta]); PCELL[cel_dst_reg] = .ADR[adr_reg]; PCELL[cel_min_loc] = .IND[cel_min_loc]; IND = BEFORE(.IND,.PCELL) End ! if a label reference then delete the reference cell Else If .ADR[adr_name_type] Eql NAME_LABEL Then ERASE(.ADR[adr_name]); Return .IND End; ! delete an instruction Routine DELETE(IND : Ref CELL) = Begin Local T : Ref CELL; ! note that a change occured SETCHANGE; ! point to the previous instruction. 'IND[cel_next]' is not correct ! because DELADR may insert register adjustments and we want to ! point to those adjustments on return. T = .IND[cel_prev]; ! delete the operands If HASSOURCE(.IND[cel_code]) Then DELADR(IND[cel_src],.IND); If HASDEST(.IND[cel_code]) Then DELADR(IND[cel_dst],.IND); ! now erase the instruction ERASE(.IND); Return .T[cel_next] End; ! ! SCAN BACKWARDS FROM IND, DELETING CELLS WHOSE SOLE PURPOSE ! IS TO CHANGE THE CONTENTS OF ADR. ! ! called from OPT_CLR and OPT_MOV ! ! notes: ! this gets rid of dubious code such as: ! ! MOV #5,R0 ! MOV #6,R0 ! ! in which the effect of the first move is lost. Routine DELBACKREF(IND : Ref CELL,ADR : Ref ADRVARSTR,BYTES : Integer) : Novalue = Begin Local OP : Integer, CURS : Ref CELL, AREF : ADRVARSTR; Label aaa; ! outer loop. each time an instruction is deleted the outer loop is ! re-executed. it simply re-initializes and restarts the backward scan. While TRUE Do Begin CURS = .IND; COPYADR(AREF,.ADR); AREF[adr_disp] = .AREF[adr_disp] + .ADR[adr_delta]; ! backward scan loop While TRUE Do aaa: Begin ! going backward, examine the next cell CURS = .CURS[cel_prev]; ! only consider code cells. we skip past label cells and only ! consider code which falls through to labels. maybe this should ! check other unconditional paths to this label. If .CURS[cel_type] Neq CELL_CODE Then Leave aaa; ! jumps, calls, branches, MxPx, and inlines stop a backward scan OP = .CURS[cel_code]; If .STOPBSCAN[.OP] Then Return; Selectone .OPERTYPE[.OP] Of Set [OPTYPE_ONE]: Begin ! check the same address. have to be careful that the previous ! instruction modified the same size or less. consider: ! ! MOV #1234,R0 ! CLRB R0 ! ! the previous move may not be deleted. ! ! note: 'CLR' has an opertype of OPTYPE_ONE but is not a problem. ! it would have been a problem if there had been an intervening ! condition branch but that was handled by the STOPBSCAN check ! above. If IS_SAME_ADR(AREF,CURS[cel_src]) And .BYTES Geq .OPBYTES[.OP] Then Begin DELETE(.CURS); Exitloop End; ! if there is a potential for using the same address and we can't ! tell for certain then we are all done. If MAY_USE_ADDR(CURS[cel_src],AREF) Then Return; If MAY_USE_ADDR(AREF,CURS[cel_src]) Then Return; ! if close enough for an overlap then return If .BYTES Lss .OPBYTES[.OP] And MAY_REFER(CURS[cel_src],AREF) Then Return; ! account for any auto-increment/decrement DOWNDATE(CURS[cel_src],AREF) End; [OPTYPE_TWO]: Begin ! if defintely the same location for the destination then delete it and ! loop again. If IS_SAME_ADR(AREF,CURS[cel_dst]) And .BYTES Geq .OPBYTES[.OP] Then Begin DELETE(.CURS); Exitloop End; ! if there is a potential for the same address then take no chances ! and return. If MAY_USE_ADDR(CURS[cel_dst],AREF) Then Return; If MAY_USE_ADDR(AREF,CURS[cel_dst]) Then Return; ! if no direct potential but a chance for overlap then return. If .BYTES Lss .OPBYTES[.OP] And MAY_REFER(CURS[cel_dst],AREF) Then Return; ! if the source depends on this location then return If MAY_REFER(CURS[cel_src],AREF) Then Return; If MAY_USE_ADDR(CURS[cel_src],AREF) Then Return ! adjust for auto-increments/decrements DOWNDATE(CURS[cel_dst],AREF); DOWNDATE(CURS[cel_src],AREF) End Tes End End End; ! ! CALLED FROM DUPADR, OPT_ADDSUB. ! EITHER DELETE A CODE CELL, OR CHANGE IT ! TO A TST, OR DO NOTHING, DEPENDING ! ON THE ENSUING CONDITION CODE TESTING. ! ! notes: ! DUPADR calls this when it has found: ! ! MOV X,X ! BIT X,X ! -or- BIS X,X ! ! OPT_ADDSUB calls this when it has found ! ! ADD #0,X ! ! these instructions may be deleted if no subsequent instructions ! depend on the condition codes. if there is a dependency and ! that dependency does no depend on the C-bit then the instruction ! is changed to: ! ! TST X ! ! if there is a dependency on the C-bit then we cannot change the ! instruction because 'TST' clears the C-bit and the C-bit may have ! been passing though this instruction. Routine DELTST(CURS : Ref CELL) = Begin Local NZV : Boolean, C : Boolean, A : Boolean; ! get the dependencies on the conditions codes NZV = TESTCC(.CURS,___VZN); C = TESTCC(.CURS,__C___); A = (.CURS[cel_class] Eql INST_ADD_IMMED); ! if there is a dependency on N, Z, or V but not on C... ! ! Q: what does it matter if CURS is an ADD immediate? ! ! A: this is how it apparently distinguishes between an 'ADD #0,X' ! from the rest. an 'ADD #0,X' would set the C-bit to zero ! the same as a TST would. If .NZV And (Not .C Or .A) Then Begin SETCHANGE; CURS[cel_code] = (If .OPBYTES[.CURS[cel_code]] Eql 1 Then PTSTB Else PTST); ! change from two operand to one operand. REDUCEADR(.CURS); Return .CURS End; ! if no dependencies downstream then delete the instruction If Not .NZV And Not (.C And .A) Then Return DELETE(.CURS); ! something relies upon the C-bit later on. Return .CURS[cel_next] End; ! ! THS ROUTINE PERFORMS WHATEVER ACTION IS NECESSARY ! WHEN IT IS DISCOVERED THAT THE SOURCE AND ! DESTINATION OPERANDS OF CURS ARE IDENTICAL. ! Routine DUPADR(CURS : Ref CELL) = Begin Local OP : Integer, BYTES : Integer; Own DUPTYPE : Vector[13,Byte] Preset( [0] = 0, [PMOV ] = 0, [PMOVB] = 0, [PCMP ] = 1, [PCMPB] = 1, [PBIT ] = 0, [PBITB] = 0, [PBIC ] = 2, [PBICB] = 2, [PBIS ] = 0, [PBISB] = 0, [PADD ] = 3, [PSUB ] = 2 ); ! special case of: ! ! MOVB R,R ! ! is really a sign extension operation OP = .CURS[cel_code]; If .OP Eql PMOVB And .CURS[cel_dst_type] Eql ADDR_REG Then Return NXTLCC(.CURS); BYTES = .OPBYTES[.OP]; Case .DUPTYPE[.OP] From 0 To 3 Of Set [0]: ! MOV X,X => -nop- CURS = DELTST(.CURS); ! MOV, BIT, BIS [1]: If TESTEQL(.CURS) Then ! CMP Begin SETCHANGE; DELADR(CURS[cel_src],.CURS); DELADR(CURS[cel_dst],.CURS); CURS[cel_code] = SETEQLOP End Else CURS = DELETE(.CURS); [2]: ! SUB X,X => CLR X ! BIC X,X => CLR X Begin ! BIC, SUB SETCHANGE; CURS[cel_code] = (If .BYTES Eql 1 Then PCLRB Else PCLR); REDUCEADR(.CURS) End; [3]: ! ADD X,X => ASL X Begin ! ADD SETCHANGE; CURS[cel_code] = PASL; REDUCEADR(.CURS) End Tes; Return .CURS End; ! ! IF ADDRESS C IN INSTRUCTION I IS EQUIVALENT TO THE LESS SIMPLE OF ! A AND B, REPLACE IT BY THE MORE SIMPLE OF THE TWO. ! ! called by EQUIVFWD ! ! returns TRUE if the address could be replaced, else FALSE. Routine EQUIVADR(A : Ref ADRVARSTR,B : Ref ADRVARSTR,C : Ref ADRVARSTR,I : Ref CELL) = Begin Local ASIMP : Integer, BSIMP : Integer; ! cannot replace auto-increment/decrement If .C[adr_delta] Neq 0 Then Return FALSE; ! classify A and B ASIMP = ADRSIMP(.A); BSIMP = ADRSIMP(.B); ! if either is impossible or there is no difference then return If .ASIMP Eql 0 Or .BSIMP Eql 0 Or .ASIMP Eql .BSIMP Then Return FALSE; ! set A to the simplier If .ASIMP Gtr .BSIMP Then SWAP(A,B); ! if C is not the same as the more complex then return If Not IS_SAME_ADR(.B,.C) Then Return FALSE; ! remove the old, more complex mode and replace it with the simplier one. DELADR(.C,.I); MAKEADR(.C,.A,.I); Return TRUE End; ! ! SCAN FORWARD FROM IND, LOOKING FOR INSTANCES OF ! THE MORE COSTLY ONE OF IA AND IB, AND REPLACING ! THEM WITH THE CHEAPER ONE. ! ! called by OPT_CLR and OPT_MOV ! ! notes: ! this is called by OPT_CLR, made to look like a 'MOV #0,X'. ! ! for an instruction: ! ! MOV X,Y ! ! it is noted that 'X' and 'Y' are inter-changable in all ! references down-stream. this routine attempts to inter-change ! them down-stream where changing it would produce a simplier ! addressing mode. Routine EQUIVFWD(IND : Ref CELL,IA : Ref ADRVARSTR,IB : Ref ADRVARSTR,BYTES) : Novalue = Begin Local CURS : Ref CELL, OP : Integer, A : ADRVARSTR, B : ADRVARSTR, T : Integer; Label aaa; ! outer loop. similar to the one in DELBACKREF. While TRUE Do Begin CURS = .IND; ! copy the addresses COPYADR(A,.IA); COPYADR(B,.IB); ! adjust the second operand in case the first one was an ! auto-increment. e.g.: ! ! MOV (R0)+,(R0) UPDATE(B,A); ! forward scan loop, finding and replacing equivalent addresses While TRUE Do aaa: Begin ! get the next scan and stop if we come across a non-cell CURS = .CURS[cel_next]; If .CURS[cel_type] Neq CELL_CODE Then Return; ! get the opcode and return if we hit a jump, call, MxPx instruction, ! or an in-line instruction. OP = .CURS[cel_code]; If .STOPFSCAN[.OP] Then Return; Selectone .OPERTYPE[.OP] Of Set [OPTYPE_ONE]: Begin ! adjust A and B, accounting for any auto-increments UPDATE(CURS[cel_src],A); UPDATE(CURS[cel_src],B); If .BYTES Eql 2 Then EQUIVIND(A,B,CURS[cel_src],.CURS); ! if this instruction alters something then return if it might ! alter the value of either A or B. If .ALTERSRCDST[.OP] Then Begin If MAY_REFER(CURS[cel_src],A) Then Return; If MAY_REFER(CURS[cel_src],B) Then Return; If MAY_USE_ADDR(A,CURS[cel_src]) Then Return; If MAY_USE_ADDR(B,CURS[cel_src]) Then Return End ! if this instruction does not alter anything then try to replace it ! with either A or B. ! ! Q: what if the instruction does alter something? cannot we still ! do an EQUIVADR on it? ! ! A: no. what is equivalent is not the address but the contents at the ! address. Else If .BYTES Geq .OPBYTES[.OP] Then EQUIVADR(A,B,CURS[cel_src],.CURS); If MAYPOP(CURS[cel_src],A) Or MAYPOP(CURS[cel_src],B) Then Return End; [OPTYPE_TWO]: Begin ! if taking the address of a local variable has been noted and this instruction is: ! ! ADD #n,SP ! -or- SUB #n,SP ! ! Q: what other immediate operations on SP can there be? ! ! Q: maybe the user manipulating the SP directly using the builtin SP ! register variable. If .flg_stack_addr And .CURS[cel_dst_type] Eql ADDR_REG And .CURS[cel_dst_reg] Eql SP And .CURS[cel_src_type] Eql ADDR_IMMED Then Begin If .OP Eql PADD Then T = .CURS[cel_src_disp] Else If .OP Eql PSUB Then T = -.CURS[cel_src_disp] Else Leave aaa; ! Q: what does it mean if 'T' is zero or negative? ! ! A: if zero, the ADD #0,SP will be optimized away. if negative then ! it is an allocation of local variables. If .T Gtr 0 Then Begin ! return if any kind of indirection is being used If .A[adr_type] Eql ADDR_INDIRECT Or .B[adr_type] Eql ADDR_INDIRECT Then Return; ! if a reference to either the stack segment or a calculated segment then ! return if ! ! a. a register other than SP is used in indexing ! ! b. a reference to memory which will be below the stack pointer ! once the instruction executes. (highly volatile memory - cannot ! trust it). If .A[adr_type] Eql ADDR_MEMORY And .A[adr_name] Leq 1 Then Begin If .A[adr_reg] Lss SP Then Return; If .A[adr_reg] Eql SP And .A[adr_disp] Lss .T Then Return End; ! same for 'B' If .B[adr_type] Eql ADDR_MEMORY And .B[adr_name] Leq 1 Then Begin If .B[adr_reg] Lss SP Then Return; If .B[adr_reg] Eql SP And .A[adr_disp] Lss .T Then Return End End End; ! done with special cases of stack adjustments. ! update A and B with any auto-increment offsets in the source. UPDATE(CURS[cel_src],A); UPDATE(CURS[cel_src],B); ! if this instruction uses the same or less amount of the operand then ! try to replace equivalent addresses. if that fails and this is a word ! reference (i.e. the size of an address) then try to replace equivalent ! indirect addresses. ! If .BYTES Geq .OPBYTES[.OP] Then If Not EQUIVADR(A,B,CURS[cel_src],.CURS) Then If .BYTES Eql 2 Then EQUIVIND(A,B,CURS[cel_src],.CURS); ! note whether A or B may match the source operand T = (IS_SAME_ADR(A,CURS[cel_src]) Or IS_SAME_ADR(B,CURS[cel_src])) And .BYTES Geq .OPBYTES[.OP]; ! now adjust A and B accounting for any auto-increment/decrement in the ! destination UPDATE(CURS[cel_dst],A); UPDATE(CURS[cel_dst],B); ! if A or B matched the source operand and now either also matches the destination ! operand then handle the case of a binary operator with both operands the same. ! ! Q: why return here and not loop, looking for further equivalences? ! ! A: some operations destroy the contents which is equivalent (e.g. ADD, SUB) ! and so would stop the scan anyway. DUPADR will do a SETCHANGE so we are ! pretty sure of coming back anyhow. If .T And (IS_SAME_ADR(A,CURS[cel_dst]) Or IS_SAME_ADR(B,CURS[cel_dst])) Then Begin DUPADR(.CURS); Return End; ! if the size of an address, try for equivalent indirection If .BYTES Eql 2 Then EQUIVIND(A,B,CURS[cel_dst],.CURS); ! if the destination operand is modified and it may alter either A or B ! then return. If .ALTERSRCDST[.CURS[cel_code]] Then Begin If MAY_REFER(CURS[cel_dst],A) Then Return; If MAY_REFER(CURS[cel_dst],B) Then Return; If MAY_USE_ADDR(A,CURS[cel_dst]) Then Return; If MAY_USE_ADDR(B,CURS[cel_dst]) Then Return End ! operand is read only. see if its value may be equivalenced. Else If .BYTES Geq .OPBYTES[.OP] Then EQUIVADR(A,B,CURS[cel_dst],.CURS); If MAYPOP(CURS[cel_src],A) Then Return; If MAYPOP(CURS[cel_src],B) Then Return; If MAYPOP(CURS[cel_dst],A) Then Return; If MAYPOP(CURS[cel_dst],B) Then Return End Tes End End End; ! ! LIKE EQUIVADR, BUT C IS ONE LEVEL OF INDIRECTION ABOVE A AND B. ! ! called by EQUIVFWD Routine EQUIVIND(A : Ref ADRVARSTR,B : Ref ADRVARSTR,C : Ref ADRVARSTR,I : Ref CELL) = Begin Bind NEWFORM = Uplit Byte ( GENREG+DEFERRED, ! R => @R INDEXED+DEFERRED, ! @R => @0(R) INDEXED+DEFERRED, ! (R)+ => @0(R) 0, ! @(R)+ INDEXED+DEFERRED, ! -(R) => @-2(R) 0, ! @-(R) INDEXED+DEFERRED, ! n(R) => @n(R) 0, ! @n(R) 0, ! PC 0, ! @PC AUTOINCR+DEFERRED, ! (PC)+ => @(PC)+ INDEXED+DEFERRED, ! @(PC)+ => @n(PC) 0, ! -(PC) 0, ! @-(PC) INDEXED+DEFERRED, ! n(PC) => @n(PC) 0 ! @n(PC) ) : Vector[,Byte]; Local ASIMP : Integer, BSIMP : Integer; ! do not consider auto-increment/decrement modes, register, or immediate modes. If .C[adr_delta] Neq 0 Or Not ONEOF(.C[adr_type],ADDR_MEMORY,ADDR_INDIRECT) Then Return FALSE; ! get the addressing mode complexities and return if either is bogus or ! there is no gain by substituting one for another. ASIMP = ADRSIMP(.A); BSIMP = ADRSIMP(.B); If .ASIMP Eql 0 Or .BSIMP Eql 0 Or .ASIMP Eql .BSIMP Then Return FALSE; ! set A to the simplier mode If .ASIMP Gtr .BSIMP Then SWAP(A,B); ! substituting one indirection for another is not what we wanted. If .A[adr_type] Eql ADDR_INDIRECT Then Return FALSE; ! return the not the same as the more complex mode If Not IS_SAME_IND(.B,.C) Then Return FALSE; ! normally, we change '@#n' to '@n' but in PIC mode the code segment is allowed to ! float and the data segment is fixed. thus data must be referenced using absolute ! modes and not relative. If .swit_pic And .A[adr_reg] Eql PC And .A[adr_mode] Eql AUTOINCR+DEFERRED Then Return FALSE; ! change mode C to A MAKEADR(.C,.A,.I); ! compute the new type and mode C[adr_type] = (If .C[adr_type] Eql ADDR_MEMORY Then ADDR_INDIRECT Else ADDR_MEMORY); C[adr_mode] = .NEWFORM[.C[adr_mode] + (IF .C[adr_reg] Eql PC Then 8 Else 0)]; Return TRUE End; Global Routine FINAL : Novalue = Begin If Not .swit_peep Then Return; SETCHANGE; FINAL1(); FINAL2(); SETCHANGE; FINAL3() End; Routine FINAL1 : Novalue = Begin Local CURS : Ref CELL, COUNT : Integer, AREF : ADRVARSTR; ! in quick mode, we only go through the code once. in non-quick mode ! we keep going until no more changes can be made. COUNT = 2; While (If .swit_quick Then (COUNT = .COUNT-1) Geq 0 Else .CHANGE) Do Begin CHANGE = FALSE; ! loop for each instruction CURS = .BRAK1; Until .CURS Eql .BRAK2 Do Begin ! optimize labels If .CURS[cel_type] Eql CELL_LABEL Then CURS = OPT_LABEL(.CURS) ! skip non-code cell.s ! ! Q: what type could this be? Else If .CURS[cel_type] Neq CELL_CODE Then CURS = NXTLCC(.CURS) ! if a two operand instruction and both operands are the same Else If (If .OPERTYPE[.CURS[cel_code]] Eql OPTYPE_TWO Then Begin COPYADR(AREF,CURS[cel_src]); UPDATE(CURS[cel_dst],AREF); IS_SAME_ADR(CURS[cel_dst],AREF) End Else FALSE) Then CURS = DUPADR(.CURS) Else ! otherwise code cell specific optimizations CURS = Bliss(.OPTROUTINE[.CURS[cel_code]],.CURS) End End End; Routine FINAL2 : Novalue = Begin Label aaa,bbb; Local Q : Ref ADRVARSTR, MINLOC : Integer, CURS : Ref CELL, VAL : Ref CELL, LASTRTS : Ref CELL; LASTRTS = 0; MINLOC = 0; ! loop for each code or label cell CURS = .BRAK1; Until (CURS = NXTLCC(.CURS)) Eql .BRAK2 Do Begin ! assign a location to this cell CURS[cel_min_loc] = .MINLOC; If .CURS[cel_type] Eql CELL_CODE Then Begin ! change infinite loop to a jump to itself. If .CURS[cel_code] Eql INFLOOPOP Then COMBLAB(MAKEJMP(.CURS,.CURS)) ! SETEQLOP is a marker of a previous 'CMP X,X'. it has not been ! optimized away so change it to 'CMP PC,PC' Else If .CURS[cel_code] Eql SETEQLOP Then Begin CURS[cel_code] = PCMP; COPYADR(CURS[cel_src],PCADR); COPYADR(CURS[cel_dst],PCADR) End ! change: ! ADD #1,X => INC X ! SUB #1,X => DEC X ! ! unless the C-bit is needed later on. Else If .CURS[cel_class] Eql INST_ADD_IMMED Then Begin CURS[cel_class] = UNUSED; VAL = (If .CURS[cel_code] Eql PSUB Then -.CURS[cel_src_disp] Else .CURS[cel_src_disp]); If .CURS[cel_src_name] Eql 0 And Abs(.VAL) Eql 1 And Not TESTCC(.CURS,__C___) Then Begin CURS[cel_code] = (If .VAL Gtr 0 Then PINC Else PDEC); REDUCEADR(.CURS) End ! change: ! ADD #4,SP => CMP (SP)+,(SP)+ ! SUB #4,SP => CMP -(SP),-(SP) Else If .CURS[cel_src_name] Eql 0 And .CURS[cel_dst_type] Eql ADDR_REG And .CURS[cel_dst_reg] Eql SP And Not TESTCC(.CURS,__CVZN) Then If Abs(.VAL) Eql 4 Then Begin CURS[cel_code] = PCMP; If .VAL Gtr 0 Then Begin CURS[cel_dst_delta] = 2; CURS[cel_dst_disp] = -2; CURS[cel_dst_mode] = AUTOINCR End Else Begin CURS[cel_dst_delta] = -2; CURS[cel_dst_mode] = AUTODECR End; CURS[cel_dst_type] = ADDR_MEMORY; COPYADR(CURS[cel_src],CURS[cel_dst]) End ! change: ! ADD #2,SP => TST (SP)+ ! SUB #2,SP => TST -(SP) Else If Abs(.VAL) Eql 2 Then Begin CURS[cel_code] = PTST; If .VAL Gtr 0 Then Begin CURS[cel_dst_delta] = 2; CURS[cel_dst_disp] = -2; CURS[cel_dst_mode] = AUTOINCR End Else Begin CURS[cel_dst_delta] = -2; CURS[cel_dst_mode] = AUTODECR End; CURS[cel_dst_type] = ADDR_MEMORY; COPYADR(CURS[cel_src],CURS[cel_dst]) End End Else If .CURS[cel_class] Eql INST_UNCOND_JUMP Then Begin ! in quick mode we apparently didn't do this when optimizing the JUMP ! instruction. If .swit_quick Then CLEARTOLAB(.CURS); ! CLEAN UP AFTER CROSS-JUMPING ! change jumps to RTS, RTI, HALT, or implied halts (falling off the end is an ! implied halt) then duplicate that instruction here, getting rid of the jump. ! ! note: we already know 'CURS' is an unconditional jump so this test ! could have been simply 'If .CURS[cel_src_name_type] Eql NAME_LABEL' If LABREFP(.CURS) Then Begin VAL = NXTCC(LABREFED(.CURS)); If .VAL[cel_code] Eql PRTS Or .VAL[cel_code] Eql PRTI Or (.VAL[cel_code] Eql PHALT And .VAL[cel_next] Eql .BRAK2) Then Begin CURS[cel_code] = .VAL[cel_code]; CURS[cel_class] = INST_NORMAL; COPYADR(CURS[cel_src],VAL[cel_src]); If .VAL[cel_next] Eql .BRAK2 Then LASTRTS = .CURS End End End; ! look for OP @reg,@0(reg) (or the like) and change to OP (reg)+,@-(reg) If .OPERTYPE[.CURS[cel_code]] Eql OPTYPE_TWO Then Begin aaa: Begin If .CURS[cel_src_reg] Neq .CURS[cel_dst_reg] Then Leave aaa; If .CURS[cel_src_reg] Eql PC Then Leave aaa; If .CURS[cel_src_reg] Neq SP And .OPBYTES[.CURS[cel_code]] Neq 2 Then Leave aaa; If .CURS[cel_src_disp] Neq 0 Or .CURS[cel_dst_disp] Neq 0 Then Leave aaa; If .CURS[cel_src_name] Gtr 1 Or .CURS[cel_dst_name] Gtr 1 Then Leave aaa; Q = CURS[cel_src]; If .Q[adr_type] Neq ADDR_INDIRECT And .Q[adr_type] Neq ADDR_MEMORY Then Leave aaa; Q = CURS[cel_dst]; If .Q[adr_type] Neq ADDR_INDIRECT And .Q[adr_type] Neq ADDR_MEMORY Then Leave aaa; If .CURS[cel_src_delta] Neq 0 Or .CURS[cel_dst_delta] Neq 0 Then Leave aaa; CURS[cel_src_delta] = 2; CURS[cel_src_disp] = -2; CURS[cel_src_mode] = (If .CURS[cel_src_type] Eql ADDR_MEMORY Then AUTOINCR Else AUTOINCR+DEFERRED); CURS[cel_dst_delta] = -2; CURS[cel_dst_mode] = (If .CURS[cel_dst_type] Eql ADDR_MEMORY Then AUTODECR Else AUTODECR+DEFERRED) End; ! look for OP #N,N(reg) and change #n to @PC. (this seems like a real reach). ADRLEN(CURS[cel_dst]); bbb: Begin If .CURS[cel_src_type] Neq ADDR_IMMED Then Leave bbb; If (.CURS[cel_dst_mode] And 6) Neq INDEXED Then If .CURS[cel_dst_reg] Neq PC Or .CURS[cel_dst_mode] Neq AUTOINCR+DEFERRED Then Leave bbb; If .CURS[cel_src_disp] Neq .CURS[cel_dst_disp] Then Leave bbb; If .CURS[cel_dst_name] Eql .CURS[cel_src_name] Or (.CURS[cel_src_name] Eql 0 And .CURS[cel_dst_name] Eql 1) Then CURS[cel_src_mode] = GENREG+DEFERRED End End; ! set the instruction length and update the location counter CURS[cel_min_len] = CELLLENGTH(.CURS); MINLOC = .MINLOC + .CURS[cel_min_len] End End; ! if the last instruction was an RTS (or an RTI or a HALT) and the instruction ! before that was a JMP to that RTS then chuck the JMP. CURS = .BRAK2[cel_prev]; If .LASTRTS Neq 0 Then Begin VAL = PRVCC(.CURS); If .VAL[cel_class] Eql INST_UNCOND_JUMP Then Begin VAL = BEFORE(.LASTRTS,DETACH(.CURS[cel_prev])); VAL[cel_min_loc] = .LASTRTS[cel_min_loc]; ERASE(.CURS) End End End; Routine FINAL3 : Novalue = Begin Local CURS : Ref CELL, VAL : Ref CELL, NEXT : Ref CELL, LAB : Ref CELL, MINLOC : Integer; Label aaa; ! loop until no more optimizations may be made While .CHANGE Do Begin CHANGE = FALSE; MINLOC = 0; ! loop for each code/label cell CURS = .BRAK1; Until (CURS = NXTLCC(.CURS)) Eql .BRAK2 Do aaa: Begin ! assign an address to the cell CURS[cel_min_loc] = .MINLOC; ! only consider code cells here If .CURS[cel_type] Eql CELL_LABEL Then Leave aaa; ! change short branches and unconditional branches to a reversed condition ! branches followed by a long jump. If .CURS[cel_class] Eql INST_COND_JUMP Or .CURS[cel_class] Eql INST_UNCOND_JUMP Then If LABREFP(.CURS) Then If REACH(.CURS) Eql 0 Then !JMP NECESSARY Begin SETCHANGE; NEXT = NXTCC(LABREFED(.CURS)); If .CURS[cel_class] Eql INST_COND_JUMP Then Begin VAL = NEWCODECELL(); BEFORE(.CURS,.VAL); COMBLAB(MAKEJMP(.VAL,NXTCC(.CURS))); VAL[cel_code] = REVCOND(.CURS[cel_code]); VAL[cel_min_len] = 1; VAL[cel_min_loc] = .MINLOC; MINLOC = .MINLOC + 1; VAL[cel_class] = INST_NORMAL; VAL = .CURS[cel_src_name]; VAL[cel_class] = INST_UNCOND_JUMP End; If .NEXT[cel_code] Eql PJMP Then COPYADR(CURS[cel_src],NEXT[cel_src]); CURS[cel_class] = INST_NORMAL; CURS[cel_code] = PJMP; CURS[cel_min_len] = 2; CURS[cel_min_loc] = .MINLOC End; ! update our program counter MINLOC = .MINLOC + .CURS[cel_min_len] End !SCAN LOOP End; !CHANGE LOOP ! save the routine size for the LISTC module FINRTNSIZE = .MINLOC; CODESIZE = .CODESIZE + .MINLOC; ! return if the size of the code is less than the span of a branch If .MINLOC Leq 127 Then Return; ! loop over all instuctions, trying to change tunnel branches to direct branches. CURS = .BRAK1; Until (CURS = NXTCC(.CURS)) Eql .BRAK2 Do If .CURS[cel_class] Eql INST_COND_JUMP Or .CURS[cel_class] Eql INST_UNCOND_JUMP Then If LABREFP(.CURS) Then Begin LAB = LABREFED(.CURS); VAL = REACH(.CURS); If .LAB Neq .VAL Then Begin LAB = COMBLAB(BEFORE(.VAL,NEWLAB())); LAB[cel_min_loc] = .VAL[cel_min_loc]; MAKELABREF(.CURS,.LAB); End End End; ! called by OPT_CASE and OPT_UNCOND_JUMP ! ! returns TRUE if 'IND' is an infinite loop Routine FIXJMPIND(IND : Ref CELL) = Begin Local NEXT : Ref CELL, LAB : Ref CELL, IL : Boolean; ! if already discovered to be an infinite loop If .IND[cel_code] Eql INFLOOPOP Then Return TRUE; ! if a branch to an external label If Not LABREFP(.IND) Then Return FALSE; ! get the label referenced. if already seen then we have an ! infinite loop. LAB = LABREFED(.IND); If .LAB[cel_lab_seen] Then Begin MAKEINFLOOP(.IND); Return TRUE End; ! see if the next instruction is another jump NEXT = NXTCC(.LAB); If .NEXT[cel_class] Neq INST_UNCOND_JUMP Then Return FALSE; ! note that our label has been seen and traverse this one LAB[cel_lab_seen] = TRUE; IL = FIXJMPIND(.NEXT); LAB[cel_lab_seen] = FALSE; ! branch to branch stuff If Not .IL Then Begin ! short branches (i.e. adrlen = 0) and case branches may not ! branch external. If Not LABREFP(.NEXT) Then If .IND[cel_code] Eql PCASE Or ADRLEN(NEXT[cel_src]) Neq 0 Then Return FALSE; DELADR(IND[cel_src],.IND); MAKEADR(IND[cel_src],NEXT[cel_src],.IND); SETCHANGE; Return FALSE End; ! branches to infinite loops become infinite loops. If .IND[cel_class] Eql INST_UNCOND_JUMP Then Begin MAKEINFLOOP(.IND); Return TRUE End; ! Q: why return FALSE? we know this leads to an infinite loop ! ! A: this must be a case parameter and case parameters may not ! change (would foul up the order of the case branch table ! something awful). that and OPT_CASE doesn't even loop ! at the result. Return FALSE End; Routine LABREFED(IND : Ref CELL) = Begin Return COMBLAB(.Block[.IND[cel_src_name],cel_ref_ef]) End; ! returns TRUE if 'I' is a branch to a label Routine LABREFP(I : Ref CELL) = Begin If ONEOF(.OPERTYPE[.I[cel_code]],OPTYPE_BR,OPTYPE_CASE) Then Return .I[cel_src_name_type] Eql NAME_LABEL Else Return FALSE End; ! ! REPLACE ADDRESS 'A' IN INSTRUCTION 'IND' BY 'B'. ! Routine MAKEADR(A : Ref ADRVARSTR,B : Ref ADRVARSTR,IND : Ref CELL) : Novalue = Begin Local AREF : Ref CELL; ! fill in the new address COPYADR(.A,.B); ! if changing the source operand of an ADD or SUB instruction to an ! immediate value then adjust the cell class If .IND[cel_code] Eql PADD Or .IND[cel_code] Eql PSUB Then If .A Eql IND[cel_src] And .A[adr_type] Eql ADDR_IMMED Then IND[cel_class] = INST_ADD_IMMED; ! don't allow auto-increment/decrement modes to replace operands. ! this would be because instructions down-stream may depend on the ! value of the register. If .A[adr_delta] Neq 0 Then Begin A[adr_delta] = 0; A[adr_mode] = (If .A[adr_type] Eql ADDR_INDIRECT Then INDEXED+DEFERRED Else If .A[adr_disp] Eql 0 Then GENREG+DEFERRED Else INDEXED) End; ! if a label then create a reference to the label If .A[adr_name_type] Eql NAME_LABEL Then Begin AREF = A[adr_name] = GETCELL(CELL_REF,SZ_CELL_REF); AREF[cel_class] = .IND[cel_class]; AREF[cel_ref_rf] = .IND; PUSHBOT(AREF[cel_ref_ef] = .Block[.B[adr_name],cel_ref_ef],.AREF) End End; ! called by FIXJMPIND ! ! notes: ! 'I' has been discovered to be a jump to itself either ! directly or indirectly. we change it here to a special ! code for that. Routine MAKEINFLOOP(I : Ref CELL) : Novalue = Begin If .I[cel_code] Eql INFLOOPOP Then Return; SETCHANGE; DELADR(I[cel_src],.I); I[cel_code] = INFLOOPOP End; ! convert cell 'F' to a jump to cell 'T' Routine MAKEJMP(F : Ref CELL,T : Ref CELL) = Begin Local LAB : Ref CELL; ! create a label and place it before T and give it the same address LAB = NEWLAB(); BEFORE(.T,.LAB); LAB[cel_min_loc] = .T[cel_min_loc]; ! now change F to be a reference to that new label F[cel_class] = INST_UNCOND_JUMP; F[cel_code] = PBR; MAKELABREF(.F,.LAB); Return .LAB End; ! change the source operand of 'IND' to a reference of label 'LAB' Routine MAKELABREF(IND : Ref CELL,LAB : Ref CELL) = Begin Local AREF : Ref CELL; COPYADR(IND[cel_src],MEMADR); AREF = IND[cel_src_name] = GETCELL(CELL_REF,SZ_CELL_REF); AREF[cel_class] = .IND[cel_class]; AREF[cel_ref_ef] = .LAB; AREF[cel_ref_rf] = .IND; PUSHBOT(.LAB,.AREF) End; ! ! TRUE IF A MAY POP B OFF STACK (thus destroying the contents of B). ! ! called by EQUIVFWD Routine MAYPOP(A : Ref ADRVARSTR,B : Ref ADRVARSTR) = Begin ! is this right? it seems to be saying that if we are in a quick mode ! and A uses auto-increment/decrement then return TRUE if B is a register ! or an immediate value. I somehow think the 'Not' should be reversed. If Not .swit_final And .A[adr_delta] Neq 0 Then Return Not (.B[adr_type] Eql ADDR_INDIRECT Or .B[adr_type] Eql ADDR_MEMORY); ! if no stack addresses were taken then B is not on the stack If Not .flg_stack_addr Then Return FALSE; ! if A is an auto-decrement or doesn't even reference the stack If .A[adr_delta] Leq 0 Or .A[adr_reg] Neq SP Then Return FALSE; ! all bets are off with indirection If .B[adr_type] Eql ADDR_INDIRECT Then Return TRUE; ! stack pops cannot destroy registers or literals If .B[adr_type] Neq ADDR_MEMORY Then Return FALSE; ! cannot pop off named segments or anything addressed using the PC If .B[adr_name] Gtr 1 Or .B[adr_reg] Eql PC Then Return FALSE; ! if a stack reference then it was destroyed if a reference below the stack If .B[adr_reg] Eql SP Then Return .A[adr_disp] Lss 0; ! if using a computed address then we cannot tell and so assume the worst Return TRUE End; ! ! TRUE IF ADDRESS A MAY REFER TO ADDRESS B ! Routine MAY_REFER(A : Ref ADRVARSTR,B : Ref ADRVARSTR) = Begin ! minimize number of cases to check If .A[adr_type] Gtr .B[adr_type] Then SWAP(A,B); Case .A[adr_type] From LO_ADDR_TYPE To HI_ADDR_TYPE Of Set ! immediates never conflict [ADDR_IMMED]: Return FALSE; ! a register may only be affected by another register [ADDR_REG]: If .B[adr_type] Eql ADDR_REG Then Return .A[adr_reg] Eql .B[adr_reg] Else Return FALSE; ! for indirection, 'A' does not refer to 'B' if 'B' is a stack location and ! the address of a stack location has never been taken. ! note that 'B' may be only ADDR_MEMORY or ADDR_INDIRECT due to the ! swap above. [ADDR_INDIRECT]: Begin If Not .swit_final Then Return TRUE; ! if the address of any stack locals has been taken If .flg_stack_addr Then Return TRUE; If .B[adr_type] Neq ADDR_MEMORY Then Return TRUE; If .B[adr_name] Neq 1 Then Return TRUE; Return FALSE End; ! note: 'B' may only be ADDR_MEMORY due to the swap above. [ADDR_MEMORY]: Begin If Not .swit_final Then Return TRUE; ! if the registers differ then they are different addresses if ! different named segments are used. If .A[adr_reg] Neq .B[adr_reg] Then Begin If .A[adr_name] Eql 0 Then Return TRUE; If .B[adr_name] Eql 0 Then Return TRUE; If .A[adr_name] Eql .B[adr_name] Then Return TRUE End Else ! if the registers are the same then they are different if different ! named segments are used or the displacement is large enough. Begin If .A[adr_name] Eql .B[adr_name] And Abs(.A[adr_disp]-.B[adr_disp]) Lss 2 Then Return TRUE End; Return FALSE End Tes End; ! ! TRUE IF ADDRESS 'A' MAY USE ADDRESS 'B'. ! Routine MAY_USE_ADDR(A : Ref ADRVARSTR,B : Ref ADRVARSTR) = Begin Local T : ADRVARSTR; Case .A[adr_type] From LO_ADDR_TYPE To HI_ADDR_TYPE Of Set [ADDR_IMMED]: Return FALSE; [ADDR_REG]: Return FALSE; [ADDR_INDIRECT]: Begin If Not .swit_final Then Return .B[adr_type] Neq ADDR_IMMED; ! if 'B' is a register, 'A' is affected if the register is the ! index register used in 'A' If .B[adr_type] Eql ADDR_REG Then Return .A[adr_reg] Eql .B[adr_reg]; ! see if 'B' may be the same location as the indirect location COPYADR(T,.A); T[adr_type] = ADDR_MEMORY; Return MAY_REFER(T,.B) End; [ADDR_MEMORY]: If Not .swit_final Then Return .B[adr_type] Neq ADDR_IMMED ! if 'B' is a register, 'A' is affected if the register is the ! index register used in 'A' Else If .B[adr_type] Eql ADDR_REG Then Return .A[adr_reg] Eql .B[adr_reg] Else Return FALSE Tes End; Routine MOVEJMP(F : Ref CELL,T : Ref CELL) = Begin Local Q : Ref CELL, LAB : Ref CELL; If .T[cel_type] Neq CELL_LABEL Then Begin Q = .T[cel_prev]; If .Q[cel_type] Eql CELL_LABEL Then T = .Q Else Begin LAB = NEWLAB(); BEFORE(.T,.LAB); LAB[cel_min_loc] = .T[cel_min_loc]; T = .LAB; End End; Return MVLABREF(.F[cel_src_name],.T) End; ! move a reference from one label to another ! ! called by COMPLAB and MOVEJMP ! Routine MVLABREF(IND : Ref CELL,LAB : Ref CELL) = Begin Local VAL : Ref CELL; SETCHANGE; ! remember where the next cell is VAL = .IND[cel_next]; ! change the label referenced in the reference cell IND[cel_ref_ef] = .LAB; ! remove the reference cell from its old label attachment ! list and add it to the attachment list of the new label. PUSHBOT(.LAB,DETACH(.IND)); Return .VAL End; ! optimizer for ADD/SUB instructions Routine OPT_ADDSUB(I : Ref CELL) = Begin Local OP : Integer, PPFLAG : Integer, CURS : Ref CELL, DST1 : ADRVARSTR; ! only optimize instructions of the form 'op #n,X' If .I[cel_class] Neq INST_ADD_IMMED Then Return .I[cel_next]; ! 'ADD #0,' -> NOP If .I[cel_src_name] Eql 0 Then If .I[cel_src_disp] Eql 0 Then Return DELTST(.I) ! change add(sub) #-n,X to sub(add) #n,X Else If .I[cel_src_disp] Lss 0 Then Begin I[cel_code] = (If .I[cel_code] Eql PSUB Then PADD Else PSUB); I[cel_src_disp] = Abs(.I[cel_src_disp]) End; If .swit_quick Then Return .I[cel_next]; ! with indirect, we cannot know what is in the indirect word and ! so cannot optimize it. If .I[cel_dst_type] Eql ADDR_INDIRECT Then Return .I[cel_next]; ! if 'ADD #n,R' If .I[cel_dst_type] Eql ADDR_REG Then Begin ! if a named segment then we cannot change any instructions to ! auto-increment/decrement but we can do other things to them. ! note the displacement otherwise. If .I[cel_src_name] Neq 0 Then PPFLAG = %o'177776' Else If .I[cel_code] Eql PADD Then PPFLAG = .I[cel_src_disp] Else PPFLAG = -.I[cel_src_disp]; PUSHPOPFLAG = .PPFLAG; ! perform a backward scan for other references to this register ! which can be optimized CURS = .I; While TRUE Do Begin ! stop when we come to a label CURS = .CURS[cel_prev]; If .CURS[cel_type] Neq CELL_CODE Then Exitloop; ! stop on flow of control instructions or in-line code OP = .CURS[cel_code]; If .STOPFSCAN[.OP] Or .STOPBSCAN[.OP] Then Exitloop; ! try to change addressing modes if possible Selectone .OPERTYPE[.OP] Of Set [OPTYPE_ONE]: If TESTPOP(CURS[cel_src],.I,.CURS) Then Exitloop; [OPTYPE_TWO]: If TESTPOP(CURS[cel_dst],.I,.CURS) Then Exitloop Else If TESTPOP(CURS[cel_src],.I,.CURS) Then Exitloop Tes End; ! delete the instruction if the optimization got rid of all the ! immediate value If .I[cel_src_disp] Eql 0 And .I[cel_src_name] Eql 0 Then Return DELTST(.I); ! now do a forward scan looking for references to this register ! which may be optimized. first, reset PUSHPOPFLAG but for the ! forward direction. PUSHPOPFLAG = -.PPFLAG; CURS = .I; While TRUE Do Begin CURS = .CURS[cel_next]; ! stop when we come to a label If .CURS[cel_type] Neq CELL_CODE Then Exitloop; ! or when we come to a change of flow instruction OP = .CURS[cel_code]; If .STOPFSCAN[.OP] Or .STOPBSCAN[.OP] Then Exitloop; ! same as above Selectone .OPERTYPE[.OP] Of Set [OPTYPE_ONE]: If TESTPUSH(CURS[cel_src],.I,.CURS) Then Exitloop; [OPTYPE_TWO]: If TESTPUSH(CURS[cel_src],.I,.CURS) Then Exitloop Else If TESTPUSH(CURS[cel_dst],.I,.CURS) Then Exitloop Tes End; ! again, delete this cell if optimization got rid of the immediate ! value (saves another final pass). If .I[cel_src_disp] Eql 0 And .I[cel_src_name] Eql 0 Then Return DELTST(.I) End; CURS = .I; COPYADR(DST1,I[cel_dst]); ! ! SCAN FORWARD FROM I, COMBINING ADDS(SUBS) OF LITERALS TO I'S DST ! WITH I ITSELF. ! ! note: there is no need for a backward scan because we would have ! called OPT_ADDSUB for any behind us and they would have ! scanned forward to us. While TRUE Do Begin CURS = .CURS[cel_next]; ! stop when we come to a label If .CURS[cel_type] Neq CELL_CODE Then Exitloop; ! stop when we come to a flow of control instruction OP = .CURS[cel_code]; If .STOPFSCAN[.OP] Or .STOPBSCAN[.OP] Then Exitloop; Selectone .OPERTYPE[.OP] Of Set [OPTYPE_ONE]: Begin ! adjust for any auto-increment/decrement in this instruction UPDATE(CURS[cel_src],DST1); ! stop if this instruction may modify this register If MAY_USE_ADDR(CURS[cel_src],DST1) Then Exitloop; If MAY_REFER(CURS[cel_src],DST1) Then Exitloop; If MAY_USE_ADDR(DST1,CURS[cel_src]) And .ALTERSRCDST[.OP] Then Exitloop End; [OPTYPE_TWO]: Begin ! adjust for any auto-increment/decrement in the source operand UPDATE(CURS[cel_src],DST1); ! stop if the source uses the register If MAY_USE_ADDR(CURS[cel_src],DST1) Then Exitloop; If MAY_REFER(CURS[cel_src],DST1) Then Exitloop; ! adjust for any auto-increment/decrement in the destination operand UPDATE(CURS[cel_dst],DST1); ! if this is an ADD/SUB #n,X to our register... If .CURS[cel_class] Eql INST_ADD_IMMED And IS_SAME_ADR(DST1,CURS[cel_dst]) Then Begin ! if this cell contains a relocatable name then return if ! we have two relcatable names (linker cannot handle it relocatable ! +/- relocatable). If .I[cel_src_name] Neq 0 Then Begin If .CURS[cel_src_name] Neq 0 Then Exitloop; ! make sure the opcodes are the same because we are going to move ! this symbol to the original ADD/SUB. (linker cannot handler ! 0 - relocatable). If .CURS[cel_code] Neq .I[cel_code] Then Exitloop End; ! compute the new segment and displacement and then ! discard this add CURS[cel_src_name] = .CURS[cel_src_name] + .I[cel_src_name]; CURS[cel_src_disp] = .CURS[cel_src_disp] + (If .I[cel_code] Eql .OP Then .I[cel_src_disp] Else -.I[cel_src_disp]); Return DELETE(.I) End Else ! this is not an add immediate to our register. see if it affects ! or uses our register. Begin If MAY_USE_ADDR(CURS[cel_dst],DST1) Then Exitloop; If MAY_REFER(CURS[cel_dst],DST1) Then Exitloop; If MAY_USE_ADDR(DST1,CURS[cel_dst]) And .ALTERSRCDST[.CURS[cel_code]] Then Exitloop End End Tes End; Return .I[cel_next] End; Routine OPT_BIC(I : Ref CELL) = Begin Return OPT_BOOLEAN(.I,PBIC,PBICB,PBIS,PBISB) End; Routine OPT_BIS(I : Ref CELL) = Begin Return OPT_BOOLEAN(.I,PBIS,PBISB,PBIC,PBICB) End; Routine OPT_BIT(I : Ref CELL) = Begin ! if no one down-stream is interested in the condition codes ! produced then we can chuck this instruction. If TESTCC(.I,___VZN) Then Return .I[cel_next] Else Return DELETE(.I) End; Routine OPT_BOOLEAN(I : Ref CELL,PBOOL,PBOOLB,XBOOL,XBOOLB) = Begin Local FWDF : Boolean, BACKF : Boolean, FWDMASK : Integer, BACKMASK : Integer, CURS : Ref CELL, OP : Integer, ADR : ADRVARSTR; Label aaa; If .swit_quick Then Return .I[cel_next]; ! only optimize: ! BIS #n,X ! -and- BIC #n,X If .I[cel_src_type] Neq ADDR_IMMED Then Return .I[cel_next]; ! only works with pure constants If .I[cel_src_name] Neq 0 Then Return .I[cel_next]; ! only works with register or memory modes If .I[cel_dst_type] Eql ADDR_INDIRECT Then Return .I[cel_next]; ! if this is a byte instruction then only use the low 8 bits of ! the constant. if that constant is now zero then the instruction ! becomes a no-op and may be deleted. ! ! Q: what about instructions later on which depend on condition ! codes being set? If .I[cel_code] Eql .PBOOLB Then I[cel_src_disp] = .I[cel_src_disp] And 255; If .I[cel_src_disp] Eql 0 Then Return DELETE(.I); ! ! SCAN FORWARD FROM I, USING LOCAL CURS, ! TRYING EITHER TO DELETE I, DELETE CURS, ! OR CHANGE CURS TO A MOV(B). DURING ! THIS FORWARD SCAN THE FOLLOWING INFORMATION ! IS KEPT AROUND: ! ! FWDF,BACKF - FWDF IS SET IF A LABEL IS SEEN, BACKF ! IS SET IF A CONDITIONAL BRANCH IS SEEN ! OR I'S DST IS USED. IF SOMETHING ELSE ! HAPPENS, SUCH AS AN UNCONDITIONAL BRANCH ! BEING SEEN, THEY ARE BOTH SET; ANY TIME ! THEY ARE BOTH SET, THE SCAN IS ENDED. ! FWDMASK - A MASK OF THE BITS AFFECTED BY "PBOOL(B)" (AND ! NOT BY "XBOOL(B)") BETWEEN I AND CURS. ! ONLY VALID UNTIL FWDF IS SET. ! BACKMASK - A MASK OF THE BITS AFFECTED BY "PBOOL(B)" (AND ! NOT BY "XBOOL(B)") BETWEEN I AND WHENEVER FWDF ! WAS SET, BUT NOT AFFECTED SINCE THEN. ! NOT VALID AFTER BACKF IS SET. ! FWDF = FALSE; BACKF = FALSE; FWDMASK = BACKMASK = .I[cel_src_disp]; COPYADR(ADR,I[cel_dst]); CURS = .I; While TRUE Do aaa: Begin ! get the next cell. if it is not a code cell then it must be a ! label cell. CURS = .CURS[cel_next]; If .CURS[cel_type] Neq CELL_CODE Then Begin ! if a conditional branch was already seen then quit the loop If .BACKF Then Exitloop; ! note that a label has been seen. FWDF = TRUE; Leave aaa End; ! jumps, calls, returns, inline, MxPi OP = .CURS[cel_code]; If .STOPFSCAN[.OP] Then FWDF = TRUE; ! jumps, calls, returns, inline, MxPi, conditional branches If .STOPBSCAN[.OP] Then BACKF = TRUE; ! note: STOPFSCAN seems to be a subset of STOPBSCAN so if FWDF is ! set then BACKF is also set. If .BACKF And .FWDF Then Exitloop; ! for single operand instructions... If .OPERTYPE[.OP] Eql OPTYPE_ONE Then Begin ! adjust ADR, accounting for auto-increments/decrements UPDATE(CURS[cel_src],ADR); ! if the destination is modified and it might be our operand then ! we are all done If .ALTERSRCDST[.OP] Then Begin If MAY_REFER(CURS[cel_src],ADR) Then Exitloop; If MAY_USE_ADDR(ADR,CURS[cel_src]) Then Exitloop End ! note any possible references to this operand Else If MAY_REFER(CURS[cel_src],ADR) Then BACKF = TRUE; If MAY_USE_ADDR(CURS[cel_src],ADR) Then BACKF = TRUE; Leave aaa End; ! if a two operand instruction... If .OPERTYPE[.OP] Neq OPTYPE_TWO Then Leave aaa; ! account for auto-increment/decrement in the source UPDATE(CURS[cel_src],ADR); ! note possible uses of our operand If MAY_REFER(CURS[cel_src],ADR) Then BACKF = TRUE; If MAY_USE_ADDR(CURS[cel_src],ADR) Then BACKF = TRUE; ! account for auto-increment/decrement in the destination UPDATE(CURS[cel_dst],ADR); ! look for immediate operations on our operand If .CURS[cel_src_type] Eql ADDR_IMMED And .CURS[cel_src_name] Eql 0 And IS_SAME_ADR(CURS[cel_dst],ADR) Then ! if the same instruction as ours... If .OP Eql .PBOOL Or .OP Eql .PBOOLB Then Begin ! mask immediate value to 8 bits of a byte operation If .OP Eql .PBOOLB Then CURS[cel_src_disp] = .CURS[cel_src_disp] And 255; ! if a label has been seen then check to see if this instruction ! cancels out whatever the original instruction did. if so then ! delete the original. If .FWDF Then Begin BACKMASK = .BACKMASK And Not .CURS[cel_src_disp]; If .BACKMASK Eql 0 Then Return DELETE(.I); Leave aaa End ! if a conditional branch or use of 'I's operand has been seen then ! if this instruction produces no new effect then delete it. ! ! Q: what about any condition codes needed later on? this seems to ! be assuming that the condition codes set at instruction at 'I' will ! carry over this far. Else If .BACKF Then If (Not .FWDMASK And .CURS[cel_src_disp]) Eql 0 Then Begin DELETE(.CURS); Return .I End Else ! compute the new forward mask Begin FWDMASK = .FWDMASK Or .CURS[cel_src_disp]; Leave aaa End Else ! neither a branch, label, or use of the result of instruction 'I' ! has been seen. merge instruction 'I' into instruction 'CURS'. Begin CURS[cel_src_disp] = .CURS[cel_src_disp] Or .FWDMASK; If .I[cel_code] Eql .PBOOL Then CURS[cel_code] = .PBOOL; Return DELETE(.I) End End ! if the opposite instruction (e.g. BIC if 'I' is a BIS) Else If .OP Eql .XBOOL Or .OP Eql .XBOOLB Then Begin ! adjust byte immediates If .OP Eql .XBOOLB Then CURS[cel_src_disp] = .CURS[cel_src_disp] And 255; ! if no references to instruction 'I's operand then compute a new ! backward mask. if the instruction here cancels out what is done ! at 'I' then delete 'I'. If Not .BACKF Then Begin BACKMASK = .BACKMASK And Not .CURS[cel_src_disp]; If .BACKMASK Eql 0 Then Return DELETE(.I) End; ! if no labels have been seen and the sum of the bits being set plus ! the bits being cleared is all ones, e.g.: ! ! BIC #%x'ff00',R0 ! BIS #%x'00ff',R0 ! ! then what we have is effectively a move. If Not .FWDF Then If (.FWDMASK Or .CURS[cel_src_disp]) Eql %x'ffff' Or (.I[cel_code] Eql .PBOOLB And .OP Eql .XBOOLB And ((.FWDMASK Or .CURS[cel_src_disp]) And 255) Eql 255) Then Begin ! determine what move to use If .I[cel_code] Eql .PBOOLB And .OP Eql .XBOOLB Then CURS[cel_code] = PMOVB Else CURS[cel_code] = PMOV; ! adjust the operand at CURS and note that something has changed. If .I[cel_code] Eql PBIS Or .I[cel_code] Eql PBISB Then CURS[cel_src_disp] = Not .CURS[cel_src_disp]; SETCHANGE; ! if no references to instruction 'I's operand then we may delete it. If Not .BACKF Then Return DELETE(.I); Return .I[cel_next] End; ! forward flag set. adjust the forward flag FWDMASK = .FWDMASK And Not .CURS[cel_src_disp]; Leave aaa End; ! if the destination of this two operand instruction may modify 'I's ! operand the exit the loop. If .ALTERSRCDST[.CURS[cel_code]] Then Begin If MAY_REFER(CURS[cel_dst],ADR) Or MAY_USE_ADDR(ADR,CURS[cel_dst]) Then Exitloop End ! if the second operand makes uses of our operand then note it. Else If MAY_REFER(CURS[cel_dst],ADR) Then BACKF = TRUE; If MAY_USE_ADDR(CURS[cel_dst],ADR) Then BACKF = TRUE End; Return .I[cel_next] End; Routine OPT_CLC(I : Ref CELL) = Begin ! if nothing needs the C-bit later on then we may delete it If TESTCC(.I,__C___) Then Return .I[cel_next] Else Return DELETE(.I) End; Routine OPT_CLR(CURS : Ref CELL) = Begin Local Q : Ref ADRVARSTR, BYTES : Integer; BYTES = .OPBYTES[.CURS[cel_code]]; ! if a clear of a register or direct memory then we treat this ! like a 'MOV #0,X' and try to change later uses of 'X' to '#0' ! and previous stores into 'X' into no-ops. Q = CURS[cel_src]; If .Q[adr_type] Eql ADDR_REG Or .Q[adr_type] Eql ADDR_MEMORY Then Begin EQUIVFWD(.CURS,CURS[cel_src],IMMEDZERO,.BYTES); DELBACKREF(.CURS,CURS[cel_src],.BYTES) End; Return NXTLCC(.CURS) End; Routine OPT_CLVC(I : Ref CELL) = Begin Local NEXT : Ref CELL; ! if the next instruction of a BGE or a BLT then adjust it according ! to our knowledge of the C and V bits. ! ! Q: why is a SETCHANGE not performed? cannot this change affect ! tunnel branches? NEXT = .I[cel_next]; If .NEXT[cel_type] Eql CELL_CODE Then If .NEXT[cel_code] Eql PBGE Then NEXT[cel_code] = PBPL Else If .NEXT[cel_code] Eql PBLT Then NEXT[cel_code] = PBMI; ! if noone needs the C or V bit later on then we may delete this instruction. If TESTCC(.I,__CV__) Then Return .I[cel_next] Else Return DELETE(.I) End; Routine OPT_CMP(CURS : Ref CELL) = Begin Local PREV : Ref CELL, CURS1 : Ref CELL, PREV1 : Ref CELL, LABCURS : Ref CELL, CMPSRC : ADRVARSTR; ! if noone needs any of the condition codes set by a CMP then ! just delete the whole thing. If Not TESTCC(.CURS,__CVZN) Then Return DELETE(.CURS); ! try to change CMP(B) to TST(B) If .CURS[cel_dst_type] Eql ADDR_IMMED Then Begin ! adjust immediates for byte compares ! ! Q: what if high-order bits are set? does this not mean that ! the compare should always fail? If .CURS[cel_code] Eql PCMPB Then CURS[cel_dst_disp] = .CURS[cel_dst_disp] And 255; If .CURS[cel_dst_name] Eql 0 And .CURS[cel_dst_disp] Eql 0 Then Begin CURS[cel_code] = (If .CURS[cel_code] Eql PCMPB Then PTSTB Else PTST); SETCHANGE; Return OPT_TST(.CURS) End End; ! check the previous non-branch instruction. if a compare of the same thing ! then we can delete this compare. this fixes code sequences like: ! ! If .X Neq 0 Then ! If .X Gtr 0 Then ! ... PREV = PREVNONBR(.CURS); If .PREV[cel_type] Eql CELL_CODE Then If IS_SAME_CMP(.PREV,.CURS) Then Return DELETE(.CURS) Else Return .CURS[cel_next]; ! 'PREV' must be a label. see if we are the first after that label. If .PREV Neq .CURS[cel_prev] Then Return .CURS[cel_next]; ! cycle over all references to this label LABCURS = .PREV[cel_top]; Until .LABCURS Eql .PREV Do Begin CURS1 = .LABCURS[cel_ref_rf]; LABCURS = .LABCURS[cel_next]; ! if the references ends in a compare (except for any condition jumps) ! and its compare the same things then we can move the jump to right ! after the compare here. ! ! Q: does 'fall-thru' code go through this code also? If .CURS1[cel_class] Eql INST_COND_JUMP Or .CURS1[cel_class] Eql INST_UNCOND_JUMP Then Begin PREV1 = PREVNONBR(.CURS1); If .PREV1[cel_type] Eql CELL_CODE And .PREV1 Neq .CURS Then If IS_SAME_CMP(.PREV1,.CURS) Then MOVEJMP(.CURS1,.CURS[cel_next]) End End; Return .CURS[cel_next] End; Routine OPT_DEC(CURS : Ref CELL) = Begin ! change DEC instructions to SUB instructions. we can't do this ! optimization if the C-bit from a previous instruction is needed ! in a later instruction. If TESTCC(.CURS,__C___) Then Return .CURS[cel_next]; ! change 'DEC x' to 'SUB #1,x' SETCHANGE; COPYADR(CURS[cel_dst],CURS[cel_src]); COPYADR(CURS[cel_src],IMMEDONE); CURS[cel_code] = PSUB; CURS[cel_class] = INST_ADD_IMMED; Return OPT_ADDSUB(.CURS) End; Routine OPT_INC(CURS : Ref CELL) = Begin ! same as DEC above except we change 'INC x' to 'ADD #1,x' If TESTCC(.CURS,__C___) Then Return .CURS[cel_next]; SETCHANGE; COPYADR(CURS[cel_dst],CURS[cel_src]); COPYADR(CURS[cel_src],IMMEDONE); CURS[cel_code] = PADD; CURS[cel_class] = INST_ADD_IMMED; Return OPT_ADDSUB(.CURS) End; Routine OPT_JSR(CURS : Ref CELL) = Begin Local NEXT : Ref CELL; ! examine the next instruction. if this is a 'JSR PC,x' and the ! next instruction is a 'RTS PC' then change the JSR to a JMP. ! note: this also gets rid of the simplier cases of tail recursion. NEXT = NXTCC(.CURS); If .NEXT[cel_class] Eql INST_UNCOND_JUMP Then If LABREFP(.NEXT) Then NEXT = NXTCC(LABREFED(.NEXT)); ! Q: can this compiler generate an RTS with anything other than PC? ! or is it just playing safe in case there are future enhancements? If .NEXT[cel_code] Eql PRTS And .NEXT[cel_src_reg] Eql PC And .CURS[cel_src_reg] Eql PC Then Begin SETCHANGE; CURS[cel_code] = PBR; CURS[cel_class] = INST_UNCOND_JUMP; ! if a tail-recursion call then just jump to the first cell ! otherwise just jump to the destination If .CURS[cel_dst_name_type] Eql NAME_NORMAL And .CURS[cel_dst_name] Eql .CURROUT And .CURS[cel_src_disp] Eql 0 Then MAKEJMP(.CURS,.BRAK1[cel_next]) Else COPYADR(CURS[cel_src],CURS[cel_dst]); ! we made this into a jump so optimzie it now to try to avoid another ! final pass. Return OPT_UNCOND_JUMP(.CURS) End; Return .CURS[cel_next] End; Routine OPT_MOV(CURS : Ref CELL) = Begin Local BYTES, Q : Ref ADRVARSTR, AREF : ADRVARSTR; ! MOV X,PC is like a JMP or BR to X If .CURS[cel_dst_mode] Eql GENREG And .CURS[cel_dst_reg] Eql PC Then CLEARTOLAB(.CURS); BYTES = .OPBYTES[.CURS[cel_code]]; ! change MOV(B) #0,X to CLR(B) X unless there is a need for the C-bit ! later on (CLR clears the C-bit). If .CURS[cel_src_type] Eql ADDR_IMMED And .CURS[cel_src_name] Eql 0 And .CURS[cel_src_disp] Eql 0 And Not TESTCC(.CURS,__C___) Then Begin SETCHANGE; REDUCEADR(.CURS); CURS[cel_code] = (If .BYTES Eql 2 Or .CURS[cel_dst_mode] Eql 0 Then PCLR Else PCLRB); Return .CURS End; If .swit_quick Then Return .CURS[cel_next]; ! after a MOV instruction the locations referenced by the source and ! destination operands contain the same value. a forward scan is made ! for instructions using either of the two and replace the more complex ! with the simplier of the two. ! ! also, a backward scan is made for alters of the destination and ! they are deleted. COPYADR(AREF,CURS[cel_src]); UPDATE(CURS[cel_dst],AREF); Q = CURS[cel_src]; If (.Q[adr_type] Eql ADDR_REG Or .q[adr_type] Eql ADDR_MEMORY) And Not MAY_USE_ADDR(AREF,CURS[cel_dst]) Then Begin EQUIVFWD(.CURS,CURS[cel_src],CURS[cel_dst],.BYTES); If Not MAY_REFER(AREF,CURS[cel_dst]) Then Begin COPYADR(AREF,CURS[cel_dst]); DOWNDATE(CURS[cel_src],AREF); DELBACKREF(.CURS,AREF,If .CURS[cel_dst_mode] Eql 0 Then 2 Else .BYTES); End End; Return NXTLCC(.CURS) End; Routine OPT_TST(CURS : Ref CELL) = Begin Local PREV : Ref CELL, LABCURS : Ref CELL, CURS1 : Ref CELL, PREV1 : Ref CELL; ! if noone wants the conditions codes produced by this TST then delete it If Not TESTCC(.CURS,__CVZN) Then Return DELETE(.CURS); ! if there is an auto-increment/decrement then we cannot remove the ! TST because removing it would generate an ADD/SUB to account for the ! auto-increment/decrement. If .CURS[cel_src_delta] Neq 0 Then Return .CURS[cel_next]; ! examine the previous non-branch code cell. if it sets the condition ! codes the way we want then we can delete this TST because the condition ! codes will already be set correctly. PREV = PREVNONBR(.CURS); If .PREV[cel_type] Eql CELL_CODE Then Case SETSCC(.PREV,.CURS) From 0 To 3 Of Set [0]: ! may not be deleted Return .CURS[cel_next]; [1]: ! may be deleted Return DELETE(.CURS); [2]: ! may be deleted if nobody cares for carry ! TST cleared carry so make sure it is clear for whoever wanted it If TESTCC(.CURS,__C___) Then Begin DELADR(CURS[cel_src],.CURS); CURS[cel_code] = PCLC; Return(.CURS) End Else Return DELETE(.CURS); [3]: ! may be deleted if nobody cares for carry/overflow ! ditto for carry + overflow If TESTCC(.CURS,__CV__) Then Begin DELADR(CURS[cel_src],.CURS); CURS[cel_code] = PCLVC; Return (.CURS) End Else Return DELETE(.CURS) Tes; ! return if not the first instruction after a label If .PREV Neq .CURS[cel_prev] Then Return .CURS[cel_next]; ! examine all paths to this label. if there is a test on the same ! thing we are testing then move the jump from our label to just ! after our TST instruction. LABCURS = .PREV[cel_top]; Until .LABCURS Eql .PREV Do Begin CURS1 = .LABCURS[cel_ref_rf]; LABCURS = .LABCURS[cel_next]; If .CURS1[cel_class] Eql INST_COND_JUMP Or .CURS1[cel_class] Eql INST_UNCOND_JUMP Then Begin PREV1 = PREVNONBR(.CURS1); If .PREV1[cel_type] Eql CELL_CODE Then If .PREV1 Neq .CURS Then Case SETSCC(.PREV1,.CURS) From 0 To 3 Of Set [0]: ! may not be deleted 0; [1]: ! may be deleted MOVEJMP(.CURS1,.CURS[cel_next]); [2]: ! may be deleted if nobody cares for carry If Not TESTCC(.CURS,__C___) Then MOVEJMP(.CURS1,.CURS[cel_next]); [3]: ! may be deleted if nobody cares for carry/ovf If Not TESTCC(.CURS,__CV__) Then MOVEJMP(.CURS1,.CURS[cel_next]) Tes End End; Return .CURS[cel_next] End; ! case parameter optimizations Routine OPT_CASE(IND : Ref CELL) = Begin Local NEXT : Ref CELL; ! after a case parameter may only be another case parameter. NEXT = NXTLCC(.IND); If .NEXT[cel_code] Neq PCASE Then NEXT = CLEARTOLAB(.IND); FIXJMPIND(.IND); Return .NEXT End; Routine OPT_COND_JUMP(IND : Ref CELL) = Begin Local NEXT : Ref CELL, NEXTC : Ref CELL, AREF : Ref CELL, LAB : Ref CELL; ! we do not allow for unconditional jumps to outside a procedure ! so we change them to a reversed branch followed by an unconditional ! jump to the outside label. NEXT = NXTLCC(.IND); If Not LABREFP(.IND) Then Begin NEXTC = NEWCODECELL(); BEFORE(.IND,.NEXTC); COMBLAB(MAKEJMP(.NEXTC,.NEXT)); AREF = .NEXTC[cel_src_name]; AREF[cel_class] = INST_COND_JUMP; NEXTC[cel_code] = REVCOND(.IND[cel_code]); NEXTC[cel_class] = INST_COND_JUMP; IND[cel_code] = PJMP; IND[cel_class] = INST_UNCOND_JUMP; Return .NEXTC End; ! get the label referenced LAB = LABREFED(.IND); ! look for: Bcond .+1 If .LAB Eql .NEXT Then Return DELETE(.IND); ! look for: Bcond lab1 ! BR lab2 (or Bcond' lab2) ! lab1: If .NEXT[cel_type] Eql CELL_CODE And .NEXT[cel_next] Eql .LAB Then If LABREFP(.NEXT) Then Begin If BRIMPLY(.IND[cel_code],.NEXT[cel_code]) Eql 2 Then Return DELETE(.IND); If .NEXT[cel_code] Eql PBR Then Begin DELADR(IND[cel_src],.IND); MAKEADR(IND[cel_src],NEXT[cel_src],.IND); IND[cel_code] = REVCOND(.IND[cel_code]); DELETE(.NEXT); Return .IND End End; ! look for: Bcond lab1 ! BR lab1 NEXTC = NXTCC(.IND); If .NEXTC[cel_class] Eql INST_UNCOND_JUMP Then If LABREFP(.NEXTC) Then If LABREFED(.NEXTC) Eql .LAB Then Return DELETE(.IND); ! if LAB is not the ultimate destination of this branch then ! change the branch to that destination. NEXTC = CONDJMPSRC(.LAB,.IND); If .NEXTC Neq .LAB Then Begin SETCHANGE; MOVEJMP(.IND,.NEXTC) End; Return .NEXT End; Routine OPT_LABEL(LAB : Ref CELL) = Begin Local VAL : Ref CELL, CURS1 : Ref CELL, CURS2 : Ref CELL, J1 : Ref CELL, J2 : Ref CELL; LAB = COMBLAB(.LAB); VAL = NXTLCC(.LAB); ! if not referenced If .LAB[cel_bot] Eql .LAB Then Begin SETCHANGE; ERASE(.LAB); Return .VAL End; ! label is referenced. perform cross-jumping CURS1 = .LAB[cel_top]; Until .CURS1 Eql .LAB Do Begin If .CURS1[cel_class] Eql INST_UNCOND_JUMP Then Begin CURS2 = .CURS1; ! apply to all permutations Until (CURS2 = .CURS2[cel_next]) Eql .LAB Do If .CURS2[cel_class] Eql INST_UNCOND_JUMP Then Begin ! set to cross-jump to the cell closer to the start of the program J1 = .CURS1[cel_ref_rf]; J2 = .CURS2[cel_ref_rf]; If .J1[cel_min_loc] Gtr .J2[cel_min_loc] Then SWAP(J1,J2); BACKOV(.J1,.J2) End End; CURS1 = .CURS1[cel_next] End; Return .VAL End; Routine OPT_UNCOND_JUMP(IND : Ref CELL) = Begin Local NEXT : Ref CELL, LAB : Ref CELL, SVCHANGE, PREV : Ref CELL, TMPRES : Ref CELL, LAB1 : Ref CELL; ! code following unconditional jump is unreachable NEXT = CLEARTOLAB(.IND); ! if a jump to an external label then note it and return. If Not LABREFP(.IND) Then Begin IND[cel_code] = PJMP; Return .NEXT End; ! get the label referenced and delete 'BR .+1' LAB = LABREFED(.IND); If .LAB Eql .NEXT Then Return DELETE(.IND); ! if infinite loop If FIXJMPIND(.IND) Then Return .NEXT; ! perform cross-jumping between this code sequence and the sequence which ! falls through to the label LAB = LABREFED(.IND); SVCHANGE = .CHANGE; CHANGE = FALSE; TMPRES = BACKOV(.IND,.LAB); If .CHANGE Then Return .TMPRES; CHANGE = .SVCHANGE; ! if the instruction before the JMP is a conditional branch then ! switch the branches and condition and try to cross-jump again PREV = PRVLCC(.IND); If .PREV[cel_type] Neq CELL_CODE Then Return .TMPRES; If .PREV[cel_class] Neq INST_COND_JUMP Then Return .TMPRES; If LABREFP(.PREV) Then Begin LAB1 = LABREFED(.PREV); MOVEJMP(.PREV,.LAB); MOVEJMP(.IND,.LAB1); PREV[cel_code] = REVCOND(.PREV[cel_code]); LAB = LABREFED(.IND); CHANGE = FALSE; TMPRES = BACKOV(.IND,.LAB); CHANGE = .CHANGE Or .SVCHANGE End; Return .TMPRES End; ! determine whether a label is reachable with a short branch ! ! notes: ! a label is reachable if the label itself is within 127 words ! of the source or there is a chain of branches which reach the ! label Routine REACH(CURS : Ref CELL) = Begin Local LAB : Ref CELL, DIST : Integer, NDIST : Integer, AREF : Ref CELL, BEST : Ref CELL, BDIST : Integer, REFCELL : Ref CELL; LAB = LABREFED(.CURS); DIST = .LAB[cel_min_loc] - .CURS[cel_min_loc]; If .DIST Leq 127 And .DIST Gtr -128 Then Return .LAB; AREF = .LAB[cel_top]; BEST = 0; BDIST = 0; Until .AREF Eql .LAB Do Begin REFCELL = .AREF[cel_ref_rf]; NDIST = .REFCELL[cel_min_loc] - .CURS[cel_min_loc]; If .NDIST Leq 127 And .NDIST Gtr -128 And Abs(.NDIST) Gtr .BDIST And BRIMPLY(.CURS[cel_code],.REFCELL[cel_code]) Eql 1 And (BRIMPLY(.REFCELL[cel_code],.CURS[cel_code]) Neq 1 Or SIGN(.NDIST) Eql SIGN(.DIST)) Then Begin BDIST = Abs(.NDIST); BEST = .REFCELL End; AREF = .AREF[cel_next] End; Return .BEST End; Routine IS_SAME_ADR(A : Ref ADRVARSTR,B : Ref ADRVARSTR) = Begin ! memory locations are the same if they use the same register, ! the same segment, the same segment displacement, and are ! not bogus (adr_type Eql UNUSED is bogus, i.e. second operand ! field of a one operand instruction). If .A[adr_name_type] Eql NAME_NORMAL Then Return .A[adr_reg] Eql .B[adr_reg] And .A[adr_name] Eql .B[adr_name] And .A[adr_disp] Eql .B[adr_disp] And .A[adr_type] Neq UNUSED ! labels are the same if they reference the same label cell Else If .A[adr_name_type] Eql NAME_LABEL Then If .B[adr_name_type] Eql NAME_LABEL Then Return .Block[.A[adr_name],cel_ref_ef] Eql .Block[.B[adr_name],cel_ref_ef]; Return FALSE End; ! check of two instructions are both compares of the same things Routine IS_SAME_CMP(PREV : Ref CELL,CURS : Ref CELL)= Begin Local CMPSRC : ADRVARSTR; ! must both be the same CMP code If .PREV[cel_code] Neq .CURS[cel_code] Then Return FALSE; ! cannot allow auto-increment/decrement in either the source or ! destination operand because the decision here is used to determine ! whether the instruction at 'CURS' should be deleted. if it had ! an auto-increment then deleting the instruction would cause a ! 'ADD #n,R' to compensate for the lost auto-increment and ! this add would set the condition codes wrong. If .CURS[cel_src_delta] Neq 0 Or .CURS[cel_dst_delta] Neq 0 Then Return FALSE; ! compare the source addresses If Not IS_SAME_ADR(PREV[cel_dst],CURS[cel_dst]) Then Return FALSE; ! form the destination and compare that COPYADR(CMPSRC,CURS[cel_src]); UPDATE(PREV[cel_dst],CMPSRC); Return IS_SAME_ADR(PREV[cel_src],CMPSRC) End; ! ! TRUE IF ADDRESS B IS ADDRESS A + INDIRECT ! ! called by EQUIVIND Routine IS_SAME_IND(A : Ref ADRVARSTR,B : Ref ADRVARSTR) = Begin ! make sure they have the same base register, segment, and segment ! displacement If .A[adr_reg] Neq .B[adr_reg] Then Return FALSE; If .A[adr_name] Neq .B[adr_name] Then Return FALSE; If .A[adr_disp] Neq .B[adr_disp] Then Return FALSE; ! # -> @# ! R -> @R If .A[adr_type] Eql ADDR_IMMED Or .A[adr_type] Eql ADDR_REG Then Return .B[adr_type] Eql ADDR_MEMORY; If .A[adr_type] Eql ADDR_MEMORY Then Return .B[adr_type] Eql ADDR_INDIRECT; ! otherwise A[adr_type] Eql ADDR_INDIRECT Return FALSE End; ! ! T IS A TST INSTRUCTION, I IS THE PREVIOUS INSTRUCTION. ! THE RESULT RETURNED IS: ! 0 - T MUST NOT BE DELETED ! 1 - T IS SUPERFLUOUS, MAY BE DELETED ! 2 - T IS SUPERFLUOUS IF NOBODY IS WORRIED ABOUT CARRY ! 3 - T IS SUPERFLUOUS IF NOBODY IS WORRIED ABOUT CARRY OR OVERFLOW ! ! called by OPT_TST Routine SETSCC(I : Ref CELL,T : Ref CELL) = Begin Local A : ADRVARSTR; ! previous cell must be an instruction. if a label, previous ! instruction could have come from several spots If .I[cel_type] Neq CELL_CODE Then Return 0; Case .CCSETTYPE[.I[cel_code]] From 0 To 7 Of Set ! UUO, CMP(B), BIT(B), JSR, RTS, HALT, WAIT, RTI, IOT, RESET ! EMT, TRAP, BR, JMP, NOP, CLC, CLVC, .WORD, CASE [0]: 0; ! MOV(B) [1]: If .OPBYTES[.I[cel_code]] Eql .OPBYTES[.T[cel_code]] Then If IS_SAME_ADR(I[cel_dst],T[cel_src]) Then Return 2 Else If Not MAY_USE_ADDR(T[cel_src],I[cel_dst]) Then Begin COPYADR(A,T[cel_src]); DOWNDATE(I[cel_dst],A); If IS_SAME_ADR(I[cel_src],A) Then Return 2 End; ! BIC(B), BIS(B) [2]: If .OPBYTES[.I[cel_code]] Eql .OPBYTES[.T[cel_code]] Then If IS_SAME_ADR(I[cel_dst],T[cel_src]) Then Return 2; ! ADD, SUB [3]: If .OPBYTES[.I[cel_code]] Eql .OPBYTES[.T[cel_code]] Then If IS_SAME_ADR(I[cel_dst],T[cel_src]) Then Return 3; ! CLR(B), COM(B), TST(B), SWAB [4]: If .OPBYTES[.I[cel_code]] Eql .OPBYTES[.T[cel_code]] Then If IS_SAME_ADR(I[cel_src],T[cel_src]) Then Return 1; ! INC(B), DEC(B), NEG(B), ADC(B), SBC(B), ROR(B), ROL(B), ASR(B), ASL(B) [5]: If .OPBYTES[.I[cel_code]] Eql .OPBYTES[.T[cel_code]] Then If IS_SAME_ADR(I[cel_src],T[cel_src]) Then Return 3; ! MFPI, MFPD, MTPI, MTPD [6]: If .OPBYTES[.T[cel_code]] Eql 2 Then If IS_SAME_ADR(I[cel_src],T[cel_src]) Then Return 2; ! branch instructions. these do not affect the condition codes ! so we may look at the previous instruction. [7]: Begin I = .I[cel_prev]; If .T Neq .I Then Return SETSCC(.I,.T) End Tes; Return 0 End; ! ! TRUE IF ANY CC BITS INDICATED BY CCS ! ARE USED BEFORE BEING RESET, ON ANY ! CONTROL PATH FOLLOWING CURS. ! Routine TESTCC(CURS : Ref CELL,CCS : Integer) = Begin Local CCSL : Integer, CCUSE : Integer, LAB : Ref CELL, T : Boolean; CCSL = (If .swit_final Then CCSHIFT(.CCS) Else .CCS); ! get the condition codes needed by the next instruction CURS = NXTCC(.CURS); CCUSE = .CCUSETYPE[.CURS[cel_code]]; ! if it uses any of the condition codes given then return TRUE If (.CCUSE And .CCSL) Neq 0 Then Return TRUE; ! if this is a branch instruction to another label... If (.CCUSE And _J____) Neq 0 Then If LABREFP(.CURS) Then Begin ! find the label referenced and make sure we are not in a recursive loop. LAB = LABREFED(.CURS); If .LAB[cel_lab_seen] Then Return FALSE; ! mark this label as seen to avoid a recursive loop and test along its ! path. LAB[cel_lab_seen] = TRUE; T = TESTCC(.LAB,.CCS); LAB[cel_lab_seen] = FALSE; ! return if the condition codes are needed If .T Then Return TRUE; ! if this is an unconditional jump then we only had to check the one path If .CURS[cel_class] Neq INST_COND_JUMP Then Return FALSE; ! we have to check both paths Return TESTCC(.CURS,.CCS) End Else ! we have a jump but it is a jump outside the routine to an external ! label. it seems better in this case to just say TRUE to me. Return Not .swit_final ! this is not a branch. most instructions affect the condition codes ! but only a few affect carry. if this instruction does not affect ! carry and we are interested in carry then look onward. Else If (.CCUSE And X_____) Neq 0 And (.CCSL And __C___) Neq 0 Then Return TESTCC(.CURS,__C___) Else Return FALSE End; ! ! MODIFY NEXT INSTRUCTION TO ASSUME ZERO CONDITION ! CODE. RETURNS 1 IF MODIFICATION NOT POSSIBLE. ! ! called by DUPADR ! ! notes: ! an instruction of the form: ! ! CMP X,X ! ! has been discovered. the result of this will produce: ! ! C = 0 ! Z = 1 ! V = 0 ! N = 0 ! Routine TESTEQL(CURS : Ref CELL) = Begin Local CURS1 : Ref CELL; Bind NOTBREQL = Uplit Byte ( FALSE, ! BR TRUE, ! NBR TRUE, ! BNE FALSE, ! BEQ FALSE, ! BGE TRUE, ! BLT FALSE, ! BLE TRUE, ! BGT FALSE, ! BPL TRUE, ! BMI TRUE, ! BHI FALSE, ! BLOS FALSE, ! BVC TRUE, ! BVS FALSE, ! BHIS TRUE ! BLO ) : Vector[,Byte]; ! if the next instruction is not a conditional branch then test whether ! any of the condition codes are needed CURS1 = NXTCC(.CURS); If .CURS1[cel_type] Neq CELL_CODE Or .CURS1[cel_class] Neq INST_COND_JUMP Then Return TESTCC(.CURS,__CVZN); ! if the next instruction branches on the Z-bit being zero then we may ! delete the branch instruction If .NOTBREQL[.CURS1[cel_code]-PBR] Then DELETE(.CURS1) Else ! otherwise we will always branch Begin SETCHANGE; CURS1[cel_code] = PBR; CURS1[cel_class] = INST_UNCOND_JUMP End; ! indicate that nothing depends on the CMP and so may be deleted Return FALSE End; ! ! FUNCTION: ! SEE IF ADDRESS "A" IN CODE CELL "I" CAN BE MODIFIED BECAUSE ! OF THE EXISTENCE OF LATER IMMEDIATE ADD (SUB) INSTRUCTION "IADD". ! A RETURN VALUE OF TRUE TERMINATES THE SCAN. ! ! CALLED FROM A BACKWARD SCAN IN OPT_ADDSUB; Routine TESTPOP(A : Ref ADRVARSTR,IADD : Ref CELL,I : Ref CELL) = Begin Local BYTES : Integer, OFFST : Integer, NEWADD : Ref CELL, NEWOFFSET : Integer; Label aaa; ! if the base registers differ... If .A[adr_reg] Neq .IADD[cel_dst_reg] Then Begin ! in quick mode, limit the forward scan to immediates and register ! (that is, no memory references). If Not .swit_final Then Return (.A[adr_type] Neq ADDR_IMMED And .A[adr_type] Neq ADDR_REG); ! return FALSE if not a stack reference or addresses of stack variables ! has not been taken. If .IADD[cel_dst_reg] Neq SP Or Not .flg_stack_addr Then Return FALSE; ! TO IADD MIGHT CAUSE A TO ! all bets off with indirection and the stack segment is in use If .A[adr_type] Eql ADDR_INDIRECT Then Return TRUE; ! POINT ABOVE TOP OF (SP) STACK. ! all bets also off with calculated addresses If .A[adr_type] Eql ADDR_MEMORY And .A[adr_name] Eql 0 And .A[adr_reg] Neq PC Then Return TRUE; Return FALSE End; ! if the base registers agree... ! only concerned with memory references - not register references If .A[adr_mode] Eql GENREG Then Return TRUE; ! if a segment other than the absolute or stack segment If .A[adr_name] Gtr 1 Then Return FALSE; ! get the displacement OFFST = (If .IADD[cel_code] Eql PSUB Then -.IADD[cel_src_disp] Else .IADD[cel_src_disp]); ! change A from (R)+ to @R If .A[adr_delta] Gtr 0 Then Begin ! Q: when would there be code generated which a register in one ! instance uses auto-increment (implying a name of 0) and then ! later on have a non-zero name? If .OFFST Geq 0 And .IADD[cel_src_name] Eql 0 Then Return TRUE; If .A[adr_type] Eql ADDR_INDIRECT Then Return TRUE; BYTES = -.A[adr_delta]; A[adr_mode] = GENREG+DEFERRED; A[adr_delta] = 0; A[adr_disp] = 0 End ! if A is -(R) or @-(R) Else If .A[adr_delta] Lss 0 Then Return TRUE ! neither auto-increment or auto-decrement Else Begin ! get the amount we would be auto-incrementing/decrementing by BYTES = (If .A[adr_type] Eql ADDR_INDIRECT Or .A[adr_reg] Geq SP Then 2 Else .OPBYTES[.I[cel_code]]); ! change A from -2(R) to -(R) or @-2(R) to @-(R) If .A[adr_disp] Eql -.BYTES Then Begin BYTES = -.BYTES; A[adr_delta] = .BYTES; A[adr_mode] = .A[adr_mode] - 2; A[adr_disp] = 0 End Else If .A[adr_disp] Eql 0 Then ! change A from @R to (R)+ or from @0(R) to @(R)+ ! with the following rules: ! ! if an absolute offset, it must be positive ! -or- it involves indirection and does not use the SP or may be a pop ! ! for the first case, we want to see offset decreasing to realize ! any gain. for the second case we can save an operand word if ! indirect as long as we don't uncover it on the stack If (.IADD[cel_src_name] Eql 0 And .OFFST-.BYTES Geq 0) Or (.A[adr_type] Eql ADDR_INDIRECT And (.A[adr_reg] Neq SP Or .OFFST Gtr 0)) Then Begin A[adr_delta] = .BYTES; A[adr_mode] = (If .A[adr_type] Eql ADDR_MEMORY Then AUTOINCR Else AUTOINCR+DEFERRED); A[adr_disp] = -.BYTES End Else ! neither of the above two cases Return TRUE Else If ! THIS BLOCK CHECKS TO SEE IF WE CAN, IN EFFECT, PUT IADD BEFORE I, TO ! CHANGE A FROM N(REG) TO @REG OR (REG)+. aaa: Begin NEWOFFSET = .A[adr_disp]; ! PUSHPOPFLAG had already been calculated in OPT_ADDSUB for SP If .A[adr_reg] Neq SP Then ! if the offsets are the same then we want it to become '@R' but ! there is no gain if indirection (must use '@0(R)'). ! the offset looks perfect if differing by the auto-increment amount. If (.NEWOFFSET Eql .OFFST And .A[adr_type] Eql ADDR_MEMORY) Or .NEWOFFSET Eql .OFFST-.BYTES Then PUSHPOPFLAG = .OFFST; ! leave if a push/pop doesn't look like it would work. If (.NEWOFFSET Neq .OFFST Or .A[adr_type] Neq ADDR_MEMORY) And .NEWOFFSET Neq .OFFST-.BYTES Then Leave aaa With FALSE; ! if indirection and we were planning on generating a stack pop and ! a stack address was calculated then don't do it. we might be popping ! off the indirection address. If .A[adr_type] Neq ADDR_MEMORY And .A[adr_reg] Eql SP And .flg_stack_addr Then Leave aaa With FALSE; ! if a single operand or we are working on the source then what we have is ok If .OPERTYPE[.I[cel_code]] Neq OPTYPE_TWO Then Leave aaa With TRUE; If I[cel_src] Eql .A Then Leave aaa With TRUE; ! if the source and destination registers differ then all we have ! to worry about is a pop of the source operand If .I[cel_src_reg] Neq .A[adr_reg] Then Begin If .A[adr_reg] Neq SP Or Not .flg_stack_addr Then Leave aaa With TRUE; If .I[cel_src_type] Eql ADDR_INDIRECT Then Leave aaa With FALSE; If .I[cel_src_type] Eql ADDR_MEMORY And .I[cel_src_name] Eql 0 And .I[cel_src_reg] Neq PC Then Leave aaa With FALSE; Leave aaa With TRUE End; ! source and destination registers are the same. we cannot tolerate ! direct references to the register (we are trying to modify the value of the ! register and we can't do so if its real value is needed) or if the ! source auto-increments/decrements it. If .I[cel_src_type] Eql ADDR_REG Or .I[cel_src_delta] Neq 0 Then Leave aaa With FALSE; ! all bets off when using indirection and the stack If .I[cel_src_type] Eql ADDR_INDIRECT And .A[adr_reg] Eql SP And .flg_stack_addr Then Leave aaa With FALSE; ! cannot use a pop if the pop would expost the source If .A[adr_reg] Eql SP And .I[cel_src_disp] Lss .NEWOFFSET-2 Then Leave aaa With FALSE; ! we are changing the source as well I[cel_src_disp] = .I[cel_src_disp] - .A[adr_disp]; ! see if the source may be an auto-increment auto If .I[cel_src_disp] Eql -.BYTES And .I[cel_src_name] Leq 1 Then Begin I[cel_src_delta] = .BYTES; I[cel_src_mode] = (If .I[cel_src_type] Eql ADDR_MEMORY Then AUTOINCR Else AUTOINCR+DEFERRED); NEWOFFSET = .NEWOFFSET-.BYTES End; Leave aaa With TRUE End Then ! IT WORKED! Begin ! change something like: ! ! MOV n(R),X ! ADD #n,R ! -to- ! ADD #n,R ! MOV @R,X If .A[adr_disp] Eql .PUSHPOPFLAG Then Begin A[adr_disp] = 0; A[adr_mode] = GENREG+DEFERRED End ! change something like: ! ! MOV n(R),X ! ADD #n+2,R ! -to- ! ADD #n,R ! MOV (R)+,X Else Begin A[adr_delta] = .BYTES; A[adr_disp] = -.BYTES; A[adr_mode] = (If .A[adr_type] Eql ADDR_MEMORY Then AUTOINCR Else AUTOINCR+DEFERRED) End; ! insert the new add instruction NEWADD = BEFORE(.I,NEWCODECELL()); NEWADD[cel_code] = PADD; NEWADD[cel_class] = INST_ADD_IMMED; COPYADR(NEWADD[cel_dst],IADD[cel_dst]); COPYADR(NEWADD[cel_src],IMMEDZERO); NEWADD[cel_src_disp] = .NEWOFFSET; BYTES = .PUSHPOPFLAG; End Else ! neither of the above two cases were found. Begin If .A[adr_disp] Lss .PUSHPOPFLAG+.BYTES Then PUSHPOPFLAG = .A[adr_disp]; Return FALSE End; End; ! come here when we have changed: ! ! (R)+ to @R ! -2(R) to -(R) or @-2(R) to @-(R) ! @R to (R)+ or @0(R) to @(R)+ ! -or- the above two cases ! adjust the old ADD #n instruction OFFST = .OFFST - .BYTES; IADD[cel_src_disp] = (If .IADD[cel_code] Eql PSUB Then -.OFFST Else .OFFST); ! fix up cells between I and IADD to reflect changes in those two cells While TRUE Do Begin IADD = .IADD[cel_prev]; Selectone .OPERTYPE[.IADD[cel_code]] Of Set [OPTYPE_ONE]: If TESTPP(.A,IADD[cel_src],.BYTES) Then Exitloop; [OPTYPE_TWO]: If TESTPP(.A,IADD[cel_dst],.BYTES) Then Exitloop Else If TESTPP(.A,IADD[cel_src],.BYTES) Then Exitloop Tes End; SETCHANGE; Return TRUE End; ! ! FUNCTION: ! ADJUST ADDRESS "I" BY AN AMOUNT "DELTA" IF ADDRESSES ! "A" AND "I" USE THE SAME REGISTER. RETURN TRUE IF ! A EQUALS I, TO SIGNAL THE END OF AN ADDRESS ADJUSTMENT SCAN. ! Routine TESTPP(A : Ref ADRVARSTR,I : Ref ADRVARSTR,DELTA : Integer) = Begin ! if instruction containing changed operand is reached If .A Eql .I Then Return TRUE; ! if a reference to the register then update it. any reference ! will be a memory or indirect reference and not a register reference. If .A[adr_reg] Eql .I[adr_reg] Then Begin I[adr_disp] = .I[adr_disp] - .DELTA; ! change 0(R) to @R If .I[adr_disp] Eql 0 And .I[adr_mode] Eql INDEXED Then I[adr_mode] = GENREG+DEFERRED End; Return FALSE End; ! ! FUNCTION: ! SEE IF ADDRESS "A" IN INSTRUCTION "I" CAN BE MODIFIED BECAUSE OF ! THE EXISTENCE OF EARLIER IMMEDIATE ADD (SUB) INSTRUCTION "IADD". ! A RETURN VALUE OF TRUE TERMINATES THE SCAN. ! ! called from OPT_ADDSUB in its forward scan Routine TESTPUSH(A : Ref ADRVARSTR,IADD : Ref CELL,I : Ref CELL) = Begin Local BYTES : Integer, OFFST : Integer, NEWADD : Ref CELL, NEWOFFSET : Integer; Label aaa; ! if the base registers differ... If .A[adr_reg] Neq .IADD[cel_dst_reg] Then Begin ! no problem in slow mode If .swit_final Then Return FALSE; ! in fast mode, don't accept anything which references memory Return .A[adr_type] Neq ADDR_IMMED And .A[adr_type] Neq ADDR_REG End; ! the registers agree. ! only accept memory modes. If .A[adr_mode] Eql GENREG Then Return TRUE; ! if a named segment then we can't change it If .A[adr_name] Gtr 1 Then Return FALSE; ! fetch the immediate add/sub displacement OFFST = (If .IADD[cel_code] Eql PSUB Then -.IADD[cel_src_disp] Else .IADD[cel_src_disp]); ! change A from -(R) to @R ! note that we do not change @-(R) to @0(R) because that ! is a loss. If .A[adr_delta] Lss 0 Then Begin If (.OFFST Leq 0 And .IADD[cel_src_name] Eql 0) Or .A[adr_type] Eql ADDR_INDIRECT Then Return TRUE; BYTES = .A[adr_delta]; A[adr_mode] = GENREG+DEFERRED; A[adr_delta] = 0; A[adr_disp] = 0 End ! A is (R)+ or @(R)+ Else If .A[adr_delta] Gtr 0 Then Return TRUE ! A is @R, n(R) or @n(R) Else Begin ! compute the decrement amount BYTES = (If .A[adr_type] Eql ADDR_INDIRECT Or .A[adr_reg] Geq SP Then 2 Else .OPBYTES[.I[cel_code]]); ! change A from -2(R) to (R)+ or from @-2(R) to @(R)+ If .A[adr_disp] Eql -.BYTES Then Begin A[adr_delta] = .BYTES; A[adr_mode] = .A[adr_mode] - 4; BYTES = -.BYTES End Else If .A[adr_disp] Eql 0 Then ! CHANGE A FROM @REG TO -(REG) ! OR FROM @0(REG) TO @-(REG). If (.IADD[cel_src_name] Eql 0 And .OFFST+.BYTES Leq 0) Or (.A[adr_type] Eql ADDR_INDIRECT And .A[adr_reg] Neq SP) Then Begin A[adr_delta] = -.BYTES; A[adr_mode] = (If .A[adr_type] Eql ADDR_MEMORY Then AUTODECR Else AUTODECR+DEFERRED); A[adr_disp] = 0 End Else Return TRUE Else If ! THIS BLOCK CHECKS TO SEE IF WE CAN, IN EFFECT, PUT IADD AFTER I, TO ! CHANGE A FROM -N(REG) TO @REG OR -(REG). aaa: Begin NEWOFFSET = .A[adr_disp]; ! re-calculate PUSHPOPFLAG. for SP, it was calculated once in OPT_ADDSUB If .A[adr_reg] Neq SP Then If (.NEWOFFSET Eql -.OFFST And .A[adr_type] Eql ADDR_MEMORY) Or .NEWOFFSET Eql -.OFFST-.BYTES Then PUSHPOPFLAG = -.OFFST; ! check to see if placing an ADD instruction here would upset things If TESTCC(.I,__CVZN) Then Leave aaa With FALSE; ! if not a nice offset If (.NEWOFFSET Neq .PUSHPOPFLAG Or .A[adr_type] Neq ADDR_MEMORY) And .NEWOFFSET Neq .PUSHPOPFLAG-.BYTES Then Leave aaa With FALSE; ! ok for one operand up to here or if we are considering the ! destination operand If .OPERTYPE[.I[cel_code]] Neq OPTYPE_TWO Then Leave aaa With TRUE; If I[cel_dst] Eql .A Then Leave aaa With TRUE; ! ok if source and destination do not conflict ! note: no need for stack conflict here because this is ! going to be a pop rather than a push. If .I[cel_dst_reg] Neq .A[adr_reg] Then Leave aaa With TRUE; ! check for a modification/use of the register. we want to twittle with ! its value and we can't do that if its real value is needed. If .I[cel_dst_type] Eql ADDR_REG Or .I[cel_dst_delta] Neq 0 Then Leave aaa With FALSE; ! MUST CHANGE I[DSTP], SINCE TESTPP WILL NOT. If .A[adr_reg] Eql SP And .I[cel_dst_disp] Lss .NEWOFFSET-2 Then Leave aaa With FALSE; I[cel_dst_disp] = .I[cel_dst_disp] - .A[adr_disp]; If .I[cel_dst_disp] Eql -.BYTES And .I[cel_dst_name] Leq 1 Then Begin I[cel_dst_disp] = 0; I[cel_dst_delta] = -.BYTES; I[cel_dst_mode] = (If .I[cel_dst_type] Eql ADDR_MEMORY Then AUTODECR Else AUTODECR+DEFERRED); NEWOFFSET = .NEWOFFSET - .BYTES End; Leave aaa With TRUE End Then ! IT WORKED! Begin ! similar to code in TESTPOP If .A[adr_disp] Eql .PUSHPOPFLAG Then A[adr_mode] = GENREG+DEFERRED Else Begin A[adr_delta] = -.BYTES; A[adr_mode] = (If .A[adr_type] Eql ADDR_MEMORY Then AUTODECR Else AUTODECR+DEFERRED) End; NEWADD = AFTER(.I,NEWCODECELL()); NEWADD[cel_code] = PADD; NEWADD[cel_class] = INST_ADD_IMMED; COPYADR(NEWADD[cel_dst],IADD[cel_dst]); COPYADR(NEWADD[cel_src],IMMEDZERO); NEWADD[cel_src_disp] = -.NEWOFFSET; BYTES = .PUSHPOPFLAG; A[adr_disp] = 0 End Else Begin If .A[adr_disp] Lss .PUSHPOPFLAG + .BYTES Then PUSHPOPFLAG = .A[adr_disp]; Return FALSE End End; OFFST = .OFFST + .BYTES; IADD[cel_src_disp] = (If .IADD[cel_code] Eql PSUB Then -.OFFST Else .OFFST); ! fix up cells between IADD and I reflecting changes in those two cells. While TRUE Do Begin IADD = .IADD[cel_next]; Selectone .OPERTYPE[.IADD[cel_code]] Of Set [OPTYPE_ONE]: If TESTPP(.A,IADD[cel_src],.BYTES) Then Exitloop; [OPTYPE_TWO]: If TESTPP(.A,IADD[cel_src],.BYTES) Then Exitloop Else If TESTPP(.A,IADD[cel_dst],.BYTES) Then Exitloop Tes End; SETCHANGE; Return TRUE End; ! used in forward scans to adjust an address, accounting for auto-increment ! and auto-decrement Routine UPDATE(A : Ref ADRVARSTR,B : Ref ADRVARSTR) : Novalue = Begin If .A[adr_reg] Eql .B[adr_reg] Then B[adr_disp] = .B[adr_disp] - .A[adr_delta] End; ! used in backward scans to adjust an address, accounting for auto-increment ! and auto-decrement Routine DOWNDATE(A : Ref ADRVARSTR,B : Ref ADRVARSTR) : Novalue = Begin If .A[adr_reg] Eql .B[adr_reg] Then B[adr_disp] = .B[adr_disp] + .A[adr_delta] End; End Eludom