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 word (32 bits) for ppc and i386 targets; 2 words (64 bit) for target x86
LONGREAL, COMPLEX, SYSTEM.CARD64, SYSTEM.INT64, SYSTEM.BCD (15 nibbles for the decimal digits, one for the
sign):
2 words (64 bits)
LONGCOMPLEX, SYSTEM.LONGDOUBLE:
4 words (128 bits)
SYSTEM.DOUBLECOMPLEX:
8 words (256 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 (possible for target x86 exclusively):
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 word (32 bits) for ppc and i386 targets; 2 words (64 bit) for target x86
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 "AlignModelPPC" the alignment model may be chosen:
For I386:
In accordance with the I386 runtime conventions, parameters are passed on stack, always aligned on word boundaries. For
parameters with a size of less then 4 bytes only the first bytes are used, the following fill bytes
are undefined.
For X86_64:
In accordance with the X86_64 runtime conventions, parameters are passed in registers or on stack, always aligned on word
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 4 bytes in size for 32-bit targets or 8 bytes for 64-bit targets, except of types "LONGREAL" or "LONGDOUBLE", 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) …
r0, r2, r11, r12 | Scratch registers, used only locally. |
r1 | Stack pointer |
r3..r10 | Parameter registers |
r13..r31 | Local variables, scratch registers in expressions, pointer for "WITH" statements etc. |
f0 | Scratch register, used only locally. |
f1..r13 | Parameter registers |
f14..f31 | Local variables, scratch registers in expressions, etc. |
v0, v1,v14..v19 | Scratch registers, used only locally. |
v2..v13 | Parameter registers |
v20..v31 | Local variables, scratch registers in expressions, etc. |
The following additional conventions are used:
r31 If necessary, contains global pointer used for addressing a module's global variables
(cf. section 6.3.4)
r30 If necessary, contains static link used for addressing variables of surrounding procedures
(cf. section 6.3.4)
All registers used for local variables (r13 .. r31, f14 .. f31, v20..v31) are saved and restored within the called procedure. Setting the register using "SYSTEM. SETREGISTER" is also regarded as a "use" of the register. 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:
eax, ecx, edx | Scratch registers, used only locally. |
ebx | Code base pointer |
ebp | Frame pointer |
esp | Stack pointer |
st0..st7 | Intermediate results in LONGDOUBLE-expressions |
xmm0..xmm7 | Intermediate results in REAL- and LONGREAL-expressions |
The following additional conventions are used:
edi Contains global pointer used for addressing a module's global variables
(cf. section 6.3.4)
esi If necessary, contains static link used for addressing variables of surrounding procedures
(cf. section 6.3.4). Within methods on level 0, it contains the SELF parameter.
The registers ebx, esi, edi, and ebp are saved and restored within the called procedure.
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.
The above assumptions may be reverted by assigning the value "TRUE" to the pragma variable "InvertBranches" (cf. 4.2).
chapter 4 (compiler) | start page | chapter 6 (compiler) |