gm2(1)

๐Ÿ“ฆ version 9.5.0, in ubuntu jammy, ubuntu noble

โœจ gm2(1) โœจ

Okay bestie, this is the manual page for GNU Modula-2 ๐Ÿ’– the gm2(1) page, and it's giving everything you need to know about the compiler, the linker helpers, and the makefile tools ๐Ÿ’…

โœจ NAME โœจ

  • gm2 โ€” the GNU Modula-2 compiler, obviously ๐Ÿ‘‘
  • gm2l โ€” a GNU Modula-2 linker subcomponent ๐Ÿ”—
  • gm2lcc โ€” another GNU Modula-2 linker subcomponent ๐Ÿ”—
  • gm2lgen โ€” yep, a GNU Modula-2 linker subcomponent too ๐Ÿ”—
  • gm2lsub โ€” the last linker subcomponent in the lineup ๐Ÿ”—
  • gm2m โ€” the GNU Modula-2 makefile generator ๐Ÿงš
  • h2def โ€” turns a 'C' header file into a Modula-2 definition file ๐Ÿฆ‹

โœจ DESCRIPTION โœจ

๐Ÿ’€ NOTE: This manual page might be out of date, so check the texinfo file.

Okay so GNU Modula-2 is a front end for GCC (the GNU Compiler Collection), see http://gcc.gnu.org/frontends.html ๐ŸŒˆ GCC has a retargetable C compiler that's been ported to a huge number of architectures and operating systems. GNU Modula-2 uses the back end of GCC and swaps the C language front end out for a Modula-2 one ๐Ÿซถ

The GNU Modula-2 front end can do a substantial amount of static analysis on your source code ๐Ÿ” Check out -Wpedantic, -Wpedantic-param-names, -Wstudents, and -Wpedantic-cast for that.

And then there's runtime checking, which is literally so iconic ๐Ÿ˜ญ It can check the bounds of subranges and array indexes, that functions actually execute a RETURN statement, that a pointer doesn't dereference a NIL pointer value, and that the expression inside a CASE statement is correctly matched ๐Ÿ’–

There are currently three sets of libraries ๐Ÿ“š the 'Programming in Modula-2' libraries, the 'University of ULM libraries', and the ISO libraries. The ISO libraries are still being written, but all the definition modules for all three library sets are included in this document โœจ

GNU Modula-2 is also super tight with GCC ๐Ÿงš which means the code is excellent and the architecture and operating system coverage is excellent too. It also borrows a bunch of GCC features. For example, GNU Modula-2 can invoke the C preprocessor to handle conditional compilation ๐ŸŽ€ Inlining of SYSTEM procedures, intrinsic functions, and memory copying routines also get exploited, and you can access assembly language using GCC syntax. Plus, GNU Modula-2 supports sets of any ordinal type, memory permitting ๐Ÿฆ‹

Here's how it reads source code. It takes files containing either (1) definition modules, (2) implementation modules, or (3) a main body module ๐Ÿซถ Definition modules usually use the standard extension .def, while implementation and main body modules usually use the standard extension .mod. If you're working with legacy code that was written for a different compiler, other extensions might be in play. GNU Modula-2 can be told to search for those extensions first, before it falls back to the standard ones. For example, you'd use the command line options -fdef=.d2 -fmod=.m2 ๐Ÿ’…

Also, GNU Modula-2 imports definition modules by reading the definition module file every single time, kind of like how C includes .h header files ๐ŸŒธ Traditional Modula-2 compilers, on the other hand, would need every definition module compiled into a symbol file before anything could be imported from it. Lowkey a different energy ๐Ÿ’€

๐ŸŒท Purpose

It's expected that the main purpose of GNU Modula-2 will be compiling legacy code ๐Ÿ•ฐ๏ธ Right now there are only a few commercial Modula-2 compilers that are actively maintained. Code that was written ten or fifteen years ago might still get compiled by older commercial compilers, possibly ones that aren't maintained anymore ๐Ÿ™ˆ

While 32 bit x86 is still around, these compilers can presumably run in compatibility mode (some of them only produced 16 bit code). Time is running out though, since the computing industry is switching over to 64 bit microprocessors โณ x86 emulation or 16 bit backwards compatibility is always an option, but it comes with some serious drawbacks ๐Ÿ˜ฌ

For the older source to run natively, the source code will either have to be translated into another high level language, or you'll need a Modula-2 compiler that can target this new generation of microprocessors ๐Ÿ”ฅ

๐ŸŒŸ Current State

Right now GNU Modula-2 (0.49) implements all PIM dialects of the language, and eventually it'll be fully compliant with ISO Modula-2 ๐Ÿ‘‘ It has been built on i[3456]86 GNU/Linux, i[3456]86 BSD, Opteron LP64 GNU/Linux, and sparc GNU/Linux systems. It's also been built as a cross compiler for MinGW and StrongARM GNU/Linux ๐ŸŒˆ

GNU Modula-2 builds and passes all of its regression tests on Debian Pure 64 (LP64 architecture), 64 bit Solaris, 32 bit x86 GNU/Linux (Suse 9.1, Debian, stable and unstable), and 32 bit x86 FreeBSD ๐Ÿ’–

Work is underway to build and test it on DARWIN/i386 and DARWIN/powerpc ๐Ÿฆ‹

And there's even a project to extend GNU Modula-2 with object oriented constructs similar to Objective-C, called 'Objective Modula-2', or objM2 for short ๐Ÿงšโœจ

โœจ OPTIONS โœจ

This section covers the compiler's command line options that are specific to GNU Modula-2 ๐Ÿ’… A bunch of the standard options are documented here too. For details on generic options, check the manual page for gcc (The GNU C Compiler) and the other members of the GNU Compiler Collection ๐Ÿซถ

๐ŸŽ€ Overall Options

  • --help โ€” prints (on the standard output) a description of the command line options gm2 understands ๐Ÿ“œ If -v is also specified, then --help gets passed on to the various processes gm2 invokes, so they can show the command line options they accept. If -f is also specified, then command line options with no documentation attached will get displayed too ๐Ÿ’œ
  • --target-help โ€” prints (on the standard output) a description of the target specific command line options for each tool ๐ŸŽฏ
  • --version โ€” displays the version number and copyrights of the invoked GCC ๐ŸŒธ
  • -c โ€” compiles and assembles the source files, but does not link ๐Ÿ™…โ€โ™€๏ธ The linking stage simply doesn't happen, so the ultimate output is an object file for each source file ๐Ÿฆ‹
  • -E โ€” stops after the preprocessing stage and doesn't run the compiler proper โœจ The output is preprocessed source code, which gets sent to the standard output ๐Ÿ“ค
  • -g โ€” inserts debugging symbols into the object files and the executable ๐Ÿ› NOTE: when you're using the debugger, it may help to not optimize at a high level ๐Ÿ™ˆ
  • -Ipath โ€” adds an include path ๐Ÿ—บ๏ธ The GNU Modula-2 compiler needs to import symbols from definition module files, so it includes the definition file rather than reading a symbol file ๐Ÿ’–
  • -On โ€” sets the optimization level and kind ๐Ÿ”ฅ -Os optimizes for size, which is great if you want small executables ๐Ÿ“ -O, -O1, -O2, ..., -O6 optimize for speed, with increasingly powerful techniques like loop unrolling and reordering of code ๐Ÿš€ When you're debugging, only use -O, or at the most -O1 ๐Ÿซถ
  • -o file โ€” places the output in file (yes, the file is called file ๐Ÿ˜ญ) This applies no matter what kind of output is being produced, whether it's an executable file, an object file, an assembler file, or preprocessed C code ๐Ÿ’…

Since only one output file can be specified, it doesn't make sense to use -o when compiling more than one input file, unless you're producing an executable file as the output ๐Ÿ™…โ€โ™€๏ธ

If you don't specify -o, the default is to put an executable file in a.out, the object file for source.suffix in source.o, its assembly language file in source.s, and all preprocessed C source on standard output ๐Ÿ“„

  • -S โ€” preprocesses and compiles the source code but doesn't assemble it ๐Ÿงฉ The temporary assembly language file isn't deleted. It stops after the compilation proper stage and doesn't assemble, so the output is an assembly language code file for each non-assembler input file you gave it ๐ŸŒˆ
  • -v โ€” verbosely displays all calls to subsidiary programs, like the C preprocessor and the GNU Modula-2 compiler and linker ๐Ÿ“ข It also prints the version of the compiler and which specs file it's using ๐Ÿค“
  • -fcpp โ€” preprocesses the source with cpp -lang-asm -traditional-cpp ๐Ÿงš If -fcpp is supplied, then all the definition modules and implementation modules that get parsed will be preprocessed by cpp ๐Ÿ’œ
  • -fdef= โ€” recognizes the suffix you specify as a definition module filename ๐ŸŽ€ The default definition module filename suffix is .def. If you use this option, GNU Modula-2 will still fall back to .def if a requested definition module isn't found ๐Ÿซถ
  • -fmod= โ€” recognizes the suffix you specify as implementation and module filenames ๐ŸŒธ The default implementation and module filename suffix is .mod. If you use this option, GNU Modula-2 will still fall back to .mod if it needs to read an implementation module and the suffixed filename doesn't exist. This option and -fdef= both also work with the -fmakeall option ๐Ÿ’–
  • -fextended-opaque โ€” allows opaque types to be implemented as any type ๐Ÿฆ‹ This is a GNU Modula-2 extension, and it requires that the implementation module defining the opaque type is available, so it can be resolved when compiling the module that imports the opaque type ๐Ÿ”
  • -fmakeall โ€” generates a temporary makefile, then builds all the dependent modules and links them ๐Ÿ—๏ธ
  • -fmakelist โ€” this option is only applicable when linking a program module โœจ The compiler generates a modulename.lst file, which lists the initialization order of all the modules that are going to be linked ๐Ÿ“œ The actual link doesn't happen. The GNU Modula-2 linker scans all IMPORTs, generates a list of dependencies, and produces an ordered list for initialization โšก It'll probably get the order wrong if your project has cyclic dependencies, but the .lst file is plain text, so you can modify it if you need to ๐Ÿ’… Once the .lst file exists, the compiler can use it to link your project through the -fuselist option ๐Ÿ”— It has no effect if the -c option is present ๐Ÿ™ˆ
  • -fsources โ€” displays the path to the source of each module ๐Ÿ—บ๏ธ
  • -fstatistics โ€” generates quadruple information: the number of quadruples generated, and the number of quadruples remaining after optimization ๐Ÿ“Š Quadruples are the intermediate language the GNU Modula-2 compiler uses internally ๐Ÿค“
  • -fuselist โ€” as long as gm2 has been told to link the program module, this option uses the file modulename.lst for the initialization order of modules ๐Ÿซถ
  • -Wverbose-unbounded โ€” okay so this one's a total tattletale ๐Ÿซฃ It tells you which non VAR unbounded parameters are gonna get passed by reference. It only says anything if you also pass -funbounded-by-reference on the command line, so pair them up, bestie ๐Ÿ’•
  • -funbounded-by-reference โ€” the glow-up option โœจ It tries to pass non VAR unbounded parameters by reference, which skips the implicit copy inside the callee procedure. GNU Modula-2 only lets unbounded parameters go by reference if, inside the callee, they're not written to, no address is calculated on the array, and they aren't passed as a VAR parameter. Those rules are non-negotiable ๐Ÿ™…โ€โ™€๏ธ
NOTE: It's possible to write code that breaks this optimization, so use it carefully!! For example, you could take the address of an array, pass the address and the array to a procedure, read from the array in the procedure, and write to the location using the address parameter. Chaos ๐Ÿ’€
Due to the dangerous nature of this option, it is not enabled when the -O option is specified. Hard pass on mixing these ๐Ÿ™ˆ

โœจ Runtime Checking Options โœจ

  • -fbounds โ€” tells the compiler to generate code that checks the bounds of subranges and array indexes ๐Ÿง
  • -freturn โ€” generates code that checks functions always exit with a RETURN and never just fall out the end ๐Ÿซถ
  • -fnil โ€” generates code that catches, at runtime, any attempt to access data through a NIL pointer value ๐Ÿšจ
  • -fcase โ€” generates code that detects at runtime whether a CASE statement needs an ELSE clause when you didn't write one. It adds an implicit ELSE to any CASE statements that don't define one. It just prints an error message and exits with an error code ๐Ÿ˜ญ
  • -fsoft-check-all โ€” turns on all the runtime checks ๐Ÿ”ฅ It's the same as invoking GNU Modula-2 with -fbounds, -freturn, -fnil, -fcase, -frange, ,-findex, -fwholediv

โœจ Style Checking Options โœจ

  • -Wstudents โ€” checks for bad programming style ๐Ÿ‘€ This one's aimed at new Modula-2 users, because it looks for situations that might cause confusion and therefore mistakes. It checks whether variables of the same name are declared in different scopes, and whether variables look like keywords. Experienced users might find it too aggressive ๐Ÿ™ˆ
  • -Wpedantic โ€” main character energy for the compiler ๐Ÿ’… It forces the compiler to reject nested WITH statements referencing the same record type, and doesn't allow multiple imports of the same item from a module. It also checks that:
    • procedure variables are written to before they're read
    • variables aren't only written to but also read from
    • variables are declared and used

If the compiler runs into a variable being read before it's written, it terminates with a message. It also checks that FOR loop indices aren't used outside the end of the loop without being reset ๐Ÿซฃ

  • -Wpedantic-param-names โ€” checks procedure parameter names in the definition module against their implementation module counterparts. This isn't necessary in ISO or PIM versions of Modula-2, but it can be extremely useful, as long as you're doing it on purpose ๐ŸŽ€
  • -Wpedantic-cast โ€” warns if the ISO system function is used and the size of the variable is different from the size of the type. It's legal in ISO Modula-2, but it can be dangerous ๐Ÿ’€ Some users may prefer VAL in these situations, and use CAST exclusively for type changes on objects that have the same size ๐Ÿฆ‹

โœจ Language Dialect Options โœจ

  • -fiso โ€” turns on ISO standard features ๐ŸŒธ Right now it enables the ISO SYSTEM module and alters the default library search path so the ISO libraries get searched before the PIM libraries. It also affects how the DIV and MOD operators behave.
  • -fpim โ€” turns on PIM standard features ๐Ÿ“ Right now it enables the PIM SYSTEM module and determines which identifiers are pervasive (declared in the base module). If no other -fpim[234] switch is used, division and modulus behave as defined in PIM4. (See LANGUAGE DIALECTS.)
  • -fpim2 โ€” turns on PIM-2 standard features ๐Ÿง Right now it removes SIZE from being a pervasive identifier (declared in the base module) and places SIZE in the SYSTEM module instead. It also affects how DIV and MOD behave.
  • -fpim3 โ€” turns on PIM-3 standard features. Right now it only affects how the DIV and MOD operators behave.
  • -fpim4 โ€” turns on PIM-4 standard features. Right now it only affects how the DIV and MOD operators behave.
  • -fpositive-mod-floor-div โ€” forces the DIV and MOD operators to behave as PIM4 defines them ๐Ÿ‘‘ All modulus results come out positive, and division results get rounded to the floor.
NOTE: Check out the LANGUAGE DIALECTS section for a table describing these results ๐Ÿ“Š

โœจ Library Search Path Options โœจ

  • -flibs=ulm โ€” tweaks the default library search path so the University of Ulm libraries get searched before the other PIM libraries ๐ŸŒˆ
  • -flibs=pim โ€” tweaks the default library search path so the PIM libraries get searched before any others. This is the default, bestie ๐Ÿ’œ
  • -flibs=pim-coroutine โ€” tweaks the default library search path so the PIM SYSTEM module that provides coroutine support gets searched before the base PIM libraries. This directory also includes lots of coroutine-related libraries ๐Ÿงš
  • -flibs=iso โ€” tweaks the default library search path so the ISO libraries get searched before any others. You don't need this if you already specified -fiso ๐Ÿซถ
  • -flibs=logitech โ€” tweaks the default library search path so the Logitech compatible libraries get searched before the base PIM libraries โญ
  • -flibs=min โ€” tweaks the default library search path so the minimal set of runtime libraries gets searched before any others. Super useful if you're targeting a small memory embedded device ๐Ÿ”‹

๐ŸŒˆ LANGUAGE DIALECTS ๐ŸŒˆ

Okay so this section is all about the dialects GNU Modula-2 understands ๐Ÿง  It also breaks down how the dialects differ from each other, and which command line switches control dialect behavior. Let's get into it ๐Ÿ’–

The GNU Modula-2 compiler is based on the language as defined in:

  • PIM2 โ€” ยดProgramming in Modula-2ยด, 2nd Edition, Springer Verlag, 1982, 1983 by Niklaus Wirth,
  • PIM3 โ€” ยดProgramming in Modula-2ยด, 3rd Corrected Edition, Springer Verlag, 1985 by Niklaus Wirth, and
  • PIM4 โ€” ยดProgramming in Modula-2ยด, 4th Edition, Springer Verlag, 1988, by Niklaus Wirth.
  • ISO โ€” The International Standards Organization's ISO/IEC 10514-1:1996 Information Technology -- Programming Languages -- Part-1: Modula-2, Base Language

It also includes ISO M2 features and GNU Modula-2 extensions ๐Ÿฆ‹ Currently GNU Modula-2 (0.49) implements all PIM dialects of the language. Eventually, GNU Modula-2 will be fully compliant with the ISO Modula-2 Base Language. It supports neither ISO Modula-2 Generics, nor ISO Object Oriented Modula-2 ๐Ÿ™…โ€โ™€๏ธ

The command line switches -fpim2, -fpim3, -fpim4, and -fiso can be used to force mutually exclusive features. However, by default the compiler won't aggressively fail if a non mutually exclusive feature is used from another dialect. For example, it's possible to specify -fpim2 and still utilize DEFINITION MODULES which have no export list ๐Ÿคญ

Some dialect differences will force a compile time error. For example, in PIM2 the user must IMPORT SIZE from the module SYSTEM, whereas in PIM3 and PIM4 SIZE is a pervasive function. So compiling PIM4 source code with the -fpim2 switch will cause a compile time error ๐Ÿ˜ญ This can be fixed quickly with an additional IMPORT, or alternatively by compiling with the -fpim4 switch.

However, there are some very important differences between the dialects which are mutually exclusive. It's vital that users choose their dialects with care when these language features are used ๐Ÿซข

๐Ÿ’… Integer Division, Remainder and Modulus ๐Ÿ’…

The most dangerous set of mutually exclusive features in the four dialects supported by GNU Modula-2 is the INTEGER division, remainder and modulus arithmetic operators. It's important to note that the same source code can be compiled to give different runtime results depending on these switches! The reference manuals for the various Modula-2 dialects are quite clear about this behavior, and sadly there are three distinct definitions ๐Ÿ’€

The table below illustrates the problem when a negative operand is used ๐Ÿ“Š

                     Pim2/3          Pim4                ISO
                  -----------    -----------    ----------------------
    lval    rval   DIV     MOD    DIV     MOD    DIV    MOD    /    REM
    31      10      3       1      3       1      3      1     3     1
   -31      10     -3      -1     -4       9     -4      9    -3    -1
    31     -10     -3       1     -3       1     Exception    -3     1
   -31     -10      3      -1      4       9     Exception     3    -1

See also pg24 of PIM2, pg27 of PIM3, pg29 of PIM4, and pg201 of the ISO Standard ๐Ÿ“– At present, all dialect division, remainder and modulus are implemented as above, apart from the exception calling in the ISO dialect. Instead of exception handling, the results are the same as the PIM4 dialect. This is a temporary implementation situation ๐ŸŒธ

๐Ÿฆ‹ The PIM SYSTEM Module ๐Ÿฆ‹

The different dialects of Modula-2 PIM-[234] and ISO Modula-2 declare the function SIZE in different places. PIM-[34] and ISO Modula-2 declare SIZE as a pervasive function (declared in the base module). PIM-2 defined SIZE in the SYSTEM module ๐Ÿง

DEFINITION MODULE SYSTEM ;

EXPORT QUALIFIED (* the following are built into the compiler: *)
                 ADDRESS, WORD, BYTE, BITSET, ADR, TSIZE, SIZE ;

END SYSTEM.

๐Ÿงš The ISO SYSTEM Module ๐Ÿงš

DEFINITION MODULE SYSTEM;

  (* Gives access to system programming facilities that
     are probably non portable. *)

  (* The constants and types define underlying properties
     of storage *)

EXPORT QUALIFIED BITSPERLOC, LOCSPERWORD,
                 LOC, BYTE, WORD, BITSET, ADDRESS,
                 ADDADR, SUBADR, DIFADR, MAKEADR, ADR, ROTATE,
                 SHIFT, CAST, TSIZE,
                             (* Internal GM2 compiler functions *)
                             ShiftVal, ShiftLeft, ShiftRight,
                             RotateVal, RotateLeft, RotateRight ;

            CONST
                              (* <implementation-defined constant> ; *)
              BITSPERLOC    = __ATTRIBUTE__ __BUILTIN__ ((BITS_PER_UNIT)) ;
                              (* <implementation-defined constant> ; *)
              LOCSPERWORD   = __ATTRIBUTE__ __BUILTIN__ ((UNITS_PER_WORD)) ;
                              (* <implementation-defined constant> ; *)
              LOCSPERBYTE = 8 DIV BITSPERLOC ;
            (*
               all the objects below are declared internally to gm2
               ====================================================

            TYPE
              LOC; (* A system basic type. Values are the uninterpreted
                      contents of the smallest addressable unit of storage *)
              ADDRESS = POINTER TO LOC;
              WORD = ARRAY [0 .. LOCSPERWORD-1] OF LOC;

              (* BYTE and LOCSPERBYTE are provided if appropriate for machine
       *)

            TYPE
              BYTE = ARRAY [0 .. LOCSPERBYTE-1] OF LOC;

            PROCEDURE ADDADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
              (* Returns address given by (addr + offset), or may raise
                 an exception if this address is not valid.
              *)

            PROCEDURE SUBADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
              (* Returns address given by (addr - offset), or may raise an
                 exception if this address is not valid.
              *)

            PROCEDURE DIFADR (addr1, addr2: ADDRESS): INTEGER;
              (* Returns the difference between addresses (addr1 - addr2),
                 or may raise an exception if the arguments are invalid
                 or address space is non-contiguous.
              *)

            PROCEDURE MAKEADR (high: <some type>; ...): ADDRESS;
              (* Returns an address constructed from a list of values whose
                 types are implementation-defined, or may raise an
                 exception if this address is not valid.

                 In GNU Modula-2, MAKEADR can take any number of arguments
                 which are mapped onto the type ADDRESS. The first parameter
                 maps onto the high address bits and subsequent parameters map
                 onto lower address bits. For example:
                 a := MAKEADR(BYTE(0FEH), BYTE(0DCH), BYTE(0BAH), BYTE(098H),
                              BYTE(076H), BYTE(054H), BYTE(032H), BYTE(010H)) ;

                 then the value of, a, on a 64 bit machine is:
                 0FEDCBA9876543210H

                 The parameters do not have to have the same type,
                 but constants _must_ be typed.
              *)

            PROCEDURE ADR (VAR v: <anytype>): ADDRESS;
              (* Returns the address of variable v. *)

            PROCEDURE ROTATE (val: <a packedset type>;
                              num: INTEGER): <type of first parameter>;
              (* Returns a bit sequence obtained from val by rotating up or
                 down (left or right) by the absolute value of num.
                 The direction is down if the sign of num is negative,
                 otherwise the direction is up.
              *)

            PROCEDURE SHIFT (val: <a packedset type>;
                             num: INTEGER): <type of first parameter>;
              (* Returns a bit sequence obtained from val by shifting up
                 or down (left or right) by the absolute value of num,
                 introducing zeros as necessary.  The direction is down
                 if the sign of num is negative, otherwise the direction
                 is up.
              *)

            PROCEDURE CAST (<targettype>; val: <anytype>): <targettype>;
              (* CAST is a type transfer function.  Given the expression
                 denoted by val, it returns a value of the type <targettype>.
                 An invalid value for the target value or a
                 physical address alignment problem may raise an exception.
              *)

            PROCEDURE TSIZE (<type>; ... ): CARDINAL;
              (* Returns the number of LOCS used to store a value of the
                 specified <type>.   The extra parameters, if present,
                 are used to distinguish variants in a variant record.
              *)
            *)

            (* The following procedures are invoked by GNU Modula-2 to
               shift non word set types. They are not part of ISO Modula-2
               but are used by GNU Modula-2 to implement the SHIFT procedure
               defined above. *)

            (*
               ShiftVal - is a runtime procedure whose job is to implement
                          the SHIFT procedure of ISO SYSTEM. GNU Modula-2 will
                          inline a SHIFT of a single WORD sized set and will
                          only call this routine for larger sets.
            *)

            PROCEDURE ShiftVal (VAR s, d: ARRAY OF BITSET;
                                SetSizeInBits: CARDINAL;
                                ShiftCount: INTEGER) ;

            (*
               ShiftLeft - performs the shift left for a multi word set.
                           This procedure might be called by the back end of
                           GNU Modula-2 depending whether amount is known at
                           compile time.
            *)

            PROCEDURE ShiftLeft (VAR s, d: ARRAY OF BITSET;
                                 SetSizeInBits: CARDINAL;
                                 ShiftCount: INTEGER) ;

            (*
               ShiftRight - performs the shift left for a multi word set.
                            This procedure might be called by the back end of
                            GNU Modula-2 depending whether amount is known at
                            compile time.
            *)

            PROCEDURE ShiftRight (VAR s, d: ARRAY OF BITSET;
                                 SetSizeInBits: CARDINAL;
                                 ShiftCount: INTEGER) ;

            (*
               RotateVal - is a runtime procedure whose job is to implement
                           the ROTATE procedure of ISO SYSTEM. GNU Modula-2
                           will inline a ROTATE of a single WORD (or less)
                           sized set and will only call this routine for
                           larger sets.
            *)

            PROCEDURE RotateVal (VAR s, d: ARRAY OF BITSET;
                                 SetSizeInBits: CARDINAL;
                                 RotateCount: INTEGER) ;

            (*
               RotateLeft - performs the rotate left for a multi word set.
                            This procedure might be called by the back end of
                            GNU Modula-2 depending whether amount is known at
                            compile time.
            *)
            PROCEDURE RotateLeft (VAR s, d: ARRAY OF BITSET;
                                  SetSizeInBits: CARDINAL;
                                  RotateCount: INTEGER) ;

            (*
               RotateRight - performs the rotate right for a multi word set.
                             This procedure might be called by the back end of
                             GNU Modula-2 depending whether amount is known
                             at compile time.
            *)

            PROCEDURE RotateRight (VAR s, d: ARRAY OF BITSET;
                                   SetSizeInBits: CARDINAL;
                                   RotateCount: INTEGER) ;

            END SYSTEM.

โœจ GM2 LANGUAGE EXTENSIONS โœจ

okay so this whole section is the GNU Modula-2 language extensions ๐Ÿ’… and it's lowkey the best part ๐Ÿ”ฅ

๐Ÿฆ‹ Abstract Types ๐Ÿฆ‹

ok so the GNU Modula-2 compiler lets abstract data types be any type ๐Ÿคฏ, not just a pointer type... but only if you pass the -fextended-opaque command line option โœจ

๐ŸŒˆ Declaration Order ๐ŸŒˆ

declarations can be made in any order ๐Ÿ™Œ whether they're types, constants, procedures, nested modules or variables. no stress about who goes first ๐Ÿ’–

๐Ÿ”Œ Interfacing to C ๐Ÿ”Œ

GNU Modula-2 also lets you hook up with C and assembly language ๐Ÿซถ

the compiler tries its absolute best to use the C calling convention wherever it can ๐Ÿ’… but some parameters have no C equivalent, so a language-specific method gets used instead. for example, unbounded arrays get passed as a struct void *address, unsigned int high ๐ŸŽ€ and the contents of these arrays are copied by callee functions when they're declared as non VAR parameters ๐Ÿ˜ญ. the VAR equivalent unbounded array parameters need no copy, but they still use the struct representation ๐Ÿ’œ

the recommended method for interfacing GNU Modula-2 to C is telling the definition module that the implementation is in the C language ๐Ÿ‘‘. you do this with the tokens DEFINITION MODULE FOR โ€œCโ€'.

     DEFINITION MODULE FOR "C" libprintf ;

     EXPORT UNQUALIFIED printf ;

     PROCEDURE printf (a: ARRAY OF CHAR; ...) ;

     END libprintf.

the UNQUALIFIED keyword in the definition module tells GNU Modula-2 not to prefix the module name to exported references in the object file ๐Ÿ™ˆ

the printf declaration says the first parameter semantically matches ARRAY OF CHAR, but since the module is for the C language it gets mapped onto 'char *' ๐Ÿงš. the token ... means a variable number of arguments (varargs), and all the parameters passed there get mapped onto their C equivalents. arrays and constant strings are passed as pointers ๐Ÿฆ‹

the hello world program can be rewritten as:

     MODULE hello ;

     FROM libprintf IMPORT printf ;

     BEGIN
        printf("hello world0)
     END hello.

and it can be compiled by: gm2 -fmakeall -g -I. hello.mod -lc.

in reality the -lc is redundant, because libc is always included in the linking process ๐Ÿ’€. it's only shown here to emphasize that the C library or object file containing printf must be present ๐Ÿซถ

๐Ÿ› ๏ธ Interfacing to Assembly Language ๐Ÿ› ๏ธ

the interface for GNU Modula-2 to assembly language is almost identical to GNU C ๐Ÿ’…. the only alterations are that the keywords asm and volatile are in capitals, following the Modula-2 convention ๐ŸŒธ

a simple, but highly non-optimal, example is below ๐Ÿ™ˆ we want to add the two CARDINALs foo and bar together and return the result.

     PROCEDURE Example (foo, bar: CARDINAL) : CARDINAL;
     VAR
        myout: CARDINAL;
     BEGIN
        ASM VOLATILE ("movl %1,%%eax; addl %2,%%eax; movl %%eax,%0"
           : "=g" (myout)           (* outputs *)
           : "g" (foo), "g" (bar)   (* inputs  *)
           : "eax");                (* we trash *)
        RETURN( myout );
     END Example;

๐ŸŽ€ Special Tokens ๐ŸŽ€

GNU Modula-2 provides support for the special tokens __LINE__, __FILE__, __FUNCTION__, and __DATE__ ๐Ÿงš. support for these tokens happens even if the -fcpp option is not supplied โœจ

here's the lowdown on these identifiers, their data types, and their values ๐Ÿ‘‡

  • Scope anywhere โ€” __LINE__ is a constant literal compatible with CARDINAL, INTEGER and WORD. Example 1234 ๐Ÿ“
  • __FILE__ (anywhere) ๐Ÿ“„ A constant string that works with a parameter of ARRAY OF CHAR, or with an ARRAY whose SIZE is >= string length. Example: "hello.mod"
  • __FUNCTION__ (procedure scope) โœจ Another constant string, compatible with ARRAY OF CHAR or an ARRAY whose SIZE is >= string length. Example: "calc"
  • __FUNCTION__ (module scope) ๐Ÿงš Example: "module hello initialization"
  • __DATE__ (anywhere) ๐Ÿ“… Constant string, compatible with ARRAY OF CHAR or an ARRAY whose SIZE is >= string length. Example: "Thu Apr 29 10:07:16 BST 2004"

โœจ Preprocessor โœจ

Okay so the preprocessor, cpp, can be summoned with the -fcpp command line option ๐Ÿ”ฎ. That runs cpp with -traditional -lang-asm, and those two options keep comments and all quotations intact. Main character preservation energy ๐Ÿ’…. gm2 treats a # in the first column as a preprocessor directive, so keep that in mind ๐Ÿซถ.

Here's a little module that calls FatalError through the ERROR macro ๐ŸŒธ

MODULE cpp ;

FROM SYSTEM IMPORT ADR, SIZE ;
FROM libc IMPORT exit, printf, malloc ;

PROCEDURE FatalError (a, file: ARRAY OF CHAR;
                         line: CARDINAL;
                         func: ARRAY OF CHAR) ;
VAR
   r: INTEGER ;
BEGIN
   r := printf("%s:%d:fatal error, %s, in %s0,
               ADR(file), line, ADR(a), ADR(func)) ;
   exit(1)
END FatalError ;

#define ERROR(X)  FatalError(X, __FILE__, __LINE__, __FUNCTION__)

VAR
   pc: POINTER TO CARDINAL;
BEGIN
   pc := malloc(SIZE(CARDINAL)) ;
   IF pc=NIL
   THEN
      ERROR('out of memory')
   END
END cpp.

๐Ÿฆ‹ Optional procedure parameter ๐Ÿฆ‹

GNU Modula-2 lets the last parameter to a procedure or function be optional ๐ŸŽ€. You make it happen by wrapping the last formal parameter in square brackets and giving it an initializer (a constant assignment).

For example, in the ISO library COROUTINES.def, NEWCOROUTINE is defined with an optional fifth argument (initProtection) which, if it's absent, is automatically replaced by NIL ๐ŸŒˆ

PROCEDURE NEWCOROUTINE (procBody: PROC; workspace: SYSTEM.ADDRESS;
                        size: CARDINAL; VAR cr: COROUTINE;
                        [initProtection: PROTECTION = NIL]);

The implementation module COROUTINES.mod implements this procedure using this syntax:

PROCEDURE NEWCOROUTINE (procBody: PROC; workspace: SYSTEM.ADDRESS;
                        size: CARDINAL; VAR cr: COROUTINE;
                        [initProtection: PROTECTION]);
BEGIN

END NEWCOROUTINE ;
๐Ÿšซ Heads up, bestie: it is illegal for this declaration to contain an initializer value for initProtection. You still have to wrap that parameter in [ and ], though, because it reminds the programmer that the last parameter was declared as optional in the definition module.

Local procedures can be declared to have an optional final parameter. In that case, the initializer is mandatory in the implementation or program module ๐Ÿ’œ

๐ŸŒˆ Builtins ๐ŸŒˆ

This section covers the built-in constants and functions in GNU Modula-2 โœจ. The following compiler constants can be accessed using the __ATTRIBUTE__ and __BUILTIN__ keywords. They aren't part of the Modula-2 language, and they may differ depending on the target architecture, but they give common libraries a way to interface with a different underlying architecture ๐Ÿงฉ.

The built-in constants are BITS_PER_UNIT, BITS_PER_WORD, BITS_PER_CHAR, and UNITS_PER_WORD. They get integrated into GNU Modula-2 through an extension to the ConstFactor rule:

ConstFactor := ConstQualidentOrSet | Number | ConstString |
               "(" ConstExpression ")" | "NOT" ConstFactor |
               ConstAttribute =:

ConstAttribute := "__ATTRIBUTE__" "__BUILTIN__" "(" "(" Ident ")" ")" =:

Here's an example taken from the ISO library SYSTEM.def ๐Ÿ“

CONST
   BITSPERLOC    = __ATTRIBUTE__ __BUILTIN__ ((BITS_PER_UNIT)) ;
   LOCSPERWORD   = __ATTRIBUTE__ __BUILTIN__ ((UNITS_PER_WORD)) ;

Built-in functions are transparent to the end user ๐Ÿ‘‘. All of them are declared in DEFINITION MODULEs and get imported as and when they're needed. You declare them in definition modules with the __BUILTIN__ keyword. Here's a bit of the ISO library LongMath.def that shows this off ๐Ÿ’–

PROCEDURE __BUILTIN__ sqrt (x: LONGREAL): LONGREAL;
   (* Returns the square root of x *)

This tells us the function sqrt will be implemented using the gcc built-in math library. If gcc can't use the built-in function (for example, if the programmer requested the address of sqrt), then code gets generated to call the alternative function implemented in the IMPLEMENTATION MODULE ๐Ÿซ 

Sometimes a function exported from the DEFINITION MODULE has a different name from the built-in function inside gcc. In those cases, the mapping between the GNU Modula-2 function name and the gcc name is expressed using the keywords __ATTRIBUTE__ __BUILTIN__ ((Ident)). For example, the function sqrt in LongMath.def maps onto the gcc built-in function sqrtl, and it's written like this ๐Ÿฆ‹

PROCEDURE __ATTRIBUTE__ __BUILTIN__ ((sqrtl)) sqrt
                                    (x: LONGREAL) : LONGREAL;
   (* Returns the positive square root of x *)

The following module, Builtins.def, lists every built-in function you can access in GNU Modula-2. It also defines the parameters and return value for each function ๐Ÿ“œ

DEFINITION MODULE Builtins ;

FROM SYSTEM IMPORT ADDRESS ;

PROCEDURE __BUILTIN__ sinf (x: SHORTREAL) : SHORTREAL ;
PROCEDURE __BUILTIN__ sin (x: REAL) : REAL ;
PROCEDURE __BUILTIN__ sinl (x: LONGREAL) : LONGREAL ;

PROCEDURE __BUILTIN__ cosf (x: SHORTREAL) : SHORTREAL ;
PROCEDURE __BUILTIN__ cos (x: REAL) : REAL ;
PROCEDURE __BUILTIN__ cosl (x: LONGREAL) : LONGREAL ;

PROCEDURE __BUILTIN__ sqrtf (x: SHORTREAL) : SHORTREAL ;
PROCEDURE __BUILTIN__ sqrt (x: REAL) : REAL ;
PROCEDURE __BUILTIN__ sqrtl (x: LONGREAL) : LONGREAL ;

PROCEDURE __BUILTIN__ fabsf (x: SHORTREAL) : SHORTREAL ;
PROCEDURE __BUILTIN__ fabs (x: REAL) : REAL ;
PROCEDURE __BUILTIN__ fabsl (x: LONGREAL) : LONGREAL ;

PROCEDURE __BUILTIN__ alloca (i: CARDINAL) : ADDRESS ;
PROCEDURE __BUILTIN__ memcpy (dest, src: ADDRESS; n: CARDINAL) : ADDRESS ;
PROCEDURE __BUILTIN__ index (s: ADDRESS; c: INTEGER) : ADDRESS ;
PROCEDURE __BUILTIN__ rindex (s: ADDRESS; c: INTEGER) : ADDRESS ;
PROCEDURE __BUILTIN__ memcmp (s1, s2: ADDRESS; n: CARDINAL) : INTEGER ;
PROCEDURE __BUILTIN__ memset (s: ADDRESS; c: INTEGER; n: CARDINAL) : ADDRESS ;
PROCEDURE __BUILTIN__ strcat (dest, src: ADDRESS) : ADDRESS ;
PROCEDURE __BUILTIN__ strncat (dest, src: ADDRESS; n: CARDINAL) : ADDRESS ;
PROCEDURE __BUILTIN__ strcpy (dest, src: ADDRESS) : ADDRESS ;
PROCEDURE __BUILTIN__ strncpy (dest, src: ADDRESS; n: CARDINAL) : ADDRESS ;
PROCEDURE __BUILTIN__ strcmp (s1, s2: ADDRESS) : INTEGER ;
PROCEDURE __BUILTIN__ strncmp (s1, s2: ADDRESS; n: CARDINAL) : INTEGER ;
PROCEDURE __BUILTIN__ strlen (s: ADDRESS) : INTEGER ;
PROCEDURE __BUILTIN__ strstr (haystack, needle: ADDRESS) : ADDRESS ;
PROCEDURE __BUILTIN__ strpbrk (s, accept: ADDRESS) : ADDRESS ;
PROCEDURE __BUILTIN__ strspn (s, accept: ADDRESS) : CARDINAL ;
PROCEDURE __BUILTIN__ strcspn (s, accept: ADDRESS) : CARDINAL ;
PROCEDURE __BUILTIN__ strchr (s: ADDRESS; c: INTEGER) : ADDRESS ;
PROCEDURE __BUILTIN__ strrchr (s: ADDRESS; c: INTEGER) : ADDRESS ;
PROCEDURE __BUILTIN__ huge_val (r: REAL) : REAL ;
PROCEDURE __BUILTIN__ huge_valf (s: SHORTREAL) : SHORTREAL ;
PROCEDURE __BUILTIN__ huge_vall (l: LONGREAL) : LONGREAL ;

END Builtins.

Even though this module exists and will generate inline code if optimization flags are passed to GNU Modula-2, you should really use the same functions from more generic libraries ๐Ÿ™ The built-in mechanism gets applied to those generic libraries where it makes sense.

๐Ÿ“ฃ NOTE: for the mathematical routines to be inlined, you need to specify the -ffast-math -O options.

โœจ LIBRARIES โœจ

Nothing is listed under this heading in the original page, so there's nothing for me to add here ๐Ÿ™ˆ

๐Ÿ“ EXAMPLES ๐Ÿ“

This section shows how to compile and link a simple 'hello world' program ๐Ÿ’… It gives you a few examples of using the different options we talked about above.

Say the file "hello.mod" contains this code ๐Ÿงš

MODULE Hello;

FROM StrIO IMPORT WriteString, WriteLn;

BEGIN
   WriteString('hello world');
   WriteLn;
END Hello.

You should be able to compile it with gm2 -c hello.mod and link it with gm2 hello.mod. The result should be an a.out file created in your directory โœจ

Or you can compile it with gm2 -g -I. -fmakeall hello.mod ๐Ÿซถ

๐ŸŒธ FILES ๐ŸŒธ

Nothing is listed under this heading either, bestie ๐Ÿ™ˆ

๐ŸŽ€ ENVIRONMENT ๐ŸŽ€

Same deal here: the original page has nothing under this heading ๐Ÿ’€

๐Ÿ”ฎ CONFORMING TO ๐Ÿ”ฎ

The GNU Modula-2 compiler is based on the language as defined in these three books ๐Ÿ“š

  • Programming in Modula-2, 2nd Edition, Springer Verlag, 1982, 1983 by Niklaus Wirth (PIM2)
  • Programming in Modula-2, 3rd Corrected Edition, Springer Verlag, 1985 (PIM3)
  • Programming in Modula-2, 4th Edition, Springer Verlag, 1988 (PIM4)

It also includes ISO M2 features and GNU Modula-2 extensions ๐Ÿ’–

๐Ÿ› BUGS ๐Ÿ›

Nothing is listed under this heading in the original, so no bugs were documented here ๐Ÿ’€

Nothing is listed under this heading in the original either.

๐Ÿ’œ LICENCE ๐Ÿ’œ

The GNU Modula-2 compiler and associated software is released under the GNU PUBLIC LICENCE (GPL), which should be available in a file named COPYING ๐Ÿซถ

The runtime libraries (pim, iso, and Logitech Compatable) are released under the LESSER GNU PUBLIC LICENCE (LGPL). That one lets any user link against the runtime libraries without invoking all the clauses of the full GPL ๐Ÿฆ‹

The Ulm library is released under the GPL.

๐ŸŽ€ AUTHOR ๐ŸŽ€

This manual page was written (using the documentation from the texinfo files) by Peter P. Eiserloh โŸจpeter@eiserloh.orgโŸฉ ๐Ÿงš

๐Ÿ‘‘ SEE ALSO ๐Ÿ‘‘

The many fine books about Modula-2 ๐Ÿ“š

The GNU Modula-2 web site at โŸจhttp://www.nongnu.org/gm2โŸฉ ๐ŸŒ


Bestie, that's the whole page! ๐Ÿ’– It's from the GM2 manual, dated 27-Nov-2005. Stay iconic and keep compiling ๐Ÿซถโœจ

๐Ÿ“ฆ where this page comes from