! File: CODE.BLI ! Module CODE= Begin ! ! CODE MODULE ! ------------ ! ! THIS MODULE PRODUCES THE ACTUAL PDP-11 MACHINE CODE. IT IS ! EXECUTED AFTER DELAYING AND TEMP-NAME BINDING. THEREFORE ESSENTIALLY ! ALL RELEVANT INFORMATION IS KNOWN. THE PRIMARY FUNCTION OF 'CODE', ! IN ADDITION TO GENERATING THE CODE, IS TO DO THE (RATHER EXTENSIVE) ! SPECIAL CASE ANALYSIS TO PRODUCE LOCALLY OPTIMAL CODE. THUS, FOR ! EXAMPLE, IT IS CODE THAT DETERMINES THE OPTIMAL SEQUENCE OF MASKING, ! ROTATE AND/OR SHIFT, AND 'SWAB' INSTRUCTIONS TO ISOLATE A SUBFIELD ! AND TO ALIGN IT AT A SPECIFIED LOCATION IN A WORD. ! ! THE MAJOR PORTIONS OF CODE ARE: ! ! 1. CODE/LABEL PLACEMENT ! THESE ROUTINES ACTUALLY EMIT THE CODE AND/OR ! LABELS IN A FORM SUITABLE FOR PROCESSING ! BY THE 'FINAL' MODULE. THE CODE REPRESENTATION ! CREATED BY THE ROUTINES IS A DOUBLY LINKED ! LIST -- ITS PRECISE FORM IS IRRELEVANT TO ! THIS MODULE, HOWEVER. ! ! 2. 'VERY-TEMP' ALLOCATION ! IN A FEW CASES THE FACT THAT AN ADDITIONAL ! TEMPORARY IS NEEDED IS NOT KNOWN UNTIL CODE-GENERATION ! TIME, THUS THE TEMPORARIES WILL NOT HAVE ! BEEN ALLOCATED BY 'TNBIND'. THE NECESSARY ! ROUTINES ARE DUPLICATED FROM TNBIND IN ORDER ! ALLOCATE THESE TEMPS 'ON THE FLY' DURING ! CODE-GENERATION. THE SAME PHILOSOPHY FOR ! ALLOCATION IS USED IN CODE AS IN TNBIND, ! ESPECIALLY WRT. LON'S AND FON'S,AS IN ! TNBIND. THEREFORE THEY WILL NOT BE DISCUSSED ! HERE. ! ! 3. ADDRESS GENERATION ! THIS SET OF ROUTINES, OF WHICH 'GMA' AND ! 'GMA8' ARE THE PRINCIPAL ONES, ARE SERVICE ! ROUTINES WHICH GENERATE THE ADDRESS FORMAT ! NEEDED BY 'FINAL'. THE PARAMETER ! TO EACH OF THESE ROUTINES IS A LEXEME -- ! IT MAY BE A LITERAL LEXEME, ST-LEXEME, OR ! GT-LEXEME. IN EACH CASE THE APPROPRIATE ADDRESS ! IS GENERATED. ! THE DIFFERENCE BETWEEN GMA AND GMA8 IS ONLY ! THAT GMA8 GENERATES THE ADDRESS OF THE HIGH ! BYTE OF THE WORD. ! ! 4. MOVE GENERATION ! THESE ROUTINES, PRIMARILY 'SIMPLEMOVE' AND ! 'GENMOVE', ARE SERVICE ROUTINE WHICH WILL ! GENERATE A DATA MOVE -- IF (AND ONLY IF) ! ONE IS ACTUALLY REQUIRED. SIMPLEMOVE ALWAYS ! MOVES AN ENTIRE WORD. GENMOVE WILL MOVE AN ! ARBITRARY FIELD OF ONE WORD TO AN ARBITRARY ! FIELD OF ANOTHER WORD. GENMOVE MAKES EXTENSIVE ! USE OF THE FIELD-ISOLATION AND ALIGNMENT ! ROUTINES -- SEE BELOW. ! ! 5. FIELD ISOLATION AND ALIGNMENT ! THIS SET OF ROUTINES PROVIDES THE PRIMITIVES ! FOR ISOLATING AN ARBITRARY SUBFIELD OF A ! WORD AND ALIGNING THE FIELD TO AN ARBITRARY ! POSITION IN THE WORD. ! ! 6. COMPARISON GENERATION ! THIS SET OF SERVICE ROUTINES GENERATES ! COMPARISONS BETWEEN ARBITRARY OPERANDS, WHICH ! IN PARTICULAR MAY BE SUBFIELDS OF A WORD, AND ! GENERATES BRANCHES TO SPECIFIED LABELS. THIS ! IS IN GENERAL A SIMPLE TASK, BUT THE SPECIAL ! CASE ANALYSIS IS ESPECIALLY EXTENSIVE HERE. ! ! 7. NODE-SPECIFIC ROUTINES ! CODE GENERATION IS EFFECTED BY AN EXECUTION-ORDER ! TREE WALK AND CALLING NODE-SPECIFIC ROUTINES ! AT EACH NODE. THESE ROUTINES ACTUALLY CONTROL ! THE CODE TO BE GENERATED FOR EACH NODE. THIS ! SECTION CONTAINS THESE ROUTINES. A GOOD EXAMPLE ! OF THE SPECIAL CASE ANALYSIS DONE BY THESE ! ROUTINES MAY BE FOUND BY LOOKING AT 'GID' WHICH ! GENERATES THE CODE FOR INCR/DECR STATEMENTS. ! ! 8. CODE & FREEUNDER ! THE SWITCHING TO THE NODE-SPECIFIC ROUTINES ! IS DONE HERE, AS ELSEWHERE IN THE COMPILER, ! BY SWITCHING THROUGH A 'PLIT'. THE ROUTINE ! 'CODE' ACTS AS THE CENTRAL SWITCHING AGENT. ! AS EACH NODE IS COMPLETED BY CODE THE ROUTINE ! 'FREEUNDER' IS CALLED BY IT TO RELEASE THE ! SPACE FOR THAT PORTION OF THE TREE WHICH IS ! NO LONGER NEEDED. THUS THE SPACE CONSUMED BY ! THE EMITTED CODE IS LARGELY THAT ! RELEASED FROM THE TREE. ! ! 9. CODEDRIVER ! THIS ROUTINE IS CALLED FROM 'GENIT' IN ! DRIVER TO INITIATE THE CODE GENERATION PROCESS. ! IT DOES THE NECESSARY INITIALIZATION AND ! CLEAN-UP. ! Require 'Bliss'; External DYTEMPS; Own REGSCHNGD : Integer, NODVT : Integer, NOVTCNT : Integer, PLSTCNT : Integer, SIGLAB : Ref CELL, SAMETOG : Integer; ! MISC. EXTERNALS FOR CODE ONLY Structure ADRWD[P,S,E] = [8]ADRWD; External ALTERSRCDST : Vector[,Byte], OPBYTES : Vector[,Byte], OPERTYPE : Vector[,Byte]; ! LABEL HANDLING ! -------------- ! allocate a new label cell Routine GENLABEL = Begin LABELNO = .LABELNO + 1; Return NEWLABCELL(LAB_COMP,.LABELNO) End; ! place a place at the current location Routine PLACELABEL(LAB : Ref CELL) : Novalue = Begin NCELL = AFTER(.NCELL,.LAB); NCELL[cel_min_loc] = .LOC End; ! generate a label reference operand ! ! notes: ! the 'OPND_LABEL+8' is a kludge. the type field is only ! three bits and so the '8' is outside of that field and ! so does not affect the field. what it does do is cause ! ISIMMEDIATE to not match the operand as an immediate ! operand. Routine REFLABEL(LAB : Ref CELL,FRM : Ref CELL,OTYPE : Integer) = Begin Local C : Ref CELL; C = GETCELL(CELL_REF,SZ_CELL_REF); C[cel_ref_ef] = .LAB; C[cel_ref_rf] = .FRM; C[cel_class] = .OTYPE; PUSHBOT(.C[cel_ref_ef],.C); Return BUILDOPD(OPND_LABEL+8,RELATIVE,PC,.C) End; ! generate a label address reference operand (used only by code_case) ! ! notes: ! see note above. Routine IMMLAB(LAB : Ref CELL,FRM : Ref CELL,OTYPE : Integer) = Begin Local C : Ref CELL; C = GETCELL(CELL_REF,SZ_CELL_REF); C[cel_ref_ef] = .LAB; C[cel_ref_rf] = .FRM; C[cel_class] = .OTYPE; PUSHBOT(.C[cel_ref_ef],.C); Return BUILDOPD(OPND_LABEL+8,IMMEDIATE,PC,.C) End; ! retrieve a node's label, creating it if necessary Routine NODELABEL(NODE : Ref GT) = Begin If .NODE[gt_label] Eql 0 Then Begin LABELNO = .LABELNO + 1; NODE[gt_label] = NEWLABCELL(LAB_COMP,.LABELNO) End; Return .NODE[gt_label] End; ! retrieve a user label, creating it if necessary Routine USERLABEL(LABST : Ref ST) = Begin If .LABST[st_lab_cell] Eql 0 Then LABST[st_lab_cell] = NEWLABCELL(LAB_USER,.LABST); Return .LABST[st_lab_cell] End; ! LIST-OF-NAME-DESCRIPTORS HANDLING ! ----------------------------------- ! ! notes: ! the list-of-names appears to be a cheap form of CSWO ! and also works for non-storage symbols. Routine SEARCHLL(HEAD : Ref CELL,LABS,LABN : Integer) = Begin Local PTR : Ref CELL; PTR = .HEAD[cel_prev]; While TRUE Do Begin If .LABS Gtr .PTR[cel_sym_name] Then Exitloop; If .LABS Eql .PTR[cel_sym_name] And .LABN Geq .PTR[cel_sym_disp] Then Exitloop; PTR = .PTR[cel_prev] End; If .LABS Eql .PTR[cel_sym_name] And .LABN Eql .PTR[cel_sym_disp] Then Return .PTR Else Return AFTER(.PTR,NEWLABCELL(.LABS,.LABN)) End; ! EMIT CODE INTO INSTRUCTION STREAM ! --------------------------------- Routine PUTADR(ADR : Ref ADRVARSTR,A : ADRWD,BYTES : Integer) : Novalue = Begin Bind IMMEDZERO = ADRPLIT(2,PC,ADDR_IMMED,NAME_NORMAL,0) : ADRVARSTR; Bind ADRTYPE = Uplit Byte( ADDR_REG, ! R ADDR_MEMORY, ! @R ADDR_MEMORY, ! (R)+ ADDR_INDIRECT, ! @(R)+ ADDR_MEMORY, ! -(R) ADDR_INDIRECT, ! @-(R) ADDR_MEMORY, ! n(R) ADDR_INDIRECT, ! @n(R) UNUSED, ! PC UNUSED, ! @PC ADDR_IMMED, ! # ADDR_MEMORY, ! @# UNUSED, ! UNUSED, ! ADDR_MEMORY, ! n(PC) ADDR_INDIRECT) ! @n(PC) : Vector[,Byte]; ! fill in default values COPYADR(.ADR,IMMEDZERO); ! SP modes and indirect modes use two bytes If (.A Eql SP) Or .A Then BYTES = 2; ! copy over the addressing mode and register ADR[adr_mode] = .A; ADR[adr_reg] = .A; ! if the addressing mode takes an operand (e.g. indexed or # or @#) If (.ADR[adr_mode] And 6) Eql INDEXED Or .ADR[adr_reg] Eql PC Then Case .A From LO_OPND_TYPE To HI_OPND_TYPE Of Set [Inrange]: 0; [Outrange]: Punt(999); [OPND_NORMAL]: ! copy the simple displacement and handle the kludge for formal parameters Begin ADR[adr_disp] = .A; If .A<31,1> Then ADR[adr_name_type] = NAME_FORMAL End; [OPND_NAME]: ! copy the complex displacement Begin ADR[adr_disp] = .Block[.A,cel_sym_disp]; ADR[adr_name] = .Block[.A,cel_sym_name] End; [OPND_LABEL]: ! copy the label displacement (adr_disp always 0 for labels) Begin ADR[adr_name_type] = NAME_LABEL; ADR[adr_name] = .A End Tes; ! set adr_name to 1 for uses of SP If .ADR[adr_reg] Eql SP And .ADR[adr_name] Eql 0 And .ADR[adr_mode] Neq GENREG Then ADR[adr_name] = 1; ! for (R)+, adjust the displacement and delta. ditto for -(R). ! ! Q: I thought only FINAL could generate AUTOINCR and AUTODECR modes? If (.ADR[adr_mode] And 6) Eql AUTOINCR And .ADR[adr_reg] Neq PC Then Begin ADR[adr_disp] = -.BYTES; ADR[adr_delta] = .BYTES; End Else If (.ADR[adr_mode] And 6) Eql AUTODECR Then Begin ADR[adr_delta] = -.BYTES End; ! note the address type ADR[adr_type] = .ADRTYPE[.ADR[adr_mode]+ (If .ADR[adr_reg] Eql PC Then 8 Else 0)] End; ! note which registers are modified by an instruction. this is used to determine ! which registers to save on procedure entry/exit Routine CKREGSCHNGD(OP : Integer,S : ADRWD,D : ADRWD) : Novalue = Begin ! NOTER(X) - note a register (X) which is modified ! RS(X) - if X is a general register, note that it is modified ! CR(X) - if X is an auto-incremented/decremented register, note that it is modified Macro NOTER(X) = REGSCHNGD = TRUE %, RS(X) = If .X Eql GENREG Then NOTER(.X) %, CR(X) = If ONEOF(.X,AUTOINCR,AUTOINCR+DEFERRED, AUTODECR,AUTODECR+DEFERRED) Then NOTER(.X) %; Case .OPERTYPE[.OP] From LO_OPTYPE To HI_OPTYPE Of Set [OPTYPE_NOP]: If .OP Eql PIOT Then NOTER(0); [OPTYPE_ONE]: Begin If .ALTERSRCDST[.OP] Then RS(S); CR(S) End; [OPTYPE_TWO]: Begin CR(D); If .ALTERSRCDST[.OP] Then RS(D); CR(S) End; [OPTYPE_BR]: CR(S); [OPTYPE_JSR]: Begin CR(D); NOTER(0); RS(S) End; [OPTYPE_RTS]: RS(S); [OPTYPE_TRAP]: NOTER(0); [OPTYPE_WORD,OPTYPE_CASE]: 0 Tes End; ! place an instruction into a cell Routine PUTCODE(WHERE : Ref CELL,OPT : Integer,OP : Integer,S : ADRWD,D : ADRWD) = Begin Local BYTES : Integer; ! note registers modified CKREGSCHNGD(.OP,.S,.D); ! place the cell after the current one and fill in the cell NCELL = AFTER(.NCELL,.WHERE); LOC = .LOC + 1; NCELL[cel_min_loc] = .LOC; NCELL[cel_class] = .OPT; NCELL[cel_code] = .OP; ! fill in the source and destination operands BYTES = .OPBYTES[.OP]; If HASSOURCE(.OP) Then PUTADR(NCELL[cel_src],.S,.BYTES); If HASDEST(.OP) Then PUTADR(NCELL[cel_dst],.D,.BYTES); ! handle special case operands Case .OPERTYPE[.OP] From LO_OPTYPE To HI_OPTYPE Of Set [Inrange]: 0; [OPTYPE_NOP]: If .OP Eql PINLINE Then NCELL[cel_inl_arg] = .S; [OPTYPE_JSR,OPTYPE_RTS]: NCELL[cel_src_reg] = .S; [OPTYPE_TRAP]: NCELL[cel_src_disp] = .S Tes; Return .NCELL End; Bind OPTYPLIT = UPlit Byte ( INST_INLINE, ! INLINE UNUSED, ! PMOV=1, UNUSED, ! PMOVB=2, UNUSED, ! PCMP=3, UNUSED, ! PCMPB=4, UNUSED, ! PBIT=5, UNUSED, ! PBITB=6, UNUSED, ! PBIC=7, UNUSED, ! PBICB=8, UNUSED, ! PBIS=9, UNUSED, ! PBISB=10, INST_ADD_IMMED, ! PADD=11, INST_ADD_IMMED, ! PSUB=12, UNUSED, ! PCLR=13, UNUSED, ! PCLRB=14, UNUSED, ! PCOM=15, UNUSED, ! PCOMB=16, UNUSED, ! PINC=17, UNUSED, ! PINCB=18, UNUSED, ! PDEC=19, UNUSED, ! PDECB=20, UNUSED, ! PNEG=21, UNUSED, ! PNEGB=22, UNUSED, ! PADC=23, UNUSED, ! PADCB=24, UNUSED, ! PSBC=25, UNUSED, ! PSBCB=26, UNUSED, ! PTST=27, UNUSED, ! PTSTB=28, UNUSED, ! PROR=29, UNUSED, ! PRORB=30, UNUSED, ! PROL=31, UNUSED, ! PROLB=32, UNUSED, ! PASR=33, UNUSED, ! PASRB=34, UNUSED, ! PASL=35, UNUSED, ! PASLB=36, UNUSED, ! PJMP=37, UNUSED, ! PSWAB=38, UNUSED, ! PJSR=39, UNUSED, ! PRTS=40, UNUSED, ! PHALT=41, UNUSED, ! PWAIT=42, UNUSED, ! PRTI=43, UNUSED, ! PIOT=44, UNUSED, ! PRESET=45, UNUSED, ! PEMT=46, UNUSED, ! PTRAP=47, INST_UNCOND_JUMP,! PBR=48, INST_UNCOND_JUMP,! PNBR=49, INST_COND_JUMP, ! PBNE=50, INST_COND_JUMP, ! PBEQ=51, INST_COND_JUMP, ! PBGE=52, INST_COND_JUMP, ! PBLT=53, INST_COND_JUMP, ! PBLE=54, INST_COND_JUMP, ! PBGT=55, INST_COND_JUMP, ! PBPL=56, INST_COND_JUMP, ! PBMI=57, INST_COND_JUMP, ! PBHI=58, INST_COND_JUMP, ! PBLOS=59, INST_COND_JUMP, ! PBVC=60, INST_COND_JUMP, ! PBVS=61, INST_COND_JUMP, ! PBHIS=62, INST_COND_JUMP, ! PBLO=63, UNUSED, ! PNOP=64, UNUSED, ! PCLC=65, UNUSED, ! PCLVC=66, UNUSED, ! PWORD=67, INST_CASE_PARAM,! PCASE=68, UNUSED, ! PMFPI=69, UNUSED, ! PMFPD=70, UNUSED, ! PMTPI=71, UNUSED, ! PMTPD=72, 0,0 ) : Vector[,Byte]; ! test whether a lexeme represents a literal Routine ISLIT(L : Ref GT) = Begin Return .L[rw_literal] And .L[rw_immediate] End; ! test whether an operand represents an immediate value ! ! note: the routine IMMLAB generates an immediate mode ! for labels but uses a hack to guarantee that it ! will not be recognized as an immediate operand ! here. this is why the test here is simply not ! '.ADR EQL IMMEDIATE And .ADR EQL PC' Routine ISIMMEDIATE(ADR : ADRWD) = Begin Bind X = (BUILDOPD(OPND_NORMAL,IMMEDIATE,PC,0)^(-32)); Return .ADR<32,32> Eql X End; ! generate an instruction Routine EMIT(OP : Integer,S : ADRWD,D : ADRWD) : Novalue = Begin Local PCELL : Ref CELL, TYPE : Integer; ! allocate a new code cell PCELL = NEWCODECELL(); ! adjust the operands depending on the instruction type TYPE = .OPTYPLIT[.OP]; Case .TYPE From LO_INST_TYPE To HI_INST_TYPE Of Set [INST_NORMAL]: If .OP Eql PJMP Then TYPE = INST_UNCOND_JUMP; [INST_CASE_PARAM]: Begin D = REFLABEL(.D,.PCELL,.TYPE); S = REFLABEL(.S,.PCELL,.TYPE) End; [INST_INLINE]: 0; [INST_UNCOND_JUMP]: S = REFLABEL(.S,.PCELL,.TYPE); [INST_ADD_IMMED]: If Not ISIMMEDIATE(.S) Then TYPE = UNUSED; [INST_COND_JUMP]: S = REFLABEL(.S,.PCELL,.TYPE) Tes; ! now place the instruction into the cell PUTCODE(.PCELL,.TYPE,.OP,.S,.D) End; ! GET HIGHLY TEMPORARY CELLS WITHIN A NODE LIFE ! --------------------------------------------- Macro ISOKPUSHVT = (.NOVTCNT Eql 0) %, OKTOPDYTEMP = (.NODVT Eql 0 And .DYTEMPS Geq 2) %, OKALLDYTMPS = (.NODVT Eql 0 And .DYTEMPS Geq 4) %; Macro SETNOPUSHVT = NOVTCNT = .NOVTCNT+1 %, RESETPUSHVT = NOVTCNT = .NOVTCNT-1 %, SETNODYNVT = NODVT = .NODVT+1 %, RESETDYNVT = NODVT = .NODVT-1 %; ! TRY THE OPEN VERY TEMPS Routine TRYOPVTEMPS(T : Ref GT) = Begin Incr I From 0 To .VTEMPS[stk_idx] Do If TRYFIT(.T,VTEMPS[stk_item(.I)]) Then SUCCESS; FAIL End; ! OPEN A NEW VERY TEMP Routine TRYCLVTEMPS(T : Ref GT) = Begin Local TX : Ref GT, TR : Ref TNREPR, R : Ref ITEM; PUSHSTK(VTEMPS); R = VTEMPS[stk_item(.VTEMPS[stk_idx])]; OPENLIST(.R); TRYFIT(.T,.R); ! NOTE THIS WILL (SHOULD) ALWAYS WORK TR = .R; Until (TR = .TR[itm_rlink]) Eqla .R Do Begin TX = .TR[tnr_ptr]; TX[tn_code] = BOUND_LOCAL; TX[gt_reg] = SP; TX[gt_mode] = INDEXED; TX[gt_disp] = .VTEMPS[stk_idx] * 2 End End; Routine COPYTNDATA(TN : Ref GT) : Novalue = Begin Local L : Ref TNREPR, P : Ref GT; L = .TN[tn_bind_list]; ! 'L' points to a list header. move to the first element of the list L = .L[itm_rlink]; P = .L[tnr_ptr]; TN[tn_code] = .P[tn_code]; TN[gt_reg] = .P[gt_reg]; TN[gt_mode] = .P[gt_mode]; TN[gt_disp] = .P[gt_disp] End; ! note: TN is bound to a register Routine MARKANDCOPY(TN : Ref GT) : Novalue = Begin Local LST : Ref TNREPR, L : Ref TNREPR, P : Ref GT; ! TN is bound to a newly opened register. this code does the ! same thing TMARK in TNBIND does for registers. LST = .TN[tn_bind_list]; L = .LST[itm_rlink]; P = .L[tnr_ptr]; P[tn_code] = BOUND_REGISTER; P[gt_reg] = (.LST - REGS[0]) / 8; ! size of a REGS entry COPYTNDATA(.TN) End; Routine MAKEPUSHVT(T : Ref GT) : Novalue = Begin If .VTN[gt_dtdelete] Eql DTDONTCARE Then VTN[gt_dtdelete] = .DYTEMPS; T[tn_code] = BOUND_PUSH; T[gt_reg] = SP; T[gt_mode] = INDEXED; T[gt_disp] = -(.MAXLOCALS+.STATICSIZE+.DYTEMPS+2) End; ! allocate a very temp variable Routine VERYTEMP(X : Ref GT) = Begin Local T : Ref GT; T = GETTN(); T[tn_lon_fu] = .LON; T[tn_lon_lu] = .LON; T[tn_fon_fu] = .FON; T[tn_fon_lu] = .FON; ! if X is bound to a register and T can fit in that register If .X Gequ 8 And .X[tn_bind_list] Neqa 0 And TRYFIT(.T,.X[tn_bind_list]) Then COPYTNDATA(.T) ! if an open register is available Else If TRYOPREG(.T) Then COPYTNDATA(.T) ! try the top of stack Else If OKTOPDYTEMP And TRYSPDYTEMP(.T,(.DYTEMPS-2)/2) Then COPYTNDATA(.T) ! try a push Else If ISOKPUSHVT Then MAKEPUSHVT(.T) ! try any dynamic temp Else If OKALLDYTMPS And TRYDYTEMPS(.T) Then COPYTNDATA(.T) ! try a static temp Else If TRYOPSTEMPS(.T) Then COPYTNDATA(.T) ! try opening a new register Else If TRYCLREG(.T) Then MARKANDCOPY(.T) ! try a very temp Else If TRYOPVTEMPS(.T) Then COPYTNDATA(.T) ! create a new very temp Else TRYCLVTEMPS(.T); Return .T End; ! allocate a very temporary register (or top of stack) ! ! called by SHIFT, code_shift, GENMOVE when multiple instructions are ! to be applied to a destination and the destination looks nasty ! (e.g. operand requires an extra word in the instruction). Routine VERYTMPREG = Begin Local T : Ref GT; T = GETTN(); T[tn_lon_fu] = .LON; T[tn_lon_lu] = .LON; T[tn_fon_fu] = .FON; T[tn_fon_lu] = .FON; ! look for an available register If TRYOPREG(.T) Then COPYTNDATA(.T) ! try the top of stack Else If OKTOPDYTEMP And TRYSPDYTEMP(.T,(.DYTEMPS-2)/2) Then COPYTNDATA(.T) ! try creating a new top of stack Else If ISOKPUSHVT Then MAKEPUSHVT(.T) ! oops, no room Else T = 0; Return .T End; ! ROUTINES TO HANDLE GENERATION OF MACHINE ADDRESSING FORMATS ! ------------------------------------------------------------ Macro ALOCAL(X) = BUILDOPD(OPND_NORMAL,INDEXED,SP,XLO(X)) %, DEFER(Z) = ((Z) Or ((DEFERRED)^_POS(gma_mode))) %, INDEXEDBY(R,X,T)= BUILDOPD(T,INDEXED,REGNUM(R),X) %, LOCALOFFSET(ZZZ)= (((.MAXLOCALS+.STATICSIZE+.DYTEMPS) +.ZZZ[gt_disp_16])And %x'ffff') %, NODEFER(Z) = ((Z) And (Not((DEFERRED)^_POS(gma_mode)))) %, NXTBYTEADR(A) = BUMPBYTEADR(A,1) %, PROGCTR = BUILDOPD(OPND_NORMAL,GENREG,PC,UNUSED) %, PUSHED = BUILDOPD(OPND_NORMAL,AUTODECR,SP,UNUSED) %, POPPED = BUILDOPD(OPND_NORMAL,AUTOINCR,SP,UNUSED) %, STACKPTR = BUILDOPD(OPND_NORMAL,GENREG,SP,UNUSED) %, XLO(X) = (((.MAXLOCALS+.STATICSIZE+.DYTEMPS)+X) And %x'ffff') %, XREG(X) = BUILDOPD(OPND_NORMAL,GENREG,X,UNUSED) %, XTOS = BUILDOPD(OPND_NORMAL,DEFERRED,SP,UNUSED) %, ZPOPPED = BUILDOPD(OPND_NORMAL,AUTOINCR,0,UNUSED) %; Forward Routine GMALIT, GMA, ISREG, NOMOVREQD, REGNUM; Macro FLD_K_FORMAL = 1^31 %; ! this is sorta a cheap CREATECSWO which also works for non-storage symbols Routine NAMEDESC(N : Ref ST) = Begin Local L : Integer, B : Ref ST; B = BASESYM(.N); L = .N[gt_disp]-.B[gt_disp]; Return SEARCHLL(.NLHEAD,.B,.L) End; Routine BUMPNAMEDESC(D : Ref CELL,N : Integer) = Begin Return SEARCHLL(.NLHEAD,.D[cel_sym_name],.D[cel_sym_disp]+.N) End; ! only called by the macro NXTBYTEADR() with a value of 1 for N ! called by GMA8, REG8, and CLEARMASK when referencing <8,8> of ! a location Routine BUMPBYTEADR(ADR : ADRWD,N : Integer) = Begin Case .ADR From 0 To 7 Of Set [GENREG,AUTODECR,AUTODECR+DEFERRED]: PUNT(581); ! @R -> n(R) [GENREG+DEFERRED]: Begin ADR = INDEXED; ADR = .N End; ! auto-increment in this situation may only be PC mode ! ! @#X -> @#X+n ! k(R) -> k+n(R) [AUTOINCR,AUTOINCR+DEFERRED,INDEXED]: If .ADR Eql OPND_NORMAL Then ADR = .ADR+.N Else ADR = BUMPNAMEDESC(.ADR,.N); [INDEXED+DEFERRED]: ! this can't be right? the code below should be generated for ! both cases. If .ADR Neq OPND_NORMAL Then ADR = BUMPNAMEDESC(.ADR,.N) Else ! this looks like it could generate worse code than for cases other ! than <8,8>. e.g.: '(.X)<8,8> = 0' would generate: ! ! mov X,R ! clrb 1(R) ! ! whereas the better code might be: ! ! bis #%x'ff00',@X ! ! the worse code may be generated in order to avoid the possibility ! of an alignment fault. Begin Local T : Ref GT; T = VERYTEMP(0); EMIT(PMOV,NODEFER(.ADR),GMA(.T)); If ISREG(.T) Then Return BUILDOPD(OPND_NORMAL,INDEXED,REGNUM(.T),.N); EMIT(PADD,GMALIT(.N),GMA(.T)); Return DEFER(GMA(.T)) End Tes; Return .ADR End; Routine ISIDT(N : Ref GT) = Begin If .N[gt_type] Eql T_NODE Then Return .N[rw_destroyable] Else Return FALSE End; ! called by code_load_node Routine FORGOTTEN(NODE : Ref GT,OP1 : Ref GT) = Begin Local TN : Ref GT; TN = .NODE[gt_reg]; If .TN Lssu 8 Then Return FALSE; If .TN[tn_code] Eql BOUND_NCSE Then Return TRUE; If .TN[tn_request] Neq BIND_MEMORY Then Return FALSE; If .TN[gt_reg] Eql .OP1 Then Return TRUE; If .OP1[gt_type] Neq T_NODE Then Return FALSE; Return FORGOTTEN(.OP1,.OP1[gt_arg1]) End; Routine ISDESTROYABLE(N : Ref GT) = Begin ! if a node, only registers are destroyable Selectone .N[gt_type] Of Set [T_NODE]: Return .N[rw_destroyable] And .N[gt_mode] Eql GENREG; [T_LITERAL]: Return FALSE; ! if a tempname, only if it has expired. [T_TEMPNAME]: Return (.N[tn_lon_lu] Lequ .LON And .N[tn_fon_lu] Lequ .FON); [Otherwise]: Return FALSE Tes End; Routine ISREG(R : Ref GT) = Begin If .R Lssu 8 THEN Return TRUE; Selectone .R[gt_type] Of Set [T_LITERAL]: Return FALSE; [T_VARIABLE]: Begin R = .R[gt_disp]; If .R[st_code] Eql S_REGISTER Then Return TRUE; If .R[st_code] Neq S_LOCAL Or .R[gt_reg] Lssu 8 Then Return FALSE; Return ISREG(.R[gt_reg]) End; [T_TEMPNAME]: Begin If .R[tn_code] Eql BOUND_REGISTER Then Return TRUE; ! Q: when is tn_code zero? If .R[tn_request] Eql BIND_MEMORY Then If .R[tn_code] Eql BOUND_NONE Or .R[tn_code] Eql BOUND_PREFERENCE Then If Not .R[tn_v_lit] Then Return ISREG(.R[gt_reg]); Return FALSE End; [T_NODE]: If .R[gt_mode] Eql GENREG Then Return ISREG(.R[gt_reg]) Else Return FALSE; [Otherwise]: Return FALSE Tes End; ! locate the register index for a node/tempname/symbol ! ! Q: what if NODE is a tempname with the tn_v_lit bit set? Routine REGNUM(R : Ref GT) = Begin While .R Gequ 8 Do R = .R[gt_reg]; Return .R End; ! locate the node/tempname/symbol referencing a register ! ! called by code_load_node ! ! Q: what if NODE is a tempname with the tn_v_lit bit set? Routine LOCDEF(NODE : Ref GT) = Begin While .NODE[gt_reg] Gtru 8 Do NODE = .NODE[gt_reg]; Return .NODE End; ! same as above but handles the tn_v_lit problem above. ! ! called by ISCHEAP Routine LOCDF1(NODE : Ref GT) = Begin While TRUE Do Begin If .NODE[gt_type] Eql T_TEMPNAME And .NODE[tn_v_lit] Then Return .NODE[tn_literal]; If .NODE[gt_reg] Lssu 8 Then Exitloop; NODE = .NODE[gt_reg] End; Return .NODE End; ! called by GMA Routine GETOFFSET(NODE : Ref GT) = Begin Local S : Ref ST; ! if the displacement is not a symbol If Not .NODE[gt_v_symoff] Then Return .NODE[gt_disp_16]; ! get the dispacement symbol S = .NODE[gt_disp]; If .S[st_code] Eql S_LOCAL Then Return LOCALOFFSET(S) Else If .S[st_code] Eql S_FORMAL Then Return LOCALOFFSET(S)+FLD_K_FORMAL Else Return NAMEDESC(.NODE[gt_disp]) End; Routine ISCHEAP(NODE : Ref GT) = Begin Local S : Ref ST; If ISREG(.NODE) Then Return TRUE; NODE = LOCDF1(.NODE); If .NODE[gt_type] Eql T_LITERAL Then Return FALSE; If .NODE[gt_type] Eql T_VARIABLE Then Begin S = .NODE[gt_disp]; If ISSTVAR(S) And .S[gt_mode] Neq GENREG Then Return FALSE End; ! see if it looks like @SP or -(SP) NODE = LOCALOFFSET(NODE); Return .NODE Eql 0 Or .NODE Eql 1^16-2 End; ! called by GMA, GAS, code_gall Routine GMOFF(NODE : Ref GT) = Begin If .NODE[gt_v_symoff] Then Return GMA(MakeVar(.NODE[gt_disp])) Else Return BUILDOPD(OPND_NORMAL,IMMEDIATE,PC,.NODE[gt_disp_16]) End; Routine GMALIT(N : Integer) = Begin Return BUILDOPD(OPND_NORMAL,IMMEDIATE,PC,.N) End; ! ! generate a memory address ! ! input ! 'NODE' is a lexeme for which an address is to be ! generated. It may be a literal, node, symbol, or ! a tempname. 'NODE' includes any state bits. ! The state bits of interest are [rw_immediate] ! and [rw_literal]. ! note: the [gt_type] field is only checked for T_LITERAL. ! for all other types, the [gt_type] field is used. ! several routines take advantage of this by guaranteeing ! that [gt_type] is zero. Routine GMA(NODE : Ref GT)= Begin Local S : Ref GT, L : Integer, TN : Ref GT, T : ADRWD, NMODE : Integer, O : Ref GT, TY : Integer; ! IF NAMING A REGISTER If .NODE Lssu 8 Then Return BUILDOPD(OPND_NORMAL,GENREG,.NODE,0); ! NOW HANDLE LITERAL LEXEMES If .NODE[gt_type] Eql T_LITERAL Then If .NODE[rw_immediate] Then Return BUILDOPD(OPND_NORMAL,IMMEDIATE,PC,.NODE[gt_disp_16]) Else Return BUILDOPD(OPND_NORMAL,ABSOLUTE,PC,.NODE[gt_disp_16]); ! IN ALL REMAINING CASES WE USE THE TYPE FIELD IN THE NODE If .NODE[gt_type] Eql T_TEMPNAME Then Begin ! if a stack reference, generate either -(SP) or n(SP). ! TNBIND has guaranteed that negative offsets (i.e. Gtr 1^15) ! will be only -2. If .NODE[tn_code] Eql BOUND_PUSH Then If LOCALOFFSET(NODE) Gtr 1^15 Then Begin DYTEMPS = .DYTEMPS+2; Return BUILDOPD(OPND_NORMAL,AUTODECR,SP,UNUSED) End Else Return BUILDOPD(OPND_NORMAL,INDEXED,SP,LOCALOFFSET(NODE)); ! this tempname is bound to another lexeme or another tempname. If .NODE[tn_request] Eql BIND_MEMORY Then If .NODE[tn_code] Eql BOUND_NONE Or .NODE[tn_code] Eql BOUND_PREFERENCE Then If .NODE[tn_v_lit] Then Return GMA(.NODE[tn_literal]) Else ! note: the high 32 bits contains the state flags. the [gt_type] field ! is irrevalent as long as it is not T_LITERAL ! ! Q: are [rw_immediate] and [rw_literal] bits being lost here? Return GMA(.NODE[gt_reg]); ! if this tempname is bound to a register If .NODE[tn_code] Eql BOUND_REGISTER Then Return BUILDOPD(OPND_NORMAL,GENREG,.NODE[gt_reg],0); ! use the regsiter and mode in the tempname Return BUILDOPD(OPND_NORMAL,.NODE[gt_mode],.NODE[gt_reg],LOCALOFFSET(NODE)) End; ! GRAPH TABLE CASES If .NODE[gt_type] Eql T_NODE Then Begin ! if delay folded this node to a constant If .NODE[rw_literal] Then ! if a literal used as a value If .NODE[rw_immediate] Then Return BUILDOPD(If .NODE[gt_v_symoff] Then OPND_NAME Else OPND_NORMAL, IMMEDIATE,PC,GETOFFSET(.NODE)) Else ! if a literal used as an address (can it not be a symbol offset also?) Return BUILDOPD(OPND_NORMAL,ABSOLUTE,PC,.NODE[gt_disp_16]); ! get the mode and handle the simple cases 'R' and '@R' NMODE = .NODE[gt_mode]; If .NMODE Eql GENREG Then Return GMA(.NODE[gt_reg]); If .NMODE Eql GENREG+DEFERRED Then Return DEFER(GMA(.NODE[gt_reg])); ! get the offset and determine whether it is absolute or relocatable O = GETOFFSET(.NODE); If Not .NODE[gt_v_symoff] Then TY = OPND_NORMAL Else Begin S = .NODE[gt_disp]; If ONEOF(.S[st_code],S_LOCAL,S_FORMAL) Then TY = OPND_NORMAL Else TY = OPND_NAME End; ! handle '(R)+' and '@(R)+' If (.NMODE And 6) Eql AUTOINCR Then Return BUILDOPD(.TY,If .NODE[rw_immediate] Then IMMEDIATE Else .NMODE, REGNUM(.NODE[gt_reg]),.O); ! at this point, the mode may only be AUTODECR or INDEXED. Since ! only FINAL may generate AUTODECR it is an error if we see it here. If (.NMODE And 6) Neq INDEXED Then PUNT(580); ! for indexed modes, there is always a tempname. TN = .NODE[gt_reg]; ! if the tempname is used as a value ! ! Q: shouldn't [rw_immediate] be passed down? If .NODE[rw_immediate] Then Return GMA(.TN); ! the tempname is being used as an address. ! if a named-cse usage then reference the symbol and defer as necessary If .TN Gequ 8 And .TN[tn_code] Eql BOUND_NCSE Then Begin T = GMA(.TN[gt_reg]); If .NMODE Then T = DEFER(.T); Return .T End; ! if a register reference If ISREG(.TN) Then Return BUILDOPD(.TY,.NMODE,REGNUM(.TN),.O); ! at this point we want an address and what we have is an index register ! and an offset. we need to return the sum of the two and not the ! contents of what they point to. ! ! Q: how can delay let this pass through? ! ! if called from SAMEADDR and friends, just return a unique descriptor If .SAMETOG Neq 0 Then Return -(.TN^32+(3 And .NMODE)^16+.O); T = VERYTEMP(0); EMIT(PMOV,GMA(.TN),GMA(.T)); If ISREG(.T) Then Return BUILDOPD(.TY,.NMODE,REGNUM(.T),.O); EMIT(PADD,GMOFF(.NODE),GMA(.T)); If .NMODE Eql INDEXED+DEFERRED Then EMIT(PMOV,DEFER(GMA(.T)),GMA(.T)); Return DEFER(GMA(.T)) End; ! SYMBOL TABLE CASES ! ! Q: shouldn't [rw_immediate] be passed down? If .NODE[gt_type] Neq T_VARIABLE Then Punt(999); S = .NODE[gt_disp]; Selectone .S[st_code] Of Set [S_REGISTER]: Return GMA(.S[gt_reg]); [S_LOCAL]: ! if bound to a register or a stack location If .S[gt_reg] Gequ 8 Then Return GMA(.S[gt_reg]) Else Return BUILDOPD(OPND_NORMAL,INDEXED,SP,LOCALOFFSET(S)); [S_FORMAL]: ! if bound to a register or a stack location If .S[gt_reg] Gequ 8 Then Return GMA(.S[gt_reg]) Else Return BUILDOPD(OPND_NORMAL,INDEXED,SP,FLD_K_FORMAL+LOCALOFFSET(S)); [S_GLOBAL,S_EXTERNAL]: ! PIC seems to mean that the code may float but variables are fixed. ! this means that absolute addresses must be used for PIC code at ! all times but relative addressing may be used for non-PIC code. Begin L = (If .NODE[rw_immediate] Then IMMEDIATE Else If .swit_pic Then ABSOLUTE Else RELATIVE); Return BUILDOPD(OPND_NAME,.L,PC,NAMEDESC(.S)) End; [S_OWN,S_ROUTINE,S_GBL_ROUTINE,S_FORWARD]: Return BUILDOPD(OPND_NAME, If .NODE[rw_immediate] Then IMMEDIATE Else RELATIVE, PC, NAMEDESC(.S)) Tes; PUNT(580); Return 0 End; ! OF GMA ! compute a memory address, adjusting for high-byte references Routine GMA8(OPND : Ref GT) = Begin Local X : ADRWD; X = GMA(.OPND); If Not ISLIT(.OPND) Then If .OPND[gt_pos] Eql 8 Then X = NXTBYTEADR(.X); Return .X End; ! ROUTINES TO HANDLE GENERAL DATA MOVES ! ------------------------------------- Forward Routine CLEARFIELD : Novalue, MAKEDESTROYABLE, DIAL, ISMOVSELF, SHIFT : Novalue, ISOLATE : Novalue, MASK, GENBITTEST : Novalue, POSITIONIT; Macro PICK8(INSTR,POS,SIZ)= Begin If (POS)+(SIZ) Leq 8 Then %Name(INSTR,B) Else INSTR End %; Routine CHECK08(NODE : Ref GT) = Begin If .NODE[gt_type] Eql T_VARIABLE Then Begin ! Q: how can a symbol start at anything other than zero? NODE = .NODE[gt_disp]; If .NODE[gt_pos] Eql 0 And .NODE[gt_len] Eql 8 Then Return TRUE; Return FALSE End; ! this should work for literals as well If .NODE[gt_type] Neq T_NODE Then Return FALSE; If .NODE[gt_pos] Lss 8 And .NODE[gt_pos] + .NODE[gt_len] Leq 8 Then Return TRUE; Return FALSE End; Routine SIMPLEMOVE(O1 : Ref GT,O2 : Ref GT) : Novalue = Begin Local A1 : ADRWD, A2 : ADRWD; ! return if a move to itself If .O1 Eql .O2 Then Return; A1 = GMA(.O1); A2 = GMA(.O2); If .A1 Eql .A2 Then Return; If ISMOVSELF(.A1,.A2) Then Return; ! if either the source or destination is a byte If CHECK08(.O1) Then EMIT(PMOVB,.A1,.A2) Else If CHECK08(.O2) Then EMIT(PMOVB,.A1,.A2) Else ! move 16 bits EMIT(PMOV,.A1,.A2) End; Routine ISMOVSELF(S : ADRWD,D : ADRWD) = Begin Macro COMBINE(MODE1,MODE2) = (MODE1*8 + MODE2) %; If .S Eql .D Then Return TRUE; If .S Neq .D Then Return FALSE; If .S Neq .D Then Return FALSE; Selectone COMBINE(.S,.D) Of Set [COMBINE(0,0), ! R COMBINE(1,1), ! @R COMBINE(6,6), ! n(R) COMBINE(7,7)]: ! @n(R) Return TRUE; [COMBINE(3,3), ! @#n,@#n COMBINE(6,3), ! n,@#n COMBINE(3,6)]: ! @#n,n Return .S Eql PC; [Otherwise]: Return FALSE Tes End; ! test whether two nodes will generate the same address Routine SAMEADDR(O1 : Ref GT,O2 : Ref GT) = Begin Local SDTD : Integer, A1 : ADRWD, A2 : ADRWD; SDTD = .DYTEMPS; SAMETOG = .SAMETOG+1; A1 = GMA(.O1); A2 = GMA(.O2); DYTEMPS = .SDTD; SAMETOG = .SAMETOG-1; Return ISMOVSELF(.A1,.A2) End; ! same as above but allows for references to different bytes in same word ! to be viewed as the same. Routine SAMEWORD(O1 : Ref GT,O2 : Ref GT) = Begin Local SDTD : Integer, A1 : ADRWD, A2 : ADRWD; SDTD = .DYTEMPS; SAMETOG = .SAMETOG+1; A1 = GMA(.O1) And (-2); A2 = GMA(.O2) And (-2); DYTEMPS = .SDTD; SAMETOG = .SAMETOG-1; Return ISMOVSELF(.A1,.A2) End; ! test whether two pairs overlap Routine OVERLAP(P1 : Integer,S1 : Integer,P2 : Integer,S2 : Integer) = Begin S1 = .S1 + .P1; S2 = .S2 + .P2; Return .S2 Geq .P1 And .P2 Leq .S1 End; ! reference a location, adjusting for references to the high byte of a word. ! ! called by GENMOVE Routine REG8(R : Ref GT) = Begin If .R[gt_pos] Neq 8 Then Return GMA(.R); R = MAKEDESTROYABLE(.R); If ISREG(.R) Then Begin EMIT(PSWAB,GMA(.R),0); Return GMA(.R) End Else Return NXTBYTEADR(GMA(.R)) End; ! called from GENMOVE Routine NOMOVREQD(O1 : Ref GT,O2 : Ref GT)= Begin ! if either is a literal then they both must be the same literal If .O1[gt_type] Eql T_LITERAL Or .O2[gt_type] Eql T_LITERAL Then Return .O1 Eql .O2; ! both must be the same pointer type but allow PFNONE to be the ! same as PF016 ! ! note: this depends on PFNONE and PF016 being zero and one. If .O1[rw_ptr_state] Neq .O2[rw_ptr_state] Then If .O1[rw_ptr_state]+.O2[rw_ptr_state] Gtr PFNONE+PF016 Then Return FALSE; Return SAMEADDR(.O1,.O2) End; ! determine whether a move destination depends on its source. ! ! called from GENMOVE Routine DEPENDON(S : Ref GT,D : Ref GT) = Begin Local RESULT : Boolean, T : ADRWD, E : ADRWD, SDTD : Integer; If ISREG(.D) Then Return REGNUM(.S) Eql REGNUM(.D); SDTD = .DYTEMPS; SAMETOG = .SAMETOG+1; T = GMA(.S); If .T Lss 0 Then T = GMA(.S[gt_reg]); E = GMA(.D); If .E Lss 0 Then E = GMA(.D[gt_reg]); RESULT = (NODEFER(.T) And (-2)) Eql (.E And (-2)); DYTEMPS = .SDTD; SAMETOG = .SAMETOG-1; Return .RESULT End; Routine GENMOVE(O1 : Ref GT,O2 : Ref GT) : Novalue = Begin Local P1 : Integer, P2 : Integer, T : ADRWD, SPC : Boolean, C, X, BS, L, LAB : Ref CELL, POS1 : Integer, POS2 : Integer, SIZE1 : Integer, SIZE2 : Integer, S, CHP : Boolean, XBIT, OP; Bind GMOVPLIT = UPlit Byte( 0,0,1,8,5,1, 0,0,1,8,5,1, 2,2,1,8,5,1, 7,7,6,8,8,6, 3,3,4,8,5,4, 2,2,1,8,5,1) : Vector[,Byte]; ! quick check for move to self If .O1 Eql .O2 Then Return; ! more complicated check for move to self If Not .swit_quick Then If NOMOVREQD(.O1,.O2) Then Return; ! handle all the special cases based on the field size P1 = .O1[rw_ptr_state]; P2 = .O2[rw_ptr_state]; Case .GMOVPLIT[.P1*6+.P2] From 0 To 8 Of Set [0]: ! 0 - FULL WORD TO FULL WORD If ISLIT(.O1) And .O1[gt_disp_16] Eql 0 Then EMIT(PCLR,GMA(.O2),0) Else EMIT(PMOV,GMA(.O1),GMA(.O2)); [1]: ! 1 - MOVE TO <0,8> OR <8,8> FROM <0,16>, <0,8> OR <8,8> ! destination is a register... ! ! note: the PDP-11 has the side effect of sign extending byte moves ! into registers. BLISS doesn't want this side effect and so ! uses a BIC and a BIS to insert the byte. If ISREG(.O2) Then Begin POS2 = .O2[gt_pos]; If ISLIT(.O1) Then Begin T = .O1[gt_disp_16]; If (.T And 255) Neq 255 Then CLEARFIELD(.O2,.POS2,8); If .T Neq 0 Then EMIT(PICK8(PBIS,.POS2,8), GMALIT(.T^.POS2), GMA(.O2)); Return End; ! if a move into itself. e.g. R<0,8> = .R<2,8> T = .O1; If DEPENDON(.O1,.O2) Then Begin T = VERYTEMP(0); SIMPLEMOVE(.O1,.T) End; If .POS2 Eql 8 Or (ISREG(.T) And .P1 Eql PF88) Then T = DIAL(.T,.O1[gt_pos],.O1[gt_len],.POS2,8); CLEARFIELD(.O2,.POS2,8); If .POS2 Eql 0 And Not ISREG(.T) Then Begin EMIT(PBISB,GMA8(.T),GMA(.O2)); Return End; EMIT(PICK8(PBIS,.POS2,8),GMA(.T),GMA(.O2)) End ! move of a literal into a byte of memory Else If ISLIT(.O1) Then If .O1[gt_disp_16] Eql 0 Then EMIT(PCLRB,GMA8(.O2),0) Else EMIT(PMOVB,GMA(.O1),GMA8(.O2)) ! move from the high byte of a register Else If ISREG(.O1) And .O1[gt_pos] Neq 0 Then Begin If Not DEPENDON(.O2,.O1) Then Begin T = GMA(.O1); EMIT(PSWAB,.T,0); EMIT(PMOVB,.T,GMA8(.O2)); ! is this wasted if R is destroyable? EMIT(PSWAB,.T,0) End Else ! something funny like: '.R = .R<8,8>' in which we may not touch R. Begin T = VERYTEMP(0); SIMPLEMOVE(.O1,.T); T = REG8(.T); EMIT(PMOVB,.T,GMA8(.O2)) End End Else EMIT(PMOVB,GMA8(.O1),GMA8(.O2)); [2]: ! 2 - MOVE TO 0,16 FROM 0,8 OR 8,8 Begin If SAMEWORD(.O1,.O2) Then Begin SHIFT(.O1,.O1[gt_pos],0,8); ISOLATE(.O2,0,.O1[gt_len]); ! why not 8? Return End; If DEPENDON(.O1,.O2) Then Begin EMIT(PMOVB,GMA8(.O1),GMA(.O2)); CLEARFIELD(.O2,8,8); Return End; ! something like: M = .R<8,8> If ISREG(.O1) And .O1[gt_pos] Eql 8 Then Begin SIMPLEMOVE(.O1,.O2); EMIT(PCLRB,GMA(.O2),0); EMIT(PSWAB,GMA(.O2),0) End Else Begin EMIT(PCLR,GMA(.O2),0); EMIT(PBISB,GMA8(.O1),GMA(.O2)) End End; [3]: ! 3 - MOVE TO 0,16 FROM ARBITRARY FIELD Begin T = .O2; ! normally, the source is moved to the destination and then shifted ! and isolated. if the destination looks nasty then do the ! work in a very temp and then move that very temp to the destination. ! if no very temps are available then we need to fall back to the ! nasty location. If Not SAMEWORD(.O1,.O2) Then If Not ISCHEAP(.O2) Then If .O1[gt_pos] Neq 0 Then Begin O2 = VERYTMPREG(); If .O2 Eql 0 Then O2 = .T End; SIMPLEMOVE(.O1,.O2); SHIFT(.O2,.O1[gt_pos],0,.O1[gt_len]); ISOLATE(.O2,0,.O1[gt_len]); SIMPLEMOVE(.O2,.T) End; [4]: ! 4 - MOVE TO 0,8 OR 8,8 FROM ARBITRARY FIELD Begin ! if something like: R<0,8> = .R<1,2> then we need an intermediary. T = .O1; If DEPENDON(.O1,.O2) Then Begin T = VERYTEMP(0); SIMPLEMOVE(.O1,.T) End; SPC = ISREG(.O2) And .O2[gt_pos] Eql 8; If .SPC Or .O1[gt_len] Lss 8 Then T = DIAL(.T,.O1[gt_pos],.O1[gt_len],8*.SPC,8) Else T = POSITIONIT(.T,.O1[gt_pos],0,8); If .SPC Then Begin CLEARFIELD(.O2,8,8); EMIT(PBIS,GMA(.T),GMA(.O2)) End Else EMIT(PMOVB,GMA(.T),GMA8(.O2)) End; [5]: ! 5 - ARBITRARY FIELD TO ARBITRARY FIELD Begin BS = TRUE; C = TRUE; POS2 = .O2[gt_pos]; SIZE2 = .O2[gt_len]; If ISLIT(.O1) Then Begin L = .O1[gt_disp_16]; X = MASK(0,.SIZE2,0); L = .L And .X; If .L Eql .X Then ! test if BIC needed C = FALSE; If .L Eql 0 Then ! test if BIS needed BS = FALSE; T = GMALIT(.L^.POS2) End Else Begin If .O1[gt_type] Eql T_TEMPNAME Then Begin POS1 = 0; SIZE1 = 16 End Else Begin POS1 = .O1[gt_pos]; SIZE1 = .O1[gt_len] End; T = GMA(DIAL(.O1,.POS1,.SIZE1,.POS2,.SIZE2)) End; If .C Then CLEARFIELD(.O2,.POS2,.SIZE2); If .BS Then EMIT(PICK8(PBIS,.POS2,.SIZE2),.T,GMA(.O2)) End; [6]: ! 6 - MOVE TO 0,8 OR 8,8 FROM E,1 Begin CHP = FALSE; P1 = .O1[gt_pos]; SPC = (ISREG(.O2) And .O2[gt_pos] Eql 8); If DEPENDON(.O1,.O2) Or (SAMEWORD(.O1,.O2) And OVERLAP(.P1,.O1[gt_len],.O2[gt_pos],.O2[gt_len])) Then Begin T = VERYTEMP(0); If ISCHEAP(.T) And Not .SPC Then Begin CHP = TRUE; S = .O2; O2 = .T End Else Begin SIMPLEMOVE(.O1,.T); O1 = .T End End; ! generate: ! CLRB dst ! BIT #n,src ! BEQ 1$ ! INC dst -or- BIS #256,dst if the high byte of a reg ! 1$: CLEARFIELD(.O2,.O2[gt_pos],.O2[gt_len]); LAB = GENLABEL(); GENBITTEST(.O1,.P1,.LAB); If Not .SPC Then EMIT(PINCB,GMA8(.O2),0) Else EMIT(PBIS,GMALIT(1^8),GMA(.O2)); PLACELABEL(.LAB); If .CHP Then EMIT(PMOVB,GMA(.O2),GMA8(.S)) End; [7]: ! 7 - MOVE TO 0,16 FROM E,1 Begin CHP = FALSE; P1 = .O1[gt_pos]; If SAMEWORD(.O1,.O2) Then Begin If (.P1 Mod 8) Leq 2 Then Begin SHIFT(.O1,.P1,0,1); ISOLATE(.O2,0,1); Return End; CHP = TRUE End Else If DEPENDON(.O1,.O2) Then CHP = TRUE; If .CHP Then Begin T = VERYTEMP(0); If ISCHEAP(.T) Then Begin S = .O2; O2 = .T End Else Begin SIMPLEMOVE(.O1,.T); CHP = FALSE; O1 = .T End End; ! generate: ! CLR dst ! BIT #n,src ! BEQ 1$ ! INC dst ! 1$: EMIT(PCLR,GMA(.O2),0); LAB = GENLABEL(); GENBITTEST(.O1,.P1,.LAB); EMIT(PINC,GMA(.O2),0); PLACELABEL(.LAB); If .CHP Then EMIT(PMOV,GMA(.O2),GMA(.S)) End; [8]: ! 8 - MOVE TO/FROM ARBITRARY ONE BIT FIELD Begin ! if moving a literal, generate: ! ! BIS #n,dst if literal is 1 ! ! BIC #n,dst if literal is 0 If ISLIT(.O1) Then Begin T = .O1[gt_disp_16]; XBIT = GMALIT(MASK(.O2[gt_pos],1,0)); If .O2[gt_pos] + .O2[gt_len] Leq 8 Then OP = (If .T Then PBISB Else PBICB) Else OP = (If .T Then PBIS Else PBIC); EMIT(.OP,.XBIT,GMA(.O2)); Return End; P1 = (If .O1[gt_type] Eql T_TEMPNAME Then 0 Else .O1[gt_pos]); If DEPENDON(.O1,.O2) Or (SAMEWORD(.O1,.O2) And OVERLAP(.P1,.O1[gt_len],.O2[gt_pos],.O2[gt_len])) Then Begin T = VERYTEMP(0); SIMPLEMOVE(.O1,.T); O1 = .T End; CLEARFIELD(.O2,.O2[gt_pos],.O2[gt_len]); LAB = GENLABEL(); GENBITTEST(.O1,.P1,.LAB); POS2 = .O2[gt_pos]; If .POS2 Eql 0 Then EMIT(PINCB,GMA(.O2),0) Else If .POS2 Eql 8 And Not(ISREG(.O2)) Then EMIT(PINCB,GMA8(.O2),0) Else EMIT(PICK8(PBIS,.POS2,1),GMALIT(1^.POS2),GMA(.O2)); PLACELABEL(.LAB); End Tes End; ! OF GENMOVE Macro TTN(Z) = (1^(Z)) %, TTM(Z) = (TTN(Z)-1) %; Routine MAKEDESTROYABLE(XOPND : Ref GT) = Begin Local T : Ref GT; If Not ISDESTROYABLE(.XOPND) Then Begin T = VERYTEMP(.XOPND[gt_reg]); SIMPLEMOVE(.XOPND,.T) End Else If .XOPND[gt_type] Eql T_TEMPNAME Then Return .XOPND Else Begin T = .XOPND[gt_reg]; ! Q: what does this mean? isn't this always a move it itself? If .XOPND[gt_mode] Neq GENREG Then SIMPLEMOVE(.XOPND,.T) End; Return .T End; Bind MASKS = Uplit Word( 0, TTM(1), TTM(2), TTM(3), TTM(4), TTM(5), TTM(6), TTM(7), TTM(8), TTM(9), TTM(10), TTM(11), TTM(12), TTM(13), TTM(14), TTM(15), TTM(16)) : Vector[,Word]; Routine MASK(POS : Integer,SIZ : Integer,COMP : Boolean) = Begin Local X : Integer; X = (.MASKS[.SIZ])^.POS; If .COMP Then X = Not(.X); Return .X And %x'7fffffff' End; Routine CLEARMASK(XOPND : Ref GT,MSK : Integer) : Novalue = Begin Local LOCN : ADRWD; MSK = .MSK And %x'ffff'; If .MSK Eql 0 Then Return; If CHECK08(.XOPND) Then MSK = .MSK And 255; LOCN = GMA(.XOPND); Selectone .MSK Of Set [%x'ffff']: EMIT(PCLR,.LOCN,0); [%x'00ff']: EMIT(PCLRB,.LOCN,0); [%x'ff00']: If Not ISREG(.XOPND) Then EMIT(PCLRB,NXTBYTEADR(.LOCN),0) Else EMIT(If .MSK Leq 255 Then PBICB Else PBIC, GMALIT(.MSK),.LOCN); [Otherwise]: EMIT(If .MSK Leq 255 Then PBICB Else PBIC,GMALIT(.MSK),.LOCN) Tes End; Routine CLEARFIELD(XOPND : Ref GT,P : Integer,S : Integer) : Novalue = Begin CLEARMASK(.XOPND,MASK(.P,.S,0)) End; Routine ISOLATE(XOPND : Ref GT,P : Integer,S : Integer) : Novalue = Begin CLEARMASK(.XOPND,MASK(.P,.S,1)) End; Routine SHIFT(XOPND : Ref GT,XFP : Integer,XTP : Integer,SZ : Integer) : Novalue = Begin Local DIST : Integer, RL : Boolean, SB : Boolean, RB : Boolean, SA : Boolean, SHIFTROT : Boolean, OP : Integer, LAB : Ref CELL, ADR : ADRWD, ADR1 : ADRWD; ! compute the shift distance and return if no shift required DIST = .XTP - .XFP; If .DIST Eql 0 Then Return FALSE; ADR = GMA(.XOPND); ! if shifting the sign bit to 2,3,10, or 11 then generate: ! ! BIC #m,X ; this tests the sign bit! ! BPL 1$ ! COM X l flip all bits ! 1$: If .SZ Eql 1 And .XFP Eql 15 And .ADR Neq 0 Then If ONEOF(.XTP,2,3,4,10,11) Then Begin LAB = GENLABEL(); EMIT(PBIC,GMALIT(1^.XTP),.ADR); EMIT(PBPL,.LAB,0); EMIT(PCOM,.ADR,0); PLACELABEL(.LAB); Return FALSE End; RL = FALSE; SB = FALSE; RB = FALSE; SA = FALSE; SHIFTROT = FALSE; ! if a right shift If .DIST Lss 0 Then Begin RL = TRUE; DIST = -.DIST End; ! for a shift of 13,14, or 15 bits, rotate around the top If .DIST Gtr 12 Then Begin RL = Not .RL; DIST = 17 - .DIST; SHIFTROT = TRUE End; ! if more than 7 bits then use a SWAB If .DIST Gtr 7 Then Begin SB = TRUE; DIST = .DIST-8 End; If .DIST Gtr 4 Then Begin Local A; If .SZ Gtr 8 Then Begin If Not .RL Or .DIST Neq 5 Then Begin RB = TRUE; SHIFTROT = TRUE; If .RL Then SA = 8 - .DIST Else SB = TRUE; DIST = 8-.DIST; RL = Not .RL End End Else Begin A = (If .RL Then 16-(.XFP+.SZ) Else .XFP); SA = Min(.A,8-.DIST); If .SA Eql 0 Then SB = TRUE; DIST = 8-.DIST; RL = Not .RL End End; ADR1 = .ADR; ! if more than one instruction is needed then try to do the work in ! a very temp location if the destination looks to be nasty. if no ! very temp can be found then we have to use the nasty destination. If .SB + (.SA Neq 0) + .DIST + .RB Gtr 1 Then If .ADR Neq 0 Then Begin ADR = VERYTMPREG(); If .ADR Eql 0 Then ADR = .ADR1 Else Begin GENMOVE(.XOPND,.ADR); ADR = GMA(.ADR) End End; ! generate a SWAB if necessary If .SB Then EMIT(PSWAB,.ADR,0); ! generate an RORB If .RB And .RL Then EMIT(PRORB,.ADR,0); OP = (If .RL Then PASR Else PASL); If .SHIFTROT Then OP = .OP + (PROR - PASR); ! ASR TO ROR, ASL TO ROL While (DIST = .DIST-1) Geq 0 Do Begin EMIT(.OP,.ADR,0); If .DIST Eql 0 And .RB And Not .RL Then EMIT(PROLB,.ADR,0); SA = .SA - 1; If .SA Eql 0 Then EMIT(PSWAB,.ADR,0) End; If .ADR Neq .ADR1 Then EMIT(PMOV,.ADR,.ADR1); !TRUE IFF OLD CARRY BIT HAS BEEN ROTATED INTO WORD Return .SHIFTROT And Not .SB End; Routine POSITIONIT(X : Ref GT,XFP : Integer,XTP : Integer,SZ : Integer)= Begin Local T : Ref GT; If .SZ Leq 8 And .XFP Eql 8 And .XTP Eql 0 Then If Not ISIDT(.X) Then If Not ISREG(.X) Then Begin T = VERYTEMP(.X[gt_reg]); EMIT(PMOVB,GMA8(.X),GMA(.T)); Return .T End; T = MAKEDESTROYABLE(.X); SHIFT(.T,.XFP,.XTP,.SZ); Return .T End; Routine DIAL(XOPND : Ref GT,XFP,XFS,XTP,XTS) = Begin Local S : Integer, T : Ref GT; S = Min(.XFS,.XTS); T = POSITIONIT(.XOPND,.XFP,.XTP,.S); ISOLATE(.T,.XTP,.S); Return .T End; Routine ALIGNX(O1 : Ref Vector,O2 : Ref Vector) : Novalue = Begin Local N1 : Ref GT, N2 : Ref GT, X1 : Ref GT, X2 : Ref GT, T1 : Ref GT, T2 : Ref GT, T : Boolean; N1 = .O1[0]; N2 = .O2[0]; If .N1[gt_pos] Gtr .N2[gt_pos] Then (X1 = .N2; X2 = .N1; T = 0) Else (X1 = .N1; X2 = .N2; T = 1); T1 = MAKEDESTROYABLE(.X1); ISOLATE(.T1,.X1[gt_pos],.X1[gt_len]); T2 = DIAL(.X2,.X2[gt_pos],.X2[gt_len],.X1[gt_pos],16); If .T Then (O1[0] = .T1; O2[0] = .T2) Else (O1[0] = .T2; O2[0] = .T1) End; ! TEST-BRANCH SPECIAL CASE ROUTINES ! --------------------------------- Routine GENBITTEST(OPND : Ref GT,IBIT : Integer,FLAB : Ref CELL) : Novalue = Begin Local OPC1 : Integer, OPC2 : Integer, ADR : ADRWD, SPC : Boolean; ! if a branch on a literal, generate either nothing or an unconditional ! branch. If ISLIT(.OPND) Then Begin If (.OPND[gt_disp_16] And (1^.IBIT)) Eql 0 Then EMIT(PBR,.FLAB,0); Return End; SPC = TRUE; ADR = GMA(.OPND); ! special cases are: ! ! bit 15 use TST and BPL ! 7 use TSTB and BPL ! 0 use ROR and BCC ! 14 use ASL and BPL ! 6 use ASLB and BPL ! ! cases 0,6, and 14 only work if the source is destroyable. If .IBIT Eql 15 Then Begin OPC1 = PTST; OPC2 = PBPL End Else If .IBIT Eql 7 Then Begin OPC1 = PTSTB; OPC2 = PBPL End Else If Not ISDESTROYABLE(.OPND) Then SPC = FALSE Else Selectone .IBIT Of Set [0]: Begin OPC1 = PROR; OPC2 = PBHIS End; [14]: Begin OPC1 = PASL; OPC2 = PBPL End; [6]: Begin OPC1 = PASLB; OPC2 = PBPL End; [Otherwise]: SPC = FALSE Tes; If Not .SPC Then Begin OPC1 = PICK8(PBIT,.IBIT,1); EMIT(.OPC1,GMALIT(MASK(.IBIT,1,0)),.ADR); OPC2 = PBEQ End Else EMIT(.OPC1,.ADR,0); EMIT(.OPC2,.FLAB,0) End; Bind RELOPTAB = Uplit Byte( PBGT, PBLE, PBLT, PBGE, PBEQ, PBNE, 0,0,0,0,0, ! not,eqv,and,or,xor PBHI, PBLOS, PBLO, PBHIS, PBEQ, PBNE) : Vector[,Byte]; Bind CRELOP = Uplit Byte( PBNE,PBEQ,PBLE,PBGT,PBGE,PBLT,0,0,PBLO,PBHIS,0,0,PBLOS,PBHI) : Vector[,Byte]; ! translate a signed branch to it corresponding unsigned branch Routine MAPTOUNSIGNED(OP : Integer)= Begin Selectone .OP Of Set [PBGT]: Return PBHI; [PBGE]: Return PBHIS; [PBLT]: Return PBLO; [PBLE]: Return PBLOS; [Otherwise]: Return OP Tes End; Macro CONVERSERELATIONAL(OP)=(.CRELOP[OP - PBNE]) %; Macro REVERSERELATIONAL(OP)=((OP) Xor 1) %; ! generate a branch and compure Routine COMPARE(OP1 : Ref GT,OP2 : Ref GT,RELOP : Integer,LAB : Ref GT) : Novalue = Begin EMIT(PCMP,GMA(.OP1),GMA(.OP2)); EMIT(.RELOP,.LAB,0) End; ! same thing but for bytes. ! ! notes: ! bytes are apparently unsigned while words are signed. Routine COMPAREBYTE(OP1 : Ref GT,OP2 : Ref GT,RELOP : Integer,LAB : Ref GT) : Novalue = Begin EMIT(PCMPB,GMA8(.OP1),GMA8(.OP2)); EMIT(MAPTOUNSIGNED(.RELOP),.LAB,0) End; Routine REALCOMPARE(OP1 : Ref GT,OP2 : Ref GT,RELOP : Integer,LAB : Ref GT) : Novalue = Begin Local T : Ref GT; Bind SSPLIT = UPlit Byte( 0,0,2,2,2,2, 0,0,2,2,2,2, 3,3,1,4,4,1, 3,3,4,4,4,4, 3,3,4,4,4,4, 3,3,1,4,4,1) : Vector[,Byte]; Case .SSPLIT[.OP1[rw_ptr_state]*6+.OP2[rw_ptr_state]] From 0 To 4 Of Set [0]: !0 -- FULL WORD COMPARISON COMPARE(.OP1,.OP2,.RELOP,.LAB); [1]: !1 -- BYTE-BYTE COMPARISON ! if either is the high byte of a register If (ISREG(.OP1) And .OP1[rw_ptr_state] Neq PF08) Or (ISREG(.OP2) And .OP2[rw_ptr_state] Neq PF08) Then Begin ALIGNX(OP1,OP2); COMPARE(.OP1,.OP2,MAPTOUNSIGNED(.RELOP),.LAB) End Else COMPAREBYTE(.OP1,.OP2,.RELOP,.LAB); [2]: !2 -- FULL WORD WITH SOMETHING SMALLER Begin T = DIAL(.OP2,.OP2[gt_pos],.OP2[gt_len],0,16); COMPARE(.OP1,.T,.RELOP,.LAB) End; [3]: !3 -- SOMETHING SMALLER WITH FULL WORD Begin T = DIAL(.OP1,.OP1[gt_pos],.OP1[gt_len],0,16); COMPARE(.T,.OP2,.RELOP,.LAB) End; [4]: !4 -- BOTH ARE FUNNY FIELDS Begin ALIGNX(OP1,OP2); COMPARE(.OP1,.OP2,MAPTOUNSIGNED(.RELOP),.LAB) End Tes End; Routine LITCOMPARE(OP1 : Ref GT,L : Ref GT,RELOP : Integer,LAB : Ref GT) : Novalue = Begin Local T : Integer, T1 : Ref GT, L1 : Ref CELL, CASINDEX : Integer, N : Integer; Bind O1SS = Uplit Byte(0,0,1,2,3,1) : Vector[,Byte]; CASINDEX = .O1SS[.OP1[rw_ptr_state]]; ! adjust if the high byte of a register If .OP1[rw_ptr_state] Eql PF88 Then If ISREG(.OP1) Then CASINDEX = 3; Case .CASINDEX From 0 To 3 Of Set [0]: !0 -- FULL WORD CASES Begin N = (If ISLIT(.L) Then .L[gt_disp_16] Else 999); If .N Eql 0 Then Begin EMIT(PTST,GMA(.OP1),0); EMIT(.RELOP,.LAB,0) End ! try to use 'DEC R' instead of 'CMP R,#1'. ditto for 'CMP R,#-1' Else If Abs(.N) Eql 1 And ISDESTROYABLE(.OP1) Then Begin If .N Eql 1 Then EMIT(PDEC,GMA(.OP1),0) Else EMIT(PINC,GMA(.OP1),0); EMIT(.RELOP,.LAB,0) End Else COMPARE(.OP1,.L,.RELOP,.LAB) End; [1]: !1 -- BYTE CASES Begin N = (If ISLIT(.L) Then .L[gt_disp_16] Else 999); RELOP = MAPTOUNSIGNED(.RELOP); If .N Eql 0 Then Begin EMIT(PTSTB,GMA8(.OP1),0); EMIT(.RELOP,.LAB,0) End Else If Abs(.N) Eql 1 And ISDESTROYABLE(.OP1) Then Begin If .N Eql 1 Then EMIT(PDECB,GMA8(.OP1),0) Else EMIT(PINCB,GMA8(.OP1),0); EMIT(.RELOP,.LAB,0) End Else COMPAREBYTE(.OP1,.L,.RELOP,.LAB) End; [2]: !2 -- ONE BIT CASES Begin T = .L[gt_disp_16]; If .T Eql 0 Or .T Eql 1 Then If .RELOP Eql PBEQ Or .RELOP Eql PBNE Then Begin If (.T Eql 0) Eqv (.RELOP Eql PBEQ) Then GENBITTEST(.OP1,.OP1[gt_pos],.LAB) Else ! produce some bad code which FINAL will clean up for us Begin L1 = GENLABEL(); GENBITTEST(.OP1,.OP1[gt_pos],.L1); EMIT(PBR,.LAB,0); PLACELABEL(.L1) End; Return End; ! handle impossible compare cases. e.g. '.R<0,1> Gtr -4' ! ! Q: what about valid cases like: '.R<0,1> Gtr 0'? ! ! A: apparently they were missed. LEXSYNFLO should check ! for impossible cases and generate warnings about them. If .T Lss 0 Then If .RELOP Eql PBGT Or .RELOP Eql PBGE Then EMIT(PBR,.LAB,0); If .T Gtr 1 Then If .RELOP Eql PBLT Or .RELOP Eql PBLE Then EMIT(PBR,.LAB,0) End; [3]: !3 -- GENERAL SUBFIELD CASES Begin T = .L[gt_disp_16]; If .T Eql 0 Then Begin RELOP = MAPTOUNSIGNED(.RELOP); Selectone .RELOP Of Set [PBHI]: RELOP = PBNE; [PBHIS]: RELOP = PBR; [PBLOS]: RELOP = PBEQ; [PBLO]: Return; Tes; If .RELOP Neq PBR Then EMIT(PICK8(PBIT,.OP1[gt_pos],.OP1[gt_len]), GMA(.OP1), GMALIT(MASK(.OP1[gt_pos],.OP1[gt_len],0))); EMIT(.RELOP,.LAB,0); Return End; If .T Gtr 0 Then Begin If (.T^(.OP1[gt_pos]) Eql ((.T^(.OP1[gt_pos])) And %x'ffff')) Then Begin T1 = MAKEDESTROYABLE(.OP1); ISOLATE(.T1,.OP1[gt_pos],.OP1[gt_len]); COMPARE(.T1,MAkeLit(.T^(.OP1[gt_pos])), MAPTOUNSIGNED(.RELOP),.LAB); Return End End; T1 = DIAL(.OP1,.OP1[gt_pos],.OP1[gt_len],0,16); COMPARE(.T1,.L,.RELOP,.LAB) End Tes End; Routine GENCOMPARE(OP1 : Ref GT,OP2 : Ref GT,OPX : Integer, LABT : Ref GT,LABF : Ref GT,SENSE : Boolean) : Novalue = Begin Local RELOP : Integer; RELOP = .RELOPTAB[.OPX - RELOPBASE]; If .SENSE Then RELOP = CONVERSERELATIONAL(.RELOP); If .LABT Eql 0 Then Begin RELOP = REVERSERELATIONAL(.RELOP); LABT = .LABF; LABF = 0 End; If .OP2[gt_type] Eql T_LITERAL Then LITCOMPARE(.OP1,.OP2,.RELOP,.LABT) Else If .OP1[gt_type] Eql T_LITERAL Then Begin RELOP = CONVERSERELATIONAL(.RELOP); LITCOMPARE(.OP2,.OP1,.RELOP,.LABT) End Else REALCOMPARE(.OP1,.OP2,.RELOP,.LABT); If .LABF Neq 0 Then EMIT(PBR,.LABF,0); End; Macro COMPAREFALSE(X1,X2,NX,XL)= GENCOMPARE(X1,X2,NX,XL,0,0) %; Routine BITLIT(O1 : Ref GT,O2 : Ref GT) : Novalue = Begin Local L : ADRWD, P : Integer, S : Integer; P = .O1[gt_pos]; S = .O1[gt_len]; L = GMALIT(.O2[gt_disp_16]^.P And .MASKS[.P+.S]); EMIT(PICK8(PBIT,.P,.S),.L,GMA(.O1)) End; Routine BITX(O1 : Ref GT,O2 : Ref GT,ENE : Boolean,LAB : Ref GT) : Novalue = Begin Local P : Integer, S : Integer; Bind BITPLIT = Uplit Byte( 0,0,1,2,2,1, 0,0,1,2,2,1, 1,1,1,2,2,1, 2,2,2,2,2,2, 2,2,2,2,2,2, 1,1,1,2,2,1) : Vector[,Byte]; If ISLIT(.O1) Then BITLIT(.O2,.O1) Else If ISLIT(.O2) Then BITLIT(.O1,.O2) Else Case .BITPLIT[.O1[rw_ptr_state]*6+.O2[rw_ptr_state]] From 0 To 2 Of Set [0]: ! 0 - BIT EMIT(PBIT,GMA(.O1),GMA(.O2)); [1]: ! 1 - BITB Begin If .O1[rw_ptr_state] Eql PF88 Then If ISREG(.O1) Then O1 = POSITIONIT(.O1,8,0,8); If .O2[rw_ptr_state] Eql PF88 Then If ISREG(.O2) Then O2 = POSITIONIT(.O2,8,0,8); EMIT(PBITB,GMA8(.O1),GMA8(.O2)) End; [2]: ! 2 - SUBFIELD CASES Begin S = Min(.O1[gt_len],.O2[gt_len]); If .O1[gt_pos] Leq .O2[gt_pos] Then (P = .O1[gt_pos]; O2 = DIAL(.O2,.O2[gt_pos],.O2[gt_len],.P,.S)) Else (P = .O2[gt_pos]; O1 = DIAL(.O1,.O1[gt_pos],.O1[gt_len],.P,.S)); EMIT(PICK8(PBIT,.P,.S),GMA(.O1),GMA(.O2)); End Tes; EMIT(If .ENE Then PBNE Else PBEQ,.LAB,0) End; !!! THE FOLLOWING ROUTINES ARE THE NODE-SPECIFIC CODE GENERATORS !!! -------------------------------------------------------------- Forward Routine CODE : Novalue; Macro ADJUSTSTACK= If .NODE[gt_dtdelete] Neq DTDONTCARE And .NODE[gt_dtdelete] Neq .DYTEMPS Then Begin EMIT(PADD,GMALIT(.DYTEMPS-.NODE[gt_dtdelete]),STACKPTR); DYTEMPS = .NODE[gt_dtdelete] End %, CHECKFORFLOW= (If .NODE[rw_flow] Then GFLOW(.NODE)) %, LASTOPERAND= gt_argv(.NODE[gt_argc]-1) %, PICKTARGET(T,N)= If .NODE[gt_v_tpath] Then (T = .NODE[gt_arg2]; N = .NODE[gt_arg1]) Else (T = .NODE[gt_arg1]; N = .NODE[gt_arg2]) %, STARTFREE(L1,L2)= Begin If Not EMPTY(L1) Or Not EMPTY(L2) Then PLSTCNT = .PLSTCNT-1 End %, STOPFREE(L1,L2)= Begin If Not EMPTY(L1) Or Not EMPTY(L2) Then PLSTCNT = .PLSTCNT+1 End %; Structure GOALWD[P,S,E]=[8]GOALWD; Macro GSIZEF = 0,32,0 %, GPOSF = 32,32,0 %; Macro GOALS(P,S) = ((P)^32 Or (S)) %, FULLWD = GOALS(0,16) %; Routine SETNEWPS(NODE : Ref GT,P : Integer,S : Integer) = Begin NODE[gt_pos] = .P; NODE[gt_len] = .S; If .S Eql 1 Then NODE[rw_ptr_state] = PFE1 Else If .P Eql 0 Then If .S Eql 8 Then NODE[rw_ptr_state] = PF08 Else If .S Eql 16 Then NODE[rw_ptr_state] = PF016 Else NODE[rw_ptr_state] = PFOTHER Else If .P Eql 8 And .S Eql 8 Then NODE[rw_ptr_state] = PF88 Else NODE[rw_ptr_state] = PFOTHER; NODE[rw_ptr_state] = .NODE[rw_ptr_state]; Return .NODE End; Routine GFLOW(NODE : Ref GT) : Novalue = Begin ADJUSTSTACK; GENBITTEST(.NODE,.NODE[gt_pos],NODELABEL(.NODE[gt_lab_f])); EMIT(PBR,NODELABEL(.NODE[gt_lab_t]),0) End; Routine PULSECODE(NODE : Ref GT,P) : Novalue = Begin Local L : Ref GT, LR : Boolean, RFF : Integer, LDT : Integer; RFF = .NODE[rw_real_flow]; If .RFF Eql RFFLOW Then Return; L = .NODE[gt_label]; LR = .NODE[gt_v_lab_req]; LDT = .NODE[gt_dtdelete]; NODE[rw_flow] = FALSE; NODE[gt_label] = 0; NODE[gt_v_lab_req] = FALSE; NODE[gt_dtdelete] = DTDONTCARE; NODE[gt_v_mustgencode] = TRUE; PLSTCNT = .PLSTCNT+1; CODE(NODE,FULLWD); PLSTCNT = .PLSTCNT-1; NODE[gt_v_lab_req] = .LR; NODE[gt_label] = .L; NODE[rw_real_flow] = .RFF; NODE[gt_dtdelete] = .LDT End; Routine GAS(NODE : Ref GT,GOAL : GOALWD,OP) = Begin Local TAR : Ref GT, NTAR : Ref GT, TMP : Ref GT, L : Ref GT, INCORDEC : Boolean; INCORDEC = FALSE; TMP = .NODE[gt_reg]; CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); If .NODE[rw_real_flow] Eql RFNONE Then Return .NODE; PICKTARGET(TAR,NTAR); If .NODE[rc_mov_offset] Then EMIT(PMOV,GMOFF(.NODE),GMA(.TMP)); If .NODE[rc_mov_target] Then GENMOVE(.TAR,.TMP); If .NODE[rc_negate] Then EMIT(PNEG,GMA(.TMP),0); If .NODE[rc_operate] Then Begin If .NTAR[rw_ptr_state] Gtr PF016 Then Begin L = VERYTEMP(.NTAR[gt_reg]); GENMOVE(.NTAR,.L); NTAR = .L End; EMIT(.OP,GMA(.NTAR),GMA(.TMP)) End; ! IF OFFSET IS LITERAL, GET ITS ABSOLUTE VALUE, TO ! TRY TO GET AS MANY INCB'S AND DECB'S AS POSSIBLE. If .NODE[rc_add_offset] Xor .NODE[rc_sub_offset] Then If Not .NODE[gt_v_symoff] And .NODE[gt_disp_16] Lss 0 Then Begin NODE[gt_disp] = -.NODE[gt_disp_16]; NODE[rc_add_offset] = Not .NODE[rc_add_offset]; NODE[rc_sub_offset] = Not .NODE[rc_sub_offset] End; ! NO NEED TO TEST [SYMOFFF] IF THE ABOVE IS TRUE! If .NODE[rc_add_offset] Then If .NODE[gt_disp_16] Eql 1 Then Begin EMIT(PICK8(PINC,.GOAL,.GOAL),GMA(.TMP),0); INCORDEC = TRUE End Else EMIT(PADD,GMOFF(.NODE),GMA(.TMP)); If .NODE[rc_sub_offset] Then If .NODE[gt_disp_16] Eql 1 Then Begin EMIT(PICK8(PDEC,.GOAL,.GOAL),GMA(.TMP),0); INCORDEC = TRUE End Else EMIT(PSUB,GMOFF(.NODE),GMA(.TMP)); CHECKFORFLOW; If .INCORDEC Then If .GOAL Eql GOALS(0,8) Then Return SETNEWPS(.NODE,0,8); Return .NODE End; Routine code_add(NODE : Ref GT,GOAL : GOALWD) = Begin Return GAS(.NODE,.GOAL,PADD) End; Routine code_sub(NODE : Ref GT,GOAL : GOALWD) = Begin Return GAS(.NODE,.GOAL,PSUB) End; Routine code_store(NODE : Ref GT,GOAL : GOALWD) = Begin Local TAR : Ref GT, NTAR : Ref GT, TGOAL : GOALWD; TAR = (If .NODE[gt_v_tpath] Then .NODE[gt_arg2] Else .NODE[gt_arg1]); TGOAL = (If .TAR[gt_type] Eql T_LITERAL Then FULLWD Else GOALS(.TAR[gt_pos],.TAR[gt_len])); CODE(NODE[gt_arg1],If .NODE[gt_v_tpath] Then .TGOAL Else FULLWD); CODE(NODE[gt_arg2],If .NODE[gt_v_tpath] Then FULLWD Else .TGOAL); PICKTARGET(TAR,NTAR); If .TGOAL Neq GOALS(0,16) Then If .NTAR[gt_type] Eql T_LITERAL Or .TGOAL Eql GOALS(.NTAR[gt_pos],.NTAR[gt_len]) Then NODE = SETNEWPS(.NODE,.TGOAL,.TGOAL); If .NODE[rw_real] Then Begin GENMOVE(.NTAR,.NODE[gt_reg]); GENMOVE(.NODE[gt_reg],.TAR) End Else GENMOVE(.NTAR,.TAR); CHECKFORFLOW; Return .NODE End; Routine code_dot(NODE : Ref GT,GOAL : GOALWD) = Begin CODE(NODE[gt_arg1],FULLWD); CHECKFORFLOW; Return .NODE End; Routine code_xnull(NODE : Ref GT,GOAL : GOALWD) = Begin CHECKFORFLOW; Return .NODE End; Routine code_null(NODE : Ref GT,GOAL : GOALWD) = Begin CODE(NODE[gt_arg1],FULLWD); If .NODE[rw_real_flow] Neq RFNONE Then GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]); CHECKFORFLOW; Return .NODE End; Routine code_binop(NODE : Ref GT,GOAL : GOALWD) = Begin CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); CHECKFORFLOW; Return .NODE End; Routine code_load_node(NODE : Ref GT,GOAL : GOALWD) = Begin Macro OFFSETLIT = GMALIT(LOCALOFFSET(OPR)) %; Local X : ADRWD, OPR : Ref GT; CODE(NODE[gt_arg1],FULLWD); OPR = .NODE[gt_arg1]; If .NODE[rw_real_flow] Eql RFNONE Then Return .NODE; If .NODE[rc_mov_target] Then If .OPR[gt_type] Eql T_LITERAL Or .OPR[gt_type] Eql T_NODE Or Not .OPR[rw_immediate] Then GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]) ! could be forgotten if a name-cse which never made it Else If Not FORGOTTEN(.NODE,.NODE[gt_arg1]) Then Begin ! WE HAVE AN ST ENTRY WITH THE IMMF BIT ON OPR = .OPR[gt_disp]; X = GMA(.NODE[gt_reg]); Select .OPR[st_code] Of Set [S_LOCAL]: Begin OPR = LOCDEF(.OPR); ! note that the address of a stack local was taken flg_stack_addr = TRUE; ! if 0(SP) and not -(SP) then just generate a MOV SP,X If LOCALOFFSET(OPR) Eql 0 And .X Neq PUSHED Then EMIT(PMOV,STACKPTR,.X) Else ! otherwise generate a MOV #n,X \ ADD SP,X Begin EMIT(PMOV,OFFSETLIT,.X); EMIT(PADD,STACKPTR,GMA(.NODE[gt_reg])) End End; [S_FORMAL]: Begin EMIT(PMOV,(OFFSETLIT)+FLD_K_FORMAL,.X); EMIT(PADD,STACKPTR,GMA(.NODE[gt_reg])) End; [Otherwise]: EMIT(PMOV,GMA(.OPR),.X) Tes End; ! if a guess at address arithmetic failed. If .NODE[rc_add_offset] Then EMIT(PADD,GMOFF(.NODE),GMA(.NODE[gt_reg])); ! load negated If .NODE[rc_negate] Then EMIT(PICK8(PNEG,.GOAL,.GOAL),GMA(.NODE[gt_reg]),0); ! load complemented If .NODE[rc_complement] Then EMIT(PICK8(PCOM,.GOAL,.GOAL),GMA(.NODE[gt_reg]),0); CHECKFORFLOW; ! note the position and size if it got lost If .GOAL Eql GOALS(0,8) Then If .NODE[rc_negate] Or .NODE[rc_complement] Then Return SETNEWPS(.NODE,0,8); Return .NODE End; Routine code_relop(NODE : Ref GT,GOAL : GOALWD) = Begin Local L : Ref CELL; CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); If .NODE[rw_flow] Then Begin SETNOPUSHVT; ADJUSTSTACK End; Case .NODE[rw_real_flow] From 0 To 3 Of Set [RFNONE]: 0; [RFREAL]: Begin EMIT(PCLR,GMA(.NODE[gt_reg]),0); L = GENLABEL(); GENCOMPARE(.NODE[gt_arg1],.NODE[gt_arg2],.NODE[gt_code],0,.L,.NODE[gt_v_tpath]); EMIT(PINC,GMA(.NODE[gt_reg]),0); PLACELABEL(.L) End; [RFFLOW]: GENCOMPARE(.NODE[gt_arg1], .NODE[gt_arg2], .NODE[gt_code], NODELABEL(.NODE[gt_lab_t]), NODELABEL(.NODE[gt_lab_f]), .NODE[gt_v_tpath]); [RFBOTH]: Begin EMIT(PCLR,GMA(.NODE[gt_reg]),0); GENCOMPARE(.NODE[gt_arg1],.NODE[gt_arg2],.NODE[gt_code],0, NODELABEL(.NODE[gt_lab_f]),.NODE[gt_v_tpath]); EMIT(PINC,GMA(.NODE[gt_reg]),0); EMIT(PBR,NODELABEL(.NODE[gt_lab_t]),0) End Tes; If .NODE[rw_flow] Then RESETPUSHVT; Return .NODE End; Routine code_bit(NODE : Ref GT,GOAL : GOALWD) = Begin Local L : Ref CELL; CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); If .NODE[rw_flow] Then Begin SETNOPUSHVT; ADJUSTSTACK End; Case .NODE[rw_real_flow] From 0 To 3 Of Set [RFNONE]: 0; [RFREAL]: Begin EMIT(PCLR,GMA(.NODE[gt_reg]),0); L = GENLABEL(); BITX(.NODE[gt_arg1],.NODE[gt_arg2],Not .NODE[rw_complemented],.L); EMIT(PINC,GMA(.NODE[gt_reg]),0); PLACELABEL(.L) End; [RFFLOW]: Begin BITX(.NODE[gt_arg1],.NODE[gt_arg2],.NODE[rw_complemented],NODELABEL(.NODE[gt_lab_t])); EMIT(PBR,NODELABEL(.NODE[gt_lab_f]),0) End; [RFBOTH]: Begin EMIT(PCLR,GMA(.NODE[gt_reg]),0); BITX(.NODE[gt_arg1],.NODE[gt_arg2],Not .NODE[rw_complemented], NODELABEL(.NODE[gt_lab_f])); EMIT(PINC,GMA(.NODE[gt_reg]),0); EMIT(PBR,NODELABEL(.NODE[gt_lab_t]),0) End Tes; If .NODE[rw_flow] Then RESETPUSHVT; Return .NODE End; Literal XNT = 0, ! MOVE NON-TARGET TO DISTINCT TEMPORARY CN = 1, ! COMPLEMENT NON-TARGET CT = 2, ! COMPLEMENT TEMP CX = 3, ! COMPLEMENT X (COPY OF NON-TARGET) BISNT = 4, ! BIS NON-TARGET TO TARGET BISXT = 5, ! BIS X TO TARGET BISTX = 6, ! BIS TARGET TO X BICNT = 7, ! BIC NON-TARGET TO TARGET BICXT = 8, ! BIC X TO TARGET BICTX = 9; ! BIC TARGET TO X Routine GBOOL(NODE : Ref GT,GOAL : GOALWD,OPS : Ref Vector,FILL : Boolean) = Begin Local NT : Ref GT, XNT2 : Ref GT, T : Ref GT, X : Ref GT, RPOS : Integer, RSIZ : Integer, TPOS : Integer, TSIZ : Integer; CODE(NODE[gt_arg1],.GOAL); CODE(NODE[gt_arg2],.GOAL); If Not(.NODE[rw_real]) Then Return .NODE; PICKTARGET(T,NT); XNT2 = .NT; If .NODE[rc_mov_target] Then SIMPLEMOVE(.T,.NODE[gt_reg]); ! YES, THIS CAN HAPPEN, E.G. ' #340 AND Not .X ' ! IN WHICH THE TARGET SUBNODE WILL BE THE ' #340 '. If ISLIT(.T) Then Begin TPOS = 0; TSIZ = 16 End Else Begin TPOS = .T[gt_pos]; TSIZ = .T[gt_len] End; If ISLIT(.NT) Then Begin RPOS = Min(.GOAL,.TPOS); RSIZ = .GOAL; If .RPOS Lss .TPOS Then SHIFT(.NODE[gt_reg],.TPOS,.RPOS,Min(.TSIZ,.RSIZ)); If .TSIZ Lss .RSIZ Then ISOLATE(.NODE[gt_reg],.RPOS,.TSIZ); XNT2 = MAkeLit(.NT[gt_disp_16]^.RPOS) End Else Begin If (.TPOS-.GOAL)*(.NT[gt_pos]-.GOAL) Lss 0 Then RPOS = .GOAL Else If Abs(.TPOS-.GOAL) Lss Abs(.NT[gt_pos]-.GOAL) Then RPOS = .TPOS Else RPOS = .NT[gt_pos]; RSIZ = Min(.GOAL,Max(.TSIZ,.NT[gt_len])); If .TPOS Neq .RPOS Then SHIFT(.NODE[gt_reg],.TPOS,.RPOS,Min(.TSIZ,.RSIZ)); If .TSIZ Lss .RSIZ Then ISOLATE(.NODE[gt_reg],.RPOS,.TSIZ); If .NT[gt_pos] Neq .RPOS Then Begin XNT2 = MAKEDESTROYABLE(.XNT2); SHIFT(.XNT2,.NT[gt_pos],.RPOS,Min(.NT[gt_len],.RSIZ)) End; If Not .NODE[rc_mov_target] Then If (.RPOS Or (.RSIZ Mod 8)) Neq 0 Then Begin XNT2 = MAKEDESTROYABLE(.XNT2); ISOLATE(.XNT2,.RPOS,.RSIZ) End; If .NT[gt_len] Lss .RSIZ Then Begin XNT2 = MAKEDESTROYABLE(.XNT2); ISOLATE(.XNT2,.RPOS,.NT[gt_len]) End End; X = 0; Incr I From 0 To .OPS[-1]-1 Do Case .OPS[.I] From 0 To 9 Of Set [XNT]: Begin X = VERYTEMP(0); GENMOVE(.XNT2,.X) End; [CN]: If ISLIT(.XNT2) Then XNT2 = MAkeLit(Not .XNT2[gt_disp_16]) Else Begin XNT2 = MAKEDESTROYABLE(.XNT2); EMIT(PCOM,GMA(.XNT2),0) End; [CT]: EMIT(PICK8(PCOM,.RPOS,.RSIZ),GMA(.NODE[gt_reg]),0); [CX]: EMIT(PCOM,GMA(.X),0); [BISNT]: EMIT(PICK8(PBIS,.RPOS,.RSIZ),GMA(.XNT2),GMA(.NODE[gt_reg])); [BISXT]: EMIT(PBIS,GMA(.X),GMA(.NODE[gt_reg])); [BISTX]: EMIT(PBIS,GMA(.NODE[gt_reg]),GMA(.X)); [BICNT]: EMIT(PICK8(PBIC,.RPOS,.RSIZ),GMA(.XNT2),GMA(.NODE[gt_reg])); [BICXT]: EMIT(PBIC,GMA(.X),GMA(.NODE[gt_reg])); [BICTX]: EMIT(PBIC,GMA(.NODE[gt_reg]),GMA(.X)) Tes; SHIFT(.NODE[gt_reg],.RPOS,.GOAL,.RSIZ); If .GOAL Gtr .RSIZ Then Begin RPOS = .GOAL+.RSIZ; RSIZ = .GOAL-.RSIZ; If .FILL Then EMIT(PICK8(PBIS,.RPOS,.RSIZ), GMALIT(MASK(.RPOS,.RSIZ,0)), GMA(.NODE[gt_reg])) Else CLEARFIELD(.NODE[gt_reg],.RPOS,.RSIZ) End; CHECKFORFLOW; Return SETNEWPS(.NODE,.GOAL,.GOAL) End; Macro PICKCASE= (If .NODE[gt_v_tpath] Then .O1[rw_complemented]*2+.O2[rw_complemented] Else .O2[rw_complemented]*2+.O1[rw_complemented]) %; Routine code_and(NODE : Ref GT,GOAL : GOALWD) = Begin Bind O1 = NODE[gt_arg1] : Ref GT, O2 = NODE[gt_arg2] : Ref GT; Bind OPS = Plit( Plit(CN,BICNT), Plit(CN,BISNT,CT), Plit(BICNT), Plit(BISNT,CT)) : Vector; Return GBOOL(.NODE,.GOAL,.OPS[PICKCASE],0) End; Routine code_or(NODE : Ref GT,GOAL : GOALWD) = Begin Bind O1 = NODE[gt_arg1] : Ref GT, O2 = NODE[gt_arg2] : Ref GT; Bind OPS = Plit( Plit(BISNT), Plit(CT,BISNT), Plit(CN,BISNT), Plit(CN,BICNT,CT) ) : Vector; Return GBOOL(.NODE,.GOAL,.OPS[PICKCASE],0) End; Routine XEFLOW(LOP : Ref GT,ROP : Ref GT,TLAB,FLAB) : Novalue = Begin Local VT : Ref GT, RPOS : Integer; SETNOPUSHVT; RPOS = .ROP[gt_pos]; If (.RPOS+.ROP[gt_len] Leq 8) And (Not ISREG(.ROP)) Then Begin VT = VERYTEMP(.ROP[gt_reg]); SIMPLEMOVE(.ROP,.VT); ROP = .VT End; VT = POSITIONIT(.LOP,.LOP[gt_pos],.RPOS,1); CLEARFIELD(.VT,0,.RPOS); EMIT(PADD,GMA(.ROP),GMA(.VT)); GENBITTEST(.VT,.RPOS,NODELABEL(.FLAB)); EMIT(PBR,NODELABEL(.TLAB),0); RESETPUSHVT End; Routine code_xor(NODE : Ref GT,GOAL : GOALWD) = Begin Bind O1 = NODE[gt_arg1] : Ref GT, O2 = NODE[gt_arg2] : Ref GT; Bind OPS = Plit( Plit(XNT,BICTX,BICNT,BISXT), Plit(XNT,BICTX,BICNT,BISXT,CT), Plit(XNT,BICTX,BICNT,BISXT,CT), Plit(XNT,BICTX,BICNT,BISXT) ) : Vector; If .NODE[rw_real_flow] Neq RFFLOW Then Return GBOOL(.NODE,.GOAL,.OPS[PICKCASE],0) Else Begin CODE(O1,FULLWD); CODE(O2,FULLWD); ADJUSTSTACK; XEFLOW(.O1,.O2,.NODE[gt_lab_t],.NODE[gt_lab_f]); Return .NODE End End; Routine code_eqv(NODE : Ref GT,GOAL : GOALWD) = Begin Bind O1 = NODE[gt_arg1] : Ref GT, O2 = NODE[gt_arg2] : Ref GT; Bind OPS = Plit( Plit(XNT,BICTX,BICNT,BISXT,CT), Plit(XNT,BICTX,BICNT,BISXT), Plit(XNT,BICTX,BICNT,BISXT), Plit(XNT,BICTX,BICNT,BISXT,CT)) : Vector; If .NODE[rw_real_flow] Neq RFFLOW Then Return GBOOL(.NODE,.GOAL,.OPS[PICKCASE],1) Else Begin CODE(O1,FULLWD); CODE(O2,FULLWD); ADJUSTSTACK; XEFLOW(.O1,.O2,.NODE[gt_lab_f],.NODE[gt_lab_t]); Return .NODE End End; Routine code_shift(NODE : Ref GT,GOAL : GOALWD) = Begin Local T : Ref ST, LAB : Ref CELL, NT : Ref GT, S : Integer, AS : Integer, OP : Integer, T1 : Ref GT, GT1 : ADRWD, CHP : Boolean, GMD : Boolean, MSK : Integer; CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); If .NODE[rw_real_flow] Eql RFNONE Then Return .NODE; PICKTARGET(T,NT); If Not ISLIT(.NT) Then PUNT(777); GMD = FALSE; S = .NT[gt_disp_16]; If .T[rw_ptr_state] Leq PF016 Then MSK = 0 Else Begin S = Min(16,.S-.T[gt_pos]); MSK = (Not MASK(.T[gt_pos],.T[gt_len],0)) ^ .S End; OP = (If .S Lss 0 Then PASR Else PASL); AS = Abs(.S); ! if it looks like the shift will take only one or two instructions ! or the destination is cheap then perform the shift in the destination. ! otherwise try to allocate a temporary to do the work in which will be ! later moved to the destination. if no temporary is available then ! use the destination. CHP = FALSE; If .AS Leq 2 Or ISCHEAP(.NODE[gt_reg]) Then Begin T1 = .NODE[gt_reg]; CHP = TRUE End Else Begin T1 = VERYTMPREG(); If .T1 Eql 0 Then T1 = .NODE[gt_reg] Else CHP = TRUE End; ! cases 13,14,15 are handled by rotating around through ! carry. an extra rotate is generated to catch the last ! bit. If .S Geq 13 Then Begin AS = 17 - .S; SIMPLEMOVE(.T,.T1); GT1 = GMA(.T1); While (AS = .AS-1) Geq 0 Do EMIT(PROR,.GT1,0); CLEARMASK(.T1,.MASKS[.S] Or .MSK); GENMOVE(.T1,.NODE[gt_reg]); CHECKFORFLOW; Return .NODE End; ! case 7 is handled by a SWAB to shift the low 8 bits, a ! RORB to set carry to the 9th bit, and a ROR to set the ! result. If .S Eql 7 Then Begin SIMPLEMOVE(.T,.T1); GT1 = GMA(.T1); EMIT(PSWAB,.GT1,0); EMIT(PRORB,.GT1,0); EMIT(PROR,.GT1,0); CLEARMASK(.T1,127 Or .MSK); GENMOVE(.T1,.NODE[gt_reg]); CHECKFORFLOW; Return .NODE End; ! cases 8,9,10,11,12 are handled by generating a SWAB and ! then treating it like a shift of 0,1,2,3, or 4. If .S Geq 8 Then Begin SIMPLEMOVE(.T,.T1); GT1 = GMA(.T1); GMD = TRUE; EMIT(PSWAB,.GT1,0); AS = .AS-8; If (.MSK And Not (255^.AS)) Eql 0 Then EMIT(PCLRB,.GT1,0) Else MSK = .MSK Or 255^.AS End; If .S Leq -7 Or Not .CHP Then If ISREG(.T1) And .S Leq -8 Then Begin SIMPLEMOVE(.T,.T1); GT1 = GMA(.T1); GMD = TRUE; EMIT(PSWAB,.GT1,0); If .MSK Eql 0 Then EMIT(PMOVB,.GT1,.GT1); AS = .AS-8 End ! for nasty right shifts of 4-6 bits or any right shift of ! 7 or more bits, create a small loop to perform the shift. Else If .AS Geq 4 Then Begin If .T1 Eql .NODE[gt_reg] Then T1 = VERYTEMP(0); SIMPLEMOVE(.T,.NODE[gt_reg]); EMIT(PMOV,GMALIT(.AS),GMA(.T1)); LAB = GENLABEL(); PLACELABEL(.LAB); EMIT(.OP,GMA(.NODE[gt_reg]),0); EMIT(PDEC,GMA(.T1),0); EMIT(PBNE,.LAB,0); CLEARMASK(.NODE[gt_reg],.MSK); CHECKFORFLOW; Return .NODE End; If Not .GMD Then Begin SIMPLEMOVE(.T,.T1); GT1 = GMA(.T1) End; ! generate the shifts While (AS = .AS-1) Geq 0 Do EMIT(.OP,.GT1,0); CLEARMASK(.T1,.MSK); ! FOR DISTRIBUTED MULTIPLICATION THAT DIDN'T MAKE IT. ! (e.g. we were guessing this shift was part of an address ! calculation but it wasn't). If .NODE[rc_add_offset] Then EMIT(PADD,GMOFF(.NODE),GMA(.T1)); GENMOVE(.T1,.NODE[gt_reg]); CHECKFORFLOW; Return .NODE End; Routine code_rot(NODE : Ref GT,GOAL : GOALWD) = Begin Local S : Integer, NT : Ref GT, T : Ref GT, OP : Integer; CODE(NODE[gt_arg1],FULLWD); PICKTARGET(T,NT); GENMOVE(.T,.NODE[gt_reg]); If Not ISLIT(.NT) Then PUNT(777); S = .NT[gt_disp_16]; If .S<15,1> Then S = (.S+17) And %x'ffff'; If .S Leq 8 Then OP = PROL Else Begin OP = PROR; S = 17 - .S End; While (S = .S-1) Geq 0 Do EMIT(.OP,GMA(.NODE[gt_reg]),0); CHECKFORFLOW; Return .NODE End; Routine code_max_min(NODE : Ref GT,GOAL : GOALWD) = Begin Local T : Ref GT, N : Ref GT, L : Ref CELL, RELOP : Integer; CODE(NODE[gt_arg1],FULLWD); CODE(NODE[gt_arg2],FULLWD); If .NODE[rw_real_flow] Eql RFNONE Then Return .NODE; PICKTARGET(T,N); RELOP = (If .NODE[gt_code] Eql OP_MIN Then OP_GTR Else OP_LSS); GENMOVE(.T,.NODE[gt_reg]); L = GENLABEL(); GENCOMPARE(.NODE[gt_reg],.N,.RELOP,0,.L,0); GENMOVE(.N,.NODE[gt_reg]); PLACELABEL(.L); CHECKFORFLOW; Return .NODE End; Routine code_swab(NODE : Ref GT,GOAL : GOALWD) = Begin CODE(NODE[gt_arg1],FULLWD); GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]); EMIT(PSWAB,GMA(.NODE[gt_reg]),0); CHECKFORFLOW; Return .NODE End; Routine code_case(NODE : Ref GT,GOAL : GOALWD) = Begin Local SDTD : Integer, T : Ref GT, L : Ref Vector, LX : Ref CELL, LE : Ref CELL, LSG : Ref CELL, CC : Ref CELL, hdr_alpha : Ref GT, hdr_omega : Ref GT; hdr_alpha = .NODE[gt_arg1]; hdr_omega = .NODE[gt_argv(.NODE[gt_argc]-1)]; STOPFREE(.hdr_alpha[que_head],.hdr_omega[que_head]); PULSELIST(PULSECODE,.hdr_alpha[que_head],T_ALPHA,0); CODE(NODE[gt_arg2],FULLWD); SDTD = .DYTEMPS; T = .NODE[gt_arg2]; LE = GENLABEL(); LX = GENLABEL(); If .swit_i_d Then LSG = GENLABEL(); CC = NEWCODECELL(); If ISREG(.T) Then PUTCODE(.CC,UNUSED,PADD, INDEXEDBY(.T,REFLABEL(.LE,.CC,UNUSED),OPND_LABEL),PROGCTR) Else Begin PUTCODE(.CC,UNUSED,PADD,IMMLAB(.LE,.CC,UNUSED),GMA(.T)); EMIT(PADD,DEFER(GMA(.T)),PROGCTR) End; If .swit_i_d Then Begin PLACELABEL(.LSG); EMIT(PWORD,GMALIT(0),0) End; PLACELABEL(.LE); If .swit_i_d Then LE = .LSG; L = GETSPACE(.NODE[gt_argc]); Incr I From 2 To .NODE[gt_argc]-2 Do Begin L[.I] = GENLABEL(); EMIT(PCASE,.L[.I],.LE) End; Incr I From 2 To .NODE[gt_argc]-2 Do Begin PLACELABEL(.L[.I]); DYTEMPS = .SDTD; CODE(NODE[gt_argv(.I)],FULLWD); If .NODE[rw_real] Then Begin SETNOPUSHVT; GENMOVE(.NODE[gt_argv(.I)],.NODE[gt_reg]); RESETPUSHVT End; EMIT(PBR,.LX,0) End; PLACELABEL(.LX); PULSELIST(PULSECODE,.hdr_omega[que_head],T_OMEGA,0); RELEASESPACE(.L,.NODE[gt_argc]); STARTFREE(.hdr_alpha[que_head],.hdr_omega[que_head]); CHECKFORFLOW; Return .NODE End; Macro POPENABLE= EMIT(PMOV,ALOCAL(.K),GMA(.LXSIGR)) %; Routine code_compound(NODE : Ref GT,GOAL : GOALWD) = Begin Incr I From 0 To .NODE[gt_argc]-1 Do CODE(NODE[gt_argv(.I)],FULLWD); If .NODE[gt_v_enable] Then EMIT(PMOV,DEFER(GMA(.LXSIGR)),GMA(.LXSIGR)); CHECKFORFLOW; Return .NODE End; Routine code_gall(NODE : Ref GT,GOAL : GOALWD) = Begin Local LNK : Ref GT, S : Ref GT; SETNODYNVT; SETNOPUSHVT; Incr I From 1 To .NODE[gt_argc]-1 Do CODE(NODE[gt_argv(.I)],FULLWD); LNK = .NODE[gt_arg1]; Case .LNK[st_lnk_type] From LO_LNK_TYPE To HI_LNK_TYPE Of Set [LNK_SPECIAL]: Begin S = .NODE[gt_arg2]; Selectone .S Of Set [.LXHALT]: EMIT(PHALT,0,0); [.LXRESET]: EMIT(PRESETX,0,0); [.LXWAIT]: EMIT(PWAIT,0,0); [.LXNOP]: EMIT(PNOP,0,0) Tes End; [LNK_BLISS]: Begin EMIT(PJSR,PROGCTR,GMA(.NODE[gt_arg2])); If .NODE[gt_disp_16] Neq 0 Then ! FOR DIST MULTIPLICATION. EMIT(PADD,GMOFF(.NODE),GMA(.NODE)) End; [LNK_EMT]: Begin S = .NODE[gt_arg2]; EMIT(PEMT,BUILDOPD(OPND_TRAP,UNUSED,UNUSED,.S[gt_disp_16]),0) End; [LNK_FORTRAN]: 0; ! Not CODED YET [LNK_INTERRUPT]: 0; [LNK_TRAP]: Begin S = .NODE[gt_arg2]; EMIT(PTRAP,BUILDOPD(OPND_TRAP,UNUSED,UNUSED,.S[gt_disp_16]),0) End; [LNK_IOT]: EMIT(PIOT,0,0) Tes; CHECKFORFLOW; RESETPUSHVT; RESETDYNVT; Return .NODE End; Routine code_if(NODE : Ref GT,GOAL : GOALWD) = Begin Macro GENTHEN = .T<0,1> %, GENELSE = .T<1,1> %; Local S : Ref GT, SDTD : Integer, L : Ref CELL, T : Integer, hdr_alpha : Ref GT, hdr_omega : Ref GT; Bind THENPART = NODE[gt_arg3] : Ref GT, ELSEPART = NODE[gt_arg4] : Ref GT; hdr_alpha = .NODE[gt_arg1]; hdr_omega = .NODE[gt_arg5]; STOPFREE(.hdr_alpha[que_head],.hdr_omega[que_head]); PULSELIST(PULSECODE,.hdr_alpha[que_head],T_ALPHA,0); CODE(NODE[gt_arg2],FULLWD); S = .NODE[gt_arg2]; If ISLIT(.S) Then T = (If .S[gt_disp] Then 1 Else 2) Else T = 3; If GENTHEN Then Begin SDTD = .DYTEMPS; CODE(THENPART,FULLWD); If .NODE[rw_real] Then Begin SETNOPUSHVT; GENMOVE(.THENPART,.NODE[gt_reg]); RESETPUSHVT End; L = GENLABEL(); EMIT(PBR,.L,0); DYTEMPS = .SDTD; End Else PLACELABEL(NODELABEL(.THENPART)); If GENELSE Then Begin CODE(ELSEPART,FULLWD); If .NODE[rw_real] Then Begin SETNOPUSHVT; GENMOVE(.ELSEPART,.NODE[gt_reg]); RESETPUSHVT End; End Else PLACELABEL(NODELABEL(.ELSEPART)); If GENTHEN Then PLACELABEL(.L); PULSELIST(PULSECODE,.hdr_omega[que_head],T_OMEGA,0); STARTFREE(.hdr_alpha[que_head],.hdr_omega[que_head]); CHECKFORFLOW; Return .NODE End; Routine code_select(NODE : Ref GT,GOAL : GOALWD) = Begin Local S : Ref GT, TN : Ref GT, FO : Integer, L : Ref CELL, SVLON : Integer, SVFON : Integer, P : Ref GT; Label aaa; TN = .NODE[gt_argv(.NODE[gt_argc]-2)]; S = .NODE[gt_argv(.NODE[gt_argc]-1)]; FO = .S[gt_disp]; CODE(NODE[gt_arg1],FULLWD); If .NODE[rw_real_flow] Neq RFNONE Then If Not .NODE[rc_otherwise] Then EMIT(PMOV,GMALIT(-1),GMA(.NODE[gt_reg])); If .FO Neq 0 Then EMIT(PCLR,GMA(.TN),0); Incr I From 1 To .NODE[gt_argc]-4 By 2 Do aaa: Begin L = GENLABEL(); P = .NODE[gt_argv(.I)]; If .P Eql .sym_otherwise Then Begin If .I Gtr .FO Then Leave aaa; EMIT(PTST,GMA(.TN),0); EMIT(PBGT,.L,0); End Else If .P Neq .sym_always Then Begin CODE(NODE[gt_argv(.I)],FULLWD); SVLON = .LON; SVFON = .FON; P = .NODE[gt_argv(.I)]; If .P[gt_type] Eql T_NODE Then Begin LON = .P[gt_lon]; FON = .P[gt_fon] End; GENCOMPARE(.NODE[gt_arg1],.P,OP_EQL,0,.L,0); LON = .SVLON; FON = .SVFON End; CODE(NODE[gt_argv(.I+1)],FULLWD); If .NODE[rw_real_flow] Neq RFNONE Then Begin SETNOPUSHVT; GENMOVE(.NODE[gt_argv(.I+1)],.NODE[gt_reg]); RESETPUSHVT End; If .I Lss .FO Then EMIT(PADD,GMALIT(1),GMA(.TN)); PLACELABEL(.L) End; CHECKFORFLOW; Return .NODE End; Routine code_movp(NODE : Ref GT,GOAL : GOALWD) = Begin Local L : Ref GT; CODE(NODE[gt_arg1],FULLWD); L = .NODE[gt_arg2]; EMIT(PMFPI+.L[gt_disp],GMA(.NODE[gt_arg1]),0); CHECKFORFLOW; Return .NODE End; Routine code_signal(NODE : Ref GT,GOAL : GOALWD ) = Begin CODE(NODE[gt_arg1],FULLWD); GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]); If .SIGLAB Eql 0 Then Begin SIGLAB = GENLABEL(); PLACELABEL(.SIGLAB); EMIT(PJMP,GMA(.LXSIGL),0) End Else EMIT(PBR,.SIGLAB,0); Return .NODE End; Routine code_enable(NODE : Ref GT,GOAL : GOALWD) = Begin Local N : Ref GT, K : Ref GT, L : Ref CELL, CC : Ref CELL, LEXIT : Ref CELL; Label aaa; N = .NODE[gt_arg1]; K = .NODE[gt_arg2]; EMIT(PMOV,GMALIT(XLO(.K[gt_disp])+6),.NODE[gt_reg]); EMIT(PJSR,PROGCTR,GMA(.LXENAB)); L = GENLABEL(); LEXIT = GENLABEL(); If Not .swit_i_d Then EMIT(PCASE,.LEXIT,.L) Else Begin CC = NEWCODECELL(); PUTCODE(.CC,UNUSED,PJMP,REFLABEL(.LEXIT,.CC,UNUSED),0); REFLABEL(.L,.BRAK1,UNUSED) End; PLACELABEL(.L); If .swit_debug Then Begin LXY612[st_v_listed_external] = TRUE; EMIT(PJSR,PROGCTR,GMA(.LXY612)) End; aaa: Begin Incr I From 1 To .N[gt_argc]-4 By 2 Do Begin If .N[gt_argv(.I)] Eql .sym_always Then Begin CODE(N[gt_argv(.I+1)],FULLWD); Leave aaa End; CODE(N[gt_argv(.I)],FULLWD); L = GENLABEL(); GENCOMPARE(.N[gt_argv(.I)],.LXSIGV,OP_EQL,0,.L,0); CODE(N[gt_argv(.I+1)],FULLWD); PLACELABEL(.L) End; EMIT(PJMP,GMA(.LXSIG1),0) End; PLACELABEL(.LEXIT); Return .NODE End; Routine code_label(NODE : Ref GT,GOAL : GOALWD) = Begin Local LAB : Ref ST; LAB = .NODE[gt_arg2]; LAB[st_lab_cell] = 0; CODE(NODE[gt_arg1],FULLWD); If .NODE[rw_real_flow] Neq RFNONE Then GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]); ADJUSTSTACK; PLACELABEL(USERLABEL(.LAB)); CHECKFORFLOW; Return .NODE End; Routine GLOOP(NODE : Ref GT,XBR : Integer,S : Ref GT) = Begin Local L : Ref CELL, hdr_rho : Ref GT, hdr_chi : Ref GT; Bind tbl = Uplit Byte (4,7,7,7,4,7,6,7,6,7,6,6) : Vector[,Byte]; If ISLIT(S) Then XBR = .XBR + (.S[gt_disp] And 1) Else XBR = .XBR + 2; hdr_rho = .NODE[gt_arg1]; hdr_chi = .NODE[gt_arg2]; STOPFREE(.hdr_rho[que_head],.hdr_chi[que_head]); XBR = .tbl[.XBR]; PULSELIST(PULSECODE,.hdr_rho[que_head],T_RHO,0); PULSELIST(PULSECODE,.hdr_chi[que_head],T_CHI,0); If .XBR<0,1> Then Begin L = GENLABEL(); PLACELABEL(.L) End; If .XBR<2,1> Then CODE(NODE[gt_arg3],FULLWD); If .XBR<1,1> Then CODE(NODE[gt_arg4],FULLWD); If .XBR<0,1> Then EMIT(PBR,.L,0); CODE(NODE[gt_arg5],FULLWD); If .NODE[rw_real_flow] Neq RFNONE Then EMIT(PMOV,GMALIT(-1),GMA(.NODE[gt_reg])); STARTFREE(.hdr_rho[que_head],.hdr_chi[que_head]); CHECKFORFLOW; Return .NODE End; Routine code_while(NODE : Ref GT,GOAL : GOALWD) = Begin Return GLOOP(.NODE,0,.NODE[gt_arg3]) End; Routine code_until(NODE : Ref GT,GOAL : GOALWD) = Begin Return GLOOP(.NODE,3,.NODE[gt_arg3]) End; Routine code_do_while(NODE : Ref GT,GOAL : GOALWD) = Begin Return GLOOP(.NODE,6,.NODE[gt_arg4]) End; Routine code_do_until(NODE : Ref GT,GOAL : GOALWD) = Begin Return GLOOP(.NODE,9,.NODE[gt_arg4]) End; Routine GID(NODE : Ref GT,WHICH : Boolean) = Begin Macro STDTST= Begin EMIT(PCMP,GMA(.NODE[gt_arg1]),GMA(.NODE[gt_arg3])); EMIT(If .WHICH Then PBGE Else PBLE,.L1,0); End %; Local L : Integer, L1 : Ref CELL, L2 : Ref CELL, T : Boolean, T2 : Integer, T3 : Integer, hdr_rho : Ref GT, hdr_chi : Ref GT; hdr_rho = .NODE[gt_arg5]; hdr_chi = .NODE[gt_arg6]; STOPFREE(.hdr_rho[que_head],.hdr_chi[que_head]); L1 = GENLABEL(); L2 = GENLABEL(); T = 1; CODE(NODE[gt_arg2],FULLWD); CODE(NODE[gt_arg3],FULLWD); CODE(NODE[gt_arg4],FULLWD); PULSELIST(PULSECODE,.hdr_rho[que_head],T_RHO,0); PULSELIST(PULSECODE,.hdr_chi[que_head],T_CHI,0); GENMOVE(.NODE[gt_arg2],.NODE[gt_arg1]); If .Block[.NODE[gt_arg3],gt_disp_16] Eql 0 Then EMIT(PTST,GMA(.NODE[gt_arg1]),0); If ISLIT(.NODE[gt_arg2]) Then If ISLIT(.NODE[gt_arg3]) Then Begin T2 = .Block[.NODE[gt_arg2],gt_disp_16]; T3 = .Block[.NODE[gt_arg3],gt_disp_16]; If .WHICH Then (If .T2 Geq .T3 Then T = 0) Else (If .T2 Leq .T3 Then T = 0) End; If .T Then EMIT(PBR,.L2,0); PLACELABEL(.L1); CODE(NODE[gt_arg7],FULLWD); EMIT(If .WHICH Then PSUB Else PADD,GMA(.NODE[gt_arg4]),GMA(.NODE[gt_arg1])); PLACELABEL(.L2); If ISLIT(.NODE[gt_arg3]) Then Begin L = .Block[.NODE[gt_arg3],gt_disp_16]; If Abs(.L) - .WHICH Eql %x'7fff' Then EMIT(PBR,.L1,0) Else If .L Eql 0 Then EMIT(If .WHICH Then PBPL Else PBLE,.L1,0) Else If .WHICH And .L Eql 1 Then EMIT(PBGT,.L1,0) Else If Not .WHICH And .L Eql -1 Then EMIT(PBMI,.L1,0) Else STDTST End Else STDTST; If .NODE[rw_real_flow] Neq RFNONE Then EMIT(PMOV,GMALIT(-1),GMA(.NODE[gt_reg])); STARTFREE(.hdr_rho[que_head],.hdr_chi[que_head]); CHECKFORFLOW; Return .NODE End; Routine code_incr(NODE : Ref GT,GOAL : GOALWD) = Begin Return GID(.NODE,FALSE) End; Routine code_decr(NODE : Ref GT,GOAL : GOALWD) = Begin Return GID(.NODE,TRUE) End; Routine NOTEREGS(RNAME : Ref GT) : Novalue = Begin RNAME[st_var_reg_save] = (.REGSCHNGD And Not .RESERVED) And ((1^6)-1) End; ! in the bliss-11 implementation, there is no frame pointer. ! instead, the stack pointer performs a dual role which is ok ! as long as the coder is careful. ! ! the coder routines know the exact offset from SP for local ! variables at the time of coding. the offset for formal ! parameters depends on knowing the number of dynamic local ! variables there will be and that is not known until the ! while body has been coded. this routines takes an adjustment ! factor (N) and adds it to the offset for all formals. Routine FIXFORMALS(N : Integer,CURS : Ref CELL,E : Ref CELL) = Begin ! routine to adjust the displacement for formal arguments, effectively ! making them like like normal references. Routine FF(A : Ref ST,N : Integer) : Novalue = Begin If .A[adr_name_type] Eql NAME_FORMAL Then Begin A[adr_name_type] = NAME_NORMAL; A[adr_disp] = .A[adr_disp]+.N End End; ! loop for each instruction within the body While (CURS = NXTCC(.CURS)) Neq .E Do ! adjust the operands Case .OPERTYPE[.CURS[cel_code]] From LO_OPTYPE To HI_OPTYPE Of Set [Inrange]: 0; [OPTYPE_ONE]: FF(CURS[cel_src],.N); [OPTYPE_TWO]: Begin FF(CURS[cel_src],.N); FF(CURS[cel_dst],.N) End; [OPTYPE_BR]: FF(CURS[cel_src],.N); [OPTYPE_JSR]: FF(CURS[cel_dst],.N) Tes End; Routine code_body(NODE : Ref GT,GOAL : GOALWD) = Begin Local RNAME : Ref GT, LNKAGE : Ref GT, BNCELL : Ref CELL, ENCELL : Ref CELL, BLOC : Integer, ELOC : Integer, LB : Integer, NR : Integer, LX : Ref CELL, L : Ref LSTHDR, I : Ref ITEM, A : ADRWD, B : ADRWD, LNKTYPE : Integer, HR : Integer, LEX : Ref GT; Own INLINSAVPLIT : Vector[9,Byte] Preset( [0] = 0, [1] = 0, [2] = 1, [3] = 1, [4] = 1, [5] = 1, [6] = 1, [7] = 0, [8] = 0), SAVN : Vector[4,Long] Preset( [0] = LXSAV2, [1] = LXSAV3, [2] = LXSAV4, [3] = LXSAV5); Macro POF2(X) =(((X) And -(X)) Eql (X)) %, INLINSAV =(.swit_debug Or .swit_zip Or POF2(.RNAME[st_var_reg_save])) %; LOC = 100; BNCELL = .NCELL; NR = 0; RNAME = .NODE[gt_arg2]; LNKAGE = .RNAME[st_var_linkage]; LNKTYPE = .LNKAGE[st_lnk_type]; NODE[gt_label] = LX = GENLABEL(); If .RNAME[st_v_unique] Then Begin CODENAME[3] = .RNAME[st_unique]; CODENAME[2] = (If .RNAME[st_code] Eql S_ROUTINE Then .CODENAME[3] Else -.CODENAME[3]) End; CODENAME[0] = Block[.RNAME[st_name],nt_data]; CODENAME[4] = .RNAME; ! load up register parameters I = L = .RNAME[st_var_reg_list]; Until (I = .I[itm_rlink]) Eqla .L Do Begin A = XREG(.I[itm_ldata(1)]); B = GMA(.I[itm_rdata(1)]); If .A Neq .B Then EMIT(PMOV,.A,.B) End; ! code the body of the routine CODE(NODE[gt_arg1],FULLWD); LB = .MAXLOCALS+.STATICSIZE+(.VTEMPS[stk_max]+1)*2; NOTEREGS(.RNAME); ! if this routine produces a result If .LNKTYPE Neq LNK_INTERRUPT Then Begin RNAME[st_var_reg_save] = .RNAME[st_var_reg_save] And (Not(1)); GENMOVE(.NODE[gt_arg1],.NODE[gt_reg]) End; ! place the label If .DYTEMPS Neq 0 Then EMIT(PADD,GMALIT(.DYTEMPS),STACKPTR); PLACELABEL(.LX); ! release locals If .LB Neq 0 Then EMIT(PADD,GMALIT(.LB),STACKPTR); NODE[gt_dtdelete] = .DYTEMPS; ! pop registers If .INLINSAVPLIT[.LNKTYPE] Or INLINSAV Then Begin Decr I From 5 To 0 Do If (.RNAME[st_var_reg_save] And 1^(.I)) Neq 0 Then EMIT(PMOV,POPPED,XREG(.I)) End; ! generate return Case .LNKTYPE From LO_LNK_TYPE To HI_LNK_TYPE Of Set [LNK_SPECIAL]: 0; [LNK_BLISS]: If Not .swit_debug Then EMIT(PRTS,PROGCTR,0) Else Begin LXX612[st_v_listed_external] = TRUE; EMIT(PJMP,GMA(.LXX612),0) End; [LNK_EMT,LNK_INTERRUPT,LNK_TRAP,LNK_IOT]: Begin If .swit_debug Then EMIT(PJSR,PROGCTR,GMA(.LXX612)); EMIT(PRTI,0,0) End; [LNK_FORTRAN]: EMIT(PRTS,XREG(5),0) Tes; ENCELL = .NCELL; ELOC = .LOC; NCELL = .BNCELL; LOC = 0; ! if debug, generate a call to the debugger If .swit_debug Then Begin LXE612[st_v_listed_external] = TRUE; EMIT(PJSR,PROGCTR,GMA(.LXE612)) End; ! if registers saved in-line, generate pushes of the registers If .INLINSAVPLIT[.LNKTYPE] Or INLINSAV Then Begin Incr I From 0 To 5 Do If (.RNAME[st_var_reg_save] And 1^(.I)) Neq 0 Then Begin NR = .NR+1; EMIT(PMOV,XREG(.I),PUSHED) End End Else ! not saved in-line. generate a call to the register save routine Begin HR = FIRSTONE(.RNAME[st_var_reg_save]); NR = .HR+1; LEX = .SAVN[.HR-2]; LEX[st_v_listed_external] = TRUE; EMIT(PJSR,XREG(1),GMA(.LEX)); End; ! allocate locals If .LB Neq 0 Then EMIT(PSUB,GMALIT(.LB),STACKPTR); NODE[gt_label] = 0; ! fix all references to formal arguments NCELL = .ENCELL; LOC = .ELOC; NR = (.NR+.VTEMPS[stk_max]+1)*2; FIXFORMALS(.NR,.BRAK1,.NCELL); Return .NODE End; Routine code_leave(NODE : Ref GT,GOAL : GOALWD) = Begin Local LAB : Ref GT, LABNODE : Ref GT, K : Integer, L : Ref GT; LAB = .NODE[gt_arg2]; LABNODE = .LAB[st_lab_node]; ! code the leave expression CODE(NODE[gt_arg1],FULLWD); ! if the expression is wanted then store it If .LABNODE[rw_real_flow] Neq RFNONE Then GENMOVE(.NODE[gt_arg1],.LABNODE[gt_reg]); ! adjust the stack as necessary If .DYTEMPS Neq .LABNODE[gt_dtdelete] Then EMIT(PADD,GMALIT(.DYTEMPS-.LABNODE[gt_dtdelete]),STACKPTR); ! if inside an enable block, pop any enable's L = .NODE[gt_arg3]; K = .L[gt_disp]; If .K Neq 0 Then POPENABLE; ! generate a branch to the block label EMIT(PBR,USERLABEL(.LAB),0); Return .NODE End; Routine code_return(NODE : Ref GT,GOAL : GOALWD) = Begin Local R : Ref GT, K : Integer, L : Ref GT; R = .NODE[gt_arg2]; R = .R[st_retlab]; CODE(NODE[gt_arg1],FULLWD); If .R[gt_reg] Neq 0 Then GENMOVE(.NODE[gt_arg1],.R[gt_reg]); If .DYTEMPS Neq 0 Then EMIT(PADD,GMALIT(.DYTEMPS),STACKPTR); L = .NODE[gt_arg3]; K = .L[gt_disp]; If .K Neq 0 Then POPENABLE; EMIT(PBR,.R[gt_label],0); Return .NODE End; Routine code_inline(NODE : Ref GT,GOAL : GOALWD) = Begin Bind ILO = BUILDOPD(OPND_INLINE,0,0,0); PUTCODE(NEWCODECELL(),INST_INLINE,0,.NODE[gt_arg1],ILO); NCELL[cel_inl_comment] = .NODE[gt_v_inlinecom]; Return .NODE End; Routine code_error(NODE : Ref GT,GOAL : GOALWD) = Begin PUNT(999); 0 End; !!! THE FOLLOWING ARE THE DRIVERS FOR THE NODE-SPECIFIC GENERATORS !!! ---------------------------------------------------------------- Bind GENPLIT = Uplit Long ( code_add, ! + code_swab, ! SWAB code_binop, ! / code_dot, ! . code_sub, ! - (BINARY) code_binop, ! MOD code_binop, ! * code_dot, ! - (UNARY) code_load_node, ! + (UNARY) code_shift, ! ^ code_bit, ! BIT code_relop, ! GTR code_relop, ! LEQ code_relop, ! LSS code_relop, ! GEQ code_relop, ! EQL code_relop, ! NEQ code_dot, ! NOT, code_eqv, ! EQV code_and, ! AND code_or, ! OR code_xor, ! XOR code_relop, ! GTRU code_relop, ! LEQU code_relop, ! LSSU code_relop, ! GEQU code_relop, ! EQLU code_relop, ! NEQU code_rot, ! ROT code_max_min, ! MAX code_max_min, ! MIN code_error, ! CARRY code_error, ! OVERFLOW code_store, ! = 0, ! ERROR OPERATOR code_case, ! CASE code_null, ! CALL-STORE code_null, ! CALL-PARM code_while, ! WHILE-DO code_until, ! UNTIL-DO code_body, ! ROUTINE DEFN code_compound, ! COMPOUND code_incr, ! INCR code_decr, ! DECR code_if, ! IF code_do_while, ! DO-WHILE code_do_until, ! DO-UNTIL 0, ! CREATE 0, ! EXCHJ code_select, ! SELECT 0, ! EXITLOOP code_label, ! LABEL PLACEMENT 0, ! MODULE 0, ! PLIT code_gall, ! CALL code_dot, ! POINTER 0, ! [ code_leave, ! LEAVE code_return, ! RETURN code_xnull, ! NULL code_inline, ! INLINE code_enable, ! ENABLE code_signal, ! SIGNAL code_movp, ! MFPI, ETC. 0,0,0,0,0,0) : Vector[,Long]; Routine FREEUNDER(NODE : Ref GT,XLON : Integer) = Begin Local L : Ref GT, B : Boolean, A : Ref GT; Label aaa; If .PLSTCNT Gtr 0 Then Return 0; ! only nodes may be freed If .NODE[gt_type] Neq T_NODE Then Return 0; ! loop for each argument Decr I From .NODE[gt_argc]-1 To 0 Do aaa: Begin L = .NODE[gt_argv(.I)]; ! if not already freed If .L[gt_type] Eql T_NODE Then Begin A = 0; B = 0; If .L[gt_code] Eql OP_STORE Then Begin A = .L[gt_reg]; B = (.A Neq 0) End; If .L[gt_code] Eql OP_LOAD_NODE Then Begin A = .L[gt_reg]; If .A Gequ 8 Then If .A[tn_request] Eql BIND_MEMORY And Not .A[tn_v_lit] Then If .A[gt_reg] Gequ 8 Then B = (.Block[.A[gt_reg],gt_type] Eql T_NODE) End; If .B And .A[tn_lon_lu] Gequ .XLON Then Return 0; If FREEUNDER(.L,.XLON) Eql 0 Then Leave aaa Else RELEASESPACE(.L,SZ_NODE(.L[gt_argc])) End Else If .L[gt_type] Eql T_QUEUE Then Begin FreeList(.L[que_head]); RELEASESPACE(.L,SZ_QUEUE) End Else If .L[gt_type] Eql T_LITERAL Or .L[gt_type] Eql T_VARIABLE Then RELEASESPACE(.L,SZ_NODE(0)); NODE[gt_argv(.I)] = NIL End; Return 1 End; Routine CODEWALK(NODE : Ref GT) : Novalue = Begin Local N : Ref GT; If Not .NODE[gt_v_coded] Then Begin N = .NODE[gt_csparent]; If .N Eql .NODE Then Return; If .N[gt_v_coded] Then Return; If Not .N[gt_v_delayed] Then ! N IS A BOGUS NODE UNBOGUS(N); PULSECODE(.N,0) End End; Routine CODE(PNODE : Ref Vector,GOAL : GOALWD) : Novalue = Begin Local NODE : Ref GT, SLON : Integer, SFON : Integer, SVTN : Ref GT, NODE1 : Ref GT; ! only nodes may be coded NODE = .PNODE[0]; If .NODE[gt_type] Neq T_NODE Then Return; ! generate any labels as needed If .NODE[gt_label] Neqa 0 Then PLACELABEL(.NODE[gt_label]) Else If .NODE[gt_v_lab_req] Then PLACELABEL(NODELABEL(.NODE)); ! if code must be generated (e.g. not a cse use) If .NODE[gt_v_mustgencode] Then ! if not already coded If Not .NODE[gt_v_coded] Then Begin ! establish a new LONN/FON and call the node specific coder SLON = .LON; SFON = .FON; SVTN = .VTN; LON = .NODE[gt_lon]; FON = .NODE[gt_fon]; VTN = .NODE; PNODE[0] = NODE = Bliss(.GENPLIT[.NODE[gt_code]],.NODE,.GOAL); ! release any dynamic temps if at a join ADJUSTSTACK; LON = .SLON; FON = .SFON; VTN = .SVTN; ! if we are the CSE parent or our cs-parent has not been coded then ! mark all its cse uses as coded NODE1 = .NODE[gt_csparent]; If .NODE1 Eqla .NODE Or Not .NODE1[gt_v_coded] Then If ISCSECREATION(NODE1) Then ! mark all CSE uses as coded (unless bogus and it must generate code) Do Begin If Not .NODE1[gt_v_delayed] Or Not .NODE1[gt_v_mustgencode] Then NODE1[gt_v_coded] = TRUE End Until (NODE1 = .NODE1[gt_csthread]) Eqla 0; NODE[gt_v_coded] = TRUE End Else ! if already coded but mustgencode set CHECKFORFLOW Else ! if mustgencode is off Begin CODEWALK(.NODE); CHECKFORFLOW End; ! release any nodes below us Selectone .NODE[gt_code] Of Set [OP_STORE]: If .NODE[gt_reg] Eqla 0 Then FREEUNDER(.NODE,.NODE[gt_lon]); [OP_LOAD_NODE,OP_DOT,OP_COMPOUND]: 0; [Otherwise]: FREEUNDER(.NODE,.NODE[gt_lon]) Tes End; Global Routine CODEDRIVER(LEX : Ref GT) : Novalue = Begin Local ENCELL : Ref CELL, T : Integer; ! initialize for code generation CODENAME[0] = .MODNAME; CODENAME[2] = 0; CODENAME[3] = 0; CODENAME[4] = 0; DYTEMPS = 0; LOC = 0; SAMETOG = FALSE; SIGLAB = 0; flg_stack_addr = FALSE; VTEMPS = GETSPACE(10); VTEMPS[stk_idx] = -1; VTEMPS[stk_max] = -1; BRAK1 = NEWCODECELL(); BRAK1[cel_class] = INST_INLINE; NCELL = .BRAK1; NLHEAD = NEWLABCELL(0,0); PLSTCNT = 0; NODVT = 0; NOVTCNT = 0; REGSCHNGD = (If .flg_enable Then %o'77' Else 0); ! code the operand CODE(LEX,FULLWD); PLSTCNT = 0; ! release everything under the node If .LEX[gt_type] Eql T_NODE Then FREEUNDER(.LEX,.LEX[gt_lon]); VTN = .VTEMPS[stk_max]+1; ! and release the node itself If .LEX[gt_type] Eql T_NODE Then RELEASESPACE(.LEX,SZ_NODE(.LEX[gt_argc])); ! if this is the module body If .MODDONE Then Begin ! the module body did not generate any prologue code and so ! generate the prologue here. there is no need to save the ! registers. all that needs to be done is allocate the ! stack space. ENCELL = .NCELL; NCELL = .BRAK1; T = .MAXLOCALS+.STATICSIZE+.VTN*2; If .T Neq 0 Then EMIT(PSUB,GMALIT(.T),STACKPTR); ! and a call to the debugger if this is a main and ! we are debugging If .MAINDECL And .swit_debug Then Begin LXINT612[st_v_listed_external] = TRUE; EMIT(PJSR,PROGCTR,GMA(.LXINT612)) End; NCELL = .ENCELL End; RELEASESPACE(.VTEMPS,10); ! generate a halt at the end of the module body while trying ! to avoid generating extra halts. If .MODDONE And (.BRAK1 Neqa .NCELL Or .MAINDECL) Then EMIT(PHALT,0,0); ! add an end marker to the list of instructions BRAK2 = PUTCODE(NEWCODECELL(),INST_INLINE,0,0,0); RELALLTNS() ! RELEASE ALL TNS End; End Eludom