Thread overview
Immutables converted to immediates
Jun 28, 2023
Cecil Ward
Jun 28, 2023
Cecil Ward
Jun 29, 2023
Timon Gehr
Jul 01, 2023
Iain Buclaw
Jul 02, 2023
Iain Buclaw
Jul 02, 2023
Cecil Ward
June 28, 2023
This is a question about either the front end or back end of GDC and maybe also LDC`.

At compile time, should the value of arr[0] etc be calculable so that the code below turns out to be something like *p == 1 where n is an immediate constant in asm?

immutable uint arr = [1,2,3];

immutable uint * p;
loop
    {
    if ( arr[0]== p[0] && arr[1] == p[1] && arr[2] == p[2]
        …
    }
GDC is generating a structure in the code segment and then fetching it even though the values of the elements ought to be known at compile-time. I’m not sure why. The actual code, x86-64 in this case, consists of a load of fetches from the code segment into successive registers before the start of the loop, and the loop then consists of a load of instructions like cmp [r8+4*rax], r9, where r9, r10 etc we’re loaded up from the fetches from [rip+disp] before th4 loop, a minor strength reduction compared to a compare-immediate, as it has plenty of registers free. The mystery is why the fetches from [rip+disp] even exist, given that they are known values.
June 28, 2023
On Wednesday, 28 June 2023 at 17:27:37 UTC, Cecil Ward wrote:
> This is a question about either the front end or back end of GDC and maybe also LDC`.
>
> At compile time, should the value of arr[0] etc be calculable so that the code below turns out to be something like *p == 1 where n is an immediate constant in asm?
>
> immutable uint arr = [1,2,3];
>
> immutable uint * p;
> loop
>     {
>     if ( arr[0]== p[0] && arr[1] == p[1] && arr[2] == p[2]
> >     }
> GDC is generating a structure in the code segment and then fetching it even though the values of the elements ought to be known at compile-time. I’m not sure why. The actual code, x86-64 in this case, consists of a load of fetches from the code segment into successive registers before the start of the loop, and the loop then consists of a load of instructions like cmp [r8+4*rax], r9, where r9, r10 etc we’re loaded up from the fetches from [rip+disp] before th4 loop, a minor strength reduction compared to a compare-immediate, as it has plenty of registers free. The mystery is why the fetches from [rip+disp] even exist, given that they are known values.

What it’s for: I have a tight loop to skip to the end of the various types of D comments eg "*/", or until a newline for "//" comments etc. as I’ve written a cheap parser for a small part of the grammar of D. There’s quite a bit of overhead before the start of the loop, and I’m not sure that this is such a great idea, jury is still out. I need to think about it more.
June 29, 2023
On 6/28/23 19:27, Cecil Ward wrote:
> This is a question about either the front end or back end of GDC and maybe also LDC`.
> 
> At compile time, should the value of arr[0] etc be calculable so that the code below turns out to be something like *p == 1 where n is an immediate constant in asm?
> 
> immutable uint arr = [1,2,3];
> 
> immutable uint * p;
> loop
>      {
>      if ( arr[0]== p[0] && arr[1] == p[1] && arr[2] == p[2]
>          …
>      }
> GDC is generating a structure in the code segment and then fetching it even though the values of the elements ought to be known at compile-time. I’m not sure why. The actual code, x86-64 in this case, consists of a load of fetches from the code segment into successive registers before the start of the loop, and the loop then consists of a load of instructions like cmp [r8+4*rax], r9, where r9, r10 etc we’re loaded up from the fetches from [rip+disp] before th4 loop, a minor strength reduction compared to a compare-immediate, as it has plenty of registers free. The mystery is why the fetches from [rip+disp] even exist, given that they are known values.

You can force constant folding in the frontend like this:

enum uint[3] a = arr[0..3];
if(a[0]== p[0] && a[1] == p[1] && a[2] == p[2])
July 01, 2023

On Wednesday, 28 June 2023 at 17:27:37 UTC, Cecil Ward wrote:

>

GDC is generating a structure in the code segment and then fetching it even though the values of the elements ought to be known at compile-time. I’m not sure why. The actual code, x86-64 in this case, consists of a load of fetches from the code segment into successive registers before the start of the loop, and the loop then consists of a load of instructions like cmp [r8+4*rax], r9, where r9, r10 etc we’re loaded up from the fetches from [rip+disp] before th4 loop, a minor strength reduction compared to a compare-immediate, as it has plenty of registers free. The mystery is why the fetches from [rip+disp] even exist, given that they are known values.

Right, immutable (as well as const) has rather wobbly meanings, because you can have immutable data/fields that are initialized by a constructor - so in the worst case it can't be rodata at all.

For example:

immutable uint[] arr;

shared static this()
{
    arr = [1,2,3];
}

Here, we can't possibly know what the length of arr will end up being at run-time.

Based on the current wording of the spec,

  1. immutable applies to data that cannot change. Immutable data values, once constructed, remain the same for the duration of the program's execution.

  2. const applies to data that cannot be changed by the const reference to that data. It may, however, be changed by another reference to that same data.

I guess there's scope for applying read-only semantics to both const and immutable locals/parameters. For static data, only immutables that aren't set in a ctor can be read-only.

July 02, 2023

On Saturday, 1 July 2023 at 20:52:14 UTC, Iain Buclaw wrote:

>

I guess there's scope for applying read-only semantics to both const and immutable locals/parameters. For static data, only immutables that aren't set in a ctor can be read-only.

Let's see how well this goes...

July 02, 2023
On Saturday, 1 July 2023 at 20:52:14 UTC, Iain Buclaw wrote:
> On Wednesday, 28 June 2023 at 17:27:37 UTC, Cecil Ward wrote:
>> GDC is generating a structure in the code segment and then fetching it even though the values of the elements ought to be known at compile-time. I’m not sure why. The actual code, x86-64 in this case, consists of a load of fetches from the code segment into successive registers before the start of the loop, and the loop then consists of a load of instructions like cmp [r8+4*rax], r9, where r9, r10 etc we’re loaded up from the fetches from [rip+disp] before th4 loop, a minor strength reduction compared to a compare-immediate, as it has plenty of registers free. The mystery is why the fetches from [rip+disp] even exist, given that they are known values.
>
> Right, `immutable` (as well as `const`) has rather wobbly meanings, because you can have immutable data/fields that are initialized by a constructor - so in the worst case it can't be rodata at all.
>
> For example:
> ```
> immutable uint[] arr;
>
> shared static this()
> {

Thanks for that Iain, I didn’t know that but I suspected as much. It would be good if immutables could be just turned into immediate values where individual elements are known. It would also be good if larger immutables could be placed in real ROM or else in a readonly no-execute data segment, where the system architecture permits.