Another stab at the HLL ...

spc@gate.net spc@gate.net
Mon, 8 Apr 1996 02:05:06 -0400 (EDT)


On some network somewhere in cyberspace, Francois-Rene Rideau transmitted:
>
> >> I'm not talking about doing occasional low level stuff from the HLL
> >> when the HLL isn't adequate. It should be possible to do that.
>
>    Yes. I'd add that when the HLL is adequate,
> it should be used, because even though code produced by the compiler
> may not be as good as hand written code,
> the result is more maintainable, portable, readable, etc.

  Hmmm, I'll dryly note that the same can be said for C (poorly written code
aside that is).

> Moreover, for those who really want to make the most out the computer,
> low-level programming will be enabled *inside the HLL* in a way
> that is consistent with HLL programming.

  Again, I'll note that Watcom C allows one to control the low level passing
techniques.  It works similarly to the following (although not exactly - it's
not that pretty in real life):

extern unsigned short _bios_keyboard(unsigned _cmd);

#pragma calling _bios_keyboard =	mov	dh,ah	\
					int	0x16	\
					jnz	_skip:	\
					and	dh,0x0f \
					dec	dh	\
					jnz	_skip:	\
					sub	ax,ax	\
				_skip:			\
				param [ah] value [ax] modifiy [dx];

  Alternatively, one could (with the proper C compiler) do:


#ifdef PCBIOS
#  pragma _scan_keyboard uses ax,dx
#endif
#pragma inline _scan_keyboard

inline unsigned short _scan_keyboard(unsigned _cmd)
{
# ifdef PCBIOS

    unsigned short result;

    asm
    {
		mov	ah,_cmd
		mov	dh,ah
		int	0x16
		jnz	skip
		and	dh,0x0f
		dec	dh
		jnz	skip
		sub	ax,ax
      skip:	mov	result,ax
    }
    return(result);

# else	/* most portible solution in case something is needed */

    return(getchar());

#endif
}

  Not pretty, but not much different than what you are proposing, but already
being (can be) done (and goes against my standards of not doing inline
assembly).  It's what #pragma is for (in C).

>    That is, you can give "equivalents" for HLL programs in some
> low-level contexts *and* prove that they indeed are equivalents,
> because you used only tools that conserve meaning,
> or check the meaning was indeed conserved.
> Using such tools will ensure that the LLL versions are always
> consistent with the HLL version, while providing the full control
> of LLL programming.
>    Thus, after having specified in HLL what a bitblt routine is,
> you can give optimized instances for graphics card X on processor Y,
> or better, some compiler tactic to optimize more or less specialized
> instances of bitblt on similar context.
>
  Again, nothing that C can't do right now (albeit ugly).

> >> I'm talking about low level programmers programming whole applications or
> >> whatever in their custum made language to play with the Tunes
> >> internals. I'm affraid nothing but the HLL will be able to allow low
> >> level programming since many aspects of the low level structure of
> >> Tunes will probably change dynamicaly as directed by the HLL (take
> >> data representations and calling conventions for exemples). This
> >> dynamism probably won't be there in the first implentations and thus
> >> it will be possible to write low level programs until dynamism is
> >> implemented. Then we will have to perform major rewrites of the low
> >> level stuff (in something more like the HLL) to make it configurable
> >> by the HLL.
>    Yes. Modular design and metatranslation may reduce the rewrite,
> but the main point is that from implementation to (re)implementation,
> LLL design may change completely. Building the OS standard from LLL
> design leads to horrible static stuff like MSDOS (LLL: 8086 asm)
> or Unix (LLL: ANSI C), that cannot scale up to newer architectures.
>
  I agree in part (not fully) on the MS-DOS part, only because it would
require an entire rewrite to have it take advantage of the rest of the Intel
line.  I only agree in part because, had Microsoft done it "properly" (ie.
followed their own programming rules, enforced them, etc) then scaling up
MS-DOS would not have been as difficult to do.	Part of the reason it's still
around in the current incantation (essentially, unchanged since 2.0) was that
Microsoft blew it and didn't make 2.0 reentrant (they didn't have to make it
multitasking.  Making it reentrant at least would have been a good first step)
nor did they provide decent device drivers for character IO.

  Those two factors alone would have made MS-DOS "more portible" across a
wider range of hardware, instead of the hardware stabalizing like it did (cf.
CP/M - which did run across a wide range of hardware).	You may not be aware,
but during the mid-80s (specifically, between 83 and 86) there were MS-DOS
compatible PCs made that were NOT IBM PC compatible (slightly different
hardware - and I have some of these).  And Microsoft shouldn't bear all the
blame for the 640k barrier.  MS-DOS can quite easily use more memory, although
a design flaw requires that the memory be contiguous.  The 640k limit comes
from the IBM PC AND the requirement of MS-DOS that all its memory be
contiguous.

  Another problem were programmers who bypassed MS-DOS and BIOS because of a
perceived (or actual) performance problem by going through those sub-systems.
Only now is the industry getting away from doing that.

  I don't agree with your statement about Unix and C.  It has scaled (and some
would say quite successfully).	It may be of a poor design (and there are many
flaws in Unix - some of which MS-DOS improved upon, strangely enough).	But
don't blame the implimentation and design on C.

> >   This is EXACTLY what bugs me about the supposed HLL.
>    What ? Not being able to say in what 80x86 register some routine
> will pass arguments, whereas the routine runs equivalently
> on a SPARC version, and the compiler can choose it better than you ?

  No.

  The impression I got was that one didn't need to specify the type, the order
or anything else of importance to the HLL about parameters.  That it all would
be worked out "automagically".  Something alone the line of:

declare foo which takes bar, baz and snafu
	return bar + baz + half a snafu

  Where the HLL decides what foo is, that type bar (if it even IS a type), baz
and snafu is, and what half a snafu is, and the relationship of the operator
'+' upon all three.

  If I'm programming in C, I let the C compiler take care of the details of
passing ints, chars and pointers around in whatever manner it sees fit.  Heck,
on the Amiga, I don't have to worry that the native C routines pass parameters
via the stack, and the shared libraries require them in registers, since
that's taken care of by #pragma's in the system headers for me.

  So yes, at that point, the actual semantics of passing are hidden from me
(unless I write a shared library or some routine in Assembly) and I'm quite
comfortable with that.

> Well, you still *will be able* to give such hints, as compiler tactics,
> given *independently* from the actual HLL code.

  And like I said, the same can be done in C right now.

> Of course, in most cases, you won't bother, because the compiler does
> it better than you; in the same way as you don't monitor binary
> output and relative jump calculations done by the assembler,
> because it just does it better than you.
>
  In some cases.  I still do monitor the output occasionally (depends upon the
assembler) since sometimes the assembler guesses wrong (notably MASM).	And in
another case, the assembler (A68K on the Amiga) did too good of a job of
optimization, thus destroying a jump table I had carefully created.

  But on the whole, yes, you are correct.

> > That somehow, all
> > these messy details about concrete implimentation details will be
> > automagically taken care of, somewhere.  Somehow.
>    NOT "automagically" in the meaning that you don't know what it is.
> This "automagic" is what makes all current commercial software
> lame and untrustable.

  Do tell ...

>    However, there is a programmable way in which these details
> are taken care of. If you don't like it, make it better.
> It won't cost you more than doing it in asm, and will be reusable.
> For instance, if you love the way C does it,
> you can force the compiler to push things on stack in the given order.
> You never lose at doing this, because the HLL lets you be in control,
> instead of having you be the one controlled by whoever choses the
> convention.
>
>
> >   How?
> >
> >   I'm not looking for the final answer.  I'm just looking for AN answer that
> > reasonably answers the question.  At at a fairly low level.  As in bits being
> > moved about the computer.
>    Any reasonable answer cannot be expressed simply in term of bits,
> but it can be done simply in terms of lists of integers, and such.
>
  Fine, I'll take what I can get 8-)

> >   Okay, maybe an example is in order.  Using the Mythical HLL, I develop,
> > what?  An object?  A class?  Library?  Subroutines?  Something.  For the sake
> > of argument, I develop Something that impliments a way to simulate rolling
> > dice.
>
>
> >   The Argument is in the form of 'XdN', where [lots of special cases]
>    In the HLL as anywhere, there will have to be a special parser for
> such a bizarre syntax. However, in the HLL, contrarily to most languages
> you can "intern" the syntax after having defined it,
> so as to use it, when it is imported in the current scope.
> The advantages of being interned are that the compiler can
> typecheck and partially evaluate it, perhaps completely before run-time.
>
> >   The Something works by simulating a roll of an N sided die X times [2] and
> > returning the sum of all the rolls, plus the individual rolls.
>    Why have the first element the total ? That's clumsy !
> A list of elements, and their sum is something heterogeneous to me.
> The Something will separately define those.

  First off, the elements are values, and the sum of those values is a value,
so you have a series of values.  The reason I decided upon having the first
element be the sum is two fold:

	1. I did NOT make any assumptions that the HLL has the ability to
	   return more than a single result from a Something (and the ability
	   to return multiple results is something I personally like)

	2. More often than not, it's the sum that is of importance, not the
	   individual tosses, although there are times (although rare) where
	   the individual results are required.  I figured it would be just
	   as easy to return a list of values with the first being the most
	   preferred result as it's usually easier (and faster) to pick up
	   the first element of a list (array, whatever) than the last.

  If the HLL has the ability to return more than a single item, then the
Something I described would return two items, the sum, and a list of the
individual results.  Happy?

> Of course, you can ask the compiler to compute those together,
> and it will automatically merge and optimize the loops much
> better than you can do yourself, automatically taking account
> whatever is known about N and X, unrolling the loop and
> merging loops after having renamed the registers to disjoint sets,
> etc. All this can be done consistently *under full user control*.
> Manual optimization would be slow, without consistency,
> just for a very particular case, with the human doing no better
> than the computer instruction scheduling.
>

  So I can infer that the HLL can, at its discretion, see a single call like:

			2d6

  And inline it, but if it sees multiple calls, just issue a call (or maybe
inline each individual case, depending upon the circumstances).  Okay, I can
buy that.

> > For lack of a better term, these results are returned in a List
> > (not the LISP kind either. More of an open ended array)
>    List is a perfect term to me. What exact implementation is better
> depends on many things: what other routines will access the list, etc.
> If you want, you can give hints to the compiler
> about which is preferrable of open array, linked list, reverse-linked list,
> doubly linked list, obstack, etc, or even force your choice to the compiler.
> But in general, you're better off optimizing the compiler heuristics
> than doing compiler job by hand.
>
  Hmmmm, again, we already have that.  To some extend in C, but definitely in
other languages, like Java (of which I've recently become an expert 8-)

  In Java you have what's called a Vector.  I'm not sure of the representation
(although it's easy to check - I have the source code to the class), but
that's beside the point.  All I do is declare a Vector, instantiate it, and
then I can add, remove retrieve, get the number of, elements in that Vector.

  Again, I don't have a real problem with that.  Someone else when to the
trouble of doing the implimentation.  I'm just using it.

> > where the first element is the total, and the
> > rest are the individual values.
>    How ugly. Put those in a record, if you really want a similar stuff !
>
  See above.  In this case, they're all the same type.

> >   The notation was picked since it's a common notation that gamers use.
>    Ah, that sweet time when I played RPGs !
>
> > How it's passed in is of no concern, right?
> > And how the results are passed out are of no concern, right?  Okay.
>    It should be of no concern most of the time to most programmers.
> If you're masochistic enough to wanna know, or if the particular routine
> has been measured to take a significant portion of execution time
> (from 15% to 99.9%), you can control that (though just telling the
> compiler to superduperoptimize with some hints should be enough).
>    However, if what you want is programming assembly
> with some glue libraries, please do, and don't bother about writing an OS.
>
  Well, I program in Assembly on the Amiga, and I do use the OS.  What's your
point?

> >[In a imaginary craps game, there is a package by you that works,
> >one by Patrick that doesn't]

  I never said that.  I said that I had written a package, and Patrick had
written a similar (if somewhat different) package that, for whatever reason,
you decided to use.

>    Well, with such conditions, I wonder why I'd want to hack Patrick's
> package, and not use yours. But perhaps yours is copyrighted,
> or runs only on the x86, and is optimized for the P6 processor,
> but unbearably slow on my new P7 for which it is really (though
> unvoluntarily) pessimized. All than would not have happened had you
> used a HLL instead of x86 assembly !

  Reread what I wrote.	I said I wrote in the HLL, and Patrick wrote HIS in
the HLL as well.  I think you missed the point I was trying to discuss.

  The impression of the HLL I had was that given two packages that did similar
things although with different arguments/interfaces, all you, as a programmer,
had to do was plug in the new package.	Somehow, it would compile.

  THAT'S what I was talking about.

> Again, all these are better done by annotating the HLL source
> than by using a LLL. The HLL can always optimize and produce code
> as good as the LLL, while the LLL is unportable, unmaintainable,
> unoptimizable.
>
  And if that's the case, why even bother with an LLL?  Why not

		HLL -> CPU binary execution code

  which is basically the same thing?  If the LLL IS unportable, unmaintainable
and unoptimizable, what does having an LLL buy you?  And if the LLL can
change, as you stated earlier:

	... but the main point is that from implementation to
	(re)implementation, LLL design may change completely.

  you'll still have to change the HLL-LLL interface.  And going from the X86
to the MIPS (say) is a reimplementation (or another implimentation) of the
LLL.

> I just wrote some perl program to do exactly what you described.
> The HLL will behave similarly (though being cleaner to design and understand,
> smaller in memory and faster in manipulation and execution, longer to print).
> I don't see what difficulty you see to that.
> I can send it to you if you're interested.
>
  Well, it seems you sent it reguardless if I wanted to see it or not, so I'll
take a look anyway 8-) even though I find the syntax of Perl blecherous.

> >   Now, this being Tunes, that doesn't matter, right?  You can just plonk in
> > Patrick's Dice Something and not have to change a thing, right?
>    If you defined a HLL interface to your routine,
> that describes the calling conventions, and outer behavior semantics.

  But shouldn't the HLL take of that?  I'm getting terribly confused here.

> > Because the messy details of calling conventions,
> > return results and data representations don't matter, right?
>    No they don't.
>
> >   THEN HOW THE BLOODY HELL DOES THIS WORK?	(Excuse my Engligh)
>    Do you want me to give you details of a compiler ?
> I don't have time here, but I invite you to read the GCC sources ;->
> Or to read the Red Dragon book.
>
  Maybe what I'm asking for is a clarification of the HLL.  What is it
exactly, what does it look like, how does it work, etc.  And yes, I'm familiar
with the internals of compilers, and I don't have the time to go through the
24 meg GCC source code.

> >   -spc (And please, some CONCRETE examples.  Implimentation details.
> >	Something better than "Automagically".  Please.)
>    You want me to give you an instruction trace ?

  Nothing so detailed, but more concrete than "Well, it just happens."

  -spc (And now, the HLL doesn't sound all that different than what's
	come before ... )