Home Tutorial Win32 API Reference Projects

Stony Brook Modula-2Stony Brook Modula-2 archive

This page briefly goes over various language extensions we have added.

Additional predefined types

CARDINAL8, CARDINAL16, CARDINAL32, CARDINAL64, SHORTCARD, LONGCARD, INTEGER8,INTEGER16, INTEGER32, INTEGER64, SHORTINT, LONGINT,  ACHAR, UCHAR, BYTEBOOL,BOOL8, WORDBOOL, BOOL16, DWORDBOOL, BOOL32

These types are pervasive just like INTEGER and CARDINAL.

These types can also be imported from the SYSTEM module. Doing this documentsthe type as an extended feature in your source if this concerns you.

NILPROC

A new pervasive identifier NILPROC is introduced, denoting a constant procedurevalue and having the type NILPROC-TYPE.

  • NILPROC-TYPE is assignment compatible to every procedure type.
  • NILPROC-TYPE is compatible to every procedure type in the comparison operations "=" and "<>".
  • A call to a procedure variable having the value NILPROC shall raise the exception "invalidLocation".

Unicode support

  • The compiler supports both ASCII character and Unicode character and string types. The definition of the CHAR type can be either ASCII or Unicode. This is changed with a compiler option. The compiler supports the ACHAR and UCHAR types, which are ASCII and Unicode respectively.
  • Ascii and Unicode string literals. By default a string literal element type is of type CHAR. You can declare a specific string literal type as ASCII or Unicode. The compiler converts string constant to/from ASCII and Unicode as necessary.
  • The ACHR and UCHR pervasive functions complement the existing CHR pervasive function and provide function that return an ASCII or Unicode character type respectively.

Bitwise Operators

BAND, BOR, BXOR, BNOT, SHL, SHR, SAR, ROL, ROR

The operators perform bitwise operations. They can be used with all cardinal andinteger types.

Dynamic array types

Dynamic arrays are dynamically allocated types and are accessed via pointer dereferencing.Dynamic arrays are arrays of a constant number of dimensions, but the size of theindividual dimensions is unknown at compile time. Dynamic arrays have their bestuse on arrays with more than one dimension.

To specify dynamic array types, use:

POINTER TO ARRAY OF {ARRAY OF}TypeName

Example:

TYPE
   Matrix = POINTER TO ARRAY OF ARRAY OF REAL;

Dynamic arrays are indexed the same as open array parameters, where the lowersubscript bound is assumed to be zero and the upper bound is a runtime value thatcan be read with the HIGH built-in function, and the type of the subscript is CARDINAL.

Allocating dynamic arrays

Dynamic arrays are set by allocating a block of memory for the object that theypoint to. The NEW standard procedure. You can deallocate the memory with theDISPOSE standard procedure. The NEW and DISPOSE procedures are discussed in the standardprocedures section of this document. Example:

NEW(MyMatrix, 5, 7);

Extended loop control

  • BREAK statement. This statement causes the current loop to be exited. This statement is allowed with all Modula-2 loop constructs.
  • CONTINUE statement. This statement starts the next iteration of the current loop. In FOR loops, the loop counter is adjusts. In REPEAT, WHOLE and LOOP constructs this statement branches to the top of the loop. This statement is allowed with all Modula-2 loop constructs.
  • Named loops. This extension is just like the Ada syntax. You can name a specific loop and use the BREAK and CONTINUE statements to exit a specific loop by name.
    OuterLoop:
    FOR i := 0 TO 9 DO
       InnerLoop:
       FOR j := 0 TO 9 DO
          IF ... THEN
             BREAK OuterLoop; (* exits the "i" loop *)
          END;
       END;
    END;
    (* BREAK OuterLoop transfers control here *)

Support for assembly code

The compiler contains a built-in assembly language parser.

  • You can embed assembly code in a Modula-2 procedure. This is strongly discouraged.
  • You can declare procedure which are implemented in assembly code rather than Modula-2. There are two forms of assembly procedures.
    1. ASSEMBLER procedures. The compiler does all of the tedious work of generating the procedure prologue and epilog. You can declare local variables and the compiler will allocate them for you. You only write the “guts” of the procedure.
    2. PUREASM procedures. The compiler generates nothing. You have complete control.

Support for definition module “macros”.

The MACRO modifier is only allowed in DEFINITION modules and it signals that aprocedure body follows the header. This procedure code will be generated inline ateach occurrence it is called. You are not allowed to declare nested procedures ormodules within a macro. You are allowed to declare local types, constants and variables.

Example:

DEFINITION MODULE MyModule;

PROCEDURE it;

PROCEDURE add(a, b : CARDINAL) : CARDINAL; MACRO;
BEGIN
    RETURN a + b;
END add;

PROCEDURE it2;

END MyModule.

Procedure Attributes

Procedure attributes are a mechanism that allows you to control and override thevarious aspects of how procedures are called and linked. This allows you to interfacewith any other system in existence.

Variable number of parameters.

You can call procedures, which accept a variable number of parameters, and youcan declare your own procedures, which accept a variable number of parameters.

Variable declaration extensions

  • You can change the public symbol name of any variable for easy interfacing with other languages. Example:
    VAR MyVar [“PublicName”] : CARDINAL;
  • Initialized variables. You can declare variables with an initial value. Global variables are initialized at program start, and procedure variables are initialized on entry to the procedure. Example:
    VAR MyVar : CARDINAL = 23;

Enumeration syntax support to allow easy interfacing with the C language.

Examples:

TYPE
enum = (one, two = 5, three);

In the above enumeration the ordinal value of the identifier "three"is six.

GtkPathPriorityType =
(
GTK_PATH_PRIO_LOWEST= 0,
GTK_PATH_PRIO_GTK= 4,
GTK_PATH_PRIO_APPLICATION = 8,
GTK_PATH_PRIO_RC= 12,
GTK_PATH_PRIO_HIGHEST= 15,
GTK_PATH_PRIO_MASK= 00fh
);

Enumeration type storage allocation adjustment.

Example:

TYPE enum = (one, two, three) BIG;

The BIG attribute makes the type have the size of CARDINAL. The SMALL attribute(the default) stores the type in a byte type if possible. Note, when the SMALLattribute is used and more than 256 enumeration identifiers are specified, the typewill have the size of CARDINAL.

Using BIG can be useful when translating C language enumerations to Modula-2 sinceC enumerations are nothing more than integer constants.

Set type storage allocation adjustment.

The BIG attribute makes the set’s size the smallest possible size, whichis a multiple of the size of CARDINAL. The SMALL attribute (the default) storesthe type in an 8-bit, or 16-bit type if possible.

When the SMALL attribute is used and the set contains between 1 and 8 elements,the type size will be 8-bits. When the SMALL attribute is used and the set containsbetween 9 and 16 elements, the type size will be 16-bits. If the set has 17or more elements, the set will be a multiple of size CARDINAL.

Record bit field support to allow easy interfacing with C language code.

To aid in interfacing with C language code, support for bit fields is added asan extension to the language. Bit fields are fields within a record that occupy anamount of space less than their declared type.

Bit fields can be declared anywhere in a record. Bit fields are declared withina bit field block which starts with the BITFIELDS keyword and ending with the ENDkeyword.

Example:

   TYPE BITREC =
        RECORD
        before : CARDINAL;

        BITFIELDS
           bitField1 : CARDINALBY 3;
           bitField2 : INTEGERBY 3;
        END;

        after : CARDINAL;
        END;

Signed types have a minimum bit size of 2, and unsigned types can occupy a singlebit. Bit fields are grouped and packed via an algorithm compatible with nearly allC compilers. This makes translation of C structures quite straightforward.

Extensions to arrays types and variables

  • HIGH function extended to all arrays
  • ISO Modula-2 only allows you to pass array types to open array parameters. We also allow you to pass a variable whose type is the same as the element type of the open array parameter.
  • Anonymous array constants. Saves declaring and array type which is only used in a single constant declaration.
  • Open array constants. The compiler determines the HIGH bound of the constant by the number of array elements contained in the constant declaration.
  • Array slices like the Ada syntax.
  • Subscript checking suppression.The compiler will never perform array subscript checking at compile time or runtime for array types declared with the same lower and upper bound. For example 0..0. A single element array like this can be used for an array whose size is not known at compile time and is sized dynamically at runtime. This is only useful for single dimensional arrays. Dynamic arrays are a better alternative for arrays of indeterminate size.
  • Arrays of Char can be assigned to each other regardless of size.
  • An open array can be the destination or source of an assignment statement.

Type coercion

The coercion qualifier is specified by:

designator:TypeName

The coercion qualifier can be applied to any variable designator. The datareferred to by designator is treated as if it has the type specified by TypeName. Coercion is a method of bypassing the strong type checking of Modula-2. UnlikeISO standard type casting type coercion has no restrictions on usage.

Use of type coercion is dangerous and not transportable, because it relies onknowledge of the data representation. On the other hand, it is more efficientthan other methods of bypassing type checking, such as use of ADDRESS typesor variant records. This is because no data is moved-you refer to the datain place.

The coercion qualifier can be used in some places where the CAST function, importedfrom the SYSTEM module, cannot be used such as the left hand side of an assignmentstatement.

Example:

number := buffer^[bp]:CARDINAL;
buffer^[bp]:CARDINAL := number

Type cast a literal constant

The compiler allows expression of the form. Example:

SYSTEM.CAST(CARDINAL, -8)

Taking the address of a literal constant

The compiler allows you to use the SYSTEM.ADR function on string literals andother structured constants. String literals are always null terminated.

Example:
CreateWindow(ADR("MyWindowClass"), ...);

Subscripting a string literal

The compiler allows you to subscript a named string literal constant.

Character literal constants

CHR(CompileTimeConstant) is treated as a character literal constant with the sameprivileges. For example the compiler treats CHR(13) the same as 15C. This means youcan use CHR(13) with the string literal concatenation operator (+). This also appliesto the ACHR, and UCHR functions.

CONST CrLf = CHR(13) + CHR(10); (* is accepted *)
CONST CrLf = 15C + 12C; (* ISO standard *)

Extensions to MIN and MAX

These pervasive functions can accept a parameter, which is a variable. The typeof the variable is used for the return value of these functions.

Extension to SIZE

The SIZE pervasive function has been extended to accept open array and dynamicarray variables.

The SIZE function allows you to reference record fields when it is passed a typeparameter.

Function procedure call extension

Function procedures can be called as procedures. The function result value isdiscarded.

System type assignment compatibility

The parameter assignment rules of the types SYSTEM.BYTE, SYSTEM.WORD, SYSTEM.DWORDand SYSTEM.MACHINEWORD have been extended to all other assignment compatibility situations.

Parameter modes

The compiler supports marking VAR parameter types with more explicit modes ofoperation. INOUT and OUT identifiers are supported. INOUT parameters expect a valueon input and return a  value upon exit. OUT parameters do not expect any inputvalue and only output a value upon procedure return.

Using these help document code and helps, the compilers uninitialized variabledetection.

PROCEDURE p1(VAR INOUT param1 : INTEGER; VAR OUT param2 : INTEGER);

The compiler supports marking value parameters as constant, CONST, parametersthat cannot be altered within a procedure.

PROCEDURE p1(CONST constParam : INTEGER);
BEGIN
    constParam := 23; (* <== a compilation error *)
END p1;

Importing symbols

Importing all items from a module unqualified.

FROM ModuleIdentifier IMPORT * [EXCEPT Identifier {, Identifier}];

This has the effect of importing all exported symbols from a given module, exceptthose identifiers in the exclusion list after the optional EXCEPT keyword. If animported symbol would collide with an already visible symbol of the same name a compilationerror is generated. In this case you would typically use the EXCEPT keyword to excludethe offending symbol from the import. Examples

FROM SYSTEM IMPORT
   DWORD;

FROM WIN32 IMPORT * EXCEPT DWORD;

FROM WINUSER IMPORT *;

EXPORTS declaration

Provides a mechanism to control which symbols are exported and visible outsideof an executable file. This is normally only used for DLLs and shared objects. Youcan export individual symbols or entire modules.

Conditional Compilation

Conditional compilation is the compiler's ability to decide at compile time whetheror not portions of your source program are to be compiled. Conditional compilationcan test a version tag that you define.

Version tags

You can use conditional compilation to create different versions of a programthat reside in the same source. The version that is compiled is controlledexternally, by version tags.

Version tags are identifiers you define in one of the following ways:

  • Through the development environment version tags option
  • Through the compiler version tag directive
  • On the compiler command line

Predefined compiler version tags

  • StonyBrook. This identifies our compiler. The Macintosh p1 compiler uses "p1" to identify itself.
  • Win32. The target operating system is Win32.
  • Linux. The target operating system is Linux.
  • SunOS. The target operating system is SunOS/Solaris.
  • Unix. The target operating system is any flavor of Unix. This tag is defined in addition to a specific operating system tag.
  • FlashTek. The target operating system is the X32 DOS extender.
  • Win16. The Target operating system is 16-bit Windows.
  • DOS. The target operating system is DOS.
  • ASCII. Modula-2 only. CHAR = ACHAR. 8-bit.
  • Unicode. Modula-2 only. CHAR = UCHAR. 16-bit
  • LittleEndian. The target processor uses little endian byte order.
  • BigEndian. The target processor uses big endian byte order.
  • IA32. The target processor is the IA-32 architecture. i.e. x86, Pentium, etc...
  • SPARC. The target processor is the SPARC processor.
  • Bits32. The target processor is 32-bit.
  • Bits16. The target processor is 16-bit.

The compile time IF statement

Conditional compilation is implemented by the compile time IF statement. Unlikethe Modula-2 IF statement, the compile time IF operates at the token level. Acompile time IF can start between any two tokens in a program, and can control thecompilation of any string of tokens.

The format of the compile time IF statement is:

%IF CompileTimeExpression %THEN
   TokenStream
{%ELSIF CompileTimeExpression %THEN
   TokenStream}
[%ELSE
   TokenStream]
%END

CompileTimeExpression is an expression formed by version tags,  parenthesesand the operators %AND, %OR, and %NOT. The precedence of the operators is asfollows, from highest to lowest:

%NOT
%AND
%OR

TokenStream is any stream of Modula-2 tokens.

Compile time IF's can be nested up to 16 levels. They can be used anywherein a source file.

The CompileTimeExpressions are evaluated as boolean expressions. The valueof a version tag is TRUE if the version tag is defined and FALSE if it is not. TheCompileTimeExpressions are evaluated in order until one is TRUE. The TokenStreamfollowing the TRUE expression is compiled. If none of the expressions is TRUE,and there is an ELSE, the TokenStream following the ELSE is compiled. All otherTokenStreams are skipped by the compiler.

Examples:

A common use of conditional compilation is to add debug code that aids in debugginga program, but is not compiled in the final version of the program. For example:

%IF DEBUG %THEN
   WriteString('After ');
   WriteCard(i, 0);
   WriteString(' iterations: X = ');
   WriteReal(X, 10);
   WriteString(' Y = ');
   WriteReal(Y, 10);
%END

Compile time IF's are not restricted to lists of statements, as in the above example. You can also use them to conditionalize data structures:

TYPE SymbolRecord =
   RECORD
   DataType    : TypePointer;
   Address     : %IF ThirtyTwoBit %THEN
                      CARDINAL;
                 %ELSE
                      LONGCARD;
                 %END
   NextSym     : SymbolPointer;
   END;

You can use the operators %AND, %OR, %NOT and parentheses to test more complexconditions. For Example:

%IF (Win32 %OR OS2) %AND %NOT Network %THEN
...
%END

Alternate conditional compilation syntax in Modula-2

The compiler also accepts the conditional compilation syntax used by the Macintoshp1 compiler. The syntax is nearly identical except that it is contained in directives.

<*IF (Win32 OR OS2) AND NOT Network THEN *>
...
<*END*>

Selected listing of various SYSTEM module extensions

Machine processor count

VAR CPUCOUNT : CARDINAL;

This variable gives the number of installed processors in the computer.

Program exit code value

VAR EXITCODE : CARDINAL;

EXITCODE contains the value passed to the HALT procedure. Procedures installedinto the termination chain may want to check the exit value of the program.

BuildNumber Variable

VAR BuildNumber : CARDINAL;

This variable contains the build number. This value is inserted by the linkervia a linker option. The value is user defined.

DebuggerPresent Variable

VAR DebuggerPresent : BOOLEAN;

This variable is TRUE if the program is being debugged by the Stony Brook Debugger.Otherwise the value is FALSE.

OFFS Procedure

PROCEDURE OFFS(TypeOrVariableName.recordField{.recordField}) : ConstantValue;

OFFS returns a compile time constant value that is equal to the offset of thespecified record field from the beginning of the record type.

Example:

TYPE
   RecType =
    RECORD
        x, y, z : CARDINAL;
    END;

CONST
   yOffset = OFFS(RecType.y);

VAR
   v : RecType;
BEGIN
   v.x := OFFS(v.z);

Unreferenced parameters.

PROCEDURE UNREFERENCED_PARAMETER(AnyParameter);

The compiler generates a warning when a parameter of a procedure is not referencedwithin that procedure. In some instances it is necessary to have an unreferencedparameter within a procedure. Operating system call back procedures are suchan example. The UNREFERENCED_PARAMETER procedure can be used to suppress warningson unreferenced parameters.

SOURCEFILE Variable

This identifier represents a string literal whose value is the filename of thesource file being compiled.

SOURCELINE Variable

This identifier represents a numeric constant whose value is the source line numberwhere this instance of the identifier is used.

ASSERT Statement

This is a statement that checks a BOOLEAN expression for TRUE and if not raisesan ASSERT exception. The assert exception will identify the module and line numberof the failed ASSERT statement. ASSERT will generate this test code, if and onlyif, the version tag M2ASSERT is set, otherwise no code for the ASSERT statement isgenerated. The format of the ASSERT statement is:

ASSERT(BooleanExpression);

Example:

Assume the M2ASSERT version tag is set.

PROCEDURE foo(ptr: ADDRESS);
BEGIN
   ASSERT(ptr <> NIL);

   (* will never get here if ptr = NIL *)
    (* think of the ASSERT statement like this
   %IF M2ASSERT %THEN
        IF ptr = NIL THEN
            RAISE(...);
        END;
   %END
   *)
END foo;

ISASSERT function

PROCEDURE ISASSERT() : BOOLEAN;

This is a function that returns a BOOLEAN value. You can use this to trap ASSERTexceptions. It returns TRUE if the current exception is an ASSERT exception.

EXCEPTADR Procedure

PROCEDURE EXCEPTADR() : ADDRESS;

This is a function that returns the address where the current exception occurred.It will return NIL if there is no exception.

EXCEPT_INFO Procedure

PROCEDURE EXCEPT_INFOADR(VAR OUT addr: ADDRESS;
                        VAR OUT lineNumber : CARDINAL;
                        VAR OUT moduleName : ARRAY OF ACHAR);

This is a function that returns information about the current exception. The procedurewill return NIL in addr if there is no exception. If the exception occurred becauseof a runtime check then the lineNumber and moduleName parameters will return thelocation of the checking error. If the exception did not result from a runtime checkthe lineNumber will return 0, and modulename will return an empty string.

SetUnhandledExceptionProc Procedure

PROCEDURE SetUnhandledExceptionProc(unhandled : PROC);

This procedure lets you set a procedure that will be called when an exceptionis unhandled.

AttachDebugger procedure (Win32 only)

TYPE
     AttachDebuggerOpt =
     (
     DoNotAttach,    (* do not attach, the default *)

     AttachExternal,(* attach on EXCEPTIONS.sysException only. this will primarilybe access violations *)
     AttachAll(* attach on all raised exceptions. this includes ALL Modula-2 exceptions *)
);

 PROCEDURE AttachDebugger(opt : AttachOptions);
 

Use this procedure to enable or disable just in time debugging support. You usuallyput this call as the first line of code in a program.

For example, if you use AttachExternal, then when you program gets an access violationor other system exception the debugger will be attached to the program allowing youto start debugging at the point of the exception.

OutputDebugMessage procedure

PROCEDURE OutputDebugMessage(str : ARRAY OF CHAR);

This procedure actually does nothing except that our debugger for Win32 and Unixsystems will trap calls to this procedure and display the passed parameter in thedebug messages window.

EnableCallTrace procedure

PROCEDURE EnableCallTrace;

By calling this procedure you enable the runtime system to perform a call tracewhen an exception of any kind is raised. If the exception goes unhandled then thecall trace will be output to disk. If the exception is handled then the informationis lost.

OutputCallTrace procedure

PROCEDURE OutputCallTrace;

This procedure is only used with EnableCallTrace. If you want to handle an exception,but still want the call trace output to disk then you should call this procedureinside your exception handler. For example this allows you to handle all exceptions,hopefully terminating gracefully, and still getting a call trace which may help youdebug the problem.

TrapAccessViolations procedure (Unix systems only)

PROCEDURE TrapAccessViolations;

This procedure is used in shared objects to enable trapping access violations.Main programs automatically trap access violations. Access violations consist ofthe SIGSEGV, SIGBUS and SIGILL signals. These signals are global to an entire process,therefore our runtime system does not assume it can possess these signals when runningin a shared object. Trapping an access violation means the violation is convertedto a native language exception.

Note that it is still possible for code to override our runtime system signalhandlers for these signals. This can happened in main programs and shared objects.When this occurs the violation will not be trapped by our runtime system.

VA_START, VA_ARG procedures

PROCEDURE VA_START(VAROUT addr : ADDRESS);
PROCEDURE VA_ARG(VAR INOUT addr : ADDRESS;
TypeIdentifier) : ADDRESS;

These procedures are used to support accessing the parameters of a procedure,which accepts a variable number of parameters (the VARIABLE procedure attribute).You use VA_START to get the address of the first variable argument. You then useVA_ARG for each argument passing the address VA_START initialized. The second parameterof the VA_ARG function is the type identifier of the argument you are about to read.The function returns a pointer to the data. The function also updates the parameteraddress, addr.

PROCEDURE example(format: ARRAY OF CHAR)
[RightToleft, Leaves, Variable];

TYPE
    Pointer = POINTER TO CARDINAL;
    (*all pointers are the same size*)

VAR
    addr : ADDRESS;
    ptrC : POINTER TO CARDINAL;
    ptrCh : POINTER TO CHAR;

BEGIN
    VA_START(addr);
    ptrC := VA_ARG(addr, CARDINAL);
    (* use ptrC *)

    ptrCh := VA_ARG(addr, Pointer);
    (* use ptrCh *)
END example;

CLONE procedure

PROCEDURE CLONE(VAR newObject : <some_class_type>; sourceObject : <some_class_type>);

This procedure creates an exact copy of the object referenced by sourceObjectand stores a reference to the new object in the variable denoted by newObject.

FUNC Keyword

This keyword allows you call a function procedure as a procedure, thus ignoringthe result. No Warning will be generated in extended syntax mode, and no error generatedin ISO mode.

Example:

BEGIN
   returnVal := MyFunction(param1, param2);
   FUNC MyFunction(param1, param2);
END;

FIXME Procedure

PROCEDURE FIXME(str : ARRAY OF CHAR);

This "procedure" allows you cause a warning to be generated in the sourcefile at the source location with a string value passed to this function. No codeis generated by the use of this procedure. This warning can never be suppressed withcompiler options. You can use this feature to document something that needs addressingin your code and by having a warning generated you can use the mechanism's builtinto the development system for finding warnings errors in source files.

SWAPENDIAN Procedure

PROCEDURE SWAPENDIAN(IntegerType) : SameIntegerType;
PROCEDURE SWAPENDIAN(VAR INOUT : IntegerOrRealType);

This is a function procedure that allows converting a number between little andbig endian byte ordering format. This only has use for code that stores data on disk,or transmits data across a network, that is used on computers that have differentnatural data formats. This procedure can be called as a function or a procedure.When call as a function only integer types can be used. When called as a procedureyou can pass both integer types and floating point types.

16-bit, 32-bit and 64-bit INTEGER and CARDINAL types can be used with this function.REAL and LONGREAL can be used.

Examples:

VAR
   card32 : CARDINAL32;
   card16 : CARDINAL16;
 

   r      : REAL;
BEGIN
   ...
   card32 := SWAPENDIAN(card32);
   card16 := SWAPENDIAN(card16);

   SWAPENDIAN(card32);
   SWAPENDIAN(r);

BIGENDIAN, LITTLEENDIAN Procedures

PROCEDURE BIGENDIAN(IntegerType) : SameIntegerType;
PROCEDURE BIGENDIAN(VAR INOUT : IntegerOrRealType);
PROCEDURE LITTLEENDIAN(IntegerType) : SameIntegerType;
PROCEDURE LITTLEENDIAN(VAR INOUT : IntegerOrRealType);

These functions are like SWAPEENDIAN except they always result in a specific endianformat. They assume the input data is in the native format for the target processorand operating system. Therefore these procedures may, or may not, generate code.For example LITTLEENDIAN will never generate code when targeting an IA-32 processor.It will however generate code if the target is a SPARC processor.

LITTLEENDIAN is equivalent to

%IF BigEndian %THEN
   SWAPENDIAN(data);
%END

Multi-processing support functions

The compiler implements various intrinsic functions useful in multiprocessing.These functions generate inline machine code rather than a procedure call. Thesefunctions perform atomic operations in a multiprocessing environment. This meansthat the processor executing these functions has exclusive access to the data beingoperated on.

Atomic compare and exchange

PROCEDURE ATOMIC_CMPXCHG(VAR INOUT data : SomeType; compare, source : SomeType): SomeType;

where SomeType can be CARDINAL, INTEGER, DWORD or ADDRESS. Remember that pointersare compatible with ADDRESS parameter types.

This procedure compares the value in data with the value in compare and if equal,then the value in source is assigned to data. The function result is the value previouslyheld in data. Only the variable data is atomically accessed. This function makesthe new value written in data visible to other processors before returning.

Atomic exchange

PROCEDURE ATOMIC_XCHG(VAR INOUT data : SomeType; source : SomeType) : SomeType;

where SomeType can be CARDINAL, INTEGER, DWORD or ADDRESS. Remember that pointersare compatible with ADDRESS parameter types.

This procedure performs an atomic exchange of the value in data with the valuein source. The function result is the value previously held in data. Only the variabledata is atomically accessed. This function makes the new value written in data visibleto other processors before returning. This operation may be used as a function oras a statement ignoring the return value.

Note: The return value may not be the same as the value in data, because anotherprocessor may have altered the value one processor cycle after this function altersthe value.

Atomic add

PROCEDURE ATOMIC_ADD(VAR INOUT data : SomeType; constantValue : INTEGER) : SomeType;

where SomeType can be CARDINAL or INTEGER.

constantValue must be in the range

-128..127 for IA32
-4096..4095 for SPARC

This procedure adds the value in constantValue with the value in data. Positivenumbers perform addition and negative numbers perform subtraction. The function resultis the result of the addition/subtraction to data. Only the variable data is atomicallyaccessed. This function makes the new value written in data visible to other processorsbefore returning. This operation may be used as a function or as a statement ignoringthe return value.

Memory fence/barrier

PROCEDURE MEMORY_FENCE;

Note: This procedure is not necessary and does not perform any actions on IA-32architecture processors (x86 processor family) since these processors support strongwrite ordering.

Note: You do not need to use a memory fence with any synchronization objects supportedby the runtime library or the operating system, as they will take necessary actions.

This procedure generates a memory fence or barrier, and is necessary on processorsthat do not guarantee memory access order.

  • SPARC
  • PowerPC
  • IA-64

The procedure guarantees that all subsequent loads or stores will not access memoryuntil after all previous loads and stores have accessed memory, as observed by otherprocessors. The following pseudo code describes this further

1) <Acquire lock>
2) MEMORY_FENCE
3) critical section code
4) MEMORY_FENCE
5) <release lock>

The first memory fence stops the processor from looking ahead and pre-fetchingany data used in the critical section. The second memory fence makes sure that anydata written in the critical section is made visible to other processors before thewrite that releases the software lock. The memory fence is generally only used whenimplementing spinlocks.


© Norman Black, Stony Brook Software. Reproduced with permission.