TYPE PROTECTION = (INTERRUPTIBLE, UNINTERRUPTIBLE);
CONST c = MAX (INTEGER) - SYSTEM. TSIZE (REAL) * 3; dif = VAL (CHAR, ORD ("a") - ORD ("A")); low = SYSTEM. CAST (CARDINAL, SYSTEM.CAST (BITSET, c) * BITSET {0..7}));
Constant calculations involving real or complex numbers are carried out in IEEE double precision mode.
SYSTEM.WORD16, SYSTEM.CARDINAL16, SYSTEM.INTEGER16, and enumeration types with more than 256 values:
1 half word (16 bits)
SYSTEM.WORD32, SYSTEM.CARDINAL32, SYSTEM.INTEGER32, and REAL:
1 word (32 bits)
SYSTEM.WORD, CARDINAL, INTEGER, BITSET, PROCEDURE types, SYSTEM.ADDRESS, pointer types:
1 doubleword (64 bits)
LONGREAL, COMPLEX, SYSTEM.BCD (15 nibbles for the decimal digits, one for the
sign):
1 doubleword (64 bits)
LONGCOMPLEX:
2 doublewords (128 bits)
SYSTEM.STR255 (Pascal string of maximum size):
256 bytes (not necessarily aligned to half-word boundary)
Subrange types:
If the pragma variable "FixedSubrangeSize" is set to "TRUE",
subrange types take the size of the base type.
If the pragma variable "FixedSubrangeSize" is set to "FALSE",
the size is determined by:
If lower and upper bound reside within [0..255] or [-128..127]:
1 byte (8 bits)
If lower and upper bound reside within [0..65535] or [-32768..32767]:
1 half word (16 bits)
If lower and upper bound reside within [0..4294967295] or [-2147483648..2147483647]:
1 word (32 bits)
If lower and/or upper bound are outside these ranges:
2 words (64 bits)
ARRAY:
Number of elements times the size of a single element.
RECORD:
Sum of the sizes of each field (the largest variant in each such case) including possible fill
bytes to align individual fields to alignment boundaries and/or a single fill byte appended to
the end of the record so that it occupies a whole number of half words (unless the record
occupies only one byte anyway, in which case it is not extended).
For example:
TYPE Rec = RECORD (* SIZE (Rec) yields the value 8. *) ch: CHAR; (* After "ch" there is a fill byte, *) i: INTEGER; (* since "i" is aligned to a half-word boundary. *) b: BOOLEAN; (* Another fill byte follows "b", so that the *) END(*RECORD*); (* record occupies a whole number of half words. *)If the records directly or indirectly contains a vector field the size is padded to an integral multiple of 16.
SET:
The ordinal values of the base type's bounds determine the memory-space requirements of
SETs. If the difference between the upper and lower bounds is in the range 0 to 7, then a
variable of this SET type occupies one byte; in all other cases an integral number of half
words is used—exactly how many is given by the following formula:
(ORD (MAX (base type)) - ORD (MIN (base type))) DIV 16 + 1 half words
PACKEDSET:
In this case the ordinal value of the base type's upper bound is what determines the amount of
memory needed. If this value lies between 0 and 7 the set occupies one byte, otherwise it
takes up an integral number of half words according to the following formula:
ORD (MAX (base type)) DIV 16 + 1 half words
CLASS:
Object variables contain a pointer to the object; it occupies
1 doubleword (64 bits)
The object itself takes up the same amount of memory as a record would which had the same
fields (including the fields of all ancestor types) plus additional runtime information.
Alignment
Variables of simple types occupying only one byte and of the type SYSTEM.STR255 can be
aligned to any byte boundary (i.e. any address is possible), the same is true for arrays of byte
alignable elements. Variables of all other types start on half-word, word, double-word, or quad-word
boundaries (depending on the alignment conventions). Arrays have the same alignments as
their elements. Constants are always aligned to word boundaries.
By assignment to the pragma variable "AlignBorder" the alignment model may be chosen modified:
For Arm_64:
In accordance with the Arm_64 runtime conventions, parameters are passed in registers or on stack, always aligned on longword
boundaries. For parameters with a size of less then 8 bytes only the first bytes are used, the following fill bytes
are undefined.
Any parameter larger than 8 bytes, except of type "LONGCOMPLEX", is always passed to a procedure as an address. If this "reference" parameter is actually intended to be a value parameter, a copy of it is then created within the procedure itself to avoid side effects.
p1 Modula-2 therefore provides the pragma variable "CopyRefparams". The copying of value parameters can be suppressed using the directive "ASSIGN (CopyRefparams, FALSE)" (or shorter: "CopyRefparams (FALSE)"). This thus has the same effect as placing "VAR" before the formal parameter's name, except that as actual parameter constants and expressions can also be passed As well as this, the compiler can check that the parameter is not written to. However the compiler is not in a position in all cases to determine whether there really is no write access to the parameter. As a result, ill-considered use of this feature can lead to strange side effects (e.g. overwriting a string constant!). In standard mode such reference parameters are always copied.
Examples:
Procedure declarations:
TYPE MyRecord = RECORD c1: CARDINAL; c2: CARDINAL; END(*RECORD*);
The address is passed as usual (i.e. in a register if available), the high indices are always passed on stack.
The initial value of all variables is undefined. The compiler attempts to detect the use of undefined variables statically and issues a warning as the case may be.
The position of an single bit can be determined as follows:
ppc
byte position = maxbytes - bit number DIV 8
bit position = bit number MOD 8
i386
byte position = bit number DIV 8
bit position = bit number MOD 8
where bit position = ORD (base type value) and maxbytes = ORD (MAX (base type)) DIV 8.
This means that for example a PACKEDSET OF [100 .. 101] takes up not 1 but 14 bytes.
Bits 0 .. 99 and 102 .. 111 are also always present, but their values are undefined.
Example:
The sign of number of type "LONGREAL" can be determined like this:
TYPE LongSet = PACKEDSET OF [0 .. 63]; VAR r: LONGREAL; … 63 IN CAST (LongSet, r) …
x0..x7 | Paramter registers, in leaf procedures used locally. |
x8 | Address register for large procedure results. |
x9..x15 | Temporary registers, used in expression evaluation and WITH-statements. |
x16, x17 | used locally by the compiler. |
x18 | Used by the system exclusively. |
x19..x25 | Preserved registers, used for local variables. |
x26 | SELF register. |
x27 | Static link register. |
x28 | Global pointer. |
x29 | Frame pointer. |
x30 | Link register. |
x31 | Stack pointer. |
v0..v7 | Paramter registers. |
v8..v15 | Preserved registers, used for local variables. |
v16..v31 | Temporary registers, used in expression evaluation. |
All registers used for local variables (x19 .. x28, v0 .. v15) are saved and restored within the called procedure. If the procedure or module has an exception handler, all of the registers listed are saved regardless.
The Intel registers are used by the compiler according to the runtime conventions:
rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11 | Scratch registers, used only locally. |
rbx | Code base pointer for PIC, scratch register otherwise |
rbp | Frame pointer |
rsp | Stack pointer |
st0..st7 | Intermediate results in LONGDOUBLE-expressions |
xmm0..xmm15 | Intermediate results in REAL- and LONGREAL-expressions |
The following additional conventions are used:
r15 Contains global pointer used for addressing a module's global variables
(cf. section 6.3.4)
r14 Within methods on level 0, and all nested procedures it contains the SELF parameter.
r13 If necessary, contains static link used for addressing variables of surrounding procedures
(cf. section 6.3.4).
The registers rbx, r12, r13, r14, and r15 are saved and restored within the called procedure.
chapter 4 (compiler) | start page | chapter 6 (compiler) |