 Okay, thank you for coming. I'm going to speak about the Apollo Guidance Computer and my attempt to write an LLVM backend for it. So I'm going to introduce myself a little bit. I'm studying at the University of Bath and I joined Demacosm last year as a UK ESF Scholar. I've been working primarily on LLVM backends and I started working on this AGC backend in October as a personal project. So I'm going to give a little bit of a background as to what the AGC actually is. Can you speak louder please? I'll try. Yes, there's no speakers. So the Apollo Guidance Computer was designed by the MIT Instrumentation Laboratory for use by NASA in their Apollo program. They used it as a general purpose controller in both the command module in Lunaland for all the Apollo missions. There were actually three versions of the AGC developed. Two for NASA and for actual use. One by an ex-designer of the original AGC to show what could be done to overcome the limitations of the second Block II model. So firstly, the most important thing to know about the AGC's registers is there was no meaningful concept of register. All data was mapped to memory locations and in the assembly registers were just defined as aliases to memory locations. Data and code shared the same memory so there's a von Neumann architecture. However, the memory was split into a raiseable and fixed memory and code was generally in fixed. So the AGC has 15 bits available to use for each 16-bit word. These are used to represent either a one's complement number or the encoding of an instruction. The spare bit was used as an odd parity bit which allowed the hardware to detect basic bit errors. Adjacent words and memory could be used as double words. The signed bit must match so this gave an effective 29-bit one's complement number. Some instructions interpreted the data as fixed point and the most significant non-signed bit represented a half. This gives a range of plus one to minus one. So the instruction set architecture of the AGC is quite a different architecture to what we're used to due to the fact that there was a lack of computing resources and also the assembly was designed for programs and not compilers. So firstly, instructions were accumulator-based. Secondly, many instructions have a very complex operation to squeeze in some sort of higher level constructs into such a small ISA. So for example, count compare and skip has an operation where the first thing it does is load the diminished absolute value of the given memory location into the accumulator which means basically increment or decrement towards zero. Then rewrite the original value back to the memory location because some memory locations modify their contents on writeback. And finally adjust the program counter according to the contents of the accumulator. So if it's greater than plus zero, then you add one. If it's equal to plus zero, you add two. If it's less than minus zero, you add three. If it's equal to minus zero, you add four. Extend and index are basically used to modify the next instruction in some way. Also, the assembly syntax was quite different to what we use now. It seems simple, but it's not GNU-like, so it's difficult for parsers. One thing to note is every single thing on this slide is a valid identifier. So why would I choose to write an LLVM back in for this? So firstly, the original engineers that wrote the programming code for the AGC were amazingly talented. They had no help from high-level languages and they worked with a huge code base, as you can see, and still managed to produce safety-critical programs. However, in order to make the AGC more accessible and more understandable for people like me, it would be good to be able to program it in C. Secondly, I wanted to see how LLVM coped with generating code for this back-end. It's notably different from modern architectures. An LLVM is a powerful infrastructure, so I wondered how much could still be utilized for such a strange back-end. Lastly, I wanted to see how well I would cope because it was a totally new experience for me implementing a back-end from scratch, though I had been working on other LLVM back-ends. So I'm going to jump right into the implementation now. So firstly, some register definitions. I defined R0 to R7 explicitly because these are internal flip-flops which have special meanings. Double-word registers can also be defined here, like RD0. They're simply pairs of registers, and each double-word register will overlap with the ones before and after it. So special single-register classes are used to specify DAG operands for instructions that use the accumulators or other special-purpose registers. Notice there's a lot of effort in documentation here since this was early in the project. It's all downhill from here. So generating definitions. So I tried several approaches to modeling memory of the AGC, but in the end, a simple brute-force approach seemed to work the best. So I've generated register definitions for every single memory location and double-word memory location. And such of these registers are classified as needed by the different instruction formats. The instruction definitions don't actually specify these register classes, but instead use immediates that actually represent the register's memory location. The reason this is is because in TableGen it's hard to define I want this integer to be represented as an octal number, and AGC requires octal numbers. Instruction definitions are relatively simple to specify according to the specifications, but it's worth noting the addition of extra codes. So the AGC designers figured out a way to double the effective encoding space of their architecture by interpreting instructions completely differently than fixed with an extended instruction. I used the is extra code bit to mark instructions which are extra codes. And another decoder namespace was necessary due to the shared encoding. Another issue with decoding these instructions in general was that accumulator operands were specified as DAG operands but had no encoding related to them. So a fix for this is this decode null ops patch. So it turns out that fixed lend decoder emitter does not add fields that don't have bits related to them or to their tied operands. And this causes problems later on as other parts of the MC layer assume that all DAG operands are present. So a fix was to add a flag for instructions where this is the case and for fixed lend decoder emitter to add a default zero width field to the op info for instructions with this field, with this annotation. So another area that needed changes in generic code was passing of some of AGC's directives which tripped up the ASIM parser. So the AGC's $file directive operates the same way as a .file directive in that it's actually includes a given file. So here I'm just passing the rest of the statement into a file name and then using the same method to include the file as .file does. Similarly an equals operator works exactly the same as an equals character. So I've just duplicated the code with an extra case. So some directives didn't trip up the ASIM parser so I implemented them instead by custom emitting pseudo-instructions. So here the bank and set lock directives are handled by switching elf sections. So bank is used to switch which memory bank the assembly is currently outputting to and set lock is used to switch assembly output to an explicit address. Some other directives were erase an oct which are used to emit bits into the current output location. So only needed to be implemented in MC code emitter. Just a short note on parity so instructions in AGC needed a parity bit to be emitted in the least significant bit. So I accumulate the parity bit here by X oring the current parity of the next incoming bit. So this is used when emitting bits in MC code emitter. So dealing with extra code instructions throughout the MC layer was a tricky task. So in the ASIM parser parsing of an extended instruction causes a flag to be set in this check early target match predicate and the instruction will not be emitted as an MC instruction. So the next time around when parse instruction is called this flag is cleared and passing extra code is set indicating that the current instruction should be an extra code. So checking this is checking the is extra code bit after parsing the next instruction indicates whether the extend plus extra code sequence is followed correctly. And I error on both an extend followed by a non-extra code and an extra code that's not preceded by an extend. To emit an extra code instruction the solution was simply to emit the raw bytes of extend followed by the bytes of the instruction. Decoding an extra code instruction required testing if the decoded instruction was an extend. Then if so taking the next instruction and decoding it using a separate extra code decoding table. Also notice masking the parity bit before decoding instructions. So finally moving on to some lowering. So some simple patterns were enough for some instructions to allow lowering to them because they had a simple tag representation. Other instructions required some more convincing. The multiply and divide instructions don't fit well with the corresponding diagonals. So specifically multiply takes two single words and produces a double word output. So it only matches an i32 multiply with sine extended inputs. Divide which isn't the implementation isn't on the screen takes one double word numerator, a single word denominator and produces two single words a result and a remainder. So it matches either a divide or a remainder with a sine extended denominator and the output is a sine extend. Materializing constants was one of the most difficult parts of the back end because there's no instructions that take immediate operands. So there is a sequence of directives that can be omitted to output a constant in order to expand that sequence. I first had to select all instances of constants into a pseudo instruction. So to expand the pseudo instruction into a sequence, I had to create a pre-emit pass to expand pseudo instructions but since I wanted this sequence to not be interrupted this has to be done with add pre-emit pass too which is the latest pre-emit pass. So when expanding the constant I take the immediate operand first ensure that it's converted to a 15-bit one's complement value. Next I create a new sequence of directives first to switch the output location of the binary to the destination register then to output raw bits at that location and then to switch back to the original bank for the function. Notice that there is a bug with this implementation which is that this implies that register allocation for the given register can only be unique for the entire program and I haven't implemented a fix for this. So if you use a constant in a register more than once then you'll just end up with the latest constant that was lowered. So there's quite a bit of future work I've basically done most of the MC layer and a little bit of lowering but several things in the clang front end and IR transformations produce IR produce IR that won't be lowered correctly in the AGC back end. So first bytes cannot be assumed to be 8 bits there are some mechanical changes to be made but a particular problem is that generic pointers will default to I8 star. Secondly some transformations for example evaluation of constant expressions assume that two's complement numbers are used so producing valid code for the AGC this is quite a likely assumption that's going to be common throughout the code base so it might take a while to hunt down all the occurrences. But for other restrictions it might be worth defining a subset of C to compile for AGC. Another thing I'd like to do is make sure that the assembly output of LLVM resembles actual AGC code so to do this I need to remove all GNU Alpha directives and replace them with AGC directives. So there's already a project that has an assembler and I'd like the output files to be assembled with that. So the AGC back end can create valid object files but a lot of the operation relies on linker functionality and I haven't implemented a linker yet so that needs to be done. AGC doesn't have stack pointer so I need to emulate a stack to use to pass arguments. Index instruction basically is what's used for indexing in that it adds a constant value to the memory location that you give it so it can be used to index a stack. I want to try to control flow statements to that the CCS instruction which could be quite difficult. So some things I found in LLVM so this might be obvious but ASIMPASTER is not flexible to GNU-like assembly. Fix-Lend decoder emitter requires this decode in LLOP's patch to function for MC instructions with hidden operands. The Lexa converts itself without the target knowing so the problem with the AGC back-end is all operands must be in octal form so when the Lexa converts something preceded with a zero to an octal integer I don't know whether I still need to convert the immediate back to an octal or not so I have to get the raw string that was converted first and do it myself at the end of my slides. Thank you. Any questions? Yes. So in the end were you able to compile the CC code into the AGC code? So I can compile basic functions with ALU operations. I can there are a few IR tests as well but I still need to implement I think function calling is the next important thing to implement before I can actually get the program running. Anything else? You mentioned the linker So the question was whether there is defined object file format and the answer is no, I'm just using ELF and using that as a stand-in. Back at the time was there a linker or was it fallen paper cards in the ATC section? So I guess the question was was there a linker at the time and the idea was that the way that you'd get a program to stick together was just by textually including all the other files so a linker wasn't really needed. I guess the equivalent of a linker was just using one function per bank? Yeah so the assembler did the entire job because it controlled the output location and switched banks or did that as necessary? Anything else?