jqj@bullwinkle (12/30/85)
From: jqj@bullwinkle (J Q Johnson) One of the design goals of C was to produce a language with very simple and efficient variable allocation. The result, with no nested procedures and only local and global variables (no uplevel references, no heap variables though you do have heap storage, no variable-dimensioned arrays) produces a language that can be implemented without a frame pointer. This is a desirable characteristic of the language, and implies that you should not expect all or even the typical implementation to use a frame pointer. Unfortunately, since the VAX CALLS/RET discipline uses a frame pointer, the alloca() routine exists. And since it exists on the VAX, it is used heavily by GNU emacs. However, its use conflicts with one of what I take to be GNU emacs's design goals -- portability. I would like to improve that portability if possible. My particular domain is porting GNU emacs to a Gould PN. I see several alternatives, and would like advice on which to pursue. Note that emacs uses alloca() in a very stylized way -- almost entirely to provide dynamically dimensioned arrays. One never sees code like: foo() { ... while (baz) { p->next = alloca(sizeof(p)+n); p = p->next; ... So alloca() would not have been necessary at all in an Algol-like language. My alternatives seem to be: 1/ implement a full alloca() for the Gould. This would not be hard but would be a gross assembler hack -- alloca()ed variables would actually be malloc()ed, but alloca() would push the ptr on a private stack and fudge the caller's return to free() then pop the private stack. setjmp{} would be extended to mark the private stack, and longjmp() would do a number of free()s. The only visible change to existing code would be redefining jmp.buf by changing the #include to reference a private setjmp.h. However, as I remarked above this would be a gross assembler hack, and nonportable. Also, it would be impossible to preserve the alloca() semantics during interrupts, and would limit you to a (small) fixed number of outstanding alloca()s based on the size of the private stack. 2/ recode entirely in C to eliminate the need for alloca() as such. This could be done in several ways. Perhaps the simplest would be to have alloca() allocate on the heap, saving the pointer on a private stack, and in alloca()'s caller record the private stack top in a local variable. Add a "mark()" routine at the beginning of each function that uses alloca() and a "release()" routine at each return: old: new: foo() { ... foo() { ... char *name=alloca(xxx); int alloclim = mymark(); ... char *name=myalloca(xxx); return baz; ... ... (myrelease(alloclim), return baz); } ... myrelease(alloclim); } Similarly, setjmp would be protected with a mark/release: old: new: if (setjmp(buf)) int alloclim; ... foo; if (alloclim = mymark(), setjmp(buf)) (myrelease(alloclim), foo); This scheme has the disadvantage of running slower if a true alloca() exists (unless all the code is conditionalized, which would be a maintenance headache). It also makes the code less clear, again increasing maintenance. But it has the advantage of being completely portable. 3/ Recode GNU emacs to do compile-time maximum-size array allocations instead of runtime bounds. This is typical C style but could increase (dramatically!) the size of the stack. Except in a few places where reasonable bounds are known in advance (e.g. dispnew.c), I think this would be a bad idea. 4/ Hack up the Gould C compiler to use a stack frame. And it's still non-portable. 5/ Do something else entirely. (e.g. use Hemlock?) 1/, 2/, or 3/ would each take, I estimate, less than a person-week to program and debug. 4/ would take several person-months for the average hacker.
gwyn@BRL.ARPA (12/30/85)
From: Doug Gwyn (VLD/VMB) <gwyn@BRL.ARPA> Alloca() is definitely not portable, and Dennis Ritchie even tried to suppress it altogether in 7th Edition UNIX (obviously not fully successfully). Except for the action of longjmp(), alloca() does nothing that malloc()/free() would not do. I don't understand why GNU EMACS wants to use setjmp() and alloca() so heavily; in the over 500,000 lines of source code that I maintain, there is not a single use of alloca(), and only a few places where it could have been used effectively if available.
macrakis@harvard.UUCP (Stavros Macrakis) (12/31/85)
In article <21@cornell.UUCP>, jqj@bullwinkle writes: > the alloca() routine ... is used heavily by GNU emacs.... > My alternatives seem to be: > 1/ implement a full alloca() for the Gould. ... > 2/ recode entirely in C to eliminate the need for alloca()... > 3/ Recode GNU emacs to do compile-time maximum-size array allocations... > 4/ Hack up the Gould C compiler to use a stack frame.... > 5/ Do something else entirely. (e.g. use Hemlock?) .... > 1/, 2/, or 3/ would each take, I estimate, less than a person-week to > program and debug. 4/ would take several person-months.... Why not the very simplest solution to alloca: a parallel stack for alloca'd objects with Mark/Release? This stack would have to be as large as the total size of allocas alloc'able at one time. In a virtual memory system with large address space, there should be no problem at all. Part of the charm of a Mark/Release scheme (as of regular alloca) is that it is robust: if a Release is `forgotten' via some coding or timing error, the stack still gets cleaned up eventually. #define Max_Total_Alloca 10000 char linear_heap[Max_Total_Alloca]; int linear_heap_pointer = Max_Total_Alloca; Every routine that used alloca would have to include the Mark macro among its declarations (of course, it doesn't hurt to have a Mark even if there is no alloca within the routine): #define Mark char *Mark_point = linear_heap_pointer; ...and would release just before returning: #define Release linear_heap_pointer = Mark_point; ...for convenience: #define Return(x) {Release; return(x);} Setjmp is slighly more complicated. Ideally, the setjmp environment should include the linear_heap_pointer. If this cannot be accomplished, then every statement that has a setjmp within it must become: { Mark; ... setjmp ... Release; } Ideally, Setjmp would just be #define Setjmp (Mark, setjmp(x) + Release) but this won't work for two reasons: 1. Mark declares a variable, and there can be no declarations within C statements; 2. the order of evaluation of f()+g() is undefined, and in particular on the Vax is the wrong way round. The function alloca itself is very simple: #define alloca(x) (Mark_point, _alloca(x)) /* Defined like this to give an error if Mark has not been used. */ char *_alloca(size) int size; { if ((linear_heap_pointer -= size) < 0) ...error...; return(&(linear_heap[linear_heap_pointer])); } Note that as a standard precaution the reset-world function should reset the linear_heap_pointer. So should the top-level loop. Since there are only 46 alloca's and 6 setjmp's in all of gnumacs (and several functions have several allocas), I estimate less than one man-day to install this type of alloca. Since I'm working on my thesis, I am not volunteering (not to mention that my machine has alloca!). Note that there is no need to conditionalize the Mark/Return's in implementations with regular alloca's, since you can just define them to be null: #define Mark On machines with regular alloca, the alloca macro as above can be preserved to provide a compile-time error indication in case of forgetting to use Mark. Of course, this doesn't guarantee that Release is used consistently. -s
shaddock@rti-sel.UUCP (Mike Shaddock) (01/01/86)
In article <21@cornell.UUCP> jqj@bullwinkle writes: >From: jqj@bullwinkle (J Q Johnson) > > ... Here jqj talks about the initial design goals of C, the rise of > alloca(), and the problems of porting GNU Emacs to a Gould PN > machine. I too am trying to port GNU Emacs 16.60 to a Gould machine, and have run into some of the problems mentioned here. I have stopped working on it for the time being, but have decided that several unfortunate design decisions were made: (1) Non-portable use of alloca. As pointed out, alloca will not work on machines without stack and frame pointers. When I pointed this out on the GNU Emacs mailing list, the response was that alloca "was used because it was good for GNU, that GNU would have alloca, and that the author did not intend support machines that were deficient in important features such as not having stack and frame pointers". According to some people very familiar with Gould machines, implementing a full alloca would be very, very hard, and certainly not worth the time for one program. (2) Use of setjmp/longjmp. GNU Emacs uses setjmp/longjmp in several places, when it would have been more portable (and more correct in a pure theoretical sense) to return error codes, etc. instead of just jumping to some other place in the program. (3) Implementation of Lisp_Objects Why wasn't a simple structure, such as typedef struct { char *multiple_things; char type; } Lisp_Object; or /* * No flames if this is slightly incorrect, I * avoid using unions and am not up on the syntax */ typedef struct { union { /* Put all the different things */ /* a Lisp_Object can be here */ } foo; char type; } Lisp_Object; used instead this rather gross way of hacking on an int? What would happen if a pointer needed more than the 24 bits that it gets in the int? This may be handled correctly (I couldn't determine that from the code), but it would certainly be a little less obscure to use a structure. Some places in the code seem to *depend* on Lisp_Objects being ints, so it is non-trivial to change this for a particular machine. (4) Unexec Use of unexec is a gross hack merely for a little efficiency. Given sufficient time I could probably re-write enough of GNU Emacs to fix these problems, but I have neither the time nor the inclination to do so. Some of these complaints may be answered in the newest version of GNU Emacs, but I have a copy of 17.31 and it doesn't look like it. GNU Emacs seems to be a very good program from the user's point of view, and could increase people's productivity, but I'm afraid that the attitude of some of the people involved with GNU may be its downfall. Portability is an important issue, and unless GNU, including GNU Emacs, is highly portable, I'm afraid that it won't be a successful as it should. -- Mike Shaddock {decvax,seismo,ihnp4}!mcnc!rti-sel!shaddock "You're in a twisty maze of sendmail rules, all obscure."
daveb@shrew.UUCP (Dave Brower) (01/05/86)
[ I expect to be corrected ] GNU is not, as far as I can see, intended to be really portable to things like BSD, SV, SV.2, etc., on different machines. Since GNU is intended to be a complete *NIX replacement, rms and the team of hackers can feel free to assume the presence of alloca() on every machine in the universe. They are, after all, designing the universe. It just happens that GNU emacs is available before the rest of the system, and if you can use it, great. I've been contemplating the conditional/macro apprach to alloca on different machines, since you really do want to use it on a machine that will support it. -dB
marick@ccvaxa.UUCP (01/06/86)
Setjmp/longjmp work just fine on Gould machines; only alloca() is missing. I haven't looked at unexec in gnumacs, but I've written a rather large program that does something similar for much the same reason. It might be nice for unexec to be a library routine, but in my experience those people who use it all need to do something slightly different -- you need an example, not a library routine. I can't provide a full example, but I can tell you how to write an unexec-like thing for ZMAGIC files: 1. Find the old executable -- you'll need it for the symbol table. The Franz Lisp gstab() routine is an example of what you need to do. 2. Unlink the destination file (so creat() doesn't reuse the modes). 3. Fetch the old header (out of the old executable or starting at the first word of this process's text space). 4. Fill in the new header -- note that both header.a_data and header.a_nbdata must be updated if you've used sbrk(). 5. Write the new header out. Its size in bytes is ZTXTOFF (from a.out.h). 6. Write the text out, starting at header.a_txbase + ZTXTOFF and going for header.a_text-ZTXTOFF bytes. 7. Write the data out. header.a_text is rounded up to a multiple of pages, so you don't have to. 8. Copy the symbol and symbol tables from the old executable to the new one. The symbol table starts at a_text + a_data in the old executable. The only problem I know of is a bug in signal handling. In (at least some distributions of) UTX, sigvec breaks when called "again" from a saved process image. See sigvec.s -- you will need to do a setsigc system call to get around the problem. This bug will be (has been?) fixed in later versions of UTX. Hope this helps someone. Usual disclaimers about opinions apply; further, I've been known to make mistakes both writing and reading code, so anything I wrote above could be wrong. Brian Marick, Wombat Consort Gould Computer Systems -- Urbana ...ihnp4!uiucdcs!ccvaxa!marick ARPA: Marick@GSWD-VMS
sjm@dayton.UUCP (Steven J. McDowall) (01/15/86)
Just a simple question, as a new member of the net, where can I get GNU emacs? Also, how is the GNU project coming along, and where can I find updated information on GNU? -- Steven J. McDowall Dayton-Hudson Dept. Store. Co. UUCP: ihnp4!rosevax!dayton!sjm 700 on the Mall ATT: 1 612 375 2816 Mpls, Mn. 55408