Portability by Design

Mapping structures are one place to start

Michael Ross

Michael is a senior compiler developer for MetaWare with over 16 years of compiler development and management experience. He can be contacted at 408-429-6382.


When it comes to writing software for multiple operating systems and microprocessors, you really only have two choices: design portability into your code from the start, or wait until you are forced into expensive rewrites.

Where I work (MetaWare), we often have to quickly port a compiler from one platform to another. It isn't unusual to have a compiler that compiles itself on the new platform (that is, a particular processor/OS combination) in six to eight weeks, with one to two people working on the project. This is possible because portability--the ability to compile, run, and achieve close to the same results as well as the same look-and-feel across platforms--was designed into the compiler from the very beginning. In this article, I'll discuss some of the techniques we've developed that you can use to enhance the portability of your code.

MetaWare got started in the compiler business in 1983 because we were having difficulty porting our parser generator to various Pascal compilers. These problems motivated us to write a portable compiler of our own. In moving to various processors and operating systems, we distilled some knowledge of the problem areas in writing transportable code.

Some people believe that if you write in C or C++ and stick to the latest ANSI or de facto standard, your code is automatically portable. If they're a bit more savvy, they may allow that you need some kind of platform-specific GUI-development package. These are the same people likely to storm into your office when you tell them that it will take six months and a rewrite of some modules to port application XYZ to a new platform.

To keep tempests out of your office, here are some things to think about when writing code:

Listing One (page 93) shows a structure used to encapsulate machine and operating-system dependencies. The mapping structure, with its associated macros, communicates dependencies between our C or Pascal front end and the code generator for the target processor. You can broadly categorize the platform characteristics encapsulated in this mapping structure as:

Listing Two (page 94) includes an ideal representation of a floating-point number, with a mantissa of 128 bits. The compiler uses this for internal manipulation of floating-point constants and then converts them to whatever external floating-point representation is required on the target machine. The code uses the knowledge of the longword size of the host machine where the compiler is running to assure that we don't shift by an amount that will yield invalid results on the host system.

Your code should contain safety checks to prevent you from relying too much on a particular word size or floating-point representation.

The problem with code that does a lot of floating-point calculation is that the precision of the results and the execution speed of the code may vary widely from one architecture to another. Even on a machine that has some variation of IEEE floating point, the defaults as to which kinds of overflow traps are signaled or ignored can vary. On the 80387 and Motorola 68040, the varying frequencies with which compilers store floating-point results to memory can significantly affect the accuracy of your results. Are the potential target platforms flexible--through library calls or some other mechanism--in determining how rounding will occur, when results are stored, and what traps are signaled? If not, you should consider implementing your own extended-precision floating-point operations. The MetaWare C compiler, for example, maintains its own internal version of a floating-point constant and constant operations. When necessary, the compiler converts floating-point constant results to the representation of the target architecture.

"Portable" applications should have roughly the same look-and-feel across platforms. When you port an application that is interactive and heavily dependent on floating point (a CAD package, for example) to a new architecture, some algorithms run too slowly. This is because they depended upon some capability of the original architecture (such as built-in transcendental instructions) for reasonable run-time performance. With the trend toward RISC architectures, you may want to examine your code for such hot spots and change the algorithm. You might even write your own transcendental routines to insulate the application from performance changes across platforms.

Alignment of data is a problem mainly when your application depends on transmitting binary data across platforms, or uses different language processors on the same platform. Aside from the obvious endianness difficulties, there are variations in field alignment. This often necessitates representing the binary data in textual form or having a data broker perform the necessary transformations between the two architectures. Consider the "bad" C code in Example 1(a), which was intentionally written to demonstrate alignment problems. The alignment of the various structure fields will vary from one compiler to another and from one architecture to another. Imagine trying to write this to a binary file and read it back on another platform! Even with the same language processor, this structure will vary in size, due to memory "holes"; for example, on SPARC sizeof(mystruct)=24 bytes, on 80386 sizeof(mystruct)=16 bytes, and on HP-PARISC sizeof(mystruct)=24 bytes.

The problem is worse, of course, in languages like Fortran. If you write code like that in Example 1(b), it may compile and run on some architectures, while others will cause a trap at run time. On the Intel i860 processor, doing a load of a double-precision value that is not aligned on a double-precision boundary is difficult and slow, and some compilers don't support it. Other RISC architectures have similar problems. Note that the Fortran code in Example 1(b) fits within the ANSI standard; it does not fit our definition of portability, however. You should examine your data structures, regardless of the programming language you use, and ensure that structure and union fields are aligned on common, natural boundaries.

There may soon be some light on the horizon to help with some of the binary-data compatibility problems, in the form of SOM (system object model), which allows a common binary format for objects across architectures and languages. SOM meshes with the activities of CORBA, which may eventually allow different C++ compilers to share a common object format.

The Fortran programming community that depends on de facto characteristics of old compilers may be in for a rude shock as vendors write new Fortran-90 compilers. I'm always amazed at code such as that in Example 1(c) that lives on in many applications. This code depends on zero initialized memory and static allocation of local variables. With its RECURSIVE keyword and name-space rules, Fortran-90 encourages automatic allocation of local variables. Most optimizers will not perform up to their potential if you use statically allocated variables.

C programmers are not exempt from writing absurdities, either. The most common used to be the use of the register specifier. Most optimizing compilers now ignore the register specifier altogether. If you move your code from one architecture to another, you can't expect the same set of variables to be maintained in registers across architectures. Usually, the register allocator can do a better job than a person of choosing registers and keeping the choices optimal as the source code changes.

Operating-system Dependencies

Occasionally, an application has to manipulate data that is inherently operating-system dependent. Listing Three (page 94) shows a class definition for managing processes. This code encapsulates everything your application might need to know about a process, regardless of the underlying operating system. Note that operating-system dependent actions such as stepping the process by an instruction are deferred to member functions that can be extremely operating-system specific. The remainder of the application, however, uses these operations through the class definition in Listing Three, allowing you to isolate the OS-specific code from the rest of the application. Virtual functions (which are not used here) can also help you to isolate system dependencies by allowing you to fill in a specific member function that matches a particular OS.

User-interface Dependencies

If the application's user interface is more complicated than a simple command-line switch interpreter, you'll need to consider a GUI manager. Since X Windows is available on almost all UNIX systems, you could create a relatively portable system by taking into consideration X Windows, Microsoft Windows, OS/2, character-based MS-DOS, and Apple Macintosh. However, if you can find a reliable vendor who has already encapsulated these APIs for you, it's probably worth your money to buy the class libraries. Be aware, however, that some GUI vendors depend on a Motif layer, which is not supplied with the product, and not bundled with the OS by many vendors.

Another consideration in GUI class-library selection is the trend toward software internationalization. If possible, examine the source code and see how hard this is going to be for you. A good technique is to group all of the messages that a user might see in a single table or separate resource file. Then the input and output can be translated without affecting the remainder of the application. If your system allows, you may even be able to have different national-language versions of the user interface in DLLs that can be loaded on demand.

Taking all of these different factors into consideration as you write a new application or port an old one seems like a lot of extra effort. However, if you design portability in from the beginning, you'll save yourself a lot of effort later.

Example 1: (a) Bad code that intentionally demonstrates problems in alignment; (b) nonportable Fortran code which will compile and run on some architectures, but on others will cause a trap at run time; (c) Fortran-90 encourages automatic allocation of local variables.

(a)
#include <stdio.h>
struct bad_layout{
   char direction;
   union {
      char var;
      float fvar;
      double dvar;
      } mess;
   int myint;
   };

main(){
 struct bad_layout mystruct;
 printf("sizeof(mystruct) = %d\n", sizeof(mystruct));
 }

(b)
     COMMON /SLOW/  I,D,L
     EQUIVALENCE (D,N(2)),(I,N(1))
     INTEGER I,N(2)
     DOUBLE PRECISION D
     LOGICAL L

     PRINT *,D
     END

(c)
     SUBROUTINE USELESS(I,J)
     INTEGER I,J
     IF (J.EQ.0) GOTO 10
     I = J ** 2
     GOTO 20
10   I = 3
     J = 10
20   CONTINUE
     END


[LISTING ONE]


/* Machine dependent information required by front-end  */
#ifndef CALLCONV_H
#include "callconv.h"
#endif

#ifndef LANGUAGE_H
#include "language.h"
#endif

#ifndef FP_H
#include "fp.h"
#endif
#ifndef TCLASSES_H
#include "tclasses.h"
#endif

typedef unsigned long inline_flags_type;

typedef enum {
     CPU_unknown,

     CPU_I386,      /* Intel 386        */
     CPU_370,       /* IBM 370          */
     CPU_RT,        /* IBM RT           */
     CPU_AM29K,     /* AM29000          */
     CPU_I860,      /* Intel 860        */
     CPU_MC68000,   /* Motorola 68000   */
     CPU_MC68020,   /* Motorola 68020   */
     CPU_VAX,       /* DEC VAX          */
     CPU_MDD,       /* McDonald Douglas */
     CPU_SPARC,     /* SPARC (Sun 4)    */
     CPU_R3000,     /* MIPS R3000       */
     CPU_HOBBIT,
     CPU_PARISC,    /* HP9000-600/700/800 PA-RISC */
     CPU_Ix86,      /* 80x86 -- 16-bit Intel      */
     CPU_RS6000,    /* IBM RS-6000                */
     CPU_PPC        /* IBM Power PC               */
     } CPU_type;

/*Object module formats*/

typedef enum {
     NOFORMAT,      /* OMF not applicable or unknown*/
     COFF,          /* Basic AT&T COFF              */
     AMDCOFF,       /* AMD version of COFF          */
     LAMDCOFF,      /*AMD version of COFF (little-endian mode) */
     INTELOMF,      /* Intel OMF (MSDOS)            */
     ADOTOUT,       /* BSD a.out                    */
     ELF,           /* SVR4 ELF format              */
     MACH,          /* MACH object module format    */
     HPUXCOFF,      /* HPUX version of COFF         */
     } objformat_type;

/* Operating systems */
typedef enum {
     NOOS,               /* OS unknown            */
     AIX_OS,             /* IBM's UNIX            */
     BSD_OS,             /* Berkeley UNIX  BSD4.x */
     SUN_OS,             /* Sun OS                */
     ATT_OS,             /* AT&T UNIX  System V.3 */
     EPI_OS,             /* EPI-OS (AM29000)      */
     VMS_OS,             /* DEC                   */
     MSDOS_OS,           /* MS-DOS                */
     OS2_OS,             /* OS/2                  */
     XNX_OS,             /* Xenix                 */
     ISIS_OS,            /* Isis                  */
     ATT4_OS,            /* AT&T UNIX  System V.4 */
     NeXT_OS,            /* NeXTStep from NeXT    */
     NEWS_OS,            /* Sony NEWS-OS          */
     MSNT_OS,            /* Microsoft NT          */
     SOL_OS,             /* Solaris               */
     HPUX_OS,            /* HP UNIX System V.     */
     } os_type;

/* Inline transcendentals */
#define SIN_INLINE  0x00000001
#define COS_INLINE  0x00000002
#define ASIN_INLINE 0x00000004     /* Arcsin or Arccos */
#define SQRT_INLINE 0x00000008
#define LOG_INLINE  0x00000010     /* Natural or common log */
#define EXP_INLINE  0x00000020
#define TAN_INLINE  0x00000040
#define ATAN_INLINE 0x00000080
#define ATAN2_INLINE 0x00000100    /* Arctan(a,b) */
#define SINH_INLINE 0x00000200
#define COSH_INLINE 0x00000400
#define TANH_INLINE 0x00000800     /* tanh */
#define FABS_INLINE 0x00001000     /* fabs */
#define ASM_INLINE  0x00002000     /* ASM directive supported */
#define INS_INLINE  0x00004000     /* _inline(bytes) */

/* Status of pragma processing: */
typedef enum {
          OKAY_DELETE_p, /* Pragma fully processed; do not pass to backend  */
          OKAY_PASS_p,   /* Pass pragma to back-end */
          TOO_LATE_p,    /* Pragma specified too late */
          ERROR_p,       /* Suboperands of pragma incorrect */
                         /*  badptr set to bad parameter */
          IGNORED_p,     /* Already specified */
         } PRAG_STATUS;
struct struct_aligns {char if_its_this_big, align_to_this;};

extern struct mapping_entry{
     /* False if structs are packed by default */
     bool align_members;
     ubyte shortsize;    /* size of "short" in bytes        */
     ubyte intsize;      /* size of "int"   in bytes        */
     ubyte longsize;     /* size of "long"  in bytes        */
     ubyte floatsize;    /* size of "float"                 */
     ubyte doublesize;   /* size of "double"                */
     ubyte longdoublesize;/* size of "long double"          */
     ubyte min_parm_align;/* Min alignment of a passed parm */
     bool unsigned_char; /*"char" unsigned by default?      */
     bool pure_32_bit;   /* All integer operations in 32 bits?*/
     ubyte code_ptr_size; /* Code pointer size              */
     ubyte data_ptr_size; /* Data pointer size              */
     /* Size of "near" pointer to code  */
     ubyte near_code_ptr_size;
     /* Size of "far" pointer to code   */
     ubyte far_code_ptr_size;
     /* Size of "near" pointer to data  */
     ubyte near_data_ptr_size;
     /*Size of "far" pointer to data    */
     ubyte far_data_ptr_size;

     ubyte short_align;  /* Alignment of shorts        */
     ubyte long_align;   /* Long alignment             */
     ubyte int_align;    /* int alignment              */
     ubyte ptr_align;    /* pointer alignment          */
     ubyte double_align; /* double alignment           */
     ubyte max_field_align; /* Maximum field alignment */
     bool  msb_first;    /*integers stored MSB first?  */
     /* Align all vars to int boundary (even chars)    */
     bool  word_align_vars;
     bool off_boundary_refs;/* Are off-boundary refs supported?*/
     char *version;      /* Version number of compiler */
     /* Align structs 3 bytes or longer to word boundary */
     bool word_align_structs;
     calling_convention_set default_calling_convention;
     /* If true varargs must be explicitly specified with "..."  syntax  */
     bool ansi_varargs_only;
     /* Which transcendentals may be inline? */
     inline_flags_type inline_flags;
     ubyte max_parm_align;/* Max alignment of a passed parm */
     ubyte offset_size;  /* Pascal offset size */
     ubyte area_size;    /* Pascal area size   */
     /* Largest array in bytes (Pascal) */
     unsigned long max_data_size;
     bool signed_halfwords_preferred; /*True for 370. (Pascal)*/
     bool range_checks_require_range; /*Do we need a lower and upper
                              value for rangecheck?*/
     /* How are Pascal sets mapped?    */
     /* Also see "set_unit_size" below */
     bool sets_mapped_LSB_first;
     /* used in machines where pointer arithmetic will not work unless the
      * computed address is a multiple of machine's word size. */
     ubyte address_resolution;
     /* In creating a common block for Professional Pascal interface packages,
      * we use the name of the package, prefixed and suffixed with a
      * special character. */
     /* char to be prefixed to Pascal package block*/
     char package_prefix;
     /* char to be suffixed to Pascal package block*/
     char package_suffix;
     /* size of "char" in bytes, believe it or not char is 8 bytes on NAM */
     ubyte charsize;
     /* Format of floating point */
     real_form floating_point_format;
     /* If off-boundary references are NOT supported, do we
      *  handle packed structures nevertheless?  */
     bool packed_structs_supported;
     ubyte char_align;        /*Alignment of "char" */
     ubyte float_align;       /*Alignment of "float" */
     ubyte longdouble_align;  /*Alignment of "long double" */
     char  bits_per_int; /*bits per integer used for bitfields */
     /* Implies the machine can convert (signed|unsigned)(char|short) to float
      * in a way more efficient than to double. The result is that the front
      * end is careful to generate FLT vs FLTU in this context so back end can
      * tell from register length and FLT/FLTU whether to generate the better
      * code and which kind of better code (it differs from U to nonU). */
     bool short_to_float;
     /* Given the case index of a pseudo-function that target machine is
      * usually supports, do we support it with corrent configuration? */
     bool (*recognize_pseudo_function)(func_case_index);
     CPU_type cpu;  /* Target CPU */
     os_type os;    /* Target OS  */
     objformat_type omf;  /*Target OMF*/
     /* For non-padded structs, search this array, terminated by {0,0}.
      * If struct size is >= first member, align to at least 2nd member.
      * Thus "word_align_structs" could be {3,4},{2,2},{0,0}. */
     struct struct_aligns *struct_aligns, *array_aligns;
     bool use_INFO; /* Can code generator tolerate INFO
                     * rather than LINE/FILE/STAB ? */
     type_class wchartype;    /* this can be any intger type */
     ubyte wcharsize;         /* sizeof (wchar_t) */
     /* We changed the way debugger information is passed to code generator.
      * If "debug_info_scheme" is 0, then old way is used. Otherwise, the
      * new way. */
     ubyte debug_info_scheme;
     bool try_supported;
     bool wchar_is_short; /* wchar_t is short int */
     bool wchar_is_long;  /* wchar_t is long int */
     /* Given a toggle that may have been defined in
      * establish_machine_dependencies, is it still good at this point? */
     /* Return TRUE if so. Otherwise, the compiler will issued */
     /* "Toggle cannot be specified here." */
     bool (*toggle_is_valid)(toggle t, bool turn_it_on);

     /* "init", if not zero, is called when the first non-pragma is seen.
      * Its primary purpose is to adjust other fields in mapping that are
      * dependant on pragmas and toggles. E.g. 1167 toggle for 386 causes long
      * doubles to be 8 bytes instead of 12  */
     void (*init)(void);
     /* The default global aliasing convention. NULL implies "%r". */
     char *global_aliasing_convention;
     /* Are "far" variables support? That is, does it make
      * sense to qualify a variable declaration with "_far"? */
     /* This must be FALSE on ESA/370. */
     bool far_variables_supported;
     /* Are "far" functions supported? If false, compiler will */
     /* ignore "_far" and issue a warning. */
     bool far_functions_supported;
     /* Does the target machine's OMF handle multiple named */
     /* control sections (segments)? */
     /* If this is false, then the front-end will ignore */
     /* "pragma code", "pragma literals(xx)", and "pragma static_segment()". */
     /* (Pragma data is handled on UNIX by converting to named blocks). */
     bool multiple_named_sections_supported;
     /* Can control sections of class "common" be initialized? */
     /* On DOS and VMS this is true. */
     bool common_section_initialization_supported;
     /*  Are we generated code for a strict IEEE machine? */
     /*  If true, we must generate unordered comparisons. */
     /*  I.e.,  X < Y is a different operation than !(X >= Y). */
     bool IEEE_unordered_compares_supported;
     /* code_sections_supported -- if true, the target machine */

     /* had separate I & D spaces but is able to reference the */
     /* I space as data. (E.g.,386 with its segment override). */
     /* If set to TRUE, the toggles "const_in_code" and "literals_in_code"
      * will be supported for putting const variables into instruction space.*/
     bool code_sections_supported;
     /* Is the calling convention "CALLEE_POPS_STACK" supported? */
     bool callee_pops_stack_supported;
     /* Is the calling convention "REVERSE_PARMS" supported? */
     /* (Applies to those machines that have a "PUSH" instruction only. */
     bool reverse_parms_supported;
     /* Default data aliasing convention for Professional Pascal */
     /* Default routine aliasing convention for Professional Pascal */
     char *data_aliasing_convention;
     char *routine_aliasing_convention;
     /* set_unit_size  in conjunction with the "sets_mapped_LSB_first" */
     /* flag above determine how (Pascal) sets are mapped in storage. */
     /* High C/Professional Pascal ordinarily maps sets as a sequence
      * of halfwords. Microsoft maps them as an even number of bytes,
      * MSB first. */
     ubyte set_unit_size;
     /* type_checking is true if type information should be kept even when
      * g_flag is off. This is used for 370/ESA type checking at link time. */
     bool type_checking;
     /* Size of a "long long" type. If such  types are not supported,
      * then the size should match "long". */
     ubyte longlongsize;
     ubyte longlong_align;    /* Alignment of long long */
     /* What is the largest auto-variable of type struct that can be mapped
      * directly into a series of one or more registers? Value is in bytes. */
     uint largest_aggregate_in_registers;
     /* When doubles or long doubles are just plain variables (or arrays
      * thereof), what should the alignment be? For Pentium, it's much
      * better for them to be 8-byte aligned. */
     ubyte double_variable_align;  /*double variable alignment */
     /*Alignment of "long double" variable */
     ubyte longdouble_variable_align;
     bool supports_call_lit;   /* Code generator supports call-lit construct.*/
     /* These tell if we require certain calling convention bits: */
     /* Must have cc_CALLEE_POPS_STACK.*/
     bool callee_pops_stack_required;
     bool reverse_parms_required;/* Must have cc_REVERSE_PARMS.*/
     /* For C++ constructors/destructors of static/global variables, does
      * get handle the initialization level protocol? True for systems
      * that support .init/.fini sections. */
     bool support_initialization_order;
     /* Use Sun's and AT&T's convention for mapping bit fields? */
     bool ABI_bit_fields;
     /* SYS_SIZETTYPE takes on one of the values s, i, l for short, int, long.
      * SYS_SIZETSIGNED takes one of the values s, u, or n for signed,
      * unsigned or non-signed (for "char"). */
     char sizet_signed;  /* s, u, n. */
     char sizet_type;    /* s, i, l.  c not supported currently. */

     }mapping;
extern const char *machine_name;   /*Name of machine*/
#define LONGLONG_SUPPORTED  (SYS_LONGLONGSIZE > SYS_LONGSIZE)
#define SYS_PACKEDSTRUCT      (!mapping.align_members)
/* CHARSIZE is always the same on all machines. Wrong!! not on NAM */
#define SYS_CHARSIZE          mapping.charsize
#define SYS_INTSIZE           mapping.intsize
#define SYS_FLOATSIZE         mapping.floatsize
#define SYS_DOUBLESIZE        mapping.doublesize
#define SYS_EXTENDEDSIZE      mapping.longdoublesize
#define SYS_LINKSIZE          mapping.near_data_ptr_size
#define SYS_LONGLONGSIZE      mapping.longlongsize
#define SYS_SHORTSIZE         mapping.shortsize
#define SYS_LONGSIZE          mapping.longsize
#define SYS_SIGNEDCHAR        (!mapping.unsigned_char)

/*Pointer that is loaded into reg*/
#define SYS_PTRSIZE                SYS_LINKSIZE
#define SYS_DPTRSIZE               mapping.data_ptr_size
#define SYS_CPTRSIZE               mapping.code_ptr_size
#define SYS_WCHARSIZE              mapping.wcharsize
#define SYS_WCHARTYPE              mapping.wchartype
#define SYS_WCHARSHORT             mapping.wchar_is_short
#define SYS_WCHARLONG              mapping.wchar_is_long
#define SYS_SIZETSIGNED            mapping.sizet_signed
#define SYS_SIZETTYPE              mapping.sizet_type
#define BITS_PER_BYTE              8
#define BITS_PER_INT               mapping.bits_per_int
#define BITS_PER_LONG              (sizeof(long)*BITS_PER_BYTE)
#define SYS_INTAL                  mapping.int_align
#define SYS_LONGAL                 mapping.long_align
#define SYS_LONGLONGAL             mapping.longlong_align
#define SYS_SHORTAL                mapping.short_align
#define SYS_FLOATAL                mapping.float_align
#define SYS_CHARAL                 mapping.char_align
#define SYS_DOUBLEAL               mapping.double_align
#define SYS_LONGDOUBLEAL           mapping.longdouble_align
#define SYS_DOUBLE_VARIABLE_AL     mapping.double_variable_align
#define SYS_LONGDOUBLE_VARIABLE_AL \
mapping.longdouble_variable_align
#define SYS_PTRAL                  mapping.ptr_align
#define SYS_OFF_BOUNDARY_REFS      mapping.off_boundary_refs
#define SYS_STRINGAL               1    /*Alignment of strings*/
#define SYS_MSB_FIRST              mapping.msb_first
#define SYS_LSB_FIRST              (!SYS_MSB_FIRST)
#define SYS_STACK_AL               mapping.min_parm_align
#define SYS_32_BIT_ARITHMETIC_ONLY mapping.pure_32_bit
#define SYS_MAX_FIELD_AL           mapping.max_field_align
#define SYS_WORD_ALIGN_STRUCTS     mapping.word_align_structs

#define SYS_ADR_RESOLUTION         mapping.address_resolution
#define SEGMENT_REG_SIZE      (mapping.far_data_ptr_size - \
SYS_INTSIZE )
#define NEW_DEBUG_INFO             (mapping.debug_info_scheme> 0)

/* Establish_machine_dependencies --  initializes "mapping" to correspond
 * to "machine". Returns FALSE if machine is not recognized.  */
extern bool establish_machine_dependencies(
               const char *machine, language_type l);
/* Defines which object module formatter should be used for the target OS and
 * machine name of the target operating system passed by the driver */
extern const char *targetos_name;


[LISTING TWO]



#define LS (sizeof(long)*8)       /* Maximum bits for left shift      */
#define BS (sizeof(long long)*8)  /* Maximum bits for long long shift */
struct longlong{ /* Define structure for systems that don't support long long*/

    long lo;
    long hi;
    };
typedef struct longlong Big_int;
    /* Grab a mantissa from a floating point number */
    static long extract_mantissa(FP_number F){
    Big_int b;
    int cnt, rbit;
        cnt = BS -1 - F->Exp; /* Determine how far to shift to find exponent */
    if (cnt >= LS){  /* If shifting at least a longword          */
    cnt -= LS;   /* subtract longword length from shift count */
            if (cnt == 0)
        rbit = long(b.lo) < 0; /* Guard bit needed? */
            b.lo = b.hi;
    b.hi = 0;
    }
        if (cnt){       /* Anything left to shift? */
    rbit = (b.lo & (1L << (cnt-1))) !=0;
    if (cnt == LS){  /* Some machines can't shift by long word length */
        b.lo = 0;    /* Defend against this. Result would be zero */
        b.hi = 0;
        }
            else{
        b.lo = ((unsigned long)b.lo >> cnt | (b.hi << (LS-cnt));
        b.hi = (unsigned long) b.hi >> cnt;
        }
    }
    }


[LISTING THREE]



#ifndef Process_h
#define Process_h

#include    "addrvect.h"
#include    "disassem.h"
#include    "itemnumb.h"
#include    "linklist.h"
#include    "memacces.h"
#include    "modulemg.h"
#include    "stmt.h"
#include    "symbolta.h"
#include    "tempbkpt.h"


typedef int Outcome;
#define user_failure -1
#define failure 0
#define success 1

class Declaration;
class DiFile;
class EventMgr;
class ExecSpec;
class ExprObj;
class StackFrame;
class Status;
class Thread;

enum PSA1 { psa1_none,
        psa1_replace_breakop,
        psa1_replace_libpt,
};
enum PSA2 { psa_none,
        psa_run_to_retaddr,
};
class Process : public ItemNumber {
    Key     key;
    Thread *    current;        // 0 in ctor
    LinkList    threadlist;
    TempBkptMgr tempbkptmgr;
    EventMgr *  eventmgr;       // set in ctor
    MemAccess   memaccess;
    Disassembler    disassembler;
    Symboltable symboltable;
    ModuleMgr   modulemgr;
    AddrVector  destvector;
    Stmt        startstmt;
    Boolean     now_executing;
    DiFile *    target_difile;
    PSA1        psa1;
    PSA2        psa2;
    char            *current_lang;   // Temporary hack to be able
                                    //  to set the language.
    Thread *    lookup_thread( unsigned int );

    Outcome     check_state();
    Outcome     set_execspec( const ExecSpec & );
    Boolean     goal_attained();

    Outcome     start_step_into( Thread * );
    Outcome     start_step_over( Thread * );
    Outcome     start_step_retaddr( Thread * );
    Outcome     start_run( Thread * );

    Outcome     analyse_stmt_into( Boolean & );
    Outcome     analyse_stmt_over( Boolean & );

    Outcome     run_to_return_addr( Thread * );
    Outcome     execute_to_return_addr( Thread * );
    Outcome     run_to_caller( Thread * );

    Outcome     end_instr_step_into( Thread * );
    Outcome     end_instr_step_over( Thread * );
    Outcome     end_stmt_step_into( Thread * );
    Outcome     end_stmt_step_over( Thread * );
    Outcome     end_run( Thread * );

    Outcome     check_instr_step_into( Thread * );
    Outcome     check_instr_step_over( Thread * );
    Outcome     check_stmt_step_into( Thread * );
    Outcome     check_stmt_step_over( Thread * );
    Outcome     check_run( Thread * );

    Outcome     respond_to_hop_completion( Thread * );
    Outcome     respond_to_retpoint( Thread * );
    Outcome     respond_to_destpoint( Thread * );
    Outcome     respond_to_breakpoint( Thread * );
    Outcome     respond_to_watchpoint( Thread * );
    Outcome     respond_to_exception( Thread * );
    Outcome     respond_to_step_completion( Thread * );
    Outcome     respond_to_suspension( Thread * );
    Outcome     respond_to_exec( Thread *, DiFile * );
    Outcome     respond_to_libpt( Thread * );

    Outcome     respond_to_module_load( const Status & );
    Outcome     respond_to_module_unload( const Status & );
    Outcome     respond_to_thread_creation( const Status & );
    Outcome     respond_to_thread_destruction( const Status & );

    Outcome     start_stmt_step_into( Thread * );
    Outcome     start_stmt_step_over( Thread * );
public:
            Process( unsigned int, DiFile * );
            ~Process();

    Outcome     get_status( Status & );
    Outcome     wait_status( Status & );
    Outcome     update_status( const Status & );
    Boolean     is_executing() { return now_executing; }

    Outcome     run( const ExecSpec & );
    Outcome     instr_step_into( const ExecSpec & );
    Outcome     instr_step_over( const ExecSpec & );
    Outcome     stmt_step_into( const ExecSpec & );
    Outcome     stmt_step_over( const ExecSpec & );
    Outcome     resume();
    Outcome     stop();

    Thread *    current_thread() { return current; }

    Outcome     disassemble( const Addr &, Instruction &, Addr & );
    Outcome     find_stmt( const Addr &, Stmt & );
    Outcome     find_function( const Addr &, Declaration & );

    Outcome     evaluate( char *, StackFrame &, ExprObj & );
        Outcome         set_language(char *input_language);
    char           *get_lang_string();
    Process *   next() { return (Process *)Item::get_next(); }
};
#endif

Copyright © 1994, Dr. Dobb's Journal