CppCast - Cheerp
Episode Date: January 21, 2021Rob and Jason are joined by Alessandro Pignotti. They first talk about a linker project, a better assert for constexpr code. Then they talk about Cheerp, LeaningTech's C++ WebAssembly compiler, how it... differs from emscripten, Cheerp optimizations and some of LeaningTech's other Cheerp products. News mold: A Modern Linker C++ Jobs Q1 A slightly better assert using C++20's is_constant_evaluated How can I write a C++ class that iterates over its base classes Links Cheerp Extreme WebAssembly 2: the sad state of WebAssembly tail calls CheerpX: a WebAssembly-based x86 virtual machine in the browser, Yuri Iozzelli CheerpX repl Sponsors Visual Assist
Transcript
Discussion (0)
Episode 283 of CppCast with guest Alessandro Pignotti recorded January 20th, 2021.
This episode of CppCast is sponsored by Visual Assist, the well-known productivity extensions
for Visual Studio.
Visual Assist speeds up development with features like smart navigation, code inspection and
suggestions, powerful refactoring commands, and a whole lot more, even spell checking
and comments. Start your free trial at wholetomato.com.
In this episode, we discuss a faster linker and a better assert.
Then we talk to Alessandro Pignotti, CTO of Leaning Tech.
Alessandro talks to us about Chirp, their WebAssembly compiler. Welcome to episode 283 of CppCast, the first podcast for C++ developers by C++ developers.
I'm your host, Rob Irving, joined by my co-host, Jason Turner.
Jason, how are you doing today?
All right, Rob, how are you doing?
Doing okay. You mentioned you were doing some more streaming this afternoon?
Yeah, I'm going to be starting a stream in almost exactly two hours from now,
but it'll be far too late by the time our listeners actually get this episode. I just haven't done any streaming in like over six months or something.
So I thought,
yeah,
what the heck?
I'll play with it again.
What are you planning on doing?
I've got,
well,
I can't aim my camera at it or anything at the moment,
but I have a Raspberry Pi here set up with four I squared C devices connected to it.
And it's just been so long since I've done any kind of embedded development.
I thought that I would try to see if I can get these things talking to me with
C++.
Very cool.
Very cool.
Okay.
Well,
at the top of every episode,
I'd like to read a piece of feedback.
This week,
we got some comments on Reddit.
This one is from Reddit user Kalmok on last week's episode.
And he writes,
with respect to networking,
can, can we get at least some basic concepts and vocabulary types into the standard?
Like buffers, maybe callback signatures, types for IPv4, v6 addresses, domain names, URLs, and maybe synchronous sockets.
And yeah, I think that all sounds great. And I'll just say, you know, Corinton made it clear last week that that was just his opinion uh but you know it
seems like the rest of the standards committee is still trying to get uh the full networking
library in so we're not really sure how that will play out but um we definitely don't think that
networking is like dead on arrival or anything right now no and i think that's kind of i mean
even i just wanted to say that the the linear algebra proposals that have come out of the graphics proposal look like they'll hopefully at least also just get some vocabulary types into the standard, at the very least.
Right. And I do think that that was a good way to continue getting something out of the graphics proposal, getting those types. Yeah. So if networking does go down and, you know, I'm not really sure where, what my opinion
is on that now after talking to quarantine last week, but yeah, if it does go down, hopefully
we would get something out of it.
Like vocabulary types.
I do agree with this comment, but we might still get the whole thing.
We'll see.
Yeah.
We'll see.
Okay.
Well, we'd love to hear your thoughts about the show.
You can always reach out to us on Facebook, Twitter, or email us at feedback at cbcast.com.
And don't forget to leave us a review on iTunes or subscribe on YouTube.
Joining us today is Alessandro Pignotti.
Alessandro is CTO and founder of Leaning Technologies, born in Rome, moved to Pisa for his computer engineering studies,
spent several months in the U.S., and lives now in Amsterdam.
He started the LightSpark project, a C++ open source implementation of the Flash Player.
Back when we had lots of free time, Alessandro is fond of movies and sci-fi literature.
Alessandro, welcome to the show.
Thanks a lot, Rob, and thanks a lot, Jason, for having me here.
I am so curious about your free implementation of the Flash Player,
although I think we might get into that a little bit later
in the interview with some questions that I want to ask you later. But I am curious what sci-fi
literature you're reading currently. So pretty much a couple of years ago, I joined a book club
with some friends here in Amsterdam. And the book club is about futuristic literature,
and we read both fiction and non-fiction stuff. But I sort of redeveloped my taste for sci-fi.
But to this day, I think that the most fascinating one I read was The Forever War.
Have you read this one?
I don't remember the author, but it describes the dynamic of war between humans and an alien civilization in a world where
there is no faster than light travel.
So it takes a lot of time to actually
move between space
and you will never know when you meet
the opposite faction
what technology they will have, because
lots of time has passed.
It's really fascinating.
Yeah, that sounds like, I mean, well, obviously
I haven't read the novel, but like, why would you bother if it takes generations to get to the other planet or whatever?
That's a good question, I guess.
Well, war rarely has logical roots, right?
So I'm just going to pull that up to look at that later.
Yeah.
Cool.
Okay.
Well, Alessandro, we got a couple news articles to discuss to discuss and then
we'll start talking to you more about chirp okay that's fine sure okay so this first one we have
is mold which is a modern linker uh and this is a github repository with a very nice looking readme
um and i thought i was interested when I first started reading through this
that the main goal of this project was
just to be faster than cat.
Like the command used
to concatenate objects.
As a linker, faster than cat.
Right. I didn't think that would
be the main way to
improve on performance, but yeah.
Yeah, but I sort of see the idea
there because in the end, most of the time in a linker is actually spent, you know, copying the sections in and out from
the input to the output files, which is sort of what Cat does, that you also need to do relocation,
this sort of logic, but I could see what could be the reasoning behind, let's compare to Cat.
And also, I mean, yeah, if it can get to the point where it is like actually faster to link
than it is to just concatenate files, that would be really impressive.
You know that, Jason, it sounds a little bit...
I guess it depends.
It's a matter of semantics there,
because that is achieved, if I understood correctly,
by keeping around pre-linking steps,
sort of a demo that we just keep an up-to-date idea of the files
in internal data structures so that you can link them faster.
So I would argue that that shouldn't count.
It's not like it really matters.
In the end, the tools are intended to do...
If they do their job fast, it's great.
But I'm skeptical that that comparison is fair.
Okay.
Okay.
Okay, and then the next thing we have
is a post on C c++ subreddit
and this is for c++ jobs that are being posted uh in 2021 q1 uh were there any specific jobs
you wanted to highlight jason or just kind of let listeners know that this is still
you know being posted every quarter and it's definitely a good resource for uh if you're
looking for work yeah i was just thinking i don know if we've ever mentioned it on the show.
Maybe we have, but, you know, once a quarter on the C++ Reddit,
if you're actively looking for a job or looking to hire people, they have a sticky thread there.
Yeah, and they all have, like, you know, very well-written job descriptions.
They're all definitely for C++ jobs.
And I think one of the requirements to make a posting here is that you need to have like a direct link to your company
and not just like to a recruiter.
Otherwise, this probably would get flooded with recruiters posting things.
I've been getting some particular recruiter lately that has been contacting me. The local
main
bakery in town
needs a logistics
person. And I'm like,
why? Why are you sending me this?
Well, they're doing
their jobs, I guess.
They're doing some job.
Some job.
Okay.
Something I can add here is that Lean Tech itself is looking for an intern
right now.
So we just started looking for it.
We have not yet added to the listing.
We have done it.
We actually used the Reddit thread
for our previous hire.
We didn't find it through the thread,
but we did post it there.
And I think we will also post the intern position
as soon as we get the time.
We are super busy,
so sometimes we don't work as efficiently
as we would like on the HR side.
Is the company based in Amsterdam?
Yes, yes.
So actually we are partially in Amsterdam
and partially in Leeds, UK,
but the technical team is fully here in Amsterdam.
Oh, okay. And so, are you hiring
people who are remote, or do you want someone ultimately
who can move to Amsterdam?
I think we will be
strongly preferring
a person that could move eventually here.
Even better, possibly,
a person already here on site. We're looking
for a junior position, an intern, so probably
somebody from the university, from the various universities of the country will be the best student. But we
don't, we will not exclude anybody from this. Okay. And I am reluctant to ask, but I'm still
going to anyhow. Does the fact that half of your company in the UK, is that going to be a problem
anytime soon? So, you know, the fair answer to this is that we have no clue yet.
This is how the situation is.
This is just incredibly confusing.
Honestly, I think we are on the safe side since we have companies on both sides of the channel.
So if everything goes very bad, we have a European company that we can use to keep the business going.
It's going to be okay. I just recently saw an article about a bunch of UK residents with.eu domain names that had all of their EU domains suspended, pending the ability for them to transfer the ownership to someone who is actually a resident of the EU. Yeah, to me, what is crazy is that there is such
an incredible amount of small details
connected to Brexit
in various ways. It's very difficult to
see all the ramifications down
the line, every single thing that needs to be changed.
Yeah. Okay.
The next thing we have here is
a post on the CPB subreddit again,
and this one is a
slightly better assert using C++20's
std is constant evaluated, and this has a nice
godbolt link. You want to tell us more about this one, Jason?
Well, yeah. I mean, if you try to use assert from a
constexpr function, then you'll see very quickly that you
can't because assert is not a
constexpr function. I mean, it's a macro, but what it does is not
constexpr. So I mean, it's a macro, but what it does is not constexpr.
So I've actually done tricks like this myself already recently to do.
If is constant evaluated, then do something else,
otherwise call assert, or do a debug statement or something like that.
So I just thought this was an interesting read.
And then a bunch of people were posting it on the subreddit for possibilities for how this could be enhanced
with other language features that we don't have
but options for the future.
This was interesting to read for me
since I tend to write very defensive code.
I tend to litter my code with assertions all the time
and I never figured out that this could have been
that using assert in a constant text context could have been a problem. So I learned something there.
Thanks for sharing this. Cool. Okay. And then the last thing we have is a post on the old new thing
blog, Raymond Shen. And this is how can I write a C++ class that iterates over its base classes?
And obviously this is something that we hope will be solved someday
with C++ reflection.
It's, you know, but it is possible.
And Raymond Chen showed an example of how you could do this with C++ today.
But did you feel, guys, that there is some actual use case for this?
So I appreciated the trick.
I'm not sure I see the use case.
Yeah, I kind of felt the same way.
I don't know. What do you think, Jason?
I have needed to use
something kind of
like this.
Oh, well,
maybe not exactly like this, but in a couple
of cases where, like, well, you
use similar techniques when you're doing
the merging multiple
lambdas into a visitor for calling on variant and i've used related techniques when implementing
a version of tuple that did not require recursive template instantiations so it is possible to
implement tuple without recursive templates.
It makes it much faster to compile.
And you kind of use something-ish like this
because you have to use multiple inheritance
and then you have to do things to be able to visit
all of the members of your base classes.
Cool.
Very cool.
Okay, so Alessandro,
I think we've talked about Chirp once or twice on the show in the past as a news update.
But let's hear more about what Chirp is from you, right?
Yeah, sure.
So what Chirp is, it's a compiler which is designed to make C++ a first-class language in the web.
So to solve this problem, of course, you need an efficient and high
quality WebAssembly code generation backend, but that's just one part of the problem. The thing is
that WebAssembly by itself cannot truly do that much because it is designed as a very self-contained
sandbox. And to give you an example, it is not even possible to truly write an LL word in pure WebAssembly.
Pure WebAssembly by itself cannot even write something to the console.
Any input-output is actually achieved using what are called WebAssembly imports.
So imports are a set of JavaScript functions, or more in general functions coming from the environment,
that you give to the WebAssembly module, and then the WebAssembly module will be able to use them. So pretty much what we did to
solve the old problem of making C++ a first class language for the web is to implement a brand new
fully working code generation that also works directly in JS. So the great thing about SHIP
is that you can write a single C++ code base,
and then you can compile it
to a combination of JavaScript and WebAssembly.
And there are a bunch of rules that you need to follow.
There are some complexity there,
but the idea is that with SHIP,
we have pretty much fused this complexity
into the type system.
So the compiler is able to guide you
in this somewhat convoluted process.
And the end result of this is that
you can pretty much use,
directly use browser APIs or JavaScript APIs
or anything, any object coming from the DOM
directly in C++.
And if for any reason you make a mistake
and you're trying to do this from WebAssembly,
which is not allowed, the compiler will tell you, you will know at compile time and you're trying to do this from WebAssembly, which is not allowed,
the compiler will tell you, you will not compile time, and you will be able to rework your code to avoid this problem in the first place.
So what does this actually look like when we're calling?
It sounds like you said we can call JavaScript functions directly from our C++.
Do I understand that correctly?
It's one of the things you can do.
Yeah, sure.
Okay.
And what does that look like? Do we have strongly typed actual function wrappers or something that's available at compile time?
So the way we express this is that we have pretty much decided that there is a special namespace,
which we call the namespace client.
And inside of this namespace, all the classes and functions that the browser defines are available with strongly typed interfaces from C++.
So you can literally call, to make the simplest example, would be client namespace console.log.
And you're going to call the console.log that the browser provides.
The cool thing about this is that these are all declarations.
There is no implementation from us.
So this does not require reimplementing of the browser. You're just exposing to C++ what
already exists in the browser. And it's also cool that this mechanism is extensible. So there's
really nothing special about the browser's function. This is just a bunch of declarations
which are available from JavaScript. So you can also use any JavaScript library pretty much.
Or you can, if the browser is extended with new APIs,
you can write the headers yourself.
You don't even need to wait for us to update the interfaces.
Yeah, very cool.
Whenever we talk about WebAssembly,
we usually wind up talking about Inscripten,
which is kind of what WebAssembly came
from. How do you compare Chirp to in Scriptum? How are they different? So I think that a good
perspective here is to see that the scope of the Chirp project is just much larger. So pretty much
in Scriptum at this point, works together with the upstream implementation of WebAssembly in LLVM.
Historically, the generation of WebAssembly was part of Emscripten.
Now it's merged into upstream LLVM.
And at this point, Emscripten works both as a front-end because it offers a bunch of script that makes it easier to port existing code.
And also some libraries are really important so that it's easier to port existing code. And also some libraries are ready to port,
so that it's easier to use existing code.
And then it also works as post-processing steps,
since at least the last time we checked,
the code generation of the upstream LLVM
is actually not very efficient.
And it is assumed that we'll run a tool
which is called WaspOpt
on top of the output of LLV lvm itself and this is all abstracted
away by mscript and it will do all these various steps which are required to get an optimized
output in the end so in terms of uh pure web assembly output at this point uh chip and mscript
are quite close there are some things that i think will better some things that they do better
we always try to always catch up.
But something nice is that everything we do, we do it without external tools. So, Chirp is completely self-contained, and the output is already high quality to begin with.
But, you know, all this is only for the WebAssembly part.
Then there is all the other things we were discussing before.
So, the JavaScript code generation and the interoperability with JS,
the GS export feature that we implement, which lets you write a C++ class and then use it from
JavaScript. So, you know, the other way around in terms of interoperability. All of these are
features that have pretty much no comparison from script. So you just mentioned writing a C++ class, being able to use it from JavaScript,
this kind of cross language interoperability with scripting language is something I've spent a lot
of time with, but mostly from the Swig perspective, using Swig to generate the interface bindings and
such. So how does that work? Can you give us some insight into how your tool actually takes the C++
and makes it visible in JavaScript?
Yeah, sure.
So the idea is that we tend to use lots of custom attributes to do what we do.
So it's a very general tool that can be used for a lot of purposes.
And so we have this special attribute that you can apply to a class, but also to freestanding functions in this Chirp JS export.
And when you do that, the compiler will actually enable a bunch of additional checks.
And these checks will make sure that what you write can be successfully supported to JavaScript.
So the idea is that, of course, there are limitations to what we can do, but one of our policies is that if something is not supported or impossible, the compiler will tell you a compile
time. So stuff will not explode at runtime without any warning. We try to make sure that if
something compiles it's also going to work. And the idea would be that if a class is tagged as JS export, it will export pretty much a JavaScript
class. So it will have a constructor which is defined with all the various methods
added to the prototype. So it will be very natural to use it.
And it's very elegant to me that this works. This is
tightly integrated with the JavaScript cogeneration of Chirp. Because one problem
that you have is that
it's about the lifetime. So in C++ you have a very well-defined lifetime of objects, but when you
allocate them from JavaScript this is not clear anymore. And it's unclear when will the destructor
be called. Well, the answer is never, because you have no visibility on the garbage collector.
So the compiler also imposes some rules that, for example, the destructors have to be trivial and so on and so forth,
so that it's going to be safe and valid to use that code directly from JavaScript.
Okay, so no sharing shared pointers between JavaScript and C++ with Jira.
Well, actually, you could do that.
It's important that you need to control the lifetime yourself. So if you
need to have pretty much an explicit
deallocate or free function that
will free the shared pointer,
but you do not expect
the destructor to run, and the compiler
will let you know that this is the case.
Okay. Now,
all of these attributes that you just mentioned,
do they get in the way? Like, if I wanted
to share the same code base between pure pure C++ library and a Chirp JavaScript land,
is my compiler going to complain at me because I have all these attributes that Chirp wants?
So in one direction, there's going to be no problem, right?
So if you take existing code and try to compile it with Chirp, there will be no attributes there.
So stuff will work.
Of course, you will not have it exported.
That's the default behavior.
But in the other way around,
if I recall correctly,
unsupported attributes are
ignored by the compiler. So you will get a warning,
but the thing will not
fail at compile time.
I guess that to this date,
the right way of dealing with
this sort of sharing would be to define
a bunch of if-death macro.
Okay.
In the end, multi-platform support works like that.
It is not great, but this is how it in practice works.
Sometimes it happens, yes.
Okay.
I'm kind of curious.
Have you had any success stories of taking existing C++ desktop or mobile applications and bringing them onto the web with Chirp?
So, yeah, actually a bunch.
Yeah.
So, for a while, our company offered the service of porting mobile games to the web, which is actually a pretty great use case because the games tend to,
so you know everything that runs in the main thread of the browser needs to be asynchronous,
needs to be event driven, because if you have an infinite loop, the browser will stop responding,
which is not great. So, but games follow this sort of piloting, right? They have a frame function
which is called over and over again to update the state and show new things. And also, mobile
games are based on GLES2, which maps perfectly to WebGL.
So we also offer a compatibility library there, so it was very
easy to implement. And we did
a bunch of this. It works very well. And all of this was before
WebAssembly time. So we made all
this part with pure JavaScript.
And it works fine because something
that is nice is that
Chirp is actually
a true compiler, right? It's not just a
transpiler that tries to convert
Citrus Plus expressions to JavaScript
expressions. Stuff is actually compiled,
which means that the output is also efficient.
Not only that, but there are a bunch of tricks that you can do
to make it so that when the JavaScript code is actually executed
by the browsers, it is fast.
So if you follow a bunch of very subtle rules, which you
can figure out by taking a look at the internals of the
JavaScript engines, you can achieve
a much higher performance of what you could do when you write code manually.
It's also a nice side effect of having something that can generate JavaScript for you.
Very cool.
So you said games, you know, they have this infinite loop main thing going on.
And if you just directly translated that to JavaScript, it would cause the browser to hang.
But you can do that.
You can just recompile your game that has a main loop in it, and Chirp takes care of that problem somehow.
No, no, you will do it.
So with Chirp, we try not to make magic solutions, but we try to offer the programmers the tools they need to solve the problems.
So we try to reason, well, you know, at the programming language and compiler level,
not truly at the framework level.
We don't believe that's our job.
Okay. So what does that actually look like then?
What does my game loop become if I need to,
if I want to compile it with Chirp?
So the idea, to make you a practical example,
is that we have ported lots of Android games, okay?
And these Android games are most usually written in C++,
but then they are driven by a Java front-end, right?
Because Android actually uses Java apps.
So what you will do will be to sort of remove the Java part
and take whatever is the C++ function that they call on update
and call it from RequestAnimationFrame,
which is an entry point from the browser,
which is called
at the refresh rate of the monitor to provide fluid animation and end games pretty much. So I guess that you take all the core, you disconnect it from the Java part in the case of Android,
and you reconnect it to the interfaces that the browser provides. But the good thing with
Chirp is that also this glue code, this connection part, is sold in C++, because that's the idea of this project.
What does performance end up looking like
when your Java slash C++ game has been recompiled to JavaScript with JIRP?
So when you compile to JavaScript,
you usually get a performance seat, maybe 4x, 6x, 6x, something like that.
But you also need to take into account
that this was in the old world. This was
before WebAssembly. So now
at least you will actually compile to WebAssembly and get
something between either native speed or 2x
and only use Jask for the
parts which are either
neat to interface with the browser
or
in the end are not so performance sensitive.
The thing is that it's not so bad as one could think.
It sounds like if you're going this route, you would want to limit the amount of calls
that you make back into the browser.
I just feel like that would have to be one of the bigger bottlenecks.
I'm not sure.
So it is historically true that the DOM is not very fast.
But in my experience, there was never truly that much of a concern.
Also, the amount of calls that go, you know, when you write code with this, with Chirp,
you tend to have a certain amount of code that goes from JavaScript to WebAssembly,
back to JavaScript and so on and so forth.
But it doesn't seem to be a problem in practice. That's fine. So the cost is pretty much a
call. So if the call is expensive, then it means that your inlining threshold is where
you need to tweak. So you need to add the inline keyword to the function pretty much.
But it's not so different compared to native, right? It's the same sort of reasoning.
I want to interrupt the discussion for just a moment
to bring a word from our sponsor, Visual Assist.
Visual Assist is used by serious C++ developers across the world.
It's got great code generation.
Do you need to implement methods from an interface?
What about changing a pointer to a smart pointer,
even an Unreal Engine smart pointer?
Adding a symbol you've typed but haven't declared?
Visual Assist will do these and much more.
Plus refactorings,
more powerful than the ones included in Visual C++, or detecting errors in code and suggesting
useful corrections, or navigation, helping you move anywhere in your code and open or locate
what you need, or even the debug extensions. Visual Assist is written by C++ developers for
C++ developers. It includes everything you need and nothing you don't. It has a low UI philosophy.
It won't take over your IDE, but will show up when useful. It's there to help, not to advertise
itself. Visual Assist is relied on by the developers building software you've used.
Whether that's office suites, operating systems, or games, software you use was built with Visual
Assist. Get the same tooling for your own development. Visual Assist supports Unreal
Engine 4 and many versions of Visual Studio, including
VS 2019 and Community.
Get it at wholetomato.com.
I think we saw some
things on the Chirp website
about Chirp-specific optimizations
compared to Inscriptum
or normal WebAssembly. Do you want to tell us a little bit
about those?
The thing is that to achieve high-quality code gen,
there is truly no silver bullet, okay?
There is no single optimization that solves the problem
where you can go home and rest for the night.
It's just a large collection of very small things
that overall give you high-quality code.
And whenever we do something,
we always take into account,
as I was saying before,
one level deeper.
So we always look at how the actual engines in Chrome and Firefox in particular,
how the engines generate native code
from WebAssembly or JavaScript
to decide how we generate the code
in the first place.
So we try to bridge this gap
between this additional level of virtualization
that is in the middle.
But okay, taking this
aside, there is also a few
optimizations that are somewhat larger
and we gave names to them.
Also, giving names to something
is nice when you have to write a blog post
and mention something that people can
read and talk about.
So, yeah, one of the things which
are more in general, we always
work, so the optimization we do always happens at the level of the LLVM internal representation,
which means that we have a lot more information compared to if we were trying to optimize
just WebAssembly code after it has been lowered.
So working at a higher level gives you more information, which is better for optimization purposes.
So one other optimization which is possible for optimization purposes. So and one other optimization
which is possible by using this approach is what we call the globalizations of globals,
which sounds a little bit weird, but the general idea is that WebAssembly has an independent
address space for global variables which is outside of the main memory you will normally use,
and it is possible by analyzing the use of globals to figure out if they never have their
pointer taken.
And in that case, you can actually put them into the global space of WebAssembly, which
makes for a slightly smaller code and also slightly faster in some cases.
The same technique can actually also be used for constants.
For example, large double constants, they fit much better into the space of WebAsimil Globals
compared to the actual regular memory space.
Also, using the high-level LLVM IR,
we can also do some advanced devirtualization,
which is an interesting problem because you can actually do...
Devirtualization works in two ways.
Because on one side, if you can prove that a certain indirect call always has the same target you can change it to a
direct call and that's fine that's great but then you can also maybe prove that there is no possible
target for an indirect call which means that that code can never be taken which means you can that
code eliminate all of it so there are a bunch of nice optimizations you can implement
by reasoning over the possible call sites.
And then there is also Pre-Executor, which is
probably the most brand name optimization that we have.
And Pre-Executor is pretty cool, and
it is designed pretty much to reduce code size at startup time
by consuming and removing the global constructors that are used to initialize global data.
Oh, okay. Interesting.
So you said, well, I have several things I want to discuss.
Apologies. I think I said too many information all at once. Apologies. Perhaps. Let's see.
The de-virtualization that you're talking about doing, that is a step more than what LLVM can do
on its own. Yeah. So the idea is that pretty much after we have done our regular dead code
elimination and we have reduced using normal techniques the amount of code as much as possible,
we then take an additional look and we use both rules of the rules of WebAssembly
and the rules of C++ to limit the set of possible code leaves.
So the idea is that in WebAssembly it is illegal to indirectly call a function
with the wrong number of arguments
or with different types in the arguments.
So this means that you can
rule out any call
to something which is not signature
compatible because otherwise it will anyway
explode at runtime.
So it's safe to remove it directly
at compile time at that point.
So the platform gives you some guarantees
about what is legal and you can propagate this information to remove some
additional code. Is that something that could theoretically
be done in a whole program optimization setting with
LLVM? I would say certainly yes. There is no
magic. If something can be done by us, it can also be done by LLVM upstream. It's just a matter
of figuring out what is the best way in the,
what is the best place in the tool chain to do this.
Of course, we always update, we rebase our work on top of upstream LVM.
So if eventually upstream LVM catches up or C-Lang, that's great.
We just can take advantage of whatever technique they've implemented as well.
Right. How hard is that, by the way, keeping your patch set so that it can be rebased on top of...
Or are you modifying LLVM, or are you working at a level higher than that,
just taking the IR and modifying it and then re-injecting it into the WebAssembly generator?
No, we are deeply integrated, so keeping up to date is sort of a challenge.
Over time, we have developed a process
that seemed to work pretty well
and it's not so time-consuming.
Still, LLVM tend to change quite often,
tend to break APIs quite often,
so that's not great.
So some of our long-term plans
is to try to decouple us when possible. So for example,
we would really like that all the front-end checks that I was describing before, so all this fact
that the compiler can tell you at compile time what is valid and what not, we will try to move
it outside as a C-Lanq plugin so that at least we will not have that part which is fully integrated in the code base,
but we have not yet started working on this project.
And eventually, we really hope to contribute some of our ideas back,
but currently we never have the time to do that so far.
Rob, I'm just reminded of our interview with Patricia
from a couple of weeks ago.
We're discussing her pain with trying to work with Chrome,
which is a constantly
moving target as well.
Yeah, also Chrome takes
10 minutes to compile
to link, 10 minutes to link
just to link to my computer
and takes over nights to compile.
Yeah, that's what we're talking about.
Building Chromium, yeah.
Apparently, following her most recent tweets
about that building Chromium. Apparently, following her most recent tweets about that building Chromium,
you do have to use Google's version of Clang to be able to make a pilot now.
I think they ship it together.
Yes, that's part of the annoyance there.
But anyhow, that's just an aside.
Now, I'm curious about your pre-executor as well.
You said it's mainly used
to eliminate startup code did i hear that right yeah that's so let me make an example there so
let's say that in c++ you allocate a global string okay an std string in a global space
and you need to analyze it with a constantly string literal So let's say that a sufficiently smart compiler, especially now maybe with constexpr, it will
be able to figure out that this is just constant initialize and just create the right layout
in memory for this class.
The thing is that this actually happens in practice.
It's not so sure.
And of course, you might have something, a more complicated data structure where it gets
not, where it's not viable.
So the idea is that when the compiler cannot truly convert this initialization to pure memory layout,
it will create what's called a global constructor.
So it will create a small function that inside contains a call to the constructor for that specific global object.
And if you start doing this a bunch of times,
you will end up with maybe possibly hundreds of these constructors,
which are all executed before main is even started.
So normally in native, you maybe don't even notice.
So it's not so important, I guess.
But when targeting the web platform,
both the code size and startup time is a very sensitive metric.
So the idea of Pre-Executor is that we actually run all this code inside the LLVM interpreter
while compiling the code.
So we take the code that we have just generated from LLVM, and we execute it in the interpreter.
And then we observe what the interpreter does.
In particular, we observe the memory allocation and the memory writes, which are done.
And at the end of this project, we are able to
well, if everything goes
according to plan, of course, this might fail and
the compiler will bail out and keep the
constructor as it is. But if everything goes according
to plan, we are able to extract
an actual, just
the memory layout of what you want in
the end. So you don't have the constructor
anymore. You just have a global and its corresponding constant
that describes the initialization of it at the LVM level.
The next step is that at this point,
the compiler can further optimize
because if this string is not used anywhere,
this is just going to be dropped and right in this case.
But maybe it's only used read-only, so maybe it can
be constant propagated into the user code. So there are a bunch of things that can be done
after Prexecutor, which are pretty cool. And so in practice, for one of the games we bought
years ago, using Prexecutor made the size of the generic code made out for the size, pretty much. It went down by half.
That was a little bit of a pathological case.
They had thousands of strings
allocated in the global space,
but this stuff can happen
if you don't think too much about it.
Okay.
So can it be used,
or do you use it for other possibilities,
like pre-executing pure functions,
like sine or cosine,
or something like that? So I would guess that in the case of sine or cosine, probably they will just be constant
propagated by the compiler itself. I'm not sure, but I think I've seen code for that,
at least in some cases. So possibly an interesting expansion would be that so far we deal with
everything that happens before main, right?
But there is also a bunch of code, usually in the main of a function, which is still initialization.
You're still allocating a bunch of either singletons or persistent other structures or whatever.
It could be interesting to expand pre-executor to also try and execute as much as possible from the main function itself.
So pre-executor, of course, stops
as soon as something becomes unpredictable.
If you try to query the time of the day
or anything random or access a file
or anything which cannot be known at compile time,
it will stop.
But all the initialization,
which does not require any of this input data,
it could, in theory, be pre-executed as well.
This is something that we've been thinking for a while.
We never got to try and work on this
problem, though. It is an interesting
possibility.
I imagine like a constant
folding on steroids, basically,
to do everything that could be
done, yeah. I guess that's more like how
D works. I believe
D is guaranteed to do that, execute
anything at compile time that could be executed at compile time.
Can you tell us a little bit more about if listeners want to get started with Chirp?
We did see on the website that you have a commercial and GPL license.
What do those options look like?
So the idea is that, well, if you just want to test and try something at home,
you can just use the GPL version because you're not
releasing anything, so there is no
effect whatsoever if you just want to try it out.
The idea is that
if you're not comfortable with
publishing your code as GPL after
you're done with it and you want to publish it,
then you need to acquire a commercial license.
Of course, we also offer highly
discounted licenses for individuals
and indie developers, so I thinked licenses for individuals and indie developers.
So I think that part is fair and it works.
We really hope that in the future we will be able to relax the licensee requirement.
We would like to just release everything under a more permissive license.
But so far, we cannot yet commit to that. So we hope that as the company grows, we will be more comfortable in betting that more adoption
will just convert more without worrying too much about it.
Okay.
Sorry, go ahead, Rob.
I was going to say, are there any features that you get
if you do pay for the commercial license that aren't present in the GPL?
So there are a couple of add-ons that we offer,
which we've seen that tend to be more useful for commercial users.
In particular, there is some additional support
for emulating the file system
and a partial compatibility with Emscripten.
So there is a bunch of custom headers and libraries from Emscripten
that you can use mostly to interface with JavaScript.
So the part that's integrated in SHRP needs special handling in the case of Emscripten.
And we provide some compatibility with that to provide that migration path.
But mostly the main thing you get is that you don't have to release the code.
Okay.
And we've mostly been talking about the Chirp WebAssembly compiler,
but there's other Chirp products as well.
Do you want to tell us a little bit about some of them?
Let's say they're part of a familiar product,
but they're not part of Chirp. So they have a similar
name, but ChirpJ and ChirpX,
they're not truly part
of different products. But they
are still very connected, though. So the thing is that
our technical team is made of four people,
including myself. When the intern
comes, we'll be five. So we're a very small team. And nevertheless, we are making three products,
which we believe to be sort of state of the art in what they do. And the way we can do this is
because there is a lot of synergy between these products. So for example, ChirPacks, which is our
virtualization technology that makes it possible to execute x86 binaries safely in the browser,
is written in C++ and compiled using SHRP.
For SHRPJ, we generate a JavaScript code from Java, in this case,
and we use the same JavaScript backend that we use for SHRP.
So we recycle the JavaScript backend in that case.
We also have some additional components of ChirpJ,
which are still written in C++ and compiled using Chirp.
Then there is the file system backend of ChirpJ,
which is also used in ChirpX, and so on and so forth.
So there is all these sort of components,
which are used across the various projects,
and which is possible for us to kind of sustain this development process
of all these very complex things at the same time.
So ChirpJ compiles Java to JavaScript or to WebAssembly?
To JavaScript at this time.
The thing is that WebAssembly is not truly a suitable target for Java right now.
So pretty much in WebAssembly, you will need to deal with garbage collection yourself.
While if you generate JavaScript code,
the browser takes care of that.
So it's a significant less thing to worry about.
We eventually plan to support WebAssembly, of course.
I'm sorry, I was just saying,
there are other WebAssembly targets
that use garbage collection.
Like, isn't there a C- C Sharp to WebAssembly compiler now?
I think that's Blazor, right?
You can certainly do it.
You can always do everything.
The question is, what is convenient in the end?
We don't believe that WebAssembly is the right target for Java right now.
I'm just so curious what the performance looks like from Java compiled to JavaScript, because JavaScript engines are so crazy optimized now.
And I'm not as convinced that as much effort is being put into optimizing JVMs these days as is being put into JavaScript engines.
Yeah, you know, back in my university time, I spent some time also working on programming language theory. And actually, from the academic point of view, the JVM is understood correctly.
It's recognized as state-of-the-art in machine learning.
So the JVM is actually also crazy optimized.
Of course, the problem is significantly simpler because JavaScript is much, much more dynamic compared to Java. In practice, the level of slowdown we see with ShipJ
is below 2x compared to running it natively.
So it's very fast.
Our customers run routinely extremely big swing applications.
It's fine.
The performance ceiling is great.
So you have to recompile all of whatever Java library is also being used into
JavaScript. So with ChipJ
we actually provide a full
OpenJDK compiled to JavaScript.
It's part of our runtime.
So it's not just a subset.
So there are many projects that try to run Java
in various ways in the browser,
but most usually they
have very strong limitations, like no reflection,
no multi-threading,
nothing graphical, whatever. In the case of ChipJ, stuff just works out of the box. Pretty much the
only real limitation is that you cannot run Minecraft because it uses native code. So any
3D application will use native code. So no JNI?
Yeah, we have JNI, and JNI does not fit in this model,
but everything which is pure Java will just work.
Okay.
And what's JIRPX then?
Well, it's one step further.
So instead of limiting yourself to just one language,
the idea is that you can run any x86 binary safely in the browser.
Okay. x86 binary safely in the browser. So in the first use case
that now we are publishing
due to timing constraints,
the ability of running
the actual Flash
player binary from Adobe
in the browser, even when
plugins are not supported anymore.
Of course.
As one does.
But the thing is that it's not only that, right?
I'm not sure if you had the chance to see the demo we published a couple of weeks ago,
which is our REPL demos.
So if you go to repl.linintech.com,
you will be able to run either Node.js or Python or Ruby,
a shell of these three languages,
directly client-side in your browser.
And this is not pre-compiled WebAssembly.
This is just the actual packages from a Linux distribution, from a 32-bit Linux distribution, running in the client.
Wow.
Do you want to tell us a little bit more about what's going on there?
And what type of performance hit that might have?
Yeah, so when I started thinking about this project,
something like something,
it may be five years ago or something like that,
but I started actually working on it.
We managed to allocate the time as a company to the project two years ago or so.
And when I started, my target,
like my objective was to, that I would
be happy if we managed to get
10x slowdown
compared to native. The thing is that
we are
somewhat better than that right now,
and we are not done with the project yet.
So the level of performance is actually
great. I mean,
sometimes when, you know, I work
on this every single day.
So for me, mostly I tend to be unhappy about the state of performance and I like to make it better. I profile, I optimize all this.
But every now and then I manage to look at it from just a step of distance
and I realize that already what we have achieved is pretty
incredible. And the way this works is that
in the end, it's not even, it's just what you would expect, right?
There are multi-stage execution systems, multi-stage VM,
where you have a fast interpreter and a just-in-time compiler that triggers
for sufficiently hot code. And this just-in-time compiler
will convert the binary code to new WebAssembly code
that can be executed at almost native speed by the browser.
So there is lots of moving parts.
It's an incredibly complicated problem.
But in the end, the pieces of the puzzle are always the same.
This is how you make VMs.
Okay, so maybe you know the next question I'm going to ask,
but is ChirpX written in Chirp, with Chirp?
Yeah, sure.
Okay.
So yeah, somehow, I guess it's not something usual for a software company,
but I think you could say that we are a vertically integrated software company
because we control the whole stack.
So all the tools we use are made by ourselves,
and we actually are also contributing
to the WebAssembly standardization project itself.
So we are making proposals.
We like to standardize the branching thing
as part of WebAssembly,
which is going to be very useful for ChipX.
So we try to extend all the way along the stack,
both at the top and on the bottom.
That's pretty cool, getting to write.
I'm assuming you're using C++ to write SharePacks in.
Yeah, right.
I imagine that there are a couple of people listening to this
who will be applying for internships now,
because it sounds like a lot of fun, personally.
Oh, that would be great.
It's fun. It's also very challenging so pretty much the stuff that we do tend to be things that you will not find the answer to your problem of stack overflow it takes it takes lots
of uh of deep understanding of complex systems to this sort of to work on compilers in general.
It's not the easiest
of tasks.
Right. Yeah.
Yeah, okay. What are some of the
most complex
programs you're able to run
through ChirpX?
To me, the answer would probably be
that I've not yet found something
that doesn't run. So, you know, the Flash plugin is an incredibly complex piece of software
which has been written over 20, 25 years of time.
At least.
Yeah, at least. That works fine.
Node.js, to clarify, the Node.js that runs in itself includes the V8 just in dimension.
Oh, wow. Yeah, okay.
And all this stack of virtual machines work, which is pretty crazy.
But actually, the next demo we want to publish is going to be a graphical demo.
So we will actually have the full Xorg server running in SharePacks.
And if we manage, I have a prototype
already, I need to polish it up.
I plan to also actually show
a Windows application running under
Wine on top of Xorg
in the browser.
That's my next
target for my next demo.
That works. I was going to suggest QEMU
running Win3.1
or something like that.
That's a good idea, actually, running QEMU.
I can consider this.
Thank you, Jason.
It's cool.
Very cool.
I feel like we've gone over a lot.
Is there anything else you want to tell us about or plug before we let you go today, Alessandro?
No, I just wanted to thank you.
Thank you guys for having me here.
It's, I think members of my team have been pestering me
to get in touch with you somehow
and get an interview at various points over the last several months.
So I'm very happy that I can now put them to peace about this.
Awesome. It was a lot of fun. Thanks.
Yeah, it's great having you on.
Thanks, Alessandro.
Thanks, guys.
Thanks so much for listening in as we chat about C++.
We'd love to hear what you think of the podcast.
Please let us know if we're discussing the stuff you're interested in,
or if you have a suggestion for a topic, we'd love to hear about that too.
You can email all your thoughts to feedback at cppcast.com.
We'd also appreciate if you can like CppCast on
Facebook and follow CppCast on Twitter. You can also follow me at Rob W. Irving and Jason at
Lefticus on Twitter. We'd also like to thank all our patrons who help support the show through
Patreon. If you'd like to support us on Patreon, you can do so at patreon.com slash cppcast.
And of course, you can find all that info and the show notes on the podcast website
at cppcast dot com.
Theme music
for this episode was provided by