! 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