December 18, 2012
On Tue, Dec 18, 2012 at 09:30:40AM -0800, Walter Bright wrote:
> On 12/18/2012 8:57 AM, Dmitry Olshansky wrote:
[...]
> >Another point is that pointer chasing data-structures is not a recipe for fast repeated execution.
> >
> >To provide an analogy: executing calculation recursively on AST tree of expression is bound to be slower then running the same calculation straight on sanely encoded flat reverse-polish notation.
> >
> >A hit below belt: also peek at your own DMDScript - why bother with plain IR (_bytecode_!) for JavaScript if it could just fine be interpreted as is on AST-s?
> 
> Give me some credit for learning something over the last 12 years! I'm not at all convinced I'd use the same design if I were doing it now.
> 
> If I was doing it, and speed was paramount, I'd probably fix it to generate native code instead of bytecode and so execute code directly. Even simple JITs dramatically speeded up the early Java VMs.
[...]

Is it too late to change CTFE to work via native code? Besides the effort required to rework the existing code (and perhaps the cross-compiling issue, though I don't see it as a major issue), I see a lot of advantages to doing that. For one thing, it will solve the current complaints about CTFE speed and memory usage (a native code implementation would allow using a GC to keep memory footprint down, or perhaps just a sandbox that can be ditched after evaluation and its memory reclaimed).


T

-- 
Obviously, some things aren't very obvious.
December 18, 2012
On 12/18/2012 9:42 AM, H. S. Teoh wrote:
> I was thinking more along the lines of things like fully automatic
> purity, safety, exception inference. For example, every function body
> eventually has to be processed by the compiler, so if a particular
> function is inferred to throw exception X, for example, then when its
> callers are compiled, this fact can be propagated to them. To do this
> for the whole program might be infeasible due to the sheer size of
> things, but if a module contains, for each function exposed by the API,
> a list of all thrown exceptions, then when the module is imported this
> information is available up-front and can be propagated further up the
> call chain. Same thing goes with purity and @safe.
>
> This may even allow us to make pure/@safe/nothrow fully automated so
> that you don't have to explicitly state them (except when you want the
> compiler to verify that what you wrote is actually pure, safe, etc.).

The trouble with this is the separate compilation model. If the attributes are not in the function signature, then the function implementation can change without recompiling the user of that function. Changing the inferred attributes then will subtly break your build.

Inferred attributes only work when the implementation source is guaranteed to be available, such as with template functions.

Having a binary format doesn't change this.

December 18, 2012
On 12/18/2012 9:49 AM, H. S. Teoh wrote:
> Is it too late to change CTFE to work via native code?

No, because doing so involves zero language changes. It is purely a quality-of-implementation issue.

> Besides the
> effort required to rework the existing code (and perhaps the
> cross-compiling issue, though I don't see it as a major issue),

Um, it does introduce major support costs for porting to different CPU targets.


December 18, 2012
On Tue, Dec 18, 2012 at 10:06:43AM -0800, Walter Bright wrote:
> On 12/18/2012 9:49 AM, H. S. Teoh wrote:
> >Is it too late to change CTFE to work via native code?
> 
> No, because doing so involves zero language changes. It is purely a quality-of-implementation issue.

Well, that much is obvious; I was just wondering if the current implementation will require too much effort to make it work with native code CTFE.


> >Besides the effort required to rework the existing code (and perhaps the cross-compiling issue, though I don't see it as a major issue),
> 
> Um, it does introduce major support costs for porting to different CPU targets.
[...]

Could you elaborate? In my mind, there's not much additional cost to what's already involved in targeting a particular CPU in the first place. Since the CPU is already targeted, we generate native code for it and run that during CTFE.

Or are you referring to cross-compiling?


T

-- 
Tech-savvy: euphemism for nerdy.
December 18, 2012
12/18/2012 9:30 PM, Walter Bright пишет:
> On 12/18/2012 8:57 AM, Dmitry Olshansky wrote:
>> But adequate bytecode designed for interpreters (see e.g. Lua) are
>> designed for
>> faster execution. The way CTFE is done now* is a polymorphic call per
>> AST-node
>> that does a lot of analysis that could be decided once and stored in
>> ... *ehm*
>> ... IR. Currently it's also somewhat mixed with semantic analysis
>> (thus rising
>> the complexity).
>
> The architectural failings of CTFE are primary my fault from taking an
> implementation shortcut and building it out of enhancing the constant
> folding code.
>
> They are not a demonstration of inherent superiority of one scheme or
> another. Nor does CTFE's problems indicate that modules should be
> represented as bytecode externally.
>
Agreed. It seemed to me that since CTFE implements an interpreter for D it would be useful to define a flattened representation of semantically analyzed AST that is tailored for execution. The same bytecode then could be be used for external representation.

There is however a problem of templates that can only be analyzed on instantiation. Then indeed we can't fully "precompile" semantic step into bytecode meaning that it won't be much beyond flattened result of parse step. So on this second thought it may not that useful after all.

>> Another point is that pointer chasing data-structures is not a recipe
>> for fast
>> repeated execution.
>>
>> To provide an analogy: executing calculation recursively on AST tree of
>> expression is bound to be slower then running the same calculation
>> straight on
>> sanely encoded flat reverse-polish notation.
>>
>> A hit below belt: also peek at your own DMDScript - why bother with
>> plain IR
>> (_bytecode_!) for JavaScript if it could just fine be interpreted as
>> is on AST-s?
>
> Give me some credit for learning something over the last 12 years! I'm
> not at all convinced I'd use the same design if I were doing it now.
>
OK ;)

> If I was doing it, and speed was paramount, I'd probably fix it to
> generate native code instead of bytecode and so execute code directly.
> Even simple JITs dramatically speeded up the early Java VMs.

Granted JIT is faster but I'm personally more interested in portable interpreters. I've been digging around and gathering techniques and so far it looks rather promising.
Though I need more field testing... and computed gotos in D! Or more specifically a way to _force_ tail-call.

-- 
Dmitry Olshansky
December 18, 2012
On 12/18/2012 08:26 PM, H. S. Teoh wrote:
> On Tue, Dec 18, 2012 at 10:06:43AM -0800, Walter Bright wrote:
>> On 12/18/2012 9:49 AM, H. S. Teoh wrote:
>>> Is it too late to change CTFE to work via native code?
>>
>> No, because doing so involves zero language changes. It is purely a
>> quality-of-implementation issue.
>
> Well, that much is obvious; I was just wondering if the current
> implementation will require too much effort to make it work with native
> code CTFE.
>
>
>>> Besides the effort required to rework the existing code (and perhaps
>>> the cross-compiling issue, though I don't see it as a major issue),
>>
>> Um, it does introduce major support costs for porting to different CPU
>> targets.
> [...]
>
> Could you elaborate? In my mind, there's not much additional cost to
> what's already involved in targeting a particular CPU in the first
> place. Since the CPU is already targeted, we generate native code for it
> and run that during CTFE.
> ...

The generated native code would need to be different in order to support proper error reporting and dependency handling. (The generated code must be able to communicate with the analyzer/jit compiler.)

The compiler does not have the full picture during analysis. It needs to figure out what information a CTFE-call depends on. The only way that works in general is running it.


Eg:

string good(){
    mixin(foo(0,()=>good())); // ok, delegate never called
}

string bad(){
    mixin(foo(1,()=>bad())); // error, need body of bad to generate body of bad
}

string foo(bool b, string delegate() dg){
    if(b) return dg();
    return q{return "return 0;";};
}
December 18, 2012
On 12/18/2012 11:26 AM, H. S. Teoh wrote:
>> Um, it does introduce major support costs for porting to different CPU
>> targets.
> [...]
>
> Could you elaborate?

Sure. You have to rewrite it when going from 32 to 64 bit code, or to ARM, or to any other processor. It's not the same as the regular code generator.

December 18, 2012
On 12/18/2012 11:58 AM, Dmitry Olshansky wrote:
> The same bytecode then could be be used for external representation.

Sigh, there is (again) no point to an external bytecode.
December 19, 2012
On Tuesday, 18 December 2012 at 17:30:41 UTC, Walter Bright wrote:
> If I was doing it, and speed was paramount, I'd probably fix it to generate native code instead of bytecode and so execute code directly. Even simple JITs dramatically speeded up the early Java VMs.

Could you re-use the compiler recursively to first compile and run CTFE, followed by the rest?

--rt
December 19, 2012
12/19/2012 1:33 AM, Walter Bright пишет:
> On 12/18/2012 11:58 AM, Dmitry Olshansky wrote:
>> The same bytecode then could be be used for external representation.
>
> Sigh, there is (again) no point to an external bytecode.

BTW In the end I think I was convinced that bytecode won't buy D much. Esp considering the cost of maintaining a separate spec for it and making sure both are in sync.

-- 
Dmitry Olshansky