Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Embedding assembler in a language

8 views
Skip to first unread message

James Harris

unread,
Nov 18, 2009, 2:07:21 PM11/18/09
to
It is generally desirable for a high level language to be independent
of any target hardware but ISTM that sometimes it is useful to write
system-specific modules. For example, these could be specific to a CPU
or to a hardware device. Any disagreement on this?

If the above is accepted, I'd like to ask about the processor as one
potential target. It could be helpful to the programmer to allow him
or her to embed assembler in high level code.

Issues include: appearance (beauty or ugliness), line continuations
and comments (use the HLL's scheme or not), freely mix HLL and
assembler or how to indicate which is being used, independence of a
particular assembler (if an assembler is used as part of code
generation), recognising registers used in the programmer's asm code,
allowing the translator to supply real register names (by allowing the
programmer to use explicit temporaries), accessing HLL symbols from
the asm code, and suchlike.

Is anyone else allowing embedded assembler or have you any thoughts on
how it *should* be done?

James

BGB / cr88192

unread,
Nov 18, 2009, 2:30:16 PM11/18/09
to

"James Harris" <james.h...@googlemail.com> wrote in message
news:2aff8423-152e-4921...@j4g2000yqe.googlegroups.com...

as long as it doesn't look like the inline-ASM support in GCC, it is
probably fine...

I mostly liked the style employed by Borland/Turbo C, where one could be
something like:

int i, j;
...
asm {
mov ax, i
mov dx, j
add ax, dx
...
};

and it worked fairly well...

if I were to do similar, I might make minor adjustments (probably making ASM
statements end in ';' and supporting multiple per-line), but would probably
keep a similar style (although, I would use NASM style syntax rather than
TASM style).


in my case, I don't support inline ASM directly in my C compiler, but mostly
this is because for what I use the compiler for, it is not needed.

for native code, I almost invariably end up using API calls (to my dynamic
assembler) instead, mostly because I have to deal with several different
compilers and architectures, and personally I have found a style of ASM
where I call some API calls and get back a function pointer to be fairly
usable.


similar could be adapted (with varying levels of difficulty) to non-C
languages.

as for embedding ASM in a non-C HLL, there are possible issues depending on
the internal workings of the language and/or VM:
this would likely only be really usable with a statically-compiled language,
or with a relatively "low-level" VM (as-in, one where ASM-level references
to variables and data would actually make sense).


> James


Marco van de Voort

unread,
Nov 18, 2009, 5:12:07 PM11/18/09
to
On 2009-11-18, James Harris <james.h...@googlemail.com> wrote:
> It is generally desirable for a high level language to be independent
> of any target hardware but ISTM that sometimes it is useful to write
> system-specific modules. For example, these could be specific to a CPU
> or to a hardware device. Any disagreement on this?

It is generally considered desirable that any _formal_ specification of a
language is independant of hardware.

However implementations typically diverge, and implementations is what
people use in the end, not the formal spec. Even if they are standard. And
thus most applications are bound by the language of the implementation in
practice, not the standard.

Only a few projects specifically targeted at portability, and portability above
all excluded, like GNU. (though they have POSIX assumptions all over the
place anyway)

> If the above is accepted, I'd like to ask about the processor as one
> potential target. It could be helpful to the programmer to allow him
> or her to embed assembler in high level code.

Any language includes processor specific assumption. Even if only assuming
that memory is byte addressable, linear etc, that a byte is 8-bits etc.

It's a matter of finding the right tradeoff.

> Issues include: appearance (beauty or ugliness), line continuations

KISS, one asm instruction per line. Maybe prefixes like REP excluded.

> and comments (use the HLL's scheme or not), freely mix HLL and
> assembler or how to indicate which is being used,

A lot of these things depend on what your goal is. Is it to implement
a runtime library? To have a few asm procedures here and there?

If you allow mixing, and are doing it for performance, shouldn't it
be register-independant to not frustrate the optimizer?

> independence of a particular assembler (if an assembler is used as part of
> code generation)

First a bit of nomenclature, We call
- the part that reads inline assembler "the assembler reader", and
- the part that writes out backend assembler "the assembler writer", and
- the part that writes out machinecode (internal backend assembler) the
"binwriter"
- the "backend assembler" is an external assembler, used for archs that
don't have a binwriter. Typically it is AS, but in theory the assembler
writer can also output TASM and MASM (it does work +/-, but this is used
more for bootstrapping experiments and asm inspection, rather than production)

All three parts are integrated into the main compiler binary. A binwriter
does not exist for all binary formats. The assembler readers support both
AT&T and intel syntax on x86/x86_64. Usually only the native format for
other architectures (typically AT&T)

Well, the startup code excluded, that is possible. E.g. FPC does that, even
when it uses a backend assembler (for targets the internal assembler can't
generate machinecode for), the inline assembler is syntax checked and transformed
by the compiler and then written out again as backend compatible assembler.

This has several advantages:

- the sourcecode is independant of the used backend assembler. (TASM and
MASM code can be generated too, though nowadays that is not maintained
much anymore)
- the interpretation by the compiler makes HLL<->ASM interaction a lot
easier, decreasing the amount of magic constants. Constants like offsets
are calculated directly from the HLL structures.
- more version independance of the source wrt backend assembler.
- opcodes the backend assembler doesn't know (or might not know, because
some @*$(@$& distribution packages a fossil) can be simply emitted as

db hex1,hex2

to the backend assembler.
- This also allows using Intel syntax in source, and writing out at&t to
the (G)AS backend. This dates from before AS had Intel support.

Ours doesn't support any form of register allocation though, like e.g.
GCC does. It is tons easier to use though (right up there with Borland
products)

The disadvantage obviously is the amount work to do it for multiple
architectures, the ever growing x86 instruction sets, and possibly multiple
backend assemblers. And then I'm not even talking about machinecode
generation (which we only do for x86 ELF and PECOFF). This is partially
tackled by generating the instruction tables from NASM.

> Is anyone else allowing embedded assembler or have you any thoughts on
> how it *should* be done?

See FPC. The sky is the limit, how much time do you want to invest?

bartc

unread,
Nov 18, 2009, 5:17:02 PM11/18/09
to

"James Harris" <james.h...@googlemail.com> wrote in message
news:2aff8423-152e-4921...@j4g2000yqe.googlegroups.com...
> It is generally desirable for a high level language to be independent
> of any target hardware but ISTM that sometimes it is useful to write
> system-specific modules. For example, these could be specific to a CPU
> or to a hardware device. Any disagreement on this?

Not from me. I depend on inline assembler to get the best performance from
my (very) non-optimising compiler.

> If the above is accepted, I'd like to ask about the processor as one
> potential target. It could be helpful to the programmer to allow him
> or her to embed assembler in high level code.
>
> Issues include: appearance (beauty or ugliness), line continuations
> and comments (use the HLL's scheme or not), freely mix HLL and
> assembler or how to indicate which is being used, independence of a
> particular assembler (if an assembler is used as part of code
> generation), recognising registers used in the programmer's asm code,
> allowing the translator to supply real register names (by allowing the
> programmer to use explicit temporaries), accessing HLL symbols from
> the asm code, and suchlike.
>
> Is anyone else allowing embedded assembler or have you any thoughts on
> how it *should* be done?

Assembler: Nasm for x86. (This used to be quite slow, taking 5 times as long
to assemble the output of my compiler (as x86 source) as compiling the
program. I either don't notice the speed now, or they've made it faster).

Appearance (within my hll syntax), multiline and single line forms:

assem
push eax
inc dword [mem]
end

asm pop esi

(I used to use <...> delimiters for assembler, the above is better.)

Comments: use both ";" (nasm) and "!" (my hll)

Registers: I can't use identifiers in the hll that would clash with
assembler registers, if access from asm is needed.

Identifiers: Assembler can access all hll variable names which are visible
from the current scope. Local frame variables don't need a frame register
for access ([x] is mapped to [ebp+x] as needed).

Function names I think need a "_" prefix when imported or exported
(something to do with the linker; I used to write my own everything, and had
none of this nonsense...).

Labels in the hll are accessed as normal in the assembler. Hll labels are
subject to scope. Asm labels should not clash with hll labels in the current
scope, and have file scope (this is to do with the assembler).

Struct member names are not accessible from the assembler...

The hll itself is compiled to assembler source, and any inline asm is shown
indented in that output. Nasm then assembles the lot, but for any errors
that occur, I have to look to the line number in the compiler output (.asm
file), then fix in the hll source... but errors are rare.

All in all, I probably wouldn't use stand-alone asm files anymore, the
advantages of a hll are too great. (My first assemblers had a hll framework:
function, variable and const declarations just as in a hll, labels with
scope local to a function, so that you didn't have to be keep inventing
unique names.., identifiers of any length, and so on. You get spoilt.)

--
Bartc

Rod Pemberton

unread,
Nov 18, 2009, 5:59:57 PM11/18/09
to
"James Harris" <james.h...@googlemail.com> wrote in message
news:2aff8423-152e-4921...@j4g2000yqe.googlegroups.com...
> It is generally desirable for a high level language to be independent
> of any target hardware

Um, I'm not sure such a thing exists... Every language I've seen and even
abstracted OSes, e.g., User-Mode Linux, has some type of interface to the
hardware, usually via the OS, even if the interface is minimal or can be
minimized. You can't do file I/O without calling the OS which comminicates
with the hardware. You can't do memory allocation. Etc. There is always
something outside the scope of the HLL that the HLL or a programmer using it
needs from the hardware or OS.

> but ISTM that sometimes it is useful to write
> system-specific modules.

More likely, it's required, for one reason or another: module functionality,
cpu customization, speed.

> For example, these could be specific to a CPU
> or to a hardware device. Any disagreement on this?

No. You will need something hardware specific for anything other than
memory management.

> If the above is accepted, I'd like to ask about the processor as one
> potential target. It could be helpful to the programmer to allow him
> or her to embed assembler in high level code.

#asm
/* blah blah */
/* blah blah */
#endasm

Well, that's the way Small C does it... Of course, that's not C spec.
compliant. But, of the C compiler's I've used, that's probably the
cleanest. OpenWatcom uses #pragma claiming doing so is more compliant with
the C standards. GCC uses strange stuff for assembly and attributes.

> Issues include: appearance (beauty or ugliness), line continuations
> and comments (use the HLL's scheme or not),

Not. That's the solution: not. Pass everything between your two markers
as-is to the assembler in the assembly format. Make sure you or your users
know *exactly* where the non-HLL code starts and stops.

> freely mix HLL and
> assembler

Above...

> or how to indicate which is being used,

As above... Preprocessor should understand and be able to remove.

> independence of a
> particular assembler (if an assembler is used as part of code
> generation),

Above...

> recognising registers used in the programmer's asm code,

That's a problem. A HLL likes to use registers without regards for what the
user may or may not be doing in assembly. You need some method to track and
control what registers are being used so the assembly and the assembly code
generated for the HLL doesn't "clobber" each others registers. Some
compilers disable optimization if assembly code is involved, due to this
issue.

> allowing the translator to supply real register names (by allowing the
> programmer to use explicit temporaries), accessing HLL symbols from
> the asm code, and suchlike.

Does this eliminate register "clobber"?

> Is anyone else allowing embedded assembler or have you any thoughts on
> how it *should* be done?

You can attempt to control it, like GCC, and likely end up with the same
issues as GCC: convoluted syntax to indicate input and output registers, no
optimization. Or, you can leave it up to the programmer, like Small C. Or,
you can do whatever you've got time, life, and money for...


Rod Pemberton


Rod Pemberton

unread,
Nov 18, 2009, 10:56:19 PM11/18/09
to
"Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
news:he1ucd$327$1...@aioe.org...

> "James Harris" <james.h...@googlemail.com> wrote in message
> news:2aff8423-152e-4921...@j4g2000yqe.googlegroups.com...
> > If the above is accepted, I'd like to ask about the processor as one
> > potential target. It could be helpful to the programmer to allow him
> > or her to embed assembler in high level code.
>
> #asm
> /* blah blah */
> /* blah blah */
> #endasm
>
> Well, that's the way Small C does it... Of course, that's not C spec.
> compliant. But, of the C compiler's I've used, that's probably the
> cleanest. OpenWatcom uses #pragma claiming doing so is more compliant
with
> the C standards. GCC uses strange stuff for assembly and attributes.
>

If you added a "shebang" line like Unixen scripts, then you could even
direct which assembler you wanted to use. I'm not sure how that'd work
without parameters and with linking though... The interpreter directive
("shebang") concept is meant for interpreters.

#asm
#!C:\NASM.EXE


/* blah blah */
/* blah blah */
#endasm


Rod Pemberton


tm

unread,
Nov 19, 2009, 3:38:18 AM11/19/09
to
On 18 Nov., 20:07, James Harris <james.harri...@googlemail.com> wrote:
> It is generally desirable for a high level language to be independent
> of any target hardware but ISTM that sometimes it is useful to write
> system-specific modules. For example, these could be specific to a CPU
> or to a hardware device. Any disagreement on this?

Yes, I deagree.
I don't think it is a good idea to offer backdoors like inline
assembler.

- Programs with inline assembler are not portable. In the internet
you can find "Pascal" programs which are 99% assembler with
additional 1% Pascal function/procedure headers. If you are
looking for a high level algorithm you are probably not happy with
such crap.

- Language implementations which advertise inline assembler often
try to cover shortcomings (e.g. performance or access to external
resources) of the language or implementation. So instead of
improving language or implementation they offer a backdoor.

- What once was a good idea to improve performance can now be the
stupiest thing to do. Inline assembler that once offered a
performance improvement over high level language code can be
slower on newer processors and newer compilers where compiled
high language language code uses additional registers and modern
machine instructions.

- There are other processor architectures beside x86 which are
probably excluded with x86 inline assembler programs.

It is really not a good idea when a language encourages writing
unportable programs. Instead of providing backdoors the language
implementers should find out where programmers would consider using
such backdoors and improve this areas.

That said, I would like to know which areas are considered
weaknesses of Seed7.

Greetings Thomas Mertes

Seed7 Homepage: http://seed7.sourceforge.net
Seed7 - The extensible programming language: User defined statements
and operators, abstract data types, templates without special
syntax, OO with interfaces and multiple dispatch, statically typed,
interpreted or compiled, portable, runs under linux/unix/windows.

Marco van de Voort

unread,
Nov 19, 2009, 6:03:17 AM11/19/09
to
On 2009-11-19, tm <thomas...@gmx.at> wrote:
> Yes, I deagree.
> I don't think it is a good idea to offer backdoors like inline
> assembler.
>
> - Programs with inline assembler are not portable. In the internet
> you can find "Pascal" programs which are 99% assembler with
> additional 1% Pascal function/procedure headers. If you are
> looking for a high level algorithm you are probably not happy with
> such crap.

As any tool, it can be abused. Just like people can use Duff's device to
manually unroll loops in C. You will also find that on the net.

Premature optimization is premature optimization, in HLL or assembler. But
it is not a reason to ban all tools or constructs that could be abused for
this.

> - Language implementations which advertise inline assembler often
> try to cover shortcomings (e.g. performance or access to external
> resources) of the language or implementation. So instead of
> improving language or implementation they offer a backdoor.

Examples? It can also simply be because the runtime is entirely written in
the language. If your lowlevel runtime is in a different language, you need
much less assembler.

Also it is a bit unfair to point to TP code for your point. It on average
two decades old and frozen code.

> - What once was a good idea to improve performance can now be the
> stupiest thing to do. Inline assembler that once offered a
> performance improvement over high level language code can be
> slower on newer processors and newer compilers where compiled
> high language language code uses additional registers and modern
> machine instructions.

In general yes, but the assembler is there for the specific cases. I'm a
fulltime Delphi devel, and _all_ of my inline code on the job is SSE2.

> - There are other processor architectures beside x86 which are
> probably excluded with x86 inline assembler programs.

That's why we also sport an x86_64,ppc,ppc64,arm (several flavours),sparc
and sparc64 assembler. Because the lowlevel parts of the program and RTL
must also be done in assembler for those parts.

> It is really not a good idea when a language encourages writing
> unportable programs. Instead of providing backdoors the language
> implementers should find out where programmers would consider using
> such backdoors and improve this areas.

Encourage and enable are two entirely different things.

bartc

unread,
Nov 19, 2009, 6:35:21 AM11/19/09
to

"tm" <thomas...@gmx.at> wrote in message
news:79019631-359d-4cae...@m38g2000yqd.googlegroups.com...

> On 18 Nov., 20:07, James Harris <james.harri...@googlemail.com> wrote:
>> It is generally desirable for a high level language to be independent
>> of any target hardware but ISTM that sometimes it is useful to write
>> system-specific modules. For example, these could be specific to a CPU
>> or to a hardware device. Any disagreement on this?
>
> Yes, I deagree.
> I don't think it is a good idea to offer backdoors like inline
> assembler.
>
> - Programs with inline assembler are not portable. In the internet
> you can find "Pascal" programs which are 99% assembler with
> additional 1% Pascal function/procedure headers. If you are
> looking for a high level algorithm you are probably not happy with
> such crap.

If there is a genuine need for assembler, isn't it much better to make it
available within the discipline of a high-level language? So you can make
full use of function blocks, scoping, declarations, control structures,...
It's possible to give some 'shape' to a program instead of it being just a
linear sequence of instructions.

(Anyway I've seen plenty of code, especially C, which is pretty much
impossible to compile anywhere else even when there is no assembler
involved.)

> - Language implementations which advertise inline assembler often
> try to cover shortcomings (e.g. performance or access to external
> resources) of the language or implementation. So instead of
> improving language or implementation they offer a backdoor.

What's wrong with improving performance? I use inline assembler to write
fast interpreters. Once done, I can then write all my code at a higher level
than the language used to implement the interpreter. But that assembler code
is a one-off. It means I can get, say, double the speed, of my higher level
code.

> - What once was a good idea to improve performance can now be the
> stupiest thing to do. Inline assembler that once offered a
> performance improvement over high level language code can be
> slower on newer processors and newer compilers where compiled
> high language language code uses additional registers and modern
> machine instructions.
>
> - There are other processor architectures beside x86 which are
> probably excluded with x86 inline assembler programs.

I don't think they are excluded at all. It is possible to take a specific
function, and code both in high-level language, and assembler, and use
mechanisms (eg. commenting) to make either available. In a similar way, you
can add more than one assembler version. Using a dedicated assembler makes
this more awkward.

When the assembler code causes problems, you switch to the high-level code,
at a cost perhaps of performance. Or, if what you say about newer processors
is correct, you will get better performance. But usually there was a good
reason to use the assembler in the first place (since it is such a PITA to
write -- and maintain -- you don't use it unless you have to).

> It is really not a good idea when a language encourages writing
> unportable programs. Instead of providing backdoors the language
> implementers should find out where programmers would consider using
> such backdoors and improve this areas.
>
> That said, I would like to know which areas are considered
> weaknesses of Seed7.

No 'goto' and (I'm guessing) no inline assembler..?

--
Bartc

tm

unread,
Nov 19, 2009, 8:06:08 AM11/19/09
to
On 19 Nov., 12:03, Marco van de Voort <mar...@stack.nl> wrote:

> On 2009-11-19, tm <thomas.mer...@gmx.at> wrote:
>
> > Yes, I deagree.
> > I don't think it is a good idea to offer backdoors like inline
> > assembler.
>
> > - Programs with inline assembler are not portable. In the internet
> > you can find "Pascal" programs which are 99% assembler with
> > additional 1% Pascal function/procedure headers. If you are
> > looking for a high level algorithm you are probably not happy with
> > such crap.
>
> As any tool, it can be abused. Just like people can use Duff's device to
> manually unroll loops in C. You will also find that on the net.
>
> Premature optimization is premature optimization, in HLL or assembler. But
> it is not a reason to ban all tools or constructs that could be abused for
> this.

Ok. But one line of assembler makes a program unportable. Inline
assembler forces you to rewrite parts of your program when it is
ported.

> > - Language implementations which advertise inline assembler often
> > try to cover shortcomings (e.g. performance or access to external
> > resources) of the language or implementation. So instead of
> > improving language or implementation they offer a backdoor.
>
> Examples?

IIRC UCSD Pascal supported assembler functions/procedures to
compensate for the slow interpretation of p-code. Providing some
support for assembler (or C) is a common strategy to improve the
performance of interpreted languages. I don't want to accuse
languages or implementations. I just want to point out that inline
assembler is also used for performance reasons and that some effort
could be used to improve the performance of the main language
instead.

> It can also simply be because the runtime is entirely written in
> the language. If your lowlevel runtime is in a different language, you need
> much less assembler.
>
> Also it is a bit unfair to point to TP code for your point. It on average
> two decades old and frozen code.

Actually it is not TP code it is assembler which pretends to be
Pascal. If it would be real (portable) Pascal code it would be
usable today without problems. This is also the reason why I see
portable code as much more valuable than unportable code.

> > - What once was a good idea to improve performance can now be the
> > stupiest thing to do. Inline assembler that once offered a
> > performance improvement over high level language code can be
> > slower on newer processors and newer compilers where compiled
> > high language language code uses additional registers and modern
> > machine instructions.
>
> In general yes, but the assembler is there for the specific cases. I'm a
> fulltime Delphi devel, and _all_ of my inline code on the job is SSE2.

Currently your SSE2 code improves performance. In the future it can
happen that equivalent compiled Pascal/Delphi code uses the SSE8
units that provide much better performance. Then your SSE2 inline
code would actually be slower.

> > - There are other processor architectures beside x86 which are
> > probably excluded with x86 inline assembler programs.
>
> That's why we also sport an x86_64,ppc,ppc64,arm (several flavours),sparc
> and sparc64 assembler. Because the lowlevel parts of the program and RTL
> must also be done in assembler for those parts.

So portable programs need to provide code for all this assemblers?

> > It is really not a good idea when a language encourages writing
> > unportable programs. Instead of providing backdoors the language
> > implementers should find out where programmers would consider using
> > such backdoors and improve this areas.
>
> Encourage and enable are two entirely different things.

Yes, but my view is different.
I want to find out exactly for which problems people consider
backdoors such as inline assembler. Then I want to examine this
problems one by one to see if it is possible to provide a
portable way to solve the problem. This way a language and its
library can be improved.

A historical portability problem not related to assembler:
In the old days positioning the cursor at a position in a terminal
was a highly unportable task. Some languages supported gotoxy and
similar functions, but different implementations supported
different flavours of this feature (E.g. Meaning of the two
parameters reversed or counting the lines form 0 or 1). Other
implementations did not support that feature at all. The operating
systems had also different solutions for this problem. Unix like
operating systms used termcap, terminfo or curses for this task
while dos/windows used bios, access to video memory, console module
or gdi. You can find whole books written about that problem.
Some with deep detail (to avoid flicker when you write into the
video memory you have to wait for a vertical retrace) (how to deal
with a terminal device to get it in a mode such that keys pressed
are not echoed in the terminal). For a long time such things were
considered low level and almost impossible to do in a portable
way. Driver librarys available on several operating systems and
language implementations solved the problem but it is still not
trivial: Try to write a portable program with cursor positioning
which works in a windows console and in an linux xterm (when
compiled on this two platforms from the same source code). The
Seed7 driver librarys make this possible. Instead of asking the
programmer to use operating system dependend features or external
librarys I decided to provide a solution in the Seed7 runtime
library.

Another areas where I tried to avoid unportable programs:

The strings are UTF-32 but when dealing with the operating system
they are automatically converted to the character encoding used
by the system calls (which is UTF-16 on windows and UTF-8 on
linux/unix/bsd).

So I am asking:
For which areas programmers consider backdoors and how could
this things be done in a portable way.

Marco van de Voort

unread,
Nov 19, 2009, 9:21:37 AM11/19/09
to
On 2009-11-19, tm <thomas...@gmx.at> wrote:
>> As any tool, it can be abused. Just like people can use Duff's device to
>> manually unroll loops in C. You will also find that on the net.
>>
>> Premature optimization is premature optimization, in HLL or assembler. But
>> it is not a reason to ban all tools or constructs that could be abused for
>> this.
>
> Ok. But one line of assembler makes a program unportable.

So makes one pointer to int cast.

>> Examples?
>
> IIRC UCSD Pascal supported assembler functions/procedures to
> compensate for the slow interpretation of p-code

Ok, maybe I should have said "in this century".

> Providing some support for assembler (or C) is a common strategy to
> improve the performance of interpreted languages. I don't want to accuse
> languages or implementations. I just want to point out that inline
> assembler is also used for performance reasons and that some effort could
> be used to improve the performance of the main language instead.

That is probably not possible, since that would mean dragging the HLL down.
Usually such lowlevel use is limited to a few lowlevel modules.

That is more or less the runtime library argument I already mentioned.

>> Also it is a bit unfair to point to TP code for your point. It on average
>> two decades old and frozen code.
>
> Actually it is not TP code it is assembler which pretends to be
> Pascal. If it would be real (portable) Pascal code it would be
> usable today without problems. This is also the reason why I see
> portable code as much more valuable than unportable code.

(probably not, the avg TP code is stuffed top till bottom with dosism and
16-bitisms).

But IMHO you simply refuse to acknowledge that their might have been a
reason then to do this. Maybe not, but also maybe it did.

>> In general yes, but the assembler is there for the specific cases. I'm a
>> fulltime Delphi devel, and _all_ of my inline code on the job is SSE2.
>
> Currently your SSE2 code improves performance. In the future it can
> happen that equivalent compiled Pascal/Delphi code uses the SSE8
> units that provide much better performance. Then your SSE2 inline
> code would actually be slower.

SSE is not like normal x86. Vectorization and intrinsics are still no match
for handcoded assembler, although there are a few rare exceptions. Moreover
the corresponding C code (like icc) doesn't look like a naieve
implementation, but is specially crafted.

As far as I know there is no tool that does this automatically based on
naieve code. Not even experimental. So I don't expect this to change in the
forseeable future.

>> That's why we also sport an x86_64,ppc,ppc64,arm (several flavours),sparc
>> and sparc64 assembler. Because the lowlevel parts of the program and RTL
>> must also be done in assembler for those parts.
>
> So portable programs need to provide code for all this assemblers?

If it is important enough for them to use assembler, and they really want to
support all architectures: yes, why not. Who am I to second guess their
motives?

They might even have to add a couple more versions, because ABIs and
alignment rules for x86 vary wildly.

>> > It is really not a good idea when a language encourages writing
>> > unportable programs. Instead of providing backdoors the language
>> > implementers should find out where programmers would consider using
>> > such backdoors and improve this areas.
>>
>> Encourage and enable are two entirely different things.
>
> Yes, but my view is different.
> I want to find out exactly for which problems people consider
> backdoors such as inline assembler

> Then I want to examine this problems one by one to see if it is possible


> to provide a portable way to solve the problem. This way a language and
> its library can be improved.

That's a self furfilling prophecy. Because the people see it can't be done
sanely, and have to wait for a solution before actually starting, they
simply go to the next. So you don't get many signals.

I gave up the idea that I can foresee every potential use and canalize it
every effort exactly how I want years ago. It's a bit a God complex thing,
and it leaves you with a few cheerleaders, but very little general use.

> For a long time such things were considered low level and almost
> impossible to do in a portable way. Driver librarys available on several
> operating systems and language implementations solved the problem but it
> is still not trivial: Try to write a portable program with cursor
> positioning which works in a windows console and in an linux xterm (when
> compiled on this two platforms from the same source code).

Note that aside from this, direct screenwrites were simply absolutely
necessary on a 386.

> The Seed7 driver librarys make this possible. Instead of asking the
> programmer to use operating system dependend features or external librarys
> I decided to provide a solution in the Seed7 runtime library.

FPC does too, but that is because we hope that our implementation covers 90%
of the suitable tasks. For the rest, users can still do it themselves.

In other words, have a standarized interface for it is no reason to disallow
external access. People are fallable, and simply cannot forsee all uses and
tradeoffs.

> Another areas where I tried to avoid unportable programs:
>
> The strings are UTF-32 but when dealing with the operating system
> they are automatically converted to the character encoding used
> by the system calls (which is UTF-16 on windows and UTF-8 on
> linux/unix/bsd).

And I pity the poor chap that has to regularly stuff a few tens of GBs of
database exports through it. Not even that odd-ball scenario btw.

> So I am asking:
> For which areas programmers consider backdoors and how could
> this things be done in a portable way.

For all areas, since the language maker is simply not omnipotent.

It also keeps the generic interfaces straight, since you don't have to
accomodate people with real strange demands anymore.

And note that the FPC assembler is not a backdoor. The compiler itself does
the complete validation, it is not a copy-and-paste to a backend.

tm

unread,
Nov 19, 2009, 10:04:55 AM11/19/09
to
On 19 Nov., 12:35, "bartc" <ba...@freeuk.com> wrote:
> "tm" <thomas.mer...@gmx.at> wrote in message

>
> news:79019631-359d-4cae...@m38g2000yqd.googlegroups.com...
>
> > On 18 Nov., 20:07, James Harris <james.harri...@googlemail.com> wrote:
> >> It is generally desirable for a high level language to be independent
> >> of any target hardware but ISTM that sometimes it is useful to write
> >> system-specific modules. For example, these could be specific to a CPU
> >> or to a hardware device. Any disagreement on this?
>
> > Yes, I deagree.
> > I don't think it is a good idea to offer backdoors like inline
> > assembler.
>
> > - Programs with inline assembler are not portable. In the internet
> > you can find "Pascal" programs which are 99% assembler with
> > additional 1% Pascal function/procedure headers. If you are
> > looking for a high level algorithm you are probably not happy with
> > such crap.
>
> If there is a genuine need for assembler, isn't it much better to make it
> available within the discipline of a high-level language?

Why is there a need for assembler?
I want to know about each and every problem where people think they
need assembler and where they think something cannot be solved in a
high level language. If it is for performance reasons I want to know
which language feature is considered slow.

> So you can make
> full use of function blocks, scoping, declarations, control structures,...
> It's possible to give some 'shape' to a program instead of it being just a
> linear sequence of instructions.
>
> (Anyway I've seen plenty of code, especially C, which is pretty much
> impossible to compile anywhere else even when there is no assembler
> involved.)

Let me guess: Such code calls functions of the operating system.

> > - Language implementations which advertise inline assembler often
> > try to cover shortcomings (e.g. performance or access to external
> > resources) of the language or implementation. So instead of
> > improving language or implementation they offer a backdoor.
>
> What's wrong with improving performance?

Nothing, but it leads to the question:
Why is the original language slow?

> I use inline assembler to write
> fast interpreters. Once done, I can then write all my code at a higher level
> than the language used to implement the interpreter. But that assembler code
> is a one-off. It means I can get, say, double the speed, of my higher level
> code.

Compiling your high level language would probably even more than
double the speed.

> > - What once was a good idea to improve performance can now be the
> > stupiest thing to do. Inline assembler that once offered a
> > performance improvement over high level language code can be
> > slower on newer processors and newer compilers where compiled
> > high language language code uses additional registers and modern
> > machine instructions.
>
> > - There are other processor architectures beside x86 which are
> > probably excluded with x86 inline assembler programs.
>
> I don't think they are excluded at all. It is possible to take a specific
> function, and code both in high-level language, and assembler, and use
> mechanisms (eg. commenting) to make either available. In a similar way, you
> can add more than one assembler version. Using a dedicated assembler makes
> this more awkward.
>
> When the assembler code causes problems, you switch to the high-level code,
> at a cost perhaps of performance. Or, if what you say about newer processors
> is correct, you will get better performance. But usually there was a good
> reason to use the assembler in the first place (since it is such a PITA to
> write -- and maintain -- you don't use it unless you have to).

Agree. But maintaining several sources in different languages which
do the same thing is not easy.

You seem to need assembler "just" for performance reasons especially
in your interpreter(s). Is this correct?

> > It is really not a good idea when a language encourages writing
> > unportable programs. Instead of providing backdoors the language
> > implementers should find out where programmers would consider using
> > such backdoors and improve this areas.
>
> > That said, I would like to know which areas are considered
> > weaknesses of Seed7.
>
> No 'goto' and (I'm guessing) no inline assembler..?

Really nothing else is missing?

1. goto statement:
Seed7 is not the only language without 'goto'. Java also has no
'goto'. So Seed7 is in good company. Exceptions can (to some
degree) be used as replacement for goto statements. If you want
to use gotos to emulate special statements like loops with middle
exit Seed7 can help by defining such a statement. But I think
your desire for 'goto' goes beyond such uses and you want to
improve the performance by using gotos. I am sorry to say
that this is not in the scope of Seed7. BTW: I think that the
performance win by using compiled vs. interpreted programs is
much bigger than what can be gained by using gotos.

2. Inline assembler for performance reasons:
To account for performance I try to compile Seed7 programs to
C (which itself compiles to machine code). So instead of an
interpreter which is partly written in assembler I lean on
machine code without interpretation.

Are there other things where you think you need a backdoor?

Marco van de Voort

unread,
Nov 19, 2009, 10:58:44 AM11/19/09
to
On 2009-11-19, tm <thomas...@gmx.at> wrote:
>> > such crap.
>>
>> If there is a genuine need for assembler, isn't it much better to make it
>> available within the discipline of a high-level language?
>
> Why is there a need for assembler?
> I want to know about each and every problem where people think they
> need assembler and where they think something cannot be solved in a
> high level language. If it is for performance reasons I want to know
> which language feature is considered slow.

These are slower than assembler in Pascal/Delphi, and thus in all languages:

- move a block of memory
- fill a block of memory (byte/word/dword/qword)
- search for a value in a block of memory (byte/word/dword/qword)
- Compare a range of memory (byte/word/dword/qword)
- int64/qword language helpers
- (>32-bit elements) set language helpers
- Two hands full of the deepest string primitives. While maybe these
could be expressed in other primitives, coding these out, and
with certain assumptions about used registers is notacibly faster.
- mpi routines. Every cycle the loop is shorter is noticable
- most math primitives, due to setting copro mask inline and using FPU
instructions.

Some stuff is only doable in assembler, because they e.g. break the
procedural regime or touch registers and instructions not modeled by the
language:

- most math primitives, due to setting copro mask inline and using FPU
instructions.
- exception helpers
- Change a coprocessor exceptions mask. Switch to MMX mode etc
- check cpu version and capabilityies
- spinlock primitives, CAS etc.

tm

unread,
Nov 19, 2009, 11:30:48 AM11/19/09
to
On 19 Nov., 15:21, Marco van de Voort <mar...@stack.nl> wrote:

> On 2009-11-19, tm <thomas.mer...@gmx.at> wrote:
>
> >> As any tool, it can be abused. Just like people can use Duff's device to
> >> manually unroll loops in C. You will also find that on the net.
>
> >> Premature optimization is premature optimization, in HLL or assembler. But
> >> it is not a reason to ban all tools or constructs that could be abused for
> >> this.
>
> > Ok. But one line of assembler makes a program unportable.
>
> So makes one pointer to int cast.

I know of "pointer to int casts" which don't make a program
unportable. In general I don't encourage any unportable programming
technic.

> >> Examples?
>
> > IIRC UCSD Pascal supported assembler functions/procedures to
> > compensate for the slow interpretation of p-code
>
> Ok, maybe I should have said "in this century".

:-)

> > Providing some support for assembler (or C) is a common strategy to
> > improve the performance of interpreted languages. I don't want to accuse
> > languages or implementations. I just want to point out that inline
> > assembler is also used for performance reasons and that some effort could
> > be used to improve the performance of the main language instead.
>
> That is probably not possible, since that would mean dragging the HLL down.
> Usually such lowlevel use is limited to a few lowlevel modules.
>
> That is more or less the runtime library argument I already mentioned.

Well, the runtime library could provide a portable support for some
lowlevel features.

> >> Also it is a bit unfair to point to TP code for your point. It on average
> >> two decades old and frozen code.
>
> > Actually it is not TP code it is assembler which pretends to be
> > Pascal. If it would be real (portable) Pascal code it would be
> > usable today without problems. This is also the reason why I see
> > portable code as much more valuable than unportable code.
>
> (probably not, the avg TP code is stuffed top till bottom with dosism and
> 16-bitisms).

Dosism and 16-bitisms are another example of how to write unportable
programs. Todays programs use Windowsisms and Linuxisms instead.
Sadly operating system dependend functions are often preferred over
operating system independend functions.

> But IMHO you simply refuse to acknowledge that their might have been a
> reason then to do this.

No, No I believe that there is often a reason to do this. As
language implementer I have just a different view. I want to know
about such cases long before someone actually has the problem. This
way I can think about an alternate portable way to solve the issue.
Therefore I need to know the details of a certain case. With a
little luck somebody can provide a portable solution before someone
hits the problem.

> Maybe not, but also maybe it did.
>
> >> In general yes, but the assembler is there for the specific cases. I'm a
> >> fulltime Delphi devel, and _all_ of my inline code on the job is SSE2.
>
> > Currently your SSE2 code improves performance. In the future it can
> > happen that equivalent compiled Pascal/Delphi code uses the SSE8
> > units that provide much better performance. Then your SSE2 inline
> > code would actually be slower.
>
> SSE is not like normal x86. Vectorization and intrinsics are still no match
> for handcoded assembler, although there are a few rare exceptions. Moreover
> the corresponding C code (like icc) doesn't look like a naieve
> implementation, but is specially crafted.
>
> As far as I know there is no tool that does this automatically based on
> naieve code. Not even experimental. So I don't expect this to change in the
> forseeable future.

My point was: Hand crafted assembler code might be slower than high
level language code compiled with future compilers on future
processors.

> >> That's why we also sport an x86_64,ppc,ppc64,arm (several flavours),sparc
> >> and sparc64 assembler. Because the lowlevel parts of the program and RTL
> >> must also be done in assembler for those parts.
>
> > So portable programs need to provide code for all this assemblers?
>
> If it is important enough for them to use assembler, and they really want to
> support all architectures: yes, why not. Who am I to second guess their
> motives?
>
> They might even have to add a couple more versions, because ABIs and
> alignment rules for x86 vary wildly.
>
> >> > It is really not a good idea when a language encourages writing
> >> > unportable programs. Instead of providing backdoors the language
> >> > implementers should find out where programmers would consider using
> >> > such backdoors and improve this areas.
>
> >> Encourage and enable are two entirely different things.
>
> > Yes, but my view is different.
> > I want to find out exactly for which problems people consider
> > backdoors such as inline assembler
> > Then I want to examine this problems one by one to see if it is possible
> > to provide a portable way to solve the problem. This way a language and
> > its library can be improved.
>
> That's a self furfilling prophecy. Because the people see it can't be done
> sanely, and have to wait for a solution before actually starting, they
> simply go to the next. So you don't get many signals.

We are talking about theoretical cases here. This implies there is
enough time to think for a good and portable solution. Seed7
provides also a solution for the emergency need you are talking
about: It is possible to introduce primitive actions (written in C).
BTW: I see "inline assembler" and sanely as contradiction in terms.

> I gave up the idea that I can foresee every potential use and canalize it
> every effort exactly how I want years ago. It's a bit a God complex thing,
> and it leaves you with a few cheerleaders, but very little general use.

:-)

> > For a long time such things were considered low level and almost
> > impossible to do in a portable way. Driver librarys available on several
> > operating systems and language implementations solved the problem but it
> > is still not trivial: Try to write a portable program with cursor
> > positioning which works in a windows console and in an linux xterm (when
> > compiled on this two platforms from the same source code).
>
> Note that aside from this, direct screenwrites were simply absolutely
> necessary on a 386.
>
> > The Seed7 driver librarys make this possible. Instead of asking the
> > programmer to use operating system dependend features or external librarys
> > I decided to provide a solution in the Seed7 runtime library.
>
> FPC does too, but that is because we hope that our implementation covers 90%
> of the suitable tasks. For the rest, users can still do it themselves.
>
> In other words, have a standarized interface for it is no reason to disallow
> external access. People are fallable, and simply cannot forsee all uses and
> tradeoffs.
>
> > Another areas where I tried to avoid unportable programs:
>
> > The strings are UTF-32 but when dealing with the operating system
> > they are automatically converted to the character encoding used
> > by the system calls (which is UTF-16 on windows and UTF-8 on
> > linux/unix/bsd).
>
> And I pity the poor chap that has to regularly stuff a few tens of GBs of
> database exports through it. Not even that odd-ball scenario btw.

Are you referring to the CPU time necessary to convert data when it
is read or written? For huge amounts of data coming from a DB or
file the harddisk performance is normally the bottleneck and the CPU
has enough time to do some conversions. At least my hard disk repair
program (see http://seed7.sourceforge.net/scrshots/savehd7.htm) has
no performance problems. There is also a low level byte string, but
until now I did not see any performance requirements which made it
necessary to use it. So I prefer to use the Unicode strings for
all things. If you just want to do something with files: The Seed7
library provides functions to handle files (e.g. copy or move files
or directory trees or do other stuff).

Or do you think that Seed7 just handles UTF-32 files and therefore
expect huge files? This is not the case. Seed7 can handle Byte,
UTF-8, UTF-16(BE & LE) and many more files.

> > So I am asking:
> > For which areas programmers consider backdoors and how could
> > this things be done in a portable way.
>
> For all areas, since the language maker is simply not omnipotent.

Really? :-)

bartc

unread,
Nov 19, 2009, 11:31:49 AM11/19/09
to

"tm" <thomas...@gmx.at> wrote in message
news:94aa3725-cc0c-4172...@x31g2000yqx.googlegroups.com...

> On 19 Nov., 12:35, "bartc" <ba...@freeuk.com> wrote:

>> impossible to compile anywhere else even when there is no assembler
>> involved.)
>
> Let me guess: Such code calls functions of the operating system.

No, just using pragmas/extensions/etc highly specific to a compiler. But it
is possible to reduce portability quite well without using assembler at all.

>> > - Language implementations which advertise inline assembler often
>> > try to cover shortcomings (e.g. performance or access to external
>> > resources) of the language or implementation. So instead of
>> > improving language or implementation they offer a backdoor.
>>
>> What's wrong with improving performance?
>
> Nothing, but it leads to the question:
> Why is the original language slow?

In my case, I use my own language/compiler, and never got round to
optimising it. And I could never beat the big boys anyway.

I was looking at a floating point benchmark recently. My (compiled) language
took 2.7 seconds (My code is hopless at FPU work.) The best optimised C+gcc
took 0.7 seconds. But a couple of minutes adding 10 lines of inline
assembler, and my code took 0.6 seconds!

>
>> I use inline assembler to write
>> fast interpreters. Once done, I can then write all my code at a higher
>> level
>> than the language used to implement the interpreter. But that assembler
>> code
>> is a one-off. It means I can get, say, double the speed, of my higher
>> level
>> code.
>
> Compiling your high level language would probably even more than
> double the speed.

So many responses to that... assuming there is enough benefit to an
interpreted, dynamic, spontaneous, bytecoded language to warrant using it,
then what's wrong with wanting the best possible performance out of it?

A couple of years ago I believed enough hype about super-optimising C
compilers beating hand-coded assembler, that I rewrote the core of my
interpreter in pure C. It slowed down by 2-3 times. Since then I've speeded
up, by various means, by 2-3 times, thanks to a tight assembler core.

Anyway compiling code to native binary would surely render it less portable?
Bytecode is far more portable: you just need to write an interpreter for
each platform (a few 10Kloc), and suddenly millions of lines of code work on
that platform without recompiling.

(BTW my interpreted language at present is 3-10x as slow as optimised C, and
there's still plenty of scope for improvement, while still having
interpreted bytecode. (This is for low-level integer code of course.) Beyond
that will involve semi-compiling techniques. Traditional compiling will not
work as well because of the dynamic nature of the language.)

>> No 'goto' and (I'm guessing) no inline assembler..?
>
> Really nothing else is missing?
>
> 1. goto statement:
> Seed7 is not the only language without 'goto'. Java also has no
> 'goto'. So Seed7 is in good company. Exceptions can (to some

Yes I know. Language purists seem to like stripping away more and more
features. Some languages don't even have variables apparently. Python
doesn't have 'switch' (meaning a mess of code instead). 'Go' doesn't have
do-while now. Etc.

--
Bartc

Rod Pemberton

unread,
Nov 19, 2009, 7:56:46 PM11/19/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:Z3aNm.6631$Ym4....@text.news.virginmedia.com...

> > "tm" <thomas...@gmx.at> wrote in message
> > [...]

>
> (Anyway I've seen plenty of code, especially C, which is pretty much
> impossible to compile anywhere else even when there is no assembler
> involved.)
>

Yep. But, try telling that to the guys on comp.lang.c. Try telling them C
compilers still exist which still don't support unions, enums, or properly
implement bitfields. Try posting K&R code. Try mentioning that people
still use Small C (no structures, char and int, no floats, single dimension
arrays, no do-while, no unstructured switches etc.). Good luck!

> What's wrong with improving performance?

Nothing. But, it's non-portable. If it's faster with GCC -O2, it's not
with GCC -O0. If it's faster with OpenWatcom, it's slower with GCC. If
it's faster for 32-bit x86, it'll be slower for 16-bit or 64-bit x86. Etc.
And, that's only on an x86 platform... You might as well forget about
optimizing HLL, like C, other cpu's, like ARM. (I know that belief goes
against c.l.c. mythology too... But, just how is one supposed to write
"portable" C code for a Cray or EBCDIC when one has never used them? Please
don't mention a C spec. It's useless here.)

How would you recommend coding a bytecode interpreter that works for 16-bit,
32-bit, and 64-bit x86?

For assembly, I'd choose intructions that are the same for all three modes.
But, therein lies a problem. There is no common addressing method for all
three modes. I did find a "solution", if you can call it that, for 16-bit
and 32-bit code. (Middle of this post)
http://groups.google.com/group/comp.lang.asm.x86/msg/ce546dada8708c27?hl=en

For C, I can usually get OpenWatcom to emit almost identical code to what
I've handcoded both 16-bit and 32-bit. I could code smaller 16-bit code in
assembly using single byte instructions, but those instructions use a longer
decode sequence and are slower. I can usually come real close my assembly
with 3.x series of GCC with -O2. It's hard to get it to emit byte
instructions and eliminate sizing instructions, e.g., and $255, etc. But,
from what I've seen of the 4.x series, it just generates poor x86 code.

> I use inline assembler to write
> fast interpreters.

I'm not against interpreters. I'm working on one for FORTH and I'm
considering another for C or C-like language, so I can continue development
of my OS and other personal tools without needing new 64-bit compilers, etc.
But, *please* don't use "fast" and "interpreter" in the same sentence. The
term "fast" is too vague and relative. IMO and experience, interpreters are
*never* fast. You can do every trick in the book, e.g., those by Anton Ertl
for FORTH ("super-instructions") or keeping the entire interpreter in cache
to minimize cache reloads etc., and you still won't have a "fast"
interpreter. Interpreters always reimplement native hardware functionality
that must be emulated in software. So, they are always much slower than
native code.

> > - There are other processor architectures beside x86 which are
> > probably excluded with x86 inline assembler programs.
>
> I don't think they are excluded at all. It is possible to take a specific
> function, and code both in high-level language, and assembler, and use
> mechanisms (eg. commenting) to make either available.

Fallback.

> When the assembler code causes problems, you switch to the high-level
code,
> at a cost perhaps of performance.

No, I use C first. I don't have to track variables by location: memory,
register, or stack, but can just use a name. However, I do like the
type-less, everything is an integer, design of most assembly languages.
Unfortunately, mimicking assembly in C means casts.


Rod Pemberton

James Harris

unread,
Nov 20, 2009, 3:10:25 AM11/20/09
to
On 19 Nov, 15:04, tm <thomas.mer...@gmx.at> wrote:
> On 19 Nov., 12:35, "bartc" <ba...@freeuk.com> wrote:
> > "tm" <thomas.mer...@gmx.at> wrote in message

...

> > > - Programs with inline assembler are not portable. In the internet
> > >  you can find "Pascal" programs which are 99% assembler with
> > >  additional 1% Pascal function/procedure headers. If you are
> > >  looking for a high level algorithm you are probably not happy with
> > >  such crap.
>
> > If there is a genuine need for assembler, isn't it much better to make it
> > available within the discipline of a high-level language?
>
> Why is there a need for assembler?
> I want to know about each and every problem where people think they
> need assembler and where they think something cannot be solved in a
> high level language.

Here are some potential problems if you want cases to cover:

1. To generate x86 real-mode code.

2. To make BIOS calls.

3. To carry out x86 privileged protected mode operations like loading
the global descriptor table register.

James

James Harris

unread,
Nov 20, 2009, 3:57:49 AM11/20/09
to
On 19 Nov, 08:38, tm <thomas.mer...@gmx.at> wrote:
> On 18 Nov., 20:07, James Harris <james.harri...@googlemail.com> wrote:
>
> > It is generally desirable for a high level language to be independent
> > of any target hardware but ISTM that sometimes it is useful to write
> > system-specific modules. For example, these could be specific to a CPU
> > or to a hardware device. Any disagreement on this?
>
> Yes, I deagree.
> I don't think it is a good idea to offer backdoors like inline
> assembler.

If the high level language design is good there won't be a need to use
assembler as a "backdoor."

>
> - Programs with inline assembler are not portable.

Sure, but also some programs are not portable by design. If you were
writing a program to determine the execution time of certain MIPS
instructions it would run on a MIPS CPU wouldn't it? You couldn't run
that on an x86. If you were writing a program to interact with a
particular device controller you would have to run on a machine which
included that device. There's no way that program can be "portably"
run on a machine which did not have the intended hardware.

...

> It is really not a good idea when a language encourages writing
> unportable programs. Instead of providing backdoors the language
> implementers should find out where programmers would consider using
> such backdoors and improve this areas.

Of course the language should not have flaws which encourage non-
portability. Being able to write programs which are portable is
*highly* desirable.

What is useful, though, is not to avoid but to manage the differences
between execution environments. A language can be a big help with
that.

James

bartc

unread,
Nov 20, 2009, 6:53:27 AM11/20/09
to

"Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
news:he4pjc$9us$1...@aioe.org...

> "bartc" <ba...@freeuk.com> wrote in message
> news:Z3aNm.6631$Ym4....@text.news.virginmedia.com...

> How would you recommend coding a bytecode interpreter that works for

> 16-bit,
> 32-bit, and 64-bit x86?

What, all together in the same program?

I have would separate interpreters. Preferably written ten years apart (eg.
1989, 1999 and 2009).

I don't see a problem with different interpreters for each
platform/architecture.

>> I use inline assembler to write
>> fast interpreters.

> But, *please* don't use "fast" and "interpreter" in the same sentence.

> The
> term "fast" is too vague and relative. IMO and experience, interpreters
> are
> *never* fast. You can do every trick in the book, e.g., those by Anton
> Ertl
> for FORTH ("super-instructions") or keeping the entire interpreter in
> cache
> to minimize cache reloads etc., and you still won't have a "fast"
> interpreter. Interpreters always reimplement native hardware
> functionality
> that must be emulated in software. So, they are always much slower than
> native code.

Ok, let's say /faster/ interpreters.

When I started using interpreted languages, I decided they would be used
sensibly when the execution time was between 1 and 2 times that of normal,
compiled/native code (because of the mix of code). By making them faster, I
could increase the range of programs that would satisfy that rule. (And when
total execution is a few seconds anyway, it might not matter.)

--
bartc


stan

unread,
Nov 20, 2009, 3:31:39 PM11/20/09
to
tm wrote:
> On 19 Nov., 12:35, "bartc" <ba...@freeuk.com> wrote:
>> "tm" <thomas.mer...@gmx.at> wrote in message
>>
>> news:79019631-359d-4cae...@m38g2000yqd.googlegroups.com...
>>
>> > On 18 Nov., 20:07, James Harris <james.harri...@googlemail.com> wrote:
>> >> It is generally desirable for a high level language to be independent
>> >> of any target hardware but ISTM that sometimes it is useful to write
>> >> system-specific modules. For example, these could be specific to a CPU
>> >> or to a hardware device. Any disagreement on this?
>>
>> > Yes, I deagree.
>> > I don't think it is a good idea to offer backdoors like inline
>> > assembler.
>>
>> > - Programs with inline assembler are not portable. In the internet
>> > you can find "Pascal" programs which are 99% assembler with
>> > additional 1% Pascal function/procedure headers. If you are
>> > looking for a high level algorithm you are probably not happy with
>> > such crap.
>>
>> If there is a genuine need for assembler, isn't it much better to make it
>> available within the discipline of a high-level language?
>
> Why is there a need for assembler?
> I want to know about each and every problem where people think they
> need assembler and where they think something cannot be solved in a
> high level language. If it is for performance reasons I want to know
> which language feature is considered slow.

Inline assembly is used to solve a deficiency in the hll, so it seems
prudent to simply fix the hll deficiency.

These deficiencies are nearly always platform specific issues. The two
most common answers are to provide inline assembly or platform
specific libraries to extend the hll capabilities to meet the original
need. Either way you face platform specific issues and solutions are
not portable.

In some cases the platform specific libraries can represent some
generalized abstraction that is useful and many platform specific
solutions can be crafted to the library interface. In other cases the
problem is platform unique and portability is nonsense.

Considering inline assembly in the design of a hll seems like a
solution looking for a problem.

>> So you can make
>> full use of function blocks, scoping, declarations, control structures,...
>> It's possible to give some 'shape' to a program instead of it being just a
>> linear sequence of instructions.
>>
>> (Anyway I've seen plenty of code, especially C, which is pretty much
>> impossible to compile anywhere else even when there is no assembler
>> involved.)
>
> Let me guess: Such code calls functions of the operating system.
>
>> > - Language implementations which advertise inline assembler often
>> > try to cover shortcomings (e.g. performance or access to external
>> > resources) of the language or implementation. So instead of
>> > improving language or implementation they offer a backdoor.
>>
>> What's wrong with improving performance?
>
> Nothing, but it leads to the question:
> Why is the original language slow?
>
>> I use inline assembler to write
>> fast interpreters. Once done, I can then write all my code at a higher level
>> than the language used to implement the interpreter. But that assembler code
>> is a one-off. It means I can get, say, double the speed, of my higher level
>> code.

Nearly all performance problems are best met with better
algorithms. With modern processor architectures, high performance
assembly requires non-intuitive tuning and deep understanding of
caches and pipe-lining. Studying algorithms is probably easier and
clearly more portable knowledge.

The holy grail would be a language that allows easy development of
algorithms and are clearly and provably correct and efficient.

There has been a lot of work done to understand correctness issues but
not that much on efficiency. Maybe languages should be adding more
and better support to profile and benchmark programs expressed in that
language. Existing tools are external and nearly always an
afterthought. Maybe some help to control the granularity of profiling
and timing could be designed in to the language. Understanding a
problem is always the first step towards a solution.

Marco van de Voort

unread,
Nov 20, 2009, 5:44:40 PM11/20/09
to
On 2009-11-20, stan <smo...@exis.net> wrote:
> Nearly all performance problems are best met with better
> algorithms. With modern processor architectures, high performance
> assembly requires non-intuitive tuning and deep understanding of
> caches and pipe-lining. Studying algorithms is probably easier and
> clearly more portable knowledge.

Is it? Sure it is a traditional textbook truth, but border conditions are
rarely given. Most likely they are given infinite resources and large enough
N.

But what in case you have limited resources, and a fixed magnitude N (say
the one you design for, and a ten or hundred fold scaling limit)?

Rod Pemberton

unread,
Nov 20, 2009, 5:49:46 PM11/20/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:XqvNm.6933$Ym4....@text.news.virginmedia.com...

>
> When I started using interpreted languages, I decided they would be used
> sensibly when the execution time was between 1 and 2 times that of normal,
> compiled/native code (because of the mix of code). By making them faster,
I
> could increase the range of programs that would satisfy that rule.
>

If you manage to come close to that, you probably should publish an academic
paper, even if not an academic. Heck, if your claim elsewhere of "3-10x as
slow as optimised C" is true, you might want to publish now. That seems
exceptional to me.


Rod Pemberton


Rod Pemberton

unread,
Nov 20, 2009, 5:50:22 PM11/20/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:XqvNm.6933$Ym4....@text.news.virginmedia.com...

> "Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
> news:he4pjc$9us$1...@aioe.org...
> >
> > How would you recommend coding a bytecode interpreter that works for
> > 16-bit, 32-bit, and 64-bit x86?
>
> What, all together in the same program?

Either way.

> What, all together in the same program?

Sure, why not? It may be slow, but it could be useful for bootstrapping an
x86 OS independent of cpu mode. To fully implement useable subset of x86
instructions, one needs:

1) math operations on one of: register, stack, or memory
2) ability to move data between register and stack
3) (optionally) ability to move data between registers
4) (optionally) ability to move data between register and memory
5) ability to load constants

IIRC, the x86 has enough older single byte instructions, whose encodings are
identical in all three modes, to do that. The real problem is memory
addressing, and/or loading of constants. That was why I pointed to the
16-bit/32-bit partial solution in the middle of this thread:
http://groups.google.com/group/comp.lang.asm.x86/msg/ce546dada8708c27?hl=en


Rod Pemberton
PS. Added a.o.d. and a.l.a, since they said they were bored...


Rod Pemberton

unread,
Nov 20, 2009, 5:52:04 PM11/20/09
to
"James Harris" <james.h...@googlemail.com> wrote in message
news:dd46104a-a95a-41b0...@k4g2000yqb.googlegroups.com...

> On 19 Nov, 08:38, tm <thomas.mer...@gmx.at> wrote:
> >
> > I don't think it is a good idea to offer backdoors like inline
> > assembler.
>
> If the high level language design is good there won't be a need to use
> assembler as a "backdoor."

From what I remember of Pascal in the '80's, I was locked into a restricted
"box" - the limited capabilities of the HLL - and couldn't do anything
outside the box (heh...), i.e., OS related. I couldn't use the OS or the
OS' CLI from my program. That was the first time I *really* wanted to use
assembly within a HLL, but couldn't. Even BASIC of that era on a number of
platforms had the ability to use assembly, although usually as some
combination of stored data and special commands. FORTRAN of the '80's was
the second time I *really* wante to use assembly, not just within an HLL,
but to eliminate FORTRAN itself from the face of the Earth...

> assembler as a "backdoor."

Well, I've only used one HLL language where you (almost) don't have to use
assembly as a "backdoor". That's C. For applications, you don't need
assembly in C at all. It's only for low-level stuff (an OS, device drivers,
etc.) that you may need some assembly.

> If you were writing a program to interact with a
> particular device controller you would have to run on a machine which
> included that device. There's no way that program can be "portably"
> run on a machine which did not have the intended hardware.

It seems to me everyone is bouncing around the definition of an
"application" or a "program" as coded in a HLL. Just what exactly should a
HLL be capable of coding? That's what needs to be defined. Is the OS
itself considered an application or program? Is a device driver considered
an application or program? In all but one of the HLLs I've used, the answer
for both is "No.". In those HLLs, an "application" or "program" is code
that executes separately from the OS and it's drivers. The HLL doesn't need
to, and usually isn't capable, of doing low-level programming. The
exception is for C language. (And, non-HLLs like assembly.) C supports
sufficient low-level functionality to program most of an OS with it. The
stuff C can't do can be hidden behind C functions, if inline assembly is
available.


Rod Pemberton


bartc

unread,
Nov 20, 2009, 6:51:07 PM11/20/09
to

"Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
news:he76h6$a4u$1...@aioe.org...

The 1-2x figure is easy, when the interpreted application has the right mix
of library calls (eg. graphics and files), and high-level data ops (eg.
strings, sets and lists). Going beyond 2x is possible if the response time
is still acceptable (eg. if something takes 50ms in C, but 200ms in
interpreted code..)

The 3-10x figure refers to simple benchmarks involving mainly integers and
floats. The 3x figure (ie. 3x as slow) is achieved on simple recursive
benchmarks, where C compilers seem to struggle a bit; maybe all those clever
register allocations have to be saved and restored on function calls.

But yes I seem to have come within a magnitude of the performance of C; at
least measured against gcc/mingw/3.4.5. There might be faster C compilers
out there (but then, some of the optimisations done by C compilers could
also be done in my bytecode compiler...)

BTW I don't think fast(ish) interpreters are unusual; I remember looking at
the superfast Euphoria language and wondering how they managed to get it so
quick, although the language has been kept simple.

--
Bartc

bartc

unread,
Nov 20, 2009, 6:58:16 PM11/20/09
to

"stan" <smo...@exis.net> wrote in message news:buajt6-...@invalid.net...

> Considering inline assembly in the design of a hll seems like a
> solution looking for a problem.

To me it's like allowing a 'goto' statement. Not recommended for general
use, but handy to get you out of trouble.

But, do you also have the same objection to non-inline assembly, ie. putting
assembly code in it's own module?

(It does a small advantage of separating out the high levels modules, so if
modules A,B are high level code, C is assembler. But then, you can also
place all the functions using inline assembly into module C, and now the
code exists within a high-level environment, is better integrated, probably
faster, and is easier to maintain.)

--
Bartc

tm

unread,
Nov 21, 2009, 5:23:47 AM11/21/09
to
On 19 Nov., 16:58, Marco van de Voort <mar...@stack.nl> wrote:

> On 2009-11-19, tm <thomas.mer...@gmx.at> wrote:
>
> >> > such crap.
>
> >> If there is a genuine need for assembler, isn't it much better to make it
> >> available within the discipline of a high-level language?
>
> > Why is there a need for assembler?
> > I want to know about each and every problem where people think they
> > need assembler and where they think something cannot be solved in a
> > high level language. If it is for performance reasons I want to know
> > which language feature is considered slow.
>
> These are slower than assembler in Pascal/Delphi, and thus in all languages:
>
> - move a block of memory
> - fill a block of memory (byte/word/dword/qword)
> - search for a value in a block of memory (byte/word/dword/qword)
> - Compare a range of memory (byte/word/dword/qword)
> - int64/qword language helpers
> - (>32-bit elements) set language helpers
> - Two hands full of the deepest string primitives. While maybe these
> could be expressed in other primitives, coding these out, and
> with certain assumptions about used registers is notacibly faster.
> - mpi routines. Every cycle the loop is shorter is noticable
> - most math primitives, due to setting copro mask inline and using FPU
> instructions.

Thank you for this information. I will look how compiled Seed7
handles such tasks and for ways to improve the performance.

As you probably know there are some well known rules concerning
performance optimisations:
- A program should be written without low level optimisations.
- The program should be tested to prove that it works correctly.
- Optimisations should only be considered when the program is too
slow.
- It should be tested if compiler optimisations flags can solve the
performance problem.
- In many cases it is cheaper to buy better hardware instead of
spending expensive developer time to improve the performance.
- Performance measurements should be used to determine the places
where optimisation makes sense.
- The use of smarter algorithms or data structures should be
considered.
- All tricks of the high level language should be used to improve
the performace.
- Assembler should only be considered if all other strategies fail.

I have seen many programs where unnecessary optimisations make a
program unportable and hard to maintain. There is a reason that
Donald Knuth made the following statement on optimization:

We should forget about small efficiencies, say about 97% of
the time: premature optimization is the root of all evil.

BTW: I am not sure that it can be deduced from the speed in
Pascal/Delphi that something is slower than assembler in all
languages. IIRC modern compilers like gcc generate inline code for
C functions like memcpy(), memset(), memchr() and memcmp(). The
compiler can customize such inline code when the size of the memory
area is known at compile time. That way SSE instructions or other
tricks may be used. That said I am neither an expert in such low
level C compiler optimisations nor an assembler expert.

> Some stuff is only doable in assembler, because they e.g. break the
> procedural regime or touch registers and instructions not modeled by the
> language:
>
> - most math primitives, due to setting copro mask inline and using FPU
> instructions.
> - exception helpers
> - Change a coprocessor exceptions mask. Switch to MMX mode etc
> - check cpu version and capabilityies
> - spinlock primitives, CAS etc.

Thank you. Exactly for such an answer I was looking for.
I am not sure that all will make sense for Seed7, but I will think
over your list.

What do you mean with "exception helpers"?

bartc

unread,
Nov 21, 2009, 6:22:01 AM11/21/09
to

"tm" <thomas...@gmx.at> wrote in message
news:8dd65d1c-8d93-4450...@p35g2000yqh.googlegroups.com...

> On 19 Nov., 16:58, Marco van de Voort <mar...@stack.nl> wrote:

> BTW: I am not sure that it can be deduced from the speed in
> Pascal/Delphi that something is slower than assembler in all
> languages. IIRC modern compilers like gcc generate inline code for
> C functions like memcpy(), memset(), memchr() and memcmp().

It doesn't always get it right. One gcc has an extremely fast strcmp()
function, when used with -O0, -O2 and -O3 optimisation. When used with -O1,
it's 300% slower. Looking at the code, it uses inline (but naive) coding
with -O1.

> Thank you. Exactly for such an answer I was looking for.
> I am not sure that all will make sense for Seed7, but I will think
> over your list.
>
> What do you mean with "exception helpers"?

That's a good point. I often need to 'bail out' of a piece of code and
return to a recovery point, restoring the stack/frame values that were in
effect in the function containing the recovery point. A few lines of
assembler is an easy way to do that.

So, inline assembler just provides an escape mechanism when one needs to do
something the language designer hasn't thought of. It might make the
difference between a practical language, and one which is just a curiosity.

--
Bartc

Marco van de Voort

unread,
Nov 21, 2009, 6:25:23 AM11/21/09
to
On 2009-11-21, tm <thomas...@gmx.at> wrote:
>> with certain assumptions about used registers is notacibly faster.
>> - mpi routines. Every cycle the loop is shorter is noticable
>> - most math primitives, due to setting copro mask inline and using FPU
>> instructions.
>
> Thank you for this information. I will look how compiled Seed7
> handles such tasks and for ways to improve the performance.

(add all (de)compression and encryption)

> As you probably know there are some well known rules concerning
> performance optimisations:

(I removed the cliches that are first order true, reply to the questionable)

> - In many cases it is cheaper to buy better hardware instead of
> spending expensive developer time to improve the performance.

Not as often as people think. e.g. quad socket license for common software
are more than linearly expensive.

Since better hardware often only means more cores nowadays, and refactoring
a whole application to take better use of threading is often not possible
willy nilly in production environments, there's a new limitation there.

> - It should be tested if compiler optimisations flags can solve the
> performance problem.

That's dangerous. You shouldn't change compiler optimizations globally on a
production app. You can try to experiment with locally, but that is as
unportable as assembler (the performance increase that is). And at least
assembler cuts right to the heart of the problem.

> - The use of smarter algorithms or data structures should be
> considered.

Not as easy in a production app, since that
1) significantly expands the scope of the modification (with more lines to
stabilize)
2) also the investment decision (performance improvement/time invested) gets
worse, and often less predictable if you go on a wild goosechase for a
better algorithm.
3) Also harder than many people think. Half of the textbook examples do not
take cache effects into account, and assume random access of uniform speed,
something that in effect doesn't exist anymore since the 486.
4) the constants more often matter than people think. O(N) theory is a limit,
and your number of elements might not be infinite.

> - All tricks of the high level language should be used to improve
> the performace.

I do not agree here. As soon as this goes against standards, uses
extensions, or questionable constructs, it is as bad as assembler.

> I have seen many programs where unnecessary optimisations make a
> program unportable and hard to maintain.

I've seen people dabble endlessly from one glorious algorithm or tweak to
the other, while anybody sane would just finger the tightest loop (RDS)
that causes the majority of the problem.

But that requires an active knowledge of optimization, profilers etc, not
just regurgitating the introduction of textbooks. Everybody knows the first
order mantras, but they are just that, first order approximations.

While a figure like Knuth's 97% sounds high, cut away all scripting
languages, VM based languages, VB (90%+ of the programming, easily) etc, and
realize that the rate determining step, even in a performance dependant
program is only 1-2% of the total code.

Then 97% is even awfully low, if you don't use a scripting language, a VM
based language, and have some need for performance in a 100000 line program,
you end up with 1000 lines to optimize :-)

> BTW: I am not sure that it can be deduced from the speed in
> Pascal/Delphi that something is slower than assembler in all
> languages. IIRC modern compilers like gcc generate inline code for
> C functions like memcpy(), memset(), memchr() and memcmp(). The
> compiler can customize such inline code when the size of the memory
> area is known at compile time. That way SSE instructions or other
> tricks may be used. That said I am neither an expert in such low
> level C compiler optimisations nor an assembler expert.

Those routines are typically too large to inline, and are afaik all in
libgcc in ... assembler. Keep in mind that you must align, check for
overlapping regions etc. Inlining them would decrease performance on
repeated use. (cache polution with same code).

Maybe that they optimize an occasional one with a constant small expression
into a few instructions (like copying a struct under 12 bytes or so), but
that is peanuts.

IIRC there's even libgcc_s even which preloads a libgcc optimized for the
current processor. This is mainly for P4's which have totally different
tradeoffs than most other recent processors since the Pentium Pro.

gcc has a few tricks more than FPC, but as can be seen in the shootout, that
is mostly tail-recursion optimization (gcc has been used to implement VMs
for functional languages that apparantly need that). Not used that much in
practice.

Delphi is a bit less in some regards, but has a better peephole optimizer
(the x86 specific part) than both, and has less portability overhead. It
misses interesting tricks like being able to prefetch in Delphi code though.
(though that can be worked around using the inline assembler)

>> Some stuff is only doable in assembler, because they e.g. break the
>> procedural regime or touch registers and instructions not modeled by the
>> language:
>>
>> - most math primitives, due to setting copro mask inline and using FPU
>> instructions.
>> - exception helpers
>> - Change a coprocessor exceptions mask. Switch to MMX mode etc
>> - check cpu version and capabilityies
>> - spinlock primitives, CAS etc.

- bitfield handlers
- prefetch (though currently in FPC this is a built in, probably so it
can be replaced with a no-op when compiling for older CPUs)


> Thank you. Exactly for such an answer I was looking for.
> I am not sure that all will make sense for Seed7, but I will think
> over your list.
>
> What do you mean with "exception helpers"?

Winding/unwinding the stack and generally the parts that are not inline of
exception support. Since exceptions can potentially happen in a lot of
routines it is quite RDS.

Marco van de Voort

unread,
Nov 21, 2009, 6:37:05 AM11/21/09
to
On 2009-11-20, Rod Pemberton <do_no...@nohavenot.cmm> wrote:
>
>> assembler as a "backdoor."
>
> Well, I've only used one HLL language where you (almost) don't have to use
> assembly as a "backdoor". That's C. For applications, you don't need
> assembly in C at all. It's only for low-level stuff (an OS, device drivers,
> etc.) that you may need some assembly.

You also need assembler to implement the system. There is afaik plenty of
assembler in zlib, openssl, libgcc etc. glibc might have also, but less.

tm

unread,
Nov 21, 2009, 8:04:57 AM11/21/09
to
On 19 Nov., 17:31, "bartc" <ba...@freeuk.com> wrote:
> "tm" <thomas.mer...@gmx.at> wrote in message

>
> news:94aa3725-cc0c-4172...@x31g2000yqx.googlegroups.com...
>
> > On 19 Nov., 12:35, "bartc" <ba...@freeuk.com> wrote:
> >> impossible to compile anywhere else even when there is no assembler
> >> involved.)
>
> > Let me guess: Such code calls functions of the operating system.
>
> No, just using pragmas/extensions/etc highly specific to a compiler. But it
> is possible to reduce portability quite well without using assembler at all.

Granted.
With Seed7 I try to avoid portability pitfalls. But at the same time
the programmer should not be restricted. I know that this two goals
conflict. It is necessary to provide good portable alternatives for
the things that are left out for portability reasons. This is much
harder to do than the simple way of just allowing everything.
I still want to reach both goals (portability without restrictions)
and therefore I ask for help.

> >> > - Language implementations which advertise inline assembler often
> >> > try to cover shortcomings (e.g. performance or access to external
> >> > resources) of the language or implementation. So instead of
> >> > improving language or implementation they offer a backdoor.
>
> >> What's wrong with improving performance?
>
> > Nothing, but it leads to the question:
> > Why is the original language slow?
>
> In my case, I use my own language/compiler, and never got round to
> optimising it. And I could never beat the big boys anyway.
>
> I was looking at a floating point benchmark recently. My (compiled) language
> took 2.7 seconds (My code is hopless at FPU work.) The best optimised C+gcc
> took 0.7 seconds. But a couple of minutes adding 10 lines of inline
> assembler, and my code took 0.6 seconds!

Your interpreter is faster than the best optimised C+gcc?
I am interested to see source programs of this floating point
benchmark.

> >> I use inline assembler to write
> >> fast interpreters. Once done, I can then write all my code at a higher
> >> level
> >> than the language used to implement the interpreter. But that assembler
> >> code
> >> is a one-off. It means I can get, say, double the speed, of my higher
> >> level
> >> code.
>
> > Compiling your high level language would probably even more than
> > double the speed.
>
> So many responses to that... assuming there is enough benefit to an
> interpreted, dynamic, spontaneous, bytecoded language to warrant using it,
> then what's wrong with wanting the best possible performance out of it?

Interpreted and bytecoded are just ways to implement a language.
AFAIK every language can be implemented as interpreted and bytecoded
or compiled to machine code. So this two things are not a feature of
a programming language and they do not make a language superior or
inferior.

I guess that 'dynamic' refers to dynamic typing. I prefer static
typing (http://seed7.sourceforge.net/faq.htm#static_type_checking)
and dynamic features can IMHO be reached with OO technics like
interfaces, virtual (dynamic) functions and multiple dispatch. As a
matter of fact everybody can decide about the typing system of his
language.

I have no idea what you mean with 'spontaneous'.
Can you explain it?

Since every language can be compiled I see no reason why your
language is the only one where this is not possible.

> A couple of years ago I believed enough hype about super-optimising C
> compilers beating hand-coded assembler, that I rewrote the core of my
> interpreter in pure C. It slowed down by 2-3 times. Since then I've speeded
> up, by various means, by 2-3 times, thanks to a tight assembler core.

Hand crafted assembler can have unbeatable performance.
But there are also massive drawbacks:
- Higher development costs.
- Higher maintenance costs.
- Unportable programs.
- Possible lower performance on future processors and future
compilers (where a compiled high level language may use the new
features offered by future processors).

> Anyway compiling code to native binary would surely render it less portable?

No, if your target code is C instead of machine code.

> Bytecode is far more portable: you just need to write an interpreter for
> each platform (a few 10Kloc), and suddenly millions of lines of code work on
> that platform without recompiling.

Oh, the urban "bytecode is portabe" myth.
Today libraries are much more important than the built-in features
of a language. To make portability work all libraries need to be
available at every target machine. Bytecode is not possible for the
machine and operating system dependend parts of the library (so
this parts must be rewritten). For performance reasons many library
functions are written in native machine code (so this functions must
be rewritten as well to get reasonable performance). Yes, Java
bytecode is portable but this archivement was neither easy nor
cheap. Sun spend huge amounts of money and it took them many many
years to reach their goal. BTW: the JVM is not a small piece of
software. I refuse to see bytecode as easy way to portability.

Seed7 supports Linux/unix/bsd, Mac OS X and Windows (using MinGW,
Visual C or Borland C) and probably also other platforms without
the need to write a single line of code. Currently I have no
Mips, Power, Sparc or PA-RISC machine available, but in the past
Seed7 ran on such processors also. So I guess the porting
effort for Seed7 is lower (10Kloc assembler vs. 0SLOC).

There is also an issue with bytecode interpreters which showed up in
a project where I am currently involved: A Borland (Embarcadero) C++
program calls a Seed7 program which does XML transformations and
other stuff and then calls a proprietory Java program. To do the
licensing check of the Java program the methods in a big *.jar
archive need to be called from a small Java program. While the C++
and Seed7 executables can just be installed or started from a
network device the Java bytecode makes problems: It is necessary to
compile Java with an older javac version (1.4) to avoid the need to
upgrade the JVM on all computers used by members of the support
team. The IT people in charge (for the installed software) have to
take many dependencies into account. So simply forcing them to
install a new JVM everywhere is not an option. I see this as
disadvantage of bytecode. Such things are simply not considered by
the bytecode fans.

> (BTW my interpreted language at present is 3-10x as slow as optimised C, and
> there's still plenty of scope for improvement, while still having
> interpreted bytecode. (This is for low-level integer code of course.) Beyond
> that will involve semi-compiling techniques. Traditional compiling will not
> work as well because of the dynamic nature of the language.)

When comparing the performance of interpreted vs. compiled code all
library functions should be excluded. So the benchmarks should be
done without calling any library function. For other benchmarks it
is okay to include library functions but especially for an
interpreted vs. compiled comparison they distort the result.

> >> No 'goto' and (I'm guessing) no inline assembler..?
>
> > Really nothing else is missing?
>
> > 1. goto statement:
> > Seed7 is not the only language without 'goto'. Java also has no
> > 'goto'. So Seed7 is in good company. Exceptions can (to some
>
> Yes I know. Language purists seem to like stripping away more and more
> features. Some languages don't even have variables apparently. Python
> doesn't have 'switch' (meaning a mess of code instead). 'Go' doesn't have
> do-while now. Etc.

Please dont take me in charge for what other languages do.
BTW: In the rest of the paragraph I tried to provide alternate
solutions.

bartc

unread,
Nov 21, 2009, 9:44:08 AM11/21/09
to

"tm" <thomas...@gmx.at> wrote in message
news:4339c7fd-1dde-43cc...@m26g2000yqb.googlegroups.com...

> On 19 Nov., 17:31, "bartc" <ba...@freeuk.com> wrote:

>> I was looking at a floating point benchmark recently. My (compiled)
>> language
>> took 2.7 seconds (My code is hopless at FPU work.) The best optimised
>> C+gcc
>> took 0.7 seconds. But a couple of minutes adding 10 lines of inline
>> assembler, and my code took 0.6 seconds!
>
> Your interpreter is faster than the best optimised C+gcc?
> I am interested to see source programs of this floating point
> benchmark.

No, this was another project, a compiled language. (I think the interpreted
one took some 4.5 seconds.)

(I did think briefly of having inline assembler, and/or inline compiled
code, in the interpreted language, but there were too many problems..)

>> So many responses to that... assuming there is enough benefit to an
>> interpreted, dynamic, spontaneous, bytecoded language to warrant using
>> it,
>> then what's wrong with wanting the best possible performance out of it?
>
> Interpreted and bytecoded are just ways to implement a language.
> AFAIK every language can be implemented as interpreted and bytecoded
> or compiled to machine code. So this two things are not a feature of
> a programming language and they do not make a language superior or
> inferior.
>
> I guess that 'dynamic' refers to dynamic typing. I prefer static
> typing (http://seed7.sourceforge.net/faq.htm#static_type_checking)
> and dynamic features can IMHO be reached with OO technics like
> interfaces, virtual (dynamic) functions and multiple dispatch. As a
> matter of fact everybody can decide about the typing system of his
> language.

I remember trying to find out what C++ was all about, and what it could do.
But all the examples I saw was stuff I'd been doing for years with
dynamic/variant types, without even knowing it.

> I have no idea what you mean with 'spontaneous'.
> Can you explain it?

Something other than the usual model of: compiling one module of many;
linking all these modules to create an executable; running the executable
(let's say an interactive application), loading the problem data, and
running the right sequence of commands to get to a bugpoint.

An application using several of my byte-coded modules can edit (or even
create), compile and run one of the modules while actively running the
application, with the data still on-screen.

> Since every language can be compiled I see no reason why your
> language is the only one where this is not possible.

Tell that to Python and Ruby users. Why aren't those (languages) compiled
(to native code)? Presumably there is advantage to not doing so, but one of
the costs is performance. Some people would like to improve the performance
whilst keeping the advantages.

>> A couple of years ago I believed enough hype about super-optimising C
>> compilers beating hand-coded assembler, that I rewrote the core of my
>> interpreter in pure C. It slowed down by 2-3 times. Since then I've
>> speeded
>> up, by various means, by 2-3 times, thanks to a tight assembler core.
>
> Hand crafted assembler can have unbeatable performance.
> But there are also massive drawbacks:
> - Higher development costs.
> - Higher maintenance costs.
> - Unportable programs.
> - Possible lower performance on future processors and future
> compilers (where a compiled high level language may use the new
> features offered by future processors).

There are a few exceptions and I think that language software is one of
those. The assembler portions are a small, one-off effort compared to the
software that will be written in the language.

>> Anyway compiling code to native binary would surely render it less
>> portable?
>
> No, if your target code is C instead of machine code.
>
>> Bytecode is far more portable: you just need to write an interpreter for
>> each platform (a few 10Kloc), and suddenly millions of lines of code work
>> on
>> that platform without recompiling.
>
> Oh, the urban "bytecode is portabe" myth.

I forget to add, 'In theory'.

> Seed7 supports Linux/unix/bsd, Mac OS X and Windows (using MinGW,
> Visual C or Borland C) and probably also other platforms without
> the need to write a single line of code.

I can do the same by rewriting the interpreter, once, as C[*]. Of course the
performance (of pure integer code) might drop 2-4x, but for applications
with a typical mix of code might still be acceptable (compared with some
languages).

But this is for a private, experimental language, and I'm interested in
seeing how far I can take it.

([*] plus whatever needs to be done to the libraries, although this would
not be limited to interpreted languages.)


> There is also an issue with bytecode interpreters which showed up in

...


> take many dependencies into account. So simply forcing them to
> install a new JVM everywhere is not an option. I see this as
> disadvantage of bytecode. Such things are simply not considered by
> the bytecode fans.

You've mentioned JVM again. There are problems when large corporations get
involved in such things as bytecode languages (JVM and CIL, if I've got that
right). They just become impossible.

>> (BTW my interpreted language at present is 3-10x as slow as optimised C,
>> and
>> there's still plenty of scope for improvement, while still having
>> interpreted bytecode. (This is for low-level integer code of course.)

> When comparing the performance of interpreted vs. compiled code all


> library functions should be excluded.

My basic benchmarks don't involve library calls (actually, I don't quite
*have* a library yet...)

--
Bartc

Rod Pemberton

unread,
Nov 21, 2009, 4:32:17 PM11/21/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:LXFNm.7123$Ym4....@text.news.virginmedia.com...

>
> But yes I seem to have come within a magnitude of the performance of C;
>

As a side note, I wrote a very simple Brainfuck to C conversion program. I
ran someone else's 187 byte Brainfuck "HelloWorld!" program through it. The
C code, when compiled as GCC -O2 (3.x series), was 195 x86 instructions.
That's amazing! GCC, mostly by optimization of constants, essentially
eliminated all the overhead of prolog and epilog for main(), setup for calls
to putchar() and getchar(), overhead for the while() loop, etc.

> But yes I seem to have come within a magnitude of the performance of C

Are you familiar with FORTH interpreters? What about the various
interpreter papers (usu. FORTH related) by Anton Ertl? He (with others)
have written a number of papers on techniques to speed up threaded
interpreters (esp. FORTH), much of which will work with non-FORTH
interpreters too. Unfortunately, you may need to understand the four types
of threaded-code interpreters (direct, indirect, subroutine, token) to
understand some of this work. A bunch of the papers show techniques to
implement faster interpreters, especially with GCC. He posts to
comp.lang.forth. You can find many of his papers by:

1) entering the following into your favorite search engine:
+pdf +Anton +Ertl +interpreter

2) his website
http://www.complang.tuwien.ac.at/anton/

3) Citeseer
http://citeseerx.ist.psu.edu/

HTH. If not, sorry.


Rod Pemberton


Rod Pemberton

unread,
Nov 21, 2009, 4:32:56 PM11/21/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:s2GNm.7125$Ym4....@text.news.virginmedia.com...

>
> To me it's like allowing a 'goto' statement. Not recommended for general
> use, but handy to get you out of trouble.
>

In C, I've only used it once, AFAIR. I used it to make readability of two
nested structured switches easier. (IIRC) I taught myself structured
programming concepts just prior to learning BASIC (self-taught) and Pascal
(school) in the '80's. So, except where forced to use goto in BASIC, I've
found no place where a "goto" was ever _required_. But, that's by using
other structured concepts like status flags and fallthrough - which may slow
your code down. It can be useful to improve code speed, e.g., for error
recovery, or useful with program generated code. However, once someone has
used goto's, esp. for error recovery, it can be a real PIA to remove them.
Frequently, such code "escapes" out of deeply nested loops - using goto's.
Yuck! Maybe, they should've used setjmp and longjmp.


Rod Pemberton

bartc

unread,
Nov 21, 2009, 5:39:51 PM11/21/09
to

"Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
news:he9mbs$551$1...@aioe.org...

> "bartc" <ba...@freeuk.com> wrote in message
> news:LXFNm.7123$Ym4....@text.news.virginmedia.com...

> Are you familiar with FORTH interpreters? What about the various


> interpreter papers (usu. FORTH related) by Anton Ertl? He (with others)
> have written a number of papers on techniques to speed up threaded
> interpreters (esp. FORTH), much of which will work with non-FORTH
> interpreters too.

That was very interesting, thanks. I liked the paper on writing 'efficient
interpreters', showing that some people have the same views as I do about
this!

I liked also the comment that threaded code is not really practical in a
high level language without 'first class labels', ie. indirect labels.
Apparently only some versions of gcc have this (which I tried once but that
wasn't quite enough for me).

This means the language not only having 'goto dest', but 'goto *label'! I
doubt 'tm' will go this far on his language...

> Unfortunately, you may need to understand the four types
> of threaded-code interpreters (direct, indirect, subroutine, token)

Well threaded code is what I use at the minute, it probably doesn't
correspond to any of those though.

Unfortunately my language has quite a rich type-system so it has other
challenges than just dispatch, which I've got down to about a 2-instruction
overhead. Forth, from the little I remember about (together with Euphoria,
another fast one) was far simpler.

--
Bartc

Robbert Haarman

unread,
Nov 21, 2009, 6:24:16 PM11/21/09
to
Hi Bartc,

I would certainly like to read more about how your interpreter works.

On Sat, Nov 21, 2009 at 10:39:51PM +0000, bartc wrote:
>
> I liked also the comment that threaded code is not really practical
> in a high level language without 'first class labels', ie. indirect
> labels. Apparently only some versions of gcc have this (which I
> tried once but that wasn't quite enough for me).

gcc calls this "computed gotos".

I wonder in what sense they were "not quite enough" for you, and if the
primitives offered by Voodoo would be more useful to you (in Voodoo, goto
works with any value you care to give it, be it a label, the value of
some variable, or even a plain integer).

> Unfortunately my language has quite a rich type-system so it has
> other challenges than just dispatch, which I've got down to about a
> 2-instruction overhead.

Could you elaborate on that? What kind of dispatch are you doing, and
how are you doing it?

Also, not all instructions are created equal. In response to an earlier
thread started by cr, I wrote a little interpreter for a very very small
subset of the IA32 instruction set, and was surprised that the test
program I used took only 3 times as long in the interpreter than on
the real hardware. But that's for the loop instruction, which does
quite a lot of work and is easy to decode.

> Forth, from the little I remember about
> (together with Euphoria, another fast one) was far simpler.

Yes. The reason there are so many variants of threaded code for Forth
is that you actually can implement most of the language constructs
without having to do any parsing. And Forth being typeless, you don't
have to write dispatching code for types, either.

Regards,

Bob

--
"Any sufficently complicated Java program requires a programmable IDE
to make up for the half of Common Lisp not implemented in the program
itself."

-- Peter Seibel


bartc

unread,
Nov 21, 2009, 9:09:33 PM11/21/09
to
"Robbert Haarman" <comp.la...@inglorion.net> wrote in message
news:2009112123...@yoda.inglorion.net...

> On Sat, Nov 21, 2009 at 10:39:51PM +0000, bartc wrote:
>>
>> I liked also the comment that threaded code is not really practical
>> in a high level language without 'first class labels', ie. indirect
>> labels. Apparently only some versions of gcc have this (which I
>> tried once but that wasn't quite enough for me).
>
> gcc calls this "computed gotos".
>
> I wonder in what sense they were "not quite enough" for you,

(Background: I had to choose between staying with my own compiled language,
which was generally old, slow, buggy, and crappy, but familiar and cosy
(and had inline Nasm assembler!), and switching to C. )

As I understood how these gotos worked, the labels had to be in the same
function. Since I had several hundred handlers, I wanted to have each one in
it's own function. Trying to set up functions with no local params/variables
and therefore no prolog code, and using the function address as the label,
didn't work.

Maybe there were ways of doing that (one label per function, jumping
somewhere into the start of it), but I didn't spend too long on it. I'd also
planned on making use of the hardware stack and hardware frame pointer, but
that seemed awkward without using assembler. And if I was going to use gcc's
awful assembler syntax, I'd rather stay with my own language!

> and if the
> primitives offered by Voodoo would be more useful to you (in Voodoo, goto
> works with any value you care to give it, be it a label, the value of
> some variable, or even a plain integer).

You'll need to explain again where Voodoo might fit in to the model of
compiled language+assembler being used to implement an interpreter. Or
perhaps it is a target language to take the place of bytecode.

>> Unfortunately my language has quite a rich type-system so it has
>> other challenges than just dispatch, which I've got down to about a
>> 2-instruction overhead.
>
> Could you elaborate on that? What kind of dispatch are you doing, and
> how are you doing it?

The opcode dispatch is straightforward: each bytecode instruction, once
fixed up in memory, is really a 32-bit function address, followed by 0 to 4
32-bit words of operands. I use a dedicated register EBX which holds the
program counter.

Dispatching after each opcode has finished, is something like:

add ebx,N
jmp [ebx]

(although I found that mov eax,[ebx]; jump eax was faster; but x86 is full
of strange things like that)

The execution model is, mostly, a stack machine, and manipulates variant
types, which are 12-byte values. These are pushed onto the normal stack
using ESP. And EBP is used for frame variables of the interpreted language.

The opcode handlers are functions, but defined with no params nor local
variables, so that I can jump straight into the start (and jump to the next
at the end). This way ESP/EBP still point to the interpreter's data, and
most handlers use inline assembler. To execute normal high level code, I
need to explicitly save EBX, and any operand pointers first.

The type system is complex, but manages to use a single small integer to
represent
the type of a variant. For single operand codes, this is then used to index
a jump table to get to the specific handler. For dual operand codes, the two
codes have to be converted to a single linear value, then again a jump table
is used. (For non-critical codes then just switch/case might be used.)

(However, while mainly dynamic, static typing hints can be used to avoid
this second dispatch. This might be considered a cheat..)

> Also, not all instructions are created equal. In response to an earlier
> thread started by cr, I wrote a little interpreter for a very very small
> subset of the IA32 instruction set, and was surprised that the test
> program I used took only 3 times as long in the interpreter than on
> the real hardware. But that's for the loop instruction, which does
> quite a lot of work and is easy to decode.

That's pretty good. I managed only 15x slowdown (on sub ebx,1; jnz l1)
during BGB's thread on his x86 interpreter (although I still didn't get the
need for such a thing).


>
>> Forth, from the little I remember about
>> (together with Euphoria, another fast one) was far simpler.
>
> Yes. The reason there are so many variants of threaded code for Forth
> is that you actually can implement most of the language constructs
> without having to do any parsing. And Forth being typeless, you don't
> have to write dispatching code for types, either.

It doesn't sound like a fun language to program though; I quite like my
types.

--
Bartc

stan

unread,
Nov 21, 2009, 9:28:09 PM11/21/09
to

You should check out "Programming Pearls" or just about anything by
Jon Bently.

stan

unread,
Nov 21, 2009, 9:33:56 PM11/21/09
to

Programming is about solving contextual specific problems. In any hll
there will be contexts where the hll model doesn't fit well and
exceptional solutions will be required. But prudence demands that time
be sent making the hll model cover the most cases.

Being able to use modules and libraries from other languages isn't
inherently evil, but it's rarely a fun experience and usually feels
somehow suboptimal. But the answer is no, it's no inherently wrong to
allow external asm modules IMHO.

Robbert Haarman

unread,
Nov 22, 2009, 1:38:15 AM11/22/09
to
On Sun, Nov 22, 2009 at 02:09:33AM +0000, bartc wrote:
> "Robbert Haarman" <comp.la...@inglorion.net> wrote in message
> news:2009112123...@yoda.inglorion.net...
>
> >On Sat, Nov 21, 2009 at 10:39:51PM +0000, bartc wrote:
> >>
> >>I liked also the comment that threaded code is not really practical
> >>in a high level language without 'first class labels', ie. indirect
> >>labels. Apparently only some versions of gcc have this (which I
> >>tried once but that wasn't quite enough for me).
> >
> >gcc calls this "computed gotos".
> >
> >I wonder in what sense they were "not quite enough" for you,
>
> (Background: I had to choose between staying with my own compiled language,
> which was generally old, slow, buggy, and crappy, but familiar and
> cosy (and had inline Nasm assembler!), and switching to C. )
>
> As I understood how these gotos worked, the labels had to be in the
> same function. Since I had several hundred handlers, I wanted to
> have each one in it's own function.

Right. I've never understood why labels are local to the function in gcc.
I could understand why labels are local to the function in C if the rationale
is that jumping to a label in a function with a different call frame
organization leads to badness. However, if you allow "goto *((void*) 0x1234);",
as gcc does, why wouldn't you allow jumping to an actual label?

> >and if the
> >primitives offered by Voodoo would be more useful to you (in Voodoo, goto
> >works with any value you care to give it, be it a label, the value of
> >some variable, or even a plain integer).
>
> You'll need to explain again where Voodoo might fit in to the model of
> compiled language+assembler being used to implement an interpreter. Or
> perhaps it is a target language to take the place of bytecode.

Well, your remarks about C made me realize that you have been running into
the same issues that got me to create Voodoo. Basically, it is a low-level
language (like assembly), but with just enough abstraction to be
platform-independent. In particular, it has functions and variables which,
in my implementation, are compatible with the ELF ABI (meaning you can
call C functions from Voodoo and vice versa).

In short, the reason you might want to use Voodoo instead of assembly is that
Voodoo is more portable. The reason you might want to use Voodoo instead of
C is that Voodoo allows things that C doesn't. In particular (w.r.t. this
discussion), Voodoo allows you to put a label anywhere in your code, and
you can jump to it no matter if it's inside the same function, inside another
function, or not inside any function at all.

> >>Unfortunately my language has quite a rich type-system so it has
> >>other challenges than just dispatch, which I've got down to about a
> >>2-instruction overhead.
> >
> >Could you elaborate on that? What kind of dispatch are you doing, and
> >how are you doing it?
>
> The opcode dispatch is straightforward: each bytecode instruction, once
> fixed up in memory, is really a 32-bit function address, followed by 0 to 4
> 32-bit words of operands. I use a dedicated register EBX which holds the
> program counter.

Ok, so what does the full opcode decoder/dispatcher look like? I mean
the actual instructions (assembly code).

> The type system is complex, but manages to use a single small
> integer to represent
> the type of a variant. For single operand codes, this is then used
> to index a jump table to get to the specific handler. For dual
> operand codes, the two codes have to be converted to a single linear
> value, then again a jump table is used. (For non-critical codes then
> just switch/case might be used.)

If I understand that correctly, your bytecode basically has some simple
generic functions (to use Common Lisp nomenclature) and you use multiple
dispatch to get to the actual method (again, CL nomenclature) for the types
of value passed at run time.

> (However, while mainly dynamic, static typing hints can be used to avoid
> this second dispatch. This might be considered a cheat..)

In the bytecode? I wouldn't consider that cheating. Why have the dispatch
done at run time when you already know the types at compile time?

> >Yes. The reason there are so many variants of threaded code for Forth
> >is that you actually can implement most of the language constructs
> >without having to do any parsing. And Forth being typeless, you don't
> >have to write dispatching code for types, either.
>
> It doesn't sound like a fun language to program though; I quite like my
> types.

Horses for courses, I think. I wouldn't want to write complex applications
in Forth, but if I need a language that is easy to implement and/or can
be implemented in very little memory, Forth would be on the list.

In the end, I see it this way: applications can and should be written in
a high-level language, and a good high-level language will let you
express your program in terms that suit the application. However, eventually,
you'll have to get down from the high level to something that suits the
hardware, and it's there that simple, low-level languages like assembly,
Forth, and Voodoo come in.

Regards,

Bob

--
I'm a dyslexic agnostic with insomnia... I lie awake at night wondering
if there really is a dog!

Marco van de Voort

unread,
Nov 22, 2009, 9:04:45 AM11/22/09
to
On 2009-11-22, stan <smo...@exis.net> wrote:
>>
>> But what in case you have limited resources, and a fixed magnitude N (say
>> the one you design for, and a ten or hundred fold scaling limit)?
>
> You should check out "Programming Pearls" or just about anything by
> Jon Bently.

Could you provide a summary if there is a point in there? I've already
bought more software engineering volumes than I would have liked.

Eryndlia Mavourneen

unread,
Nov 22, 2009, 2:45:38 PM11/22/09
to
On Nov 20, 5:52 pm, "Rod Pemberton" <do_not_h...@nohavenot.cmm> wrote:
> "James Harris" <james.harri...@googlemail.com> wrote in message


I find it curious that no one has mentioned the Ada language. While I
am not an Ada "language lawyer," I do know something about the
language, having programmed application code and kernel code,
including manipulation of device registers, without ever having to use
assembly language. Here are just a few Ada language features that
help:

1 -- Better optimization than other languages. This largely stems
from the fact that the Ada compiler generally knows much more about
the program and program structure than compilers for other languages.

2 -- The ability to detect, from within the program, specific hardware
features such as word-lengths, how integers are signed, sizes of
floating-point values and whether and how floating-point numbers over/
underflow ....

3 -- The ability to specify the exact bit layout of a data structure
and how fields are packed.

4 -- The ability to specify, as a natural part of the language and at
run-time, the specific address at which the compiler is to place a
data structure. The result is that the data structure and its fields
are referenced normally as if the clause to specify the address had
not been there.

There is much more in Ada, of course, that software developers
concerned with portability can use to make their programs portable
across a wide variety of hardware and software platforms, including
device and/or software interrupt handlers. Oh, and I nearly forgot:
There actually are cases when it is necessary to descend into assembly
language, mostly to be able to execute an instruction that has a
special effect and for which there is no high-level language
construct, and so ...

5 -- The ability to provide assembly language instructions from within
the Ada program.

Eryndlia

Rod Pemberton

unread,
Nov 22, 2009, 4:22:46 PM11/22/09
to
"Robbert Haarman" <comp.la...@inglorion.net> wrote in message
news:2009112206...@yoda.inglorion.net...
>
> ... if I need a language that is easy to implement and/or can

> be implemented in very little memory, Forth would be on the list.
>

There are simpler solutions than FORTH. This old Dr. Dobb's article shows
how to implement "mini-interpreters". Their example is much like a
"unrolled" subroutine threaded (STC) FORTH with a single stack.
http://www.ddj.com/184408206?pgno=3

Up a message, bartc:

>> add ebx,N
>> jmp [ebx]

Would "lea" be more suitable than "add" here?

lea ebx, [ebx+N]

At a minimum, it doesn't affect flags. It also allows more than just
addition since it supports all the memory addressing modes. For 32-bit
code, this allows quite a few combinations using a register, 8-bit or 32-bit
offsets, and register mulitplied by 1, 2, 4, or 8.

(FYI, stuff like this, interpreters and lea instruction, is discussed
somewhat regularly on alt.lang.asm and comp.lang.asm.x86.)


Rod Pemberton


stan

unread,
Nov 22, 2009, 8:30:06 PM11/22/09
to
Marco van de Voort wrote:

I'd check local libraries, possibly an interlibrary loan. I'd also
check out bookstores, I've seen copies on the shelves of places like
Barnes and Noble and I wouldn't be surprised to see it in others.

One story he relates refers to someone who came to him wanting help to
implement a merge sort. The source data was too large to fit into
available memory so the plan was to read the source in chunks and sort
them into temporary files and finally merge the sorted temporary files
into one final sorted output. This needed to be done quickly because
it happened often.

This seems like a reasonable plan and one that could be done and
optimized for success. Possibly even resorting to assembly to save
some precious available memory.

Before starting work, they got involved in a discussion about the
problem and some further details emerged. It turned out the records in
the source were 7 digit numbers, phone numbers to be exact. After some
thought, he realized that the records were unique so the actual
problem was to sort a large number of unique records in limited
memory. Then he realized that there was another way to see this
context. You could consider this as detecting which phone numbers
exist in the source file.

His solution was to use bits to represent the phone numbers. As the
phone numbers were read from the source file the corresponding bit was
set. This only required reading the file once and the memory required
was acceptable.

The point is that tunneling in on optimizing prevents seeing other
possible solutions. Optimizing loops and expressions leads to
relatively small improvements, better algorithms lead to large
improvements. The moral is to solve the right problem with the right
tools.

By the way I spelled Jon's name wrong. It's Jon Bentley; sorry Jon.

Rod Pemberton

unread,
Nov 23, 2009, 3:15:48 AM11/23/09
to
"Rod Pemberton" <do_no...@nohavenot.cmm> wrote in message
news:heca5t$3qa$1...@aioe.org...

> "Robbert Haarman" <comp.la...@inglorion.net> wrote in message
> news:2009112206...@yoda.inglorion.net...
> >
> > ... if I need a language that is easy to implement and/or can
> > be implemented in very little memory, Forth would be on the list.
> >
>
> There are simpler solutions than FORTH. This old Dr. Dobb's article shows
> how to implement "mini-interpreters". Their example is much like a
> "unrolled" subroutine threaded (STC) FORTH with a single stack.
> http://www.ddj.com/184408206?pgno=3
>

Of course, you guys probably want a scripting language:
http://cbmbasic.sourceforge.net/


Rod Pemberton

Bart

unread,
Nov 23, 2009, 5:30:12 AM11/23/09
to
On 22 Nov, 22:22, "Rod Pemberton" <do_not_h...@nohavenot.cmm> wrote:

> Up a message, bartc:
>
> >>    add ebx,N
> >>    jmp [ebx]
>
> Would "lea" be more suitable than "add" here?
>
>   lea ebx, [ebx+N]
>
> At a minimum, it doesn't affect flags.  It also allows more than just

Actually I can't remember whether I use add or lea (I can't check at
the minute). I've tried both, sometimes one is faster, sometimes the
other...

The strange thing was mov eax,[ebx], jmp eax being faster than jmp
[ebx]. Somehow using 2 instructions instead of 3 is more satisfying.
(The speedup for this one averaged 5%.)

--
Bartc

Bart

unread,
Nov 23, 2009, 5:52:52 AM11/23/09
to
On 22 Nov, 07:38, Robbert Haarman <comp.lang.m...@inglorion.net>
wrote:

> On Sun, Nov 22, 2009 at 02:09:33AM +0000, bartc wrote:
> > "Robbert Haarman" <comp.lang.m...@inglorion.net> wrote in message
> >news:2009112123...@yoda.inglorion.net...

> > >I wonder in what sense they were "not quite enough" for you,

> > As I understood how these gotos worked, the labels had to be in the


> > same function. Since I had several hundred handlers, I wanted to
> > have each one in it's own function.
>
> Right. I've never understood why labels are local to the function in gcc.
> I could understand why labels are local to the function in C if the rationale
> is that jumping to a label in a function with a different call frame
> organization leads to badness. However, if you allow "goto *((void*) 0x1234);",
> as gcc does, why wouldn't you allow jumping to an actual label?

If gcc does funny things with registers then I can understand why
randomly jumping into and out of functions isn't recommended. Another
reason to use a language that is not that clever..

> > You'll need to explain again where Voodoo might fit in to the model of
> > compiled language+assembler being used to implement an interpreter. Or
> > perhaps it is a target language to take the place of bytecode.
>
> Well, your remarks about C made me realize that you have been running into
> the same issues that got me to create Voodoo. Basically, it is a low-level
> language (like assembly), but with just enough abstraction to be
> platform-independent. In particular, it has functions and variables which,
> in my implementation, are compatible with the ELF ABI (meaning you can
> call C functions from Voodoo and vice versa).
>
> In short, the reason you might want to use Voodoo instead of assembly is that
> Voodoo is more portable. The reason you might want to use Voodoo instead of
> C is that Voodoo allows things that C doesn't. In particular (w.r.t. this
> discussion), Voodoo allows you to put a label anywhere in your code, and
> you can jump to it no matter if it's inside the same function, inside another
> function, or not inside any function at all.

You'll have to bear with me; Voodoo is an implementation language
suitable for writing an interpreter, or as a target language itself?
Or both? Can it be mixed with a traditional language?

> > The opcode dispatch is straightforward: each bytecode instruction, once
> > fixed up in memory, is really a 32-bit function address, followed by 0 to 4
> > 32-bit words of operands. I use a dedicated register EBX which holds the
> > program counter.
>
> Ok, so what does the full opcode decoder/dispatcher look like? I mean
> the actual instructions (assembly code).

The code for ++i looks like this (when it is known that i is a static
integer variant):

add ebx,...
jmp [ebx] ;dispatch from previous instr
...
mov eax,[ebx+4]
inc dword [eax+4]
add ebx,8
jmp [ebx] ;dispatch to next instr

> > The type system is complex, but manages to use a single small
> > integer to represent
> > the type of a variant. For single operand codes, this is then used
> > to index a jump table to get to the specific handler. For dual
> > operand codes, the two codes have to be converted to a single linear
> > value, then again a jump table is used. (For non-critical codes then
> > just switch/case might be used.)
>
> If I understand that correctly, your bytecode basically has some simple
> generic functions (to use Common Lisp nomenclature) and you use multiple
> dispatch to get to the actual method (again, CL nomenclature) for the types
> of value passed at run time.

Yes, about that. Except it can be even more complex that that. In this
source code:

a:=b

In C, that might be just 2 instructions when a,b are both int. But
when they are variants, they can be anything. This assignment involves
freeing resources used by a (which is a recursive process when a is a
list of variants than can be lists...), and duplicating b, since my
language requires a deep copy; another possibly recursive process.

And in a[i]:=b, there might be 4 types involved: the type of the
array, the type of the element (each element could be a variant and
therefore of different type), the type of i, and the type of b.

That's why I said the type system was challenging! The language uses
mostly variant types but also allows normal non-variant unboxed ones
for arrays elements and struct members. And a pointer P might point to
a variant containing an int, or directly to an unboxed int...

All this is not much of a big deal, it just makes it harder to write
interpreters with decent performance. And (a note to 'tm') compiling
wouldn't help a great deal.

> > (However, while mainly dynamic, static typing hints can be used to avoid
> > this second dispatch. This might be considered a cheat..)
>
> In the bytecode? I wouldn't consider that cheating. Why have the dispatch
> done at run time when you already know the types at compile time?

The hinting is done in the source code.

--
Bartc

tm

unread,
Nov 23, 2009, 9:42:04 AM11/23/09
to
On 23 Nov., 11:52, Bart <b...@freeuk.com> wrote:
> On 22 Nov, 07:38, Robbert Haarman <comp.lang.m...@inglorion.net>
> wrote:
> > On Sun, Nov 22, 2009 at 02:09:33AM +0000, bartc wrote:
> > > The type system is complex, but manages to use a single small
> > > integer to represent
> > > the type of a variant. For single operand codes, this is then used
> > > to index a jump table to get to the specific handler. For dual
> > > operand codes, the two codes have to be converted to a single linear
> > > value, then again a jump table is used. (For non-critical codes then
> > > just switch/case might be used.)
>
> > If I understand that correctly, your bytecode basically has some simple
> > generic functions (to use Common Lisp nomenclature) and you use multiple
> > dispatch to get to the actual method (again, CL nomenclature) for the types
> > of value passed at run time.
>
> Yes, about that. Except it can be even more complex that that. In this
> source code:
>
> a:=b
>
> In C, that might be just 2 instructions when a,b are both int. But
> when they are variants, they can be anything. This assignment involves
> freeing resources used by a (which is a recursive process when a is a
> list of variants than can be lists...), and duplicating b, since my
> language requires a deep copy; another possibly recursive process.

Well. The Seed7 assignment does also a deep copy. For structured
types Seed7 uses also a logic of freeing (possible recursive
depending on the type) and duplicating (possible recursive depending
on the type). But Seed7 also uses some improvement: When the right
hand expression of the assignment is a temporary (which would be
freed afterwards) a simple pointer assignment is done instead of the
duplicating (and the "temporary" is not freed, but used as normal
value). For simpler types like integer, float and others the
assignment is implemented with a simple copy without freeing and
duplicating. BTW: All this things can be decided at compile time.

> And in a[i]:=b, there might be 4 types involved: the type of the
> array, the type of the element (each element could be a variant and
> therefore of different type), the type of i, and the type of b.

I am not a big fan of arrays with mixed type elements. It hinders
debugging and type checks or casts need to be added at many places
(probably every time an array element is accessed). When there is
a demand for an array with mixed type elements it is often possible
that all desired element types can be described with one interface
type (the elements probably need some general handling anyway). In
this case the array has elements of the interface type. There are
also some differences in the logic and implementation of arrays and
hashes (maps) which make it reasonable to keep them apart.

> That's why I said the type system was challenging! The language uses
> mostly variant types but also allows normal non-variant unboxed ones
> for arrays elements and struct members. And a pointer P might point to
> a variant containing an int, or directly to an unboxed int...
>
> All this is not much of a big deal, it just makes it harder to write
> interpreters with decent performance. And (a note to 'tm') compiling
> wouldn't help a great deal.

The predecessor of Seed7 was also highly dynamic (which made
it hard to compile to efficient machine code), but it also had an
compiler. The redesign which resulted in Seed7 was especially done
to allow compilation to efficient code. There is now a static
concept around the dynamic features which makes this combination
possible. So you need to define interface types and interface
functions and the element type of an array must be known at compile
time, but there is multiple dispatch, methods attached to objects
(instead of classes) and types as parameters upon which an elegant
template mechanism is based.

> > > (However, while mainly dynamic, static typing hints can be used to avoid
> > > this second dispatch. This might be considered a cheat..)
>
> > In the bytecode? I wouldn't consider that cheating. Why have the dispatch
> > done at run time when you already know the types at compile time?
>
> The hinting is done in the source code.

What happens when the hints are wrong?

BTW: Is it possible to download a documentation and an
implementation of your language?

As you probably know, the documentation and an implementation of
Seed7 is available.

Robbert Haarman

unread,
Nov 23, 2009, 5:09:26 PM11/23/09
to
On Mon, Nov 23, 2009 at 02:52:52AM -0800, Bart wrote:
>
> You'll have to bear with me; Voodoo is an implementation language
> suitable for writing an interpreter, or as a target language itself?
> Or both? Can it be mixed with a traditional language?

Both, and yes. But some more explanation is in order, even though I'm
starting to feel guilty of hijacking the thread ...

First of all: implementation language for writing an interpreter. You've
posted a snippet of your interpreter in assembly language. Thanks, by
the way. That answers all my questions about how it works. Very clever!

> add ebx,...
> jmp [ebx] ;dispatch from previous instr
> ...
> mov eax,[ebx+4]
> inc dword [eax+4]
> add ebx,8
> jmp [ebx] ;dispatch to next instr

Basically, you are using ebx to hold the "instruction pointer", your
instructions are machine-word size subroutine addresses, and these
subroutines do the work of incrementing the instruction pointer and
invoking the next subroutine. In Voodoo, this could be rendered as
follows, using "ip" as instruction pointer and "x" and "y"
as temporaries:

set ip add ip ... # add instruction size to ip
set x get-word ip 0 # get word at adress ip
goto x # jump to address x
..
some_label:
set x load-word ip 1 # load word one word after address ip
set y load-word x 1 # load word one word after address x
set y add y 1 # increment y
set-word x 1 y # store word y at address one word after x
set ip add ip 8 # add 8 to ip
set x get-word ip 0 # get word at address ip
goto x # jump to address x

Now, in its current state, my Voodoo compiler lacks a good peephole
optimizer, so if you compile the above code to i386 machine code, it will
probably not be as fast as the code you provided (but I've been surprised
before). On the other hand, I think you can see how the Voodoo program is
structurally similar to the assembly program. The advantage of Voodoo in
this case is that Voodoo isn't tied to a specific instruction set.

As a target language, Voodoo offers that same benefit: it abstracts
platform details such as instruction set and calling convention. The
path to generating Voodoo code is about the same as the path to
generating assembly code (since Voodoo offers constructs similar to
common assembly languages), but the resulting code is not tied to a
specific CPU family.

My compiler currently supports i386 and AMD64, with MIPS and ARM
backends planned but not implemented yet. I'm working on compilers that
use Voodoo as a backend, first. If you look at the Voodoo code above,
though, and you know MIPS, you can probably imagine how to map Voodoo to
MIPS already. It took me about 2 days to add AMD64 support, and I imagine
MIPS support wouldn't take much longer. ARM would take a bit longer, because
I'd have to study the language, first.

As for mixing Voodoo with another language: this is not in the
language specification, but my compiler emits code compatible with
cdecl for i386 and the ELF ABI for AMD64. These are the same calling
conventions used by C on most operating systems, the notable exception
being Windows, which uses a different calling convention on AMD64.
It would be easy to add a code generator that supported the Win64
calling convention, though. What this means is that you can call
C code from Voodoo and Voodoo code from C. The test programs that
ship with the compiler link against the C library, because Voodoo
doesn't have a standard library.

Short example:

#### Hello world in Voodoo

section data
greeting:
string "Hello, world!\x00"

section code
import puts
export main

main:
function argc argv
call puts greeting
return 0
end function

I hope that answers your questions. If you have more, I'll be happy
to answer them.

Kind regards,

Bob

--
Using a (signed) 64-bit value introduces a new wraparound date in about
290 billion years, on Sunday, December 4, 292,277,026,596. However, this
problem is not widely regarded as a pressing issue.

-- Wikipedia, Year 2038 Problem

tm

unread,
Nov 24, 2009, 3:39:41 AM11/24/09
to
On 20 Nov., 23:52, "Rod Pemberton" <do_not_h...@nohavenot.cmm> wrote:
> "James Harris" <james.harri...@googlemail.com> wrote in message
>
> news:dd46104a-a95a-41b0...@k4g2000yqb.googlegroups.com...
>
> > On 19 Nov, 08:38, tm <thomas.mer...@gmx.at> wrote:
>
> > > I don't think it is a good idea to offer backdoors like inline
> > > assembler.
>
> > If the high level language design is good there won't be a need to use
> > assembler as a "backdoor."
>
> From what I remember of Pascal in the '80's, I was locked into a restricted
> "box" - the limited capabilities of the HLL - and couldn't do anything
> outside the box (heh...), i.e., OS related. I couldn't use the OS or the
> OS' CLI from my program. That was the first time I *really* wanted to use
> assembly within a HLL, but couldn't.

This "box" concept drove me away from Pascal. Portable Pascal
programs were only possible when you stayed in the "box" of the
"Pascal User Manual and Report". The implementations offered
features outside the "box", but in incompatible ways. So you had
to provide alternate solutions for different compilers. But that
was also hard to do, since the mechanisms to do so (preprocessors
or separate compilation units) were not supported in a portable way.

I dont't want to accuse Pascal. I write about this to demonstrate a
general problem of portability:

- When there is direct access to operating system functions
unportable programs can also be written easily. If you look
at the source of many programs in the internet you will be
shocked how many programs are presented as being portable
although they are windows only or unix only programs.

- When an implementation tries to forbid creating unportable
programs the programmer feels like he is in a "box". This is
also not desireable (as can be seen in the example of Pascal).

It is hard to archive both goals (portability without box). I think
the only solution is a portable library which allows access to the
operating system features without being tied too close to a certain
operating system.

Some try to carry one operating system interface like posix or
windows everywhere. AFAIK perl tries to support posix also outside
the unix world (at least to some degree), but this would only help
portability when win32 interfaces were completely replaced by posix
(which perl decided not to do).

Java decided to create its own library and I consider this solution
as much more successful. Many people confuse the portability offered
by this library with the portability of bytecode. Therefore many
implementations try to use bytecode as solution for the portability
problem. This are just the wrong conclusions. IMHO bytecode helps
portability just a little but a portable library can solve the
portability problem.

Since I am not happy with various aspects of the Java library I try
to create an independend portable library for Seed7. Until now there
are functions for:
- opening, closing, reading and writing files
- communication with sockets
- support for http and html
- reading and writing directories
- suport for ASCII, UTF-8, UTF-16 and UTF-32 and various codepages
- copying, moving and removing files and directory trees.
- environment variables and program arguments
- Output and positioning of the cursor at the console
- graphics (based on gdi or X11, depending on the os)

Other supported areas don't need the support of the operating
system:
- support for scanning from a file or string
- arrays, hashes and bitmaps
- unlimited precision integers.

I would be interested to know about areas not covered by the Seed7
library. I am also interested to know how important the support for
a certain area is.

Robbert Haarman

unread,
Nov 24, 2009, 5:00:46 AM11/24/09
to
On Tue, Nov 24, 2009 at 12:39:41AM -0800, tm wrote:
>
> It is hard to archive both goals (portability without box). I think
> the only solution is a portable library which allows access to the
> operating system features without being tied too close to a certain
> operating system.

I like Scheme's approach: there is a portable subset, specified by the
standard, which is offered by all implementations. Then there are features
that aren't in the standard. These can be added by implementations at will,
but there is also a process called SRFI (Scheme Request For Implementation)
where people can propose and discuss the desired syntax and semantics of
extended features. Multiple Scheme implementations can and do implement
the same SRFIs, and code that depends on these SRFIs then works on any
implementation that implements them. This way, you get extensions to the
standard without completely sacrificing portability.

> Some try to carry one operating system interface like posix or
> windows everywhere.

It is worth noting that POSIX was created specifically for this purpose:
a common interface that all operating systems could implement, so that
programs written to that interface would run on all compliant operating
systems.

Of all operating systems I have worked with, Windows is the only popular
one that doesn't offer a useful degree POSIX compliance out of the box.
However, this can be added.

> Java decided to create its own library and I consider this solution
> as much more successful.

The problem with Java's library is that so much behavior is unspecified that
programs still become non-portable. For example, when dealing with files,
File.renameTo may or may not allow moving files. Similarly, opening a file
may or may not obtain a lock on that file that prevents opening the same
file in another place from succeeding. So, even with something as simple
as dealing with files, I have observed programs that weren't portable.

> Many people confuse the portability offered
> by this library with the portability of bytecode.

Bytecode does not aid portability. It is really just another form of code,
and whether that code is portable or not depends on whether or not the
specification it conforms to is portable.

> Since I am not happy with various aspects of the Java library I try
> to create an independend portable library for Seed7. Until now there
> are functions for:

So, questions for you. Note that these are not meant to be unfriendly,
they are just quick questions out of curiosity. Things for you to think
about, and about which I wonder how you have solved them.

> - opening, closing, reading and writing files

How does this deal with permissions? Locking? Binary files vs. text
files? Stream file vs. record files? Fixed-length records vs. variable
length records? How does it work on Palm OS?

Not saying you _must_ deal with all these, just asking you to realize that
these issues are there so you can make a conscious decision to either
specify the behavior or leave some unspecified behavior, with the associated
risk of non-portability.

> - communication with sockets

TCP? UDP? Local? Raw? IPv4? IPv6? Can one plug in ones own protocols somehow?

> - support for http and html

How far does the support go? HTTP clients? Servers? Which versions of HTTP?
Keep-alives? Caching?

For HTML: how do you deal with malformed HTML? What happens when I have
well-formed HTML with, say, MathML in it, all neatly according to the
specifications?

> - reading and writing directories

Hard links? Soft links? Resource forks?

> - copying, moving and removing files and directory trees.

How does this deal with, e.g., permissions, ownership, special files, etc.?

> - Output and positioning of the cursor at the console

How do you deal with different terminal types? What if there isn't a terminal?

> - graphics (based on gdi or X11, depending on the os)

Ok, I'll read the documentation to find out what primitives you implement.

Regards,

Bob

--
The early bird gets the worm, but the second mouse gets the cheese.


Marco van de Voort

unread,
Nov 24, 2009, 8:14:36 AM11/24/09
to
On 2009-11-24, tm <thomas...@gmx.at> wrote:
>> OS' CLI from my program. That was the first time I *really* wanted to use
>> assembly within a HLL, but couldn't.
>
> This "box" concept drove me away from Pascal. Portable Pascal
> programs were only possible when you stayed in the "box" of the
> "Pascal User Manual and Report". The implementations offered
> features outside the "box", but in incompatible ways. So you had
> to provide alternate solutions for different compilers. But that
> was also hard to do, since the mechanisms to do so (preprocessors
> or separate compilation units) were not supported in a portable way.

> I dont't want to accuse Pascal. I write about this to demonstrate a
> general problem of portability:
>
> - When there is direct access to operating system functions
> unportable programs can also be written easily. If you look
> at the source of many programs in the internet you will be
> shocked how many programs are presented as being portable
> although they are windows only or unix only programs.
>
> - When an implementation tries to forbid creating unportable
> programs the programmer feels like he is in a "box". This is
> also not desireable (as can be seen in the example of Pascal).

The first goes for any form of portable code. The second is partially the
bytecode mentality of the UCSD compilers of the time.

This had a significant backlash, and the direct successor in the pascal
world (at least in the sense of popularity), Turbo Pascal, was not standards
(report) conforming, and did allow direct OS access.

> It is hard to archive both goals (portability without box). I think
> the only solution is a portable library which allows access to the
> operating system features without being tied too close to a certain
> operating system.

POSIX is often named, but is essentially a Unix standard, minorly cleaned
up to give a appearance of general support.

POSIX is not strictly OS independant btw, since it relies on implementation
details in the form of system specific headers. These headers are often only
interpretable with the Unix system compiler -> out goes the OS independance.

This can be seen in the fact that e.g. many of the *nix toolchains
(scripting languages,gcc etc) are a bit weird on non-windows. mingw is only
just getting usable. Cygwin is an unix emulation. The scripting
languages require a lot of porting (by activestate/microsoft), and the
versions genuinely used on Windows are not the same sourcetree as used on
unix.

> Some try to carry one operating system interface like posix or
> windows everywhere. AFAIK perl tries to support posix also outside
> the unix world (at least to some degree), but this would only help
> portability when win32 interfaces were completely replaced by posix
> (which perl decided not to do).

this sounds a bit like it is os independant if all OSes are the same. that
is not OS independance, but supporting only one OS and forgetting about the
rest.

> Java decided to create its own library and I consider this solution
> as much more successful. Many people confuse the portability offered
> by this library with the portability of bytecode. Therefore many
> implementations try to use bytecode as solution for the portability
> problem. This are just the wrong conclusions. IMHO bytecode helps
> portability just a little but a portable library can solve the
> portability problem.

Java is the box principle again, just a larger and more complex version. It
is the direct successor to the Pascal systems you abhor in the first part of
the message.

> - opening, closing, reading and writing files
> - communication with sockets
> - support for http and html
> - reading and writing directories
> - suport for ASCII, UTF-8, UTF-16 and UTF-32 and various codepages
> - copying, moving and removing files and directory trees.
> - environment variables and program arguments
> - Output and positioning of the cursor at the console
> - graphics (based on gdi or X11, depending on the os)

FPC does the same, but does not try to use POSIX for anything else then Unix
like (roughly *BSD/Linux/Solaris/OS X/BeOS). The other platforms are simply
fully native.

Note that one of the problems of this are the assumptions. What is normal
for one platform, requires libraries on the other (ncurses,X11,GTK etc, but
also the other way around, namely the ability to convert between arbitrary
codepages that are not the current one (iconv))

> Other supported areas don't need the support of the operating
> system:
> - support for scanning from a file or string
> - arrays, hashes and bitmaps
> - unlimited precision integers.

(but might be dependant on the architecture, and via the ABI sometimes also
on the operating system)

> I would be interested to know about areas not covered by the Seed7
> library. I am also interested to know how important the support for
> a certain area is.

Duh, the most important one for business. Generalized database connectivity.

Portable GUI is also an important aspect. However there are several
tradeoffs there:

- easy portability of small apps -> use QT or Wxwidgets.
- full portability of complex apps -
1) own widgetset independant abstraction, added
2) use QT or so, but customize it platform dependantly or
use specialized builds to get it as native as possible.
3) (if native is less important) use C#/Mono/Java.
(the advantage of (1) is that you can stuff in assumptions about
what is important for your own apps.
- full control, own design gui (POS systems, many embedded applications),
with possibly, non-standard input methods
-> own fully owner drawn widget set.

People often rave on about the 'portability' of Unix widgetsets as QT and
GTK, but I often find them a bit clunky on Windows and OS X. Though it has
been getting better the last few years. I don't really have wxwidget
experience.

tm

unread,
Nov 24, 2009, 8:23:20 AM11/24/09
to
On 24 Nov., 11:00, Robbert Haarman <comp.lang.m...@inglorion.net>
wrote:
> > to create an independent portable library for Seed7. Until now there

> > are functions for:
>
> So, questions for you. Note that these are not meant to be unfriendly,
> they are just quick questions out of curiosity. Things for you to think
> about, and about which I wonder how you have solved them.

No problem. Please keep in mind that I add features when they are
needed. So there are features where no Seed7 user told me that
he/she needs them. If you really want a feature implemented now,
please tell me about and help me implementing it. :-)

> > - opening, closing, reading and writing files
>
> How does this deal with permissions? Locking?

In the area of permissions and locking I just use what fopen() from
posix and _wfopen() from Win32 delivers. AFAIK the windows behavior
to lock every file can be changed, but I am not sure that this is
possible at the level of _wfopen(). Maybe you can help me with that
issue.

> Binary files vs. text files?

When you open a file in Seed7 it is automatically binary. A C file
mode like "r" is binary under unix and text under windows. To allow
binary file modes everywhere C invented the modes "rb" (and friends).
I think this is not acceptable since it leads to unportable
programs. I turned the default around. The Seed7 file mode "r" is
always binary and there is a (non portable) "rt" file mode which is
text mode under windows and still binary under unix (where no such
distinction exists). The mid level reading functions (which read
words or lines from a file) ignore CRs when they are just before LF.
So for most normal applications you don't have to deal with this
differences.

> Stream file vs. record files?

Like in C all files are stream files. Record files can easily be
emulated based on stream files since file positioning is also
available.

> Fixed-length records vs. variable
> length records?

Currently there is no support for such things. I guess fixed-length
records and variable length records can be implemented using basic
file functions.

> How does it work on Palm OS?

I can't say, since I have no access to Palm OS. Can it be that Palm
OS has no file-system at all?

> Not saying you _must_ deal with all these, just asking you to realize that
> these issues are there so you can make a conscious decision to either
> specify the behavior or leave some unspecified behavior, with the associated
> risk of non-portability.
>
> > - communication with sockets
>
> TCP? UDP? Local? Raw? IPv4? IPv6? Can one plug in ones own protocols somehow?

Currently only TCP/IPv4 is supported. Seed7 can only use the
features provided by unix sockets and winsock so some unix
only or windows only features are not supported. Plug in
protocols are currently not supported.

> > - support for http and html
>
> How far does the support go? HTTP clients? Servers? Which versions of HTTP?
> Keep-alives? Caching?

There are functions to get files with HTTP. This functions deal
with various encodings, codepages and compression. Determining the
proxy server automatically is currently not supported, but planned
for the future. With the current HTTP support it is possible to get
www pages, the source of Wikipedia pages or a list of Google hits.
I know that there are also tools to do this, but using external
tools to accomplish tasks is not my vision of how a language should
support things.

The Seed7 package contains also Comanche, a simple HTTP server for
static pages. Keep-alives and Caching is currently not in the scope.

> For HTML: how do you deal with malformed HTML? What happens when I have
> well-formed HTML with, say, MathML in it, all neatly according to the
> specifications?

The support of HTML consists of scanner functions which are capable
to scan HTML symbols/tags/attributes/content from a file or string.
There is also similar scanner support for XML. For XML there is also
a dom parser. For HTML a dom parser is planned for the future.

> > - reading and writing directories
>
> Hard links? Soft links? Resource forks?

The creations of hard links is currently not supported. Symbolic
links are supported. As a matter of fact symbolic links can only
be supported when the operating system supports them. I consider
Resource forks as too operating system dependent to deal with
them. If you provide me with code that automatically copies
resource forks together with files I will happily include your
changes.

> > - copying, moving and removing files and directory trees.
>
> How does this deal with, e.g., permissions, ownership, special files, etc.?

For permissions, ownership, special files, etc. I use what posix
(and the posix like functions of Win32) allows me to do.

Your Java problem that File.renameTo may or may not allow moving
files is solved in Seed7. The Seed7 library tries to rename a file.
When this fails the file (or directory tree) is copied (with all
permissions, ownership and special files) to the new place and with
the new name. When the copy succeeds the original is deleted.
When the copy fails (no space on device or other reason) all
remains of the failed copy are removed. Note that this strategy
to move/rename files works for symbolic links but does not
preserve hard links (they are resolved to distinct files).

To change the modification time of a directory under windows
required a special function to work around a windows problem (the
windows utime() function does not work for directories).

Another problem is the behavior of utime with Borland for FAT32
files where the rounding of seconds is wrong. This required also a
special case function.

Getting the size of device files using the stat() function is not
guaranteed to work (just regular files are guaranteed to have a
size). Seed7 uses alternate code with seek() and tell() to obtain
the size of such files.

> > - Output and positioning of the cursor at the console
>
> How do you deal with different terminal types? What if there isn't a terminal?

On linux/unix/bsd I use terminfo (there is also code for termcap)
and under windows the console functions are used. The program
decides if it wants to do cursor positioning at the console. When
a program trys to write to a non existing console (under
linux/unix/bsd) some escape sequences will be written to stdout.
There are also text files (files with line structure and
cursor positioning which display in a graphical window.

> > - graphics (based on gdi or X11, depending on the os)
>
> Ok, I'll read the documentation to find out what primitives you implement.

Sorry to say, but there is currently no online documentation for
Seed7 graphics. The only "documentation" I can offer now is:
The file "graph.s7i" (part of the Seed7 package) which contains
function headers with documentation comments.

Several other things described above also have currently no online
documentation. Instead there are documentation comments in the
corresponding headers. If you find shortcomings in the current
online documentation please tell me.

Marco van de Voort

unread,
Nov 24, 2009, 8:23:46 AM11/24/09
to
On 2009-11-24, Robbert Haarman <comp.la...@inglorion.net> wrote:
>> windows everywhere.
>
> It is worth noting that POSIX was created specifically for this purpose:
> a common interface that all operating systems could implement, so that
> programs written to that interface would run on all compliant operating
> systems.

As far as I know this is not true. It was designed to let the different Unix
ports converge, and was only relabeled for general OS independance in later
(1990) iterations. The 1986 version was about Unix, and unix only afaik.

> Of all operating systems I have worked with, Windows is the only popular
> one that doesn't offer a useful degree POSIX compliance out of the box.
> However, this can be added.

Afaik it has been discontinued for years, since it was a security hazard.

And Classic macos, amigaos are not POSIX either.

Actually I can't really think of a POSIX supporting bit that isn't Unix from
an userland perspective. OS X and Beos count as unix in that sense IMHO
(complete with default installed unix shell and libraries)

>> Many people confuse the portability offered
>> by this library with the portability of bytecode.
>
> Bytecode does not aid portability. It is really just another form of code,
> and whether that code is portable or not depends on whether or not the
> specification it conforms to is portable.

bytecode provides some architecture independance. This should not be
underestimated.

>> - opening, closing, reading and writing files
>
> How does this deal with permissions? Locking? Binary files vs. text
> files? Stream file vs. record files? Fixed-length records vs. variable
> length records? How does it work on Palm OS?

Multiple file-streams ? Resource streams? VAX style file I/O? Amiga
Volumes? Or even ordinary SMB or NFS shares ? \\xx\yy

Opening devices? (also on windows with \\.\ notation).

> Not saying you _must_ deal with all these, just asking you to realize that
> these issues are there so you can make a conscious decision to either
> specify the behavior or leave some unspecified behavior, with the associated
> risk of non-portability.

This is one of the other problems of POSIX, it is the lowest common
denomitor only.

>> - communication with sockets
>
> TCP? UDP? Local? Raw? IPv4? IPv6? Can one plug in ones own protocols
> somehow?

Blocking or asynchonous? If (also) async, which OS dependant signaling
system do you use? The problem is that windows generally uses sockets
asynchronously, and unix synchronously.

>> - reading and writing directories
>
> Hard links? Soft links? Resource forks?

Joins and other NTFSv5 functionality.

Though it is generally already hard enough to make file I/O working reliably
on everything that windows can mount as share, since locking and
errorhandling are often slightly different.

>> - Output and positioning of the cursor at the console
>
> How do you deal with different terminal types? What if there isn't a
> terminal?

What about terminal Encoding independance ?

>> - graphics (based on gdi or X11, depending on the os)
>
> Ok, I'll read the documentation to find out what primitives you implement.

(usually general graphics are fine for showing a graph or a simple game in
an educational environment at best)

Rod Pemberton

unread,
Nov 24, 2009, 6:59:19 PM11/24/09
to
"bartc" <ba...@freeuk.com> wrote in message
news:LXFNm.7123$Ym4....@text.news.virginmedia.com...
>
> BTW I don't think fast(ish) interpreters are unusual; I remember looking
at
> the superfast Euphoria language and wondering how they managed to get it
so
> quick, although the language has been kept simple.

And? What'd they do? Threaded interpreter underneath? FORTH underneath?


Euphoria
http://www.rapideuphoria.com/

From what I gather:
- interpreted, compilable
- reduced version of C
- C type system removed
- C declarations and blocking converted to Pascal style

It seems to me that since they have only two types (atoms, sequences) - both
constructed using integers - that the interpreter could be stack based...


Another interpreted, C based, typeless language is Pawn. It was derived
from Ron Cain's Small C. They claim it is fast too.

Pawn (formerly Small Scripting Language)
http://www.compuphase.com/pawn/pawn.htm
http://www.ddj.com/184411074


Rod Pemberton


bartc

unread,
Nov 25, 2009, 10:15:12 AM11/25/09
to

"tm" <thomas...@gmx.at> wrote in message
news:c2f9b038-5bb2-4b91...@v37g2000vbb.googlegroups.com...

> On 23 Nov., 11:52, Bart <b...@freeuk.com> wrote:

>> a:=b
>>
>> In C, that might be just 2 instructions when a,b are both int. But
>> when they are variants, they can be anything. This assignment involves
>> freeing resources used by a (which is a recursive process when a is a
>> list of variants than can be lists...), and duplicating b, since my
>> language requires a deep copy; another possibly recursive process.
>
> Well. The Seed7 assignment does also a deep copy. For structured
> types Seed7 uses also a logic of freeing (possible recursive
> depending on the type) and duplicating (possible recursive depending
> on the type). But Seed7 also uses some improvement: When the right
> hand expression of the assignment is a temporary (which would be
> freed afterwards) a simple pointer assignment is done instead of the
> duplicating (and the "temporary" is not freed, but used as normal
> value). For simpler types like integer, float and others the
> assignment is implemented with a simple copy without freeing and
> duplicating. BTW: All this things can be decided at compile time.

That sounds pretty much like I what I do, except that (without the type
hinting I have now), I don't know what's what until runtime.

I avoid GC (for many reasons), and this scheme of using copies (ie. shallow
copies) of complex objects inside expressions works fairly well. Deep copy
(obtaining an independent copy) is done only on assignment to a variable,
and when returning a value from a function or operator.

The problem is trying to avoid all this work (even just the checking for it)
for most assignments which will be basic types.

>> And in a[i]:=b, there might be 4 types involved: the type of the
>> array, the type of the element (each element could be a variant and
>> therefore of different type), the type of i, and the type of b.
>
> I am not a big fan of arrays with mixed type elements. It hinders
> debugging and type checks or casts need to be added at many places
> (probably every time an array element is accessed).

Probably most arrays will have a uniform element type. But it is handy to be
able to create lists like this sometimes:

a:=('+',(10,('*',15.7,('len',"abcdef"))))

(That wasn't meant to look like a language, just the first mixed element
array example I could think of...)

My idea is to just allow anything while developing a program: no
declarations are needed and therefore all values are variant types. Then
gradually static typing can be added to refine the code, make it a bit
faster and maybe add a bit of checking.

> When there is
> a demand for an array with mixed type elements it is often possible
> that all desired element types can be described with one interface
> type (the elements probably need some general handling anyway).

(I will have to go and look up 'interface type')

>> All this is not much of a big deal, it just makes it harder to write
>> interpreters with decent performance. And (a note to 'tm') compiling
>> wouldn't help a great deal.
>
> The predecessor of Seed7 was also highly dynamic (which made
> it hard to compile to efficient machine code), but it also had an
> compiler. The redesign which resulted in Seed7 was especially done
> to allow compilation to efficient code. There is now a static
> concept around the dynamic features which makes this combination
> possible. So you need to define interface types and interface
> functions and the element type of an array must be known at compile
> time, but there is multiple dispatch, methods attached to objects
> (instead of classes) and types as parameters upon which an elegant
> template mechanism is based.

I have spent a lot of effort in trying to create a single language that was
fast and compilable, and that also had all the benefits of a dynamically
typed language.

Eventually I gave up; I couldn't reconcile the two. They serve different
purposes I think.

>> The hinting is done in the source code.
>
> What happens when the hints are wrong?

That's something I've just discovered: hints are supposed to result in
faster code. Now I find out you have to type-check all assignments! At
least, when the rhs is not known.


>
> BTW: Is it possible to download a documentation and an
> implementation of your language?

I'm not that advanced yet. And it wasn't really for public consumption. But
I might get round to it some day, as it might be of interest at least. I can
tell you that it is probably not as rigorous as Seed7.

--
bartc

bartc

unread,
Nov 25, 2009, 10:19:50 AM11/25/09
to

"Robbert Haarman" <comp.la...@inglorion.net> wrote in message
news:2009112322...@yoda.inglorion.net...

> On Mon, Nov 23, 2009 at 02:52:52AM -0800, Bart wrote:

> Both, and yes. But some more explanation is in order, even though I'm
> starting to feel guilty of hijacking the thread ...

(So did I...)

> Short example:
>
> #### Hello world in Voodoo
>
> section data
> greeting:
> string "Hello, world!\x00"
>
> section code
> import puts
> export main
>
> main:
> function argc argv
> call puts greeting
> return 0
> end function
>
> I hope that answers your questions. If you have more, I'll be happy
> to answer them.

OK, so perhaps a much more accessible version of llvm? (You know llvm, the
one with 44 documentation sets, none of which explains the basics.)

I might a have a look at Voodoo, after I have at least one working version
of my interpreter ...

--
Bartc

tm

unread,
Nov 28, 2009, 3:48:49 PM11/28/09
to
On 25 Nov., 16:15, "bartc" <ba...@freeuk.com> wrote:
> "tm" <thomas.mer...@gmx.at> wrote in message

That is exactly the problem I had with HAL (the predecessor of
Seed7). For many programs more than 95% of the assignments are
between basic types (which don't need runtime overhead for
dispatching and probably also type checking) and with the right
concept such basic assignments can be determined at compile time.
With the highly dynamic HAL language it was almost never possible
to decide such things at compile time.

For several years I thought about this problem again and again.
Being dynamic and optimizing the basic type assignments (and other
things) at compile time is just not possible for most of the time.
So I tried to turn the logic around: Being static by default and
switching to dynamic behavior automatically. This also turned to be
impossible. Either you have almost everything decided at runtime
without the possibility to optimize reasonable parts of the code or
you almost forbid the use of dynamic features. So after several
years of research I failed to find the magic bullet to do the
passage between static and dynamic typing automatically.

But I found something else: If you build a static typed language
with gateways to dynamic typing you can get best of both worlds.
This gateways must be explicit. This is the result of my research:
Automatic gateways just cannot work (I made a proof for this, but
currently I don't have this proof at hand).

Explicit gateways to dynamic typing work like this: The compiler
knows at which places you desire to do dynamic dispatch (deciding
about the function to execute at runtime). All normal (statically
dispatched) functions and the gateways can be determined and
type checked at compile time. This way the compiler can optimize
assignments and other things done with basic types.

To define the gateways to dynamic typing you have to define
interface types and interface functions. The exact details of the
OO system of Seed7 are explained here:

http://seed7.sourceforge.net/manual/objects.htm

> >> And in a[i]:=b, there might be 4 types involved: the type of the
> >> array, the type of the element (each element could be a variant and
> >> therefore of different type), the type of i, and the type of b.
>
> > I am not a big fan of arrays with mixed type elements. It hinders
> > debugging and type checks or casts need to be added at many places
> > (probably every time an array element is accessed).
>
> Probably most arrays will have a uniform element type. But it is handy to be
> able to create lists like this sometimes:
>
> a:=('+',(10,('*',15.7,('len',"abcdef"))))

Seed7 has a special type (ref_list) to describe expressions and
a type (reference) which describes one element of such an
expression.

> (That wasn't meant to look like a language, just the first mixed element
> array example I could think of...)
>
> My idea is to just allow anything while developing a program: no
> declarations are needed and therefore all values are variant types. Then
> gradually static typing can be added to refine the code, make it a bit
> faster and maybe add a bit of checking.

This sounds good, but I think you omitted something. Developing a
program consists of developing the functions AND developing the
data. Your concept concentrates on developing the functions without
developing the data. IMHO functions and data should have the same
importance: While you develop the functions you should (and probably
do) also think about the data types the functions deal with. You
probably do reasoning about the data too, but it is easy to forget
to write this things down. Functions and data descriptions should
be written in a machine readable (and therefore checkable) form.
Your programs needs a precise definition of function code and only
vague data definitions (in optional hints and comments). This is
IMHO an asymmetric and unbalanced approach. BTW, I don't thing that
the request for formal data definitions slows the programming down.
See here:

http://seed7.sourceforge.net/faq.htm#development_speed_and_type_checking

> > When there is
> > a demand for an array with mixed type elements it is often possible
> > that all desired element types can be described with one interface
> > type (the elements probably need some general handling anyway).
>
> (I will have to go and look up 'interface type')
>
> >> All this is not much of a big deal, it just makes it harder to write
> >> interpreters with decent performance. And (a note to 'tm') compiling
> >> wouldn't help a great deal.
>
> > The predecessor of Seed7 was also highly dynamic (which made
> > it hard to compile to efficient machine code), but it also had an
> > compiler. The redesign which resulted in Seed7 was especially done
> > to allow compilation to efficient code. There is now a static
> > concept around the dynamic features which makes this combination
> > possible. So you need to define interface types and interface
> > functions and the element type of an array must be known at compile
> > time, but there is multiple dispatch, methods attached to objects
> > (instead of classes) and types as parameters upon which an elegant
> > template mechanism is based.
>
> I have spent a lot of effort in trying to create a single language that was
> fast and compilable, and that also had all the benefits of a dynamically
> typed language.

Interestingly I did the same. This goal seems to be something that
many people try to reach.

> Eventually I gave up; I couldn't reconcile the two. They serve different
> purposes I think.

Reconciling the two automatically is impossible (see above). The
best way to reconcile them is IMHO by using explicit gateways.

> >> The hinting is done in the source code.
>
> > What happens when the hints are wrong?
>
> That's something I've just discovered: hints are supposed to result in
> faster code. Now I find out you have to type-check all assignments! At
> least, when the rhs is not known.
>
> > BTW: Is it possible to download a documentation and an
> > implementation of your language?
>
> I'm not that advanced yet. And it wasn't really for public consumption. But
> I might get round to it some day, as it might be of interest at least. I can
> tell you that it is probably not as rigorous as Seed7.

Some people probably think that Seed7 has "rigorous" concepts
because of sadistic or masochistic desires. This is not correct.
Declarations, static typing, structured constructs, portability and
other concepts are NOT introduced to torture programmers, but for
other reasons.

I like to study the source code of programs. For many application
areas you can find source code in the Internet or at other places.
Programs are written in various languages. Executing a program is
only one aspect. Trying to find out how programs work and possibly
rewriting them in another language like Seed7 is another aspect.
Knowing the programming language naturally helps understanding a
program. But there are also other things that hinder understanding
even when you are an expert of the language used. E.g.:

- Some old Basic programs from the line number era are a good
example of spaghetti code. Have you ever tried to find out what
is going on in spaghetti code? I developed the Bas7 Basic
interpreter (see: http://seed7.sourceforge.net/scrshots/bas7.htm)
to help porting historic programs like Startrek and Eliza.

- Turbo "Pascal" programs with direct access to hardware, BIOS
calls and inline assembler often turned out to be essentially
useless. You need to know the address of video memory, at which
address the shift state is stored, the meaning of various BIOS
calls and x86 assembler instructions just to find out that the
program is a high performance version of hello world. The
programmer was probably proud to squeeze every cycle of the
processor, but for a reader such "optimizations" were just
useless.

- Programs in dynamic languages are probably more easy to write
than to read. To find out what it going on it is really helpful
to know what values are held by variables or parameters. You end
up with static analysis over several function calls just to find
out that a certain variable will only hold integer values (In the
calling function a + is used, so it probably processes numeric
values. But wait, the + can also concatenate strings so it might
processing strings or both. Here the value is assigned to another
variable, so lets look what is done with this variable). If you
write such programs or execute them you will probably not care,
but to understand them declarations and type information is
helpful.

There were also certain properties which help understanding (aka
reading) a program.

- Structured constructs.
- When language functions are used instead of direct access to
the hardware or operating system specific functions.
- Declarations which specify a type.
- And several other properties.

So Seed7 tries to encourage a programming style which I would like
to find when I search for source code.

Programming is not only about writing programs. It is also about
reading the programs written by other people. Programs are more
often read than written, so making reading easier saves more time
than making writing easier (at the cost of readability).

bartc

unread,
Nov 29, 2009, 6:43:36 PM11/29/09
to

"tm" <thomas...@gmx.at> wrote in message
news:cd3b1f9a-6947-42b7...@e23g2000yqd.googlegroups.com...

> On 25 Nov., 16:15, "bartc" <ba...@freeuk.com> wrote:
>> "tm" <thomas.mer...@gmx.at> wrote in message

> See here:
> http://seed7.sourceforge.net/faq.htm#development_speed_and_type_checking

That specific link doesn't seem to exist, or the faq section name has
changed.

> Some people probably think that Seed7 has "rigorous" concepts
> because of sadistic or masochistic desires. This is not correct.
> Declarations, static typing, structured constructs, portability and
> other concepts are NOT introduced to torture programmers, but for
> other reasons.

My memories of using someone else's language are of spending 90% of my time
fighting the language and compiler, instead of getting on with writing my
code. (That was nearly 30 years ago but I suspect the same might still be
true now if I tried to use one seriously.)

(Mind you I did also use my own hardware for a few years, until it was no
longer practical.)

> I like to study the source code of programs. For many application
> areas you can find source code in the Internet or at other places.
> Programs are written in various languages. Executing a program is
> only one aspect. Trying to find out how programs work and possibly
> rewriting them in another language like Seed7 is another aspect.
> Knowing the programming language naturally helps understanding a
> program. But there are also other things that hinder understanding
> even when you are an expert of the language used. E.g.:
>
> - Some old Basic programs from the line number era are a good
> example of spaghetti code. Have you ever tried to find out what
> is going on in spaghetti code?

Not much, but I have tried to decipher complex C preprocessor macros (some
of those I've seen for C++ headers I doubt could ever have been intended for
humans to understand).

I think a lot of code is difficult to follow because much of the overall
structure and rationale was in the mind of the programmer, and that doesn't
come across in the source code. Even commenting, at the local level, doesn't
help. (I know this from looking at my own projects months or years later.)

Restricting syntax and having coding disciplines might help to some extent,
provided they aren't responsible for the convoluted code in the first place.

> - Turbo "Pascal" programs with direct access to hardware, BIOS
> calls and inline assembler often turned out to be essentially
> useless. You need to know the address of video memory, at which
> address the shift state is stored, the meaning of various BIOS
> calls and x86 assembler instructions just to find out that the
> program is a high performance version of hello world. The
> programmer was probably proud to squeeze every cycle of the
> processor, but for a reader such "optimizations" were just
> useless.

Those programs were written to perform a task; being able to run the same
source 25 years later probably wasn't a priority.

Doubtless I had similar software which was superceded as hardware changed;
why would somebody want to run some crummy application from the 1980s
anyway, other than for curiosity.

> - Programs in dynamic languages are probably more easy to write
> than to read. To find out what it going on it is really helpful
> to know what values are held by variables or parameters. You end
> up with static analysis over several function calls just to find
> out that a certain variable will only hold integer values (In the
> calling function a + is used, so it probably processes numeric
> values. But wait, the + can also concatenate strings so it might
> processing strings or both. Here the value is assigned to another
> variable, so lets look what is done with this variable). If you
> write such programs or execute them you will probably not care,
> but to understand them declarations and type information is
> helpful.

Have you noticed pseudo-code tends to be written in a certain style? Such a
style is chosen to convey an idea without bothering with details.

And yet that style also easily be some actual scripting language or other
(and not far off what my language looks like).

You then have to wonder why the code is then written in something like C
with it's mess of punctuation and macros rather than something closer to the
pseudo-code.

> So Seed7 tries to encourage a programming style which I would like
> to find when I search for source code.

The main feature I need, when looking at other people's code, is 'comments',
especially to do with how the thing works rather than licences, copyrights,
and version histories.

--
Bartc

robin

unread,
Dec 9, 2009, 6:07:56 AM12/9/09
to
"James Harris" <james.h...@googlemail.com> wrote in message
news:2aff8423-152e-4921...@j4g2000yqe.googlegroups.com...
| It is generally desirable for a high level language to be independent
| of any target hardware but ISTM that sometimes it is useful to write
| system-specific modules. For example, these could be specific to a CPU
| or to a hardware device. Any disagreement on this?
|
| If the above is accepted, I'd like to ask about the processor as one
| potential target. It could be helpful to the programmer to allow him
| or her to embed assembler in high level code.
|
| Issues include: appearance (beauty or ugliness), line continuations
| and comments (use the HLL's scheme or not), freely mix HLL and
| assembler or how to indicate which is being used, independence of a
| particular assembler (if an assembler is used as part of code
| generation), recognising registers used in the programmer's asm code,
| allowing the translator to supply real register names (by allowing the
| programmer to use explicit temporaries), accessing HLL symbols from
| the asm code, and suchlike.
|
| Is anyone else allowing embedded assembler or have you any thoughts on
| how it *should* be done?

The language XPL permits assembler-style code to be embedded
in the source code. Such instructions can reference variables defined
in the high-level code.
The facility is quite handy when developing a compiler (which was the
intended use for the language).


James Harris

unread,
Feb 1, 2010, 9:58:50 AM2/1/10
to
On 9 Dec 2009, 11:07, "robin" <robi...@bigpond.com> wrote:
> "James Harris" <james.harri...@googlemail.com> wrote in message

If XPL was intended for compiler development it seems odd that it
needs to drop to assembler.

Just thinking about it, though, having support for embedded asm in a
language *being developed* may have benefits if used to write a
bootstrap compiler.

Although writing much of a compiler in assembler rather than a high-
level language sounds a seriously bad idea because of the work
involved (i.e. the work involved for a full language) it has the
distinct advantage to a language designer of forcing him or her to
consider how each HLL concept will compile. This may also encourage
inclusion of simple concepts rather than abstract or baroque ones.

It wouldn't be good for all languages as some are designed around
abstract concepts but it should help to design elements which are
simple and efficient.

When talkng about Algol-60 the book Priciples of Programming Languages
speaks of an analogy between language design and building bridges.
Goals of

Efficiency,
Economy and
Elegance

are suggested. The first two are fairly easy to appreciate but what
about elegance? The author says this is related to simplicity, balance
and "form following function," and is something gained by design
experience, an eye for what is right.

My take on that, in part, adds that a language should focus on and
expose fundamental concepts, not try and hide them in abstract
mechanism. Certainly the things I am most pleased about with my design
so far are those where I have successfully stepped back and seen the
bigger picture and then identified the underlying fundamentals.

Although not all fundamental concepts are exposed in assembler many
are. So bootstrapping a compiler from assembler may not be a bad idea.

Any views on this?

James

Marco van de Voort

unread,
Feb 1, 2010, 10:53:32 AM2/1/10
to
On 2010-02-01, James Harris <james.h...@googlemail.com> wrote:
> Although not all fundamental concepts are exposed in assembler many
> are. So bootstrapping a compiler from assembler may not be a bad idea.
>
> Any views on this?

I wouldn't spend one minute more on the bootstrapping system than required.
It is rather uninteresting.

While assembler in the compiler is not terribly interesting, in the runtime
lib it is almost required.


Ray

unread,
Feb 1, 2010, 11:44:41 AM2/1/10
to
Marco van de Voort wrote:

>> Any views on this?

In a truly high level language where you're doing things where
you never have to care about binary representation, never have to
interface with system drivers or call OS functions, don't intend
to write any drivers, and can get "enough" efficiency from good
algorithms and overall data structure design as opposed to
micro-optimizing data structures or machine code sequences down
to the last byte, you don't need assembly, or anything like it.
It is enough, in such a language, to provide binary serial I/O
so you can read and write data streams over named pipes. That,
plus a bunch of "shim" code, will get you the ability to integrate
(albeit somewhat slowly) with most things without ever having to
care about their internal representations or them having to care
about yours. For an example of this, think of a good "toy lisp"
system such as most of us build sometime before graduating college.

In a language where you want to interface with the Operating System
(which is probably written in C) or conform to an ABI (which is
probably defined in C) so you can interface directly (via procedure
calls) with another language, you pretty much have to have a standard
way to embed or integrate with C (or C++) and provide data structures
and pointers to them in the form that C and C++ expects (which form
is probably dictated by the local C compiler). Assembly is optional.
This is the graduate project or open-source language where you take
that "toy lisp" I mentioned above and make it able to talk to the
outside world, probably via compilation to C and/or various degrees
of "integration" with C.

And in a system programming language, where you have to care about
the exact binary representation of things down to the last byte,
so you can write drivers, define ABI's, map datagrams directly onto
arrays, etc, assembly (or something very very much like it) is the
only way.

Bear

PS. FWIW, I think that plan9/Inferno (and their scheme where all
processes communicated via named pipes) were a much much better
approach to integration with the OS than the POSIX paradigm (where
integration is based on transfer of control via procedure calls).

And much easier for language implementors, of course, because
once you can do binary I/O in a language, you're done. There's
no need for assembly unless you want to *internally* microoptimize
your implementation, and when you do microoptimize, it won't mess
up integrating with anything else.

Marco van de Voort

unread,
Feb 2, 2010, 3:48:06 AM2/2/10
to
On 2010-02-01, Ray <be...@sonic.net> wrote:
>> While assembler in the compiler is not terribly interesting, in the
>> runtime lib it is almost required.
>
> In a truly high level language where you're doing things where

<skip rant>

I don't care about toy lisp languages.

I was (obviously) talking about the case where your runtime systems are in
the same language. I realize this is not the case in all languages.

I also realize that you can do with only a binary pipe as input/output (we
called it telegraph key in, bell out). I also realize that this can
happen even in this VLSI age. (e.g. when you feed a stream over spi to a
HLL engine on a uc). But these are the exceptions. Most languages have I/O
nowadays :-)

(and btw, I like to use MATLAB's language as example. Much less people would
call it a toy, but it is still quite high level)

But again, I was talking about use in the runtime library. It should be
clear that that excludes whole classes of toy langages.

Ray

unread,
Feb 2, 2010, 11:00:59 PM2/2/10
to
Marco van de Voort wrote:

> On 2010-02-01, Ray <be...@sonic.net> wrote:
>>> While assembler in the compiler is not terribly interesting, in the
>>> runtime lib it is almost required.

>> In a truly high level language where you're doing things where

> I don't care about toy lisp languages.

It's okay; you don't have to! Undergrads will still be forced to
make them whether we care or not.

> But again, I was talking about use in the runtime library. It should be
> clear that that excludes whole classes of toy langages.

Okay. In that case you'll be looking at what I called a "systems
programming language" and you need to get down to assembly (or
to something that assembly-language routines can easily integrate
with) somewhere. 'Cause, simply put, you cannot write runtime libs
(drivers in particular) without being able to refer to particular
addresses in memory space or being able to specify particular
binary layouts for data representation.

C is an excellent example of a systems programming language.

Bear


Robbert Haarman

unread,
Feb 3, 2010, 3:08:26 AM2/3/10
to
Hi Ray,

I agree with what you have written. I also have some additions.

On Mon, Feb 01, 2010 at 08:44:41AM -0800, Ray wrote:
> Marco van de Voort wrote:
>
> > On 2010-02-01, James Harris <james.h...@googlemail.com> wrote:
> >> Although not all fundamental concepts are exposed in assembler many
> >> are. So bootstrapping a compiler from assembler may not be a bad idea.
>
> >> Any views on this?
>
> > I wouldn't spend one minute more on the bootstrapping system than
> > required. It is rather uninteresting.
>
> > While assembler in the compiler is not terribly interesting, in the
> > runtime lib it is almost required.
>
> In a truly high level language where you're doing things where
> you never have to care about binary representation, never have to
> interface with system drivers or call OS functions, don't intend
> to write any drivers, and can get "enough" efficiency from good
> algorithms and overall data structure design as opposed to
> micro-optimizing data structures or machine code sequences down
> to the last byte, you don't need assembly, or anything like it.

In fact, allowing assembly may very well complicate your compiler
so that optimization becomes much more difficult. For an example, see
gcc's annotations for inline assembly:
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5

Without annotations, the compiler has to either figure out which registers
and memory locations are clobbered, or assume everything becomes clobbered,
or trust the programmer not to break the compiler's idea of which values
are where (which the programmer may or may not be aware of).

Thus, it is not clear that allowing assembly in the language will help
bootstrap the compiler; it may well mean extra work.

In any case, allowing assembly code will generally make your language
unsafe, which is, in my opinion at least, an undesirable property for
programming languages in general, and for application programming in
particular. And make no mistake: a large fraction of software development,
perhaps even most of it, is application programming, where high-level,
safe languages are a boon.

> And in a system programming language, where you have to care about
> the exact binary representation of things down to the last byte,
> so you can write drivers, define ABI's, map datagrams directly onto
> arrays, etc, assembly (or something very very much like it) is the
> only way.

To expand on the "or something very very much like it" part, it is important
to keep in mind which features are actually needed. While it is tempting to
say "assembly allows full control, so I'll just use that", assembly is
also tedious to program in and non-portable. Those are not the features
you are looking for.

If what you need is to interface with C, perhaps all you need is
something like Perl's XS, Ruby's dl, or SWIG. If you actually need to
control exactly how things are stored in memory, something like Ada's
representation clauses can help: these allow you to define how things
are stored down to the bit level. In all these cases, you can interface
with code that expects a specific calling convention or data layout
without abandoning your high-level language.

There is one thing, however, that assembly language allows you to do that
may not otherwise be possible in a high-level language, and that is
determining the exact instructions that are used. Even though it has been
said for many years now that great compilers do a better job of generating
performant machine code than great coders writing assembly, it is conceivable
that certain instruction sequences, while optimal, would never be generated
by a compiler (for example, because the instructions did not yet exist
at the time the compiler was written).

Another example would be instructions like the x86 LMSW instruction,
which is used so rarely that it is doubtful that a compiler should be able
to emit it, yet, on the other hand, indispensible for modern operating
systems.

To accomodate such cases, it must be possible for the language to interface
with code that includes the instructions that the compiler will not emit.
Though wether the best way to accomplish that is through inline assembly or
through providing a way to call code written in a different language
(foreign function interface), I am not certain about.

Regards,

Bob


Calum Grant

unread,
Feb 3, 2010, 5:04:49 AM2/3/10
to
On Feb 3, 4:00 am, Ray <b...@sonic.net> wrote:
> Marco van de Voort wrote:
>

Every programming language needs an API which allows it to talk with
the outside world. This allows you to e.g.

- embed your language in another system
- if your language is interpreted, then provides scripting
capability to the host
- gives you access to a much richer set of libraries
- allows programmer to do native/OS operations
- allows your language's libraries to just wrap existing C libraries
- makes writing language libraries much more easy and can be done by
third-parties
- allows programmer to statically link to assembler code
- allows you to write performance-critical code in C or assembler
(assuming your language does not run as fast as C).

I'm struggling to think of a case where inline assembly language would
be better than an external C function (with embedded assembly if you
must). The API pretty-well covers the assembler case, and you can
then just use a proper system assembler instead of implementing your
own. Good API examples include Python, Tcl and Java (JNI), though
pretty well every language implementation has its own API.

A particularly nice feature is to be able to dynamically-link [C]
libraries at run-time.

Embedding assembler is just a convenience. I would imagine that the
only people who need to do this are library writers. If you find
yourself needing to resort to assembler a lot, then it's a clue that
either (a) your language isn't efficient, or (b) your language lacks
some other capability. I'd address the root causes of (a) and (b) as
a priority.

Calum

Marco van de Voort

unread,
Feb 3, 2010, 7:31:07 AM2/3/10
to
On 2010-02-03, Ray <be...@sonic.net> wrote:
>> On 2010-02-01, Ray <be...@sonic.net> wrote:
>>>> While assembler in the compiler is not terribly interesting, in the
>>>> runtime lib it is almost required.
>
>>> In a truly high level language where you're doing things where
>
>> I don't care about toy lisp languages.
>
> It's okay; you don't have to! Undergrads will still be forced to
> make them whether we care or not.

Sure. They'll also make GUIs in 16-bit assembler.

>> But again, I was talking about use in the runtime library. It should be
>> clear that that excludes whole classes of toy langages.
>
> Okay. In that case you'll be looking at what I called a "systems
> programming language" and you need to get down to assembly (or
> to something that assembly-language routines can easily integrate
> with) somewhere.

For me a system programming language is a language that is solely targeted
at system programming (like C and M2), not one that is just able to.


> 'Cause, simply put, you cannot write runtime libs (drivers in particular)
> without being able to refer to particular addresses in memory space or
> being able to specify particular binary layouts for data representation.

But being able to refer to an adress doesn't make you a system programming
language. E.g. C++, Delphi can do so too, and are targeted towards app
development.

> C is an excellent example of a systems programming language.

I don't know. I think Modula2 is a better example since directly targeted as
such. C's original main objective was afaik more the asm->hll transition to
achieve architecture independance.

Marco van de Voort

unread,
Feb 3, 2010, 7:49:26 AM2/3/10
to
On 2010-02-03, Robbert Haarman <comp.la...@inglorion.net> wrote:
>> algorithms and overall data structure design as opposed to
>> micro-optimizing data structures or machine code sequences down
>> to the last byte, you don't need assembly, or anything like it.
>
> In fact, allowing assembly may very well complicate your compiler
> so that optimization becomes much more difficult. For an example, see
> gcc's annotations for inline assembly:
> http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5
>
> Without annotations, the compiler has to either figure out which registers
> and memory locations are clobbered, or assume everything becomes clobbered,
> or trust the programmer not to break the compiler's idea of which values
> are where (which the programmer may or may not be aware of).

Yes. If you use it in a big procedure with lots of live variables. But I
refered to the RTL, where it is mostly e.g. floatingpoint-to-string stuff.

> Thus, it is not clear that allowing assembly in the language will help
> bootstrap the compiler; it may well mean extra work.

I did not talk about bootstrapping at all. After the initial bootstrap, the
whole concept is IMHO uninteresting and solved 30 years ago.

> In any case, allowing assembly code will generally make your language
> unsafe, which is, in my opinion at least, an undesirable property for
> programming languages in general, and for application programming in
> particular.

I don't see this, since then the said part of the runtime will be done in a
different unsafe language. So the netto amount of unsafe code remains the
same.

> And make no mistake: a large fraction of software development, perhaps
> even most of it, is application programming, where high-level, safe
> languages are a boon.

.NET is by far the largest in that, and .NET apps can be mixed safe and unsafe.

But more importantly nobody said something about application development
(did you load a precooked rant?). It was specifically targeted at the case
that the language runtime in the same language

>> so you can write drivers, define ABI's, map datagrams directly onto
>> arrays, etc, assembly (or something very very much like it) is the
>> only way.
>
> To expand on the "or something very very much like it" part, it is important
> to keep in mind which features are actually needed. While it is tempting to
> say "assembly allows full control, so I'll just use that", assembly is
> also tedious to program in and non-portable. Those are not the features
> you are looking for.

I did not say that. I talked specifically about a need for it in the runtime
lib.

> If what you need is to interface with C, perhaps all you need is

That is a whole different matter and is not the same what you rant about
above, since C is still unsafe.

Anyway, skipped the rest, note that the whole discussion was about the
*language runtime*.

> something like Perl's XS, Ruby's dl, or SWIG.

Well, I do want to comment on that since it is a particular interest of
mine. Nearly all C header translation systems that I
have seen are crippled semi-automatic at best. (and usually far from even
that). And yes, I do know swig.

James Harris

unread,
Feb 3, 2010, 10:58:40 AM2/3/10
to
On 1 Feb, 15:53, Marco van de Voort <mar...@snail.stack.nl> wrote:

> On 2010-02-01, James Harris <james.harri...@googlemail.com> wrote:
>
> > Although not all fundamental concepts are exposed in assembler many
> > are. So bootstrapping a compiler from assembler may not be a bad idea.
>
> > Any views on this?
>
> I wouldn't spend one minute more on the bootstrapping system than required.
> It is rather uninteresting.

I suspect you are looking at this from the point of view of compiling
a language that's already been designed, given your experience with
Virtual Pascal and whatever else. From that standpoint I'd see it the
same way.

I'd also agree (to a point) if varying an existing language. Probably
most languages begin this way and it seems far easier to modify a
language than design a new one from scratch.

However, what of coming up with a mainly new design?

> While assembler in the compiler is not terribly interesting, in the runtime
> lib it is almost required.

In the suggestion the assembler in the compiler would be present only
until it could be replaced (a little at a time) by the new language.
I'm not saying I'm definitely sold on the idea but it may be worth
exploring.

James

Marco van de Voort

unread,
Feb 3, 2010, 11:09:13 AM2/3/10
to
On 2010-02-03, Calum Grant <spa...@calumgrant.net> wrote:
> I'm struggling to think of a case where inline assembly language would
> be better than an external C function (with embedded assembly if you
> must).

E.g. on windows you don't have an external C function. Adding a C function
means adding a dependancy to a C compiler and its libs, quirks.

> The API pretty-well covers the assembler case, and you can then just use a
> proper system assembler instead of implementing your own. Good API
> examples include Python, Tcl and Java (JNI), though pretty well every
> language implementation has its own API.

Neither of these implement their own runtime system in the own language,
which was the subject. Then even relative simple things (like floating point
mask control, floating point primitives, exception handling, helpers for
the object mode, GC/memory management, bitlevel instructions that aren't
modeled), and more advanced stuff like SSE, MMX, drivers.

It also helps to move intrinsics from compiler to libraries.

bartc

unread,
Feb 3, 2010, 12:19:37 PM2/3/10
to

"James Harris" <james.h...@googlemail.com> wrote in message
news:f8e9d24a-71ff-4261...@19g2000yql.googlegroups.com...

This is exactly how I worked on my first compiler, which was a student
project.

Probably there were other alternatives than assembler, but the project spec
was to start with a 3-inch thick assembly listing, the output of a
cross-compiler, that didn't work, and get it working. I found it easier to
start again, still in assembler.

And at the end, just to test it out, it got rewritten gradually in the
language** it was compiling.

There was no runtime that I remember, just OS calls, as the language was
rather simple.

I used assembler to create a compiler a couple more times, once because
there was no alternative, and later because I just found it easier (than
battling with someone else's language...)

These days however with computers and HLLs everywhere for the purposes of
cross-compiling, there is no real need for this.

(** 'Babbage', a machine-oriented language now long dead.)

--
Bartrc

Robbert Haarman

unread,
Feb 3, 2010, 1:55:06 PM2/3/10
to
Hi Marco,

My previous post seems to have caused some confusion and rubbed you the
wrong way. For that, I apologize. I value you and your contributions to
this group greatly. My sincerest apologies to you and anyone else who
has been confused and/or angered by my earlier post; I assure you that
this has been entirely unintentional on my part.

To attempt to clear up some of the confusion: my post was meant as a reply
to comments by Ray Dillinger about high-level programming vs. system
programming, and, for a small part, James Harris's question about
using assembly to bootstrap a compiler.

Regards,

Bob


Ray

unread,
Feb 3, 2010, 5:23:28 PM2/3/10
to
Robbert Haarman wrote:

>> In a truly high level language where you're doing things where
>> you never have to care about binary representation, never have to
>> interface with system drivers or call OS functions, don't intend
>> to write any drivers, and can get "enough" efficiency from good
>> algorithms and overall data structure design as opposed to
>> micro-optimizing data structures or machine code sequences down
>> to the last byte, you don't need assembly, or anything like it.
>
> In fact, allowing assembly may very well complicate your compiler
> so that optimization becomes much more difficult. For an example, see
> gcc's annotations for inline assembly:
> http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5

Yah. C makes binary-layout guarantees (though not as many as some
folks think) that practically invite assembly-language routines to
mess with its live data, and also provides for embedded assembly.
It is difficult, perhaps impossible, in that situation, to also
make guarantees either of correctness or safety.

But C, on most systems, is the fundamental systems language - the
means by which all the ABI's, drivers, OS interfaces, etc, are
*DEFINED*. And the flexibility provided by assembly-language
routines is employed (I don't quite say necessary, just 'employed')
to facilitate that task.

Here's the thing; C facilitates a certain *KIND* of operating
system design. If you're designing an OS for a single-tasking,
single-cpu system with a language, like C, that naturally has
only a single flow of control, it makes sense for most things
to be provided via transfers of that single flow of control.
IOW, in that scenario it's most efficient if most operating
system services are provided by function calls, with arguments
often including pointers to in-memory data structures.

If you're on an operating system that uses that design paradigm
then your language runtime has to conform to a particular ABI
when calling OS functions. These dictate the layout of data,
the format of pointers, the layout of call frames, etc. You
have to care about the location and binary format of your data,
or your OS calls won't work. And in order to do that, you
need something like assembly.

But Operating systems don't have to be that way. It made sense
when most machines were in fact single-processor machines, but
it makes less sense now. If you were designing an operating
system today, you'd take advantage of multiprocessing and have
operating system services provided by parallel threads. Instead
of making an ABI-conforming procedure call, you'd access OS
services via a communications channel so the requesting thread
could go off and do other stuff while waiting for the service
to be provided by another thread running on another CPU. The
ABI's and data layouts of your runtime relative to the Operating
System's wouldn't matter. Check Inferno for an example of this.

So what it comes down to is that a hard-core systems programming
language in which you're actually writing drivers and stuff has to
have the kind of low-level capability that assembly etc provides.
But beyond that, for application programming, etc, the requirement
breaks differently depending on what kind of OS you're talking about.

In a unix-ish operating system (windows, mac, unix, linux, etc)
applications programming also requires sufficient capabilities
to make calls to the OS routines. If your data layouts, runtimes,
or call-frame representations are any different from those of the
host system, that inevitably means some kind of translation
running at the level of bits and bytes has to sit between you
and the operating system, and assembly language (or whatever
language the OS is implemented in) FFI's are the traditional
means of achieving it.

In a more modern OS (Inferno, Plan9, etc) all that applications
programming requires is the ability to access operating system
services by doing binary I/O over named pipes. The classic
"toy lisp" that Mr. Van De Voort is so adamantly not interested
in, while still not suitable for writing the absolute lowest-
level drivers, is at least a full-fledged applications language
on an equal footing with everything else on the system, and
doesn't have to make promises about the binary layout of
anything, not even its call frames.

> In any case, allowing assembly code will generally make your language
> unsafe, which is, in my opinion at least, an undesirable property for
> programming languages in general, and for application programming in
> particular.

Agreed, somewhat. But it takes artistry to make a language safe
without making it crippled. And, when you actually need to do
systems programming rather than applications programming, it kind
of sucks (for most people -- we have to own up to being aberrations
here) to have to learn an entire new language.

> And make no mistake: a large fraction of software development,
> perhaps even most of it, is application programming, where high-level,
> safe languages are a boon.

Sure. But none of the applications programs are worth a damn if
not supported by good systems programming. The need for systems
languages will never go away.


> To expand on the "or something very very much like it" part, it is
> important to keep in mind which features are actually needed. While it is
> tempting to say "assembly allows full control, so I'll just use that",
> assembly is also tedious to program in and non-portable. Those are not the
> features you are looking for.

Assembly avoids dependencies on anything else besides the hardware,
and when you're doing systems programming, that is *EXACTLY* what
you're looking for.


> To accomodate such cases, it must be possible for the language to
> interface with code that includes the instructions that the compiler will
> not emit. Though wether the best way to accomplish that is through inline
> assembly or through providing a way to call code written in a different
> language (foreign function interface), I am not certain about.

There is really no difference. Inline assembly *is* "code written in
a different language" and your runtime *does* call it via some kind of
"foreign function interface", even if the FFI can be trivial.

Bear

Robbert Haarman

unread,
Feb 3, 2010, 10:51:57 PM2/3/10
to
Hi Ray,

On Wed, Feb 03, 2010 at 02:23:28PM -0800, Ray wrote:
>
> > To expand on the "or something very very much like it" part, it is
> > important to keep in mind which features are actually needed. While it is
> > tempting to say "assembly allows full control, so I'll just use that",
> > assembly is also tedious to program in and non-portable. Those are not the
> > features you are looking for.
>
> Assembly avoids dependencies on anything else besides the hardware,
> and when you're doing systems programming, that is *EXACTLY* what
> you're looking for.
>
> > To accomodate such cases, it must be possible for the language to
> > interface with code that includes the instructions that the compiler will
> > not emit. Though wether the best way to accomplish that is through inline
> > assembly or through providing a way to call code written in a different
> > language (foreign function interface), I am not certain about.
>
> There is really no difference. Inline assembly *is* "code written in
> a different language" and your runtime *does* call it via some kind of
> "foreign function interface", even if the FFI can be trivial.

In a way, you are right. Indeed, inline assembly is just one way to
provide the ability to interface with code in other languages - in this
case, assembly.

However, there is one important distinction that I
would like to make, which is also how it all ties in to what Marco van der
Voort is concerned with, and that distinction lies in the status
assembly occupies in the language specification and/or implementation.

When I wrote "call code written in a different language", I was thinking
about a module, written in that language (language A), in any way you
normally could write code in that language. That is, without any
reference to the language (language B) the rest of your code is written in.
The code in language B interacts with the code in language A either through
a common calling convention (language support) or through glue code
(no language support needed).

By contrast, to support inline language A in language B, at least the
implementation of language B must have some sort of support for it. At the
very least, it must provide a syntactic form to allow embedding code in
language A and dump it straight through to its output, but many
implementations go much further than that, e.g. allowing identifiers in
language B to be referenced from the embedded language A, which requires
the implementation of language B to have at least enough knowledge of
language A to be able to recognize such identifiers and substitute
appropriate language A code for them.

It seems to me that providing an interface to external modules written in
different languages has several benefits over allowing a language to be
embedded, namely:

- The impact on your language and/or its implementation is likely to be
less (no extra syntactic forms may be needed, and certainly no parser
for another language, hooks into the optimizer/register allocater, etc.)

- You can interface with several other languages through the same mechanism
(no code specifically for supporting language A also means language A
is not your only choice)

- You don't compromise the safety of programs written in your language.
Code in modules in different languages may still be unsafe, but if your
language is safe, you can trivially see if your program is: if it
doesn't link to modules in different languages, it's safe.

All this makes me lean towards saying: You don't have to bother supporting
assembly in your language; just make sure that you can interface with
code in other languages somehow and you should be fine. And since assembly
allows you to code anything your machine can do, about the only thing you
need to be able to interface with assembly code is a predictable calling
convention and a way to tell your language implementation "Don't worry, this
function is actually there, even if you can't see it" (cf. C's prototypes,
NASM's extern).

Regards,

Bob


Calum Grant

unread,
Feb 4, 2010, 11:33:36 AM2/4/10
to
On Feb 3, 4:09 pm, Marco van de Voort <mar...@stack.nl> wrote:

> On 2010-02-03, Calum Grant <spam...@calumgrant.net> wrote:
>
> > I'm struggling to think of a case where inline assembly language would
> > be better than an external C function (with embedded assembly if you
> > must).
>
> E.g. on windows you don't have an external C function. Adding a C function
> means adding a dependancy to a C compiler and its libs, quirks.

C is far more portable than machine code. Of course you have static
and dynamic external linkage on Windows. You only create a dependency
on the C standard library if you decide to use functions from the
libraries.

I'm not suggesting that you write C inline in your code (though it
would be a more portable machine language).

And seeing how machine code/assembly language is not portable, one
might go down the route of embedding a "generic low level language"
which gets translated into machine code. Though at that point you may
as well just drop the distinction and make the functionality offered
by the "generic low level language" part of the main language.

> > The API pretty-well covers the assembler case, and you can then just use a
> > proper system assembler instead of implementing your own.  Good API
> > examples include Python, Tcl and Java (JNI), though pretty well every
> > language implementation has its own API.
>
> Neither of these implement their own runtime system in the own language,
> which was the subject. Then even relative simple things (like floating point
> mask control, floating point primitives, exception handling, helpers for
> the object mode, GC/memory management, bitlevel instructions that aren't
> modeled), and more advanced stuff like SSE, MMX, drivers.

Many languages implement large parts of their runtime in their own
language (C for example).

> It also helps to move intrinsics from compiler to libraries.

So why restrict yourself to creating libraries in your own language?
If your language supports external linkage then what does it matter
what language a library at the other end of a call is written in?

Marco van de Voort

unread,
Feb 5, 2010, 4:33:16 AM2/5/10
to
On 2010-02-04, Calum Grant <spa...@calumgrant.net> wrote:
>> > I'm struggling to think of a case where inline assembly language would
>> > be better than an external C function (with embedded assembly if you
>> > must).
>>
>> E.g. on windows you don't have an external C function. Adding a C function
>> means adding a dependancy to a C compiler and its libs, quirks.
>
> C is far more portable than machine code.

For ordinary code: yes. But the whole point of this was that this is machine
and OS specific code that is not doable in HLL easily. The whole point was
even that inline assembler primitives can help to have a higher percentage
of your development system's rtl in a HLL.

See previous msgs for examples, like copro mask handling, exception and
other language helpers, startup code (initializing check), fast string
primitives, synchronization primitives (lock prefix) etc.

> Of course you have static and dynamic external linkage on Windows.
> You only create a dependency
> on the C standard library if you decide to use functions from the
> libraries.

If not, C is no better than Pascal, or any HLL suitable enough to fully
write its own RTL/VM in (which was what I was talking about)

> I'm not suggesting that you write C inline in your code (though it
> would be a more portable machine language).

Than Pascal ? I disagree out of principle ( :-) ), but the reality is that it
depends on compiler extensions.). FPC is already luxurious, but GCC offers
more, though not always easier.

I might come further when starting to use gccisms (since contrary to gcc,
fpc doesn't have register independant asm or asm inlining, which was where I
was coming from), but the bits that then translate to HLL would still be
system dependant. Just easier to maintain.

> And seeing how machine code/assembly language is not portable, one
> might go down the route of embedding a "generic low level language"
> which gets translated into machine code.

Not all HLL code is automatically portable, and not everything done is
because of portability. We also minimize machinecode in favour of unportable
HLL out of maintenance reasons.

> Though at that point you may as well just drop the distinction and make
> the functionality offered by the "generic low level language" part of the
> main language.

Or implement an extension to the existing one. Or have more compiler
features. And inline ASM is just that.

>> Neither of these implement their own runtime system in the own language,
>> which was the subject. Then even relative simple things (like floating point
>> mask control, floating point primitives, exception handling, helpers for
>> the object mode, GC/memory management, bitlevel instructions that aren't
>> modeled), and more advanced stuff like SSE, MMX, drivers.
>
> Many languages implement large parts of their runtime in their own
> language (C for example).

So do we. But (borland dialect) Pascal's are conceptually not that
different.

>> It also helps to move intrinsics from compiler to libraries.
>
> So why restrict yourself to creating libraries in your own language?

> If your language supports external linkage then what does it matter
> what language a library at the other end of a call is written in?

It complicates the buildprocess, adds multiple language requirements on
developers, and the situation on the main platform, Windows, is a mess with
multiple contendents (cygwin,mingw,MSVC) with all their own advantages and
disadvantages etc.

Note that we can link to C/gcc perfectly. (though must provide own headers,
since C headers are not parsable, and automated translation is a patchy hack
if I'm mild). We just choose not to for the project itself.

In reality we provide headers for a significant portion of the windows API,
OS X (Carbon), all popular dbs and many other libraries out of the box. But
the project itself (compiler,utils, core libraries) doesn't.

Currently the core compiler and library are statically even on Linux/FreeBSD, as are
small programs generated with them. They interface to the system over
syscalls. (though that has different reasons, that are partially historic)

Marco van de Voort

unread,
Feb 5, 2010, 5:07:00 AM2/5/10
to
On 2010-02-03, Robbert Haarman <comp.la...@inglorion.net> wrote:
> My previous post seems to have caused some confusion and rubbed you the
> wrong way.

I sometimes get carried away a bit when discussing. Don't worry, if you
really step on my toes, you'll know :-)

> programming, and, for a small part, James Harris's question about
> using assembly to bootstrap a compiler.

The latter part should be avoided at all cost. But I'm in the camp that
any bootstrap solution should be temporary only, specially in this age of
simu and emulators of historic systems.l

Marco van de Voort

unread,
Feb 5, 2010, 5:20:44 AM2/5/10
to
On 2010-02-03, James Harris <james.h...@googlemail.com> wrote:
>> > Any views on this?
>>
>> I wouldn't spend one minute more on the bootstrapping system than required.
>> It is rather uninteresting.
>
> I suspect you are looking at this from the point of view of compiling
> a language that's already been designed, given your experience with
> Virtual Pascal and whatever else. From that standpoint I'd see it the
> same way.

In general, I think the bootstrapping is only interesting till the system
gets strong enough to become self hosting. Even if not finished, if a
workable enough subset is attained, the bootstrap is unnecessary.

Free Pascal bootstrapped from turbo pascal. While the same language, the
16-bit->32-bit situation took a large toll, and that pain is probably also a
reason for my opinion to no prolong the bootstrap solution too long. But it
took us 4-6 years nevertheless (the last years it was only used for certain
debug purposes)

The realization that any work on the bootstrap is not a project result (but
more infrastructural) is another reason. This comes more from watching the
GNU Pascal (gcc frontend) from a distance, and seeing them having to do a
lot of maintenance for differing gcc versions.

Virtual Pascal is just another Pascal compiler (i386 only) that was entirely
written in assembler.

> I'd also agree (to a point) if varying an existing language. Probably
> most languages begin this way and it seems far easier to modify a
> language than design a new one from scratch.
> However, what of coming up with a mainly new design?

The same. The time the temporary situation lasts tiil self-hosting just takes
longer.

>> While assembler in the compiler is not terribly interesting, in the runtime
>> lib it is almost required.
>
> In the suggestion the assembler in the compiler would be present only
> until it could be replaced (a little at a time) by the new language.
> I'm not saying I'm definitely sold on the idea but it may be worth
> exploring.

I can imagine that. Certainly. My remark was more about doing away the
bootstrap after the system is self hosting (and, more important,
debuggable), not the setup of the bootstrap.

Since this solution inheritly disappears over time, we thus completely
agree.

Marco van de Voort

unread,
Feb 5, 2010, 9:23:16 AM2/5/10
to
On 2010-02-03, Ray <be...@sonic.net> wrote:
> But C, on most systems, is the fundamental systems language - the
> means by which all the ABI's, drivers, OS interfaces, etc, are
> *DEFINED*.

People often confuse procedural API's with C. What you say is mostly only
true for unices.

While other OSes, like e.g. Windows have a C callable API, the API is a
general procedural API that does not conform to C 100%. (other calling
convention and other default packing, namely always packed). The COM api's
are in IDL (and the C headers are generated from it)

The same with e.g. the Classic MacOS API (that was defined with calling
conventions stemming from 68k Pascal's)

Another gigantic difference between C apis and general procedural ones is
the use of macros instead of functions. This is e.g. much larger in Unix
than in Windows (which limits itself to a few typecasting sendmessage
macros, and some to construct complex types that fit in a machine word, like
COLOR_REF)


0 new messages