February 06, 2018
On Mon, Feb 05, 2018 at 11:18:59PM -0800, Walter Bright via Digitalmars-d wrote: [...]
> Except for 16 bit shorts. Shorts will exact a penalty :-) and so shorts should only be used for data packing purposes.

Really?! How so?


T

-- 
Talk is cheap. Whining is actually free. -- Lars Wirzenius
February 06, 2018
On 2/5/2018 10:08 PM, H. S. Teoh wrote:
> IMO, we should extend this past just one statement. It would lead to
> more possibilities for optimizations and possibly other features too.
> Though I understand that Walter has reservations about doing this for
> some reason.

What you're asking for is data flow analysis. DFA's effectiveness is definitely a quality of implementation thing. But if you want to have a successful compile depend on DFA, then you've got to define in the Specification exactly what DFA is done.

The next problem is DFA is a complex thing. This is why DFA is done on the vastly simplified and canonicalized intermediate code, not the ASTs. With dmd's inliner, I made the mistake of doing inlining of the ASTs (other compilers do it with the intermediate code). This was a major error, and has led to all kinds of peculiar bugs and shortcomings. I'm not going to make that mistake again.

Doing DFA for VRP means doing it on the ASTs.

I know what you're asking for sounds simple enough. But it ain't.
February 06, 2018
On Monday, 5 February 2018 at 23:56:51 UTC, Adam D. Ruppe wrote:
> 1) Given:
>     byte a, b;
>     byte c = a + b;
>
> The cast seems a bit silly: you are already explicitly using `byte` everywhere, so your intention is pretty clear: you only want to use the bytes and are ok with the rest of it being discarded. Therefore, I find the cast to be an unnecessary addition.

Yeah, it's annoying. For my MSP430 work (16-bit, lots of shorts and bytes) I created a generic type which works around this, so you would do:

byte c = a.nx + b;

where .nx means "non-extending" and converts/wraps the (u)byte/(u)short in my special type. The arithmetic operations are infectious, so you only need to apply it to one of the operands (and you can preserve it across statements by using "auto" instead of "byte"). Because D doesn't have automatic type conversions for the function calls, the function signatures still take the standard types, and you add .nx where needed in the body. Because the higher-order bits are discarded after each operation, the optimizer easily optimizes away the multi-word operations (as least in all of the cases I bothered checking...).

Given the impasse between preserving C compatibility and the problems that that compatibility brings, I think the ideal situation would be to have some kind of mode, in the same way that extern(C) is a "mode":

// option 1
pragma(nointpromos): // opt-in
byte c = a + b;

// option 2
pragma(cintpromos): // opt-out of D's new, non-compatible, rules
byte c = cast(byte) (a + b);

Unfortunately, I'm not holding my breath for something like this. At least D is flexible enough that my .nx solution works! That's saying a lot about the language.
February 06, 2018
On 02/06/2018 01:22 AM, H. S. Teoh wrote:
> 
> No need to wait for the future, you can do this today:
> 
> 	enum toStr(alias X) = X.stringof;
> 
> 	enum x = 100;
> 	pragma(msg, toStr!1);
> 	pragma(msg, toStr!3.14159);
> 	pragma(msg, "Hello " ~ toStr!10 ~ " world");
> 	pragma(msg, "Hello " ~ toStr!x ~ " world");
> 
> Note, however, that this doesn't work in CTFE, only at template
> expansion time.
> 

There's an easier way that does work in CTFE:

---------------------------

import std.conv;

enum x = 100;
pragma(msg, 1.text);
pragma(msg, "Hello " ~ 10.text ~ " world");
pragma(msg, "Hello " ~ x.text ~ " world");

enum y = 2.text;
pragma(msg, y);

//pragma(msg, 3.14159.text); // Ugh, ok, floats don't work though :(
February 07, 2018
On Wednesday, 7 February 2018 at 00:00:05 UTC, Nick Sabalausky (Abscissa) wrote:
> //pragma(msg, 3.14159.text); // Ugh, ok, floats don't work though :(

So I saw a stb_sprintf with a from-scratch impl, including floats, public domain. Someone on IRC was thinking about porting it.

Maybe we should actually bring that in for the ctfe floats.
February 06, 2018
On Wed, Feb 07, 2018 at 12:10:14AM +0000, Adam D. Ruppe via Digitalmars-d wrote:
> On Wednesday, 7 February 2018 at 00:00:05 UTC, Nick Sabalausky (Abscissa)
> wrote:
> > //pragma(msg, 3.14159.text); // Ugh, ok, floats don't work though :(
> 
> So I saw a stb_sprintf with a from-scratch impl, including floats, public domain. Someone on IRC was thinking about porting it.
> 
> Maybe we should actually bring that in for the ctfe floats.

Formatting of floats, while apparently rather straightforward on the surface, is in fact a hairy, monstrously complicated beast of a task, with all sorts of lovely corner cases and special behaviours (as is usual for IEEE floats, y'know, bread and butter, nothing surprising). It's definitely not for the faint of heart to attempt to implement, and is probably a big reason why Phobos just calls the C function to do the job for us. :-P

Kudos in advance to whoever has the guts to reimplement it in D so that we can use it in CTFE.


T

-- 
Political correctness: socially-sanctioned hypocrisy.
February 06, 2018
On Tue, Feb 06, 2018 at 12:23:02PM -0800, Walter Bright via Digitalmars-d wrote:
> On 2/5/2018 10:08 PM, H. S. Teoh wrote:
> > IMO, we should extend this past just one statement. It would lead to more possibilities for optimizations and possibly other features too.  Though I understand that Walter has reservations about doing this for some reason.
> 
> What you're asking for is data flow analysis. DFA's effectiveness is definitely a quality of implementation thing. But if you want to have a successful compile depend on DFA, then you've got to define in the Specification exactly what DFA is done.
> 
> The next problem is DFA is a complex thing. This is why DFA is done on the vastly simplified and canonicalized intermediate code, not the ASTs. With dmd's inliner, I made the mistake of doing inlining of the ASTs (other compilers do it with the intermediate code). This was a major error, and has led to all kinds of peculiar bugs and shortcomings. I'm not going to make that mistake again.

What are the chances for inlining to be moved back to the IR? Or is that not worth contemplating because it's too huge / disruptive of a project?


> Doing DFA for VRP means doing it on the ASTs.
> 
> I know what you're asking for sounds simple enough. But it ain't.

Is it possible to retain VRP information in the IR? Or is that far too late?


T

-- 
Freedom of speech: the whole world has no right *not* to hear my spouting off!
February 06, 2018
On Tue, Feb 06, 2018 at 10:38:36PM +0000, Luís Marques via Digitalmars-d wrote: [...]
> Yeah, it's annoying. For my MSP430 work (16-bit, lots of shorts and bytes) I created a generic type which works around this, so you would do:
> 
> byte c = a.nx + b;
> 
> where .nx means "non-extending" and converts/wraps the (u)byte/(u)short in my special type. The arithmetic operations are infectious, so you only need to apply it to one of the operands (and you can preserve it across statements by using "auto" instead of "byte"). Because D doesn't have automatic type conversions for the function calls, the function signatures still take the standard types, and you add .nx where needed in the body.  Because the higher-order bits are discarded after each operation, the optimizer easily optimizes away the multi-word operations (as least in all of the cases I bothered checking...).
[...]
> Unfortunately, I'm not holding my breath for something like this. At least D is flexible enough that my .nx solution works! That's saying a lot about the language.

I really like your .nx idea!  It neatly sidesteps the nonsensical mandatory casts and on top of that documents intent (the .nx being a telltale sign of truncation -- much better than arbitrary implicit rules).  I think I'll adopt it in some form in my code, to make dealing with narrow ints saner.  I don't know what your .nx type does, but for my purposes I'll probably just have a thin wrapper around byte/ubyte/etc. with overloaded arithmetic operators that perform the requisite casts.

And yeah, one thing I really like about D is that it empowers you with the tools you need to implement types that are (almost) as powerful as built-in types. While for the most part the built-in stuff is pretty cool, when it's not up to snuff, in 99% of the cases you can just replace it with your own solution, and it Just Works(tm).


T

-- 
Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
February 07, 2018
On Wednesday, 7 February 2018 at 00:24:26 UTC, H. S. Teoh wrote:
> I really like your .nx idea!  It neatly sidesteps the nonsensical mandatory casts and on top of that documents intent (the .nx being a telltale sign of truncation -- much better than arbitrary implicit rules).  I think I'll adopt it in some form in my code, to make dealing with narrow ints saner.  I don't know what your .nx type does, but for my purposes I'll probably just have a thin wrapper around byte/ubyte/etc. with overloaded arithmetic operators that perform the requisite casts.

Yeah, it's just a thin wrapper. I implemented just enough to cover my use cases but just in case it's useful to you or someone else, here goes my implementation...

private struct NX(T)
{
    T value;
    alias value this;

    this(T value)
    {
        this.value = value;
    }

    NX!T opUnary(string s)()
    {
        return NX!T(cast(T) mixin(s ~ "value"));
    }

    auto opBinary(string op, U)(NX!U rhs)
    {
        static if(rhs.value.sizeof > value.sizeof)
            return mixin("rhs " ~ op ~  " value");
        else
            return NX!T(cast(T) mixin("value " ~ op ~ " rhs"));
    }

    NX!T opBinary(string op)(T rhs)
    {
        return NX!T(cast(T) mixin("value " ~ op ~ " rhs"));
    }
}

alias i16 = NX!short;
alias u16 = NX!ushort;
alias i8 = NX!byte;
alias u8 = NX!ubyte;

auto nx(byte v)
{
    return i8(v);
}

auto nx(ubyte v)
{
    return u8(v);
}

auto nx(short v)
{
    return i16(v);
}

auto nx(ushort v)
{
    return u16(v);
}

auto nx(int v)
{
    return i16(cast(short) v);
}

auto nx(uint v)
{
    return u16(cast(ushort) v);
}

Those last two nx() probably won't make sense outside of my 16-bit target (even though I build the same code for 32 bits too, to run the tests on my x86 host).

If you start using something like this in your code please send me a link, so I can copy a better implementation :-)
February 07, 2018
On Monday, 5 February 2018 at 20:45:22 UTC, H. S. Teoh wrote:
> On Mon, Feb 05, 2018 at 03:23:20PM -0500, Steven Schveighoffer via Digitalmars-d wrote:
>> On 2/5/18 2:30 PM, H. S. Teoh wrote:
>> > Even better yet:
>> > 
>> > 	byte b;
>> > 	b = -b;		// Deprecation error
>> > 
>> > WAT????
>> 
>> In the future, -b will be typed as an int, so you can't reassign it to b.  You can see this today with -transition=intpomote:
>> 
>> Error: cannot implicitly convert expression -cast(int)b of type int to
>> byte
> [...]
>
> Honestly, this just makes narrow int types basically useless when it comes to any arithmetic at all.
>

In fact you really shouldn’t do arithmetic on narrow integers, especially as loop counters.
It usually results in suboptimal machine code on optimizing compilers.

That leaves arrays of bytes and array-wise operations though, SIMD by definition can operate on a pack of e.g. byte-sized integers.

Bottom line is to use narrow integers only as a way to pack data and for SIMD operations in arrays.

>
>
> T