Skip to content

First 1802 Output From LLVM

December 14, 2014

So, I spent a few hours poking at the “LEG” example of a back end for LLVM.

My goal was to get the llvm back end to emit some code for the 1802 with llvm Intermediate Language (IL) as input. I’m just cloning/changing bits and pieces within the LEG example rather than building from scratch.
The input fragment is from a C function that returns the sum of two arguments. It looks like this

; ModuleID = 'ex1.ll'
target datalayout = "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32"
target triple = "leg"

; Function Attrs: nounwind readonly
define i32 @foo(i32 %a, i32 %b) #0 {
entry:
    %c = add i32 %a, %b
    br label %exit
exit:
    ret i32 %c
}

attributes #0 = { nounwind readonly "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = metadata !{metadata !"clang version 3.5.0 "}

The standard LEG output for it is:

; ModuleID = 'ex1.ll'
	.text
	.file	"ex1.ll"
	.globl	foo
	.type	foo,@function
foo:                                    # @foo
# BB#0:                                 # %entry
	addr r0,r0,r1
# BB#1:                                 # %exit
	bx lr
.Ltmp0:
	.size	foo, .Ltmp0-foo


	.ident	"clang version 3.5.0 "

My goal is to change the “bx lr” (which is an ARM branch to the link register) to something like “sep 5” which can stand in for an 1802 return.

To do this I changed a bunch of source files:

Actually defining the format of my return instruction took the following:

//===----------------------------------------------------------------------===//
// COSMAC Return Instruction
//===----------------------------------------------------------------------===//

let isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [LR] in {
    def COSRET : InstCOS<(outs), (ins variable_ops),
                "SEP 5",  [(COSRetFlag)]> {
        let Inst{7-0}  = 0xD5;
    }
}

So that gets the notion of “SEP 5” in the game and associates it somehow with COSRetFlag.
Getting it emitted took the following – the important line is two from the bottom:

LEGTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
                               bool isVarArg,
                               const SmallVectorImpl<ISD::OutputArg> &Outs,
                               const SmallVectorImpl<SDValue> &OutVals,
                               SDLoc dl, SelectionDAG &DAG) const {
  ...<snip>...
  return DAG.getNode(LEGISD::COSRET_FLAG, dl, MVT::Other, RetOps);
}

Making that COSRET_FLAG a thing, took the following:

namespace LEGISD {
enum NodeType {
  // Start the numbering where the builtin ops and target ops leave off.
  FIRST_NUMBER = ISD::BUILTIN_OP_END,
  RET_FLAG,
  // This loads the symbol (e.g. global address) into a register.
  LOAD_SYM,
  // This loads a 32-bit immediate into a register.
  MOVEi32,
  CALL,
    COSRET_FLAG,
};

and

def COSRetFlag    : SDNode<"LEGISD::COSRET_FLAG", SDTNone,
                           [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;

I also had to define the idea of 8 bit cosmac instructions with the following:

//===----------------------------------------------------------------------===//
// 8 bit COSMAC instruction
//===----------------------------------------------------------------------===//
class InstCOS<dag outs, dag ins, string asmstr, list<dag> pattern>
    : Instruction {
  field bits<8> Inst;

  let Namespace = "LEG";
  dag OutOperandList = outs;
  dag InOperandList = ins;
  let AsmString   = asmstr;
  let Pattern = pattern;
  let Size = 1;

  // Set a dummy pattern default
  let Inst{7-0} = 0xAA;
}

I also had to hack one spot where it was expected that all instructions were 4 bytes.

void LEGMCCodeEmitter::EncodeInstruction(const MCInst &amp;MI, raw_ostream &amp;OS,
                                         SmallVectorImpl &amp;Fixups,
                                         const MCSubtargetInfo &amp;STI) const {
  const MCInstrDesc &amp;Desc = MCII.get(MI.getOpcode());
  if (Desc.getSize() != 4  &amp;&amp; Desc.getSize() != 1) { //wjr
    llvm_unreachable("Unexpected instruction size!");
  }

  const uint32_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);

  EmitConstant(Binary, Desc.getSize(), OS);
  ++MCNumEmitted;
}

These changes also got my COSMAC instruction output to a .elf object file which seems very nice. I know from experience with SDCC that you can’t get very far just poking at the very back like this but I feel like it’s necessary to get my hands around the code.

Advertisements

From → LLVM

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: