UmbralRaptor changed the topic of #kspacademia to: https://gist.github.com/pdn4kd/164b9b85435d87afbec0c3a7e69d3e6d | Dogs are cats. Spiders are cat interferometers. | Космизм сегодня! | Document well, for tomorrow you may get mauled by a ネコバス. | <UmbralRaptor> egg|nomz|egg: generally if your eyes are dewing over, that's not the weather. | <ferram4> I shall beat my problems to death with an engineer. | We can haz pdf
egg|cell|egg has quit [Ping timeout: 204 seconds]
e_14159_ has joined #kspacademia
e_14159 has quit [Ping timeout: 378 seconds]
<_whitenotifier-a3da>
[Principia] vladtcvs opened issue #2383: "No active engines, falling back to RCS" - https://git.io/Je1ri
<_whitenotifier-a3da>
[Principia] vladtcvs edited issue #2383: "No active engines, falling back to RCS" - https://git.io/Je1ri
<_whitenotifier-a3da>
[Principia] vladtcvs edited issue #2383: "No active engines, falling back to RCS" - https://git.io/Je1ri
<_whitenotifier-a3da>
[Principia] vladtcvs edited issue #2383: "No active engines, falling back to RCS" for complicated vessels - https://git.io/Je1ri
<egg|zzz|egg>
!wpn whitequark
* galois
gives whitequark a neon silicon glaive
<egg|zzz|egg>
!wpn UmbralRaptop
* galois
gives UmbralRaptop a free electron laser
<_whitenotifier-a3da>
[Principia] pleroy opened pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<_whitenotifier-a3da>
[Principia] pleroy synchronize pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<_whitenotifier-a3da>
[Principia] pleroy synchronize pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<egg|zzz|egg>
(changing the default calling convention breaks this)
<whitequark>
ouch
<whitequark>
what in the actual hell is that doing
<_whitenotifier-a3da>
[Principia] pleroy synchronize pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<egg|zzz|egg>
whitequark: suggesting to the linker that this zone_info_source_factory be used if not otherwise defined
<egg|zzz|egg>
but weakly, if you actually define zone_info_source_factory, I think this has no effect
<egg|zzz|egg>
whitequark: I think I'll have to make zone_info_source_factory a cdecl pointer and the default factory cdecl, because tweaking that monstrosity to make it vectorcall sounds unpleasant
<egg|zzz|egg>
and I'm not even sure I can detect the default calling convention with preprocessor macros
<kmath>
<eggleroy> @stephentyrone @volatile_void Cursed thought: expression-level rounding mode control is probably feasible in C++ on… https://t.co/xLOeVduggW
<whitequark>
i could make it less verbose but it'd involve like three levels of foo_traits crap
<whitequark>
so i'll just ... keep it verbose
<whitequark>
i guess
<egg|zzz|egg>
whitequark: I find the foo traits to generally be worth it tbh
<egg|zzz|egg>
so it looks like in C++ it's A for cdecl and Q for vectorcall? Ꙩ_ꙩ
<whitequark>
sounds cursed
<whitequark>
hm
<whitequark>
is there like a constexpr for?
<whitequark>
guess not
<egg|cell|egg>
Whitequark: what do you want to do?
<egg|cell|egg>
There's an if constexpr but that's 17
<egg|cell|egg>
There's for or if in constexpr functions in 14
<whitequark>
unroll for at compile time
<whitequark>
-O3 is probably going to do a good enough job tbh
<whitequark>
egg|cell|egg: do you know how to use variadic templates to write a fold?
<whitequark>
um
<whitequark>
i want to write a `concat(T... args)` that's implemented by doing arg0.concat(arg1.concat(arg2.concat(...)))
<whitequark>
could be a different fold too, doesn't matter
<whitequark>
oh nvm i can just write it recursively
<egg|zzz|egg>
whitequark: re. doing things at compile time for performance, that's not really what things like if constexpr are for (as you say, O3 does the job unless you massively confuse it); if constexpr is semantically different, in that the things in the branch not taken may be illegal
<whitequark>
yes, i know
<whitequark>
i may not even *want* to unroll the loop
<whitequark>
e.g. if i'm summing two 4096 bit values
<whitequark>
then probably not
<egg|zzz|egg>
yeah
<whitequark>
i think verilog allows up to 1 million bit values or something like that
<egg|zzz|egg>
I'm not good at bit twiddling so I'd recommend a different reviewer for the correctness aspect (but I am happy to assist in C++ criminality)
<whitequark>
oh, hm
<whitequark>
okay. do you have any style suggestions?
<whitequark>
it seems largely fine but i don't write a lot of c++.
<egg|zzz|egg>
not yet but I haven't read through it yet
egg|zzz|egg_ has joined #kspacademia
<whitequark>
there's one particularly horrifying aspect i need
<whitequark>
i need a version of slice and concat that is an lvalue
<whitequark>
returns an lvalue
<egg|zzz|egg_>
slightly confused by the interface of the member functions; meow.shr<n>() shifts right, but not by n bits, rather *to* n bits?
<whitequark>
yes.
<whitequark>
it's like um.
<egg|zzz|egg_>
right, for symmetry with extension
<whitequark>
yes.
<whitequark>
these aren't actually arithmetic operations because arithmetic shifts preserve width.
<whitequark>
or logical.
<whitequark>
these are basically casts
<egg|zzz|egg_>
yeah, they're de-extensions
<whitequark>
no, i mean, all members
<egg|zzz|egg_>
yeah
<egg|zzz|egg_>
what's the difference between copy and zext
<whitequark>
copy is an implementation detail of all right-aligned casts
<egg|zzz|egg_>
right
<whitequark>
i could probably just inline it
<egg|zzz|egg_>
copy should probably have the same static_assert
<egg|zzz|egg_>
without it, it does UB crimes
<whitequark>
ok yeah i should have inlined it, that'd make it obvious
<whitequark>
good catch
<whitequark>
so in trunc it's n < result.chunks, in sext and zext it's n < chunks
<whitequark>
can a const overload and a non-const overload have different return values?
<egg|zzz|egg_>
yeah
<whitequark>
and the const will be selected preferentially?
<egg|zzz|egg_>
the const will be selected if you pass something const
<egg|zzz|egg_>
you have that with, say, operator[] on a vector
<whitequark>
hm
<whitequark>
what if i use a temporary
<whitequark>
that would be the non-const one, right
<egg|zzz|egg_>
no I think you'll get the const one
<whitequark>
alright great
<egg|zzz|egg_>
unless you overload on value category as well
<whitequark>
and if i try to use the overloaded function on lhs, i'll get the non-const one that returns an rvalue, right
<whitequark>
er, an lvalue
<egg|zzz|egg_>
if the lhs is non-const
<whitequark>
yes
<egg|zzz|egg_>
I'm somewhat confused by the masks
<egg|zzz|egg_>
they each apply to a specific chunk?
<whitequark>
low_mask, high_mask and sign_mask only apply to the last chunk
<egg|zzz|egg_>
what do they do
<whitequark>
sign_mask is a mask for the sign bit
<whitequark>
low_mask is a mask for the sign bit and all less significant ones
<whitequark>
high_mask is a mask for all bits more significant than the sign bit, which must always be 0 as an invariant of this data structure
<egg|zzz|egg_>
ah
<egg|zzz|egg_>
I wonder whether it would be possible to wrap access to the last chunk to enforce that
<egg|zzz|egg_>
might be overkill
<egg|zzz|egg_>
I'd have a bool sign() const that returns data[chunks - 1] & sign_mask, that seems directly useful
<egg|zzz|egg_>
(and you're using data[chunks - 1] & sign_mask in one place already)
<egg|zzz|egg_>
(or sign_bit maybe)
<whitequark>
done
<egg|zzz|egg_>
(or sign_bit_is_negative)
<egg|zzz|egg_>
(naming_is_hard)
<whitequark>
i think wrapping access is overkill, provided that i deliberately have everything public
<whitequark>
yosys doesn't use privacy and this code goes into yosys
<egg|zzz|egg_>
concat isn't C++11, right?
<whitequark>
it's c++17, yes
<whitequark>
i'll probably have to get rid of it
<whitequark>
which is ok because all code that uses this API will be generated in the first place
<egg|zzz|egg_>
they're good C++ crimes whitequirk
<whitequark>
i don't always write c++ / but when i do i generate it from more c++
<egg|zzz|egg_>
I do that quite a lot too
<egg|zzz|egg_>
(I've shown you the Principia proto-to-C++ and C# interface generator, right?)
<egg|zzz|egg_>
(I also do that kind of silliness at work :-p)
<egg|zzz|egg_>
the prolog phase of the compiler <3
<whitequark>
i need to cast T to value<something>
<egg|zzz|egg_>
whitequark: honestly re. compilation time, I wouldn't worry too much in that instance
<whitequark>
so that then i could do shr and trunc on whatever the cast returns
<whitequark>
i could cast T to value<T::bits> if there's no other way to write it
<whitequark>
so i'm just wondering about it for completeness
<egg|zzz|egg_>
what's T here
<whitequark>
another slice or a concat
<egg|zzz|egg_>
oh huh
<whitequark>
slice<value<...>, ...> would be handled in a specialization i think
<whitequark>
but basically i'm building a huge tree of slices, concats and values
<egg|zzz|egg_>
right your slices have to know what they're slicing
<whitequark>
so that i can then assign to it on lhs
<egg|zzz|egg_>
that's slightly unusual (string_view and spans and whatever just have a pointer, but your stuff has more structure so you need to know where the ends actually are)
<egg|zzz|egg_>
whitequark: this is kind of starting to feel like expression templates
<galois>
[WIKIPEDIA] Expression templates | "Expression templates are a C++ template metaprogramming technique that builds structures representing a computation at compile time, where expressions are evaluated only as needed to produce efficient code for the entire computation. Expression templates thus allow programmers to bypass the normal order..."
<kmath>
<eggleroy> @stephentyrone @volatile_void Cursed thought: expression-level rounding mode control is probably feasible in C++ on… https://t.co/xLOeVduggW
<whitequark>
yes, i am very much doing boneless expression templates
<whitequark>
with just two operations: slice and concat
<whitequark>
and honestly it doesn't even need to be very efficient
<whitequark>
the reason i'm doing this is that there's a huge project doing a similar thing (verilator), which knows how to parse verilog, synthesize verilog, optimize verilog, and emit optimized C++ code (well, it's mostly C expressions)
<whitequark>
and i'm thinking i could offload 70% of what it does to yosys, and the other 30% to clang
<egg|zzz|egg_>
yeah that makes sense
<egg|zzz|egg_>
so what's the concern with the XXX
<whitequark>
so i'm writing a translation layer that converts yosys IR to C++ almost 1:1 (ok, it'll eventually do some optimizations to help alias analysis)
<whitequark>
and a C++ library that specializes expressions
<whitequark>
oh
<whitequark>
well, i was wondering if i could somehow write a cast to value<auto> and have it choose whichever (one) operator value<...>() T has
<egg|zzz|egg_>
well, an arbitrary T could have several, so I don't think there really is such a thing
<whitequark>
in theory the compiler could complain if it can't decide which one to use
<egg|zzz|egg_>
whitequark: you could have all those types have a to_value
<egg|zzz|egg_>
or as you said a trait of sorts
<whitequark>
right
<whitequark>
ok
<whitequark>
the other question is: which cast would be appropriate there?
<whitequark>
should i just write (value<T::bits>)view ?
<whitequark>
i heard c style casts are considered harmful or something
<egg|zzz|egg_>
I'd go with static_cast
<whitequark>
ok thanks
<whitequark>
yea that works
<egg|zzz|egg_>
whitequark: I always forget what the relation with C-style casts is
<whitequark>
the relation?
<egg|zzz|egg_>
the C style casts are overly powerful iirc but I'm not sure exactly in which way
<whitequark>
right, they could simultaneously cast types, constness, and um
<whitequark>
perform conversions?
<egg|zzz|egg_>
whitequark: oh yeah they can remove const
<egg|zzz|egg_>
you very rarely want that
<egg|zzz|egg_>
and they can reinterpret_cast too, which you basically never want
<egg|zzz|egg_>
though that only works through pointers anyway
<whitequark>
whaaat, i want reinterpret_cast quite often
<egg|zzz|egg_>
(and is not insta-UB only with chars)
<whitequark>
hm
<whitequark>
wait, i can't reinterpret_cast a float as uint32_t?
<egg|zzz|egg_>
nope
<egg|zzz|egg_>
UB
<whitequark>
ugh
<egg|zzz|egg_>
whitequark: bit_cast in the future, memcpy now
* whitequark
stabs C++
<whitequark>
aaaaaaaaaa
<egg|zzz|egg_>
(all compilers now that that memcpy actually does nothing)
<whitequark>
and it's also full of such "attractive nuisance" features
<egg|zzz|egg_>
given how much praise you sing of verilog this sounds scary
<whitequark>
like verilog has a footgun where it silently infers latches. you *never*, **ever** want latches
<whitequark>
so systemverilog adds always_comb and always_ff blocks in which it's supposedly not inferring latches
<whitequark>
except i open the spec and it turns out that (a) there is no specification of their behavior. at all. and (b) they're not actually binding
<whitequark>
all they do is make the implementation emit a warning if it infers a latch
<egg|zzz|egg_>
I recommend writing your own bit_cast with a memcpy in the meantime btw
<whitequark>
which it still ACTUALLY HAS TO DO
<egg|zzz|egg_>
static_assert equal size and triviality, and memcpy
<whitequark>
even though literally no one in the entire history of synthesizable verilog wanted a latch
<_whitenotifier-a3da>
[Principia] pleroy synchronize pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<mofh>
tl;dr the Romanian gov't fucked up Unicode ON THEIR BANKNOTES
<mofh>
also holy fuck that standards fuckup
<mofh>
that 20-year chain of standards fuckups
<egg|zzz|egg_>
mofh: well everyone does so
<egg|zzz|egg_>
s is the messiest one iirc
<egg|zzz|egg_>
but the comments in that chain are a bit loaded; the concepts of unification/disunification and what is up to the font and up to the encoding are very very fuzzy as you go back in time
<egg|zzz|egg_>
lots of things seem like a good idea at the time
<mofh>
I think it's less "seemed like a good idea at the time" and more "this was the only thing possible at the time".
<_whitenotifier-a3da>
[Principia] eggrobin labeled pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<egg|zzz|egg_>
mofh: same
<egg|zzz|egg_>
mofh: lots of things are broken only in hindsight because of things not even conceivable when they are chosen
<egg|zzz|egg_>
mofh: e.g. you can't blame the turks for not foreseeing the mess of programmatic capitalization when they chose dotless i
<egg|zzz|egg_>
nor the 8-bit encoding for encoding the four different glyphs of i rather than inventing two kinds of "i"ish letters (which would be a different flavour of hell, overly disunified things are also an unholy mess)
<mofh>
yeah, like as much as I want to find a time machine to yell at Mustafa Kemal I know he could've never predicted this outcome when he designed that bit of the Turkish alphabet.
<egg|zzz|egg_>
mofh: speaking of capitalization you would have to yell at the irish
egg|zzz|egg_ has quit [Remote host closed the connection]
<_whitenotifier-a3da>
[Principia] pleroy closed pull request #2384: Add a new constructor to RigidMotion and use it in the interface - https://git.io/Je1PA
<_whitenotifier-a3da>
[Principia] pleroy pushed 6 commits to master [+0/-0/±20] https://git.io/Je1dZ
<_whitenotifier-a3da>
[Principia] pleroy f7b1290 - Add a new constructor to RigidMotion.
<egg|zzz|egg_>
argh `.template `, this language is so broken
<whitequark>
lol
<whitequark>
i want to factor those out somehow
<whitequark>
it's not strictly required but would be nice
<egg|zzz|egg_>
yeah that seems reasonable
<whitequark>
if i just extract them into something like expr_traits then the types don't line up
<whitequark>
cuz the type of *this is then expr_traits*
<whitequark>
or something
<whitequark>
let me try to do this and show you the error
<egg|zzz|egg_>
so one pattern that can sometimes help is to have a templatized free function slice (or in a traits class), and then .slice calls that
<whitequark>
but... the body is already 1 statement long
<whitequark>
if i can't just get rid of the function it makes no sense to do that
<egg|zzz|egg_>
yeah
<egg|zzz|egg_>
then inheritance might work but that's always hairy
<whitequark>
i could make both slice and concat free functions, which *sort of* works but it messes up readability
<whitequark>
because slicing is like wire[31:0]
<whitequark>
so i want it to become wire.slice<31,0>()
<whitequark>
not slice<31,0>(wire)
<egg|zzz|egg_>
yeah that definitely makes sense
<egg|zzz|egg_>
it's tricky I'll come back in half an hour to an hour, I need to watch my soufflé (food with water vapour as a structural component are kinda time sensitive)
<egg|zzz|egg_>
I mean, the llvm devs get a license to do C++ crimes of the highest order in my book, because they have a chance of actually knowing what they are doing
<whitequark>
oh StringRef is a constant source of bugs
<whitequark>
everyone hates it
<egg|zzz|egg_>
lol
<whitequark>
exactly the sort of bug you'll expect
<egg|zzz|egg_>
preventable use-after-free because your stack variable is no more
<egg|zzz|egg_>
?
<whitequark>
correct
<egg|zzz|egg_>
(or temporary)
<whitequark>
temporary mostly
<whitequark>
StringRef s = (some std::string) + ".foo";
<whitequark>
boom
<whitequark>
or more like
<egg|zzz|egg_>
yeah don't take && outside standard patterns (forward, move constructor & assignments) unless several people whom you think they know what they are doing agree
<whitequark>
you pass a StringRef constructed from a temporary to some method that takes StringRef
<egg|zzz|egg_>
s/they //
<galois>
egg|zzz|egg_ meant to say: yeah don't take && outside standard patterns (forward, move constructor & assignments) unless several people whom you think know what they are doing agree
<whitequark>
and it then captures it
<whitequark>
because of course it captures it to reduce the amount of copies
<egg|zzz|egg_>
yeah, getting a lifetime requirement by && is outright evil
<egg|zzz|egg_>
getting one by & is already a thing I avoid
<whitequark>
solvespace has a rule of no non-const & anywhere
<egg|zzz|egg_>
I'm fine with mutable &, but I take anything that wants a lifetime by pointer
<egg|zzz|egg_>
whitequark: yeah, that's a google thing too; but so is lifetime by pointer, and I find that constness by pointer sort of makes that rule less powerful
<whitequark>
hm
<egg|zzz|egg_>
because in google-style code you never know whether "by pointer" means mutates or wants lifetime, and a bunch of stuff is a pointer already because it needs mutation
<egg|zzz|egg_>
Principia-style only has "by pointer for lifetime"
<whitequark>
solvespace has ... weird lifetimes
<egg|zzz|egg_>
mutation by reference is OK, so your mutable reference can't be passed to a thing that also wants lifetime without giving you pause
<whitequark>
so the rule is "by-value or by-const-ref if immutable, by-pointer if mutable"
<whitequark>
for lifetimes solvespace uses "handles" and avoids storing pointers anywhere
<egg|zzz|egg_>
yeah, that's google style wrt mutability
<whitequark>
so you only have to make sure you don't capture a reference to something you're about to delete or move
<egg|zzz|egg_>
hm
<whitequark>
or a pointer
<whitequark>
and an invalid handle is an assert (usually) or misbehavior without UB (occasionally)
<egg|zzz|egg_>
interesting
<whitequark>
it's ... not that great tbh. like the basic idea is ok but handles also encode a notion of hierarchy
<whitequark>
and they're fixed size so it gets weird
<whitequark>
that's more cad-specific issues though
<whitequark>
of course the handles *also* go into savefiles so i can't just change them
<egg|zzz|egg_>
aaaaaa
<egg|zzz|egg_>
putting the memory model into the serialization is generally cursed
<whitequark>
oh, you should have seen the deserialization code
<whitequark>
so there are n objects it can deserialize, right?
<whitequark>
n types
<whitequark>
for each of those, it would create a single instance, and capture pointers to its fields
<whitequark>
and fill those fields when encountering the field name in the savefile
<whitequark>
but if that wasn't cursed enough, they are also all in a union
<egg|zzz|egg_>
whitequark: those traits have definitely gone beyond what I would call traits; but they form a reasonably sane base class for expressions; so maybe just expr, or expr_base
<egg|zzz|egg_>
whitequark: but I would make the destructor virtual or protected, to avoid users slicing
<galois>
[WIKIPEDIA] Object slicing | "In C++ programming, object slicing occurs when an object of a subclass type is copied to an object of superclass type: the superclass copy will not have any of the member variables defined in the subclass. (These variables have, in effect, been "sliced off".) More subtly, object slicing can also occur..."
<whitequark>
asdjfaskdl
<whitequark>
ok how does making the destructor virtual help
<egg|zzz|egg_>
hmmm
<egg|zzz|egg_>
I'm not sure
<whitequark>
also i feel like it's not a *huge* issue since this code should all be autogenerated
<egg|zzz|egg_>
whitequark: I mean tbh you could also just make this expr_base very hidden (in an internal_do_not_even_think_about_using_this_or_whitequark_will_bite_you namespace) and then nobody will declare pointers to it
<whitequark>
oh
<egg|zzz|egg_>
(of course this means you need to bite the people who inevitably end up looking into that namespace, which is a lot of work)
<whitequark>
lol
<egg|zzz|egg_>
(maybe delegate that to your cats)
<whitequark>
in some cases i'm fond of "if you broke it, you get to keep all the pieces" approach
<whitequark>
in theory the only things users will ever need to do is to convert between e.g. value<64> and uint64_t/int64_t
<whitequark>
there should be something like .as<int64_t>()
<whitequark>
and maybe value<64>::from(int64_t{1})
<egg|zzz|egg_>
yeah that can work
<egg|zzz|egg_>
the latter can be tricky
<egg|zzz|egg_>
if you want to avoid footguns like T::from(1) being an unexpected type because promotion, you may want a template that says "nope, no weird types" and prevents implicit conversions
<egg|zzz|egg_>
(basically a helper type; might be more readable to have a zcast_trait with 2 specializations)
<egg|zzz|egg_>
s/zcast::/trunc/g
<egg|zzz|egg_>
s/zcast::/trunc::/g even
<egg|zzz|egg_>
wait no
<egg|zzz|egg_>
s/::zcast/::trunc/g
<galois>
egg|zzz|egg_ meant to say: whitequark: maybe tempalte<blah> using zcast_function = std::conditional_t<(some comparison>, std::integral_constant<decltype(&value::zext), &value::zext>>, std::integral_constant<decltype(&value::trunc), &value::trunc>>
<egg|zzz|egg_>
whee
<whitequark>
./cxxrtl.hh:111:39: error: reference to overloaded function could not be resolved; did you mean to call it? std::integral_constant<decltype(&value<Bits>::zext), &value::zext>,
<whitequark>
hm
<egg|zzz|egg_>
oh right it's overloaded
<egg|zzz|egg_>
aaa
<egg|zzz|egg_>
I should pack my luggage
<egg|zzz|egg_>
afk a bit
<whitequark>
tbh i don't really need those overloads