فهرست منبع

Replace encoding for ASH and LSH to give correct semantics in case the shift amount is greater than the data-word width.

k_john_gough_cp 12 سال پیش
والد
کامیت
e96c905369
3فایلهای تغییر یافته به همراه512 افزوده شده و 135 حذف شده
  1. 11 7
      gpcp/ExprDesc.cp
  2. 184 1
      gpcp/JavaMaker.cp
  3. 317 127
      gpcp/MsilMaker.cp

+ 11 - 7
gpcp/ExprDesc.cp

@@ -942,17 +942,19 @@ MODULE ExprDesc;
           ELSE
             IF ~arg0.isIntExpr() THEN arg0.ExprError(37) END;
             IF ~arg1.isIntExpr() THEN arg1.ExprError(37) END;
-	   (* NO FOLDING IN THIS VERSION 
+	   (* NO FOLDING IN THIS VERSION
             IF (arg0.kind = numLt) & (arg1.kind = numLt) THEN
               rslt := mkNumLt(ASH(arg0(LeafX).value.int(),
                 arg1(LeafX).value.int()));
             ELSE
 	   *)
-	      IF arg0.type = Builtin.lIntTp THEN
+              IF arg0.type = Builtin.lIntTp THEN
                 dstT := Builtin.lIntTp;
-              ELSIF arg0.type # Builtin.intTp THEN
-                arg0 := convert(arg0, Builtin.intTp);
-		dstT := Builtin.intTp;
+			  ELSE
+                IF arg0.type # Builtin.intTp THEN
+                  arg0 := convert(arg0, Builtin.intTp);
+				END;
+                dstT := Builtin.intTp;
               END;
               IF arg1.type # Builtin.intTp THEN
                 arg1 := convert(arg1, Builtin.intTp);
@@ -973,8 +975,10 @@ MODULE ExprDesc;
 			(* FIXME, no folding yet ... *)
 			IF arg0.type = Builtin.lIntTp THEN
 			  dstT := Builtin.lIntTp;
-            ELSIF arg0.type # Builtin.intTp THEN
-              arg0 := convert(arg0, Builtin.intTp);
+			ELSE
+              IF arg0.type # Builtin.intTp THEN
+                arg0 := convert(arg0, Builtin.intTp);
+			  END;
 			  dstT := Builtin.intTp;
             END;
             IF arg1.type # Builtin.intTp THEN

+ 184 - 1
gpcp/JavaMaker.cp

@@ -1178,6 +1178,182 @@ MODULE JavaMaker;
 
 (* ============================================================ *)
 
+  (* Assert: lOp is already pushed. *)
+  PROCEDURE ShiftInt(kind : INTEGER; e : JavaEmitter; lOp : Sy.Expr; rOp : Sy.Expr);
+    VAR indx : INTEGER;
+	    out  : Ju.JavaFile;
+	    shrLab, fixLab, s31Lab, exitLb : Ju.Label; 
+  BEGIN
+    out := e.outF;
+    IF rOp.kind = Xp.numLt THEN
+      indx := intValue(rOp);
+      IF indx = 0 THEN  (* skip *)
+      ELSIF indx < -31 THEN (* right shift out *)
+	    IF kind = Xp.ashInt THEN
+		  out.PushInt(31);
+		  out.Code(Jvm.opc_ishr);
+		ELSE
+		  out.Code(Jvm.opc_pop);
+		  out.PushInt(0);
+		END;
+	  ELSIF indx < 0 THEN (* right shift *)
+        out.PushInt(-indx);
+		IF kind = Xp.ashInt THEN (* arith shift *)
+		  out.Code(Jvm.opc_ishr);
+		ELSE (* logical shift *)
+		  out.Code(Jvm.opc_iushr);
+		END;
+	  ELSIF indx > 31 THEN (* result is zero *)
+		out.Code(Jvm.opc_pop);
+		out.PushInt(0);	    
+	  ELSE (* a left shift *)
+		out.PushInt(indx);
+        out.Code(Jvm.opc_ishl);
+	  END;
+    ELSE  (* variable sized shift *)
+      shrLab := out.newLabel();
+	  fixLab := out.newLabel();
+	  s31Lab := out.newLabel();
+      exitLb := out.newLabel();
+     (*
+      *  This is a variable shift. Do it the hard way.
+      *  First, check the sign of the right hand op.
+      *)
+      e.PushValue(rOp, Bi.intTp);              (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_dup);                   (* TOS: rOp, rOp, lOp, ...      *)
+      out.CodeLb(Jvm.opc_iflt, shrLab);        (* TOS: rOp, lOp, ...           *)
+     (*
+      *  Positive selector ==> shift left;
+	  *  But first: a range check ...
+      *)
+      out.Code(Jvm.opc_dup);                   (* TOS: rOp, rOp, lOp, ...      *)
+	  out.PushInt(31);                         (* TOS: 31, rOp, rOp, lOp, ...  *)
+      out.CodeLb(Jvm.opc_if_icmpgt, fixLab);   (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_ishl);                  (* TOS: rslt, ...               *)
+      out.CodeLb(Jvm.opc_goto, exitLb);
+	 (*
+	  *  Out of range shift, set result to zero.
+	  *)
+	  out.DefLab(fixLab);                      (* TOS: rOp, lOp, ...           *)
+	  out.Code(Jvm.opc_pop2);                  (* TOS:  ...                    *)
+	  out.PushInt(0);	                       (* TOS: 0, ...                  *)
+      out.CodeLb(Jvm.opc_goto, exitLb);
+	 (*
+	  *  Out of range, rslt = rOp >> 31.
+	  *)
+	  out.DefLab(s31Lab);                      (* TOS: rOp, lOp, ...           *)
+	  out.Code(Jvm.opc_pop);                   (* TOS: lOp, ...                *)
+	  out.PushInt(31);	                       (* TOS: 31, lOp, ...            *)
+	  out.Code(Jvm.opc_ishr);
+      out.CodeLb(Jvm.opc_goto, exitLb);
+     (*
+      *  Negative selector ==> shift right;
+      *)
+      out.DefLab(shrLab);                      (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_ineg);                  (* TOS: -rOp, lOp, ...          *)
+      out.Code(Jvm.opc_dup);                   (* TOS: -rOp, -rOp, lOp, ...    *)
+	  out.PushInt(31);                         (* TOS: 31, -rOp, -rOp, lOp, ...*)
+	  IF kind = Xp.lshInt THEN (* LSH *)
+	    out.CodeLb(Jvm.opc_if_icmpgt, fixLab); (* TOS: -rOp, lOp, ...          *)
+		out.Code(Jvm.opc_iushr);               (* TOS: rslt, ...               *)
+	  ELSE (* ASH *)                           (* TOS: 31, rOp, rOp, lOp, ...  *)
+	    out.CodeLb(Jvm.opc_if_icmpgt, s31Lab); (* TOS: rOp, lOp, ...           *)
+		out.Code(Jvm.opc_ishr);                (* TOS: rslt, ...               *)
+	  END;
+      out.DefLab(exitLb);
+    END;
+  END ShiftInt;
+
+(* ============================================================ *)
+
+  (* Assert: lOp is already pushed. *)
+  PROCEDURE ShiftLong(kind : INTEGER; e : JavaEmitter; lOp : Sy.Expr; rOp : Sy.Expr);
+    VAR indx : INTEGER;
+	    out  : Ju.JavaFile;
+	    shrLab, fixLab, s63Lab, exitLb : Ju.Label; 
+  BEGIN
+    out := e.outF;
+    IF rOp.kind = Xp.numLt THEN
+      indx := intValue(rOp);
+      IF indx = 0 THEN  (* skip *)
+      ELSIF indx < -63 THEN (* right shift out *)
+	    IF kind = Xp.ashInt THEN
+		  out.PushInt(63);
+		  out.Code(Jvm.opc_lshr);
+		ELSE
+		  out.Code(Jvm.opc_pop2);
+		  out.PushLong(0);
+		END;
+	  ELSIF indx < 0 THEN (* right shift *)
+        out.PushInt(-indx);
+		IF kind = Xp.ashInt THEN (* arith shift *)
+		  out.Code(Jvm.opc_lshr);
+		ELSE (* logical shift *)
+		  out.Code(Jvm.opc_lushr);
+		END;
+	  ELSIF indx > 63 THEN (* result is zero *)
+		out.Code(Jvm.opc_pop2);
+		out.PushLong(0);	    
+	  ELSE (* a left shift *)
+		out.PushInt(indx);
+        out.Code(Jvm.opc_lshl);
+	  END;
+    ELSE  (* variable sized shift *)
+      shrLab := out.newLabel();
+	  fixLab := out.newLabel();
+	  s63Lab := out.newLabel();
+      exitLb := out.newLabel();
+     (*
+      *  This is a variable shift. Do it the hard way.
+      *  First, check the sign of the right hand op.
+      *)
+      e.PushValue(rOp, Bi.intTp);              (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_dup);                   (* TOS: rOp, rOp, lOp, ...      *)
+      out.CodeLb(Jvm.opc_iflt, shrLab);        (* TOS: rOp, lOp, ...           *)
+     (*
+      *  Positive selector ==> shift left;
+	  *  But first: a range check ...
+      *)
+      out.Code(Jvm.opc_dup);                   (* TOS: rOp, rOp, lOp, ...      *)
+	  out.PushInt(63);                         (* TOS: 63, rOp, rOp, lOp, ...  *)
+      out.CodeLb(Jvm.opc_if_icmpgt, fixLab);   (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_lshl);                  (* TOS: rslt, ...               *)
+      out.CodeLb(Jvm.opc_goto, exitLb);
+	 (*
+	  *  Out of range shift, set result to zero.
+	  *)
+	  out.DefLab(fixLab);                      (* TOS: rOp, lOp, ...           *)
+	  out.Code(Jvm.opc_pop);                   (* TOS: lOp, ...                *)
+	  out.Code(Jvm.opc_pop2);                  (* TOS:  ...                    *)
+	  out.PushLong(0);	                       (* TOS: 0, ...                  *)
+      out.CodeLb(Jvm.opc_goto, exitLb);
+	 (*
+	  *  Out of range, rslt = rOp >> 63.
+	  *)
+	  out.DefLab(s63Lab);                      (* TOS: rOp, lOp, ...           *)
+	  out.Code(Jvm.opc_pop);                   (* TOS: lOp, ...                *)
+	  out.PushInt(63);	                       (* TOS: 63, lOp, ...            *)
+	  out.Code(Jvm.opc_lshr);
+      out.CodeLb(Jvm.opc_goto, exitLb);
+     (*
+      *  Negative selector ==> shift right;
+      *)
+      out.DefLab(shrLab);                      (* TOS: rOp, lOp, ...           *)
+      out.Code(Jvm.opc_ineg);                  (* TOS: -rOp, lOp, ...          *)
+      out.Code(Jvm.opc_dup);                   (* TOS: -rOp, -rOp, lOp, ...    *)
+	  out.PushInt(63);                         (* TOS: 63, -rOp, -rOp, lOp, ...*)
+	  IF kind = Xp.lshInt THEN (* LSH *)
+	    out.CodeLb(Jvm.opc_if_icmpgt, fixLab); (* TOS: -rOp, lOp, ...          *)
+		out.Code(Jvm.opc_lushr);               (* TOS: rslt, ...               *)
+	  ELSE (* ASH *)                           (* TOS: 31, rOp, rOp, lOp, ...  *)
+	    out.CodeLb(Jvm.opc_if_icmpgt, s63Lab); (* TOS: rOp, lOp, ...           *)
+		out.Code(Jvm.opc_lshr);                (* TOS: rslt, ...               *)
+	  END;
+      out.DefLab(exitLb);
+    END;
+  END ShiftLong;
+
+(* ============================================================ *)
   (* Assert: lOp is already pushed. *)
   PROCEDURE RotateInt(e : JavaEmitter; lOp : Sy.Expr; rOp : Sy.Expr);
     VAR
@@ -1642,8 +1818,14 @@ MODULE JavaMaker;
 		END;
     (* -------------------------------- *)
     | Xp.ashInt, Xp.lshInt :
-        e.PushValue(lOp, lOp.type);
 		long := dst.isLongType();
+	    e.PushValue(lOp, lOp.type);
+		IF long THEN
+		  ShiftLong(exp.kind, e, lOp, rOp);
+		ELSE
+		  ShiftInt(exp.kind, e, lOp, rOp);
+		END;
+	  (*
         IF rOp.kind = Xp.numLt THEN
           indx := intValue(rOp);
           IF indx = 0 THEN  (* skip *)
@@ -1709,6 +1891,7 @@ MODULE JavaMaker;
 		  END;
           out.DefLab(exLb);
         END;
+	  *)
     (* -------------------------------- *)
     | Xp.strCat :
         e.PushValue(lOp, lOp.type);

+ 317 - 127
gpcp/MsilMaker.cp

@@ -1410,6 +1410,317 @@ MODULE MsilMaker;
     END;
   END PushUnary;
 
+(* ============================================================ *)
+
+  PROCEDURE Rotate(e : MsilEmitter; lOp, rOp : Sy.Expr);
+    VAR out  : Mu.MsilFile;
+	    rtSz : INTEGER;  (* rotate size in bits *)
+		hstT : Sy.Type;  (* host type on stack  *)
+		indx : INTEGER;  (* literal rOp value   *)
+        temp : INTEGER;  (* local to save lOp   *)
+		ixSv : INTEGER;  (* local for left rslt *)
+  BEGIN
+    out := e.outF;
+    e.PushValue(lOp, lOp.type);
+
+   (* Convert TOS value to unsigned *)
+	hstT := Bi.intTp;
+	IF (lOp.type = Bi.sIntTp) THEN 
+		  rtSz := 16;
+		  out.Code(Asm.opc_conv_u2);
+	ELSIF (lOp.type = Bi.byteTp) OR (lOp.type = Bi.uBytTp) THEN
+		  rtSz := 8;
+		  out.Code(Asm.opc_conv_u1);
+	ELSIF lOp.type = Bi.lIntTp THEN
+		  rtSz := 64;
+		  hstT := Bi.lIntTp;
+	ELSE
+		  rtSz := 32;
+		  (* out.Code(Asm.opc_conv_u4); *)
+	END;		
+    IF rOp.kind = Xp.numLt THEN
+		  indx := intValue(rOp) MOD rtSz;
+          IF indx = 0 THEN  (* skip *)
+		  ELSE (* 
+		    *  Rotation is achieved by means of the identity
+			*  Forall 0 <= n < rtSz: 
+			*    ROT(a, n) = LSH(a,n) bitwiseOR LSH(a,n-rtSz);
+			*)
+			temp := out.proc.newLocal(hstT);
+			out.Code(Asm.opc_dup);
+			out.PushInt(indx);
+			out.Code(Asm.opc_shl);
+			out.StoreLocal(temp);
+			out.PushInt(rtSz - indx);
+			out.Code(Asm.opc_shr_un);
+			out.PushLocal(temp);
+			out.Code(Asm.opc_or);
+			out.proc.ReleaseLocal(temp);
+          END;
+		  out.ConvertDn(hstT, lOp.type, FALSE);
+	ELSE
+         (*
+          *  This is a variable rotate.
+		  *
+		  *  Note that in the case of a short left operand the value
+		  *  on the stack has been converted to unsigned.  The value is
+		  *  saved as a int (rather than a shorter type) so that the 
+		  *  value does not get sign extended on each new load, 
+		  *  necessitating a new conversion each time.
+          *)
+		  temp := out.proc.newLocal(hstT);
+		  ixSv := out.proc.newLocal(Bi.intTp);
+		  out.Code(Asm.opc_dup);         (* TOS: lOp, lOp, ...               *)
+		  out.StoreLocal(temp);          (* TOS: lOp, ...                    *)
+          e.PushValue(rOp, rOp.type);    (* TOS: rOp, lOp, ...               *)
+		  out.PushInt(rtSz-1);           (* TOS: 31, rOp, lOp, ...           *)
+          out.Code(Asm.opc_and);         (* TOS: rOp', lOp, ...              *)
+		  out.Code(Asm.opc_dup);         (* TOS: rOp', rOp', lOp, ...        *)
+		  out.StoreLocal(ixSv);          (* TOS: rOp', lOp, ...              *)
+		  out.Code(Asm.opc_shl);         (* TOS: lRz, ...    (left fragment) *)
+		  out.PushLocal(temp);           (* TOS: lOp, lRz, ...               *)
+		  out.PushInt(rtSz);             (* TOS: 32, lOp, lRz, ...           *)
+		  out.PushLocal(ixSv);           (* TOS: rOp', 32, lOp, lRz, ...     *)
+		  out.Code(Asm.opc_sub);         (* TOS: rOp'', lOp, lRz, ...        *)
+	     (* mask the shift amount in case idx = 0 *)
+		  out.PushInt(rtSz-1);           (* TOS: 31, rOp, lOp, ...           *)
+          out.Code(Asm.opc_and);         (* TOS: rOp', lOp, ...              *)
+		  out.Code(Asm.opc_shr_un);      (* TOS: rRz, lRz, ...               *)
+		  out.Code(Asm.opc_or);          (* TOS: ROT(lOp,rOp), ...           *)
+		  out.proc.ReleaseLocal(ixSv);
+		  out.proc.ReleaseLocal(temp);
+		  out.ConvertDn(hstT, lOp.type, FALSE);
+    END;
+  END Rotate;
+
+(* ============================================================ *)
+(*
+  PROCEDURE Shift(e : MsilEmitter; lOp, rOp : Sy.Expr; kind : INTEGER);
+    VAR out  : Mu.MsilFile;
+		long : BOOLEAN;
+		indx : INTEGER;      (* literal rOp value   *)
+        temp : INTEGER;      (* local to save lOp   *)
+		maskSz : INTEGER;    (* size of index mask  *)
+        exitLb : Mu.Label;
+        rshLab : Mu.Label;
+	(* --------------------------- *)
+	PROCEDURE ReplaceWithZero(i64 : BOOLEAN; f : Mu.MsilFile);
+	BEGIN
+	  f.Code(Asm.opc_pop);
+	  IF i64 THEN f.PushLong(0) ELSE f.PushInt(0) END;
+	END ReplaceWithZero;
+	(* --------------------------- *)
+  BEGIN
+    out := e.outF;
+    e.PushValue(lOp, lOp.type);
+    long := lOp.type = Bi.lIntTp;
+	IF long THEN maskSz := 63 ELSE maskSz := 31 END;
+   (*
+    *  Deal with shift by literal sizes 
+	*)
+    IF rOp.kind = Xp.numLt THEN
+      indx := intValue(rOp);
+      IF indx = 0 THEN (* skip *)
+	  ELSIF indx > maskSz THEN
+	    ReplaceWithZero(long, out);
+	  ELSIF indx > 0 THEN
+        out.PushInt(indx);
+        out.Code(Asm.opc_shl);
+      ELSIF kind = Xp.ashInt THEN
+        out.PushInt(MIN(-indx, 31)); 
+        out.Code(Asm.opc_shr);
+	  ELSIF indx < -maskSz THEN (* LSHR > wordsize () *)
+	    ReplaceWithZero(long, out);
+	  ELSE (* ==> kind = lshInt *)
+        out.PushInt(-indx);
+        out.Code(Asm.opc_shr_un);
+	  END;
+    ELSE
+      rshLab := out.newLabel();
+      exitLb := out.newLabel();
+	  temp := out.proc.newLocal(Bi.intTp);
+     (*
+      *  This is a variable shift. Do it the hard way.
+      *  First, check the sign of the right hand op.
+      *)
+      e.PushValue(rOp, rOp.type);       (* TOS: rOp, lOp, ...            *)
+      out.Code(Asm.opc_dup);            (* TOS: rOp, rOp, lOp, ...       *)
+      out.PushInt(0);
+      out.CodeLb(Asm.opc_blt, rshLab);  (* TOS: rOp, lOp, ...            *)
+     (*
+      *  Positive selector ==> shift left;
+	  *  Do range limitation on shift index
+      *)
+	  out.Code(Asm.opc_dup);            (* TOS: rOp, rOp, lOp, ...       *)
+	  out.StoreLocal(temp);             (* TOS: rOp, lOp, ...            *)
+	  out.PushInt(maskSz+1);            (* TOS: 32, rOp, lOp, ...        *)
+	  out.Code(Asm.opc_clt);            (* TOS: 0/1, lOp, ...            *)
+      out.Code(Asm.opc_neg);
+	  IF long THEN out.Code(Asm.opc_conv_i8) END;
+	  out.Code(Asm.opc_and);
+	  out.PushLocal(temp);
+	 (*
+	  *  Now do the shift
+	  *)
+      out.Code(Asm.opc_shl);
+      out.CodeLb(Asm.opc_br, exitLb);
+     (*
+      *  Negative selector ==> shift right;
+      *)
+      out.DefLab(rshLab);
+      out.Code(Asm.opc_neg);
+	  IF kind = Xp.ashInt THEN
+	    out.Code(Asm.opc_dup);             (* TOS: -rOp, -rOp, lOp, ...     *)
+	    out.PushInt(maskSz);               (* TOS: 31, -rOp, -rOp, lOp, ... *)
+	    out.Code(Asm.opc_cgt);             (* TOS: 0/1, -rOp, lOp, ...      *)
+	    out.PushInt(maskSz);               (* TOS: 31, 0/1, rOp, lOp, ...   *)
+	    out.Code(Asm.opc_mul);             (* TOS: 0/31, -rOp, lOp, ...     *)
+	    out.Code(Asm.opc_or);              (* TOS: MIN(-rOp,31), lOp, ...   *)
+	   (*
+	    *  Now do the shift
+	    *)
+        out.Code(Asm.opc_shr);
+	  ELSE (* ==> kind = lshInt *)
+	  (* FIXME *)
+	    out.Code(Asm.opc_dup);            (* TOS: rOp, rOp, lOp, ...       *)
+	    out.StoreLocal(temp);             (* TOS: rOp, lOp, ...            *)
+	    out.PushInt(maskSz+1);            (* TOS: 32, rOp, lOp, ...        *)
+	    out.Code(Asm.opc_clt);            (* TOS: 0/1, lOp, ...            *)
+        out.Code(Asm.opc_neg);
+	    IF long THEN out.Code(Asm.opc_conv_i8) END;
+	    out.Code(Asm.opc_and);
+	    out.PushLocal(temp);
+	   (*
+	    *  Now do the shift
+	    *)
+        out.Code(Asm.opc_shr_un);
+	  END;
+      out.DefLab(exitLb);
+	  out.proc.ReleaseLocal(temp);
+    END;
+  END Shift;
+ *)
+(* ============================================================ *)
+
+  PROCEDURE Shift2(e : MsilEmitter; lOp, rOp : Sy.Expr; kind : INTEGER);
+    VAR out  : Mu.MsilFile;
+		long : BOOLEAN;
+		indx : INTEGER;      (* literal rOp value   *)
+        temp : INTEGER;      (* local to save lOp   *)
+		maskSz : INTEGER;    (* size of index mask  *)
+        rshLab : Mu.Label;
+        exitLb : Mu.Label;
+        entryL : Mu.Label;
+        zeroLb : Mu.Label;
+	(* --------------------------- *)
+	PROCEDURE ReplaceWithZero(i64 : BOOLEAN; f : Mu.MsilFile);
+	BEGIN
+	  f.Code(Asm.opc_pop);
+	  IF i64 THEN f.PushLong(0) ELSE f.PushInt(0) END;
+	END ReplaceWithZero;
+	(* --------------------------- *)
+  BEGIN
+    out := e.outF;
+    e.PushValue(lOp, lOp.type);
+    long := lOp.type = Bi.lIntTp;
+	IF long THEN maskSz := 63 ELSE maskSz := 31 END;
+   (*
+    *  Deal with shift by literal sizes 
+	*)
+    IF rOp.kind = Xp.numLt THEN
+      indx := intValue(rOp);
+      IF indx = 0 THEN (* skip *)
+	  ELSIF indx > maskSz THEN
+	    ReplaceWithZero(long, out);
+	  ELSIF indx > 0 THEN
+        out.PushInt(indx);
+        out.Code(Asm.opc_shl);
+      ELSIF kind = Xp.ashInt THEN
+        out.PushInt(MIN(-indx, 31)); 
+        out.Code(Asm.opc_shr);
+	  ELSIF indx < -maskSz THEN (* LSHR > wordsize () *)
+	    ReplaceWithZero(long, out);
+	  ELSE (* ==> kind = lshInt *)
+        out.PushInt(-indx);
+        out.Code(Asm.opc_shr_un);
+	  END;
+    ELSE
+      entryL := out.newLabel();
+      rshLab := out.newLabel();
+      zeroLb := out.newLabel();
+      exitLb := out.newLabel();
+	  temp := out.proc.newLocal(Bi.intTp);
+	  e.PushValue(rOp, rOp.type);           (* TOS: rOp, lOp, ...            *)
+	  out.Code(Asm.opc_dup);                (* TOS: rOp, rOp, lOp, ...       *)
+	  out.StoreLocal(temp);                 (* TOS: rOp, lOp, ...            *)
+	  IF kind = Xp.lshInt THEN (* logical shift *)
+		out.PushInt(maskSz);                (* TOS: 31, rOp, lOp, ...        *)
+		out.Code(Asm.opc_add);              (* TOS: rOp*, lOp, ...           *)
+		out.PushInt(maskSz * 2);            (* TOS: 62, rOp*, lOp, ...       *)
+		out.CodeLb(Asm.opc_ble_un, entryL); (* TOS: lOp, ...                 *)
+		ReplaceWithZero(long, out);         (* TOS: rslt, ...                *)
+		out.CodeLb(Asm.opc_br, exitLb);     (* Jump directly to exit label   *)
+	   (* 
+	    *  Normal, in-range control flow.
+		*)
+		out.DefLab(entryL);
+		out.PushLocal(temp);              (* TOS: rOp, lOp, ...            *)
+        out.PushInt(0);                   (* TOS: 0, rOp, lOp, ...         *)
+        out.CodeLb(Asm.opc_blt, rshLab);  (* TOS: lOp, ...                 *)
+	   (* 
+	    *  Positive shift ==> left shift
+		*)
+		out.PushLocal(temp);              (* TOS: rOp, lOp, ...            *)
+	    out.Code(Asm.opc_shl);            (* TOS: rslt, ...                *)
+		out.CodeLb(Asm.opc_br, exitLb);   (* Jump directly to exit label   *)
+       (*
+        *  Negative selector ==> shift right;
+        *)
+        out.DefLab(rshLab);
+		out.PushLocal(temp);              (* TOS: rOp, lOp, ...            *)
+        out.Code(Asm.opc_neg);            (* TOS: -rOp, lOp, ...           *)
+		out.Code(Asm.opc_shr_un);         (* And fall through to exitLb    *)
+      ELSE (* kind = ashInt ==> Arithmetic Shift *)
+		out.PushInt(maskSz);              (* TOS: 31, rOp, lOp, ...        *)
+		out.CodeLb(Asm.opc_bgt, zeroLb);  (* TOS: lOp, ...                 *) 
+	    out.PushLocal(temp);              (* TOS: rOp, lOp, ...            *)
+		out.PushInt(-maskSz);             (* TOS: -31, rOp, lOp, ...       *)
+		out.CodeLb(Asm.opc_bgt, entryL);  (* TOS:  lop, ...                *)
+	   (*
+	    *  Negative shift is out of range.
+		*)
+		out.PushInt(-maskSz);
+		out.StoreLocal(temp); (* overwrite temp! *)
+		out.CodeLb(Asm.opc_br, rshLab);   (* TOS:  lop, ...                *)
+		out.DefLab(zeroLb);
+		ReplaceWithZero(long, out);
+		out.CodeLb(Asm.opc_br, exitLb);   (* Jump directly to exit label   *)
+	   (* 
+	    *  Normal, in-range control flow.
+		*)
+		out.DefLab(entryL);
+		out.PushLocal(temp);              (* TOS: rOp, lop, ...            *)
+        out.PushInt(0);
+        out.CodeLb(Asm.opc_blt, rshLab);  (* TOS: lOp, ...                 *)
+	   (* 
+	    *  Positive shift ==> left shift
+		*)
+		out.PushLocal(temp);              (* TOS: rOp, lop, ...            *)
+	    out.Code(Asm.opc_shl);
+		out.CodeLb(Asm.opc_br, exitLb);   (* Jump directly to exit label   *)
+       (*
+        *  Negative selector ==> shift right;
+        *)
+        out.DefLab(rshLab);
+		out.PushLocal(temp);              (* TOS: rOp, lop, ...            *)
+		out.Code(Asm.opc_neg);            (* TOS: -rOp, lop, ...           *)
+		out.Code(Asm.opc_shr);            (* And fall through to exitLb    *)
+	  END;
+      out.DefLab(exitLb);
+	  out.proc.ReleaseLocal(temp);
+    END;
+  END Shift2;
+
 (* ============================================================ *)
 
   PROCEDURE (e : MsilEmitter)
@@ -1440,6 +1751,12 @@ MODULE MsilMaker;
     ovfl := out.proc.prId.ovfChk & dst.isIntType();
     CASE exp.kind OF
     (* -------------------------------- *)
+    | Xp.rotInt: 
+	    Rotate(e, lOp, rOp);
+    (* -------------------------------- *)
+    | Xp.ashInt, Xp.lshInt:
+	    Shift2(e, lOp, rOp, exp.kind); 
+    (* -------------------------------- *)
     | Xp.index :
         rasd := exp(Xp.BinaryX).lKid.type IS Ty.Vector;
         IF rasd THEN
@@ -1720,133 +2037,6 @@ MODULE MsilMaker;
         out.Code(Asm.opc_ldnull);
         out.Code(Asm.opc_cgt_un);
     (* -------------------------------- *)
-    | Xp.ashInt, Xp.lshInt:
-        e.PushValue(lOp, lOp.type);
-        IF rOp.kind = Xp.numLt THEN
-          indx := intValue(rOp);
-          IF indx = 0 THEN (* skip *)
-		  ELSIF indx > 0 THEN
-            out.PushInt(indx);
-            out.Code(Asm.opc_shl);
-          ELSIF exp.kind = Xp.ashInt THEN
-            out.PushInt(-indx);
-            out.Code(Asm.opc_shr);
-		  ELSE (* ==> exp.kind = lshInt *)
-            out.PushInt(-indx);
-            out.Code(Asm.opc_shr_un);
-		  END;
-        ELSE
-          tpLb := out.newLabel();
-          exLb := out.newLabel();
-	      long := dst(Ty.Base).tpOrd = Ty.lIntN;
-         (*
-          *  This is a variable shift. Do it the hard way.
-          *  First, check the sign of the right hand op.
-          *)
-          e.PushValue(rOp, rOp.type);
-          out.Code(Asm.opc_dup);
-          out.PushInt(0);
-          out.CodeLb(Asm.opc_blt, tpLb);
-		 (*
-		  *  Trim the shift index
-		  *)
-		  IF long THEN out.PushInt(63) ELSE out.PushInt(31) END;
-		  out.Code(Asm.opc_and);
-         (*
-          *  Positive selector ==> shift left;
-          *)
-          out.Code(Asm.opc_shl);
-          out.CodeLb(Asm.opc_br, exLb);
-         (*
-          *  Negative selector ==> shift right;
-          *)
-          out.DefLab(tpLb);
-          out.Code(Asm.opc_neg);
-		 (*
-		  *  Trim the shift index
-		  *)
-		  IF long THEN out.PushInt(63) ELSE out.PushInt(31) END;
-		  out.Code(Asm.opc_and);
-		  IF exp.kind = Xp.ashInt THEN
-            out.Code(Asm.opc_shr);
-		  ELSE (* ==> exp.kind = lshInt *)
-            out.Code(Asm.opc_shr_un);
-		  END;
-          out.DefLab(exLb);
-        END;
-    (* -------------------------------- *)
-    | Xp.rotInt:
-        e.PushValue(lOp, lOp.type);
-	   (* Convert TOS value to unsigned *)
-	    hstT := Bi.intTp;
-		IF (lOp.type = Bi.sIntTp) THEN 
-		  rtSz := 16;
-		  out.Code(Asm.opc_conv_u2);
-		ELSIF (lOp.type = Bi.byteTp) OR (lOp.type = Bi.uBytTp) THEN
-		  rtSz := 8;
-		  out.Code(Asm.opc_conv_u1);
-		ELSIF lOp.type = Bi.lIntTp THEN
-		  rtSz := 64;
-		  hstT := Bi.lIntTp;
-		ELSE
-		  rtSz := 32;
-		  (* out.Code(Asm.opc_conv_u4); *)
-		END;
-		
-        IF rOp.kind = Xp.numLt THEN
-		  indx := intValue(rOp) MOD rtSz;
-          IF indx = 0 THEN  (* skip *)
-		  ELSE (* 
-		    *  Rotation is achieved by means of the identity
-			*  Forall 0 <= n < rtSz: 
-			*    ROT(a, n) = LSH(a,n) bitwiseOR LSH(a,n-rtSz);
-			*)
-			temp := out.proc.newLocal(hstT);
-			out.Code(Asm.opc_dup);
-			out.PushInt(indx);
-			out.Code(Asm.opc_shl);
-			out.StoreLocal(temp);
-			out.PushInt(rtSz - indx);
-			out.Code(Asm.opc_shr_un);
-			out.PushLocal(temp);
-			out.Code(Asm.opc_or);
-			out.proc.ReleaseLocal(temp);
-          END;
-		  out.ConvertDn(hstT, lOp.type, FALSE);
-		ELSE
-         (*
-          *  This is a variable rotate.
-		  *
-		  *  Note that in the case of a short left operand the value
-		  *  on the stack has been converted to unsigned.  The value is
-		  *  saved as a int (rather than a shorter type) so that the 
-		  *  value does not get sign extended on each new load, 
-		  *  necessitating a new conversion each time.
-          *)
-		  temp := out.proc.newLocal(hstT);
-		  ixSv := out.proc.newLocal(Bi.intTp);
-		  out.Code(Asm.opc_dup);         (* TOS: lOp, lOp, ...               *)
-		  out.StoreLocal(temp);          (* TOS: lOp, ...                    *)
-          e.PushValue(rOp, rOp.type);    (* TOS: rOp, lOp, ...               *)
-		  out.PushInt(rtSz-1);           (* TOS: 31, rOp, lOp, ...           *)
-          out.Code(Asm.opc_and);         (* TOS: rOp', lOp, ...              *)
-		  out.Code(Asm.opc_dup);         (* TOS: rOp', rOp', lOp, ...        *)
-		  out.StoreLocal(ixSv);          (* TOS: rOp', lOp, ...              *)
-		  out.Code(Asm.opc_shl);         (* TOS: lRz, ...    (left fragment) *)
-		  out.PushLocal(temp);           (* TOS: lOp, lRz, ...               *)
-		  out.PushInt(rtSz);             (* TOS: 32, lOp, lRz, ...           *)
-		  out.PushLocal(ixSv);           (* TOS: rOp', 32, lOp, lRz, ...     *)
-		  out.Code(Asm.opc_sub);         (* TOS: rOp'', lOp, lRz, ...        *)
-	     (* mask the shift amount in case idx = 0 *)
-		  out.PushInt(rtSz-1);           (* TOS: 31, rOp, lOp, ...           *)
-          out.Code(Asm.opc_and);         (* TOS: rOp', lOp, ...              *)
-		  out.Code(Asm.opc_shr_un);      (* TOS: rRz, lRz, ...               *)
-		  out.Code(Asm.opc_or);          (* TOS: ROT(lOp,rOp), ...           *)
-		  out.proc.ReleaseLocal(ixSv);
-		  out.proc.ReleaseLocal(temp);
-		  out.ConvertDn(hstT, lOp.type, FALSE);
-        END;
-    (* -------------------------------- *)
     | Xp.strCat :
         e.PushValue(lOp, lOp.type);
         e.PushValue(rOp, rOp.type);