This morning, I mentioned WebAssembly and I said, I actually don't know anything about WebAssembly.
So I invited a wide-o'er to actually fix this problem and take me to WebAssembly and
come back, run for plus.
Thank you. All right. So let's get started then. So who the hell am I? I mean, I'm working for a bunch of different companies, but the last few are mostly relevant to this.
That rigged when I worked on was zero full-time and then I joined another company that's called ILEEP. So
where I'm working still on was zero and another in WebAssembly or in times the call that's called Chickery. So hopefully
knowledgeable enough to tell you about this. So what is was zero was zero is a go-er on time for WebAssembly.
It comes with an interpreter that entirely it's entirely written in Go. Well, both the compiler and the interpreter,
written in Go. The thing about the the thing about the interpreter is that it runs everywhere where go runs. So
it can be just ported. You bring it in as a library, it will just work. But we also implement an ahead of time,
load time, optimizing compiler that targets MD64 and M64 so you can get great performance and without
hopefully not too many compromises. Oh, I also delivered in this talk if you want to learn more about WebAssembly
tomorrow, there's a WebAssembly dev room and I give you this talk where there's a bit of overlap with this one.
So if you really really like this talk, you can come and see it again well for a part or if you
really hated it, you can bring tomatoes and. And I'm going to talk a little bit about Chickery as well,
which is a WebAssembly runtime for Java, which means that since we mentioned Android earlier,
it also runs on Android. So that's interesting. And we're going to talk, I'm going to talk about that
too tomorrow. All right. So how many people here familiar with WebAssembly? Nice. How many people
have used WebAssembly or are using WebAssembly? Cool. Okay, I can go super fast in this first part then.
So WebAssembly is essentially a VM the runs in the browser together with the VM. Actually,
it's part of the VM that runs JavaScript. And it was born out of need for better improve performance
in the browser. So yeah, JavaScript has great performance in the browser because there's a just
intent compiler there that produces native code on the fly. But if you want to run and write code
written any other language than JavaScript, you have to compile it to JavaScript. And that's not
optimal. I mean, it kind of it works and we've demonstrated that it actually produces good code.
But why not coming up with a compilation target that was designed to be targeted by compilers?
All right. And that's what WebAssembly is. So it's a safe, portable, low-level,
good format, design for efficient execution, comparable representation. But I think it's not designed.
We'd only the browser in mind. So it can run outside the browser. It's just designed to be a good
VM. All right. So yeah, it does run in the browser, but it does not make any web specific assumption,
does not provide web specific features. So we can be employed in other environments as well.
And that's where this talk with focus, where this focus with focus on. So inside the browser,
whether you realize it or not, you may be using already WebAssembly. If you're using games,
it's very, very possible that they're being compiled, at least in part, to WebAssembly for performance.
If you use Google Earth, part of it, it's compiled in WebAssembly. If you have a used
Figma for graphics, that uses WebAssembly. So all right. So there's Google sheets. There are a lot
of pieces of WebAssembly on the web in production today. All right. But again, I want to focus on
use cases outside the browser. So those use cases where people are comparing it to container
technology. But where I'm mostly interested in is what I'm mostly interested in in software
extensions. All right. So when it comes to extending software, especially when it's tricky to
reveal, if they're large code bases, like for instance, C++ code bases, where you want to plug
user defined behavior, usually you have to take, you're taking a choice between two languages
today, right. In the past there were other languages, but mostly today it's about Lua and JavaScript.
And of course they work and it's great and they're usually pretty efficient too because there
are language VMs that work pretty well for these two languages. But you're making a choice for
your end users. They will be forced to use either Lua or JavaScript. They will have to learn
those languages and that's a constraint. What about WebAssembly? WebAssembly? It's a minimal VM. It's
fast to boot and it's easy to embed. So as I mentioned earlier, most JavaScript VMs nowadays
ship a WebAssembly runtime. So you could embed just JavaScript and be done with it and some people
do that. But the wisdom is a much smaller specification as compared to JavaScript. Well,
JavaScript essentially now incorporates embeds, includes the the Waza and the specifications
are by definition, it's larger. So Waza libraries are smaller in general than JavaScript runtime.
There are companies that are using already WebAssembly for the purpose of letting user write
their own code. For instance, Red Panda is an alternative Kafka implementation that's shipping
data transformation that use WebAssembly so that users can write them using their favorite language,
including obviously go. Avoid it's a network proxy. You can write filters using a number of
a number of strategies. There's a pretty fine filters. There's native filters. You can write them
using your native language or new one, of course, and make sure that's or WebAssembly. So
or OPA, if you're familiar with open policy agent, they compile policies in WebAssembly,
so you can then run them everywhere and compile the user. What about Waza? Waza is already used
in a bunch of different projects. This is a slide with just a few of the projects that are
ready using Waza in production. So there's a stopper on the country repo, but there are
Mazen, which is a network proxy similar to employee. There's Kubernetes. They just ship
customs scheduling using Waza. Aquatriev is a security scanner, also a shoutout to mechanoid,
which is a framework to run WebAssembly on tiny devices, and it's a zero compile using tiny
gun. That's cool. All right. So Waza lets your users write software extensions using their
favorite language and run it in a sandbox environment. So a shoutout to my company too,
that's an old slide. The number of languages is growing both in the embed part and the first
point and the third point. All right. So now it's like 17 languages and counting nine languages
and counting for Waza and Targeting languages, but regardless, X-Tism is an open source project
that obstructs a way some of the needy greedy about WebAssembly and using WebAssembly in your
applications. And it provides you with a unified interface regardless of the language your
application is written in. So we provide a framework so you can plug a WebAssembly run time there
and the other side. We provide a framework so you can compile your plugins using your favorite
language and use it as a plugin for your host application. So and that's why we have several
different WebAssembly run times we contribute to because you use all of them. We use V8. We target
browsers so there's also spider monkey and WebKit there. And then there's Chikari, the cup
cup there. Well, Chikari cup there and was zero among the others. It provides this unified interface
which simplifies a lot. But we're not going to go into that and just wanted to give another
quick quick. Yeah shoutout to ncp.run which is a project that we just launched allows you to
write your own agent code for using WebAssembly and it uses all of these open source projects
I just mentioned. And so ncp.run if you want to play with it it uses the modal contest protocol
if you're familiar with it it's something that's been people have been blabbering about for the last
few weeks and yeah it's a fun project. All right. Now let's talk wasm. How does wasm look like?
So let's say you have this function you're breathing using say tiny go. This will more or less it's
not this is not really true but for the sake of this conversation it's all right. This will
compile pretty much something like this. So it's a stack-based VM so we have upcodes that push
values onto the stack and pop codes that pop values of the stack. So local get look put value
values on the stack to values to sub traction is a binary operation so it pops to values
then it pushes the results onto the stack and that's how it works. That's pretty much how all
operations work and that's how WebAssembly looks. WebAssembly runs in the browser and outside the browser
so on the top you can see how you use it from JavaScript. We exported this subtraction function
that we saw earlier this one it's exported and so we can call this sub function we can call it from
from JavaScript at the top and at the bottom you can see you can see go using was zero and as you can
see the number of lines it's comparable and so they produce obviously the same result because
wasn't bytecode looks a little bit like jvm bytecode which I mentioned because I'm familiar with the
jvm and to the point that really you can just scan through these lines of code and have a very
strict correspondence but it differs in significant way when it comes to control flow. On the jvm
control flow is instructor you have conditional and unconditional jumps and you can jump in this
case forwards but also backwards and then makes it harder to validate and make sure that your code
is not to you know weird stuff but WebAssembly does not allow you to do unstructured jumps it comes
with structure control flow blocks loops if then else is and yeah and that allows the
validator to do more strict controls of their what's happening in your code and our interesting
thing about WebAssembly it does not provide a standard library in general and so the way you provide
essentially WebAssembly at this point is a glorified calculator it only does computation it's pure
compute as they say so the only way to do side effects interact with the word it's to provide
functions that you can import right we so exports you can export functions so you can call them
and then you can import function that are defined potentially externally and this functions that you could
define using your favor language in our case go or JavaScript in the case of the browser can provide
capabilities that allow you to interact with external word and so not just produce heat but also
right on this makes something as flow data. Yeah there's a set of let's say standard dyes for
some of the finish standard dyes a set of agreed upon interfaces called yz that it's
post like and gives you primitives to bright files access network and stuff like that and that's
what you should think about when you read the word yz more or less and that's how they work essentially
they provide these for instance fd read primitive and this fd read is writing to a file descriptor
that's virtualized so it's not a real file descriptor generally but it's you know it's an indexing
to something all right okay so as I mentioned modules are all about functions and they can import
an export functions that's how they work they can also export memory tables goables but we won't
go into that today yeah then for instance you could have a function called use add the find here
that it's exported here that it's calling a function add that it's imported and as you can see
the imported function does not have a body because it's provided externally to define it somewhere
else and that's how it gets translated into web assembly at the bottom so how do you wired things
together how do you plug these function signatures with an implementation these are what are usually
called host functions because they are provided by the host that is the environment that embeds
your web assembly runtime because of the bra in the case of the browser the host is usually
the browser which means a JavaScript environment so here we're defining the add function
remember the add function at only a signature and not a body so we'll find the add function there
this way we can call the use add function remember the use add function is calling add
and it won't break because there's an actual implementation for add this is in JavaScript at the
bottom you can see how we do it with was zero and regardless of the syntax regardless of the
methods that we're calling it's pretty much the same thing with a find and add function here
and with a find and add function here here we give it a date add and here with a find it in line
using an animal's function and we call the exported function it's pretty much the same it's just
a slightly different API but more or less so there are multiple languages that you can use to write
web assembly you can depending on the provided that there's a compiler that is able
to compile to web assembly or the daring interpreter has been compiled to web assembly
for instance python runs on web assembly by a means of having to interpret her well the runtime
compiled to web assembly so it's polygonal all right what about go obviously go
works with web assembly a composite web assembly tiny good these are old slides with old versions
of the compilers but tiny go was the first to provide support for wasom as far as I know
or the very least it was the first providing very tiny output and the first to provide
important export that worked seamlessly and still is the best tool chain to this day if you want to
use web assembly if you want to compile to web assembly go but big go is catching up
so things go one twenty one you have support for these wazis set of libraries and you can
already you can already use it but you can not you can not import you can not export
functions in this case but the next version of go one twenty four will will close the gap so
you will be able to use it as a grouping replacement for tiny go but tiny go will still provide
best performance when it comes to size of executables so was there is so portable that you can
compile was zero using the go compiler and also the tiny go compiler and create a was zero binary
that can run in was zero so you can so you can was and well you wasn't and this I don't know
if you can see it from from there but this is a screenshot of doom in a ski art running nested
was zero inside another was zero indeterminate just because we can so how does it work
whoa 12 minutes you got to go fast here all right there's many run times there's many run times
and but they you'll have one thing in common most of these run times all of these run times compiled
to native code and there are native libraries so that means that you have to use to go if you want to
link them against a go executable send that's where was the arrow comes to the rescue that was
zero was started in 2020 by Takeshi at Tetrate at the time all was run times were native libraries
and required you to use to go now I argue I would argue that the defining features some of the
defining features of the go run time and the go ecosystem are static linking so you have self-contained
executables cross compilation with no paying and go routines for convenient concurrency right but
unfortunately if you compile statically you cannot really load dynamic executable dynamic binaries
you cannot dynamically load binaries I mean you can do it but it's kind of painful cross compilation
becomes a pain because if you link against native libraries you have to rely on a sequimpiler
that it's on that host platform or you have to set up the sea sprue just I mean you can do it but
still and go routines are an abstraction over as threads so they must be aware whatever native code you
bringing must be aware that there's a different run time there so in general foreign function interfaces are
a pain where the language is maintained this is true for patent this is true for Java and it is just a
true for go um more over if you're linking against native executables there's no boundaries between
the goren time and what these native libraries do so this is how this memory looks like they're all
they're all mixed up and but in web assembly there's isolation memory's isolated and virtualized
so they cannot cross their boundaries they cannot overwrite and corrupt the memory of the host
all right so was zero points to go entirely and the main goal of this talk in the less than
minutes or it's to show you how that works let's see if I can manage mostly I can manage the problem
is that you will follow that's that's the issue here all right so how does was your awards in
general you it works at runtime you load a web assembly run binary and tell it please go please
was zero load is web assembly binary and compile it now regardless if you're running an interpreter
mode on in compile mode was zero we'll compile it to an internal representation and then at runtime
you will instantiate to discompile representation in order to execute it obviously we
compile it beforehand so we don't have to do it at runtime over and over again so how does and this is
where from wasm to azum the title comes in function compilation how does that work all right well
obviously first we need to decode the module to validate it and that just standard across all of the
web assembly run times and then we compile the module into this executable representation how so how does
the interpreter work in that regard we do some sort of translation some translation into the
internal representation it's another form of bytecode essentially the wasm the wasm bytecode
can be transformed into a more compact representation for efficiency where a lot of of codes
repeats in web assembly there's multiple of codes depending on the type of the operands integers
integer with 64 bits floating points floating point 64 bits and instead we just use one
upcode that that's which is over the type and so we essentially compact the representation internally
but then after we do this a very lightweight translation we just switched over the upcodes
going inside a loop and loop over those pretty much and that's how it works so that's interpreter
what about the compiler so originally we had a very straightforward compiler from web assembly
to native code but then we realized the optimizing compiler most of the word tacheshia actually did
and the new compiler architecture it's a proper multi-stage architecture where we have several passes
and it's inspired by states of the ar compilers and just in tech compilers such as v8 so all
the end goes on compiler so I'm going to get through this well we have enough time very fast
but this is not a compiler class so very limited for that all right so let's say we have this
web assembly binary on the on the left web assembly function it does a sum and there's a subtraction
with a result right some into values then the result is subtracted from the third value that you see at the
right this is translated into an internal presentation and essentially it removes the stack
representation and instead use a value-based representation this is a normal a traditional
I don't know who to call it a common representation and it's a basic blocks to control
of the way to represent to represent control flow it essentially transforms a listing of code into
a graph inside every note of the graph there's instructions and
but these instructions do not include jumps instead of jumps we use arrows we use edges the
jumps and instead represent a branch itself represented by edges in the graph and essentially
by doing this transformation and another transformation that's called that gives the name SSAC single
static assignment it renames all the variables so essentially you'd only occurs once on the left and
side on the left and side these are all transformation that simplifies further analysis later
and after doing this first form we are free to do some further analysis and simplifications so
for each of us we can do optimizations suppose you have this listing C code and you have a
debug constant and at build time you can set it to true or to false if it's false it will
evaluate to zero at build time which means that your code here will look like this if zero now if there's
an if zero your compiler knows statically at build time that that never actually gets evaluated
and that means that an entire block of code can be eliminated that's called that code elimination
now in this particular listing we also know that we have a value a we'll value b and we have a
value c and here at this line of code a is exactly equal to five b is exactly equal to six
and they want they don't change between all of those statements so that means that we can
substitute those values in the expression and compute that expression at build time which means
this is called constant propagation and constant folding and means that instead of all the
listing that we had before we just return 15 as a constant fall this is called these are
the optimization passes that we saw earlier all right once you have done all of these optimization
passes and there are more more complicated they don't just delete code they can also add code
is if it's more efficient we translate that into native code or at least we start to select
instruction this is instruction selection process in this procedure we replace the internal
presentation that we have whoops that we have on the left-hand side we actual that we do
from instruction this is our our instructions and as you can see here there's there's worth
there's values with a question mark these are virtual registers now on a machine you have a
finite number of registers you don't have infinite register but before we actually generate
the finalized version of the code we fake it and just say okay I don't know many register I have
infinite and only in a later stage we allocate and fit the infinite number of register over the
register we actually have so on armor we have 30 30 register 30 general purpose register for integers
and 32 floating points slash senior register which does might not seem like a lot but it's actually
plenty as compared to md64 so in the register allocation phase we replace all of the
uses of the virtual register we use the use of proper register doing spilling and loading them
back as required and then and then finally we generate the code how does it work we generate
the binary as bytes lies and we actually encode the single instruction as bytes lies and then
we prepare to jump into the bytes lies and let the CPU execute what instruction by instructions
all of that is done without using CGO by using some fancy trickery using assembly code and that's
super cool because the go compiler allows you to do that finally if you words about function evocation
well essentially we use this azim code to set up the register in such a way that we don't
break the go runtime and then once the register has stashed away we jump into the execution
this is called a trampoline we jump into the code that we generated this is called a trampoline
and what about user code what about host function what about errors we return an error code
and then this code is transformed into this uh error code is a return to the
go runtime so they can safely handle can be safely handled by user code so we replace the
register we make them safely restore the registers and then let give control back to the runtime
what about host functions they work exactly the same as an error except we return an ID to represent
the function that we want to invoke and then once the function has been invoked in go space we return
control to the executable code in wasn't space by again doing all the fancy dance we the registers
and that's pretty much how it works nothing fancy and that's all I had so my name is Eduardo Vaki
you can find me on Twitter or on Masadon of Luskai and get up whatever we did nickname and
try and see if you're on try and stay zoom for it was zero thank you