Copyright (C) 1993, Digital Equipment Corporation
All rights reserved.
See the file COPYRIGHT for a full description.
File: Target.m3
Last Modified On Tue Jan 24 09:58:43 PST 1995 By kalsow
Modified On Fri Dec 9 12:56:47 PST 1994 By Olaf Wagner
MODULE Target;
IMPORT Text, TargetMap, M3RT, TextUtils, TInt;
VAR (*CONST*)
CCs : ARRAY [0..8] OF CallingConvention;
PROCEDURE Init64 () =
BEGIN
Integer := Int64;
Word := Word64;
Address := Word64;
Address.cg_type := CGType.Addr;
END Init64;
PROCEDURE IsX86(): BOOLEAN =
BEGIN
IF TextUtils.StartsWith(System_name, "I") AND
(TextUtils.StartsWith(System_name, "I386_")
OR TextUtils.StartsWith(System_name, "I486_")
OR TextUtils.StartsWith(System_name, "I586_")
OR TextUtils.StartsWith(System_name, "I686_")) THEN
RETURN TRUE;
END;
CASE System OF
| Systems.FreeBSD4, Systems.NT386, Systems.LINUXLIBC6 => RETURN TRUE;
ELSE RETURN FALSE;
END;
END IsX86;
PROCEDURE IsAMD64(): BOOLEAN =
BEGIN
RETURN TextUtils.StartsWith(System_name, "AMD64_");
END IsAMD64;
PROCEDURE IsSPARC(): BOOLEAN =
BEGIN
RETURN (TextUtils.StartsWith(System_name, "S")
AND (TextUtils.StartsWith(System_name, "SPARC")
OR TextUtils.StartsWith(System_name, "SOL")));
END IsSPARC;
PROCEDURE Init (system: TEXT; in_OS_name: TEXT; backend_mode: M3BackendMode_t): BOOLEAN =
CONST FF = 16_FF;
VAR sys := 0; max_align := 64;
BEGIN
(* lookup the system -- linear search *)
IF (system = NIL) THEN RETURN FALSE END;
WHILE NOT Text.Equal (system, SystemNames[sys]) DO
INC (sys); IF (sys >= NUMBER (SystemNames)) THEN RETURN FALSE END;
END;
System := VAL(sys, Systems);
System_name := SystemNames[sys];
(* build a generic 32-bit/IEEE system description *)
Int8.cg_type := CGType.Int8;
Int8.size := 8;
Int8.align := 8;
Int8.min := Int{16_80,FF,..};
Int8.max := Int{16_7f,00,..};
Int16.cg_type := CGType.Int16;
Int16.size := 16;
Int16.align := 16;
Int16.min := Int{00,16_80,FF,..};
Int16.max := Int{FF,16_7f,00,..};
Int32.cg_type := CGType.Int32;
Int32.size := 32;
Int32.align := 32;
Int32.min := Int{00,00,00,16_80,FF,..};
Int32.max := Int{FF,FF,FF,16_7f,00,..};
Int64.cg_type := CGType.Int64;
Int64.size := 64;
Int64.align := 64;
Int64.min := Int{00,00,00,00,00,00,00,16_80};
Int64.max := Int{FF,FF,FF,FF,FF,FF,FF,16_7f};
Word8.cg_type := CGType.Word8;
Word8.size := 8;
Word8.align := 8;
Word8.min := Int{00,00,..};
Word8.max := Int{FF,00,..};
Word16.cg_type := CGType.Word16;
Word16.size := 16;
Word16.align := 16;
Word16.min := Int{00,00,00,..};
Word16.max := Int{FF,FF,00,..};
Word32.cg_type := CGType.Word32;
Word32.size := 32;
Word32.align := 32;
Word32.min := Int{00,00,00,00,00,..};
Word32.max := Int{FF,FF,FF,FF,00,..};
Word64.cg_type := CGType.Word64;
Word64.size := 64;
Word64.align := 64;
Word64.min := Int{00,00,00,00,00,00,00,00};
Word64.max := Int{FF,FF,FF,FF,FF,FF,FF,FF};
Integer := Int32; (* default for the 32-bit platforms *)
Longint := Int64;
Word := Word32; (* default for the 32-bit platforms *)
Long := Word64;
Address := Word32; Address.cg_type := CGType.Addr;
Char := Word8;
Void.cg_type := CGType.Void;
Void.size := 0;
Void.align := Byte;
Void.min := Int{0,..};
Void.max := Int{0,..};
Real.cg_type := CGType.Reel;
Real.pre := Precision.Short;
Real.size := 32;
Real.align := 32;
Real.min := Float { Precision.Short, 0, -3.40282346638528860x+38 };
Real.max := Float { Precision.Short, 0, 3.40282346638528860x+38 };
Longreal.cg_type := CGType.LReel;
Longreal.pre := Precision.Long;
Longreal.size := 64;
Longreal.align := 64;
Longreal.min := Float { Precision.Long, 0,-1.79769313486231570x+308 };
Longreal.max := Float { Precision.Long, 0, 1.79769313486231570x+308 };
Extended.cg_type := CGType.XReel;
Extended.pre := Precision.Extended;
Extended.size := 64;
Extended.align := 64;
Extended.min := Float{Precision.Extended, 0,-1.79769313486231570x+308};
Extended.max := Float{Precision.Extended, 0, 1.79769313486231570x+308};
OS_name := in_OS_name;
(* common values *)
Atomic_lock_free :=
ARRAY [CGType.Word8..CGType.Addr] OF BOOLEAN { TRUE, .. };
(* this is overly optimistic... *)
Allow_packed_byte_aligned := FALSE;
All_floats_legal := TRUE;
PCC_bitfield_type_matters := TRUE;
Structure_size_boundary := 8;
Little_endian := TRUE;
Setjmp := "_setjmp";
(* There is no portable stack walker, and therefore few systems have one.
Having a stack walker theoretically speeds up everything nicely. If
you are familiar with NT exception handling, all but x86 have a stack
walker. Not having a stack walker means that functions that have
try/except/finally/raise incur a codegen cost even if there is never a
raise -- for having the try. Having a stack walker means "zero cost"
for exception handling code that does not actually raise an exception.
If there is NOT a stack walker, prologues/epilogues for functions that
try/except/finally/raise call PushEFrame / PopEFrame, essentially to
build a walkable parallel stack.
If there is a stack walker, then raise can discover what PushEFrame /
PopEFrame effectively recorded.
NT/x86 has a highly optimized equivalent of PushEFrame / PopEFrame, not
currently used by Modula-3. *)
Has_stack_walker := FALSE;
(* "Closures" in Modula-3 -- function pointers to nested functions, are
represented as a pointer to -1 (of size?) followed by other data. -1
is assumed to be invalid code. Prior to calling any function pointer,
the generated code first checks for the marker -1, to decide how to
call it. On systems where data alignment matters, but functions are
not aligned, this can result in an alignment fault. Most systems either
don't care about alignment (x86, AMD64) or have fixed sized and aligned
instructions (PowerPC), in which case the check for -1 can just be a
direct read, in which case Aligned_procedures := TRUE. This logic can
break down on 64bit platforms, where the -1 is perhaps 64bits, but the
fixed size instructions may be 32bits. Or of course, on systems that
care about data alignment but not code alignment, or in which the
alignments differ.
Setting this to FALSE is safe, but results in slower code -- code that
checks function pointers for alignment before checking for the -1, and
if they aren't aligned, doing a byte-wise read instead of an
integer-wise read.
We can probably do better here, such as ensuring the marker is 4 bytes
instead of 8, if that works (for 64 bit platforms, that care about
alignment, but with fixed sized aligned 32 bit instructions, which
probably describes some (e.g., MIPS64 and SPARC64) *)
Aligned_procedures := TRUE;
(* The effect of First_readable_addr is that (static?) array indices
(offsets) lower than it (and positive?) do not have a NULL check on the
array base. Reading NULL + an offset less than First_readable_addr is
assumed to access violate the same as reading NULL. It is a nice
optimization. Setting the value too low results in correct but
suboptimal code. However just setting it to a small non-zero number
should provide most of the benefit. Setting the value too high results
in missing NULL checks -- a loss of safety enforcement. Typically
setting it to one hardware page is a good estimate, since if NULL is
not accessible, nor is any address on the same page. As well, setting
it to getpagesize, whatever the granularity of mmap/VirtualAlloc, often
larger than a hardware page, is another good guess. *)
First_readable_addr := 4096 * Char.size;
(* add the system-specific customization *)
(* 64bit *)
IF TextUtils.StartsWith(system, "ALPHA_")
OR TextUtils.Contains(system, "64") THEN
Init64();
END;
(* ALPHA, SPARC64, HPPA, MIPS64: aligned_procedures *)
IF TextUtils.StartsWith(system, "SPARC64")
OR TextUtils.StartsWith(system, "ARMEL")
OR TextUtils.StartsWith(system, "ALPHA_")
OR TextUtils.StartsWith(system, "ALPHA64")
OR TextUtils.StartsWith(system, "PA")
OR TextUtils.StartsWith(system, "HPPA")
OR TextUtils.StartsWith(system, "MIPS64") THEN
Aligned_procedures := FALSE;
END;
(* big endian *)
IF TextUtils.StartsWith(system, "PA")
OR (TextUtils.StartsWith(system, "MIPS")
AND NOT TextUtils.StartsWith(system, "MIPSEL")
AND NOT TextUtils.StartsWith(system, "MIPS32EL")
AND NOT TextUtils.StartsWith(system, "MIPS64EL"))
OR TextUtils.StartsWith(system, "PPC") (* ambiguous *)
OR TextUtils.StartsWith(system, "SPARC")
OR TextUtils.StartsWith(system, "SOL") THEN
Little_endian := FALSE;
END;
(* SPARC: 8K pages *)
IF IsSPARC() THEN
First_readable_addr := 8192 * Char.size;
END;
(* x86 and AMD64 allow unaligned loads/stores *)
IF IsX86() OR IsAMD64() THEN
Allow_packed_byte_aligned := TRUE;
Aligned_procedures := TRUE; (* Assume aligned => unaligned is ok. *)
END;
IF System IN SET OF Systems{Systems.ALPHA32_VMS,
Systems.ALPHA64_VMS} THEN
Setjmp := "decc$setjmp";
END;
IF System IN SET OF Systems{Systems.PA32_HPUX,
Systems.PA64_HPUX} THEN
Structure_size_boundary := 16;
END;
CASE System OF
| Systems.ALPHA_LINUX => Jumpbuf_size := 34 * Address.size;
| Systems.ALPHA_OPENBSD => Jumpbuf_size := 81 * Address.size;
| Systems.ALPHA_OSF => Jumpbuf_size := 84 * Address.size;
| Systems.I386_FREEBSD, Systems.FreeBSD4 =>
Jumpbuf_size := 11 * Address.size;
| Systems.AMD64_NETBSD,
Systems.AMD64_OPENBSD,
Systems.AMD64_FREEBSD =>
Jumpbuf_size := 12 * Address.size;
| Systems.ARMEL_LINUX =>
Jumpbuf_size := 32 * Int64.size;
| Systems.PA32_HPUX =>
(* 200 bytes with 8 byte alignment *)
Jumpbuf_size := 50 * Address.size;
| Systems.PA64_HPUX =>
(* 640 bytes with 16 byte alignment *)
Jumpbuf_size := 80 * Address.size;
| Systems.MIPS64_OPENBSD,
Systems.MIPS64EL_OPENBSD =>
Jumpbuf_size := 16_53 * Address.size;
| Systems.I386_INTERIX =>
(* Visual C++'s 16 plus 2 ints: is sigmask saved, its value. *)
Jumpbuf_size := 18 * Address.size;
| Systems.NT386, Systems.I386_NT, Systems.I386_CYGWIN, Systems.I386_MINGW =>
(* Cygwin: 13, Visual C++: 16, Interix: 18.
Use 18 for interop.
Cygwin's setjmp.h is wrong by a factor of 4.
Cygwin provides setjmp and _setjmp that resolve the same.
Visual C++ provides only _setjmp.
Visual C++ also has _setjmp3 that the compiler generates
a call to. In fact _setjmp appears to only use 8 ints
and _setjmp3 appears to use more. Consider using _setjmp3.
*)
Jumpbuf_size := 18 * Address.size;
| Systems.IA64_FREEBSD, Systems.IA64_HPUX,
Systems.IA64_LINUX, Systems.IA64_NETBSD, Systems.IA64_NT,
Systems.IA64_OPENBSD, Systems.IA64_VMS =>
(* random guess: 1K *)
Jumpbuf_size := 128 * Address.size;
| Systems.SPARC32_SOLARIS, Systems.SOLgnu, Systems.SOLsun =>
(* 76 bytes with 4 byte alignment *)
Jumpbuf_size := 19 * Address.size;
| Systems.SPARC32_LINUX =>
Jumpbuf_size := 16_90 * Char.size;
| Systems.SPARC64_OPENBSD =>
Jumpbuf_size := 14 * Address.size;
| Systems.SPARC64_LINUX =>
Jumpbuf_size := 16_280 * Char.size;
| Systems.SPARC64_SOLARIS =>
(* 96 bytes with 8 byte alignment *)
Jumpbuf_size := 12 * Address.size;
| Systems.I386_SOLARIS =>
(* 40 bytes with 4 byte alignment *)
Jumpbuf_size := 10 * Address.size;
| Systems.AMD64_SOLARIS =>
(* 64 bytes with 8 byte alignment *)
Jumpbuf_size := 8 * Address.size;
| Systems.I386_LINUX, Systems.LINUXLIBC6 =>
Jumpbuf_size := 39 * Address.size;
| Systems.AMD64_LINUX =>
Jumpbuf_size := 25 * Address.size;
| Systems.I386_DARWIN =>
Jumpbuf_size := 18 * Address.size;
| Systems.AMD64_DARWIN =>
Jumpbuf_size := ((9 * 2) + 3 + 16) * Int32.size;
| Systems.ARM_DARWIN =>
Jumpbuf_size := 28 * Address.size;
| Systems.PPC_DARWIN =>
Jumpbuf_size := 768 * Word8.size;
| Systems.PPC64_DARWIN =>
Jumpbuf_size := 872 * Word8.size;
| Systems.PPC_LINUX =>
Jumpbuf_size := 74 * Int64.size;
(* ideal alignment is 16 bytes, but 4 is ok *)
| Systems.PPC32_OPENBSD =>
Jumpbuf_size := 100 * Address.size;
| Systems.I386_NETBSD =>
Jumpbuf_size := 14 * Address.size; (* 13? *)
| Systems.ALPHA32_VMS,
Systems.ALPHA64_VMS =>
Jumpbuf_size := 68 * Word64.size;
Systems.I386_MSDOS =>
Jumpbuf_size := 172 * Char.size; TBD
| Systems.I386_OPENBSD =>
Jumpbuf_size := 10 * Address.size;
ELSE RETURN FALSE;
END;
InitCallingConventions (backend_mode,
System IN SET OF Systems{Systems.I386_INTERIX,
Systems.NT386,
Systems.I386_NT,
Systems.I386_CYGWIN,
Systems.I386_MINGW});
(* fill in the "bytes" and "pack" fields *)
FixI (Address, max_align);
FixI (Integer, max_align);
FixI (Word, max_align);
FixI (Longint, max_align);
FixI (Long, max_align);
FixF (Real, max_align);
FixF (Longreal, max_align);
FixF (Extended, max_align);
FixI (Int8, max_align);
FixI (Int16, max_align);
FixI (Int32, max_align);
FixI (Int64, max_align);
FixI (Word8, max_align);
FixI (Word16, max_align);
FixI (Word32, max_align);
FixI (Word64, max_align);
FixI (Void, max_align);
FixI (Char, max_align);
(* fix the alignments *)
FOR i := FIRST (Alignments) TO LAST (Alignments) DO
<* ASSERT Alignments[i] = MIN (Alignments[i], max_align) *>
END;
(* initialize the other target-specific modules *)
TargetMap.Init ();
M3RT.Init ();
RETURN TRUE;
END Init;
PROCEDURE InitCallingConventions(backend_mode: M3BackendMode_t;
calling_conventions: BOOLEAN) =
PROCEDURE New(name: TEXT; id: [0..1]): CallingConvention =
VAR cc := NEW(CallingConvention, name := name);
BEGIN
(* The external backend handles more calling convention details than the
integrated backend -- reversing parameter order and knowing how to
return structs. *)
IF calling_conventions THEN
cc.m3cg_id := id;
cc.args_left_to_right := NOT integrated;
cc.results_on_left := TRUE;
cc.standard_structs := NOT integrated;
ELSE
cc.m3cg_id := 0;
cc.args_left_to_right := TRUE;
cc.results_on_left := FALSE;
cc.standard_structs := TRUE;
END;
RETURN cc;
END New;
VAR integrated := BackendIntegrated[backend_mode];
BEGIN
(* 0 is __cdecl, 1 is __stdcall. *)
CCs := ARRAY OF CallingConvention{ New("C", 0),
New("WINAPIV", 0),
New("__cdecl", 0),
New("WINAPI", 1),
New("CALLBACK", 1),
New("APIENTRY", 1),
New("APIPRIVATE", 1),
New("PASCAL", 1),
New("__stdcall", 1) };
DefaultCall := CCs[0];
END InitCallingConventions;
PROCEDURE FixI (VAR i: Int_type; max_align: INTEGER) =
VAR success := TRUE;
BEGIN
<* ASSERT i.align = MIN (i.align, max_align) *>
i.align := MIN (i.align, max_align);
i.bytes := i.size DIV Byte;
i.pack := (i.size + i.align - 1) DIV i.align * i.align;
success := success AND TInt.FromInt (i.align, i.talign);
success := success AND TInt.FromInt (i.bytes, i.tbytes);
success := success AND TInt.FromInt (i.pack, i.tpack);
<* ASSERT success *>
END FixI;
PROCEDURE FixF (VAR f: Float_type; max_align: INTEGER) =
VAR success := TRUE;
BEGIN
f.align := MIN (f.align, max_align);
f.bytes := f.size DIV Byte;
(* f.pack := (f.size + f.align - 1) DIV f.align * f.align; *)
success := success AND TInt.FromInt (f.align, f.talign);
success := success AND TInt.FromInt (f.bytes, f.tbytes);
<* ASSERT success *>
END FixF;
PROCEDURE FindConvention (nm: TEXT): CallingConvention =
VAR cc: CallingConvention;
BEGIN
FOR i := 0 TO LAST (CCs) DO
cc := CCs[i];
IF (cc # NIL) AND Text.Equal (nm, cc.name) THEN RETURN cc; END;
END;
RETURN NIL;
END FindConvention;
PROCEDURE ConventionFromID (id: INTEGER): CallingConvention =
VAR cc: CallingConvention;
BEGIN
FOR i := 0 TO LAST (CCs) DO
cc := CCs[i];
IF (cc # NIL) AND (cc.m3cg_id = id) THEN RETURN cc; END;
END;
RETURN NIL;
END ConventionFromID;
BEGIN
END Target.