C++ Club - 166. WG21 September 2023 mailing, Contracts, Modules, Cppfront
Episode Date: November 12, 2023With Gianluca Delfino, VladimÃr ArnoÅ¡t, Sergey Ishin, Damask Talary-Brown et al.Notes: https://cppclub.uk/meetings/2023/166/Video: https://youtu.be/6L3Vk6Zax_w...
Transcript
Discussion (0)
Welcome to the club. This is meeting 166 and today is the 5th of October 2023.
We start with a bit of feedback from Roy Barkan on the previous episode regarding erroneous behavior.
He writes, I think the erroneous behavior proposal is meant to allow the standard to specify situations where it is allowed to
either fail on or to allow but be deterministic if it allows the situation. To try and clarify,
here are various situations that the standard describes. Ill-formed, compilation must fail undefined behavior
compilation must succeed
compiler might warn
but code can do anything
the third one
ill-formed
no diagnostics required
the dreaded if-and-the-are
compilation is allowed to fail
or generate code that can do anything
basically without warning you and the fourth one Compilation is allowed to fail or generate code that can do anything.
Basically without warning you. And the fourth one, the new one, is erroneous
behavior. Compilation is allowed to fail or generate deterministic, not explode,
behavior. So thanks to Roy Barkan for the clarification, although how it all plays out in the end is still a bit murky, how these four can coexist together.
I must say, you know, I was a bit confused by this, but I'm warming up to the idea. If we manage to get into a situation where people would get less worried about using C++,
because at least there is some...
There is not going to be undefined behavior, there's going to be erroneous behavior,
but the missiles won't launch and bad things will not necessarily happen, then maybe, maybe it's a
good thing. And there is some more, I've seen some more proposals building on top of this.
So I'm open to the ideas. I still think we'll need to explain this to newcomers and it's going to be adding to the complexity but maybe it solves
more problems than it creates.
Yeah, anything potentially improving the situation with undefined behavior is good. Next topic is the committee mailing for September 2023.
There's quite a few papers.
Let me go through some of them.
Unfortunately, as a spoiler alert, there's nothing on reflection or pattern matching.
Boo.
This paper is called Vector API for Random Number Generation.
This is at revision 9, so it's been brewing for quite a while.
It's a convenience, I guess, improvement.
In the motivation and scope, they say,
the C++11 random number API is essentially a scalar one.
Stateful nature and the scalar definition of
underlying algorithms prevent auto vectorization by the compiler. So this taper proposes a new
interface which is basically an equivalent of generating random numbers in a loop, but it allows for better vectorization and better convenience.
So the proposed API is a member of StudRanger's namespace and it's called generate underscore where you create a random number engine, set it up, saying you need, for example, uniform
real distribution, and then pass those parameters to generate random together with an array
of floats, for example, of certain length, and it fills that array with random numbers. And the paper also added iterator-based versions
for when you don't have ranges. Yeah, that sounds good.
Next paper on my list is related to contracts. It's titled A Natural Syntax for Contracts
by Jens Maurer and Tim O'Donoghue. I must say it's a clever title. When you call something
a natural syntax, it kind of prepares your brain for accepting it as something completely natural.
This is different from the previously proposed syntax which is attribute-like.
And this one looks like a function or a concept.
Again, they use the same sort of keywords, pre, post, and assert.
I think there is an assert somewhere in the paper.
But it looks pretty much exactly like attributes, but instead of using attributes like syntax,
you use sort of a statement.
It's not a function, it's more like a statement. Pre-parentheses, condition
in the parentheses, parentheses close, semicolon, and after the function signature you have a
series of those preconditions and postconditions. And you also... It seems like they want to keep this fairly
short, like they don't intend contracts to be massive amount of code,
just something that you can put in a quick line.
Yeah, actually that's a good point. All the contract-related papers illustrate contract conditions as something short.
Yeah. It would be dreadful to see the code where a contract, a condition, spans pages of code.
Although I'm sure it's... someone's gonna write that and this syntax kind of disencourages that
because you don't have curly braces you have normal brackets round brackets that makes it
kind of look like you know it shouldn't be too much code there but yeah and does it introduce a scope in a way? I've seen this syntax with the columns somewhere.
It's an interesting take, I think. And they say, quote, this order is consistent with the natural
order of reading a function declaration. Typically, the reader will first want to see the function
signature and whether it's virtual, then any compile time constraints, the reader will first want to see the function signature and whether it's virtual,
then any compile-time constraints, the requires clause, and finally any runtime constraints,
the contract checking annotations.
So that's the explanation of the natural word.
The next paper is also contracts related.
It's titled A Principled Approach to Open Design Questions for Contracts by Joshua Byrne.
So this is like a summary of the approach and a list of open questions for the contract's MVP,
minimum viable proposal.
Quote, SG21 has made significant progress
toward producing a complete design for our contract's
facility MVP.
As work proceeds for completing this feature proposal, open questions must be considered
and answered such that the feature will eventually have widespread adoption throughout the entire
C++ ecosystem. Fundamental design principles that help guide such decisions for the
contract facility are presented in this paper as are proposals that address most,
if not all, of the open questions affecting the design of the contract's facility MVP.
In the introduction, they summarize the other papers with links, which is convenient.
And then they list principles and basically all kinds of stuff about the current state of things in MVP.
I must admit I've only skimmed this paper.
Quote.
The contract MVP being developed by SG21 is progressing smoothly towards a complete
proposal according to the agreed upon schedule.
Hold that thought!
The next paper, 48 pages long, by Joshua Byrne, Jake Fevold and John Lakos.
And it's titled A bold plan for a complete contract facility.
I'm not actually sure.
This looks like a complete thing.
And I'm not sure if it builds on the MVP work or it's something separate.
The previous paper was written by Joshua Byrne and he's also one of the co-authors of this one,
so I'm guessing this built on the MVP. But given the previous quote...
I mean, it happened before, when the existing work was suddenly...
How should I say it?
Well, previously existing work was derailed by a new paper.
And I wouldn't be completely surprised if it happened again.
But yeah, this is interesting actually. I will need
to read that paper more thoroughly at the moment, I've just skimmed it. And
given that it's from Bloomberg, it could be that they apply their own requirements to the existing work, so that contracts that
get into C++ will be tailored more towards their situation.
But interestingly, there's a chapter called syntax and they list both proposed
syntaxes, the attribute-like syntax and the natural syntax. And they say
until a final choice is made on the syntax for contracts this paper will use
the alternate attribute-like syntax and will include footnotes, where a
significant deviation with the other syntax choices is worth noting. And the alternate
attribute-like syntax... So a third one then? Sure, why not?
Yeah, anyway, I thought it was interesting that this paper suddenly appeared in such a
complete form maybe it's a good sign maybe as you pointed out you know one of the others are the
same so maybe there is some convergence maybe the completeness of this paper means that we are getting closer?
I should hope so.
I would really like there not to be a repeat of the previous situation with contracts.
Yeah, we've been burned before.
Yeah.
So yeah, okay, we'll watch this one.
This is at version zero, so anything can happen.
We need to see what will the committee feedback be.
Next paper is titled, A plan for C++ 26 ranges by Barry Revzin, Connor Hextra, and Tim Song. And it's a plan.
It summarizes the current proposed features,
current papers in flight.
There's a big table which summarizes things
that were already implemented,
including in range V3 library, which is not standard.
So yeah, it's a very useful summary and explanation of what's coming in the future.
So yeah, it's very useful paper.
Next paper is titled, sender receiver interfaceiver Interface for Networking. It's Sutter Vision 1. We've seen
it before. It's by Dietmar Kuhl of Bloomberg. If you remember a thing called Networking TS,
this is sort of a rebase of Networking TS on STED execution proposal. Not rebase as such, but let's see what the
intro says. Quote. This document proposes the addition of senders for asynchronous
networking operations to the Networking TS and ultimately to the C++ standard. As the execution proposal
isn't landed yet, this proposal is kept at a high level and is primarily intended
to discuss what a potential interface for asynchronous networking operations
could look like. So this is sort of a paper to maintain the ground for eventual introduction of senders
and receivers into a future version of networking TS.
Have I got that right?
Hopefully.
The proposed changes augment the networking TS rather than defining entirely new networking components.
The main dispute over the networking TS is the model for asynchronous execution.
The vocabulary used to interact with networking facilities like addresses, socket protocols, etc.
doesn't need to be replaced entirely to support sender-receiver.
The sender-receiver capabilities can be added, which is what the current proposal does. If the
current direction of having only one model for asynchronous operations is confirmed,
it will need to also remove the executor-based facilities. So it's like a work in progress to facilitate the future
development of networking TS once senders and receivers are complete, whenever that happens.
Which is, I think, good and a noble endeavor. And let it continue, because otherwise we'll never get any networking in the standard.
Which some people think would be a good thing. I would probably like to have it in the standard.
Okay.
The next paper is about erroneous behavior again, and it's a new proposal for adding a new type of erroneous behavior. The title is
erroneous behavior for missing return from assignment. So it's kind of a small one.
The motivation section says flowing off the end of an assignment operator overload is a common
mistake. In current C++, unlike in C, it's undefined behavior to call a
function with non-void return type that flows off the end regardless of whether the result of the
function call is used. p2795 proposes the definition of erroneous behavior in C++,
which is well-defined behavior that is nonetheless an error. This is the proposal that kind of softened me on Ronu's behavior concept because
it's a new thing and as new thing it requires new training. But it does seem to solve some issues. Here, for instance, avoiding undefined behavior again.
Maybe it's not bad.
Yeah.
This one definitely sounds like a good thing.
Although you should probably use default implementations
for things like this.
But yeah, I completely understand that there is a
potential. Maybe it was a contrived example. Brown and it's called Implication for C++.
Looking at the title, I thought it would be something pretty grand.
What is the implication for C++?
Yeah, it's a good title.
It's a good title. It's a good title. However, this is about
proposing a new operator. This paper proposes to introduce operator equals
greater than or arrow the implication operator into the C++ core language. And then the paper explains what is implication. And it's just
another kind of logical operation. I think if you have two booleans P and Q. So the semantics of the proposed operator is
identical to not P in parentheses or logical or Q. There are truth tables to illustrate this and yeah lots of
interesting words like if antecedent then consequent I was reading this
it took me a minute to figure out
the logic behind
but the more I read it
the more I liked it
at the beginning I must confess
I was a bit puzzled
but Walter Brown
is a master metaprogrammer
so
I was trying to keep an open mind.
And in fact, you know, later on in the paper,
there are examples where this thing is useful in static asserts.
And yeah, I mean, the logic is just having some sort of operator
that states if something is true, then something else also must be true.
Ignoring every case where the first thing is not true.
If the first thing is not true, then whatever.
Then everything is fine.
But if the first one is true, then the second thing must also be true.
It makes sense.
Again, I don't know if we want to go and add even more stuff like that.
But, you know, if Walter Brown says it's useful,
I'm willing to keep an open mind. Yeah, the paper is very well written,
and the language is just really nice to read. And I've written quite a few of those exact conditions in my code or seen in other people's code.
But as for the operator being introduced, I can't shake the feeling that it's a waste of a perfectly good arrow operator.
You would like this nice arrow to be used in some other way.
Give me short lambdas.
Short lambdas.
Yeah, then it's a syntax problem.
Can you use something else?
Some other double thing?
I don't know.
Anyway.
It's interesting if there's going to be any bike shedding
about what exact operator to use for this.
Probably.
Historically, C++ is a very new operator of ours.
So, yeah.
I believe the reason behind using an arrow is that in mathematics
an implication is actually denoted as an arrow,
a right-pointing arrow.
So this is kind of a logical thing, but the thin arrow is already used for pointers.
Right. I'm exposing my ignorance here. Thanks.
Maybe we could have equal, then straight line, like a pipe, and then greater than
to make into a more complete, but longer to type arrow. I don't know.
Or double minus and greater than, whatever yeah but it will be probably
error prone or ambiguous to parse yeah double minus is also a problem because you get this
you remember a few weeks ago i'm glad you showed us the uh the double minus greater than uh in a
in a while loop there was like something going to zero yeah
the ghost 2 operator
yes
operators are hard
right
next
someone on reddit says
question
is SG7 reflection is still active
and Reddit says, question, is SG7 reflection is still active?
And the answer, unfortunately, is no.
Someone replies, I'm not very informed,
but I read on previous Reddit posts that the author of reflection papers had some more important stuff come up.
So there's no one to push the reflection papers for now.
And yeah,
someone posted earlier that the forums and I think mailing lists are
completely silent.
It's a very sad, very a very sad story. So we might not even get it in 26.
Next topic is CMake.
And it gets C++ module support at last in version 3.28.
I kind of feel that this warrants a bigger version increase given that's such a huge feature. It's like 27 modules, no modules, 28 modules.
3.3 maybe at least.
I'm looking forward to using this.
After five years, it's finally done. Next CMake 3.28 release will support CPP modules.
C++20 named modules are now supported by Ninja generators and Visual Studio generators for Visual Studio 2022 and newer.
In combination with the MSVC 14.34 toolset provided with Visual Studio 17.4
nice version numbers there Visual Studio and newer, LLVM Clang 16 and newer and GCC 14 after the 2023 0920 daily bump and newer. So
GCC module support hasn't even warranted a new version number or anything. It's like, this is the commit, use it for modules. But
yeah, at last, well done everyone involved in this. I'm really glad to see it.
Maybe now people will start using modules at last. Is there support for import std on any of the compilers? I think maybe...
MSVC I think yes, others I'm not sure. I think...
Maybe this gcc14 from a couple of weeks ago.
Yeah, but Clang I don't think it has it yet.
I don't know.
I'm not sure.
So yeah, and then the rest of the thread on Reddit
is about how bad CMake is.
Like, I had this shower thought the other day. CMake is the PHP of build systems.
PHP is notoriously badly designed as a language,
but everyone uses it. And I dare say so is CMake.
Right, another modules related thing. Gavidos Reyes writes on the
on the Visual Studio blog, open sourcing IFC SDK for C++ modules.
Yeah, quote, today we are thrilled to announce the availability of the IFC SDK,
a Microsoft implementation of the IFC specification. This is an open source project under the Apache license.
The IFC specification formalizes C++ programs as data amenable to semantics-based manipulation.
So this is what describes the format of the BMI, built module interface.
This is like an intermediate representation of a C++ program.
And he hopes that other compilers will adopt it
and potentially will support the same BMI format.
Currently, both GCC and Clang, I think, have their own
format for storing pre-built modules, module binary interfaces.
But I think even if they don't adopt it, there may be some useful tidbits there for the other compilers to implement or just have to look at.
Next topic is Herbsata CppFront Autumn Update.
CppFront is the compiler for Herb's Cpp2 language.
And in his blog post, he says that the compiler started self-hosting. It's interesting, he says, I haven't spent a lot of time yet converting CppFront's own
code from today's syntax 1 to my alternate syntax 2, which I'm calling Cpp1 and Cpp2
for short. So there are now two Cpp2 syntaxes?
Or does he mean that Cpp1 is just standard C++? It's not clear to me. I think he just calls it...
I believe it's just his own evolution of C++, version 1 and version 2, some improvements he made.
Right, yeah, maybe.
Maybe it is confusing. Maybe Cpp1 just does mean normal Cpp.
That would be Cpp0, we are in the CPP. That would be CPP0.
So next he says no data left behind mandatory explicit discard.
So in CPP2 no discard is now the default.
Including when calling existing C++ libraries. So he's just appending noDiscard to basically everything.
He says it's pretty painless and
his proposed way to do an explicit discard is to assign the result to the don't care wildcard, the underscore.
We do have stdignore.
I don't know if stdignore is only sanctioned to be used with std tie in principle.
And it's a little bit used as an underscore here and there but I do think
there were proposals to standardize this the underscore in a similar way I remember well
yeah yeah I think underscore the underscore is in C++ 23 but it sort of works as a normal variable. You can only have one in one scope.
And here, I wonder.
Yeah, good question.
Std ignore, you can use however many times you like.
I used it outside the std tie context, so I don't know.
I've seen it in many places as well but I'm not 100% sure that it's not a slight
misuse of standard ignore. So Cpp2 has in-out and out parameters and this leads to his explanation of how
the optimization works with CppFront when it has to determine which usage of
a variable in a scope is the last one, so that it can perform an efficient move, for example.
And here he says he presents a piece of code which
assigns something to a local variable and then passes that variable into a function.
But basically, if you don't then assign the x to a don't care variable, the compiler might think that the
function call is the last usage and might over optimize something. So you sort of have to assign it to the underscore and the assignment has to be the last operation
in a scope.
Which looks to me like there's additional nuance that you have to keep in mind.
And sort of...
Those two things are like you need to do in a different line
something that maybe it could be done in one I don't know I mean if you do std
move when you call function then you know okay so that that's it you know in
this case you would pass and then you set underscore to it I mean it does look like a two-phase process.
I'm not sure I understand it.
Could it be related to exceptions? In line 4 you actually destroy the X.
If you have some code in between and some exception happen, you will have to destroy X with other
variables at the end of the scope
so if you have some exception after line 4 in this case X already destroyed and
it could be optimized better
I don't know here if we have the usual uh c plus plus uh understanding of what
things uh what destroying means uh uh you know i mean it's it's called the structure for the vector
so so the structure of this x variable would still probably be called that line you know when when the scope ends i guess uh although
no i'm not sure if it if it should go away at the end of the scope it means if you will have
something in between and it created an exception and at the end of the scope you will have multiple objects to destroy.
In this case you will have less objects and you can optimize this exception catch.
It's really difficult to reason about this code because we don't know what happens in CppFront,
how Herb has implemented it. He doesn't even mention exceptions anyway in this article,
so maybe it wasn't on his mind even.
I think his considerations were for optimization of things
more than error handling.
I suspect that he might not have reached
the dealing with exceptions phase in his CPP front work.
But what other reason could be to destroy some variable
in the middle of the scope?
Well, he probably means to make it easier
to move the data automatically
if the receiving function can actually accept
like a right
value reference, R references. But it's important to realize that in standard C++, just passing
something by R value reference doesn't mean that the move has to happen. So the object,
the original object still needs to be destructed. Even though it has no value, the destructor
is still going to be called correct so you would do a city
move in the function call and not advanced but you know whatever is the
function call and then the object will be in this unspecified state and then
the destructor will still do something maybe nothing but still gonna be called
so I don't know I maybe looking at the generated C++ would
shed some light here.
So let's read how he explains this. In this example, that call to std.advance with the
parameter x is a definite last use of x, and so Cpp2 will automatically pass x as an r-value and make it a move candidate.
And the call won't compile, because you can't pass an r-value to a Cpp2 in-out parameter.
Which is apparently what Advanced has in Cpp2.
That's a feature, not a bug, because if that's the last use of x, that means
the function is not looking at x again, so it's ignoring the out value of this
advanced function call, which is exactly like ignoring a return value. And the
guidance is the same. If you really meant to do that, just explicitly discard x's final value. So it's not even about
optimization or moving, it's about just letting the code compile, because it won't compile
with the Cpp2 in-out parameter annotation. So then it does compile when you add underscore equal x afterwards?
Apparently yes.
Because then I suspect Cpp2
will not pass x as an
r-value by default. So an
additional statement changes how this variable is passed to a previous
function. I'm not sure I like this, to be honest. Neither do I. Yeah, it's something that he's
trying to fix in his own implementation of C++. That's what it looks like, right? Yeah. And then listen to this paragraph, quote,
I really, really like how my C++ code data flow is explicit and fully protected and safe in syntax2,
and I'm very pleased to see how it just works naturally throughout the language from universal
guaranteed initialization to explicit constructors by default, to banning implicitly discarding any values,
to uniform treatment of returned variables, whether returned by return value or the out part of in-out and out parameters,
and all of it working also with the existing C++ libraries so they are safer and nicer to use from syntax too.
That's a long sentence.
Data is always initialized. Data is never silently lost.
Data flow is always visible. Data is precious and it's always safe. This feels right and proper to
me." So that's a lot of enthusiasm. Yeah, let's see some examples on that
no-warp scope above because it's literally doing nothing.
So it could be optimized away completely, no movement needed, it's just, you know,
you get a value, add two to it and discard it.
He also added support for requires clauses. Oh yeah, this one is weird. Generalized aliases plus constexpr with equals equals.
So in CPP2 declarations are of the form thing colon type equals value.
Quote, I decided to try using the same syntax but with equals equals to denote always equal to.
And the example is namespace alias lit colon namespace equals equals std literals.
Always equal means that it's not going to change? That's what it means? I guess, yeah. It's like code-text for alias,
as opposed to variable declarations.
Yeah. Yeah. It feels like Pascal to me.
And then this other chapter, save enum and flag enum metafunctions.
In short, in CPP2, quote,
in CPP2, there's no enum feature hardwired into the language.
Instead, you write an ordinary class type
and just apply the enum metafunction.
Okay.
Then quote continues, why have this meta function on an ordinary C++ class when C++ already has both C's enum and C++'s enum class? Because it keeps the language smaller and
simpler, because it doesn't hardwire-purpose divergent splinter types into the language and compiler. And then... Okay, I'm going to omit this Beatles
quote. It's a better enum than ceenum because ceenum is unscoped and not as strongly typed. And it's a better enum class
than C++11 enum class because it's more flexible. Basically, you now can add member functions to
enums. And a flag enum variation has power of two semantics and an unsigned underlying type.
And the same he does with the union metafunction, which is also now a sort of a class.
And then he continues exactly the same points, including the Beatles thing. Yeah, so he's on fire.
He's hiding stuff like there's no tomorrow.
I mean, the Enum problem is a real problem.
I'm not sure I'm on board with a solution,
but I agree that because we don't have
reflections, then people are trying to iterate over enums in many ways, and we have
people using macros to do things with enums yeah his enums and
unions can have
a toString function which
automatically produces
what's needed
so yeah that would be a plus
but this
quote from
Reddit
first thanks to Mr. Sata that at least he's trying, which is more than what others do, myself included.
Next, an unpopular opinion. The more I look at Cpp2, the less I like the syntax it uses. It's becoming complex really fast.
And it's great that it changes improves some things but the ones i think are a mistake like
six types of arguments for a function remain so this will end in the complex syntax and a complex
language which will be an issue sooner than rather than later i think it's a very good comment i would yeah especially the part thank you to Mr. Satter
to try
which also applies
to myself
that is doing more than I am doing
so I'm just complaining
about his approach
which feels very
unfair I think
in general
me too, we're just complaining on the internet about something
that someone does.
Just grumps.
Yep.
Old man shouts at the cloud.
And then, interestingly, in the same thread,
Sator appears and says, while you're all here,
let me ask a question.
And then he proceeds with a bit of reddit-driven design,
gathering feedback on something he wants to do in Cpp2.
So recently there was an article on the llvm blog... no, it was on libc++ documentation of the LLVM project, which described modules in libc++.
They say this page contains information regarding C++23 module support in libc++.
There are two kinds of modules available in Clang.
Clang-specific modules and C++ modules? This page mainly
discusses the C++ modules. Yeah, so Clang-specific modules have been there for a good few years now,
but C++ modules are still in development as far as I know. So yeah, they list things that already work and some of the current limitations.
So this is an older article than the one about CMake support. So at the time of writing this
document there was no official build system support, although libc++ has experimental CMake support.
And they list versions of CMake that are required for each C++ standard.
This requires a recent Clang 17.
There's quite a few limitations.
But the fact that there's a separate page in the documentation about modules,
I think it's a good sign. And yeah, it all goes
toward the common goal of just simply having C++ modules everywhere.
Ah, wouldn't that be nice? There is a useful piece of C++ code on GitHub called cppdump.
It's a convenience function, an all-around dump function library for C++ that supports
even user-defined classes.
It's a debug facility, a nice debug helper. So there is a macro and there's also
macroless API which just dumps variables to the console or whatever,
to a string. Yeah, might be useful for debugging. Although if you use the fmt library, the text formatting library, it already has support
for dumping vectors, as far as I know.
This should also support all kinds of other classes.
Like this is an example of what the output looks like.
And then they support sets, maps, tuples.
Basically, whatever you throw at it, it will dump.
Looks nice. Is it single header?
Probably.
Yeah, there's, well, single header. There's quite a few headers, but it's just headers.
I think there's nothing to compile.
And headers only.
Yeah, yeah.
There's a single header to import and then to include and then
yeah it's pretty simple to use.
And finally I'll leave you with this interesting article. The URL itself is interesting. HTTPS x86.mov is Turing complete.
It's a paper that demonstrates that the x86 instruction is Turing complete.
All by itself.
The paper starts with this.
Quote. The paper starts with this, quote, It is well known that x86 instruction set is baroque, overcomplicated, and redundantly redundant.
We show just how much fluff it has by demonstrating that it remains Turing-complete when reduced to just one instruction.
This paper inspired Morphuscator, which is the single instruction C compiler.
It's completely mind-blowing. Building on this, there's a branchless,
move-only version of the classic Doom video game. Of course there's a Doom video game with this.
It's the first thing you do.
How much did the size of the binary increase, I wonder?
I don't know about the size of binary, but the quote continues.
This is thought to be entirely secure against the Meltdown and Spectre CPU vulnerabilities,
which require speculative execution and branch instructions.
Because they're none.
The move-only Doom renders approximately one frame every
seven hours, so playing this version requires somewhat increased patience.
Ouch.
Right, that's it for today. Thank you very much for coming, and I'll see you soon.
Bye.
Thank you.
Thank you, Gleb.
Thanks, Gleb.