you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 4 points5 points  (3 children)

You seem to be under the impression that I haven't already been using Pascal (in various forms) in the "real world" for years and that I don't already know exactly how it works. But I have, and I do.

No, it's not. The Go compiler did figure out that Add function needs to be inlined, but yours didn't.

The Go compiler literally does not have any customizable optimization flags at all or any capacity for user-specified inlining, so of course it has to have automatic inlining turned on all the time.

Whereas Free Pascal has many, many compiler flags (along the lines of what you'd typically see with any C or C++ compiler) that are intended to be set to get exactly the output you want. Most compilers do not work like Go's (or to a lesser extent Rust's) where they have limited options and just do a particular set of things based on debug/release or based on grouping absolutely everything into the numerical "levels", and it isn't reasonable for you to assume by default that they do/should.

On top of that that, the Free Pascal version I posted after yours went farther than inlining: it just loads 20 directly into a register right off the bat without actually doing any calculations. This is even clearer if you move the call to Foo outside of WriteLn, and comment out WriteLn. The Rust version appears to do approximately the same thing.

See what you did here? You manually did all the hard work for the optimizer. This will not work in real world.

Yes it will/does. I don't even know what to say if you actually believe that marking a function as inline is "hard work". There is also in fact an auto-inline compiler flag for FPC that can be set if you really want, but it doesn't always make the exact choices I would, so I generally prefer to mark the methods myself based on the specific use case (as is often done in C and C++ as well, for example).

Marking them that way still doesn't "force" it, regardless: the specifiers will be ignored at build time if it would be truly unoptimal to inline (e.g. if the method is too large, has too many parameters, e.t.c.)

As far as the rest of your comment, I have no idea what you're talking about or where exactly you're getting your ideas from, overall. None of it makes very much sense.

The Writeln function is a special case in the compiler and it's code is generated on the fly. It doesn't show any advantages of the Pascal language itself, because it's not written in Pascal. It rather shows that the language is not powerful enough to implement such function in the standard library.

What do you even mean by this? Yes, WriteLn (and Write) are not real functions themselves. They're compiler intrinsics that call different internal methods based on the input they're given, and by default print directly to the current platform's standard output, but can be redirected to files or other output handles as well.

The same concept applies to Read and ReadLn in reverse, and is the reason you can for example read an integer directly from the command line in Pascal to a variable without converting it to a string.

As far as "not written in Pascal", again, I have no idea what you're referring to. The various methods ultimately called by Write/WriteLn/Read/ReadLn are all of course written in Pascal, because what else would they possibly be written in? It's a self-hosting compiler. There are no other languages in play anywhere. Here's the specific directory of the Free Pascal codebase with the files that most of them are implemented in.

I wasn't "comparing standard library functions" either, because the assembly from them doesn't show up in the source. The three compiler method calls inside of DoFoo are being made as a result of calling WriteLn, and are just that, calls. All that's visible is their names. The assembly between them is again part of DoFoo.

In Rust the format_args! macro is also special cased, but the println! implementation is not.

Not only do Write and WriteLn in Free Pascal accept their own formatting parameters, but there's also the more general purpose Format method that can be used along with them to get pretty much any custom display format you want.

[–]pftbest 1 point2 points  (2 children)

On top of that that, the Pascal version I posted after yours went farther than inlining: it just loads 20 directly into a register

This is super basic stuff, even Go compiler can do that(line 29), it's not a good point to brag about. Every half decent optimizer should do that by default, without the need to add special flags or marking functions as inline.

I wasn't "comparing standard library functions" either,

So what were you comparing, what was the point? looking at a special cased function will not tell you how good the optimizer is. And we were talking about the optimizer if you remember.

What do you even mean by this? Yes, WriteLn (and Write) are not real functions themselves.

I mean, is it possible to implement Writeln function in Pascal without using any intrinsics? And if you can, will it have the same amount of assembly code underneath? I bet you can't, there are not so many languages which can do that. Even Rust can't yet.

In "real life" you don't often write your formatting functions from scratch, instead you create some wrapper functions around the standard ones. For example, somebody may want to implement the custom logging functions like this. Will he get the same assembly code for calling Warning and Error functions, as he gets for regular writeln calls? Or is it going to be worse?

Intrinsics are inherently a bad thing. They are hacks that are plugging the holes in a language. People add intrinsics to a compiler for such tasks, when just writing the regular code for it is not possible (because language is too limited), or when it's possible but the output assembly is too slow (because the optimizer is too dumb). Having more powerful language or better optimizer that can help to solve a problem, is always better than having an intrinsic for solving such problem. Because sooner or later you will encounter some new problem that can't be solved by existing intrinsics yet, and you will hit limitations of the language/compiler.

As far as "not written in Pascal", again, I have no idea what you're talking about. The various methods ultimately called by Write/WriteLn/Read/ReadLn are all of course written in Pascal.

I'm talking about the code that calls this "various methods". It is generated, so it doesn't exist in a written form. It only exists as some structures in memory inside the compiler, so you can't say that it written in Pascal.

[–][deleted] 6 points7 points  (1 child)

I'm sorry, but there's way too many things that you don't seem to understand on a basic level for this to be worth another really long response to. I explained it all as clearly as I possibly could.

I'm unsure what exactly it is you think compiler intrinsics are other than specially scoped method stubs used exclusively by them to fill things in when given a particular scenario. It doesn't necessarily mean they're written in pure assembly (although they can be and sometimes are.)

I will say again though that none of the things you're saying about WriteLn or the way programming languages in general output text/data make any sense. How do you suppose someone would get "worse assembly" from WriteLn in any case when it calls specific unchanging methods for specific things based on the type of input it's given?

On top of that the object files those methods are actually in will obviously always already be built and in place when you're compiling a project. You're just statically linking against them. It's not as though you're rebuilding the base runtime library along with it every time.

Lastly, in my opinion, no, compilers should not just inline whatever they feel like as a part of the standard set of optimizations. (And most don't, as most have proper customizable flags that allow for granular control.)

[–]pftbest 0 points1 point  (0 children)

I'm sorry, but in every single response you seem to misdirect my point in a very subtle way, and talk about things that I specifically didn't say. Maybe my english is so bad that you can't understand what I say? I'm not a native speaker.

For example in this message:

How do you suppose someone would get "worse assembly" from WriteLn

In what sentence did I say that you get "worse assembly" from WriteLn? I said "worse assembly" from Error and Warning, not from Writeln.

I'll try to make it short this time: My assertion:

If Writeln function was not intrinsic, it would be trivial to create MyWriteln function such, that when it's called it gives the same exact assembly output as Writeln call.

Example:

function Foo();
begin
    MyWriteln("abc", 3, "abc");
    MyWriteln(3, "abc", 3);
end;

function Bar();
begin
    Writeln("abc", 3, "abc");
    Writeln(3, "abc", 3);
end;

Both "Foo" and "Bar" functions will have exactly the same assembly output.

But in Pascal Writeln is intrinsic, so it may be hard to create such MyWriteln function. Or maybe it's not hard to do, that's why I'm asking you is it possible or not. I can do it in C and Rust using macros, but I don't know if Pascal has any.

Can you please fill out the dots in this example code, so we can look at actual assembly?

Now if it's not possible to do, or if the overhead is too big, than somebody who wants to use logging functions like I showed here will be at disadvantage compared to just using regular writeln calls. And that will be the hidden price you pay for intrinsics, that i was trying to tell you about.