SECTION "CGC"

GET "b.CGheader"

STATIC {
   // Version of 14 Apr 86 14:00:11
   dummy = VersionMark;
   version = 1*256+19 };

/* 1.6	01 Nov 85 13:12:03
      Recognise that after  CMP .., #0; BNE .. a register contains 0
      Bug fix: x op:= .. must flush pending stores for x
   1.7	 04 Nov 85 15:24:43
      Load of register for indirect store may be deferred
   1.8	 14 Jan 86 15:56:12
      Bug fix: Lock base register in op:=
   1.9	 30 Jan 86 11:14:35
      Bug fix: CGRelopX not to leave a register marked as containing 0
      if it hasn't compiled a compare.
   1.10  06 Feb 86 17:10:10
      Incorporation of floating point (not in op:= though)
   1.11  18 Feb 86 11:19:54
      Byte selectors mended to get the right byte, not 3- it.
      Storing of non-constant to general field corrected.
   1.12  25 Feb 86 12:33:01
      Improvement of code for field update where there is no indirection
   1.13  26 Feb 86 11:41:46
      Improvement of code for stind & op:=
   1.14
      Bug fix: SlctSt did not check for indirection before considering
      the type of the selected thing.
   1.15  06 Mar 86 12:57:32
     CGSlctSt did not lock its base.
   1.16  07 Mar 86 21:30:10
     k.static
   1.17  16 Mar 86 22:36:16
     Improvements in coding of Condition (field : constant) and
     Field update (value also a field).
   1.18  21 Mar 86 11:08:57
     Deferred shifts
   1.19  14 Apr 86 13:59:28
     Stind with locals safe etc need not force the stored register to be
     loaded
*/

STATIC {
   regTestedAgainstZero = 0 }

MANIFEST {
   k.isafr = 256 };

LET CGStind() BE {
   LET r = h1!arg2=k.freg ->
	     MoveToAnyFR(arg2)+k.isafr,
	     MoveToAnyCRSomeTimeForStoreTo(arg2, ShiftedUp, arg1);
   StindOrAssOp(-1, r) }

AND CGAssOp() BE {
   LET r = -1;
   LET f = ?;
   op := ReadOp();
   f := VALOF SWITCHON op INTO {
      CASE s.plus:  RESULTIS CGPlus
      CASE s.minus: RESULTIS CGMinus
      CASE s.mult:  r := ArgumentRegister(1); RESULTIS CGMult
      CASE s.div:   r := ArgumentRegister(1); RESULTIS CGDiv
      CASE s.rem:   r := ArgumentRegister(1); RESULTIS CGRem
      CASE s.logand:
      CASE s.logor:
      CASE s.eqv:
      CASE s.neqv:  RESULTIS CGLogop
      CASE s.fplus:
      CASE s.fminus:
      CASE s.fmult:
      CASE s.fdiv:  RESULTIS CGFop };
   StindOrAssOp(f, r) }

AND StindOrAssOp(x, r) BE {
   LET s, m, a = 0, 0, 0;
   LET t, ind, n, k = h1!arg1, h2!arg1, h3!arg1, 4*h4!arg1;
   LET simpleSLoc = FALSE;
   LET ss = ssp-2;

   IF k.isafr>r>=0 THEN Lock(r, k.reg);

   TEST (k.lvloc<=t<=k.lvlab) & ind<0 THEN
      TEST t=k.lvlab THEN {
	 s := CheckRLLoaded()
	 m, a := n, k }
      ELSE {
	 t, n := t-k.lvloc+k.loc, n+k/4;
	 h1!arg1, h2!arg1, h3!arg1, h4!arg1 := t, -1, n, 0;
	 simpleSLoc := TRUE }

   ELSE TEST ind<0 & TurnIndIntoSLoc(arg1) THEN {
      t, n := h1!arg1, h3!arg1;
      simpleSLoc := TRUE }

   ELSE {
      TEST ind<0 & t~=k.shreg THEN {
	 a := k; h4!arg1 := 0 }
      ELSE
	 MoveToAnyR(arg1);
      s := LoadAddress(arg1);
      Lock(s, k.reg);
      TEST localsSafe THEN {
	 FlushPendingLoadsExceptLocals();
	 FlushPendingStoresExceptLocalsAndIregs() }
      ELSE {
	 FlushPendingLoads();
	 FlushPendingStores() } };

   TEST x>=0 THEN {
      IF ~simpleSLoc THEN
	 TEST x=CGFop THEN {
	    r := NextFR();
	    GenCPDT(f.ld, r, s, m, a);
	    Stack(ssp-1);
	    Load(k.freg, r) }
	 ELSE {
	    TEST r<0 THEN
	       r := NextR()
	    ELSE
	       FreeReg(r);
	    GenOpToStore(f.ldr, r, s, m, a);
	    FlushPendingLoadsForReg(r);
	    Stack(ssp-1);
	    Load(k.reg, r) };

      SwapSS(arg1, arg2);
      x(op, FALSE);
      r := h3!arg1 }

   ELSE Stack(ssp-1);

   TEST simpleSLoc THEN {
      LET rt = h1!arg1;
      GenStore(t, n);
      IF rt=k.reg & r<k.isafr THEN Unlock(r, k.reg) }
   ELSE TEST x=CGFop | r>=k.isafr THEN
      GenCPDT(f.st, r&15, s, m, a)
   ELSE TEST localsSafe & t=k.loc & ind<0 THEN {
      MoveToRSomeTime(r, arg1);
      AddToPendingStores(r, k.ireg, s, FALSE, a/4) }
   ELSE {
      MoveToR(r, arg1);
      GenF2(f.str, r, s, m, a) };

   Unlock(s, k.reg);
   IF ~simpleSLoc THEN {
      TEST LocalsSafe THEN
	 DiscardNonLocalRegs()
      ELSE
	 DiscardNonConstRegs();
      Stack(ss) } }

AND CgStcar(n, vacar) BE {
   LET ind, k = h2!arg1, h4!arg1;
   LET r = ?;
   LET s = 0;
   TEST h1!arg2=k.freg THEN
      r := MoveToAnyFR(arg2)+k.isafr
   ELSE {
      r := MoveToAnyCRSomeTime(arg2);
      Lock(r, k.reg) };
   TEST ind<0 & h1!arg1~=k.shreg THEN
      n, h4!arg1 := n+k, 0
   ELSE
      MoveToAnyR(arg1);

   s := (vacar -> MoveToAnyCR(arg1),
		  LoadCarAdd(arg1));
   TEST r>=k.isafr THEN {
      FlushPendingLoadsExceptLocals();
      FlushPendingStoresExceptLocalsAndIregs();
      GenCPDT(f.st, r&15, s, 0, n/BytesPerWord) }
   ELSE {
      AddToPendingStores(r, k.ireg, s, FALSE, n/BytesPerWord);
      UnLock(r, k.reg) };
   Stack(ssp-2) }

AND CGShift(sh) BE
   TEST h2!arg2<0 & (k.lvloc<=h1!arg2<=k.lvlab) &
	IsConst(arg1) & h3!arg1=2 & sh=sh.asl THEN {
      h1!arg2 := h1!arg2+k.lvs;
      h4!arg2 := (h4!arg2)<<2;
      Stack(ssp-1) }
   ELSE {
      LET s = ?;
      LET deferredShift = Null;
      LET r = FindSourceAndDestinationRegisters(@s, arg2, FALSE);
      TEST IsConst(arg1) THEN
	 TEST s=r THEN
	    deferredShift := sh*32+h3!arg1
	 ELSE
	    ShiftRegisterDS(r, s, sh, h3!arg1)
      ELSE
	 ShiftRegisterDS(r, s, sh, -MoveToAnyCR(arg1));

      Unlock(s, k.reg);
      LoseR(r, deferredShift) }

AND CGLogop(logop, readAhead) BE {
   LET x, y = arg1, arg2;
   IF Class(arg1, TRUE)<Class(arg2, TRUE) THEN x, y := arg2, arg1;
   {  LET s = IsInARegister(y);
      LET r = -1;
      LET nextOperand = ?;
      LET iscf, lnpending = FALSE, FALSE;
      LET f = VALOF SWITCHON logop INTO {
	 CASE s.logand: RESULTIS f.and
	 CASE s.logor:	RESULTIS f.orr
	 CASE s.eqv:
	 CASE s.neqv:	RESULTIS f.eor };

      IF readAhead THEN {
	 op := ReadOp();
	 TEST op=s.ln THEN {
	    lnpending := TRUE;
	    nextOperand := ReadN();
	    op := ReadOp();

	    IF nextOperand=0 &
	       (op=s.eq | op=s.ne) &
	       (logop=s.logand | logop=s.neqv)
	       THEN iscf := TRUE }

	 ELSE IF op=s.res | op=s.fnrn THEN
	    r := 1 };

      TEST s=Null |
	   (~Locked(s, k.reg) & [(h1!y=k.loc & h3!y>=ssp-2) |
			 /* an heuristic to avoid consuming registers
			    over-rapidly */
			  h1!y=k.reg]) THEN {
	 s := MoveToAnyR(y);
	 IF ~iscf & r<0 THEN r := s }
      ELSE {
	 FlushPendingLoadsForReg(s);
	 Lock(s, k.reg);
	 IF ~iscf & r<0 THEN r := NextR() };

      TEST iscf THEN {
	 TEST logop=s.logand THEN
	    f, r := f.tsts, 0
	 ELSE
	    f, r := f.teqs, 0
	 IF (pendingMask ~= m.always) THEN {
	    Store(0, ssp-4);
	    FlushPendingStores()
	 }
      } ELSE {
	 FlushPendingUsesOfReg(r);
	 IF f=f.and & IsConst(x) & EightBitsOrFewer(~h3!x) THEN {
	    f := f.bic; h3!x := ~h3!x } };

      GenFDS(f, r, s, x);
      IF logop=s.eqv THEN GenRR(f.mvn, r, 0, r)
      Unlock(s, k.reg);
      TEST r~=0 THEN
	 Lose(r, k.reg)
      ELSE
	 Stack(ssp-1);

      IF lnpending THEN Load(k.number, nextOperand);
      IF iscf THEN CGRelopX(op, ReadOp(), FALSE) } }

AND CGRelop(relop, nextop) BE
   CGRelopX(relop, nextop, TRUE)

AND CGRelopX(relop, nextop, compneeded) BE {
   op := nextop;
   TEST op=s.jt | op=s.jf THEN {
      LET c = ?;
      LET l = ReadL();
      LET x = TransferredLabel(l);

      TEST x~=0 THEN {
	 Store(0, ssp-3);
	 FlushPendingStores() }
      ELSE
	 FlushPendingStoresExceptLocals();

      c := ConditionX(relop, op=s.jt, compneeded);
      SaveStateForLab(l);
//    IF x~=0 THEN DelLocsAbove(0, FALSE);
      CondJump(c, l);
      IF regTestedAgainstZero~=Null & c=m.ne & compneeded THEN
	 AddRegInfo(k.reg, regTestedAgainstZero, k.number, 0);
      countFlag := counting;
      op := ReadOp() }
   ELSE {
      LET r = NextR();
      Lock(r, k.reg);
      IF pendingLab~=Null THEN
	 FlushPendingInsts(Null, FALSE);
      GenF1K(f.mov, r, 0, 0);
      F1Inst(f.mvn, r, 0, r, 0, Null, 0,
	     conditionX(relop, TRUE, compneeded));
      Load(k.reg, r);
      UnLock(r, k.reg) } }

AND Condition(relop, b) = ConditionX(relop, b, TRUE)

AND ConditionX(relop, b, compneeded) = VALOF {
   LET x, y = arg1, arg2;
   LET k, mergek = 0, FALSE;
   LET f = f.cmps;

   IF compneeded THEN {
      LET i1, i2 = h2!arg1, h2!arg2;
      IF i1<0 & h4!arg1~=0 & h1!arg1~=k.shreg &
	 i2<0 & h4!arg2~=0 & h1!arg2~=k.shreg THEN {
	 k := h4!arg1-h4!arg2;
	 h4!arg1, h4!arg2 := 0, 0;
	 mergek := TRUE };

      IF Class(arg1, TRUE)<Class(arg2, TRUE) THEN {
	 x, y := arg2, arg1;
	 relop := Reverse(relop);
	 k := -k };

   /* Now a bit of special case detection which says if both
      operands are in the stack and in adjacent locations,
      use a LDM to extract them.  This is worth doing even
      if there is deferment of register loading, because it
      can ensure that registers get allocated the right way
      round (and it will often be applicable to FOR loops)
   */
      IF IsInTheStack(x) & IsInTheStack(y) & usesFrame & relop<s.fadd THEN {
	 LET loc1, loc2 = h3!x, h3!y;
	 IF [loc1=(loc2+1) | loc2=(loc1+1)] &
	    LookFor(x, NotAddr)=Null &
	    LookFor(y, NotAddr)=Null THEN {
	    LET r, s = NextR(), ?;
	    LET b = r.0;
	    LET toploc = loc1>loc2 -> loc1+1, loc2+1;
	    LET wb = f.wb;

	    Lock(r, k.reg); s := NextR(); Unlock(r, k.reg);
	    IF (((r-s) NEQV (loc1-loc2)) NEQV nextStackWord)<0 THEN {
	    // the registers have been allocated the wrong
	    // way round - swap them
	       LET t = r; r := s; s := t };

	    TEST toploc=TOSOffset THEN
	       b, wb := r.ts, 0
	    ELSE {
	       IF toploc~=R0Offset THEN
		  SetRtoRplusK(r.0, r.p, nextStackWord*toploc);
	       R0Offset := toploc-2 };

	    F4Inst(f.ldm, b, (1<<r)+(1<<s), reversedStack -> f.preup+wb,
							     f.predown+wb);

	    MoveSToR(k.reg, r, k.loc, h3!x);
	    MoveSToR(k.reg, s, k.loc, h3!y);
	    h1!x, h3!x := k.reg, r;
	    h1!y, h3!y := k.reg, s } };

      IF mergek THEN h4!x := k;
      regTestedAgainstZero := Null;
      TEST relop>s.fadd THEN
	 CGFCompare(y, x)
      ELSE {
	 LET r = ?;
	 LET ind = h2!y;
	 IF 0<ind<CarMark & IsConst(x) &
	    (relop=s.eq | relop=s.ne) THEN {
	    LET size = ind/32;
	    LET shift = ind REM 32;
	    LET k = h3!x;
	    IF (size<=8 & k=0) | (size=1 & k=1) THEN {
	       h2!y := 0;
	       h3!x := [(-1)>>(32-size)]<<shift;
	       IF k=1 THEN b := ~b;
	       f := f.tsts } };

	 r := MoveToAnyCR(y);
	 IF IsConst(x) & f=f.cmps THEN {
	    LET k = h3!x;
	    LET m = MaskFor(relop, b);
	    IF k=0 THEN regTestedAgainstZero := r;
	    IF LookForSLoc(NotAddr, k.number, k)=Null THEN {
	       CompareAgainstK(r, k,  m);
	       Stack(ssp-2);
	       RESULTIS m } };
	 GenF(f, r, x) } };
   Stack(ssp-2);
   RESULTIS MaskFor(relop, b) }

AND MaskFor(relop, b) = VALOF SWITCHON relop INTO {
   CASE s.ne:
   CASE s.fne: b := ~b;
   CASE s.eq:
   CASE s.feq: RESULTIS b -> m.eq, m.ne

   CASE s.ls:
   CASE s.fls: b := ~b;
   CASE s.ge:
   CASE s.fge: RESULTIS b -> m.ge, m.lt

   CASE s.gr:
   CASE s.fgr: b := ~b;
   CASE s.le:
   CASE s.fle: RESULTIS b -> m.le, m.gt

   CASE s.lls: b := ~b;
   CASE s.lge: RESULTIS b -> m.geu, m.ltu

   CASE s.lgr: b := ~b;
   CASE s.lle: RESULTIS b -> m.leu, m.gtu }

AND Reverse(relop) = VALOF SWITCHON relop INTO {
   CASE s.ls:  RESULTIS s.gr
   CASE s.gr:  RESULTIS s.ls
   CASE s.le:  RESULTIS s.ge
   CASE s.ge:  RESULTIS s.le

   CASE s.lls: RESULTIS s.lgr
   CASE s.lgr: RESULTIS s.lls
   CASE s.lle: RESULTIS s.lge
   CASE s.lge: RESULTIS s.lle

   CASE s.fls: RESULTIS s.fgr
   CASE s.fgr: RESULTIS s.fls
   CASE s.fle: RESULTIS s.fge
   CASE s.fge: RESULTIS s.fle

   DEFAULT:    RESULTIS relop }

AND ComplementCondition(mask) = VALOF SWITCHON mask INTO {
   CASE m.eq:  RESULTIS m.ne
   CASE m.ne:  RESULTIS m.eq

   CASE m.lt:  RESULTIS m.ge
   CASE m.ge:  RESULTIS m.lt

   CASE m.gt:  RESULTIS m.le
   CASE m.le:  RESULTIS m.gt

   CASE m.ltu: RESULTIS m.geu
   CASE m.geu: RESULTIS m.ltu

   CASE m.gtu: RESULTIS m.leu
   CASE m.leu: RESULTIS m.gtu

   CASE m.always: RESULTIS m.never
   CASE m.never:
   CASE Null:  RESULTIS m.always }


AND CGSlctSt() BE {
   LET size = ReadN()
   LET shift = ReadN();
   LET offset = ReadN();
   LET simpleSLoc = FALSE;
   IF h2!arg1>=0 THEN MoveToAnyR(arg1);

   IF size=0 & h1!arg1~=k.shreg THEN {
      h4!arg1 := h4!arg1+offset;
      CGStind();
      RETURN };

   {  LET b, m = -1, 0;
      LET n, t, k = h3!arg1, h1!arg1, h4!arg1;
      LET classofsel = TypeOfField(size, shift);
      LET a = 4*offset;

      IF t~=k.shreg THEN a := a+4*k;
      SWITCHON t INTO {
	 CASE k.lvloc:
	 CASE k.lvglob:
	 CASE k.lvstatic:
	    t, n := t+k.loc-k.lvloc, a/4+n;
	    h1!arg1, h2!arg1, h3!arg1, h4!arg1 := t, -1, n, 0;
	    simpleSLoc := TRUE;
	    ENDCASE

	 CASE k.lvlab:
	    b, m := CheckRLLoaded(), n;
	    ENDCASE

	 DEFAULT:
	    FlushPendingStores();
	    h4!arg1 := 0;
	    b := LoadAddress(arg1);
	    Lock(b, k.reg);
	    TEST localsSafe THEN {
	       FlushPendingLoadsExceptLocals();
	       FlushPendingStoresExceptLocalsAndIregs() }
	    ELSE {
	       FlushPendingLoads();
	       FlushPendingStores() } };

      TEST classofsel=bytefield & ~simpleSLoc THEN
	 GenF2(f.strb, MoveToAnyCR(arg2), b, m, a+shift/8)

      ELSE TEST h1!arg2=k.number THEN {
	 LET mask = (-1>>(TargetWordSize-size))<<shift;
	 LET r, n1 = NextR(), (h3!arg2)<<shift;
	 LET s = r;
	 TEST simpleSLoc THEN {
	    s := IsInARegister(arg1);
	    IF s=Null THEN s := r;
	    MoveToR(s, arg1);
	    h3!arg1 := r;
	    DiscardReg(r, k.reg) }
	 ELSE
	    GenF2(f.ldr, r, b, m, a);
	 IF n1~=mask THEN {
	    TEST size<=16 THEN
	       GenF1K(f.bic, r, s, mask)
	    ELSE
	       GenF1K(f.and, r, s, ~mask);
	    s := r };
	 IF (n1&mask)~=0 THEN
	    GenF1K(f.orr, r, s, n1&mask);
	 TEST simpleSLoc THEN
	    GenStore(t, n)
	 ELSE
	    GenF2(f.str, r, b, m, a) }

      ELSE {
	 LET mask = (-1>>(TargetWordSize-size))<<shift;
	 LET r, s = Null, Null;
	 LET i2 = h2!arg2;
	 LET realshift = shift;
	 IF 0<i2<CarMark THEN {
	    LET size2, shift2 = i2/32, i2 REM 32;
	    IF size2>=size THEN {
	       h2!arg2 := 0;
	       shift := shift-shift2 } };
	 r := MoveToAnyR(arg2);
	 TEST simpleSLoc THEN {
	    s := IsInARegister(arg1);
	    TEST s=Null THEN
	       s := MoveToAnyCR(arg1)
	    ELSE
	       MoveToR(s, arg1);
	    h3!arg1 := r;
	    DiscardReg(r, k.reg) }
	 ELSE {
	    s := NextR();
	    GenF2(f.ldr, s, b, m, a) };

	 TEST (size+realshift)=32 THEN {
	    LET s1 = simpleSloc -> NextR(), s;
	    TEST shift=0 THEN {
	       ShiftRegisterDS(s1, s, sh.asl, size);
	       F1Inst(f.eor, r, r, s1, 0, sh.lsr, size, m.always) }
	    ELSE {
	       TEST size<=16 THEN
		  GenF1K(f.bic, s1, s, mask)
	       ELSE
		  GenF1K(f.and, s1, s, ~mask);
	       TEST shift>0 THEN
		  F1Inst(f.eor, r, s1, r, 0, sh.asl, shift, m.always)
	       ELSE
		  F1Inst(f.eor, r, s1, r, 0, sh.lsr, -shift, m.always) } }

	 ELSE {
	    TEST shift>0 THEN
	       F1Inst(f.eor, r, s, r, 0, sh.asl, shift, m.always)
	    ELSE TEST shift<0 THEN
	       F1Inst(f.eor, r, s, r, 0, sh.lsr, -shift, m.always)
	    ELSE
	       GenRR(f.eor, r, s, r);

	    TEST realshift=0 THEN {
	       ShiftRegister(r, sh.asl, 32-size);
	       F1Inst(f.eor, r, s, r, 0, sh.lsr, 32-size, m.always) }
	    ELSE {
	       TEST size>=16 THEN
		  GenF1K(f.bic, r, r, ~mask)
	       ELSE
		  GenF1K(f.and, r, r, mask);
	       GenRR(f.eor, r, s, r) } };
	 TEST simpleSLoc THEN
	    GenStore(t, n)
	 ELSE
	    GenF2(f.str, r, b, m, a) };
      TEST simpleSLoc THEN
	 Stack(ssp-1)
      ELSE {
	 DiscardRegs();
	 Stack(ssp-2) };

      IF b>=0 THEN Unlock(b, k.reg) } }

AND CGByteAp(op) BE {
   LET a, r, x = ?, ?, Null;
   LET sh, m = sh.asl, 0;
   LET b = ?;

   TEST IsConst(arg1) & (-k4<=h3!arg1<=k4) & h2!arg2<0
      THEN SWITCHON h1!arg2 INTO {
	 CASE k.lvloc:
	    a := h3!arg1+4*h4!arg2;
	    b := linkageNotStored -> r.ts, r.p;
	    IF ~usesFrame THEN b, a := r.ts, a-4*saveSpaceSize;
	    a := a+nextStackWord*h3!arg2;
	    ENDCASE

	 CASE k.lvstatic:
	    a, b := h3!arg1+4*(h4!arg2+h3!arg2), CheckRLLoaded();
	    ENDCASE

	 CASE k.lvglob:
	    a, b := h3!arg1+4*(h4!arg2+h3!arg2), r.g;
	    ENDCASE

	 CASE k.lvlab:
	    a, m := h3!arg1+4*h4!arg2, h3!arg2;
	    b := CheckRLLoaded();
	    ENDCASE;

	 DEFAULT:
	    IF h1!arg2~=k.shreg THEN {
	       a := h3!arg1+nextStackWord*h4!arg2;
	       h4!arg2 := 0 };
	    b := LoadAddress(arg2) }

   ELSE TEST IsConst(arg2) & h3!arg2=0 THEN {
       // special case of 0%something.
       // because this is a construct much used in Lisp,
       // where the something is a tagged pointer, I mask
       // out potential tag bits
      TEST h2!arg1<0 & h4!arg1~=0 THEN {
	 a := h4!arg1;
	 h4!arg1 := 0 }
      ELSE
	 a := 0;
      b := LoadCarAdd(arg1) }

   ELSE {
      x := MoveToAnyCR(arg1); Lock(x, k.reg);
      TEST h2!arg2<0 THEN {
	 IF k.lvloc<=h1!arg2<=k.lvlab THEN {
	    h1!arg2, sh := h1!arg2+k.lvs, Null;
	    h4!arg2 := h4!arg2*4 };
	 b := LookFor(arg2, ShiftedUp);
	 TEST b>=0 & h4!arg2=0 THEN
	    sh := Null
	 ELSE
	    b := MoveToAnyCR(arg2) }
      ELSE
	 b := MoveToAnyCR(arg2) };

   FlushPendingLoads();
   FlushPendingStores();
   Lock(b, k.reg);
   TEST op=s.putbyte THEN {
      r := LoadArg3ToSomeCR();
      TEST x=Null THEN
	 GenF2(f.strb, r, b, m, a)
      ELSE
	 F2Inst(f.strb, r, x, 0, b, f.preup, sh, 2, m.always);
      TEST LocalsSafe THEN
	 DiscardNonLocalRegs()
      ELSE
	 DiscardNonConstRegs();
      Stack(ssp-3) }

   ELSE {
      r := NextR();
      TEST x=Null THEN
	 GenF2(f.ldrb, r, b, m, a)
      ELSE
	 F2Inst(f.ldrb, r, x, 0, b, f.preup, sh, 2, m.always);
      DiscardReg(r, k.reg);
      Lose(r, k.reg);
      UnLock(b, k.reg);
      IF x~=Null THEN UnLock(x, k.reg) } }
