Re: 2-D array initialization

2020-08-01 Thread Andy Balba via Digitalmars-d-learn

On Saturday, 1 August 2020 at 22:00:43 UTC, Ali Çehreli wrote:

On 8/1/20 12:57 PM, Andy Balba wrote:

> On Saturday, 1 August 2020 at 00:08:33 UTC, MoonlightSentinel
wrote:
>> On Friday, 31 July 2020 at 23:42:45 UTC, Andy Balba wrote:
>>> How does one initialize c in D ?
>>
>> ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35,
35,35]  ];

> I'm a D newbie. moving over from C/C++, and I'm really
finding it hard
> to adjusting to D syntax, which I find somewhat cryptic
compared to C/C++.

That's surprising to me. I came from C++03 year ago but 
everything in D was much better for me. :)


I wanted to respond to your question yesterday and started 
typing some code but then I decided to ask first: Do you really 
need a static array? Otherwise, the following is a quite usable 
2D array:


  ubyte[][] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 
35,35]  ];


However, that's quite different from a ubyte[3][4] static array 
because 'c' above can be represented like the following graph 
in memory. (Sorry if this is already known to you.)


c.ptr --> | .ptr | .ptr | .ptr | .ptr |
  |  |  |  |
  .  .  |   --> | 35 | 35 | 35 | 35 |
 etc.   etc. --> | 25 | 25 | 25 | 25 |

In other words, each element is reached through 2 dereferences 
in memory.


On the other hand, a static array consists of nothing but the 
elements in memory. So, a ubyte[3][4] would be the following 
elements in memory:


  | 5 | 5 | ... | 35 | 35 |

One big difference is that static arrays are value types, 
meaning that all elements are copied e.g. as arguments during 
function calls. On the other hand, slices are copied just as 
fat pointers (ptr+length pair), hence have reference semantics.


Here are some ways of initializing a static array. This one is 
the most natural one:


  ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 
35,35]  ];


Yes, that works! :) Why did you need to cast to begin with? One 
reason may be you had a value that could not fit in a ubyte so 
the compiler did not agree. (?)


This one casts a 1D array as the desired type:

  ubyte[3][4] c = *cast(ubyte[3][4]*)(cast(ubyte[])[ 5, 5, 5, 
15, 15, 15, 25, 25, 25, 35, 35, 35 ]).ptr;


The inner cast is required because 5 etc. are ints by-default.

There is std.array.staticArray as well but I haven't used it.

Ali


Although not detailed in my original question, in my actual app
I have array ubyte [1000][3] Big which consists of research data 
I obtained,
 and from which I want to randomly select 4 observations to 
construct

ubyte c[ ][ ].

i.e. construct c= [ Big[r1][3], Big[r2][3], Big[r3][3], 
Big[r4][3] ]

where r1, r2, r3 and r4 are 4 random integers in 0..1001

Being a D newbie, my naive way of doing this was to declare c 
using:

ubyte[3][4] c= [ Big[r1][3], Big[r2][3], Big[r3][3], Big[r4][3] ]

Obviously, I want to learn how to this the smart D way, but I not 
smart enough at this point.





Re: D on lm32-CPU: string argument on stack instead of register

2020-08-01 Thread Chad Joan via Digitalmars-d-learn

On Saturday, 1 August 2020 at 08:58:03 UTC, Michael Reese wrote:

[...]
So the compiler knows how to use r1 and r2 for arguments. I 
checked again in the lm32 manual 
(https://www.latticesemi.com/view_document?document_id=52077), 
and it says:


"As illustrated in Table 3 on page 8, the first eight function 
arguments are
passed in registers. Any remaining arguments are passed on the 
stack, as

illustrated in Figure 12."

So strings and structs should be passed on the stack and this 
seems to be more an issue of the gcc lm32 backend than a D 
issue.




Nice find!

Though if the compiler is allowed to split a single uint64_t into 
two registers, I would expect it to split struct/string into two 
registers as well. At least, the manual doesn't seem to 
explicitly mention higher-level constructs like structs. It does 
suggest a one-to-one relationship between arguments and registers 
(up to a point), but GCC seems to have decided otherwise for 
certain uint64_t's. (Looking at Table 3...) It even gives you two 
registers for a return value: enough for a string or an array. 
And if the backend/ABI weren't up for it, it would be 
theoretically possible to have the frontend to lower strings 
(dynamic arrays) and small structs into their components before 
function calls and then also insert code on the other side to 
cast them back into their original form. I'm not sure if anyone 
would want to write it, though. o.O



But I just found a workaround using a wrapper function.

void write_to_host(in string msg) {
write_to_hostC(msg.ptr, msg.length);
}

I checked the assembly code on the caller side, and the call 
write_to host("Hello, World!\n") is inlined. There is only one 
call to write_to_hostC. This is still not nice, but I think I 
can live with that for now. Now I have to figure out how make 
the cast back from from pointer-length pair into a string. I'm 
sure I read that somewhere before, but forgot it and was unable 
to find it now on a quick google search...


That's pretty clever. I like it.

Getting from pointer-length to string might be pretty easy:
string foo = ptr[0 .. len];

D allows pointers to be indexed, like in C. But unlike C, D has 
slices, and pointers can be "sliced". The result of a slice 
operation, at least for primitive arrays+pointers, is always an 
array (the "dynamic" ptr+length kind). Hope that helps.



And since this is D: is there maybe some CTFE magic that allows 
to create these wrappers automatically? Somthing like


fix_stack!write_to_host("Hello, World!\n");



It ended up being a little more complicated than I thought it 
would be. Hope I didn't ruin the fun. ;)


https://pastebin.com/y6e9mxre


Also, that part where you mentioned a 64-bit integer being passed 
as a pair of registers made me start to wonder if unions could be 
(ab)used to juke the ABI:


https://pastebin.com/eGfZN0SL




Good luck with your lm32/FPGA coding. That sounds like cool 
stuff!


I'm doing this mainly to improve my understanding of how 
embedded processors work, and how to write linker scripts for a 
given environment. Although I do have actual hardware where I 
can see if everything runs in the real world, I mainly use 
simulations. The coolest thing in my opinion is, nowadays it 
can be done using only open source tools (mainly ghdl and 
verilator, the lm32 source code is open source, too). The 
complete system is simulated and you can look at every logic 
signal in the cpu or in the ram while the program executes.


Thanks for the insights; I've done just a little hobby electrical 
stuff here-and-there, and having some frame of reference for tool 
and component choice makes me feel good, even if I don't plan on 
buying any lm32s or FPGAs anytime soon :)  Maybe I can Google 
some of that later and geek out at images of other people's 
debugging sessions or something. I'm curious how they manage the 
complexity that happens when circuits and massive swarms of logic 
gates do their, uh, complexity thing. o.O





Re: 2-D array initialization

2020-08-01 Thread Ali Çehreli via Digitalmars-d-learn

On 8/1/20 12:57 PM, Andy Balba wrote:

> On Saturday, 1 August 2020 at 00:08:33 UTC, MoonlightSentinel wrote:
>> On Friday, 31 July 2020 at 23:42:45 UTC, Andy Balba wrote:
>>> How does one initialize c in D ?
>>
>> ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 35,35]  ];

> I'm a D newbie. moving over from C/C++, and I'm really finding it hard
> to adjusting to D syntax, which I find somewhat cryptic compared to 
C/C++.


That's surprising to me. I came from C++03 year ago but everything in D 
was much better for me. :)


I wanted to respond to your question yesterday and started typing some 
code but then I decided to ask first: Do you really need a static array? 
Otherwise, the following is a quite usable 2D array:


  ubyte[][] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 35,35]  ];

However, that's quite different from a ubyte[3][4] static array because 
'c' above can be represented like the following graph in memory. (Sorry 
if this is already known to you.)


c.ptr --> | .ptr | .ptr | .ptr | .ptr |
  |  |  |  |
  .  .  |   --> | 35 | 35 | 35 | 35 |
 etc.   etc. --> | 25 | 25 | 25 | 25 |

In other words, each element is reached through 2 dereferences in memory.

On the other hand, a static array consists of nothing but the elements 
in memory. So, a ubyte[3][4] would be the following elements in memory:


  | 5 | 5 | ... | 35 | 35 |

One big difference is that static arrays are value types, meaning that 
all elements are copied e.g. as arguments during function calls. On the 
other hand, slices are copied just as fat pointers (ptr+length pair), 
hence have reference semantics.


Here are some ways of initializing a static array. This one is the most 
natural one:


  ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 35,35]  ];

Yes, that works! :) Why did you need to cast to begin with? One reason 
may be you had a value that could not fit in a ubyte so the compiler did 
not agree. (?)


This one casts a 1D array as the desired type:

  ubyte[3][4] c = *cast(ubyte[3][4]*)(cast(ubyte[])[ 5, 5, 5, 15, 15, 
15, 25, 25, 25, 35, 35, 35 ]).ptr;


The inner cast is required because 5 etc. are ints by-default.

There is std.array.staticArray as well but I haven't used it.

Ali



Re: 2-D array initialization

2020-08-01 Thread Andy Balba via Digitalmars-d-learn
On Saturday, 1 August 2020 at 00:08:33 UTC, MoonlightSentinel 
wrote:

On Friday, 31 July 2020 at 23:42:45 UTC, Andy Balba wrote:

How does one initialize c in D ?


ubyte[3][4] c = [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 
35,35]  ];



none of the statements below works

 c = cast(ubyte) [ [5, 5, 5], [15, 15,15], [25, 25,25], [35, 
35,35]  ];


This is an invalid cast because it tries to coerce the entire 
literal into an ubyte. Also  it would be an assignment instead 
of an initialization because this is independent of c's 
declaration.



c[0] = ubyte[3] [5, 5, 5]   ;  c[1] = ubyte[3] [15, 15,15] ;
c[2] = ubyte[3] [25, 25,25] ;  c[3] = ubyte[3] [35, 35,35] ;


A cast is usually specified as `cast(TargetType) value` but not 
necesseray in this example. Use this instead:


c[0] = [5, 5, 5]   ;  c[1] = [15, 15,15] ;
c[2] = [25, 25,25] ;  c[3] = [35, 35,35] ;

for (int i= 0; i<3; i++) for (int j= 0; i<4; j++) c[i][j]= 
cast(ubyte)(10*i +j) ;


The array indices and the inner loop condition are wrong

for (int i= 0; i<3; i++) for (int j= 0; j<4; j++) c[j][i]= 
cast(ubyte)(10*i +j) ;


You could also use foreach-loops, e.g.

foreach (j, ref line; c)
foreach (i, ref cell; line)
cell = cast(ubyte) (10 * i + j);


@ MoonlightSentinel: sorry about the typo in for (int i= 0; i<3; 
i++) for (int j= 0; j<4; j++)


I'm a D newbie. moving over from C/C++, and I'm really finding it 
hard to adjusting to D syntax, which I find somewhat cryptic 
compared to C/C++.
After going thru a D on-line tutorial, I decided D was much 
better than C/C++, so I began by journey into D by converting one 
of my less complicated C++ apps.
I'm surprised at the enormous amount of time this D-conversion 
has taken compared to GO conversion of the very same app. 
Obviously, I need a faster way of climbing the D learning curve, 
so I would appreciate any suggestions.


Re: Lack of asm volatile qualifier (explicitly) again.

2020-08-01 Thread Iain Buclaw via Digitalmars-d-learn

On Saturday, 1 August 2020 at 02:36:41 UTC, Cecil Ward wrote:

On Thursday, 30 July 2020 at 07:05:39 UTC, Iain Buclaw wrote:

[...]


Ah. I wasn’t thinking about pure, although I do use it 
everywhere I can as a matter of course. The absence of 
something doesn’t hit you in the eye as an expression of the 
programmer’s intent I suppose, absence of pure just could mean 
the author forgot to put it in. I see your point though. The 
value of volatile I saw as in documentation.


When the baseline for asm is volatile, I don't think it's 
entirely surprising to consider pure as a cancellation of that - 
afterall, if it truly is side-effect free, then it's fine for the 
compiler to remove the statement block.


Re: dynamic array .length vs .reserve - what's the difference?

2020-08-01 Thread Steven Schveighoffer via Digitalmars-d-learn

On 7/31/20 12:32 PM, wjoe wrote:
On Friday, 31 July 2020 at 04:28:57 UTC, Ali Çehreli wrote: 
Another option, which is curiously said to be more performant in 
memory allocation than native arrays, is std.array.Appender. I've used 
function-local static Appenders to cut down on memory allocation. Here 
is an uncompiled pseudo code:


[...]


This looks like an even better way to do it.

Thanks, Ali :)


Just FYI, the reason this is faster is because there is no need to go 
through the opaque calls into druntime to figure out if 
appending-in-place is possible. The reserved length is stored directly 
in the struct.


-Steve


Re: Overload comparison operators for "nullable" types

2020-08-01 Thread Oleg B via Digitalmars-d-learn

On Thursday, 30 July 2020 at 14:52:17 UTC, H. S. Teoh wrote:
On Thu, Jul 30, 2020 at 01:41:05PM +, Oleg B via 
Digitalmars-d-learn wrote: [...]
Logically we can compare versions, but what must return 
`opCmp` if one of versions has 'not comparible' state?

[...]

opCmp is allowed to return float; so you could return float.nan 
in this case.



T


Thank you! Simple answer but not in documentation...


Re: D on lm32-CPU: string argument on stack instead of register

2020-08-01 Thread Michael Reese via Digitalmars-d-learn

On Friday, 31 July 2020 at 15:13:29 UTC, Chad Joan wrote:
THAT SAID, I think there are things to try and I hope we can 
get you what you want.


If you're willing to entertain more experimentation, here are 
my thoughts:


Thanks a lot for the suggestions and explanations. I tried them 
all but the only time I got different assembly code was your 
suggestion (3) which produced the following.



(3) Try a different type of while-loop in the D-style version:

// ideomatic D version
void write_to_host(in string msg) {
// a fixed address to get bytes to the host via usb
char *usb_slave = cast(char*)BaseAdr.ft232_slave;
size_t i = 0;
while(i < msg.length) {
*usb_slave = msg[i++];
}
}

_D10firmware_d13write_to_hostFAyaZv:
addi sp, sp, -8
addi r4, r0, 4096
sw   (sp+8), r2
sw   (sp+4), r1
addi r2, r0, 0
.L3:
or   r5, r2, r0
be r2,r1,.L1
lw   r3, (sp+8)
addi r2, r2, 1
add  r3, r3, r5
lbu  r3, (r3+0)
sb   (r4+0), r3
bi   .L3
.L1:
addi sp, sp, 8
bra

At any rate, I don't think your code is larger or less 
efficient due to utf-8 decoding, because I don't see the utf-8 
decoding.


Agreed, I think there's no code for autodecoding being generated.

I did some more experiments:
Trying to put pointer and length into a struct and pass that to 
the function. Same result, the argument ended up on the stack.


Then, I wrote the function in C and compiled it with the 
C-compiler of lm32-elf-gcc. It also puts the 64-bit POD structure 
on the stack. It seems the only arguments passed in registers are 
primitive data types. However, if I pass a uint64_t argument, it 
is registered using registers r1 and r2. So the compiler knows 
how to use r1 and r2 for arguments. I checked again in the lm32 
manual 
(https://www.latticesemi.com/view_document?document_id=52077), 
and it says:


"As illustrated in Table 3 on page 8, the first eight function 
arguments are
passed in registers. Any remaining arguments are passed on the 
stack, as

illustrated in Figure 12."

So strings and structs should be passed on the stack and this 
seems to be more an issue of the gcc lm32 backend than a D issue.


But I just found a workaround using a wrapper function.

void write_to_host(in string msg) {
write_to_hostC(msg.ptr, msg.length);
}

I checked the assembly code on the caller side, and the call 
write_to host("Hello, World!\n") is inlined. There is only one 
call to write_to_hostC. This is still not nice, but I think I can 
live with that for now. Now I have to figure out how make the 
cast back from from pointer-length pair into a string. I'm sure I 
read that somewhere before, but forgot it and was unable to find 
it now on a quick google search...
And since this is D: is there maybe some CTFE magic that allows 
to create these wrappers automatically? Somthing like


fix_stack!write_to_host("Hello, World!\n");


Good luck with your lm32/FPGA coding. That sounds like cool 
stuff!


I'm doing this mainly to improve my understanding of how embedded 
processors work, and how to write linker scripts for a given 
environment. Although I do have actual hardware where I can see 
if everything runs in the real world, I mainly use simulations. 
The coolest thing in my opinion is, nowadays it can be done using 
only open source tools (mainly ghdl and verilator, the lm32 
source code is open source, too). The complete system is 
simulated and you can look at every logic signal in the cpu or in 
the ram while the program executes.