digitalmars.D.ldc - WebAssembly image dithering example
- Allen Garvey (40/40) Aug 02 2018 Hi all. Using the WebAssembly tutorial from a few week ago, I
- kinke (19/25) Aug 02 2018 Hey, interesting, thx for sharing.
- Allen Garvey (13/24) Aug 02 2018 Thanks for your response and your offer to look into 4. The links
- Radu (7/16) Aug 03 2018 Cool stuff!
- Allen Garvey (8/11) Aug 03 2018 Thanks for the advice. I wanted to use something similar to C's
- kinke (42/47) Aug 03 2018 I won't be able to run your full example (I'll keep my personal
- Allen Garvey (43/85) Aug 03 2018 Thanks for taking the time to look in to this for me. I think on
- kinke (8/35) Aug 03 2018 I haven't looked into the actual wasm semantics; guessing by your
- kinke (72/73) Aug 03 2018 Nope, LLVM apparently doesn't like the pixels array starting at
- Allen Garvey (26/31) Aug 03 2018 Thank you very much for your help! My knowledge of pointers is a
- kinke (17/29) Aug 03 2018 They apparently are (numbers on the JS side, pointers on wasm),
- Allen Garvey (21/34) Aug 04 2018 I was reviewing your pull request and it looks very nice, much
- kinke (14/20) Aug 05 2018 If you provide the start address as null literal directly in the
- Allen Garvey (9/22) Aug 05 2018 I get that now, I guess I just meant it's sort of amusing in a
- Johan Engelen (22/32) Aug 07 2018 To add: dereferencing `null` is UB with LDC, and the optimizer
Hi all. Using the WebAssembly tutorial from a few week ago, I created a small example to dither an image. You can see the source at https://github.com/allen-garvey/wasm-dither-example and the demo at https://allen-garvey.github.io/wasm-dither-example/. I had heard about WebAssembly before this, but this was my first experience actually using it, so it was an interesting project for me, and overall a positive experience. I also wanted to share some of the challenges I overcame for the benefits of others and I would be grateful if someone more knowledgeable than me can point out if these are the results of my own stupidity, or if they are due to gaps in LDC documentation/implementation or the limits of WebAssembly in general. (For reference, I was compiling on Kubuntu 18.04 with LDC 1.11.0 beta2) 1. I had to comment out `"-L--no-warn-search-mismatch"` in LDC's `/etc/ldc2.conf` or I would get this error: -d: error: unknown argument: --no-warn-search-mismatch 2. I had to use the disable array bounds checking flag when using arrays or I would get this error: undefined symbol: __assert 3. I was confused for a while when trying to use global arrays or stack allocated arrays since I was getting pointers to garbage. To be fair to LDC, I'm not sure if WebAssembly actually supports doing this, as from my research I'm starting to think all arrays might have to be heap allocated, but it would be nice to get some sort of error or warning if you tried doing this. Also to be fair, array bounds checking was turned off at this point, which might have thrown an exception to let me know something was wrong. 4. I was not able to use any of the optimization flags except for enable inlining, since using any of them would optimize out the entire program. I'm assuming this is because there is no main function, so the compiler can't tell which functions are actually being called. I tried using the `export` or `public` keywords, but that didn't seem to make a difference. Is there some keyword or syntax I'm missing, or is that just a limitation of LDC? 5. When trying to import the std library for the cbrt and max and min function, I got various errors about undefined identifier and such. This wasn't such a big deal in this instance, as the cbrt calculation was a compile time constant, and writing functions to find the min and max values of 3 numbers is not difficult.
Aug 02 2018
On Thursday, 2 August 2018 at 22:04:50 UTC, Allen Garvey wrote:I also wanted to share some of the challenges I overcame for the benefits of others and I would be grateful if someone more knowledgeable than me can point out if these are the results of my own stupidity, or if they are due to gaps in LDC documentation/implementation or the limits of WebAssembly in general.Hey, interesting, thx for sharing. 1. Won't be an issue with v1.11 final anymore, it's already fixed in master. Also, you won't need an explicit `-link-internally` anymore. 2. To be expected. Bounds errors trigger assertions, and assertions are redirect to the C assert with `-betterC`. See https://github.com/ldc-developers/ldc/blob/master/tests/baremetal/wasm2.d#L10 for how you can stub it out (or implement it properly). 3. According to a Hello-world Rust tutorial (can't find the link), global pointers (such as a "Hello world" string literal) are relative to the module's linear memory, exposed as something like an ArrayBuffer in exports.memory in JS IIRC. I guess that's all done by LLVM and doesn't need any special handling by LDC, but I haven't tested any of this yet. 4. I'll check it out. 5. To be expected. See https://github.com/ldc-developers/ldc/pull/2787 to get an idea of what'd be required to get parts of druntime and Phobos working for bare-metal targets without C runtime libs.
Aug 02 2018
On Thursday, 2 August 2018 at 22:31:40 UTC, kinke wrote:3. According to a Hello-world Rust tutorial (can't find the link), global pointers (such as a "Hello world" string literal) are relative to the module's linear memory, exposed as something like an ArrayBuffer in exports.memory in JS IIRC. I guess that's all done by LLVM and doesn't need any special handling by LDC, but I haven't tested any of this yet. 4. I'll check it out. 5. To be expected. See https://github.com/ldc-developers/ldc/pull/2787 to get an idea of what'd be required to get parts of druntime and Phobos working for bare-metal targets without C runtime libs.Thanks for your response and your offer to look into 4. The links you posted look very helpful, and I will look into stubbing the assert, cbrt and min and max functions with JavaScript, as I found this C++ tutorial https://www.lucidchart.com/techblog/2017/05/16/webassembly-overview-so-fast-so- un-sorta-difficult/ on how to pass in external functions from JavaScript. With regards to 3, I found this link https://stackoverflow.com/questions/47529643/how-to-return-a-string-or-similar-from rust-in-webassembly about passing in a string in Rust, not sure if that is what you were referring to. It seems a bit painful, so I think I'll avoid that for now. Not that I expect you to know, and I don't really know anything about compilers, but since it is possible for the compiler to reserve space on the stack for 4 float variables, I had assumed it would be the same thing if you were using an array of 4 floats, rather than having to manually do everything. As for 5, I wasn't surprised that it didn't work, but I was sort of naively hoping it would. I don't have any experince with this type of low level programming, so I had thought no garbage collection meant it would run on anything, but I am starting to get my head around how the runtime is separated from the language.
Aug 02 2018
Looks like wasm module is sandboxed, so you should marshal anything non-trivial. You can return string pointer (ptr member) and store length in an exported global variable. If the caller reads the stored length right upon receiving the return value, it should be fine.
Aug 03 2018
On Friday, 3 August 2018 at 09:40:30 UTC, Kagamin wrote:Looks like wasm module is sandboxed, so you should marshal anything non-trivial. You can return string pointer (ptr member) and store length in an exported global variable. If the caller reads the stored length right upon receiving the return value, it should be fine.How you pass strings from wasm to js ```wasm.d extern(C): void _log(immutable(char)*, size_t); void log(string msg) { _log(msg.ptr, msg.length); } void echo() { log("Helo from WASM!"); } ``` ```wasm.js const request = new XMLHttpRequest(); request.open('GET', 'wasm.wasm'); request.responseType = 'arraybuffer'; request.onload = () => { const wasmCode = request.response; const importObject = { env: { _log: (ptr, len) => { try { var buffer = new Uint8Array(linearMemory.buffer, ptr, len); var msg = ''; for (var i=0; i < buffer.length; i++) { msg += String.fromCharCode(buffer[i]); } console.log(msg); } catch(err) { console.log(err); } } } }; var wasmModule = new WebAssembly.Module(wasmCode); var wasmInstance = new WebAssembly.Instance(wasmModule, importObject); // obtain the module memory var linearMemory= wasmInstance.exports.memory; wasmInstance.exports.echo(); }; request.send(); ```
Aug 03 2018
Something like ```wasm.d extern(C): static shared size_t len; const(char)* towastr(string s) { len=s.length; return s.ptr; } const(char)* echo() { return towastr("Hello from WASM!"); } ``` ```wasm.js ... function wastr(ptr) { var buffer = new Uint8Array(linearMemory.buffer, ptr, wasmInstance.exports.len); var msg = ''; for (var i=0; i < buffer.length; i++) { msg += String.fromCharCode(buffer[i]); } return msg; } var str=wastr(wasmInstance.exports.echo()); console.log(str); ```
Aug 03 2018
On Friday, 3 August 2018 at 11:34:23 UTC, Kagamin wrote:Something like ```wasm.d extern(C): static shared size_t len; const(char)* towastr(string s) { len=s.length; return s.ptr; } const(char)* echo() { return towastr("Hello from WASM!"); } ``` ```wasm.js ... function wastr(ptr) { var buffer = new Uint8Array(linearMemory.buffer, ptr, wasmInstance.exports.len); var msg = ''; for (var i=0; i < buffer.length; i++) { msg += String.fromCharCode(buffer[i]); } return msg; } var str=wastr(wasmInstance.exports.echo()); console.log(str); ```It wasn't a question :) it was an example, the way I did mine - you pass the length to the Js side, no need to mess with statics.
Aug 03 2018
On Friday, 3 August 2018 at 11:42:18 UTC, Radu wrote:It wasn't a question :) it was an example, the way I did mine - you pass the length to the Js side, no need to mess with statics.With your approach the callee must know what will be done with the return value, it's more handy to just have a string and then the caller decides what to do with it.
Aug 03 2018
On Friday, 3 August 2018 at 12:01:10 UTC, Kagamin wrote:On Friday, 3 August 2018 at 11:42:18 UTC, Radu wrote:Yes, but in that case I think there are better solutions, like returning a `long` that masks ptr and size, you have plenty of room given that ptr is an offset from a predefined chunk o linear memory (thus a small value) and you are also limited in size. Js will map that to Number from which you can use 56 bits for that ptr+size pair.It wasn't a question :) it was an example, the way I did mine - you pass the length to the Js side, no need to mess with statics.With your approach the callee must know what will be done with the return value, it's more handy to just have a string and then the caller decides what to do with it.
Aug 03 2018
On Thursday, 2 August 2018 at 22:04:50 UTC, Allen Garvey wrote:Hi all. Using the WebAssembly tutorial from a few week ago, I created a small example to dither an image. You can see the source at https://github.com/allen-garvey/wasm-dither-example and the demo at https://allen-garvey.github.io/wasm-dither-example/. I had heard about WebAssembly before this, but this was my first experience actually using it, so it was an interesting project for me, and overall a positive experience. [...]Cool stuff! You might wanna change some of the code to be more idiomatic, for example `DitherRCoefficient` can be turned into and enum manifest constant. As side note, compiler explorer added support for webassembly assembly :) check this out https://godbolt.org/g/PskqaL
Aug 03 2018
On Friday, 3 August 2018 at 09:41:53 UTC, Radu wrote:You might wanna change some of the code to be more idiomatic, for example `DitherRCoefficient` can be turned into and enum manifest constant.Thanks for the advice. I wanted to use something similar to C's #define, but with my quick Google search a mixin template looked like the closest I could get. Based on your comment and reading the docs on it, the enum manifest constant looks like what I was originally trying to do, though I think now I'm going to change it to actually compute the cbrt through a callback to JavaScript's Math.cbrt function.
Aug 03 2018
On Thursday, 2 August 2018 at 22:04:50 UTC, Allen Garvey wrote:4. I was not able to use any of the optimization flags except for enable inlining, since using any of them would optimize out the entire program. I'm assuming this is because there is no main function, so the compiler can't tell which functions are actually being called.I won't be able to run your full example (I'll keep my personal machine Node.js-free! ;)), so I just used `ldc2 -mtriple=wasm32-unknown-unknown-wasm -betterC -O main.wasm` with current LDC master (and added the missing __assert stub). The produced .wasm is 620 bytes small (without -O: 3,235 bytes); loading it in Firefox shows that the functions are there, here's an excerpt: (func $fillBayerMatrix (;3;) (param $var0 i32) (param $var1 i32) get_local $var1 i64.const 4469670136257392600 i64.store align=4 get_local $var1 i32.const 8 i32.add i64.const -4753701902744867000 i64.store align=4 ) (func $dither (;5;) (param $var0 i32) (param $var1 i32) (param $var2 i32) (param $var3 i32) get_local $var2 i64.const 4469670136257392600 i64.store align=4 get_local $var2 i32.const 8 i32.add i64.const -4753701902744867000 i64.store align=4 block $label0 get_local $var0 get_local $var1 i32.mul i32.const 2 i32.shl i32.const 1 i32.ge_s br_if $label0 return end $label0 unreachable unreachable )
Aug 03 2018
On Friday, 3 August 2018 at 19:01:02 UTC, kinke wrote:I won't be able to run your full example (I'll keep my personal machine Node.js-free! ;)), so I just used `ldc2 -mtriple=wasm32-unknown-unknown-wasm -betterC -O main.wasm` with current LDC master (and added the missing __assert stub). The produced .wasm is 620 bytes small (without -O: 3,235 bytes); loading it in Firefox shows that the functions are there, here's an excerpt: (func $fillBayerMatrix (;3;) (param $var0 i32) (param $var1 i32) get_local $var1 i64.const 4469670136257392600 i64.store align=4 get_local $var1 i32.const 8 i32.add i64.const -4753701902744867000 i64.store align=4 ) (func $dither (;5;) (param $var0 i32) (param $var1 i32) (param $var2 i32) (param $var3 i32) get_local $var2 i64.const 4469670136257392600 i64.store align=4 get_local $var2 i32.const 8 i32.add i64.const -4753701902744867000 i64.store align=4 block $label0 get_local $var0 get_local $var1 i32.mul i32.const 2 i32.shl i32.const 1 i32.ge_s br_if $label0 return end $label0 unreachable unreachable )Thanks for taking the time to look in to this for me. I think on a slightly earlier version of my code, when compiling with the optimizations the output was around 350 bytes and the error was something about the dither function not being able to be found. Compiling it now (still using beta 2) with the -O flag and the assert stub I get a similar 618 byte output to you, but when I try to run it, the browser gives this error: Uncaught RuntimeError: unreachable. The wasm output for the dither function is this (func $dither (export "dither") (type $t4) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (i64.store align=4 (get_local $p2) (i64.const 4469670136257392629)) (i64.store align=4 (i32.add (get_local $p2) (i32.const 8)) (i64.const -4753701902744866827)) (block $B0 (br_if $B0 (i32.ge_s (i32.shl (i32.mul (get_local $p0) (get_local $p1)) (i32.const 2)) (i32.const 1))) (return)) (unreachable) (unreachable)) and the browser says the error is on the first unreachable declaration at the end of the function. Also, if you have either python 2 or python 3 on your system, you can run a server by cd-ing into the docs directory of the project and running `python -m SimpleHTTPServer 3000` (python 2) or `python3 -m http.server 3000` (python 3). They both do the same thing as the npm script, which is to serve the site on localhost:3000. I will try seeing if I can build the current LDC master from source to test with that, but I have had mixed success with building projects from source in the past.
Aug 03 2018
On Friday, 3 August 2018 at 19:47:14 UTC, Allen Garvey wrote:I will try seeing if I can build the current LDC master from source to test with that, but I have had mixed success with building projects from source in the past.Probably not worth it, the changes since beta2 are minimal.(func $dither (export "dither") (type $t4) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (i64.store align=4 (get_local $p2) (i64.const 4469670136257392629)) (i64.store align=4 (i32.add (get_local $p2) (i32.const 8)) (i64.const -4753701902744866827)) (block $B0 (br_if $B0 (i32.ge_s (i32.shl (i32.mul (get_local $p0) (get_local $p1)) (i32.const 2)) (i32.const 1))) (return)) (unreachable) (unreachable)) and the browser says the error is on the first unreachable declaration at the end of the function.I haven't looked into the actual wasm semantics; guessing by your report, it looks as if the return after the conditional branch [the Firefox textual display is clearer IMO] would return from the enclosing block, and not from the function. Otherwise, the 2 (!) unreachables at the end would truly be unreachable. An LLVM issue, I guess.
Aug 03 2018
An LLVM issue, I guess.Nope, LLVM apparently doesn't like the pixels array starting at null (so just pass a null pointer from JS to WebAssembly). With this diff and your python2 help, I got it to work locally now (849 bytes): diff --git a/docs/js/worker.js b/docs/js/worker.js index 57756e9..b512fad 100644 --- a/docs/js/worker.js +++ b/docs/js/worker.js -162,7 +162,7 const heapSize = wasmHeap.length - imageByteSize; //dither image const performanceResults = Timer.megapixelsPerSecond('WASM ordered dithering performance', imageWidth * imageHeight, ()=>{ - wasmExports.dither(imageWidth, imageHeight, heapOffset, heapSize); + wasmExports.dither(0, imageWidth, imageHeight, heapOffset, heapSize); }); performanceResults[2] = ditherId; //dithered image is now in the wasmHeap diff --git a/wasm_src/main.d b/wasm_src/main.d index 4878a98..6959b7b 100644 --- a/wasm_src/main.d +++ b/wasm_src/main.d -11,7 +11,7 template TInitialize(T){ //sort of halfway between static and dynamic array //like dynamic array in that length and offset can be runtime values //but like static array in that length cannot change after initialization without possible causing problems - T[] fixedArray(int offset, int length){ + T[] fixedArray(void* offset, int length){ //take pointer to (global/heap? not sure correct term) memory and convert to array by taking slice //(make sure you disable bounds checking in compiler since assert is not supported in wasm currently) return (cast(T*) offset)[0..length]; -58,16 +58,16 void fillBayerMatrix(float[] bayerMatrix){ bayerMatrix[3] = -.166666667 * DITHER_R_COEFFICIENT; } -void dither(int imageWidth, int imageHeight, int heapOffset, int heapLength){ +void dither(void* pixelsData, int imageWidth, int imageHeight, void* heapStart, int heapLength){ //* 4 since RGBA format immutable int pixelsLength = imageWidth * imageHeight * 4; //pixels array starts at offset 0 in wasm heap - ubyte[] pixels = TInitialize!(ubyte).fixedArray(0, pixelsLength); + ubyte[] pixels = TInitialize!(ubyte).fixedArray(pixelsData, pixelsLength); //2x2 bayer matrix immutable int bayerDimensions = 2; //create array using heap memory - float[] bayerMatrix = TInitialize!(float).fixedArray(heapOffset, bayerDimensions*bayerDimensions); + float[] bayerMatrix = TInitialize!(float).fixedArray(heapStart, bayerDimensions*bayerDimensions); /* //adjust heapOffset and heapLength, in case we want to use them again -105,5 +105,7 void dither(int imageWidth, int imageHeight, int heapOffset, int heapLength){ } } +void __assert(const(char)* msg, const(char)* file, uint line) {} + // seems to be the required entry point void _start() {}
Aug 03 2018
On Friday, 3 August 2018 at 21:05:09 UTC, kinke wrote:Thank you very much for your help! My knowledge of pointers is a bit sketchy, so I had assumed they were pretty much interchangeable with ints. With your changes I was able to get it to compile and run beta2, with I assume similar output, as the binary was also 849 bytes. I'm seeing a speed increase of over 2x compared to the non-optimized version. This is surprising to me, because since browsers JIT everything I had assumed optimizing the wasm binary would not make that much of a difference. It's also a bit strange how llvm was able to output valid code with no optimizations, but not do it with optimizations turned on. After skimming through this link https://webassembly.org/docs/semantics/ for wasm semantics, it seems as though a function (or possibly block as well?) can end with a return statement or an unreachable statement, but not both, as in the optimized dither function output (before your changes). """ unreachable: An instruction which always traps. It is intended to be used for example after calls to functions which are known by the producer not to return. """ Also, on an unrelated note, do you by any chance know if version(WebAssembly) is supported yet (or maybe it is in master but not the beta)? I was trying to use it to conditionally include the assert stub, but it doesn't seem to be working for me.An LLVM issue, I guess.Nope, LLVM apparently doesn't like the pixels array starting at null (so just pass a null pointer from JS to WebAssembly). With this diff and your python2 help, I got it to work locally now (849 bytes):
Aug 03 2018
On Friday, 3 August 2018 at 21:55:56 UTC, Allen Garvey wrote:My knowledge of pointers is a bit sketchy, so I had assumed they were pretty much interchangeable with ints.They apparently are (numbers on the JS side, pointers on wasm), so you can simply declare the params as appropriately typed pointers. That wasn't the problem though, see below.I'm seeing a speed increase of over 2x compared to the non-optimized version.And further doubling of that performance [at least for me] with https://github.com/allen-garvey/wasm-dither-example/pull/1. :)It's also a bit strange how llvm was able to output valid code with no optimizations, but not do it with optimizations turned on.The optimizer apparently figured you were going to dereference a seemingly invalid address (probably `3`, the alpha channel of the 1st pixel, which is read unconditionally in each iteration) and so optimized the whole loop body to a trap (and branched directly to the first unreachable in the 1st iteration). These low addresses are perfectly valid and to be expected in wasm though (well, I don't encourage using null though :]). Passing the pointer as argument prevents LLVM from such aggressive optimizations.Also, on an unrelated note, do you by any chance know if version(WebAssembly) is supported yet (or maybe it is in master but not the beta)? I was trying to use it to conditionally include the assert stub, but it doesn't seem to be working for me.Ah yeah, that made it into master shortly after beta2. [In beta2, it's `WebAssembly32`.]
Aug 03 2018
On Friday, 3 August 2018 at 23:36:18 UTC, kinke wrote:And further doubling of that performance [at least for me] with https://github.com/allen-garvey/wasm-dither-example/pull/1. :)I was reviewing your pull request and it looks very nice, much more succinct and roughly 20% faster than my code. It was interesting to see what idiomatic D looks like, as I'm sure you noticed, my own D style is somewhere between C without the cruft and Java compiled to native code :). The only thing is that I've noticed a weird visual glitch that I've narrowed down to the use of the enum array for some reason, as storing the array on the heap is the only thing that seems to make it go away. To see what I'm seeing, create a png with width >= 256 pixels and make it either completely black or completely white. The glitch only shows up once and in the same general place no matter the image size, but not at the exact same array index, so I'm baffled as to what could be causing it.The optimizer apparently figured you were going to dereference a seemingly invalid address (probably `3`, the alpha channel of the 1st pixel, which is read unconditionally in each iteration) and so optimized the whole loop body to a trap (and branched directly to the first unreachable in the 1st iteration). These low addresses are perfectly valid and to be expected in wasm though (well, I don't encourage using null though :]). Passing the pointer as argument prevents LLVM from such aggressive optimizations.I see, that makes sense. The thing that's counter-intuitive to me is that if you write the pointer address as 0 right in the code it gets interpreted as null, but if you pass in the same value as 0, everything works as expected. I guess that has to do with the legacy of other systems, as only on WebAssembly is 0 a memory valid address.Ah yeah, that made it into master shortly after beta2. [In beta2, it's `WebAssembly32`.]That's good to hear.
Aug 04 2018
On Sunday, 5 August 2018 at 03:01:46 UTC, Allen Garvey wrote:The thing that's counter-intuitive to me is that if you write the pointer address as 0 right in the code it gets interpreted as null, but if you pass in the same value as 0, everything works as expected.If you provide the start address as null literal directly in the D code, the optimizer infers that you're going to read from address 0x3 in the first iteration. If you provide the start address as argument from outside code not available during optimization, LLVM cannot make any assumptions in this regard.The only thing is that I've noticed a weird visual glitch that I've narrowed down to the use of the enum array for some reasonI wasn't sure about the enum array; it'd probably make more sense to define it as static immutable directly in the function. I'd then expect this global to live in the module's memory (`exports.memory`) and be initialized properly during instantiation; this means that the start address/offset of the pixel data probably shouldn't be 0 and that you shouldn't overwrite any existing data after instantiation, just appending to it, in order not to overwrite any D globals.
Aug 05 2018
On Sunday, 5 August 2018 at 14:06:25 UTC, kinke wrote:If you provide the start address as null literal directly in the D code, the optimizer infers that you're going to read from address 0x3 in the first iteration. If you provide the start address as argument from outside code not available during optimization, LLVM cannot make any assumptions in this regard.I get that now, I guess I just meant it's sort of amusing in a philosophical sense, as it's not what you know, but how you know it.I wasn't sure about the enum array; it'd probably make more sense to define it as static immutable directly in the function. I'd then expect this global to live in the module's memory (`exports.memory`) and be initialized properly during instantiation; this means that the start address/offset of the pixel data probably shouldn't be 0 and that you shouldn't overwrite any existing data after instantiation, just appending to it, in order not to overwrite any D globals.Gotcha. I was wondering where exactly the enum array was being stored, but I see what you mean that it was being stored in the same place we were putting the pixel data. I've merged your changes and everything's looking good! Thanks again for taking the time to help me out with this.
Aug 05 2018
On Sunday, 5 August 2018 at 14:06:25 UTC, kinke wrote:On Sunday, 5 August 2018 at 03:01:46 UTC, Allen Garvey wrote:To add: dereferencing `null` is UB with LDC, and the optimizer makes use of that. (I didn't look at the code, but reading address 3 is OK. Probably you are indexing off of the null ptr, which is UB) There is the `null-pointer-is-valid` attribute that you can put on functions to make `null` dereference valid defined behavior. In D, that'd be : ``` import ldc.attributes; void invalid() { int* i = null; *i = 1; } llvmAttr("null-pointer-is-valid", "true") void valid() { int* i = null; *i = 1; } ``` Probably needs trunk LLVM to work. (I think the attribute is new) -JohanThe thing that's counter-intuitive to me is that if you write the pointer address as 0 right in the code it gets interpreted as null, but if you pass in the same value as 0, everything works as expected.If you provide the start address as null literal directly in the D code, the optimizer infers that you're going to read from address 0x3 in the first iteration. If you provide the start address as argument from outside code not available during optimization, LLVM cannot make any assumptions in this regard.
Aug 07 2018