Internal Memo XEROX To From Cedar to C interest Russ Atkinson PARC/CSL Subject Date Installation notes July 20, 1987 Revised by C. Hauser April 17, 1990 Filed as: [Ivy]Mimosa>InstallationNotes.tioga The environmental assumptions In this note we assume that we want to target the C language from the Cedar language. We also assume that we want to preserve most of the abilities that we have in the Cedar programming environment, although we also assume that the operating system is normally provided by some external source. We do not want to make assumptions about which hardware is being used, about which operating system is being used, or which languages we must accomodate in the same addressing space. We assume the operating systems provides some kind of loader that supports independent compilation. In particular, if some module provides a definition for some named global variable, then the address of that variable is made available at load time to other modules. The current Cedar loader is based on the object files (known as BCD files) produced by the compiler and binder. These files contain a number of tables that provide information on imports, exports, types, and literals. This information is used at load time to maintain the load state. This load state is used to connect together independently compiled modules. The load state is more complicated that a simple naming of global variables, since Mesa and Cedar provide for a separation between interfaces and implementations. The load state is also useful for debugging purposes. It maintains a hierarchical space of named modules, interfaces and configurations (collections of modules). This support is used by the interpreter. The Cedar support for runtime types and collectible storage (aka Safe Storage) depends on another set of tables interpreted by the Cedar loader. Since we want to keep our assumptions about the C compiler and operating system minimal, we cannot assume that tables can be easily generated and passed through to the loader. Rather, we assume that must generate code to perform these functions. The installation procedure The installation procedure is a distinguished procedure in a module that achieves the behavior of the Cedar loader in an environment that has only a simple loader. This procedure is called as soon as practical after a module is loaded. The installation procedure relies on a set of runtime routines presumed to be present in any system that supports the Cedar language. The installation procedure must not depend on calls to any other modules, or on the order of installation of other modules. For simplicity we assume that the effects of the installation routine appear to be atomic; to the load state the installation routine appears to be either fully executed or not at all executed. The installation support The installation support is a set of procedures that support the primitive operations needed by the installation procedure. RC Map instantiation RC maps are used to determine where the references are in objects. Each type has a single RC map. The installation support for RC maps is quite simple: GetRCIndex: PROC [desc: RCMapPtr] RETURNS [RCMapIndex] This procedure instantiates a description of an RC map and returns the canonical index for that map. The current implementation does not use RCMaps, and the fucntion is not provided. Type instantiation At runtime each collectible object has a type, which can be retrieved cheaply. The runtime only requires that types can be checked cheaply for equivalence, and that there is an association between types and RC maps. For debugging, however, we also require that each type has a "location", which indicates where the information about the structural description of the type can be found. The operation is declared as: TypeDescPtr: TYPE = STRING; GetTypeIndex: PROC [typeString: TypeDescPtr, rcMap: RCMapIndex] RETURNS [TypeIndex] This procedure instantiates a description of a type and returns the canonical index for that type. Ref literals Ref literals are either atom literals (type ATOM), rope literals (type ROPE) or text literals (type REF TEXT). The runtime support simply requires the ability to instantiate such literals, although we provide for literals of any type. LiteralDescPtr: TYPE = STRING; GetRefLiteral: PROC [type: TypeIndex, contents: LiteralDescPtr] RETURNS [REF] This procedure returns the canonical REF literal of the given type and contents. An error is raised if the type given is not supported in the system. Global frames Global frames are named objects in the load state. For debugging and trace and sweep garbage we require that each global frame has a type. Name: TYPE = POINTER TO CHAR[]; -- that is, pointer to a C string; GlobalFramePtr: TYPE = POINTER; DeclareGlobalFrame: PROC [name: Name, base: GlobalFramePtr, type: TypeIndex, initialization: PROC] This procedure declares that the global frame (starting at base) has the given name and type. The name is useful for maintaining the load state, and the type (and RC map) are useful for garbage collection. The initialization procedure is called when the module is started (either by START or a start trap). We assume that the global frame storage already exists, and has been allocated and cleared by the underlying loader. It is perfectly acceptable to have multiple instances of the same name, possibly with different types, in the same name scope. Interface records In PrincOps implementations of Mesa an interface record was an abstract notion. That is, one could define a type for the interface that could be expressed as a record type, but that record did not have an existance. Actually, the Cedar load state created such instances for debugging purposes, but did not use them for dispatching calls or variable references. In the Mimosa model, interface records have actual instances that will be used as indirection sites for procedure calls and variable references. We believe that this actually simplifies the overall implementation. A module can either import or export an interface. The import or export is performed at installation time, although the order of imports and exports is unimportant in the absence of type or name clashes. The operations to import and export interfaces are: ImportInterface: PROC [name: Name, type: TypeIndex, size: INT] RETURNS [InterfacePtr] ExportInterface: PROC [name: Name, type: TypeIndex, size: INT] RETURNS [InterfacePtr] The two procedures in fact have identical semantics: they provided binding to an interface record with the correct type and size in the current scope (one is created if this is the first time). An anomaly is reported if there is already an interface name in the scope with the same name and a different type or size. The size is given to allow allocation of a new interface record. This is necessary since the type index alone does not have sufficient information to indicate the amount of storage necessary. A name conflict in importing or exporting an interface may or may not be an error. It is a policy decision. The runtime support leaves that decision to higher levels of software, but reports such conflicts as anomalies. If different interfaces with the same name are allowed in a name scope version checking is partially defeated. However, this is useful in allowing new versions of modules based on new interfaces to be loaded. In implementing interface records one must decide how they should be initialized. Every imported variable uses a pointer, which can be initially NIL (causing an address fault if used). However, calling an unexported procedure should cause a resumable trap. So for each procedure imported, the installation routine calls ImportProc to fill in that slot with the appropriate trapping procedure. The interface is: ImportProc: PROC [interface: InterfacePtr, index: INT] Exporting is similar to importing, in that the same name scope rules apply, and in that a new interface record may need to be created. Exporting a procedure causes the appropriate slot in the interface record to be filled with a procedure that causes a start trap when called. The trapping procedure will cause the initialization procedure of the module to be called, and will also remove the cause of further start traps for procedures exported by the module. Of course, since such procedures can be passed as values as well as being called, the trapping procedures must be benign if stumbled across after the module is started, but this should be rare. Exported types Exporting a type causes a global association between an abstract an a concrete type. Unfortunately, since the type space is global, there can only be one concrete type for any given abstract type in the system, although the same concrete type can be exported to many abstract types. The operation to export a type is described as: ExportType: PROC [name: Name, abstract: TypeIndex, concrete: TypeIndex] ExportType exports the given concrete type as an implementation for the given abstract type under the given name. An anomaly is reported if the exported concrete type is inconsistent with a previously exported concrete type for the same abstract type. The name is given for debugging purposes. Consider the problem if we allowed more than one concrete type for a given abstract type. It might then be possible to take a value of abstract type and pass it to a module that assumed one concrete type when, infact, the abstract type had a completely different concrete type as its implementation. Since there is no runtime checking for coercions between abstract and concrete, this error would be difficult to detect, and could be abused to invalidate the type safety assumptions of Mesa. Name scope control Name scope control is not needed for modules produced by the compiler, but it essential for modules produced by the binder.  In the generated C code, the name of this procedure is XR_GetTypeIndex. In the current implementation the rcMap is ignored. When rcMap # unknownRCMapIndex, the rcMap is used to establish the RC Map for objects of the type. If a previous RC Map (# unknownRCMapIndex) existed for the type, and the previous desc is not the same as the current desc, then an error is raised. In the generated C code this procedure is XR_GetRefLiteral. In the generated C code this procedure is XR_DeclareGlobalFrame. In the generated C code they are XR_ImportInterface and XR_Export Interface. In the generated C code this procedure is XR_ExportType. <NewlineDelimiter (cedardoc) styleStyleDefBeginStyle (Cedar) AttachStyle (firstHeadersAfterPage) {0} .cvx .def (root) "format for root nodes" { FontPrefix docStandard 36 pt topMargin 36 pt headerMargin .5 in footerMargin .5 in bottomMargin 1.75 in leftMargin 1.5 in rightMargin 5.25 in lineLength 24 pt topIndent 24 pt topLeading 0 leftIndent 10 pt rightIndent } StyleRule (positionInternalMemoLogo) "Xerox logo: screen" { docStandard 1 pt leading 1 pt topLeading 1 pt bottomLeading } ScreenRule (positionInternalMemoLogo) "for Xerox logo" { docStandard 1 pt leading 1 pt topLeading -28 pt bottomLeading -1.5 in leftIndent } PrintRule (internalMemoLogo) "Xerox logo: screen" { "Logo" family 18 bp size 20 pt topLeading 20 pt bottomLeading } ScreenRule (internalMemoLogo) "for Xerox logo" { "Logo" family 18 pt size 12 pt leading -28 pt topLeading 36 pt bottomLeading -1.5 in leftIndent } PrintRule (memoHead) "for the To, From, Subject nodes at front of memos" { docStandard AlternateFontFamily 240 pt tabStops } StyleRule EndStyleIblockMark insideHeaderbox IpositioninternalmemologoIinternalmemologomemoheadsNt !N NNNNN N N#NNN77headKKKKKKKKKKK{Icode16 bp topLeadingn k 6KffKeQQP16 bp topLeading P16 bp topLeading - SbbInoteGGQ3Q  ,P16 bp topLeadingP16 bp topLeading ,MK%nQ;; P16 bp topLeading c"BP16 bp topLeadingP16 bp topLeadingDbKKQ@@QKK3P16 bp topLeading$UP16 bp topLeading$UQLLQQQP16 bp topLeading "6KKP16 bp topLeading 7GQ88QK}&1v