[comp.compilers] Squashing C Source

htf@sari.edinburgh.ac.uk (H T Fallside) (12/06/90)

I'm after a preprocessor that will do in-line expansion of procedure
calls IN SOURCE for C to produce one lovely long main() procedure, 
something capable of dealing intelligently with parameters and local
variables. Anyone out there got any ideas before I start writing ?

hamish
---------------------
htf@uk.ac.ed.castle
[GCC has an "inline" keyword which expands procedures in line, but I haven't
seen any source-to-source converters.  What do you plan to do about
directly or indirectly recursive routines? -John]
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

markhall@pyrps5.pyramid.com (Mark Hall) (12/12/90)

In article <10767.9012051639@subnode.sari.ed.ac.uk> H T Fallside <htf@sari.edinburgh.ac.uk> writes:
>I'm after a preprocessor that will do in-line expansion of procedure
>calls IN SOURCE for C to produce one lovely long main() procedure, 
>something capable of dealing intelligently with parameters and local
>variables. Anyone out there got any ideas before I start writing ?

Since the one true answer has yet to be posted, allow me:

	A Study of a C Function Inliner
	Jack W. Davidson and Anne M. Holler
	University of Virginia

Heaven knows where I got this paper, probably heard they were working
on it through the grapevine.

I got a copy of the INLINER program from them for a small fee and 
one UNIX(tm) proof-of-purchase seal.

You can get a copy by contacting Jack Davidson using either of:

	jwd@virginia.edu
	uunet!virginia!jwd
	(804)924-7605

Two years ago, when I first used it, it did a fine job.  Since then
Anne Holler has been improving it.  By now I'm sure it'll
be more than adequate for you needs.

-Mark Hall (smart mailer): markhall@pyrps5.pyramid.com
	   (uucp paths): {ames|decwrl|sun|seismo}!pyramid!markhall
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

djones@decwrl.dec.com (Dave Jones) (12/14/90)

> I'm after a preprocessor that will do in-line expansion of procedure
> calls IN SOURCE for C to produce one lovely long main() procedure, 
> something capable of dealing intelligently with parameters and local
> variables. Anyone out there got any ideas before I start writing ?

You might look for inspiration in "cfront" or some other C++ to C compiler
that does in-line procedure expansion. Look at the C code that it produces
for in-line calls and you'll get the flavor of it.

Now then, please tell us why in the world you would want to make an entire
program into one procedure. That will certainly make the resulting object
code slower. For one thing, on most machines, the procedure call/return is
an efficient way to save and restore registers. For another, unless the
program is a rather simple one with most procedures being called from only
one place, the program will be larger. On machines that do paging, larger
usually means slower, sometimes much slower.

If you are trying to get around some system limitation, tell us what it is
and maybe someone will come up with an alterative method.

I will leave you with one last word: "recursion". You will have to
reinvent procedure calls to handle recursion.
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

pardo@cs.washington.edu (David Keppel) (12/17/90)

>>[I want to create one huge main()]

megatest!djones@decwrl.dec.com (Dave Jones) writes:
>[Why?  It will certainly make the resulting object code slower.]
>[For example, call/return is an efficient way to save/restore
> registers; the program will be larger unless procedures have only
> one call site; a larger program is usually slower; you will have to
> reinvent procedure calls to handle recursion.]

Um, er, ah, well, uh...

I'm not the original author so I can't comment on why s/he wanted to do
it.  It does seem to me, though, that you're imagining a particular
model for how things get expanded.  I don't see any particular reason
why ANY of the problems mentioned above are ``problems''.


* Call/return as an efficient way to save/restore registers

The VAX and Tahoes are optimized to save and restore registers in
call/return, but also have an explict register save with mask
instruction that does the same thing without the overhead of setting up
the argument/frame pointer(s), etc.  The 680x0 family has a similar
instruction.

On many RISCy machines -- and on a number of CISCy machines -- register
save/restore is left almost entirely up to the programmer, the major
exceptions being CISCy machines that dedicate the program counter and
the stack pointer.


* The program will be larger unless procedures have only one call site.

That's assuming that you have a function inlined everywhere that they
appear.  An alternative is to take each function |x| and produce a
label |x| and a set of ``calling conventions'' for transferring control
to that label.  You do wind up reinventing the procedure call, it is
true, but you also wind up with equivalent-size code.


* A larger program will generally be slower

That depends in great detail on things like caching behavior.  Strictly
inling long squences is almost always a lose, but shorter sequences can
profitably be replicated at a substantial increase in ojbect code size
if, say, the larger code improves prefetching.


* You have to reinvent procedure calls to handle recursion.

Excluding the case of tail calling, yes, you do have to reinvent
procedure calls.  However, you might get more freedom about designing
custom calling conventions.


In summary, aggressive compilers such as the MIPS compiler generally
try to create one large procedure out of a program.  They generally
don't inline recursive calls unless they are tail-recursive.  The major
difference is that they reserve ``squashing everything in to one big
function'' for e.g., link time.

Yeah, well, I'd like to squash C source, too, but for my needs it's the
best programming language I have avaialable.

	;-D on  ( But working on it anyway )  Pardo
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

htf@castle.ed.ac.uk (H T Fallside) (12/17/90)

In article <14662@goofy.megatest.UUCP> megatest!djones@decwrl.dec.com (Dave Jones) writes:
First thanks to those people writing to tell me about Jack Davidson's
and Ann Holler's inliner (see 1254) which sounds like being
just the thing I need. 

>Now then, please tell us why in the world you would want to make an entire
>program into one procedure.

I'm looking at doing some silicon compilation on dsp algorithms written
in C. It's much easier to analyse the allocation requirements, loop
nesting depth and any parallelism when the code is non-procedural. I'm not
concerned at this stage about size or speed issues on host machines
just whether the code remains compilable after i've been playing around
with it :-)

>I will leave you with one last word: "recursion". 

I was under the impression that any recursive procedure could be
exchanged for an iterative one (and vice versa) am i wrong ?
thanks again

hamish
----------
htf@uk.ac.ed.castle
[It's true, recursion and iteration are equivalent, but the translation can
be ugly, particularly in the presence of things like indirect recursion via
function pointers. -John]
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

leland@cs.columbia.edu (Lee Woodbury) (12/18/90)

In article <10767.9012051639@subnode.sari.ed.ac.uk> H T Fallside <htf@sari.edinburgh.ac.uk> writes:
>I'm after a preprocessor that will do in-line expansion of procedure
>calls IN SOURCE for C to produce one lovely long main() procedure, 
>something capable of dealing intelligently with parameters and local
>variables. Anyone out there got any ideas before I start writing ?

In late 1987, this fellow

	Steven McGeady
	3714 SE 26th Ave.
	Portland, OR 97202
	(503) 235-2462
	(503) 696-4393
	tektronix!ogcvax!omepd!mcg
	intelca!omepd!mcg

posted source to comp.sources.unix for a program called 'inline' which
did just what you wanted.  I got it, saved it, and still have it, but
have never used it, so I can't tell you how good it is.  The package
contained a copyright notice that prevents me from simply sending it to
you, but I have enclosed the message that accompanied the original
posting, the copyright notice, and the manual page (which the copyright
notice specifically allows me to distribute).  The source itself should
still be in the comp.sources.unix archives (Volume 11, Issue 39,
Archive-name:  inline/Part01...Part04); there was also a patch (Volume
11, Issue 43, Archive-name:  inline/Patch1).

You may also want to consider trying to reach the author to see if he
has a more recent version.

Good luck.  I would be interested to hear how useful this code is.

Leland Woodbury
ARPANET/INTERNET: leland@cs.columbia.edu
	  USENET: ...!columbia!cs.columbia.edu!leland
	  BITNET: leland%cs.columbia.edu@cuvmb
	  USMAIL: Columbia Univ., 457 CS, 500 W. 120 St., NYC 10027-6699
       TELEPHONE: 212-854-8109
	     FAX: 212-666-0140
-----------------------------------------------------------------------
: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract: 
:
:	NN-HEADER
:	COPYRIGHT
:	inline.1
:
echo x - NN-HEADER
sed 's/^X//' > NN-HEADER << '//go.sysin dd *'
XFrom columbia!rutgers!ukma!uunet!rs Thu Sep 24 13:11:15 EDT 1987
X>From: rs@uunet.UU.NET (Rich Salz)
XNewsgroups: comp.sources.unix
XSubject: v11i039:  Inline code expander for C, Part01/04
XMessage-ID: <1582@uunet.UU.NET>
XDate: 16 Sep 87 03:57:56 GMT
XOrganization: UUNET Communications Services, Arlington, VA
XLines: 852
XApproved: rs@uunet.UU.NET
X
XSubmitted-by: omepd!mcg@uunet.UU.NET
XPosting-number: Volume 11, Issue 39
XArchive-name: inline/Part01
X
X[  Please note The copyright restrictions; if you have problems with them,
X   calm reasoned e-mail to Mr. McGeady will probably be most effective.
X   Don't send ME mail, as I don't care enough.  --r$  ]
X
XI have a submission for comp.sources.unix, my inline code substituter,
X'inline'.  It has been in beta-test for over a month now, and I believe
Xthat it is stable enough to be released.  The next four messages will
Xcontain the four shar files with the source, documentation, test files,
Xand design notes.
X
XA word about the copyright.  I have chosen to copyright the source to
Xthe program, but place the binaries in the public domain.  This is
Xpredominantly to retain control over modifications to the source and
Xover what people might do with it.  The copyright notice file COPYRIGHT
Xcontains explicit permission for distribution over comp.sources, and
Xvia the archive server, but disallows other distribution.  If there is
Xa problem with this, please let me know.
X
XThank you for your help.
X
XS. McGeady
Xtektronix!ogcvax!omepd!mcg
Xintelca!mipos3!omepd!mcg
X(503) 696-4393
X
//go.sysin dd *
echo x - COPYRIGHT
sed 's/^X//' > COPYRIGHT << '//go.sysin dd *'
X
XThe source code to this program ('inline') is copyrighted, and all rights
Xare reserved by the author.  The copyrighted files include the C source files
Xand header files (all these files have copyright notices on them).
XRedistribution of this source code requires explicit permission from the
Xauthor.  The author explicit grants permission to the comp.sources
Xmoderators to redistribute the source code to end-users.  Other persons
Xwishing to redistribute this code must have permission from the author. (*)
X
XPermission is hereby granted to distribute the binary object code for this
Xprogram without explicit permission from the author, so long as such
Xdistribution is not for profit.
X
XThe Manual page for this program is hereby placed in the public domain.
X
X4/30/87
X
XSteven McGeady
X3714 SE 26th Ave.
XPortland, OR 97202
X(503) 235-2462
X(503) 696-4393
Xtektronix!ogcvax!omepd!mcg
Xintelca!omepd!mcg
X
X(*) I am taking the position that the posting of "diffs", including
Xreasonable context diffs, is "fair use", and is permitted (or at least
XI won't complain about it).  However, I would prefer if all modifications
Xwere sent to me first.
X
X
//go.sysin dd *
echo x - inline.1
sed 's/^X//' > inline.1 << '//go.sysin dd *'
X.TH INLINE 1 
X.\" $Header: inline.1,v 1.8 87/06/24 13:21:28 mcg Rel $
X.SH NAME
Xinline \- C preprocessor for inline functions
X.SH SYNOPSIS
X.B inline
X[
X.B \-w
X] [
X.B \-e
X] [
X.B \-s
X] [
X.B \-n
X] [
X.B \-d
X] [
X.B \-2
X] [
X.B \-S [ em
X]
X]
X[ infile ] [ outfile ]
X.SH DESCRIPTION
X.I Inline
Xis a
Xpreprocessor that accepts C language programs
Xcontaining the additional storage-class keyword
X\fBinline\fR applied to function declarations, and
Xgenerates identical C programs that (usually) have the
Xfunctions so marked expanded where they are called.
XInput code lacking the
X.B inline
Xkeyword is output unchanged.
XThus
X.I inline
Xallows C programmers a feature similar to that provided
Xby the C++ language.
XHowever, while the specifications are the same, the
X.I inline
Xprogram works substantially differently from currently
Ximplemented C++ compilers.
XCurrent C++ compilers rewrite
Xinline functions into expressions, thus prohibiting loops in them, but
Xallowing their use in contexts such as control statements of looping
Xconstructs.
X.I Inline
Xnormally
Xreformats
Xinline functions into code concealed in local blocks, adds
Xvariables for parameter and return values, and changes
X.B return
Xstatements into
X.BR goto s.
XThis allows use of all normal C constructs within inlines.
XThis rewriting is appropriate in all contexts except when
Xinline functions are called in the control parts of
X.B for
Xand
X.B while
Xloops, and in a few other cases.
XIn these cases, the C++ style expansion is used, and the procedures are
Xrewritten as expressions when
Xpossible (that is, when they lack loops, switches, and goto's).
X.PP
X.I Inline
Xemits
X.B extern
X(or, optionally,
X.BR static )
Xpredeclarations for
Xinline functions, so that unexpanded instances are compiled correctly,
Xand can be supplied externally.
XAdditionally, options are provided to emit the bodies of any unexpanded
Xfunctions as static procedures at the end of a module, or to emit
Xthe bodies of all inline functions alone, allowing inline functions to
Xbe collected into a library.
X.PP
XIn normal operation,
X.I inline
Xfunctions must be declared before they are used (like C++).
XThe \-2 (two-pass) option relaxes this requirement, at a 30% penalty in
Xprocessing time.
X.PP
XThe following options are provided:
X.IP \-w
Xsupress warning messages about unexpandable instances of inline functions.
X.IP \-e
Xemit predeclarations as
X.BR extern s,
Xand do not dump bodies of unexpanded functions (default).
X.IP \-s
Xemit predeclarations as
X.BR static s,
Xand dump bodies of unexpanded functions.
X.IP \-n
Xemit no predeclarations at all, and do not
Xdump bodies of unexpanded functions.
XThis allows the gathering of
X.B inline
Xfunctions into a library, to resolve unexpanded
Xreferences and any instances where the address
Xof an
X.B inline
Xwas taken.
X.IP \-d
Xdo not emit the main program, only dump the
Xbodies of all inlines (incompatible with \-e, \-s, and \-n).
X.IP \-2
Xprocess the file in two passes.  This allows inline functions to be
Xdeclared after their use.  For this option, standard input is not
Xaccepted.
X.IP \-S
Xemit (on the standard error output) statistics
Xabout the expansion process.
X.IP \-Se
Xemit extended statistics, giving expansion statistics
Xfor each
X.BR inline .
X.IP \-Sm
Xemit statistics about the memory usage of the program (if the program is
Xcompiled to collect these statistics).
X.PP
XIf no input file is specified, the standard input is assumed,
Xand likewise for the standard output.
X.SH BUGS/CAVEATS
X.I Inline
Xdoes not perform predictably when given incorrect C programs.
XIt is easy to test a program that contains inlines with the
Xcommand:
X.sp
X.nf
X.na
X	cc -c -Dinline=static file.c
X.fi
X.ad
X.PP
XWhen multiple inline functions are present in a single expression,
Xthe order in which the functions are executed in the inline code
Xis sometimes different from the order in which they would have been
Xcalled had they not been expanded.
XIn particular, all functions in the expression will be evaluated,
Xleft-to-right, before the operations in the expression are performed.
XThis is correct for most C operators,
X.I except
Xthe comma, boolean-or (||), and boolean-and (&&) operators.
XInline handles all but the comma operator correctly, expressionizing
Xcalls on the right side of the conditional operators.
X.PP
XWhile
X.I inline
Xattempts to pass preprocessor defines through without change,
Xit is strongly suggested that
X.I inline
Xbe executed
Xon code that has already been processed by
Xthe C preprocessor /lib/cpp.
X.PP
X.I Inline
Xdoes not recognize certain degenerate cases of function declarations
Xand calls, in particular:
X.sp
X.nf
X.na
X	(foo)(arg);
X.fi
X.ad
X.I Inline
Xalso does not correctly handle inline functions which take the address of
Xlabels, or which use labels other than in \fBgoto\fR statements, both
Xextremely distasteful (and probably illegal) practices.
X.SH "SEE ALSO"
Xcc(1), cpp(1)
X.SH NOTE
X.I Inline
Xsource code
Xis Copyright, 1986, 1987 by S. McGeady, all rights reserved.
X.br
XThe binaries for the VAX version of this program and this manual page are
Xin the public domain.
X.SH AUTHOR
XS. McGeady
X.br
X3714 SE 26th Ave.
X.br
XPortland, OR 97202
X.br
X(503) 235-2462
//go.sysin dd *
exit

-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.

Olivier.Levillain@cl.bull.fr (Olivier Levillain) (12/18/90)

In article <14662@goofy.megatest.UUCP>, megatest!djones@decwrl.dec.com
(Dave Jones) writes:

|>Now then, please tell us why in the world you would want to make an entire
|>program into one procedure. That will certainly make the resulting object
|>code slower. For one thing, on most machines, the procedure call/return is
|>an efficient way to save and restore registers. For another, unless the
|>program is a rather simple one with most procedures being called from only
|>one place, the program will be larger. On machines that do paging, larger
|>usually means slower, sometimes much slower.

I don't agree with these arguments. 

|> For one thing, on most machines, the procedure call/return is
|>an efficient way to save and restore registers.

Saving and restoring registers even if it is efficient, can be expensive
especially on CISC machine. On our machine (BULL DPS7), it uses 50 cycles + 1
cycle by register to save/restore.

|> For another, unless the
|>program is a rather simple one with most procedures being called from only
|>one place, the program will be larger.

I wrote myself an inliner for a common code generator which generates code
for several front-ends (C, Fortran, PL1). Inlining is done on an intermediary
language and takes place before local and global optimization. So that the
optimizers can work on larger sequences of linear code.  Big parts in and
around call of inlined functions disappear in the optimizers because their
context is more completely defined. If you write:

f (x)
int x; {
 switch (x) {
  case 1: return 1;break;
  case 2: return 0;break;
  case 3: return 1;break;
  case 4; return 2;break;
 }
}

main () {
int y;
 ...
 y=2;
 ...
 if (f(y))
 ...
 else <stat1>;
 ...

Without an inliner, the whole code will be generated. If you "inline" f,
generated  code will look as if you wrote:

main () {
int y;
 ...
 y=2;
 ...
 <stat1>;
 ...

because, after f has been inlined, optimizer can compute at compile-time that
f(y) will return 0. So that, the final size of generated code is not
necessarily very much bigger.

|>On machines that do paging, larger
|>usually means slower, sometimes much slower.

If the procedure you call is not in the same page that the page from where you
call this procedure, OS will also have a missing page exception to handle.

|>I will leave you with one last word: "recursion". You will have to
|>reinvent procedure calls to handle recursion.

Inlining procedure does not mean that you don't call a procedure any longer.
You can just inline a call to a (directly or not) recursive procedure and
leave the really recursive call in place.

As a conclusion, just one thing: we have an improvement of 30% on
dhrystone1.1 benchmark when using the inliner.
                         
Olivier LEVILLAIN --- Bull S.A.
 BSS7-LANG-LSLI		             e-mail: Olivier.Levillain@cl.bull.fr
 Rue Jean-Jaures, F6/1D/10, BP 53    tel: (33-1) 34-80-65-52
 78340 Les Clayes-sous-Bois, France  Fax: (33-1) 34-80-79-50
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.