gm2
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Portability Considerations (was: gm2 internal compiler error report)


From: Benjamin Kowarsch
Subject: Portability Considerations (was: gm2 internal compiler error report)
Date: Sat, 16 Mar 2024 14:01:58 +0900

I feel it is worthwhile emphasising portability as an issue.

Unfortunately, Modula-2 is very bad when it comes to portability.

The absence of ISO-only features/syntax from PIM isn't even the biggest issue. There are incompatibilities between different versions of PIM which need to be worked around, and there are plenty of incompatibilities between compilers even when using the same dialect.

Some basic math, bitset and bit operations are among the troublespots.

For example the semantics of unary minus is entirely undefined in PIM and implementation dependent. Different compilers implement semantics differently, either following mathematically correct precedence or following precedence implied by Wirth's EBNF grammar.

The cardinality of type BITSET is implementation defined. Thus when using BITSET, one cannot rely on how many bits it will support when the code is compiled on a different compiler, or simply for a different target or dialect.

Bit operations are not even defined in PIM, every compiler provided its own incompatible extensions or library functions. And although individual bits of numeric values can in principle be tested using DIV and MOD, the semantics of those operations vary between different versions of PIM and between PIM and ISO. All this needs to be worked around.

ISO does have built-in bit operations but those are not available in PIM.

In order to write code that is portable between dialects, versions and compilers, it is advisable to avoid all those troublespots in one's code altogether and instead write a library that provides low-level functions with clearly defined semantics across dialects and implementations.

The most important functions are ...

(* Unary minus *)
PROCEDURE NEG ( i : INTEGER ) : INTEGER;

(* Euclidean integer division *)
PROCEDURE ediv ( i, j : INTEGER ) : INTEGER;
PROCEDURE emod ( i, j : INTEGER ) : INTEGER;

https://github.com/m2sf/m2bsk/blob/master/src/lib/IntMath.def

(* Floored integer division *)
PROCEDURE fdiv ( i, j : INTEGER ) : INTEGER;
PROCEDURE fmod ( i, j : INTEGER ) : INTEGER;

https://github.com/m2sf/m2bsk/blob/master/src/lib/IntMath.def

(* Truncated integer division *)
PROCEDURE tdiv ( i, j : INTEGER ) : INTEGER;
PROCEDURE tmod ( i, j : INTEGER ) : INTEGER;

https://github.com/m2sf/m2bsk/blob/master/src/lib/IntMath.def

(* Test bit at index *)
PROCEDURE BIT ( value : T1; bitIndex : T2 ) : BOOLEAN;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#bit

(* Set bit at index *)
PROCEDURE SETBIT ( VAR target : T1; bitIndex : T2; bit : BOOLEAN );

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#setbit

(* Shift left *)
PROCEDURE SHL ( value : T1; shiftFactor : T2 ) : T3;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#shl

(* Shift right *)
PROCEDURE SHR ( value : T1; shiftFactor : T2 ) : T3;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#shr

(* Shift arithmetically right *)
PROCEDURE ASHR ( value : T1; shiftFactor : T2 ) : T3;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#ashr

(* Bitwise NOT *)
PROCEDURE BWNOT ( value : T1 ) : T2;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#bwnot

(* Bitwise AND *)
PROCEDURE BWAND ( value : T1; mask : T2 ) : T3;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#bwand

(* Bitwise OR *)
PROCEDURE BWOR ( value : T1; mask : T2 ) : T3;

https://github.com/m2sf/m2bsk/wiki/Language-Specification-(11)-:-Low-Level-Facilities#bwor


Portable math and bit operations for types CARDINAL, INTEGER and LONGINT are in our github repo:

https://github.com/m2sf/m2bsk/blob/master/src/lib/CardMath.def
https://github.com/m2sf/m2bsk/blob/master/src/lib/CardBitOps.def

https://github.com/m2sf/m2bsk/blob/master/src/lib/IntMath.def
https://github.com/m2sf/m2bsk/blob/master/src/lib/IntBitOps.def

https://github.com/m2sf/m2bsk/blob/master/src/lib/LongIntMath.def
https://github.com/m2sf/m2bsk/blob/master/src/lib/LongIntBitOps.def

implementations are in:
https://github.com/m2sf/m2bsk/tree/master/src/lib/imp


Note that if performance is a concern with portable code, it is always possible to have different implementations for the same interfaces, for example dialect or compiler specific implementations so as to then take advantage of any (possibly) more efficient underlying implementation. Also, modern compilers can often inline such function calls. Don't commit the crime of premature optimisation ;-)

The interfaces should always be the same though.

regards
benjamin

On Fri, 15 Mar 2024 at 07:52, Alice Osako wrote:
Benjamin Kowarsch:
Since you mentioned you wanted the code to be portable across PIM and ISO ...

On Fri, 15 Mar 2024 at 00:03, Alice Osako wrote:

TYPE
   Octet = PACKEDSET OF CARDINAL [0 .. 0FFH];

PACKEDSET is only available in ISO but not PIM.

Portable across PIM and ISO would be:

TYPE Octet = CARDINAL [0 .. 255];

Ah, thank you for pointing that out. I will change that now.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]