[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference smurf::unix_objsym

Title:Digital UNIX Object File/Symbol Table Notes Conference
Moderator:SMURF::LOWELL
Created:Mon Nov 25 1996
Last Modified:Thu Jun 05 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:71
Total number of notes:314

44.0. "ISSUE 39: Split routines" by SMURF::LOWELL () Wed Dec 04 1996 16:01

T.RTitleUserPersonal
Name
DateLines
44.1comments on issue 39 from David C. P. LaFrance-LindenSMURF::LOWELLThu Dec 05 1996 16:344
44.2AssignedSMURF::LOWELLFri Dec 06 1996 14:541
44.3First draft proposal (open for review)QUARRY::petertrigidly defined areas of doubt and uncertaintyFri Mar 07 1997 17:29334
A Symbol Table Proposal for Representing Split Routines

by Peter Thompson

Introduction

        Put simply, the idea of routine splitting is essentially a
means to improve performance.  The typical executable has a small
amount of code that is the most frequently exercised, and a larger
portion that is less frequently hit.  Often a small loop may call a
routine through each iteration, and if the two sections of code are
widely separated, we increase the chances of cache misses.  If instead
we move the code from various routines that is most often exercised,
placing them together in a common spot, then the chance of a cache hit
increases.  For example, we might have a call to a function inside a
loop:


   for (x = 0; x < MAX; x++) {
        if (analyze_array(array_head, x) == TRUE)
          do_deep_scan(array_head, x);
   }

Depending on the size of analyze_array, it may be advantageous
to move the code from the loop outside of its enclosing routine,
and bunch it together with analyze_array, and possibly do_deep_scan.
This way, when the loop is entered, and analyze_array is called in 
each loop, instead of having to flush the cache each time to 
read in analyze_array, we can speed through the loop with everything
needed loaded into cache with the first iteration and not flushed
until we leave the loop.  The mechanics of actually splitting the
routine and grouping the code into cacheable pages is left as
an exercise for the compilers, linker, loader, and om.  However,
as a side effect, this will place some strain on symbol table 
consumers unless we plan this carefully.  

Requirements

        The producers and consumers of symbol tables will need to work
closely together to coordinate this change.  In order to do the actual
routine splitting the proposal is to do performance analysis of the
executable, and, using the generated information, rearrange the
executable so that the basic blocks most often exercised, the 'hot
blocks',are grouped together at the top of executable.  The 'cold
blocks' will move be moved downward. When this is done, the symbol
table must be changed to allow the consumers to follow the changes.
The consumers can be seen in two ways.  There are what I would
consider to be the 'passive' consumers, such as odump, and stdump,
which read and display the symbol table.  These will merely need to be
modified to recognize and display any new information.  Then there are
the active consumers.  These are the debuggers, the profilers, and
likely ostrip.  The debuggers need to be able to set breakpoints and
interpret variables in the section of code that is relocated, but
logically part of the original routine.  The profilers need to be able
to read and instrument the executable before it is run, so that they
can produce logical output when the instrumented executable is run.
Ostrip will need to recognize the new information to decide what to
keep and what to discard at various option levels.

Proposal

        There are some specific needs that can be described briefly.
First, we need a way to indicate which procedures have been split.
Then we need to know where the code was moved from, where it moved
to, and how much of the code was moved.

Taking a look at the procedure descriptor, there is a reserved 
field of 13 bits.  If we take one bit here for use as a flag, 
to indicate the split procedure, then we can maintain the size of the
PDR.  So the reserved field in the PDR would change to:

        unsigned split_proc : 1;        /* true if procedure is split */
        unsigned reserved : 12;         /* reserved: must be zero     */

Next there are addresses and size that would be needed by the consumers 
in order to process the executables correctly.  These are; the 
location that the code was originally located at, or more aptly,
the location of the branch to the moved code; the address
the moved code now resides at; and the length or size of the code that
was moved.  All this information should be readily available
to the producer at the time the routine is split.  A more complete 
explanation of how the code will look after it is split is given 
below in the producer task section.  Suffice it to say here that
by the original location, it should be understood that this is 
the location of the branch to the moved code after the procedure
has been split.  It is assumed that rather than leaving a gap
that is jumped over, the remaining code would be collapsed or moved
to fill in this gap.  

The location the code was originally at gives us 2 important 
pieces of information.  First, it allows us to line up each piece
of split code with it's original procedure.  It's a simple translation
from address to procedure, and if many procedures are split, 
this will be good way to keep them straight.  Second, it allows
us to deal with the line numbers in a somewhat straight forward
manner.  Due to the way the line number info is set up, it would
be easier to keep track of line numbers by looking to see if this 
is a split procedure, and if so, keeping track of the location
where the split takes place.  When we reach that location, 
then we can manipulate the counters so that we start tracking things
at the new location, and jump back when we reach the end of the
moved portion of code, which of course we know from the size of
that code.

Given the current structure of the symbol table, it seems the 
best location to store this information is in the comment 
section.  I propose the following to encode this information.
We need a new tag in the comment section to identify and locate 
the data.  The new tag, to stay with easily understandable names,
could be called:

CM_SPLIT_PRC

and would be assigned a value that does not conflict with any 
existing (or soon to be assigned) tag.

The information described above, branch address, new address, and
the size of the moved code, would fit naturally in a structure
which would be define in the scncomment.h header.  The structure
can be described as following:

struct split_info {
        unsigned long   branch_addr;    /* location of branch to code */
        unsigned long   vaddr;          /* current location of code   */
        unsigned long   size;           /* size of moved code         */
};

This should suffice to fit the needs of both consumers and producers.
The size field here would probably never to exceed the size of an int, as
that would likely exceed the size of an cache and invalidate the whole
exercise, but it seems that it would ease alignment if it is kept at
an unsigned long.  Perhaps we could break this into an unsigned
int followed by a reserved int.

Producer Tasks

        Currently OM is the only producer capable of splitting
a procedure.  Prior to the split let us presume that we have an
image that looks something like the following:

        A1->                    /* Main entry point  */
        A2->    (C)             /* Basic block, tight loop calling C */
        A3->                    /* intermediate initializing code    */
        A4->    (B)             /* Another tight loop calling B      */
        A5                      /* exit code, clean up tasks         */
        B1->                    /* Initializing code mostly skipped  */
                                /* after first run.                 */
        B2->                    /* Main body of routine B, tight loop */
        B3                      /* post loop cleanup code           */
        C                       /* whole body of C                  */


The basic plan is to move the most exercised routines and blocks,
the "hot" code, to the top of the executable, and for blocks,
to the top of the routine if they are not entirely split out of 
the routine.  The 'cold' routines and blocks are pushed towards
the bottom of the executable or routine.  

After OM has split the executable, the layout of the file will look
very different.  Depending on which is the hotter loop, either 
C or B may come out on top.  For the sake of argument, let us suppose
that A2 is the hotter loop, and C therefore the hotter routine.
This leaves us with something that should resemble the following:

        A2->            /* hottest loop, calls C, ends with branch to A3 */
        C               /* hottest routine */
        A4->            /* next hottest loop, calls B, ends with br to A5 */
        B2              /* hottest block in B ends with branch to B3 */
        B1->            /* entry point in B, branches to b2 */
        B3              /* exit point of routine */
        A1->            /* main entry point, ends with branch to A2 */
        A3->            /* code immediately follows br to A2, ends with branch
                        /* to A4 */
        A5              /* code immediately follows br to A4 */

Once OM has split the procedure in the above or a similar fashion,
all the addresses in the symbol table will be updated to reflect
the new location of the various routines and entry points.  The 
debuggers could probably use this as is, to set breakpoints on
main entry points, and follow the code with next and step 
instructions, but would get confused in setting breakpoints 
and determining scope in the sections of a routine that has
been moved.  Exception handling should not be affected, since
OM will modify the code range descriptors to reflect the new layout of 
the program.  The same runtime procedure descriptors can be used,
as the flow through the code is not affected, just the location of
the actual pieces.

In order to allow the consumers to deal with the split procedures, OM
(and any other producer that may do this in the future) will need to
modify the procedure descriptor and write the proposed information
into the comment section.  First the procedure descriptor will have
the new split_proc flag set to 1.  Then a comment section header would be
written out with the CM_SPLIT_PRC tag, a cm_len equal to the 
sizeof(struct split_info) and a cm_val pointing to the split_info
structure in the free-form data area of the comment section.
The split_info struct would have the location of the branch in
the split routine in .branch_addr, the current location in .vaddr,
and the size of the code moved in .size.  

All of this can be done by the producer itself, or with the proposed
mcs tool.

Consumer Tasks

In order to facilitate access of the split procedure data a number of API's
will be designed.  To handle the line number information, the current
access routines in libmld and libst will be updated, with the actual line
number info remaining unchanged for the purpose of this proposal.
The consumers can then decide for themselves how best to handle 
this information.


   API's  (very roughly designed)

New structures:

Note:  The following structures were based on some code I experimented
with while investigating using the code range descriptors to passively
collect the needed info without any modification to the symbol table.
This didn't pan out the way I expected, and I'm not certain that
using the pdsc_crd typedef will really be flexible enough to suit
the needs here.  But since something like it will be needed, I'm
setting this up this way just to illustrate how the structures would
look in general.

struct sp_range_list {
        pdsc_crd                crditem;
        struct split_info       *sp_info;
        long                    size;
        struct sp_range_list    *next;
};

struct sp_info {
        pPDR                    ppd;
        struct sp_range_list    *spr_head;
        struct sp_range_list    *spr_tail;
};

sp_init:

sp_info *
sp_init(pObj obj)

        Sp_init is called when dealing with an executable that has been
split.  It basically traverses the comment section, looking for split
procedure info.  It creates an array of structures ordered by procedure
descriptors.  Each structure has a linked list of code ranges that
cover the extent of the entire procedure, in order as they would be
encountered if the procedure were not split.


sp_size:

unsigned long 
sp_size(sp_info *spi, pPDR ppd)

        Sp_size returns the size of the input procedure by looking up
the proc in the sp_info structure, and adding up the individual 
pieces of the procedure.

sp_ranges:

sp_range *
sp_ranges(sp_info *spi, pPDR ppd)

        Sp_ranges returns a pointer to a linked list which comprise the 
various code ranges of a particular procedure, as indicated by the 
ppd input


    Line Numbers


While this would require a number of changes in a number of files,
the line number info is surprisingly tractable.  The line number
info does not rely on addresses, and is all represented as offset
information.  At discreet points these offsets are added to an 
existing address, starting with the entry address of a routine,
and then inspected to see if they compare with the desired address or
line number.  At these places in the code, we can add a check to 
see if we are in a split routine, and if so, compare the generated
address with the address we know contains a branch to the moved code.
At that point, we merely change the current generated address
with the address the code has been moved to.  We will know the 
size of this moved code, so when reach the end of that code, 
we change the address again, so that it is one instruction past
the branch to the moved code.  A brief psuedo-code example would
look something like this:

        branch_addr = spr_list->sp_info->branch_addr;
        new_addr = spr_list->sp_info->vaddr;
        final_addr = 0;

        code for manipulating the line number info, up to

        address = address + (count * 4);
        if (address == branch_addr && final_addr == 0) {
          address = new_addr;
          branch_addr++;   /* keep to make the switch back later */
          final_addr = new_addr + (size*4);
        }
        else if (final_addr && address == final_addr) {
          address = branch_addr;
          final_addr = 0;
          branch_addr = next branch address, if any, 0 otherwise;
        }

A preliminary search indicates that the following files will probably
need changing, and there may be more:

libmld/
        ldfcn.c
        ldlread.c
        line.c
        llib-lmld.c  
        obj.c
        obj_nlist.c
        obj_rewrite.c
        stcu.c
        stfd.c
        stio.c
        stprint.c

libst/
        st_line.c
        st_obj.c
        st_open.c

This was based by a grep search on the word 'pline' which is 
frequently used in line number translation.  It is quite possible
that some of these files use 'pline' in some other fashion, or 
are make reference to another variable with pline as part of a
name, and do not actually manipulate the line number info. 
44.4Comments, concerns and questions...GEMGRP::BRENDERRon BrenderTue Mar 11 1997 16:1060
Here are a few comments, concerns and questions regarding the proposal in 44.3.
I will be out next week and haven't had a lot of time this week to review the
proposal as carefully as I would like, so some of these may merely be oversights
or misunderstandings... In no particular order:

1) Is it possible to provide some more context about the environment in which
this proposal would be used? In particular, it seems that a tool like OM
is likely to do a lot more than just move routines or parts of routines
around. I am not confortable that I understand how the particular scheme
suggested here would play with other optimizations.

2) The proposal seems very specific in the split routine transformation that
it addresses, namely: removal of a chunck of code from within a routine that
is moved elsewhere, together with addition of exactly two BR instructions to
tie the parts together. Specific questions:

  - What if the moved section is at the beginning of the routine? Does the
    remaining code have to begin with a BR just to get to the moved code, or
    can the moved code retain it's role as "the entry".

  - What if the moved section is at the end of the routine. Does the moved
    code have to branch back to where it came from, or can it just RET (if
    that is all it needs to do)?

3) There seems to be an implicit assumption that moved code is completely
unchanged, other than being stitched together with BRs. Is that intended?
Is that acceptable? (Seems too inflexible to me.)

4) There seems to be an implicit assumption that exactly two one word BRs
are always added to connect moved code with its origin. Is that intended?
What if the moved code is, for example, one arm of a switch/case dispatch
so that no new instructions are needed (just update the dispatch table with
a new destination address) -- How does/can this proposal handle it?

5) I am unclear re whether moved code has to remain contigous with other code
"covered" by the same file descriptor rather than be freely moved to some
arbitarily remote part of the .text section.

6) It is unclear exactly what set of fields need to be updated to perform
the split transformation: For example, it appears that the adr field plus
setting of the new split flag are the *only* changes to the object file PDR,
right? But further changes appear to be needed to local STEs for stProc,
stBlock, stEnd, etc. I would be reassuring to have at a list of all such
anticipated changes required to be performed by the splitter. (I am not
concerned with fields that need to be updated -- like the size of the comment
section -- to maintain the coherence of the object file itself; just the
fields that describe the transformed code.)

7) Given that we are on the verge of introducing an optimization symbols
section, we should consider whether this proposal could/should use it rather
than the comment section.

8) I am unclear what the routine sp_size() is for? Where/why is the size of
the "input procedure" needed? [Is the "input procedure" pre-split or post-
split?]

9) The split_info structure that is kept in the comment section has two
addresses that refer to locations in the text section, and require relocation
is the image is re-positioned in the address space. Does that work?

44.5and they're off...QUARRY::petertrigidly defined areas of doubt and uncertaintyWed Mar 12 1997 12:0664
Ron,
   Valid questions all.  I've got some ideas, but not definite answers for
some of these.  

1) OM does many things.  I don't know enough about what or how it does
these things to give you a good answer, but I'll track them down.
I believe you can choose, through options, what om does, so that you could
choose just routine splitting, but that might not be the typical or 
most desired option.  Were I to design it, I would make the routine
splitting the last thing, so that things were rather settled by
the time you got to it, but that may be naive.  

2) The code to do routine splitting is in OM already.  I can check to 
see what happens with entry and exit points.  

3)  It would be nice if the code were unchanged.  Om is all post link
optimization, so the compiler has already worked on it.  We already know
of problems with line numbers in these areas, which is why it might make
sense to also look at the sematic events you've proposed.  Code might be
further coerced by OM but I'd like to see the data presented in
the sp_info represented as if splitting were the last thing done to it.
So that you could basically slap things back together into their
old places if you had to.

4)  I'm unclear about what you're unclear about.  How does adding a new 
destination branch qualify as splitting a routine?  Or are you suggesting that
the switch might have branched into the region that has now been relocated,
and we don't need to branch into what is now just a branch, but could
point to the new location instead?  But that new location is tracked by
dint of the sp_info in an address_to_procedure routine so I'm not sure
there's any conern.  It shouldn't matter if things are changed to directly
point there, we just need the origin info to tell us what procedure we
are actually in.
4a) What you don't ask here, but I was thinking about when I first read 
this, was that it may take more than one branch to get to the new
location, via trampoline code.  That should be easily enough handled
through steps and such in the debugger, since we shouldn't stop
UNTIL we're within the new location.

5) There is no restriction as to where the new location might be.  As hot 
blocks and routines bubble to the top they may or may not be contiguous
with their original code.

6)  I'd been thinking originally of stBlock stMoved stEnd combo's but 
it seemed somewhat awkward.  After all, we only represent the beginning
and end of routines, right?  We don't issue an stBlock stEnd pair for
each basic block within a routine.  That seemed more the focus of
the pdsc_cdr info, which may still be used.  Or maybe you have
some other ideas?

7)  The optimization section is a possibility.  

8)  The sp_size was more or less a node to the profiler folks, who are always
wondering (at least in their questions to me) about the size of a particular
block or routine.  It would be post split.

9)  Are you saying we should make these addrs offsets instead of absolute
addrs?  I've got nothing against that.  

Let me know if I've interpreted these correctly.  I'm going to have to take
some of the stuff to the OM guy (whom I haven't heard back from yet on the
copy I sent to him) to get a clearer idea.

PeterT
44.6OM guy checking in...QUARRY::nethCraig NethWed Mar 12 1997 16:0070
>1) Is it possible to provide some more context about the environment in which
>this proposal would be used?

I'm not sure what you're asking.  I can describe more about OM.

-split_procedures is a class of feedback optimization (i.e. it only happens 
when the user has requested that om reorder procedures according to profile 
data).   Other than that, there are no restrictions - it can be used in 
addition to other optimizations that om does (.lita removal, dead code 
removal, etc) or not.

>2) The proposal seems very specific in the split routine transformation that
>it addresses

Yes.  The current 'OM' algorithm is very flexible - it more or less sorts
the basic blocks in a 'hot' routine according to their usage and then 
determines the demarcation line between the 'hot' and 'cold' parts of the
routine.   The cold blocks all get moved somewhere 'near' the end of the
.text section.     So this means there can be 'many' branches between
the two sections.   For example, if the profile showed the routine had
the following characteristics:

	block1: hot
	block2: cold
	block3: hot
	block4: cold
	block5: hot
	block6: cold

Then the transformed routines would look like:

	hot			cold
	------			-----
	block1:			block2:
	block3:			block4:
	block5:			block6:
 
Further, om will only insert branches if they are needed - if there are
existing ones that can be retargeted they will get used.

>3) There seems to be an implicit assumption that moved code is completely
>unchanged, other than being stitched together with BRs.

That's _currently_ all that happens.  However, Robert Cohn has prototyped
a even more agressive form of this optimization that tries to move code
that supports the cold routine out of the hot blocks (this of register saves
in the prolog for external routine calls that only happen in the 'cold' blocks).

>6) It is unclear exactly what set of fields need to be updated to perform
>the split transformation

Yes, I had this concern too.   A list of things needing fixing would be
most appreciated by the om maintainer :-)

----------------------

My concern is that this design does not parallel the descriptiveness of
the code range descriptors/rpds.   The rpd/crd model allows a procedure to
be split into multiple pieces - as far as I know there is no limit of how
many pieces.    If a transformation can be described by the rpd/crds, then
I think it should be a requirement that we be able to describe it in the
symbol table too.

My (possibly naive) thinking was that the split pieces would all be described
in a manner similar to routines are currently, with the addition of some sort
of additional information that described which one was the 'parent' and
how to find it.



44.7Thanks Craig. Any other suggestions, concerns?QUARRY::petertrigidly defined areas of doubt and uncertaintyThu Mar 13 1997 16:4959
I don't have a problem using the crd/rpd's to help out here, but 
prior investigation showed some problems that I wasn't sure how to
address.  First there is the little matter of -non_shared code, which
does not contain any .pdata or .xdata sections.  Do we limit routine
splitting ONLY to shared images?  If so, how do we find which crd/rpds
fit in which procedure?  

I had an idea which I thought would work without even having to modify 
anything.  Cruise through the crd's and link up all the ones that
point to the same rpd.  And then go through the PDR's and checking the
start addresses, line up the PDR's with a certain crd that contained
that start address.  So I thought it would work out that I'd
have a many (crd's) to one (rpd) mapping, and then I'd do a 
one (PDR) to many (crd's) mapping.  So it would look something like


crd1list:  crd1;crd3;crd4;crd5...crdn-> rpd1
crd2list:  crd2;crd6;crd7...crdn+1->rpd2
crd3list:  ....->rpd3


and then I'd have

PDR1 ->crd1list
PDR2 ->crd2list
PDR3 ->crd3list

But, instead, I found it had

PDR1 ->crd1list
PDR2 ->crd1list
PDR3 ->crd1list
PDR4 ->crd2list
PDR5 ->crd3list

So the rpd's were getting reused for many different crd's which were
not necessarrily related.  I realized at that point that the calling
standard says that every basic block (or was it entry?)
needs a unique procedure descriptor, but that PD, not to be confused 
with the PDR, was a crd/rpd pair, rather than just a rpd, which could be 
reused.  At that point I sort of gave up on that approach and came up
with this, apparently too simple, scheme.  Any thoughts on how I reconcile
these two together?  This should be seen as a starting point, not the
be all and end all.  

What the debuggers need is a way to take a particular address and figure
out which procedure and scope they are in.
And they need to be able to have someone set a breakpoint on a 
particular line, and find the address that line is represented at.
Although this is notoriously problematical when things are optimized at
the moment anyway.

What the profilers and atom indicated they needed was to be able to 
find all the basic blocks for a particular routine.

This seems to be a step in the right direction, but I can see
we need to hammer this out a bit more to make it feasible.

PeterT
44.8not suggesting we use crdsQUARRY::nethCraig NethThu Mar 13 1997 20:519
I wasn't trying to suggest that we use the prd/crd stuff,
for the symbol table, just that there needs to be the same
descriptiveness in the symbol table as the exception system.

If you can describe a routine split into 255 chunks in the
crds, then you should be able to describe that same routine
in the symbol table...


44.9New Proposal, Line Number infoSMURF::PETERTrigidly defined areas of doubt and uncertaintyWed Apr 16 1997 02:07218
     	A Symbol Table Proposal to Extend Source Location Information
    
    	Authors: Ron Brender, Dennis Murphy, Peter Thompson
    	
    
    
                       Extended Source Location Information.
                       
       
    1.1 Introduction
    
    This document describes how Extended Source Location information
    is to be contained in the Third Eye Symbol table and is intended
    to serve as a specification as to how that information is
    constructed and accessed by the compilers and tools.
    
    Extended Source Location information is intended to provide more
    information than is contained in the existing line number table
    including PC deltas, line number deltas, file transitions,
    columns, and line/column ranges.
    
    It is also intended that the Extended Source Location information
    be a compact and extensible representation. Those issues will be
    addressed in the detailed description.
    
    While it was initially intended that Extended Source Location
    information be defined to allow ATOM to accurately describe "split
    routines" it seems likely that it will also be used be the
    compilers to describe "out-of-line" code where functions are
    segregated into hot and cold regions.
    
    The existing line number table is unable to describe either split
    routines or out-of-line code, resulting in difficult to debug
    code.
    
    This document makes use of a Tag/Length/Value (TLV)
    representation, a self-describing data format. This particular TLV
    representation is described in UNIX_OBJSYM note 33.3 and will not
    be explained further in this document.
    
    1.2 Basic rules
    
    o If a compilation is to build Extended Source Location information 
      that information is to be provided for the entire compilation. It is 
      NOT a pick-and-choose mechanism. 
    
    o Extended Source Location information is to be contained in the 
      Optimization Table.
    
    o Extended Source Location information will be produced in addition 
      to the existing line number table, at least initially. This is
    intended
      to provide backward compatibility with existing tools. Issues
      of object file size and compact line number representation are
      to be deferred.
    
    o Each procedure descriptor will describe its Optimization Table
      contribution via the pdr iopt element as detailed below.
    
    o File descriptor entries describing the Optimization Table are
      defined in a following section.
    
    1.3 Symbol Table Placement
    
    The Extended Source Location information will be located in the
    currently unused Optimization Table.
    
    Compilers will construct Extended Source Location information in
    the Optimization Table in addition to any other information to be
    placed in that table.
    
    All entries placed in the Optimization Table are in the form of
    self-describing TLV entries.
    
    The compilation of a module will produce an object file with a
    single Optimization Table that is constructed and accessed using
    the existing file and procedure mechanisms.
    
    The linker will concatenate the Optimization Tables of all modules
    contributing to an image. Note in particular that the linker need
    not parse or analyze the Optimization Table information in any
    way.  The resulting image Optimization Table will be accessed by
    post link tools such as OM and debuggers
    
    1.4 Form
    
    The Extended Source Location information is managed as a 
    Tag/Length/Value (TLV) element of the Optimization Table.
        
    The fdr copt element is a count of the number of bytes a file
    contributed to the Optimization table.
    
    The fdr ioptBase is used to locate the files first TLV entry.
    The files first TLV entry must be the same as the first TVL entry
    of the first procedure (via PD table) of the file.
    
    Each procedure descriptor iopt member will locate the Optimization
    Table contribution using the standard Third Eye rules for indexing
    via the procedure descriptor.
    
    The iopt value will indicate the first TLV entry for a given routine.
    
    Tools, including the debuggers, seeking to process the Extended Source 
    Location information will scan a procedure's Optimization table TVL
    elements until the Extended Source Location element is found or the
    procedure's terminal TVL entry is encountered.
    
    It is an error for a module containing Extended Source Location 
    information to omit producing an Extended Source Location TLV entry
    for any routine. However, the error is always nonfatal.    
    
    1.5 Issues
    
    The Tag value for Extended Source Location information will need to
    be specified.
        
    
    
    2.1 Detailed Extended Source Location Format
        
        
    The current line number information has a very compact and very
    restrictive format.  We propose some changes to the current format
    in order to gain some flexibility and allow for future expansion.
        
    The current format is basically a byte stream, with each byte
    being divided into two 4 bit halves.  The high ordered bits are
    used to indicate the line number delta, with a range of -7 to 7
    lines.  A bit string indicating -8, or '1000'b is used to indicate
    that the line number delta is too large, and is represented in the
    next 2 bytes.
        
    The low 4 bits of each byte, except in the case above, are used to
    represent an instruction delta, from 1 to 16 instructions (the
    range covers from 0 to 15, but you always add 1, as each pc delta
    represents at least one instruction).
        
        
    2.2 New Proposal
        
    In order to give us greater flexibilty in dealing with line
    numbers we want to separate the byte stream into two different
    types of formats, which we will call data mode and command mode.
    The basic format described above will now be known as data mode 1.
    Since we know the initial state of things from the procedure and
    file descriptors before we start looking at the line number info,
    it is intended that we will always presume to start in data mode
    1.  There will also be a data mode 2, which will be used to
    represent line, pc, and column information for programs with more
    than one statement on a line.  Data mode 0 is currently not
    defined, and would be seen as an error.
        
    The current 'escape' of '1000'b in the line delta information
    would now become a escape to the command mode.
        
    Once an escape is encountered, the line number algorithm will
    switch to interpreting command mode, and subsequent bytes will be
    treated as commands, paired with LEB128 data.  LEB(Little Endian
    Byte)128 is a compact way of expressing integer data and does not
    need to be used only on little endian machines.  Each command will
    be 1 byte in length, with the 2 high order bits as flags for mark
    and resume, respectively.  The mark bit will be used to indicate
    the last command in a sequence that takes us to the next line. The
    resume flag would be an indication to return to the previous data
    mode.  The low 6 bits then, will be used to distinguish the
    various commands.
        
    The LEB128 is a byte stream way of encoding data taking advantage
    that most of the integers will be fairly small.  Integers are
    split into 7 bit segments, '1111111'b, and the segments are put
    into the 7 lower bits of a byte. The high order eighth bit is used
    as a flag to indicate whether there are any more bytes in the
    number. A 1 indicates a continuation and a 0 indicates completion.
    Large numbers would thus be formed by concatenating together the 7
    bit segments.
        
    2.3 Commands
        
    We've identified a small group of commands that we intend to start
    with.  These may grow as new needs are assessed in the future.
    The commands, with an explanation of how the LEB data should be
    interpreted follow below.  No particular bit pattern has been
    established yet for each command.
        
    add_pc, LEB 
    	        signed delta, Value is added to current pc 'value' (as
        	determined by previous line number machinations) to 	
        	end up with a pc value that is greater or less than the
        	current value.
        
    add_line, LEB	
    	        signed delta, added to current line number
        
    set_col, LEB	
        	absolute value, used to associate pc with a particular
        	section of a line, rather than just the beginning of 
        	a line.
        
    set_file, LEB	
        	absolute value, used to set the current information to
        	a particular source file.  Needed to handle include 
        	files, which we have no way of currently representing 
        	in the present symbol table.
        
    set_data_mode, LEB
        	absolute value.  Only a 1 or a 2 would be acceptable 
        	here, for data mode 1 or data mode 2.  Anything
        	else would be an error.
        
    add_pc_line, LEB, LEB	
        	Sort of a larger, more flexible representation of the
        	current delta/pc pair.  Both signed deltas.
        
    add_pc_line_col, LEB, LEB, LEB
        	To handle pc, line and column data all at once.  
        	The first 2 are signed delta's.  The third is an
        	absolute value.
    
44.9Extended source info proposalSMURF::LOWELLWed Apr 16 1997 11:143
I've moved the extended source info proposal, previously posted here,
to its own basenote (68.0) since it stands on its own and may be used 
to solve more issues than split routine representation.