Okay, should be back online, yeah.
Right, the room is full.
That's great as every year.
It's a third year at GCC Deafroom, fully packed, great.
Up next is Slantlot 6, who is a maintainer of GDP, in particular of the AMD GPU back end,
and developer of the downstream rock GDP support for AMD GPUs.
He will, well, I guess he won't actually talk about AMD GPUs specifically, but rather about dwarf 6.
That's dark.
Thank you very much.
Can I keep that, actually?
Let me see.
That work?
Yes, so perfect.
So yes, I said, I'm Lenslot 6.
I'm working for AMD on ROGGB, which is a developer for debugging AMD GPU programs.
And we're not going to be talking about GPUs, we're going to be talking about debugging information,
and some changes that recently made it into what should become dwarf 6 eventually.
Before we start, just a tiny bit of background, what I'm going to be talking about is,
initially being developed within AMD, and that was to, like, overcome the limitation of dwarf 5 to debug GPU code,
mainly, like, GPUs are a bit different from from CPUs of different optimization opportunities,
working differently, and so the dwarf 5 didn't really work for that.
So that's why those extensions are to be made.
It's currently implemented so in the work on blockchain, so LVM based on the compiler site,
and GDP based on the debugger site.
And what's initially published as one big omnibus documentation of what's been changed from from standard 12,
and that changed as it is.
It's not really fit the, what could be said, we did for to offer inclusion for 12 of the standard.
And so eventually what happens, some years ago, four years ago, almost, I guess,
a kind of hard work working group kind of formed to rework those extensions, including, you know,
maybe it's industry players and individuals.
So that group at kind of, you know, two task one is make sure that the extension we come with work for all the parties involved,
because we have multiple GPU vendors involved, and the other bit was to kind of,
split the changes that have been made into more manageable chunks that can be submitted for, for review to the toaster edit.
One thing to note here, as you see, I am not one of your third of the original work.
So I don't take any ownership on what I'm presenting, I'm just presenting it.
We do have some of the other here in the room and thanks.
So one of the outcome of that particular work was a proposal called here,
the location description on the 12 stack and it's been accepted last October and last October.
So it's been quite a track to get there.
And as we said, part of the work of that committee was to try to reduce the size of, you know, what we have and have something manageable.
And we ended up with something which is like 20 pages long, which is, let's say,
larger than the typical dwarf proposal.
But the point I'm going to try to make today is to show that what we did is actually a pretty simple change,
but it just happens to be quite fundamental to some aspects of our working.
And so that doesn't apply a lot of editorials, so the standard, you know, remains consistent.
So for those who don't really know dwarf that much, dwarf in the nutshell is the debugging formation produced by comparison.
So the, a consumer usually a debugger can, some of the understanding of the program.
It's pretty much a tree structure for the main plots where you know we have cooperation units, which contains types and functions,
and like no function they kind of basically block and variables and everything.
And the nodes, they have attributes.
Most of the attributes usually are statically known, you know, values.
So like when you have a function like the, the name of functions here main, that's something that,
that's, that means statically and we can express that.
But there are some properties that we do not know statically.
Let's say, let's go serious here, you know, we have a simple program.
We have, you know, strikes, which uses that, you know, a bit through the presentation.
We have an instance.
And so in the debugging formation for that, we see, you know, the name F, we have some information of, you know,
where it was declared the type, all of that is known statically.
But the location where do you go to find the bytes that make up the value?
You cannot, you know, at compile time.
So what, what we have instead is instead of the value.
We have what's going to do our expression, which when the value age is, will give you the value.
So it's something that the client needs to evaluate.
So dwarf expression, it's, it's not programmed generated by the compiler, that the consumer can,
can use to describe a dynamic value location.
Like when executed, it will yield, yields a location or value.
And it's a pretty simple model to, like, work on values.
It's a stack-based, you know, language, like mini language, where you can push in for values.
And you have, of course, you know, taking the addition, you pop to values, you add them together, you push the value back.
And that's how it works with, you know, a lot of, of course, for that.
The locations will come a bit to that later.
It, you know, it was a bit differently.
So let's explore a bit, you know, what we can do with expressions or it works.
So let's take, you know, us same structure and two variables, where we have an instance of that, that class.
And we have a pointer to a member, a, in member of that class.
So we have here the deeper information that goes with that.
And so far, nothing is, you know, pretty unusual. We have the F variable.
And the information says here, if you want to get the value, we have DWP FB range, which pretty much means,
take the base, like the base frame address, subtract 32, and that will give you the address of your variable.
The same for the pointer to member.
And we have some information in the pointer to member type.
And especially the DWAT use location, which is used to that dear reference the pointer in the way.
And if you look at our use doc, let's say we have that, that's actually in the debugger.
So we have our object, we can take it address.
So why seven F have something.
We can look at the value of the pointer to member.
And what to the debugger do to evaluate, you know, prince F dot star member.
And the way it works is we use that DWAT use location attribute that we add.
And the standard says, yeah, it's easy busy.
You initialize a stack of two values.
One of them is going to be the address of your object.
The other one is going to be the value of the member.
And then you evaluate the expression associated with DWAT use location.
Let's do that real quick. We have our address on the tag here.
So like seven F has something.
We have four, which is the value of the member.
If you do DWAT plus, we plot two values of them together.
Put the result back.
And that's the address in memory of the field that we are looking for.
Easy busy.
Now let's forget all the seconds about the pointer to member.
And look at what can happen to our object.
Let's say we have an optimizing computer.
That's decides for some reason.
That's, yeah, let's not put that value inside memory.
But let's promote it to registers.
We did that kind of a lot on the GPUs.
So again, we have some some way to expose that in DWAT with DWAT expression,
which are composites that kind of describe this kind of layout.
And how do we, you know, layout the data in different memory areas.
So we have here a way to represent that in DWAT, that expression over there.
We'll go and look at how that evaluates.
And see what we have.
So that would be the expression in DWAT location of the variable itself.
So if we have that.
So the first of code of code is DWAT.
Red 0.
So that means we put, we're pretty much saying that.
Yeah, we're going to talk about registered here.
Then we have DWAT 4, which means take four bytes out of red 0,
which is like, in our case, the other register.
And start a composite with that.
So we now reference four bytes out of red 0.
And then we continue.
Register 1.
So we talk about registered 1.
And then DWAT is 4, which means take four bytes of red 0.
And just concatenate that with the end of red 0.
So now we have a composite which describes four bytes outside of red 0.
And this is 0.
Then four bytes of red 0.
Which is the one which is 0.
8 bytes in total.
And then we have the entire value for update.
And that works great.
You know, if you want to print the value of the object,
you can do that.
No, be it.
But how do we make that work?
How do we evaluate, you know,
the pre print food of star member in that case.
If you go back to what we had,
we should create a dwarf type.
We should do values.
One of them is going to be the value of the pointer.
That's okay.
And the other one should be the address of the object.
But we didn't have an address.
So there is pretty much nothing we can do.
And that's kind of, you know, one
illustration of what didn't really work in Wi-Fi.
And that's known as being to,
as letting all that work.
There are many places in Wi-Fi where we assume that's.
Location is an address.
And we can take the address and use it as a value.
And work on the dwarf type because it's,
you know, the numerical value of the address.
And as soon as you start to optimize
stuff away and, you know,
they're promoted to registers or they live in different
different interfaces and everything.
That kind of, you know, doesn't really work.
So DWT location doesn't work.
We have DWT, push object address,
which has kind of the same problem.
And that's what waft 6 is trying to address.
So out of the 20 pages,
also, you know, of proposal.
I guess it all fits in one slide.
And that's what has been proposed and no accepted.
The answer is actually pretty simple.
It's say we have a stack,
which used to contain only values.
And so now we'll make it so you can contain values.
And locations.
A location is really like a generalization of what an address is.
It just points to the beginning or learning
of the beginning of an object somewhere in some memory.
So a location is just a double containing, you know,
a reference to some storage and enough settings.
I guess storage.
Storage can be pretty much anything which is
no way stream of bits and indexable stream of bits.
And we define a couple of kinds of storage here.
So memory register composites, which we'll, you know,
kind of come back to.
And we have implicit and undefined.
We don't really, you know, just let's hear.
We have junior upgrades to work with location.
So DWT, and DWT of DWT of DWT of sets.
And the rest is really.
A tutorial changes and, you know,
but what's compatibility with DWT5.
So everything used to having DWT5 still works in DWT6.
So with that, let's go and briefly,
I know our point to remember example we had.
And see how that works in DWT6.
So we have exactly the same program.
And DWT6 can't debug information.
So we have the same variable.
The location for our, you know,
variable is, you know, chromatic registers.
We have the pointer here.
Doesn't really matter where it is.
And we have some slight change to the,
the pointer pointer to map a type.
And DWT used location the way it's going to be defined.
And so if we do the same exist again,
we need to find out what is,
know, the location of our variable.
And so we have the exact same piece of dwarf.
We need the same expression that we need to evaluate.
So let's go and do that.
So we have DWT6,
which is really defined as,
push a location on the stack,
which, you know, references register zero.
Um, out of set zero.
So let's do that.
And now we have a location on the stack,
which is not in the dress.
Just a generic location that points to some bits somewhere.
That's kind of the, the new thing in,
in DWT6.
And then we can, you know,
carry on and execute the,
the rest.
So DWT4,
we take four bytes out of,
a reference to four bytes out of that,
that location and create a new composite.
So the text is here.
You can, you know,
since it afterwards,
you know, we'll publish the slides,
but like I'm not going to read through that just at the moment.
Um,
but that's what we do.
We take four,
four bytes of the,
um,
of that register.
Great composite for that.
Push the composite on the stack.
And we can continue.
So wedge one.
We put, push one new,
um,
new location of the stack,
which is no.
Of that zero into register one.
And DWT4,
we take four bytes out of register one.
That we can catch in a concatenate out of the four,
four bytes of register zero.
That gives us a,
a composite of eight bytes.
And we're pointing to offset zero of,
that composite.
And so at the end of the evaluation,
we have.
That location,
which is on top of the stack and we just say,
whatever,
you know,
remains at the top of the stack at the end of the evaluation,
is no way can go and look for your value.
So so far,
it works.
Um,
no issue.
And as you see,
we have the same outcome as into our five.
So we describe the exact same layout,
which we have the same.
By the code.
So we have the same expression.
It remains compatible.
The only thing that we really changed is the semantics of,
how do we evaluate and how do we,
um,
go through evaluating that.
But now that we have that,
we can,
we did,
you know,
pointed to member kind of thing.
And it kind of makes sense now.
If you want to evaluate DWT's location,
we can do what,
you know,
what was impossible before,
which is,
we initialize a stack.
We,
two elements.
One of them is going to be the location of the objects.
The other one is going to be the value of the pointer.
And so that's what we have here.
We have a stack,
which is,
which makes it together.
Locations and values.
Um,
and then we can run our program as before.
So DWT of sets.
Again,
you have the text here for the walls.
Um,
mostly for online.
But um,
what we do is we pop the value,
which is in your initial set.
We pop the.
The location,
which was behind it,
we just modified the offset of the location.
So we add,
we add the offset.
And we push that back.
And we end up now with a composite,
consisting of,
consisting of widgets as you will register one.
We add,
offset four,
four bytes inside that composite that's really registered one.
And that's where the member we're looking for is.
And so we can go and evaluate that expression now.
now. And really, that's all it is. So, location on the stack, it's when you look at it
quite simple as a change in dwarf, but it's pretty fundamental in the way we define
the evaluation of an expression. It simplifies quite greatly the, you know, the
evaluation mode that are at least in my mind. We don't have that, you know, the
location that are at the side and some other rules that we challenge. But we
need to follow. We have backwards compatibility with R5. So, we keep the same
back, we keep the same outcome. We just change the, the semantics of the
operation to get there. And from there, that does open up a lot of
options to do more stuff like multiple other space support, which we need on
GPUs. We can really find out we know we create over this, that has some
application that, you know, we can do some extensions on second winding, which
also uses expression and so on. And so that's pretty much it. So, we have
acknowledgement. So, a big thanks to all the people involved in that, you know,
dwarf or GPU work group. They've been doing most of the work. I
part of that, but like I don't really claim authorship of all of that. They did all
the work. And thanks to everyone who helped me put that together. And I think
with it, let's go slide, because I have to. And questions.
We had one question behind, and then, yeah.
So, the question is about, you know, what is, what happens is the value is
partially under stack, partially on, on registers. We can create a composite
system. It can reference multiple storage. So, you can say, I want to take four
bytes out of memory from that address, then take two bytes of that register, then take
16 bytes of memory in another, I just mentioned you wanted to. And then you can say,
then there are two bytes that have been completely optimized. I will title notes. So, we
use the define storage and so on. So, a composite doesn't have to be uniform in
anywhere shape or form. So, that also means when the multiple address space
thing will come later, like, that will also work. Yes. And the question is, that is like
an observation that when multiple address space is coming into play later. Yes, we can
have objects which are spread across multiple address spaces, and that will work
as composite just fine. That was one of the main, you know, motivation initially to
write all that work. I think you had a question. Yes.
So, none of this used to be a problem because even the law of the address of a red
server will be taken. Yes.
Then, like, no, it's not absolutely sure that we're going to have.
I will come back to that. But, yeah, Karen.
So, my mind says, you're coming from the GPU field. Is that you can use
our notes, putting a lot more data into the larger address or something like that?
Yes. So, the rare mark is, you know, that was not a problem because see,
it doesn't really allow you to take the address of the register anyway. And I argue,
most of the time, if your program itself doesn't use that pointer to
remember, you know, in the region of your program, the compiler is absolutely
allowed to promote your object into a register.
Because from your program perspective, it's just going to be the same. Your program
will never take the address of your register. It just, once you're in your
debugger, we might want to do that. And, like, in my example here, it's a bit
contrived because I still have, you know, the pointer to remember, which is still
on the stack and everything. And that's, like, not likely what's what
happened is, you know, your register says, I know the pointer to remember,
I know the value for that section. And so, I can promote stuff and make it
so everything, you know, looks like it, but it's never really, like the program
is never really going to do that reference that I wrote. It's just something
that you do in your debug session. So, we could have that in optimized, you know,
in optimized cases on regularcy. And the next part of the question is, do we have
that because on register, you know, on GPUs, we have larger register banks. And, yes,
definitely. On GPUs, we have, like, really, hundreds of registers. And the
extra memory is quite expensive. So, one of the, the first optimization
in your compiler would do is just no take as much of the data that you can
out of the memory and put that into registers as long as it fits. And we
can have those that. And I don't know for all the vendors, like, for
the, we do even have some way to, like, indexing. We have some instruction
where we can access, you know, where we can index into registers
in a way. So, like, the move relative kind of instruction would say, like,
take some data out, you know, you say register five. But then the,
the semantics of the operation is just, it's registered five,
plus the value you have in some of the register. So, we can do that on
a few years. And these are five options.
These are five options.
Although there are register offsets. You register number offsets.
So, also, you register indices.
Yes, that we do register indices. Yeah.
Yeah.
I don't think I have to add one more case.
I don't know, there are many other cases that are going to be
easily disabled.
But I used to work on a specific architecture, but at a few,
a few, to register back.
They actually used it in stack.
And then we use a lot of the indices that are going to
register.
And then they do basically the stack.
These are the, in the basic space.
Yeah.
Just, so, like, just repeat that for the mic in every,
every and every outside of the room.
So, the remark was, um, someone who's been working on the
architecture, where using quite large register bank, where the
stack is actually actually implemented in register.
And you just offset inside your register bank.
Um, which is kind of what we would be describing here.
Yes.
I suppose that won't work, but what about the function
where those, like, the complete function,
so the function is, yeah, I'm just,
wrapping up that question and then I'm, I'm familiar.
Uh, I guess you can start and, like, you know,
I think.
Um, the case of function pointer, where, you know,
when you optimize that with the function, or you just,
know, just so much, we have the, the same problem that, like,
where, we can actually take the, you know,
like, where we can actually take the address of the function.
It's not going to be the address of the same function.
Like, 12, as way to, to tell you, I know that object exists,
but I know I cannot give you the address.
And, you know, there are cases where that, that's actually true.
And what we're trying to make sure is reduce,
know the set of cases where, where, where,
roughly, know, it just comes to an end and say,
no, I, I cannot answer that question.
To only the case is where, yes, that question is just
genuinely not answerable.
And I guess that's it.
Yeah.
That's it.
Thank you very much.