We are almost done with emitting the IR code for a function! We only need to put the pieces together to emit a function, including its body:

  1. Given a procedure declaration from tinylang, first, we will create the function type and the function:

void CGProcedure::run(ProcedureDeclaration *Proc) {
this->Proc = Proc;
Fty = createFunctionType(Proc);
Fn = createFunction(Proc, Fty);

  1. Next, we will create the first basic block of the function and make it the current one: llvm::BasicBlock *BB = llvm::BasicBlock::Create(
    CGM.getLLVMCtx(), “entry”, Fn);
    setCurr(BB);
  2. Then, we must step through all formal parameters. To handle VAR parameters correctly, we need to initialize the FormalParams member (used in readVariable()). In contrast to local variables, formal parameters have a value in the first basic block, so we must make these values known: for (auto [Idx, Arg] : llvm::enumerate(Fn->args())) {
    FormalParameterDeclaration *FP =
    Proc->getFormalParams()[Idx];
    FormalParams[FP] = &Arg;
    writeLocalVariable(Curr, FP, &Arg);
    }
  3. After this setup, we can call the emit() method to start generating the IR code for statements: auto Block = Proc->getStmts();
    emit(Proc->getStmts());
  4. The last block after generating the IR code may not be sealed yet, so we must call sealBlock() now. A procedure in tinylang may have an implicit return, so we must also check if the last basic block has a proper terminator, and add one if not: if (!Curr->getTerminator()) {
    Builder.CreateRetVoid();
    }
    sealBlock(Curr);
    }

With that, we’ve finished generating IR code for functions. However, we still need to create the LLVM module, which holds all the IR code together. We’ll do this in the next section.

Setting up the module and the driver

We collect all the functions and global variables of a compilation unit in an LLVM module. To ease the IR generation process, we can wrap all the functions from the previous sections into a code generator class. To get a working compiler, we also need to define the target architecture for which we want to generate code, and also add the passes that emit the code. We will implement this in this and the next few chapters, starting with the code generator.

Leave a Reply

Your email address will not be published. Required fields are marked *