LLVM Exercise VI

Posted by on February 8, 2025

It is time to bring in some conditionals. Adding an “if” to our friend foo():

    int foo() {
      int retval1 = 0x0AB;
      int retval2 = 0;
      if (retval2)
        return retval2;
      return retval1;
    }

We need to implement instructions for both conditional and unconditional branches (i.e jumps). An unconditional branch is just like this (assuming no carry flag is set):

    ...

    def brtarget : Operand;
    
    ...
    
    let isBarrier=1, isBranch=1, isTerminator=1 in
    {
    def JNC : HP41MCODEInst<0x00B, (outs), (ins brtarget:$addr),
                            "JNC $addr", [(br bb:$addr)]>;
    }

We will use custom lowered pseudo instructions as an easy solution. This is a start:

    class HP41MCODEPseudo pattern> : HP41MCODEInst<0, outs, ins,
        asmstr, pattern> {
      let isCodeGenOnly = 1;
      let isPseudo = 1;
    }

    ...

    SDT_HP41MCODEBRCC : SDTypeProfile<0, 4, [SDTCisVT<0,
                                             OtherVT>]>;
    
    def HP41MCODEBRCC     : SDNode<"HP41MCODEISD::BRCC",
            SDT_HP41MCODEBRCC, [SDNPHasChain, SDNPInGlue]>;

    ...
    
    let isBranch=1, isTerminator=1 in
    {
    def BRCC : HP41MCODEPseudo< (outs), (ins brtarget:$addr,
            RC:$lhs, i32imm:$rhs, i32imm:$cond),
            "#BRCC $addr $lhs $rhs $cond",
            [(HP41MCODEBRCC bb:$addr, RC:$lhs, (i32 imm:$rhs),
             (i32 imm:$cond))]>;
    }

Note that this instruction will only match the case we have in this example. Different cases should use different instructions. More code snippets:

    ...

      BRCC,        // Branch to dest on condition
          
    ...
          
    SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;

    ...
      
    setOperationAction(ISD::BR_CC, MVT::i32, Custom);
      
    ...
      
      case HP41MCODEISD::BRCC: return "HP41MCODEISD::BRCC";
      
    ...
    
    SDValue HP41MCODETargetLowering::LowerBR_CC(SDValue Op,
            SelectionDAG &DAG) const {
      SDValue Chain = Op.getOperand(0);
      ISD::CondCode CC = cast(Op.getOperand(1))->get();
      SDValue LHS = Op.getOperand(2);
      SDValue RHS = Op.getOperand(3);
      SDValue Dest = Op.getOperand(4);
      SDLoc dl(Op);

      return DAG.getNode(HP41MCODEISD::BRCC, dl, MVT::Other,
              Chain, Dest, LHS, RHS,
              DAG.getConstant(CC, dl, MVT::i32));
    }
    
    ...

      case ISD::BR_CC:
        return LowerBR_CC(Op, DAG);
    

So for this example we only need to produce machine instructions that compares the C register to 0 and then branches if it is equal. This must be done in a post-RA pass:

    bool HP41MCODEInstrInfo::expandPostRAPseudo(
          MachineInstr &MI) const {
      MachineBasicBlock &MBB = *MI.getParent();
      MachineFunction &MF = *MBB.getParent();
      const HP41MCODEInstrInfo &TII = *static_cast(
          MF.getSubtarget().getInstrInfo());
      DebugLoc dl = MBB.findDebugLoc(MI);

      switch (MI.getOpcode()) {
      case HP41MCODE::BRCC: {
        assert(MI.getOperand(1).getReg() == HP41MCODE::C &&
               "Not implemented!");
        assert(MI.getOperand(2).getImm() == 0 &&
               "Not implemented!");
        assert(MI.getOperand(3).getImm() == ISD::SETEQ &&
               "Not implemented!");

        BuildMI(MBB, MI, dl, TII.get(HP41MCODE::qCneq0ALL));
        BuildMI(MBB, MI, dl,
                TII.get(HP41MCODE::JNC)).addMBB(
                        MI.getOperand(0).getMBB());
        MI.eraseFromParent();
        return true;
      }
      }
      return false;
    }

This is the machine instruction used:

    def qCneq0ALL : HP41MCODEInst<0x2EE, (outs), (ins),
                                  "?C!=0 ALL", []>;

And we get:

	.file	"hello.c"
	.text
	.globl	foo                     ! -- Begin function foo
	.type	foo,@function
foo:                                    ! @foo
! %bb.0:                                ! %entry
	LDI S&X HEX: 0AB
	WRIT 2
	C=0 ALL
	WRIT 1
	READ 1
	?C!=0 ALL
	JNC .LBB0_2
! %bb.1:                                ! %if.then
	READ 1
	WRIT 3
	JNC .LBB0_3
.LBB0_2:                                ! %if.end
	READ 2
	WRIT 3
.LBB0_3:                                ! %return
	READ 3
	RTN
.Lfunc_end0:
	.size	foo, .Lfunc_end0-foo
                                        ! -- End function
	.ident	"clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea1dfd50bfdfbd75969fd7653bc71c81f2a2350f)"
	.section	".note.GNU-stack"
	.addrsig

Comments are closed.