-- where Bits is the shift count mod Esize (the mod operation here
-- deals with ludicrous large shift counts, which are apparently OK).
- -- What about nonbinary modulus ???
+ if Modify_Tree_For_C then
+ declare
+ Loc : constant Source_Ptr := Sloc (N);
+ Rtp : constant Entity_Id := Etype (Right_Opnd (N));
+ Typ : constant Entity_Id := Etype (N);
- declare
- Loc : constant Source_Ptr := Sloc (N);
- Rtp : constant Entity_Id := Etype (Right_Opnd (N));
- Typ : constant Entity_Id := Etype (N);
+ begin
+ -- Sem_Intr should prevent getting there with a non binary modulus
+
+ pragma Assert (not Non_Binary_Modulus (Typ));
- begin
- if Modify_Tree_For_C then
Rewrite (Right_Opnd (N),
Make_Op_Rem (Loc,
Left_Opnd => Relocate_Node (Right_Opnd (N)),
Duplicate_Subexpr_No_Checks (Right_Opnd (N))))));
Analyze_And_Resolve (N, Typ);
- end if;
- end;
+ end;
+ end if;
end Expand_N_Op_Rotate_Left;
------------------------------
-- where Bits is the shift count mod Esize (the mod operation here
-- deals with ludicrous large shift counts, which are apparently OK).
- -- What about nonbinary modulus ???
+ if Modify_Tree_For_C then
+ declare
+ Loc : constant Source_Ptr := Sloc (N);
+ Rtp : constant Entity_Id := Etype (Right_Opnd (N));
+ Typ : constant Entity_Id := Etype (N);
- declare
- Loc : constant Source_Ptr := Sloc (N);
- Rtp : constant Entity_Id := Etype (Right_Opnd (N));
- Typ : constant Entity_Id := Etype (N);
+ begin
+ -- Sem_Intr should prevent getting there with a non binary modulus
- begin
- Rewrite (Right_Opnd (N),
- Make_Op_Rem (Loc,
- Left_Opnd => Relocate_Node (Right_Opnd (N)),
- Right_Opnd => Make_Integer_Literal (Loc, Esize (Typ))));
+ pragma Assert (not Non_Binary_Modulus (Typ));
+
+ Rewrite (Right_Opnd (N),
+ Make_Op_Rem (Loc,
+ Left_Opnd => Relocate_Node (Right_Opnd (N)),
+ Right_Opnd => Make_Integer_Literal (Loc, Esize (Typ))));
- Analyze_And_Resolve (Right_Opnd (N), Rtp);
+ Analyze_And_Resolve (Right_Opnd (N), Rtp);
- if Modify_Tree_For_C then
Rewrite (N,
Make_Op_Or (Loc,
Left_Opnd =>
Duplicate_Subexpr_No_Checks (Right_Opnd (N))))));
Analyze_And_Resolve (N, Typ);
- end if;
- end;
+ end;
+ end if;
end Expand_N_Op_Rotate_Right;
----------------------------
Hi : Uint;
begin
+ -- Sem_Intr should prevent getting there with a non binary modulus
+
+ pragma Assert (not Non_Binary_Modulus (Typ));
+
if Compile_Time_Known_Value (Right) then
if Expr_Value (Right) >= Siz then
Rewrite (N, Make_Integer_Literal (Loc, 0));
Binary_Op_Validity_Checks (N);
-- If we are in Modify_Tree_For_C mode, there is no shift right
- -- arithmetic in C, so we rewrite in terms of logical shifts.
+ -- arithmetic in C, so we rewrite in terms of logical shifts for
+ -- modular integers, and keep the Shift_Right intrinsic for signed
+ -- integers: even though doing a shift on a signed integer is not
+ -- fully guaranteed by the C standard, this is what C compilers
+ -- implement in practice.
+ -- Consider also taking advantage of this for modular integers by first
+ -- performing an unchecked conversion of the modular integer to a signed
+ -- integer of the same sign, and then convert back.
-- Shift_Right (Num, Bits) or
-- (if Num >= Sign
-- Here Mask is all 1 bits (2**size - 1), and Sign is 2**(size - 1)
- -- Note: in almost all C compilers it would work to just shift a
- -- signed integer right, but it's undefined and we cannot rely on it.
-
-- Note: the above works fine for shift counts greater than or equal
-- to the word size, since in this case (not (Shift_Right (Mask, bits)))
-- generates all 1'bits.
- -- What about nonbinary modulus ???
+ if Modify_Tree_For_C and then Is_Modular_Integer_Type (Etype (N)) then
+ declare
+ Loc : constant Source_Ptr := Sloc (N);
+ Typ : constant Entity_Id := Etype (N);
+ Sign : constant Uint := 2 ** (Esize (Typ) - 1);
+ Mask : constant Uint := (2 ** Esize (Typ)) - 1;
+ Left : constant Node_Id := Left_Opnd (N);
+ Right : constant Node_Id := Right_Opnd (N);
+ Maskx : Node_Id;
- declare
- Loc : constant Source_Ptr := Sloc (N);
- Typ : constant Entity_Id := Etype (N);
- Sign : constant Uint := 2 ** (Esize (Typ) - 1);
- Mask : constant Uint := (2 ** Esize (Typ)) - 1;
- Left : constant Node_Id := Left_Opnd (N);
- Right : constant Node_Id := Right_Opnd (N);
- Maskx : Node_Id;
+ begin
+ -- Sem_Intr should prevent getting there with a non binary modulus
- begin
- if Modify_Tree_For_C then
+ pragma Assert (not Non_Binary_Modulus (Typ));
-- Here if not (Shift_Right (Mask, bits)) can be computed at
-- compile time as a single constant.
Maskx,
Make_Integer_Literal (Loc, 0)))));
Analyze_And_Resolve (N, Typ);
- end if;
- end;
+ end;
+ end if;
end Expand_N_Op_Shift_Right_Arithmetic;
--------------------------
end if;
case Nam is
- when Name_Shift_Left => Eval_Shift (N, E, N_Op_Shift_Left);
- when Name_Shift_Right => Eval_Shift (N, E, N_Op_Shift_Right);
- when others => null;
+ when Name_Shift_Left =>
+ Eval_Shift (N, E, N_Op_Shift_Left);
+ when Name_Shift_Right =>
+ Eval_Shift (N, E, N_Op_Shift_Right);
+ when Name_Shift_Right_Arithmetic =>
+ Eval_Shift (N, E, N_Op_Shift_Right_Arithmetic);
+ when others =>
+ null;
end case;
end Eval_Intrinsic_Call;
end Check_Elab_Call;
begin
- -- Evaluate logical shift operators on binary modular types
-
- if Is_Modular_Integer_Type (Typ)
- and then not Non_Binary_Modulus (Typ)
- and then Compile_Time_Known_Value (Left)
+ if Compile_Time_Known_Value (Left)
and then Compile_Time_Known_Value (Right)
then
+ pragma Assert (not Non_Binary_Modulus (Typ));
+
if Op = N_Op_Shift_Left then
Check_Elab_Call;
elsif Op = N_Op_Shift_Right then
Check_Elab_Call;
- -- Fold Shift_Right (X, Y) by computing X / 2**Y
+ -- Fold Shift_Right (X, Y) by computing abs X / 2**Y
Fold_Uint
(N,
- Expr_Value (Left) / (Uint_2 ** Expr_Value (Right)),
+ abs Expr_Value (Left) / (Uint_2 ** Expr_Value (Right)),
Static => Static);
+
+ elsif Op = N_Op_Shift_Right_Arithmetic then
+ Check_Elab_Call;
+
+ declare
+ Two_Y : constant Uint := Uint_2 ** Expr_Value (Right);
+ Modulus : Uint;
+ begin
+ if Is_Modular_Integer_Type (Typ) then
+ Modulus := Einfo.Modulus (Typ);
+ else
+ Modulus := Uint_2 ** RM_Size (Typ);
+ end if;
+
+ -- X / 2**Y if X if positive or a small enough modular integer
+
+ if (Is_Modular_Integer_Type (Typ)
+ and then Expr_Value (Left) < Modulus / Uint_2)
+ or else
+ (not Is_Modular_Integer_Type (Typ)
+ and then Expr_Value (Left) >= 0)
+ then
+ Fold_Uint (N, Expr_Value (Left) / Two_Y, Static => Static);
+
+ -- -1 (aka all 1's) if Y is larger than the number of bits
+ -- available or if X = -1.
+
+ elsif Two_Y > Modulus
+ or else Expr_Value (Left) = Uint_Minus_1
+ then
+ if Is_Modular_Integer_Type (Typ) then
+ Fold_Uint (N, Modulus - Uint_1, Static => Static);
+ else
+ Fold_Uint (N, Uint_Minus_1, Static => Static);
+ end if;
+
+ -- Large modular integer, compute via multiply/divide the
+ -- following: X >> Y + (1 << Y - 1) << (RM_Size - Y)
+
+ elsif Is_Modular_Integer_Type (Typ) then
+ Fold_Uint
+ (N,
+ (Expr_Value (Left)) / Two_Y
+ + (Two_Y - Uint_1)
+ * Uint_2 ** (RM_Size (Typ) - Expr_Value (Right)),
+ Static => Static);
+
+ -- Negative signed integer, compute via multiple/divide the
+ -- following:
+ -- (Modulus + X) >> Y + (1 << Y - 1) << (RM_Size - Y) - Modulus
+
+ else
+ Fold_Uint
+ (N,
+ (Modulus + Expr_Value (Left)) / Two_Y
+ + (Two_Y - Uint_1)
+ * Uint_2 ** (RM_Size (Typ) - Expr_Value (Right))
+ - Modulus,
+ Static => Static);
+ end if;
+ end;
end if;
end if;
end Fold_Shift;