⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 porttour1

📁 unix v7是最后一个广泛发布的研究型UNIX版本
💻
📖 第 1 页 / 共 4 页
字号:
but a singular lack of grace..PPWhen a name is read in the input, it is hashed, and theroutine.I lookupis called, together with a flag which tells which symbol tableshould be searched (actually, both symbol tables are stored in one, and a flagis used to distinguish individual entries).If the name is found,.I lookupreturns the index to the entry found; otherwise, it makesa new entry, marks it UNDEF (undefined), and returns theindex of the new entry.This index is stored in the.I rvalfield of a NAME node..PPWhen a declaration is being parsed, this NAME node ismade part of a tree with UNARY MUL nodes for each *,LB nodes for each array descriptor (the right descendanthas the dimension), and UNARY CALL nodes for each functiondescriptor.This tree is passed to the routine.I tymerge ,along with the attribute type of the whole declaration;this routine collapses the tree to a single node, by calling.I tyreduce ,and then modifies the type to reflect the overalltype of the declaration..PPDimension and size information is stored in a table called.I dimtab .To properly describe a type in C, one needs not just thetype information but also size information (for structures andenums) and dimension information (for arrays).Sizes and offsets are dealt with in the compiler bygiving the associated indices into.I dimtab ..I Tymergeand.I tyreducecall.I dstashto put the discovered dimensions away into the.I dimtabarray..I Tymergereturns a pointer to a single node that containsthe symbol table index in its.I rvalfield, and the size and dimension indices infields.I csizand.I cdim ,respectively.This information is properly considered part of the type in the first pass,and is carried around at all times..PPTo enter an element into the symbol table, the routine.I defidis called; it is handed a storage class, and a pointerto the node produced by.I tymerge ..I Defidcalls.I fixtype ,which adjusts and checks the given type depending on the storageclass, and converts null types appropriately.It then calls.I fixclass ,which does a similar job for the storage class;it is here, for example, thatregister declarations are either allowed or changedto auto..PPThe new declaration is now compared against an older one,if present, and several pages of validity checks performed.If the definitions are compatible, with possibly some added information,the processing is straightforward.If the definitions differ, the block levels of thecurrent and the old declaration are compared.The current block level is kept in.I blevel ,an external variable; the old declaration level is kept in the symbol table.Block level 0 is for external declarations, 1 is for arguments to functions,and 2 and above are blocks within a function.If the current block level is the same as the old declaration, an errorresults.If the current block level is higher, the new declaration overrides the old.This is done by marking the old symbol table entry ``hidden'', and makinga new entry, marked ``hiding''..I Lookupwill skip over hidden entries.When a block is left, the symbol table is searched,and any entries defined in that block are destroyed; ifthey hid other entries, the old entries are ``unhidden''..PPThis nice block structure is warped a bit because labelsdo not follow the block structure rules (one can do a.B gotointoa block, for example);default definitions of functions in inner blocks also persist clear out to the outermost scope.This implies that cleaning up the symbol table after block exit is moresubtle than it might first seem..PPFor successful new definitions,.I defidalso initializes a ``general purpose'' field,.I offset ,in the symbol table.It contains the stack offset for automatics and parameters, the register numberfor register variables, the bit offset into the structure forstructure members, andthe internal label number for static variables and labels.The offset field is set by.I fallocfor bit fields, and.I dclstructfor structures and unions..PPThe symbol table entry itself thus containsthe name, type word, size and dimension offsets,offset value, and declaration block level.It also has a field of flags, describing what symbol table thename is in, and whether the entry is hidden, or hides another.Finally, a field gives the line number of the last use,or of the definition, of the name.This is used mainly for diagnostics, but is useful to.I lintas well..PPIn some special cases, there is more than the above amount of information keptfor the use of the compiler.This is especially true with structures; for use in initialization,structure declarations must have access to a list of the members of thestructure.This list is also kept in.I dimtab .Because a structure can be mentioned long before themembers are known, it is necessary to have anotherlevel of indirection in the table.The two words following the.I csizentry in.I dimtabare used to hold the alignment of the structure, andthe index in dimtab of the list of members.This list contains the symbol table indices for the structure members, terminated by a\-1..SHTree Building.PPThe portable compiler transforms expressionsinto expression trees.As the parser recognizes each rule making up anexpression,it calls.I buildtreewhich is given an operator number, and pointers to theleft and right descendants..I Buildtreefirst examines the left and right descendants,and, if they are both constants, and the operatoris appropriate, simply does the constantcomputation at compile time, and returnsthe result as a constant.Otherwise,.I buildtreeallocates a node for the head of the tree,attaches the descendants to it, and ensures thatconversion operators are generated if needed, and thatthe type of the new node is consistent with the typesof the operands.There is also a considerable amount of semantic complexity here;many combinations of types are illegal, and the portablecompiler makes a strong effort to checkthe legality of expression types completely.This is done both for.I lintpurposes, and to prevent such semantic errorsfrom being passed through to the code generator..PPThe heart of.I buildtreeis a large table, accessed by the routine.I opact .This routine maps the types of the left and rightoperands into a rather smaller set of descriptors, and thenaccesses a table (actually encoded in a switch statement) whichfor each operator and pair of types causesan action to be returned.The actions are logical or's of a number ofseparate actions, which may becarried out by.I buildtree .These component actions may includechecking the left side to ensure that itis an lvalue (can be stored into),applying a type conversion to the left or right operand,setting the type of the new node tothe type of the left or right operand, calling variousroutines to balance the types of the left and right operands,and suppressing the ordinary conversionof arrays and function operands to pointers.An important operation is OTHER, which causessome special code to be invokedin.I buildtree ,to handle issues which are unique to a particular operator.Examples of this arestructure and union reference(actually handled bythe routine.I stref ),the building of NAME, ICON, STRING and FCON (floating point constant) nodes,unary * and &, structure assignment, and calls.In the case of unary * and &,.I buildtreewill cancel a * applied to a tree, the top node of whichis &, and conversely..PPAnother special operation isPUN; thiscauses the compiler to check for type mismatches,such as intermixing pointers and integers..PPThe treatment of conversion operators is still a ratherstrange area of the compiler (and of C!).The recent introduction of type casts has only confounded thissituation.Most of the conversion operators are generated bycalls to.I tymatchand.I ptmatch ,both of which are given a tree, and asked to make theoperands agree in type..I Ptmatchtreats the case where one of the operands is a pointer;.I tymatchtreats all other cases.Where these routines have decided on theproper type for an operand, they call.I makety ,which is handed a tree, and a type word, dimension offset, and size offset.If necessary, it inserts a conversion operation to make thetypes correct.Conversion operations are never inserted on the left side ofassignment operators, however.There are two conversion operators used;PCONV, if the conversion is to a non-basic type (usually a pointer),andSCONV, if the conversion is to a basic type (scalar)..PPTo allow for maximum flexibility, every node produced by.I buildtreeis given to a machine dependent routine,.I clocal ,immediately after it is produced.This is to allow more or less immediate rewriting of thosenodes which must be adapted for the local machine.The conversion operations are given to.I clocalas well; on most machines, many of theseconversions do nothing, and should be thrown away(being careful to retain the type).If this operation is done too early,however,later calls to.I buildtreemay get confused about correct type of thesubtrees;thus.I clocalis given the conversion ops only after the entire tree is built.This topic will be dealt with in more detail later..SHInitialization.PPInitialization is one of the messier areas in the portable compiler.The only consolation is that most of the mess takes placein the machine independent part, where itis may be safely ignored by the implementor of thecompiler for a particular machine..PPThe basic problem is that the semantics of initializationreally calls for a co-routine structure; one collectionof programs reading constants from the input stream, while another,independent set of programs places these constants into theappropriate spots in memory.The dramatic differences in the local assemblers alsocome to the fore here.The parsing problems are dealt with by keeping a ratherextensive stack containing the currentstate of the initialization; the assemblerproblems are dealt with by having a fair number of machine dependent routines..PPThe stack contains the symbol table number,type, dimension index, and size index for the current identifierbeing initialized.Another entry has the offset, in bits, of the beginningof the current identifier.Another entry keeps track of how many elements have been seen,if the current identifier is an array.Still another entry keeps track of the currentmember of a structure being initialized.Finally, there is an entry containing flagswhich keep track of the current state of the initialization process(e.g., tell if a } has been seen for the current identifier.).PPWhen an initialization begins, the routine.I beginitis called; it handles the alignment restrictions, ifany, and calls.I instkto create the stack entry.This is done by first making an entry on the top of the stack for the itembeing initialized.If the top entry is an array, another entry is made on the stackfor the first element.If the top entry is a structure, another entry is made on thestack for the first member of the structure.This continues until the top element of the stack is a scalar..I Instkthen returns, and the parser begins collecting initializers..PPWhen a constant is obtained, the routine.I doinitis called; it examines the stack, and does whateveris necessary to assign the current constant to thescalar on the top of the stack..I gotscalis then called, which rearranges the stack so that thenext scalar to be initialized gets placed on top of the stack.This process continues until the end of the initializers;.I endinitcleans up.If a { or } is encountered in thestring of initializers, it is handled by calling.I ilbraceor.I irbrace ,respectively..PPA central issue is the treatment of the ``holes'' thatarise as a result of alignment restrictions or explicitrequests for holes in bit fields.There is a global variable,.I inoff ,which contains the current offset in the initialization(all offsets in the first pass of the compiler are in bits)..I Doinitfigures out from the top entry on the stack the expectedbit offset of the next identifier; it calls themachine dependentroutine.I inforcewhich, in a machine dependent way, forcesthe assembler toset aside space if need be so that thenext scalar seen will go into the appropriatebit offset position.The scalar itself is passed to one of the machine dependentroutines.I fincode (for floating point initialization),.I incode(for fields, and other initializations less than an int insize),and.I cinit(for all other initializations).The size is passed to all these routines, and it is up tothe machine dependent routines to ensure that the

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -