Hi,
during some debugging I noticed that GPC generates very hairy code for the `mod' operator with signed arguments. A closer look revealed that the code contains a lot of branching on conditions which can be determined on compile time, and most surprisingly, the value of X mod Y is recomputed _twice_, if X is negative. (I've attached a commented assembler dump.)
This may be partially a back-end problem (CSE?), but I feel there's something wrong in the relevant code in gpc-common.c (build_pascal_binary_op) as well. `exp1 mod exp2' gets translated into something like
exp2 <= 0 ? error : exp1 >= 0 ? exp1 mod exp2 : ((-exp1) mod exp2) == 0 ? 0 : exp2 - (-exp1) mod exp2
This arranges that both arguments supplied to the "internal mod" are non-negative, but this information is not passed on -- the arguments are still declared as signed types, which apparently makes the back-end to produce the mess it does. I've done a little test: the following replacement for `mod', which differs from the built-in one essentially just by the type-casts to `Cardinal',
inline operator xmod (X, Y: Integer) = Z: Integer; begin if Y <= 0 then RuntimeError (714); if X >= 0 then Z := Cardinal (X) mod Y else begin Z := Cardinal (-X) mod Y; if Z <> 0 then Z := Y - Z end end;
indeed generates a better code, it measures about 15% faster for positive X and more than twice faster for negative X. YMMV. If this is possible in user code, it should work in the compiler internals as well :)
The result type of the whole `mod' expression looks dubious too. IIUIC, `exp1 mod exp2' inherits the type of exp1. Now, the result of `mod' is guaranteed to be nonnegative and to fit in the type of exp2 (or more precisely, it is in 0 .. High (type of (exp2)) - 1), whereas it needn't have anything to do with the type of exp1: consider
var X, Z: Integer; Y: LongInt;
begin X := -8; Y := High (LongInt); Z := X mod Y { -> overflow } end
Or am I missing something?
Emil
(BTW, where is fjf434c.pas?)