[net.lang.c] Pointers to freshly minted functions

throopw@dg_rtp.UUCP (03/07/86)

> Some machines won't let you do what you want at all, in fact,
> because on them, code is code and data is data and never the twain shall
> meet.  (Examples:  a pdp11 running split-space; a segmented machine that
> makes a distinction between code and data segments.)

Taking "what you want" to be "creating an instruction stream, and then
executing it", I think that what Henry says here is mildly misleading
(though, not surprisingly, correct).  Consider a C function which
creates an instruction stream, and returns a pointer to a function which
when invoked will execute this instruction stream.  Here is a definition
and an invocation of such a function (and an invocation of the function
it returns a pointer to, for good measure).

    { void (*(make_callable_istream()))(); ...
        (*(make_callable_istream( &proto_istream )))();
        ...
    }

It must *always* be possible to implement this function, otherwise
compilers would not be possible, since compilers are simply programs
that treat code as data (though sometimes an implementation of the above
function will have to start one or more new processes...) So, a machine
where it is strictly true that "code is code and data is data and never
the twain shall meet" makes compilers impossible.  A more accurate
phrasing of the restriction that Henry was thinking of might be "within
a single process, code is code and data is data and never the twain
shall meet".

Now it may well be that the implementation of this function is so
expensive to execute that it is impractical for some given application.
This seems to be the case for the original query, which seemed to be
about how to shave a few microseconds off of a "case" or "jump-table"
construct.  So it goes.

> Henry Spencer {allegra,ihnp4,linus,decvax}!utzoo!henry
--
(So I like to pick nits.  What can I say?)
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!dg_rtp!throopw

roy@phri.UUCP (Roy Smith) (03/08/86)

In article <204@dg_rtp.UUCP> throopw@dg_rtp.UUCP writes:
[In an ongoing discussion about self-modifying code]

> [on certain machines, like a pdp-11 with I&D spaces] code is code and
> data is data and never the twain shall meet.  [...]  Consider a C
> function which creates an instruction stream, and returns a pointer to a
> function which when invoked will execute this instruction stream. [...]
> It must *always* be possible to implement this function, otherwise
> compilers would not be possible, since compilers are simply programs that
> treat code as data (though sometimes an implementation of the above
> function will have to start one or more new processes...)

	The Buroughs B5700 had (in addition to the strangest subroutine
linkage I've ever seen) a tagged architecture.  Each memory word had a
(3-bit?) tag which defined the value stored there as integer, real,
pointer, instruction, etc.  This tag was not directly accessible by a
programmer which made it kind of hard to implement a compiler.  Presumably
(I never actually used a B5700) there was some magic way the OS used to
convert data into code, but I never ran accross any reference to it.

	One of the other oddities about the 5700 was a lack of conventional
assembler -- you did systems programming in an Algol derivitive.
-- 
Roy Smith, {allegra,philabs}!phri!roy
System Administrator, Public Health Research Institute
455 First Avenue, New York, NY 10016

gwyn@BRL.ARPA (03/09/86)

The B5700 and other early Burroughs mainframes indeed had stack frames
(delimited by a Mark Stack Control Word), tagged data types, and ESPOL
(roughly C-level systems programming language).  They also had virtual
memory, segment linkers, and other design features that were ahead of
their time.  But what does all this have to do with C?

andrew@aimmi.UUCP (Andrew Stewart) (03/15/86)

In article <2277@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>In article <204@dg_rtp.UUCP> throopw@dg_rtp.UUCP writes:
>[In an ongoing discussion about self-modifying code]
>
>	The Buroughs B5700 had (in addition to the strangest subroutine
>linkage I've ever seen) a tagged architecture.  Each memory word had a
>(3-bit?) tag which defined the value stored there as integer, real,
>pointer, instruction, etc.  This tag was not directly accessible by a
>programmer which made it kind of hard to implement a compiler.  Presumably
>(I never actually used a B5700) there was some magic way the OS used to
>convert data into code, but I never ran accross any reference to it.

There was a simple trick - the tag bits were not held on disk, only
in memory. (I'm not sure if they even existed when a segment was paged
out); the compiler generated a file whose format was defined and
understood by the loading procedures.
When a program was run, the MCP (Burrough's name for the OS) created a new
stack segment for the run-time info which was linked back to the system
stack; it then  built the segment dictionary for the code segments and the data
segments, loaded the segments and lit the blue touch paper.
Only the system could change tag bits, and not even the system could change
certain tag bits once they were created. You had to destroy the segment and
reallocate it.
It also did memory management ('buddy' method, I think) in microcode.
Nice machine. Bit sluggish in a hundreds-of-little-jobs environment, though.
Have a look at Elliot Organick's book on the B6700 - it's quite a machine.
-- 
-------------------------------------------
Andrew Stewart		 USENET:   ...!mcvax!ukc!aimmi!andrew

"My axioms just fell into a Klein bottle"

dave@inset.UUCP (Dave Lukes) (03/18/86)

In article <2277@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>In article <204@dg_rtp.UUCP> throopw@dg_rtp.UUCP writes:
>[In an ongoing discussion about self-modifying code]
>> Consider a C
>> function which creates an instruction stream, and returns a pointer to a
>> function which when invoked will execute this instruction stream. [...]
>> It must *always* be possible to implement this function, otherwise
>> compilers would not be possible, since compilers are simply programs that
>> treat code as data ...

WHAAAATTT???
Uh-uh: you mean LOAD-AND-GO compilers aren't possible:
most compilers generate a file of code which is read into the I space by the OS.
			  ^^^^
>	The Buroughs B5700 had (in addition to the strangest subroutine
>linkage I've ever seen) a tagged architecture.  Each memory word had a
>(3-bit?) tag which defined the value stored there as integer, real,
>pointer, instruction, etc.  This tag was not directly accessible by a
					  ^^^ ^^^^^^^^ ^^^^^^^^^^ ^^ ^
>programmer which made it kind of hard to implement a compiler.  Presumably
 ^^^^^^^^^^

Wrong: there is/was (at least on the B6700) a ``set tag'' instruction
(I don't rememeber what it was called) which overwrote the tags in the
operand.

Anyways: there are plenty other reasons why implementing a compiler on
those beasts is strange: the major one being the total lack of protection.

>(I never actually used a B5700) there was some magic way the OS used to
>convert data into code, but I never ran accross any reference to it.

Again (see above), the code was generated in a file usually, then run by the OS.
>	One of the other oddities about the 5700 was a lack of conventional
>assembler -- you did systems programming in an Algol derivitive.

Sure, it was an Algol derivative (ESPOL: Executive Systems PrOgraming
Language it was called), but totally machine oriented.

BTW: tou HAD to program in a high level language: assembler was forbidden
due to the lack of protection (which was all provided by the compilers ...)
>-- 
>Roy Smith, {allegra,philabs}!phri!roy
>System Administrator, Public Health Research Institute
>455 First Avenue, New York, NY 10016

P.S. BTW: Burroughs called their OS MCP (Master Contol Program) long before
Disney did TRON. Does anyone know if Burroughs sued?
-- 
		Dave Lukes. (...!inset!dave)

All opinions, philosophies, dogmas and idiosyncrasies expressed in this article
INCLUDING THIS DISCLAIMER, are solely those of the author.

rose@think.ARPA (John Rose) (04/11/86)

I have posted to net.sources a small utility which allows creation
of new functions, called ``closures''.  I've already posted this
code, at the end of a lengthy article on closures in Lisp and C,
but I suspect that few people made it to the end of that article.
So, since the topic is still alive, and for the sake of concreteness,
you can look at the code.  Here's an example of what it can do:

typedef double (*PFD)();
extern PFD make_myexp(/* double base */);
/* make_myexp is implemented with malloc, _init_closure,
 * and a `base function' which calls exp. */

PFD Exp, Exp10, Exp2;

/* Each call mallocs a new function object: */
Exp = make_myexp(2.71828183);
Exp10 = make_myexp(10.0);
Exp2 = make_myexp(2.0);

/* Now, pow(x,10.0) == (*Exp10)(x) == Exp10(x) */
-- 
----------------------------------------------------------
John R. Rose		     Thinking Machines Corporation
245 First St., Cambridge, MA  02142    (617) 876-1111 X270
rose@think.arpa				  ihnp4!think!rose