Last time we looked at the problem - or the great advantage, if you like - of using
intermediate codes as a staging point in our compilers. And we spent a lot of
effort talking about how to improve your compiler, but still on the same machine,
but with an intermediate code step. I would strongly advise you to watch that
video first because what we're now going to find, in this next one we're doing, is
we're just changing the rules slightly. instead of B* (improved binary)
we're moving from B' to B''. We are generating a different
binary for a completely different architecture. But we are using the front
ends that we've already established that get us as far as intermediate code. What
we're now talking about is: "How can I produce a new back end?" And you will find
that the diagrams I draw are remarkably similar to the ones on a single machine,
for improving yourself. So do watch that one first unless you're absolutely sure
that you've taken every single thing it said(!) And then we can go on the adventure
of just how intermediate codes are pretty well vital for porting compilers.
In the last video we used this as our home base - if you like - our master
referral point for everything that we were trying to do. We had source codes
for two vital pieces and then compiled versions of them over here.
What we're going to find with this one is, yes, we'll still be having an H compiler
written in H producing intermediate code the thing that's going to change this
time is instead of saying things like: "Take my intermediate code and write a
compiler for it that produces B' I'll be saying: Take that, write it in
high-level language of your choice but make the binary it produces be for a
brand new machine - that we can't wait to get some software working on. So, that B'
will start becoming B''. And whereas, in the previous episode, we
were obsessed with getting really high quality B', and [then] we got to B'*
even better! We're not going to be as obsessed this time with improving the
quality of any binary - although that's possible in the end - what we are
concerned with is this time saying: "I don't want better binary for the old
machine I want some brand new binary B'', for the new machine, and I
don't care how rubbish it is initially, I just want to get some binary for the new
machine working and established. What we can say about any cross compiler is it's
going to look like this. It's going to be a chosen higher-level language; it is going
to originally be written in B' but it's going to produce B''.
And if you want an actual example - referring back to previous
episodes - for this: we were in a situation where our H was 'C'. The original
binary it was running on - on a PDP 11 - and our B'', over here, was Z80 binaary.
So, there you are then, that's a generic form of any cross-compiler.
There's my actual example of my first encounter with cross-compilation.
The bigger question is how does this help you? If you start saying: "Well, instead of
just sending boatloads of code across to that new machine - and having no real control over it -
what happens if I want to, sort of, you know ... yeah, I did think of it in
those terms, send an Expeditionary Force onto the foreign machine, set up a few
outposts, do a little bit more on the foreign land
than just unload the binary that's been sent on a boat. Actually set up a 'binary
factory' on your newly conquered land, you see? Well, yes, and actually, I mean,
intermediate code helps that. This is the one new piece of software you need to
write, since everything is working to intermediate code now. It's a
non-optional intermediate stage. Your front end of your compiler
will quite cheerfully be producing 'I'. All you need to do, to get something started
on your new machine, is to write something in a high-level language which
takes intermediate code but produces B'' at the other side.
So, that's your intervention point. Let me say again it is a worthwhile step to do.
Because the difference in detail between a PDP-11 binary and a Z80 binary is
pretty considerable. And if you don't use intermediate codes you have to go right
back to square one and think: "Oh! I've got to create a binary from scratch, all over again".
But I've got to get in there - in that code generator trying to throw the old
one away" You say is there anything I can learn from this? And just out of nowhere
bridge the gap between your high-level language and your new B''
which you've got. What we're saying is, it's a lot simpler if you break it into two
parts. Leave the front end as it is, on the other machine, for the moment and
it's producing I at you, like mad. What you need is a new back end. So, we're in a
situation, now, that as I've made clear ... any cross-compiler is going to be
running on your old machine, to start with, producing binary code for some
other new machine. So what we've now got to do is revisit all we did before.
We said: "Here's how the transformation chain works with intermediate codes." If all you
want to do is to improve the quality of the coding for your home machine B'.
This is "similar but different" now. We're going to say: "Instead of producing
better quality binary for this home machine we want to produce brand-new
binary, but for a foreign and different machine." And we want to know exactly what
processes we're going to have to go through. Similar to before, but a little
bit different in detail. So, hold tight this is what we have to do. We've got
this new piece of software something that takes an intermediate code we know
the spec of that. We can write it in H, our chosen high-level language. It's always a
good thing. But we know that, eventually, that thing
has got to be compiled out, down to binary, but it's going to produce B''.
We take our new piece of software and we compile it. Now notice
that is my original 'H-to-Intermediate compiler' that runs on the old machine,
but I'm using it to compile this new thing. We know what we're going to get
and I write it out up here. H gets translated into I, via this. So, we are
going to end up with I, B''- but the H has gone through to I. So, we've now
got a piece of software that takes in I, is running on I, but produces B''.
Don't forget that there is - as the fourth component in our chain, always
available to us - an intermediate code compiler that takes it down to binary.
Now we want, back from the beginnings of time, the original component number 4,
which is that. So, I've written an I interpreter, in I, that produces B''
But, from work on the old machine, I've got something that takes I runs on B'
[and] produces B'. net result of all of that what do we end up with?
We end up with I, B'', B'. So, that is the result, if you like, of
compiling the intermediate code right down to binary. We've now got a really
- well it's close to being wonderful but not quite (!) - it's an Intermediate Code
Compiler. It produces super duper new B'' for the new machine.
[The] slight drawback is it runs on the binary for the old machine [B']. So we're almost there.
We've established our bridgehead but we've not quite invaded yet because what we'd love
to do is to get this thing down here saying B''. It's just a
question again of feeding the right thing back into itself. We've got I,
written in I, producing B''. We've compiled that with an I, B', B'
and we've ended up with this: Intermediate Code producing B''
written in B'. What do you do now that you've got this executable answer?
Final stage: feed it back to itself. Take the original thing and do it one more time
If you take this look: I, I, B'' and feed it into your newly created
binary I running on B prime .... hurrah! let trumpets sound! You have finally
achieved what you wanted to achieve - which is that goes through there and you
end up with I, B'', B''. In my original set-out of
pieces of componentry you need, I had 1, 2, 3 and 4. And, looking back at
it now, what we have got is that this is the equivalent of [piece] 4. Look, back in the
early days when we only had one machine, you had needed an I compiler, to take I
into B' ... B' there. What we've now done with all this
jiggery-pokery with T diagrams is: we have produced ourselves an I, B'', B''
It is totally on the new machine now. It doesn't rely on B' at all.
Now. those of you who have soldiered - how should we say? - really
soldiered hard on the previous episodes of this, will now recognize that this is
the same old story. You want to have the binary you are running on being of the
same quality as the best binary you can produce. Except now it's not so much
'best binary', it's 'new binary'. Rather than generating new binary for the new
machine off the old hardware can you "feed yourself back to yourself" enough so
that eventually you end up with something that is totally happy on the
new machine and doesn't need old machine support in terms of B' at all, any more.
So, that's been a bit of a marathon folks. We've mounted our
galleys, we've invaded the distant shore. We finally had enough cold compresses on
our head that we think we're clear what we've done. But if you just go over this
carefully, for yourself, and draw yourself lots of T-diagrams - or better still make
yourself some T-diagram shapes - and just experiment with them until you're quite
happy with with what you're doing. And I hope it's been convincing that although
it is messy at times it is so much easier when you move to a new machine to
say: "All I need to write, to get myself going, is an intermediate code compiler".
I don't need to redo the whole chain. That can be back-filled as we go along. But I
could take the existing front-end and port it into being a new front-end
simply if I've got the back-end tools that can produce B'' for me.
It's as simple as that really!