HNHacker News
TopNewBestAskShowJobs

dzaima

1,748 karma · joined September 30, 2019

https://github.com/dzaima/

https://codeberg.org/dzaima/

submissionscomments
dzaima··on RISC-V Conditional Moves
"3R1W integer pipeline" is kinda ambiguous; I think it'd be extremely-stupid for any core to have all their ALUs be 3R. Much more sane is having ~half be such (if even that), and the rest at 2R.

Or, better yet, have the 3R extra port come from some of the 2R being split up; e.g. for a block of 3×2R1W ALUs, be able to split one up for its read ports, reusing it as 2×3R1W when needed, thereby being able to do 3R1W at 66% the throughput of 2R1W without any extra register ports (i.e. 1.3x throughput benefit of 3R1W over two 2R1W instrs). Probably has some extra costs from scheduling & co needing to handle 3R though.

dzaima··on Pointer Tagging in C++: The Art of Packing Bits into a Pointer
Indeed most architectures error if the top bits are non-zero, but that's trivially worked around by just a manual `x & 0xffffffffffff` or `x << 16 >> 16` or similar, and software already utilizes that (and indeed must do that to not immediately break on x86).

It is somewhat unfortunate to just force the larger address space to specific mmap usage, but it's hard for me to imagine that many programs actually needing more than 256TB of virtual memory that aren't doing so in a very-specialized way.

Certainly much less frequent than the already-infrequent (but very much existing, and significant! incl. both Firefox/SpiderMonkey and WebKit/JavaScriptCore) cases of programs utilizing top 16 bits being zeroes.

Then there's the option of mmap returning ranges from from the low 2^48 while possible, and using larger addresses only when that completely runs out; should mean existing software works fine before it needs more than 256TB of RAM, and, if the software checks the top bits of mmap's result being zeroes, it's not negatively affected anyway.

Really the proper solution is to go back in time and make mmap have separate lower and upper bounds addresses though.

dzaima··on Pointer Tagging in C++: The Art of Packing Bits into a Pointer
At least on Linux, you're guaranteed to get the top 16 bits free on at least x86[1] and ARM[2]. Maybe other OSes are less nice, but generally an OS doesn't really have a reason to force down the full 57-or-whatever-bit range before the program actually requests 256 terrabytes of virtual address space.

It is generally kinda sad though that there's not a way to request from mmap or equivalent that the result is in a specific range of memory (in (0; 1<<48) here). Would be useful for JIT-compiling code that needs to call into precompiled functions too.

[1]: https://www.kernel.org/doc/html/v5.8/x86/x86_64/5level-pagin...

[2]: https://www.kernel.org/doc/html/v5.8/arm64/memory.html#bit-u...

dzaima··on Slack has raised our charges by $195k per year
There is newer action on version tags: https://framagit.org/framasoft/framateam/mostlymatter/-/comm... (10.12 seeming to be the latest mattermost version, so fully up-to-date)
dzaima··on WASM 3.0 Completed
Besides making it much nicer for GC'd languages to target WASM, an important aspect is that it also allows cross-language GC.

Whereas with a manual GC, if you had a JS object holding a reference to an object on your custom heap, and your heap holds a reference to that JS object (with indirections sprinkled in to taste) but nothing else references it, that'd result in a permanent memory leak, as both heaps would have to consider everything held by the other as GC roots; so you'd still be forced to manually avoid cycles despite only ever using GC'd languages. Wasm GC entirely avoids this problem.

dzaima··on Klotski
This is an interactive accompaniment to a video that contains more information: https://www.youtube.com/watch?v=YGLNyHd2w10 (previously discussed a bit at https://news.ycombinator.com/item?id=45002866)
dzaima··on Removing newlines in FASTA file increases ZSTD compression ratio by 10x
The decompressed output needn't be in-memory (or even on-disk; it could be directly streamed to analysis) all at the same time, at which point resource limits aren't a problem at all. And I believe --long already is a "grater than or equal to" value, and should also be effectively a memory limit (or pretty close to one at least).

Seeking back in the input might theoretically work, but I feel like that could easily get very bad (aka exponential runtime); never mind needing actual seeking.

dzaima··on NPM debug and chalk packages compromised
With just a single file you can trivially use it such that everything is inlined (if it's of the sort that static-s all functions, at least), even across multiple files using it, without needing the full compile-time-destruction of LTO.

And generally it's one less file to look at, more easy to copy-paste into your project (and as a very minor security benefit you'll potentially look at arbitrary subsets of the contents every time you do a go-to-definition or use the header as docs (thus having chances to notice oddities) instead of just looking at a header).

dzaima··on AMD claims Arm ISA doesn't offer efficiency advantage over x86
Another bad choice (perhaps more accurately called a bug, but they chose to not do anything about it): vmv1.r & co (aka whole-vector-register move instructions) depend on valid vtype being set, despite not using any part of it (outside of the extreme edge-case of an interrupt happening in the middle of it, and the hardware wanting to chop the operation in half instead of finishing it (entirely pointless for application-class CPUs where VLEN isn't massive enough for that to in any way be useful; never mind moves being O(1) with register renaming))

So to move one vector register to another, you need to have a preceding vsetvl; worse, with the standard calling convention you may get illegal vtype after a function call! Even worse, the behavior is actually left reserved for for move with illegal vtype, so hardware can (and some does) just allow it, thereby making it impossible to even test for on some hardware.

Oh, and that thing about being able to stop a vector instruction midway through? You might think that's to allow guaranteeing fast interrupts while keeping easy forwards progress; but no, vector reductions cannot be restarted.. And there's the extremely horrific vfredosum[1], which is an ordered float sum reduction, i.e. a linear chain of N float adds, i.e. a (fp add latency) * (element count in vector) -cycle op that must be started completely over again if interrupted.

[1]: https://dzaima.github.io/intrinsics-viewer/#0q1YqVbJSKsosTtY...

dzaima··on A clickable visual guide to the Rust type system
Oh, true. Even further down low-level/frequently-unused details though; and RISC-V does without it (/ flags in general) roughly fine.
dzaima··on A clickable visual guide to the Rust type system
You could pretty easily have an integer representation using [-127; 128]; 128 being 0x80 of course (all other values being the same as in two's complement). Still would hold that -n == 1 + ~n, zero is all-zeroes, and the property that add/sub needn't care about signed vs unsigned. Only significant difference being that top bit doesn't determine negativeness, though of course it's still "x < 0" in code. (at the hardware level, sign extension & comparisons might also get very slightly more complicated, but that's far outside what typical programmers would need to know)

For most practical purposes outside of low-level stuff all that really matters about two's complement is Don't Get Near 2^(width-1) Or Bad™ Things Happen. Including +128 would even have the benefit of 1<<7 staying positive.

dzaima··on The repercussions of missing an Ampersand in C++ and Rust
Problem is it doesn't affect outcome at all unless you do mutation, and as such testing is irrelevant, but still can significantly impacts perf, and performance problems can take a while to surface; like, it may slowly grow from 0.1% of runtime to like 2%, low enough to not get get noticed at all at first, and still be too low to have significant thought put into it afterwards (but still way too high from a single missing character).

And, as you said, this is a meaningful difference in intent, so linting can't just blanket complain on every single instance of a non-&-ed argument.

And the difference in writing down intent is the wrong direction - doing a full nested object clone should require adding code in any sane language, whereas, in C++, making code clone takes.. negative one characters.

Whereas in Rust, the only thing that's ever implicit is a bitwise copy on objects with constant size; everything else requires either adding &-s or .clone()s, or your code won't compile.

dzaima··on Matrix.org service offline: corrupted database
Matrix's federation already does result in redundancy of the entire chat history; users not on @matrix.org can currently communicate between each other just fine even on :matrix.org rooms despite matrix.org being down.

It's entirely just user accounts that are tied to a homeserver currently. There's a proposal to make it possible for clients to fully manage their account identity (https://github.com/matrix-org/matrix-spec-proposals/pull/408...) but that doesn't look particularly active.

dzaima··on Jujutsu for everyone
Bookmarks take precedence over change ID prefixes. And indeed if you add a bookmark "xyz" and a change had a unique prefix "xyz", the change's unique prefix will grow by one. (the unique prefixes can already grow or shrink semi-arbitrarily so this doesn't "break" anything more)
dzaima··on Jujutsu for everyone
You can do a `jj new; jj restore --restore-descendants -f commit_id -t @-` with a commit id (not change id!) from the evolog to "inject" a snapshot from the evolog below your current @, leaving the file state at @ (and everywhere else in the log) intact.

Or if you have a commit ID (or otherwise revset; so a "main@origin" or whatever would work) of a specific commit that you want already, can use that instead of course too.

dzaima··on Jujutsu for everyone
Working copy handling differences is certainly an aspect of jj, but, at least for me personally (esp. given me having grown up with git), it's probably my least favorite difference from git. And yet I've moved most of my personal things from git to jj (albeit with a bunch of custom scripts to make the working copy stuff more git-y).

A significant other thing jj does is introduce change IDs (i.e. a (randomly-generated) ID that stays stable even as a commit is amended), with which it should be easier to track changed commits across forks/rebases/edits/pulls/fetches, though I've yet to use jj for collaborative projects to see how much that pans out.

Generally jj makes rebasing things, and generally editing history so much more easy than git, so force-pushes messing with branches is much nicer to "fix" however needed. Being able to leave commits in a conflicted state and resolving only when actually needed also should help.

dzaima··on Complex Iterators Are Slow
Doesn't this just replace the "slow if next() is complex" with "slow if the body of the loop is complex"? A complex iterator does have the problem of having the allocation for its result, not being able to directly branch on the `done` computation, but the usage body could also have drawbacks from not being inlined with its scope (though of course if you know that next() is sufficiently complex, there's largely no downside to at least try the other way around).

Also, depending on the VM implementation, it might run into issues if you use the same iterator-function with many different callbacks, if there's a limit to how many different values for the callback argument it specializes; don't know what exactly the limits or effects are, but in some simple testing, both Node/V8 and FF/SpiderMonkey slow down on a callback-based-iterator bench if before the main bench the "iterator" was used with multiple different callbacks (whereas the inverse of a single loop iterating through many different iterator types is probably quite a bit more rare).

dzaima··on Hardening mode for the compiler
I'm of course not saying that C has a single universal ABI. But for any single platform (OS+ISA) where it is possible and meaningful to have shared libraries in the first place, there's a pretty clear single ABI to follow, so the distinction is, for practical purposes, completely meaningless. (ok windows does have some mess, but that's windows for you, it'll achieve a mess in anything)

Still have no clue what you mean by threading; sure, threads exist, even officially so in C11, but still just in completely no way whatsoever affect ABI any more than any other part of the standard library, i.e. "as stable as the stdlib impl is".

dzaima··on Hardening mode for the compiler
Release vs debug shouldn't affect ABI (or, at least, the library author can decide whether it does; all it takes is.. not having `#if DEBUG` in your exposed header files changing types or function signatures).

Multi-threading doesn't affect ABI in any way at all.

fast-math doesn't affect ABI (maybe you mean the setting of FTZ/DAZ? but modern clang & gcc don't do that either, and anyway that breaks everything float in general, ABI itself is one of the few float things that don't immediately break, really).

Presence or absence of SIMD extensions, hard-float, or indeed any other form of extension, also doesn't modify the ABI by itself.

There's a separate -mabi=... that controls hard-float & co, but generally people don't touch that, and those that do, well, have a clear indication of "abi" in "-mabi" that tells them that they're touching something about ABI. (SIMD does have some asterisks on passing around SIMD types, but gcc does give a -Wpsabi warning when using a natively-unsupported SIMD type in a function signature; and this affects only very specialized low-level libraries, and said functions should be marked via an attribute to assume the presence of the necessary intended extension anyway, and probably are header-only and thus unaffected in the first place)

That said, it would probably make sense to have a way to configure -mabi at the function level (if this doesn't already exist).

General CPU ISA is one thing that does inescapably affect ABI of compiled programs; but you can have a stable ABI within one ISA. But yes, there's the wider requirement of "You must have version X of library Y for ISA W installed", but yet "You must have version X of library Y for ISA W compiled by compiler Z installed" is still worse.

dzaima··on This Month in Ladybird
It's not cleanly one or the other, really. It's UCS-2-y by `str.length` or `str[i]`, but UTF-16-y by `str.codePointAt(i)` or by iteration (`[...str]` or `for (x of str)`).

Generally though JS's strings are just a list of 16-bit values, being intrinsically neither UCS-2 nor UTF-16. But, practically speaking, UTF-16 is the description that matters for everything other than writing `str.length`/`str[i]`.

dzaima··on Hardening mode for the compiler
Can't solve the issue of "you just don't have the library (or a specific version thereof) installed".

But you can make it worse by changing "You must have version X of library Y installed" to "You must have version X of library Y compiled by compiler Z installed".

As-is, one can reasonably achieve ABI stability for their C library if they want to; really all it takes is "don't modify exposed types or signatures of exposed functions" and "don't use intmax_t", and currently you can actually break the latter.

dzaima··on Hardening mode for the compiler
It's certainly not impossible to write code that breaks, or modify a library in an ABI-incompatible way, but ABI stability, at least on Linux, does largely Just Work™. A missing older shared library can be quite problematic, but that's largely it.

And while, yes, there are times where ABIs are broken, compiler versions affecting things would add a whole another uncontrollable axis on top of that. I would quite like to avoid a world of "this library can only be used by code compiled with clang-25" as much as possible.

dzaima··on Hardening mode for the compiler
Maybe some things could be translated by a linker, but a linker can't change the size/layout of an in-memory data structure, and there's no info on what to translate from, even if info was added on what to translate to, anyway.
dzaima··on Hardening mode for the compiler
Compilers at least allow specifying the standard to target, which solves the ISO revision issue. But breaking within the same -std=... setting is quite a bit more annoying, forcing either indefinite patching on otherwise-complete functional codebases, or keeping potentially every compiler version on your system, both of which are pretty terrible options.
dzaima··on Using uninitialized memory for fun and profit (2008)
Right; generally the constant factors of this approach are horrible though, can't think of any situation where it'd be worth it on systems with, well, cache (or a TLB for that matter, which is even worse off with the sparse memory usage).
dzaima··on Using uninitialized memory for fun and profit (2008)
That could be classified as a bug if it was decided that the allocator must guarantee that uninitialized memory is readable as a consistent value. Otherwise, making the allocator clear the hint is just unnecessary work.

Clearing the hint on read would probably be more sane, but would mean many more potential situations of unnecessarily losing it (GC doing unnecessary scanning, doing a heap dump, debuggers trying to read it, other sorts of memory scanning)

dzaima··on Using uninitialized memory for fun and profit (2008)
Wouldn't it be just a trivial O(n) of a loop of "for (x in dense vector of one set) { if (is x a member of the other set) add to result; }"?
dzaima··on The .a file is a relic: Why static archives were a bad idea all along
--localize-hidden seems to be more what I was thinking of. So this works:

    ld --relocatable --whole-archive crappy-regular-static-archive.a -o merged.o
    objcopy --localize-hidden merged.o merged.o
This should (?) then solve most issues in the article, except that including the same library twice still results in an error.
dzaima··on The .a file is a relic: Why static archives were a bad idea all along
How possible would it be to have a utility that merges multiple .o files (or equivalently a .a file) into one .o file, via changing all hidden symbols to local ones (i.e. alike C's "static")? Would solve the private symbols leaking out, and give a single object file that's guaranteed to link as a whole. Or would that break too many assumptions made by other things?
dzaima··on Jujutsu for busy devs
I specifically have the WIP head description include the name of bookmark that it's attached to, if any (emulating git's non-detached head), so can't leave it empty (and generally wouldn't want to, for easy way to identify it, esp. with multiple workspaces).

Some push blocker for certain commit name patterns would make sense to look into.

← PreviousPage 6 of 32Next →