Assembler

This chapter include the assembly programming support in Cpu0 backend.

When it comes to assembly language programming, there are two type of writting in C/C++ as follows,

ordinary assembly

asm("ld       $2, 8($sp)");

inline assembly

int foo = 10;
const int bar = 15;

__asm__ __volatile__("addu %0,%1,%2"
                     :"=r"(foo) // 5
                     :"r"(foo), "r"(bar)
                     );

In llvm, the first is supported by LLVM AsmParser, and the second is inline assembly handler. With AsmParser and inline assembly support in Cpu0 backend, we can hand-code the assembly language in C/C++ file and translate it into obj (elf format).

AsmParser support

This section lists all the AsmParser code for Cpu0 backend with only a few explanation. Please refer here [1] for more AsmParser explanation.

Run Chapter10_1/ with ch11_1.cpp will get the following error message.

lbdex/input/ch11_1.cpp

asm("ld	$2, 8($sp)");
asm("st	$0, 4($sp)");
asm("addiu $3,	$ZERO, 0");
asm("add $v1, $at, $v0");
asm("sub $3, $2, $3");
asm("mul $2, $1, $3");
asm("div $3, $2");
asm("divu $2, $3");
asm("and $2, $1, $3");
asm("or $3, $1, $2");
asm("xor $1, $2, $3");
asm("mult $4, $3");
asm("multu $3, $2");
asm("mfhi $3");
asm("mflo $2");
asm("mthi $2");
asm("mtlo $2");
asm("sra $2, $2, 2");
asm("rol $2, $1, 3");
asm("ror $3, $3, 4");
asm("shl $2, $2, 2");
asm("shr $2, $3, 5");
asm("cmp $sw, $2, $3");
asm("jeq $sw, 20");
asm("jne $sw, 16");
asm("jlt $sw, -20");
asm("jle $sw, -16");
asm("jgt $sw, -4");
asm("jge $sw, -12");
asm("jsub 0x000010000");
asm("jr $4");
asm("ret $lr");
asm("jalr $t9");
asm("li $3, 0x00700000");
asm("la $3, 0x00800000($6)");
asm("la $3, 0x00900000");
JonathantekiiMac:input Jonathan$ clang -c ch11_1.cpp -emit-llvm -o
ch11_1.bc
JonathantekiiMac:input Jonathan$ /Users/Jonathan/llvm/test/build/bin/llc
-march=cpu0 -relocation-model=pic -filetype=obj ch11_1.bc
-o ch11_1.cpu0.o
LLVM ERROR: Inline asm not supported by this streamer because we don't have
an asm parser for this target

Since we havn’t implemented Cpu0 assembler, it has the error message as above. The Cpu0 can translate LLVM IR into assembly and obj directly, but it cannot translate hand-code assembly instructions into obj.

The Chapter11_1/ include AsmParser implementation as follows,

lbdex/chapters/Chapter11_1/AsmParser/Cpu0AsmParser.cpp

//===-- Cpu0AsmParser.cpp - Parse Cpu0 assembly to MCInst instructions ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Cpu0.h"
#if CH >= CH11_1

#include "MCTargetDesc/Cpu0MCExpr.h"
#include "MCTargetDesc/Cpu0MCTargetDesc.h"
#include "Cpu0RegisterInfo.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

#define DEBUG_TYPE "cpu0-asm-parser"

namespace {
class Cpu0AssemblerOptions {
public:
  Cpu0AssemblerOptions():
    reorder(true), macro(true) {
  }

  bool isReorder() {return reorder;}
  void setReorder() {reorder = true;}
  void setNoreorder() {reorder = false;}

  bool isMacro() {return macro;}
  void setMacro() {macro = true;}
  void setNomacro() {macro = false;}

private:
  bool reorder;
  bool macro;
};
}

namespace {
class Cpu0AsmParser : public MCTargetAsmParser {
  MCAsmParser &Parser;
  Cpu0AssemblerOptions Options;


#define GET_ASSEMBLER_HEADER
#include "Cpu0GenAsmMatcher.inc"

  bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                               OperandVector &Operands, MCStreamer &Out,
                               uint64_t &ErrorInfo,
                               bool MatchingInlineAsm) override;

  bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override;

  OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc,
                                        SMLoc &EndLoc) override;

  bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                        SMLoc NameLoc, OperandVector &Operands) override;

  bool ParseDirective(AsmToken DirectiveID) override;

  OperandMatchResultTy parseMemOperand(OperandVector &);

  bool ParseOperand(OperandVector &Operands, StringRef Mnemonic);

  int tryParseRegister(StringRef Mnemonic);

  bool tryParseRegisterOperand(OperandVector &Operands,
                               StringRef Mnemonic);

  bool needsExpansion(MCInst &Inst);

  void expandInstruction(MCInst &Inst, SMLoc IDLoc,
                         SmallVectorImpl<MCInst> &Instructions);
  void expandLoadImm(MCInst &Inst, SMLoc IDLoc,
                     SmallVectorImpl<MCInst> &Instructions);
  void expandLoadAddressImm(MCInst &Inst, SMLoc IDLoc,
                            SmallVectorImpl<MCInst> &Instructions);
  void expandLoadAddressReg(MCInst &Inst, SMLoc IDLoc,
                            SmallVectorImpl<MCInst> &Instructions);
  bool reportParseError(StringRef ErrorMsg);

  bool parseMemOffset(const MCExpr *&Res);
  bool parseRelocOperand(const MCExpr *&Res);

  const MCExpr *evaluateRelocExpr(const MCExpr *Expr, StringRef RelocStr);

  bool parseDirectiveSet();

  bool parseSetAtDirective();
  bool parseSetNoAtDirective();
  bool parseSetMacroDirective();
  bool parseSetNoMacroDirective();
  bool parseSetReorderDirective();
  bool parseSetNoReorderDirective();

  int matchRegisterName(StringRef Symbol);

  int matchRegisterByNumber(unsigned RegNum, StringRef Mnemonic);

  unsigned getReg(int RC,int RegNo);

public:
  Cpu0AsmParser(const MCSubtargetInfo &sti, MCAsmParser &parser,
                const MCInstrInfo &MII, const MCTargetOptions &Options)
    : MCTargetAsmParser(Options, sti, MII), Parser(parser) {
    // Initialize the set of available features.
    setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits()));
  }

  MCAsmParser &getParser() const { return Parser; }
  MCAsmLexer &getLexer() const { return Parser.getLexer(); }

};
}

namespace {

/// Cpu0Operand - Instances of this class represent a parsed Cpu0 machine
/// instruction.
class Cpu0Operand : public MCParsedAsmOperand {

  enum KindTy {
    k_Immediate,
    k_Memory,
    k_Register,
    k_Token
  } Kind;

public:
  Cpu0Operand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}

  struct Token {
    const char *Data;
    unsigned Length;
  };
  struct PhysRegOp {
    unsigned RegNum; /// Register Number
  };
  struct ImmOp {
    const MCExpr *Val;
  };
  struct MemOp {
    unsigned Base;
    const MCExpr *Off;
  };

  union {
    struct Token Tok;
    struct PhysRegOp Reg;
    struct ImmOp Imm;
    struct MemOp Mem;
  };

  SMLoc StartLoc, EndLoc;

public:
  void addRegOperands(MCInst &Inst, unsigned N) const {
    assert(N == 1 && "Invalid number of operands!");
    Inst.addOperand(MCOperand::createReg(getReg()));
  }

  void addExpr(MCInst &Inst, const MCExpr *Expr) const{
    // Add as immediate when possible.  Null MCExpr = 0.
    if (Expr == 0)
      Inst.addOperand(MCOperand::createImm(0));
    else if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr))
      Inst.addOperand(MCOperand::createImm(CE->getValue()));
    else
      Inst.addOperand(MCOperand::createExpr(Expr));
  }

  void addImmOperands(MCInst &Inst, unsigned N) const {
    assert(N == 1 && "Invalid number of operands!");
    const MCExpr *Expr = getImm();
    addExpr(Inst,Expr);
  }

  void addMemOperands(MCInst &Inst, unsigned N) const {
    assert(N == 2 && "Invalid number of operands!");

    Inst.addOperand(MCOperand::createReg(getMemBase()));

    const MCExpr *Expr = getMemOff();
    addExpr(Inst,Expr);
  }

  bool isReg() const override { return Kind == k_Register; }
  bool isImm() const override { return Kind == k_Immediate; }
  bool isToken() const override { return Kind == k_Token; }
  bool isMem() const override { return Kind == k_Memory; }

  StringRef getToken() const {
    assert(Kind == k_Token && "Invalid access!");
    return StringRef(Tok.Data, Tok.Length);
  }

  unsigned getReg() const override {
    assert((Kind == k_Register) && "Invalid access!");
    return Reg.RegNum;
  }

  const MCExpr *getImm() const {
    assert((Kind == k_Immediate) && "Invalid access!");
    return Imm.Val;
  }

  unsigned getMemBase() const {
    assert((Kind == k_Memory) && "Invalid access!");
    return Mem.Base;
  }

  const MCExpr *getMemOff() const {
    assert((Kind == k_Memory) && "Invalid access!");
    return Mem.Off;
  }

  static std::unique_ptr<Cpu0Operand> CreateToken(StringRef Str, SMLoc S) {
    auto Op = std::make_unique<Cpu0Operand>(k_Token);
    Op->Tok.Data = Str.data();
    Op->Tok.Length = Str.size();
    Op->StartLoc = S;
    Op->EndLoc = S;
    return Op;
  }

  /// Internal constructor for register kinds
  static std::unique_ptr<Cpu0Operand> CreateReg(unsigned RegNum, SMLoc S, 
                                                SMLoc E) {
    auto Op = std::make_unique<Cpu0Operand>(k_Register);
    Op->Reg.RegNum = RegNum;
    Op->StartLoc = S;
    Op->EndLoc = E;
    return Op;
  }

  static std::unique_ptr<Cpu0Operand> CreateImm(const MCExpr *Val, SMLoc S, SMLoc E) {
    auto Op = std::make_unique<Cpu0Operand>(k_Immediate);
    Op->Imm.Val = Val;
    Op->StartLoc = S;
    Op->EndLoc = E;
    return Op;
  }

  static std::unique_ptr<Cpu0Operand> CreateMem(unsigned Base, const MCExpr *Off,
                                 SMLoc S, SMLoc E) {
    auto Op = std::make_unique<Cpu0Operand>(k_Memory);
    Op->Mem.Base = Base;
    Op->Mem.Off = Off;
    Op->StartLoc = S;
    Op->EndLoc = E;
    return Op;
  }

  /// getStartLoc - Get the location of the first token of this operand.
  SMLoc getStartLoc() const override { return StartLoc; }
  /// getEndLoc - Get the location of the last token of this operand.
  SMLoc getEndLoc() const override { return EndLoc; }

  void print(raw_ostream &OS) const override {
    switch (Kind) {
    case k_Immediate:
      OS << "Imm<";
      OS << *Imm.Val;
      OS << ">";
      break;
    case k_Memory:
      OS << "Mem<";
      OS << Mem.Base;
      OS << ", ";
      OS << *Mem.Off;
      OS << ">";
      break;
    case k_Register:
      OS << "Register<" << Reg.RegNum << ">";
      break;
    case k_Token:
      OS << Tok.Data;
      break;
    }
  }
};
}

void printCpu0Operands(OperandVector &Operands) {
  for (size_t i = 0; i < Operands.size(); i++) {
    Cpu0Operand* op = static_cast<Cpu0Operand*>(&*Operands[i]);
    assert(op != nullptr);
    LLVM_DEBUG(dbgs() << "<" << *op << ">");
  }
  LLVM_DEBUG(dbgs() << "\n");
}

//@1 {
bool Cpu0AsmParser::needsExpansion(MCInst &Inst) {

  switch(Inst.getOpcode()) {
    case Cpu0::LoadImm32Reg:
    case Cpu0::LoadAddr32Imm:
    case Cpu0::LoadAddr32Reg:
      return true;
    default:
      return false;
  }
}

void Cpu0AsmParser::expandInstruction(MCInst &Inst, SMLoc IDLoc,
                        SmallVectorImpl<MCInst> &Instructions){
  switch(Inst.getOpcode()) {
    case Cpu0::LoadImm32Reg:
      return expandLoadImm(Inst, IDLoc, Instructions);
    case Cpu0::LoadAddr32Imm:
      return expandLoadAddressImm(Inst,IDLoc,Instructions);
    case Cpu0::LoadAddr32Reg:
      return expandLoadAddressReg(Inst,IDLoc,Instructions);
    }
}
//@1 }

void Cpu0AsmParser::expandLoadImm(MCInst &Inst, SMLoc IDLoc,
                                  SmallVectorImpl<MCInst> &Instructions){
  MCInst tmpInst;
  const MCOperand &ImmOp = Inst.getOperand(1);
  assert(ImmOp.isImm() && "expected immediate operand kind");
  const MCOperand &RegOp = Inst.getOperand(0);
  assert(RegOp.isReg() && "expected register operand kind");

  int ImmValue = ImmOp.getImm();
  tmpInst.setLoc(IDLoc);
  if ( 0 <= ImmValue && ImmValue <= 65535) {
    // for 0 <= j <= 65535.
    // li d,j => ori d,$zero,j
    tmpInst.setOpcode(Cpu0::ORi);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(
              MCOperand::createReg(Cpu0::ZERO));
    tmpInst.addOperand(MCOperand::createImm(ImmValue));
    Instructions.push_back(tmpInst);
  } else if ( ImmValue < 0 && ImmValue >= -32768) {
    // for -32768 <= j < 0.
    // li d,j => addiu d,$zero,j
    tmpInst.setOpcode(Cpu0::ADDiu); //TODO:no ADDiu64 in td files?
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(
              MCOperand::createReg(Cpu0::ZERO));
    tmpInst.addOperand(MCOperand::createImm(ImmValue));
    Instructions.push_back(tmpInst);
  } else {
    // for any other value of j that is representable as a 32-bit integer.
    // li d,j => lui d,hi16(j)
    //           ori d,d,lo16(j)
    tmpInst.setOpcode(Cpu0::LUi);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm((ImmValue & 0xffff0000) >> 16));
    Instructions.push_back(tmpInst);
    tmpInst.clear();
    tmpInst.setOpcode(Cpu0::ORi);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm(ImmValue & 0xffff));
    tmpInst.setLoc(IDLoc);
    Instructions.push_back(tmpInst);
  }
}

void Cpu0AsmParser::expandLoadAddressReg(MCInst &Inst, SMLoc IDLoc,
                                         SmallVectorImpl<MCInst> &Instructions){
  MCInst tmpInst;
  const MCOperand &ImmOp = Inst.getOperand(2);
  assert(ImmOp.isImm() && "expected immediate operand kind");
  const MCOperand &SrcRegOp = Inst.getOperand(1);
  assert(SrcRegOp.isReg() && "expected register operand kind");
  const MCOperand &DstRegOp = Inst.getOperand(0);
  assert(DstRegOp.isReg() && "expected register operand kind");
  int ImmValue = ImmOp.getImm();
  if ( -32768 <= ImmValue && ImmValue <= 32767) {
    // for -32768 <= j < 32767.
    //la d,j(s) => addiu d,s,j
    tmpInst.setOpcode(Cpu0::ADDiu); //TODO:no ADDiu64 in td files?
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(SrcRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm(ImmValue));
    Instructions.push_back(tmpInst);
  } else {
    // for any other value of j that is representable as a 32-bit integer.
    // la d,j(s) => lui d,hi16(j)
    //              ori d,d,lo16(j)
    //              add d,d,s
    tmpInst.setOpcode(Cpu0::LUi);
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm((ImmValue & 0xffff0000) >> 16));
    Instructions.push_back(tmpInst);
    tmpInst.clear();
    tmpInst.setOpcode(Cpu0::ORi);
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm(ImmValue & 0xffff));
    Instructions.push_back(tmpInst);
    tmpInst.clear();
    tmpInst.setOpcode(Cpu0::ADD);
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(DstRegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(SrcRegOp.getReg()));
    Instructions.push_back(tmpInst);
  }
}

void Cpu0AsmParser::expandLoadAddressImm(MCInst &Inst, SMLoc IDLoc,
                                         SmallVectorImpl<MCInst> &Instructions){
  MCInst tmpInst;
  const MCOperand &ImmOp = Inst.getOperand(1);
  assert(ImmOp.isImm() && "expected immediate operand kind");
  const MCOperand &RegOp = Inst.getOperand(0);
  assert(RegOp.isReg() && "expected register operand kind");
  int ImmValue = ImmOp.getImm();
  if ( -32768 <= ImmValue && ImmValue <= 32767) {
    // for -32768 <= j < 32767.
    //la d,j => addiu d,$zero,j
    tmpInst.setOpcode(Cpu0::ADDiu);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(
              MCOperand::createReg(Cpu0::ZERO));
    tmpInst.addOperand(MCOperand::createImm(ImmValue));
    Instructions.push_back(tmpInst);
  } else {
    // for any other value of j that is representable as a 32-bit integer.
    // la d,j => lui d,hi16(j)
    //           ori d,d,lo16(j)
    tmpInst.setOpcode(Cpu0::LUi);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm((ImmValue & 0xffff0000) >> 16));
    Instructions.push_back(tmpInst);
    tmpInst.clear();
    tmpInst.setOpcode(Cpu0::ORi);
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createReg(RegOp.getReg()));
    tmpInst.addOperand(MCOperand::createImm(ImmValue & 0xffff));
    Instructions.push_back(tmpInst);
  }
}

//@2 {
bool Cpu0AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                            OperandVector &Operands,
                                            MCStreamer &Out,
                                            uint64_t &ErrorInfo,
                                            bool MatchingInlineAsm) {
  printCpu0Operands(Operands);
  MCInst Inst;
  unsigned MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo,
                                              MatchingInlineAsm);
  switch (MatchResult) {
  default: break;
  case Match_Success: {
    if (needsExpansion(Inst)) {
      SmallVector<MCInst, 4> Instructions;
      expandInstruction(Inst, IDLoc, Instructions);
      for(unsigned i =0; i < Instructions.size(); i++){
        Out.emitInstruction(Instructions[i], getSTI());
      }
    } else {
        Inst.setLoc(IDLoc);
        Out.emitInstruction(Inst, getSTI());
      }
    return false;
  }
//@2 }
  case Match_MissingFeature:
    Error(IDLoc, "instruction requires a CPU feature not currently enabled");
    return true;
  case Match_InvalidOperand: {
    SMLoc ErrorLoc = IDLoc;
    if (ErrorInfo != ~0U) {
      if (ErrorInfo >= Operands.size())
        return Error(IDLoc, "too few operands for instruction");

      ErrorLoc = ((Cpu0Operand &)*Operands[ErrorInfo]).getStartLoc();
      if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc;
    }

    return Error(ErrorLoc, "invalid operand for instruction");
  }
  case Match_MnemonicFail:
    return Error(IDLoc, "invalid instruction");
  }
  return true;
}

int Cpu0AsmParser::matchRegisterName(StringRef Name) {

   int CC;
    CC = StringSwitch<unsigned>(Name)
      .Case("zero",  Cpu0::ZERO)
      .Case("at",  Cpu0::AT)
      .Case("v0",  Cpu0::V0)
      .Case("v1",  Cpu0::V1)
      .Case("a0",  Cpu0::A0)
      .Case("a1",  Cpu0::A1)
      .Case("t9",  Cpu0::T9)
      .Case("t0",  Cpu0::T0)
      .Case("t1",  Cpu0::T1)
      .Case("s0",  Cpu0::S0)
      .Case("s1",  Cpu0::S1)
      .Case("sw",  Cpu0::SW)
      .Case("gp",  Cpu0::GP)
      .Case("fp",  Cpu0::FP)
      .Case("sp",  Cpu0::SP)
      .Case("lr",  Cpu0::LR)
      .Case("pc",  Cpu0::PC)
      .Case("hi",  Cpu0::HI)
      .Case("lo",  Cpu0::LO)
      .Case("epc", Cpu0::EPC)
      .Default(-1);

  if (CC != -1)
    return CC;

  return -1;
}

unsigned Cpu0AsmParser::getReg(int RC,int RegNo) {
  return *(getContext().getRegisterInfo()->getRegClass(RC).begin() + RegNo);
}

int Cpu0AsmParser::matchRegisterByNumber(unsigned RegNum, StringRef Mnemonic) {
  if (RegNum > 15)
    return -1;

  return getReg(Cpu0::CPURegsRegClassID, RegNum);
}

int Cpu0AsmParser::tryParseRegister(StringRef Mnemonic) {
  const AsmToken &Tok = Parser.getTok();
  int RegNum = -1;

  if (Tok.is(AsmToken::Identifier)) {
    std::string lowerCase = Tok.getString().lower();
    RegNum = matchRegisterName(lowerCase);
  } else if (Tok.is(AsmToken::Integer))
    RegNum = matchRegisterByNumber(static_cast<unsigned>(Tok.getIntVal()),
                                   Mnemonic.lower());
    else
      return RegNum;  //error
  return RegNum;
}

bool Cpu0AsmParser::
  tryParseRegisterOperand(OperandVector &Operands,
                          StringRef Mnemonic){

  SMLoc S = Parser.getTok().getLoc();
  int RegNo = -1;

    RegNo = tryParseRegister(Mnemonic);
  if (RegNo == -1)
    return true;

  Operands.push_back(Cpu0Operand::CreateReg(RegNo, S,
      Parser.getTok().getLoc()));
  Parser.Lex(); // Eat register token.
  return false;
}

bool Cpu0AsmParser::ParseOperand(OperandVector &Operands,
                                 StringRef Mnemonic) {
  LLVM_DEBUG(dbgs() << "ParseOperand\n");
  // Check if the current operand has a custom associated parser, if so, try to
  // custom parse the operand, or fallback to the general approach.
  OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic);
  if (ResTy == MatchOperand_Success)
    return false;
  // If there wasn't a custom match, try the generic matcher below. Otherwise,
  // there was a match, but an error occurred, in which case, just return that
  // the operand parsing failed.
  if (ResTy == MatchOperand_ParseFail)
    return true;

  LLVM_DEBUG(dbgs() << ".. Generic Parser\n");

  switch (getLexer().getKind()) {
  default:
    Error(Parser.getTok().getLoc(), "unexpected token in operand");
    return true;
  case AsmToken::Dollar: {
    // parse register
    SMLoc S = Parser.getTok().getLoc();
    Parser.Lex(); // Eat dollar token.
    // parse register operand
    if (!tryParseRegisterOperand(Operands, Mnemonic)) {
      if (getLexer().is(AsmToken::LParen)) {
        // check if it is indexed addressing operand
        Operands.push_back(Cpu0Operand::CreateToken("(", S));
        Parser.Lex(); // eat parenthesis
        if (getLexer().isNot(AsmToken::Dollar))
          return true;

        Parser.Lex(); // eat dollar
        if (tryParseRegisterOperand(Operands, Mnemonic))
          return true;

        if (!getLexer().is(AsmToken::RParen))
          return true;

        S = Parser.getTok().getLoc();
        Operands.push_back(Cpu0Operand::CreateToken(")", S));
        Parser.Lex();
      }
      return false;
    }
    // maybe it is a symbol reference
    StringRef Identifier;
    if (Parser.parseIdentifier(Identifier))
      return true;

    SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1);

    MCSymbol *Sym = getContext().getOrCreateSymbol("$" + Identifier);

    // Otherwise create a symbol ref.
    const MCExpr *Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None,
                                                getContext());

    Operands.push_back(Cpu0Operand::CreateImm(Res, S, E));
    return false;
  }
  case AsmToken::Identifier:
  case AsmToken::LParen:
  case AsmToken::Minus:
  case AsmToken::Plus:
  case AsmToken::Integer:
  case AsmToken::String: {
     // quoted label names
    const MCExpr *IdVal;
    SMLoc S = Parser.getTok().getLoc();
    if (getParser().parseExpression(IdVal))
      return true;
    SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1);
    Operands.push_back(Cpu0Operand::CreateImm(IdVal, S, E));
    return false;
  }
  case AsmToken::Percent: {
    // it is a symbol reference or constant expression
    const MCExpr *IdVal;
    SMLoc S = Parser.getTok().getLoc(); // start location of the operand
    if (parseRelocOperand(IdVal))
      return true;

    SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1);

    Operands.push_back(Cpu0Operand::CreateImm(IdVal, S, E));
    return false;
  } // case AsmToken::Percent
  } // switch(getLexer().getKind())
  return true;
}

const MCExpr *Cpu0AsmParser::evaluateRelocExpr(const MCExpr *Expr,
                                               StringRef RelocStr) {
  Cpu0MCExpr::Cpu0ExprKind Kind =
      StringSwitch<Cpu0MCExpr::Cpu0ExprKind>(RelocStr)
          .Case("call16", Cpu0MCExpr::CEK_GOT_CALL)
          .Case("call_hi", Cpu0MCExpr::CEK_CALL_HI16)
          .Case("call_lo", Cpu0MCExpr::CEK_CALL_LO16)
          .Case("dtp_hi", Cpu0MCExpr::CEK_DTP_HI)
          .Case("dtp_lo", Cpu0MCExpr::CEK_DTP_LO)
          .Case("got", Cpu0MCExpr::CEK_GOT)
          .Case("got_hi", Cpu0MCExpr::CEK_GOT_HI16)
          .Case("got_lo", Cpu0MCExpr::CEK_GOT_LO16)
          .Case("gottprel", Cpu0MCExpr::CEK_GOTTPREL)
          .Case("gp_rel", Cpu0MCExpr::CEK_GPREL)
          .Case("hi", Cpu0MCExpr::CEK_ABS_HI)
          .Case("lo", Cpu0MCExpr::CEK_ABS_LO)
          .Case("tlsgd", Cpu0MCExpr::CEK_TLSGD)
          .Case("tlsldm", Cpu0MCExpr::CEK_TLSLDM)
          .Case("tp_hi", Cpu0MCExpr::CEK_TP_HI)
          .Case("tp_lo", Cpu0MCExpr::CEK_TP_LO)
          .Default(Cpu0MCExpr::CEK_None);

  assert(Kind != Cpu0MCExpr::CEK_None);
  return Cpu0MCExpr::create(Kind, Expr, getContext());
}

bool Cpu0AsmParser::parseRelocOperand(const MCExpr *&Res) {

  Parser.Lex(); // eat % token
  const AsmToken &Tok = Parser.getTok(); // get next token, operation
  if (Tok.isNot(AsmToken::Identifier))
    return true;

  std::string Str = Tok.getIdentifier().str();

  Parser.Lex(); // eat identifier
  // now make expression from the rest of the operand
  const MCExpr *IdVal;
  SMLoc EndLoc;

  if (getLexer().getKind() == AsmToken::LParen) {
    while (1) {
      Parser.Lex(); // eat '(' token
      if (getLexer().getKind() == AsmToken::Percent) {
        Parser.Lex(); // eat % token
        const AsmToken &nextTok = Parser.getTok();
        if (nextTok.isNot(AsmToken::Identifier))
          return true;
        Str += "(%";
        Str += nextTok.getIdentifier();
        Parser.Lex(); // eat identifier
        if (getLexer().getKind() != AsmToken::LParen)
          return true;
      } else
        break;
    }
    if (getParser().parseParenExpression(IdVal,EndLoc))
      return true;

    while (getLexer().getKind() == AsmToken::RParen)
      Parser.Lex(); // eat ')' token

  } else
    return true; // parenthesis must follow reloc operand

  Res = evaluateRelocExpr(IdVal, Str);
  return false;
}

bool Cpu0AsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc,
                                  SMLoc &EndLoc) {

  StartLoc = Parser.getTok().getLoc();
  RegNo = tryParseRegister("");
  EndLoc = Parser.getTok().getLoc();
  return (RegNo == (unsigned)-1);
}

OperandMatchResultTy Cpu0AsmParser::tryParseRegister(unsigned &RegNo,
                                                     SMLoc &StartLoc,
                                                     SMLoc &EndLoc) {
  StartLoc = Parser.getTok().getLoc();
  RegNo = tryParseRegister("");
  EndLoc = Parser.getTok().getLoc();
  return (RegNo == (unsigned)-1) ? MatchOperand_NoMatch
                                 : MatchOperand_Success;
}

bool Cpu0AsmParser::parseMemOffset(const MCExpr *&Res) {
  switch(getLexer().getKind()) {
  default:
    return true;
  case AsmToken::Integer:
  case AsmToken::Minus:
  case AsmToken::Plus:
    return (getParser().parseExpression(Res));
  case AsmToken::Percent:
    return parseRelocOperand(Res);
  case AsmToken::LParen:
    return false;  // it's probably assuming 0
  }
  return true;
}

// eg, 12($sp) or 12(la)
OperandMatchResultTy Cpu0AsmParser::parseMemOperand(
               OperandVector &Operands) {

  const MCExpr *IdVal = 0;
  SMLoc S;
  // first operand is the offset
  S = Parser.getTok().getLoc();

  if (parseMemOffset(IdVal))
    return MatchOperand_ParseFail;

  const AsmToken &Tok = Parser.getTok(); // get next token
  if (Tok.isNot(AsmToken::LParen)) {
    Cpu0Operand &Mnemonic = static_cast<Cpu0Operand &>(*Operands[0]);
    if (Mnemonic.getToken() == "la") {
      SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer()-1);
      Operands.push_back(Cpu0Operand::CreateImm(IdVal, S, E));
      return MatchOperand_Success;
    }
    Error(Parser.getTok().getLoc(), "'(' expected");
    return MatchOperand_ParseFail;
  }

  Parser.Lex(); // Eat '(' token.

  const AsmToken &Tok1 = Parser.getTok(); // get next token
  if (Tok1.is(AsmToken::Dollar)) {
    Parser.Lex(); // Eat '$' token.
    if (tryParseRegisterOperand(Operands,"")) {
      Error(Parser.getTok().getLoc(), "unexpected token in operand");
      return MatchOperand_ParseFail;
    }

  } else {
    Error(Parser.getTok().getLoc(), "unexpected token in operand");
    return MatchOperand_ParseFail;
  }

  const AsmToken &Tok2 = Parser.getTok(); // get next token
  if (Tok2.isNot(AsmToken::RParen)) {
    Error(Parser.getTok().getLoc(), "')' expected");
    return MatchOperand_ParseFail;
  }

  SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1);

  Parser.Lex(); // Eat ')' token.

  if (!IdVal)
    IdVal = MCConstantExpr::create(0, getContext());

  // Replace the register operand with the memory operand.
  std::unique_ptr<Cpu0Operand> op(
      static_cast<Cpu0Operand *>(Operands.back().release()));
  int RegNo = op->getReg();
  // remove register from operands
  Operands.pop_back();
  // and add memory operand
  Operands.push_back(Cpu0Operand::CreateMem(RegNo, IdVal, S, E));
  return MatchOperand_Success;
}

bool Cpu0AsmParser::
ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc,
                 OperandVector &Operands) {

  // Create the leading tokens for the mnemonic, split by '.' characters.
  size_t Start = 0, Next = Name.find('.');
  StringRef Mnemonic = Name.slice(Start, Next);
  // Refer to the explanation in source code of function DecodeJumpFR(...) in 
  // Cpu0Disassembler.cpp
  if (Mnemonic == "ret")
    Mnemonic = "jr";

  Operands.push_back(Cpu0Operand::CreateToken(Mnemonic, NameLoc));

  // Read the remaining operands.
  if (getLexer().isNot(AsmToken::EndOfStatement)) {
    // Read the first operand.
    if (ParseOperand(Operands, Name)) {
      SMLoc Loc = getLexer().getLoc();
      Parser.eatToEndOfStatement();
      return Error(Loc, "unexpected token in argument list");
    }

    while (getLexer().is(AsmToken::Comma) ) {
      Parser.Lex();  // Eat the comma.

      // Parse and remember the operand.
      if (ParseOperand(Operands, Name)) {
        SMLoc Loc = getLexer().getLoc();
        Parser.eatToEndOfStatement();
        return Error(Loc, "unexpected token in argument list");
      }
    }
  }

  if (getLexer().isNot(AsmToken::EndOfStatement)) {
    SMLoc Loc = getLexer().getLoc();
    Parser.eatToEndOfStatement();
    return Error(Loc, "unexpected token in argument list");
  }

  Parser.Lex(); // Consume the EndOfStatement
  return false;
}

bool Cpu0AsmParser::reportParseError(StringRef ErrorMsg) {
   SMLoc Loc = getLexer().getLoc();
   Parser.eatToEndOfStatement();
   return Error(Loc, ErrorMsg);
}

bool Cpu0AsmParser::parseSetReorderDirective() {
  Parser.Lex();
  // if this is not the end of the statement, report error
  if (getLexer().isNot(AsmToken::EndOfStatement)) {
    reportParseError("unexpected token in statement");
    return false;
  }
  Options.setReorder();
  Parser.Lex(); // Consume the EndOfStatement
  return false;
}

bool Cpu0AsmParser::parseSetNoReorderDirective() {
    Parser.Lex();
    // if this is not the end of the statement, report error
    if (getLexer().isNot(AsmToken::EndOfStatement)) {
      reportParseError("unexpected token in statement");
      return false;
    }
    Options.setNoreorder();
    Parser.Lex(); // Consume the EndOfStatement
    return false;
}

bool Cpu0AsmParser::parseSetMacroDirective() {
  Parser.Lex();
  // if this is not the end of the statement, report error
  if (getLexer().isNot(AsmToken::EndOfStatement)) {
    reportParseError("unexpected token in statement");
    return false;
  }
  Options.setMacro();
  Parser.Lex(); // Consume the EndOfStatement
  return false;
}

bool Cpu0AsmParser::parseSetNoMacroDirective() {
  Parser.Lex();
  // if this is not the end of the statement, report error
  if (getLexer().isNot(AsmToken::EndOfStatement)) {
    reportParseError("`noreorder' must be set before `nomacro'");
    return false;
  }
  if (Options.isReorder()) {
    reportParseError("`noreorder' must be set before `nomacro'");
    return false;
  }
  Options.setNomacro();
  Parser.Lex(); // Consume the EndOfStatement
  return false;
}
bool Cpu0AsmParser::parseDirectiveSet() {

  // get next token
  const AsmToken &Tok = Parser.getTok();

  if (Tok.getString() == "reorder") {
    return parseSetReorderDirective();
  } else if (Tok.getString() == "noreorder") {
    return parseSetNoReorderDirective();
  } else if (Tok.getString() == "macro") {
    return parseSetMacroDirective();
  } else if (Tok.getString() == "nomacro") {
    return parseSetNoMacroDirective();
  }
  return true;
}

bool Cpu0AsmParser::ParseDirective(AsmToken DirectiveID) {

  if (DirectiveID.getString() == ".ent") {
    // ignore this directive for now
    Parser.Lex();
    return false;
  }

  if (DirectiveID.getString() == ".end") {
    // ignore this directive for now
    Parser.Lex();
    return false;
  }

  if (DirectiveID.getString() == ".frame") {
    // ignore this directive for now
    Parser.eatToEndOfStatement();
    return false;
  }

  if (DirectiveID.getString() == ".set") {
    return parseDirectiveSet();
  }

  if (DirectiveID.getString() == ".fmask") {
    // ignore this directive for now
    Parser.eatToEndOfStatement();
    return false;
  }

  if (DirectiveID.getString() == ".mask") {
    // ignore this directive for now
    Parser.eatToEndOfStatement();
    return false;
  }

  if (DirectiveID.getString() == ".gpword") {
    // ignore this directive for now
    Parser.eatToEndOfStatement();
    return false;
  }

  return true;
}

extern "C" void LLVMInitializeCpu0AsmParser() {
  RegisterMCAsmParser<Cpu0AsmParser> X(TheCpu0Target);
  RegisterMCAsmParser<Cpu0AsmParser> Y(TheCpu0elTarget);
}

#define GET_REGISTER_MATCHER
#define GET_MATCHER_IMPLEMENTATION
#include "Cpu0GenAsmMatcher.inc"

#else // #if CH >= CH11_1
extern "C" void LLVMInitializeCpu0AsmParser() {}
#endif

lbdex/chapters/Chapter11_1/AsmParser/CMakeLists.txt

add_llvm_component_library(LLVMCpu0AsmParser
  Cpu0AsmParser.cpp

  LINK_COMPONENTS
  MC
  MCParser
  Cpu0Desc
  Cpu0Info
  Support

  ADD_TO_COMPONENT
  Cpu0
  )

The Cpu0AsmParser.cpp contains one thousand lines of code which do the assembly language parsing. You can understand it with a little patience only. To let files in directory of AsmParser be built, modify CMakeLists.txt as follows,

lbdex/chapters/Chapter11_1/CMakeLists.txt

set(LLVM_TARGET_DEFINITIONS Cpu0Asm.td)
tablegen(LLVM Cpu0GenAsmMatcher.inc -gen-asm-matcher)
  Cpu0AsmParser
add_subdirectory(AsmParser)

lbdex/chapters/Chapter11_1/Cpu0Asm.td

//===-- Cpu0Asm.td - Describe the Cpu0 Target Machine ------*- tablegen -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This is the top level entry point for the Cpu0 target.
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
// Target-independent interfaces
//===----------------------------------------------------------------------===//

include "llvm/Target/Target.td"

//===----------------------------------------------------------------------===//
// Target-dependent interfaces
//===----------------------------------------------------------------------===//

include "Cpu0RegisterInfo.td"
include "Cpu0RegisterInfoGPROutForAsm.td"
include "Cpu0.td"

lbdex/chapters/Chapter11_1/Cpu0RegisterInfoGPROutForAsm.td


//===----------------------------------------------------------------------===//
// Register Classes
//===----------------------------------------------------------------------===//

def GPROut : RegisterClass<"Cpu0", [i32], 32, (add CPURegs)>;

The CMakeLists.txt adds code as above to generate Cpu0GenAsmMatcher.inc used by Cpu0AsmParser.cpp. Cpu0Asm.td include Cpu0RegisterInfoGPROutForAsm.td which define GPROut to CPURegs while Cpu0Other.td include Cpu0RegisterInfoGPROutForOther.td which define GPROut to CPURegs exclude SW. Cpu0Other.td is used when translating llvm IR to Cpu0 instruction. In latter case, the register SW is reserved for keeping the CPU status and not allowed to be allocated as a general purpose register. For example, if setting GPROut to include SW, when compile with C statement “a = (b & c);”, it may generate instruction “and $sw, $1, $2”, as a result that interrupt status in $sw will be destroyed. When programming in assembly, instruction “andi $sw, $sw, 0xffdf” is allowed. This assembly program is accepted and Cpu0 backend treats it safe since assembler programmer can disable trace debug message by “andi $sw, $sw, 0xffdf” and enable debug message by “ori $sw, $sw, 0x0020”. In addition, the interrupt bits can also be enabled or disabled by “ori” and “andi” instructions.

The EPC must set to CPURegs as follows, otherwise, MatchInstructionImpl() of MatchAndEmitInstruction() will return fail for “asm(“mfc0 $pc, $epc”);”.

lbdex/chapters/Chapter2/Cpu0RegisterInfo.td

def CPURegs : RegisterClass<"Cpu0", [i32], 32, (add
  ...
  , PC, EPC)>;

lbdex/chapters/Chapter11_1/Cpu0.td

def Cpu0AsmParser : AsmParser {
  let ShouldEmitMatchRegisterName = 0;
}

def Cpu0AsmParserVariant : AsmParserVariant {
  int Variant = 0;

  // Recognize hard coded registers.
  string RegisterPrefix = "$";
}
def Cpu0 : Target {
  ...
  let AssemblyParsers = [Cpu0AsmParser];
  let AssemblyParserVariants = [Cpu0AsmParserVariant];
}

lbdex/chapters/Chapter11_1/Cpu0InstrFormats.td

// Pseudo-instructions for alternate assembly syntax (never used by codegen).
// These are aliases that require C++ handling to convert to the target
// instruction, while InstAliases can be handled directly by tblgen.
class Cpu0AsmPseudoInst<dag outs, dag ins, string asmstr>:
  Cpu0Inst<outs, ins, asmstr, [], IIPseudo, Pseudo> {
  let isPseudo = 1;
  let Pattern = [];
}

lbdex/chapters/Chapter11_1/Cpu0InstrInfo.td

def Cpu0MemAsmOperand : AsmOperandClass {
  let Name = "Mem";
  let ParserMethod = "parseMemOperand";
}

// Address operand
def mem : Operand<i32> {
  ...
  let ParserMatchClass = Cpu0MemAsmOperand;
}
...
//===----------------------------------------------------------------------===//
// Pseudo Instruction definition
//===----------------------------------------------------------------------===//

let Predicates = [Ch11_1] in {
class LoadImm32< string instr_asm, Operand Od, RegisterClass RC> :
  Cpu0AsmPseudoInst<(outs RC:$ra), (ins Od:$imm32),
                     !strconcat(instr_asm, "\t$ra, $imm32")> ;
def LoadImm32Reg : LoadImm32<"li", shamt, GPROut>;

class LoadAddress<string instr_asm, Operand MemOpnd, RegisterClass RC> :
  Cpu0AsmPseudoInst<(outs RC:$ra), (ins MemOpnd:$addr),
                     !strconcat(instr_asm, "\t$ra, $addr")> ;
def LoadAddr32Reg : LoadAddress<"la", mem, GPROut>;

class LoadAddressImm<string instr_asm, Operand Od, RegisterClass RC> :
  Cpu0AsmPseudoInst<(outs RC:$ra), (ins Od:$imm32),
                     !strconcat(instr_asm, "\t$ra, $imm32")> ;
def LoadAddr32Imm : LoadAddressImm<"la", shamt, GPROut>;
}

Above Cpu0InstrInfo.td declare the let ParserMethod = “parseMemOperand” and implement the parseMemOperand() in Cpu0AsmParser.cpp to handle the “mem” operand which used in Cpu0 instructions ld and st. For example, ld $2, 4($sp), the mem operand is 4($sp). Accompany with “let ParserMatchClass = Cpu0MemAsmOperand;”, LLVM will call parseMemOperand() of Cpu0AsmParser.cpp when it meets the assembly mem operand 4($sp). With above “let” assignment, TableGen will generate the following structure and functions in Cpu0GenAsmMatcher.inc.

build/lib/Target/Cpu0/Cpu0GenAsmMatcher.inc

  enum OperandMatchResultTy {
    MatchOperand_Success,    // operand matched successfully
    MatchOperand_NoMatch,    // operand did not match
    MatchOperand_ParseFail   // operand matched but had errors
  };
  OperandMatchResultTy MatchOperandParserImpl(
    OperandVector &Operands,
    StringRef Mnemonic);
  OperandMatchResultTy tryCustomParseOperand(
    OperandVector &Operands,
    unsigned MCK);
...
Cpu0AsmParser::OperandMatchResultTy Cpu0AsmParser::
tryCustomParseOperand(OperandVector &Operands,
            unsigned MCK) {

  switch(MCK) {
  case MCK_Mem:
    return parseMemOperand(Operands);
  default:
    return MatchOperand_NoMatch;
  }
  return MatchOperand_NoMatch;
}

Cpu0AsmParser::OperandMatchResultTy Cpu0AsmParser::
MatchOperandParserImpl(OperandVector &Operands,
             StringRef Mnemonic) {
  ...
}

/// MatchClassKind - The kinds of classes which participate in
/// instruction matching.
enum MatchClassKind {
  ...
  MCK_Mem, // user defined class 'Cpu0MemAsmOperand'
  ...
};

Above three Pseudo Instruction definitions in Cpu0InstrInfo.td, such as LoadImm32Reg, are handled by Cpu0AsmParser.cpp as follows,

lbdex/chapters/Chapter11_1/AsmParser/Cpu0AsmParser.cpp

bool Cpu0AsmParser::needsExpansion(MCInst &Inst) {

  switch(Inst.getOpcode()) {
    case Cpu0::LoadImm32Reg:
    case Cpu0::LoadAddr32Imm:
    case Cpu0::LoadAddr32Reg:
      return true;
    default:
      return false;
  }
}

void Cpu0AsmParser::expandInstruction(MCInst &Inst, SMLoc IDLoc,
                        SmallVectorImpl<MCInst> &Instructions){
  switch(Inst.getOpcode()) {
    case Cpu0::LoadImm32Reg:
      return expandLoadImm(Inst, IDLoc, Instructions);
    case Cpu0::LoadAddr32Imm:
      return expandLoadAddressImm(Inst,IDLoc,Instructions);
    case Cpu0::LoadAddr32Reg:
      return expandLoadAddressReg(Inst,IDLoc,Instructions);
    }
}
bool Cpu0AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                            OperandVector &Operands,
                                            MCStreamer &Out,
                                            uint64_t &ErrorInfo,
                                            bool MatchingInlineAsm) {
  printCpu0Operands(Operands);
  MCInst Inst;
  unsigned MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo,
                                              MatchingInlineAsm);
  switch (MatchResult) {
  default: break;
  case Match_Success: {
    if (needsExpansion(Inst)) {
      SmallVector<MCInst, 4> Instructions;
      expandInstruction(Inst, IDLoc, Instructions);
      for(unsigned i =0; i < Instructions.size(); i++){
        Out.emitInstruction(Instructions[i], getSTI());
      }
    } else {
        Inst.setLoc(IDLoc);
        Out.emitInstruction(Inst, getSTI());
      }
    return false;
  }
  ...
}

Finally, remind that the CPURegs as below must follow the order of register number because AsmParser uses them when do register number encoding.

lbdex/chapters/Chapter11_1/Cpu0RegisterInfo.td

//===----------------------------------------------------------------------===//
// The register string, such as "9" or "gp" will show on "llvm-objdump -d"
//@ All registers definition
let Namespace = "Cpu0" in {
  //@ General Purpose Registers
  def ZERO : Cpu0GPRReg<0,  "zero">, DwarfRegNum<[0]>;
  def AT   : Cpu0GPRReg<1,  "1">,    DwarfRegNum<[1]>;
  def V0   : Cpu0GPRReg<2,  "2">,    DwarfRegNum<[2]>;
  def V1   : Cpu0GPRReg<3,  "3">,    DwarfRegNum<[3]>;
  def A0   : Cpu0GPRReg<4,  "4">,    DwarfRegNum<[4]>;
  def A1   : Cpu0GPRReg<5,  "5">,    DwarfRegNum<[5]>;
  def T9   : Cpu0GPRReg<6,  "t9">,   DwarfRegNum<[6]>;
  def T0   : Cpu0GPRReg<7,  "7">,    DwarfRegNum<[7]>;
  def T1   : Cpu0GPRReg<8,  "8">,    DwarfRegNum<[8]>;
  def S0   : Cpu0GPRReg<9,  "9">,    DwarfRegNum<[9]>;
  def S1   : Cpu0GPRReg<10, "10">,   DwarfRegNum<[10]>;
  def GP   : Cpu0GPRReg<11, "gp">,   DwarfRegNum<[11]>;
  def FP   : Cpu0GPRReg<12, "fp">,   DwarfRegNum<[12]>;
  def SP   : Cpu0GPRReg<13, "sp">,   DwarfRegNum<[13]>;
  def LR   : Cpu0GPRReg<14, "lr">,   DwarfRegNum<[14]>;
  def SW   : Cpu0GPRReg<15, "sw">,   DwarfRegNum<[15]>;
//  def MAR  : Register< 16, "mar">,  DwarfRegNum<[16]>;
//  def MDR  : Register< 17, "mdr">,  DwarfRegNum<[17]>;

//#if CH >= CH4_1 1
  // Hi/Lo registers number and name
  def HI   : Cpu0Reg<0, "ac0">, DwarfRegNum<[18]>;
  def LO   : Cpu0Reg<0, "ac0">, DwarfRegNum<[19]>;
//#endif
  def PC   : Cpu0C0Reg<0, "pc">,  DwarfRegNum<[20]>;
  def EPC  : Cpu0C0Reg<1, "epc">, DwarfRegNum<[21]>;
}

//===----------------------------------------------------------------------===//
//@Register Classes
//===----------------------------------------------------------------------===//

def CPURegs : RegisterClass<"Cpu0", [i32], 32, (add
  // Reserved
  ZERO, AT, 
  // Return Values and Arguments
  V0, V1, A0, A1, 
  // Not preserved across procedure calls
  T9, T0, T1,
  // Callee save
  S0, S1,
  // Reserved
  GP, FP, 
  SP, LR, SW)>;

Run Chapter11_1/ with ch11_1.cpp to get the correct result as follows,

JonathantekiiMac:input Jonathan$ /Users/Jonathan/llvm/test/build/bin/llc
-march=cpu0 -relocation-model=pic -filetype=obj ch11_1.bc -o
ch11_1.cpu0.o
JonathantekiiMac:input Jonathan$ /Users/Jonathan/llvm/test/build/bin/
llvm-objdump -d ch11_1.cpu0.o

ch11_1.cpu0.o:  file format ELF32-unknown

Disassembly of section .text:
.text:
       0:     01 2d 00 08                                     ld      $2, 8($sp)
       4:     02 0d 00 04                                     st      $zero, 4($sp)
       8:     09 30 00 00                                     addiu   $3, $zero, 0
       c:     13 31 20 00                                     add     $3, $1, $2
      10:     14 32 30 00                                     sub     $3, $2, $3
      ...

The instructions cmp and jeg printed with explicit $sw displayed in assembly and disassembly. You can change the code in AsmParser and Dissassembly (the last chapter) to hide the $sw printed in these instructions (such as “jeq 20” rather than “jeq $sw, 20”).

Both AsmParser and Cpu0AsmParser inherited from MCAsmParser as follows,

llvm/lib/MC/MCParser/AsmParser.cpp

class AsmParser : public MCAsmParser {
  ...
}
...

AsmParser will call functions ParseInstruction() and MatchAndEmitInstruction() of Cpu0AsmParser as follows,

llvm/lib/MC/MCParser/AsmParser.cpp

bool AsmParser::parseStatement(ParseStatementInfo &Info) {
  ...
  // Directives start with "."
  if (IDVal[0] == '.' && IDVal != ".") {
    // First query the target-specific parser. It will return 'true' if it
    // isn't interested in this directive.
    if (!getTargetParser().ParseDirective(ID))
      return false;
    ...
  }
  ...
  bool HadError = getTargetParser().ParseInstruction(IInfo, OpcodeStr, IDLoc,
                                                     Info.ParsedOperands);
  ...
  // If parsing succeeded, match the instruction.
  if (!HadError) {
    unsigned ErrorInfo;
    getTargetParser().MatchAndEmitInstruction(IDLoc, Info.Opcode,
                                              Info.ParsedOperands, Out,
                                              ErrorInfo, ParsingInlineAsm);
  }
  ...
}

Assembler structure

Directory AsmParser handle the assembly to obj translation. The assembling Data Flow Diagram (DFD) as Fig. 52 and Fig. 53.

// Free usage license, author: Chung-Shu Chen 陳鍾樞
// dot -tPng asmDfd.gv -oasmDfd.png

digraph G {
  rankdir=LR;
  subgraph cluster_0 {
    style=filled;
//    label = "Assemble flow";
    node [style=filled,color=white]; user, asmParser, encoder, elfobj;
    user -> asmParser [ label = "cpu0 assembly" ];
    asmParser -> encoder [ label = "opcode ID & operand IDs" ];
    encoder -> elfobj [ label = "binary" ];
    color=lightgrey
  }
}

Fig. 52 Assembly flow

// Free usage license, author: Chung-Shu Chen 陳鍾樞
// dot -Tpng asmDfdEx.gv -oasmDfdEx.png

digraph G {
  rankdir=LR;
  subgraph cluster_2 {
    style=filled;
//    label = "Assemble flow, for instance: add $v1, $v0, $at";
    subgraph clusterA {
      label = "asmParser";
      node [style=filled,color=white]; ParseInstruction [label="ParseInstruction()"];
      node [style=filled,color=white]; MatchAndEmitInstruction [label="MatchAndEmitInstruction()"];
      ParseInstruction -> MatchAndEmitInstruction [ label = "Operands:\n (Cpu0::ADD, Cpu0::V1,\n Cpu0::AT, Cpu0::V0)" ];
    }
    subgraph clusterB {
      label = "encoder: Cpu0MCCodeEmitter.cpp";
      node [style=filled,color=white]; encodeInstruction [label="encodeInstruction()"];
    }
    MatchAndEmitInstruction -> encodeInstruction [ label = "Inst.Opcode=\nCpu0::ADD,\nInst.Operand[0] = V1,\nInst.Operand[1] = AT,\nInst.Operand[2] = V0" ];
    color=lightgrey
  }
}

Fig. 53 Assembly flow, for instance: add $v1, $v0, $at

Given an example of assembly instruction “add $v1, $v0, $at”, llvm AsmParser kernel call backend ParseInstruction() of Cpu0AsmParser.cpp when it parses and recognises that the first token at the beginning of line is identifier. ParseInstruction() parses one assembly instruction, creates Operands and return to llvm AsmParser. Then AsmParser calls backend MatchAndEmitInstruction() to set Opcode and Operands to MCInst, then encoder can encode binary instruction from MCInst with the information come from Cpu0InstrInfo.td which includes binary value for Opcode ID and Operand IDs of the instruction.

List the key functions and data structure of MatchAndEmitInstruction() and encodeInstruction(), explaining in comments which begin with ///.

llvm/build/lib/Target/Cpu0/Cpu0GenAsmMatcher.inc

enum InstructionConversionKind {
  Convert__Reg1_0__Reg1_1__Reg1_2,
  Convert__Reg1_0__Reg1_1__Imm1_2,
  ...
  CVT_NUM_SIGNATURES
};

} // end anonymous namespace

  struct MatchEntry {
    uint16_t Mnemonic;
    uint16_t Opcode;
    uint8_t ConvertFn;
    uint32_t RequiredFeatures;
    uint8_t Classes[3];
    StringRef getMnemonic() const {
      return StringRef(MnemonicTable + Mnemonic + 1,
                       MnemonicTable[Mnemonic]);
    }
  };

static const MatchEntry MatchTable0[] = {
  { 0 /* add */, Cpu0::ADD, Convert__Reg1_0__Reg1_1__Reg1_2, 0, { MCK_CPURegs, MCK_CPURegs, MCK_CPURegs }, },
  { 4 /* addiu */, Cpu0::ADDiu, Convert__Reg1_0__Reg1_1__Imm1_2, 0, { MCK_CPURegs, MCK_CPURegs, MCK_Imm }, },
  ...
};

unsigned Cpu0AsmParser::
MatchInstructionImpl(const OperandVector &Operands,
                     MCInst &Inst, uint64_t &ErrorInfo,
                     bool matchingInlineAsm, unsigned VariantID) {
  ...
  // Find the appropriate table for this asm variant.
  const MatchEntry *Start, *End;
  switch (VariantID) {
  default: llvm_unreachable("invalid variant!");
  case 0: Start = std::begin(MatchTable0); End = std::end(MatchTable0); break;
  }
  // Search the table.
  auto MnemonicRange = std::equal_range(Start, End, Mnemonic, LessOpcode());
  ...
  for (const MatchEntry *it = MnemonicRange.first, *ie = MnemonicRange.second;
       it != ie; ++it) {
    ...
    // We have selected a definite instruction, convert the parsed
    // operands into the appropriate MCInst.

    /// For instance ADD , V1, AT, V0
    /// MnemonicRange.first = &MatchTable0[0]
    /// MnemonicRange.second = &MatchTable0[1]
    /// it.ConvertFn = Convert__Reg1_0__Reg1_1__Reg1_2

    convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands);
    ...
  }
  ...
}

static const uint8_t ConversionTable[CVT_NUM_SIGNATURES][9] = {
  // Convert__Reg1_0__Reg1_1__Reg1_2
  { CVT_95_Reg, 1, CVT_95_Reg, 2, CVT_95_Reg, 3, CVT_Done },
  // Convert__Reg1_0__Reg1_1__Imm1_2
  { CVT_95_Reg, 1, CVT_95_Reg, 2, CVT_95_addImmOperands, 3, CVT_Done },
  ...
};

/// When kind = Convert__Reg1_0__Reg1_1__Reg1_2, ConversionTable[Kind] is equal to CVT_95_Reg
/// For Operands[1], Operands[2], Operands[3] do the following:
///   static_cast<Cpu0Operand&>(*Operands[OpIdx]).addRegOperands(Inst, 1);
/// Since p = 0, 2, 4, then OpIdx = 1, 2, 3 when OpIdx=*(p+1).
/// Since, Operands[1] = V1, Operands[2] = AT, Operands[3] = V0,
///   for "ADD , V1, AT, V0" which created by ParseInstruction().
/// Inst.Opcode = ADD, Inst.Operand[0] = V1, Inst.Operand[1] = AT,
///   Inst.Operand[2] = V0.
void Cpu0AsmParser::
convertToMCInst(unsigned Kind, MCInst &Inst, unsigned Opcode,
                const OperandVector &Operands) {
  assert(Kind < CVT_NUM_SIGNATURES && "Invalid signature!");
  const uint8_t *Converter = ConversionTable[Kind];
  unsigned OpIdx;
  Inst.setOpcode(Opcode);
  for (const uint8_t *p = Converter; *p; p+= 2) {
    OpIdx = *(p + 1);
    switch (*p) {
    default: llvm_unreachable("invalid conversion entry!");
    case CVT_Reg:
      static_cast<Cpu0Operand&>(*Operands[OpIdx]).addRegOperands(Inst, 1);
      break;
    ...
    }
  }
}

lbdex/chapters/Chapter11_1/AsmParser/Cpu0AsmParser.cpp

/// For "ADD , V1, AT, V0", ParseInstruction() set Operands[1].Reg.RegNum = V1,
///   Operands[2].Reg.RegNum = AT, ..., by Cpu0Operand::CreateReg(RegNo, S,
///   Parser.getTok().getLoc()) in calling ParseOperand().
/// So, after (*Operands[1..3]).addRegOperands(Inst, 1),
///   Inst.Opcode = ADD, Inst.Operand[0] = V1, Inst.Operand[1] = AT,
///   Inst.Operand[2] = V0.
class Cpu0Operand : public MCParsedAsmOperand {
  ...
  void addRegOperands(MCInst &Inst, unsigned N) const {
    assert(N == 1 && "Invalid number of operands!");
    Inst.addOperand(MCOperand::createReg(getReg()));
  }
  ...
  unsigned getReg() const override {
    assert((Kind == k_Register) && "Invalid access!");
    return Reg.RegNum;
  }
  ...
}

lbdex/chapters/Chapter11_1/MCTargetDesc/Cpu0MCCodeEmitter.cpp

void Cpu0MCCodeEmitter::
encodeInstruction(const MCInst &MI, raw_ostream &OS,
                  SmallVectorImpl<MCFixup> &Fixups,
                  const MCSubtargetInfo &STI) const
{
  uint32_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
  ...
  EmitInstruction(Binary, Size, OS);
}

llvm/build/lib/Target/Cpu0/Cpu0GenMCCodeEmitter.inc

uint64_t Cpu0MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,
    SmallVectorImpl<MCFixup> &Fixups,
    const MCSubtargetInfo &STI) const {
  static const uint64_t InstBits[] = {
    ...
    UINT64_C(318767104),      // ADD  /// 318767104=0x13000000
    ...
  };
  ...

  const unsigned opcode = MI.getOpcode();
  ...
  switch (opcode) {
    case Cpu0::ADD:
    ...
      // op: ra
      op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI);
      Value |= (op & UINT64_C(15)) << 20;
      // op: rb
      op = getMachineOpValue(MI, MI.getOperand(1), Fixups, STI);
      Value |= (op & UINT64_C(15)) << 16;
      // op: rc
      op = getMachineOpValue(MI, MI.getOperand(2), Fixups, STI);
      Value |= (op & UINT64_C(15)) << 12;
      break;
    }
    ...
  }
  return Value;
}
// Free usage license, author: Chung-Shu Chen 陳鍾樞
// dot -Tpng asmDfdEx2.gv -oasmDfdEx2.png

digraph G {
  rankdir=LR;
  subgraph cluster_2 {
    style=filled;
//    label = "Data flow in MatchAndEmitInstruction(), for instance: add $v1, $v0, $at";
    subgraph clusterA {
      label = "MatchAndEmitInstruction()";
      node [style=filled,color=white]; MatchTable0 [label="Start = std::\nbegin(MatchTable0);\nEnd = std::end\n(MatchTable0);"];
      node [style=filled,color=white]; equal_range [label="std::equal_range(Start, End, \nMnemonic, LessOpcode());"];
      node [style=filled,color=white]; convertToMCInst [label="convertToMCInst\n(Kind, ...)"];
      MatchTable0 -> equal_range [ label = "Start,\nEnd" ];
      equal_range -> convertToMCInst [ label = "Kind=\nConvert__Reg1_0__\nReg1_1__Reg1_2" ];
    }
    color=lightgrey
  }
}

Fig. 54 Data flow in MatchAndEmitInstruction(), for instance: add $v1, $v0, $at”

// Free usage license, author: Chung-Shu Chen 陳鍾樞
// dot -Tpng asmDfdEx3.gv -oasmDfdEx3.png

digraph G {
  rankdir=LR;
  subgraph cluster_2 {
    style=filled;
//    label = "Data flow in and between MatchAndEmitInstruction() and encodeInstruction(), for instance: add $v1, $v0, $at";
    subgraph clusterA {
      label = "MatchAndEmitInstruction()";
      node [style=filled,color=white]; convertToMCInst [label="convertToMCInst()"];
    }
    subgraph clusterB {
      label = "encodeInstruction()";
      node [style=filled,color=white]; getBinaryCodeForInstr [label="getBinaryCodeForInstr()"];
      node [style=filled,color=white]; EmitInstruction [label="EmitInstruction()"];
      getBinaryCodeForInstr -> EmitInstruction [ label = "Binary" ];
    }
    convertToMCInst -> getBinaryCodeForInstr [ label = "Inst.Opcode = ADD,\nInst.Operand[0] = V1,\nInst.Operand[1] = AT,\nInst.Operand[2] = V0" ];
    color=lightgrey
  }
}

Fig. 55 Data flow between MatchAndEmitInstruction() and encodeInstruction(), for instance: add $v1, $v0, $at

MatchTable0 include all the possibile combinations of opcode and operands type. Even the assembly instruction of user input may pass Cpu0AsmParser in syntax check, the MatchAndEmitInstruction() still can be fail. For example, instruction “asm(“move $3, $2);” can pass but “asm(“move $3, $2, $1”);” will fail.

List flow of calling functions for Cpu0AsmParser as Fig. 56.

digraph G {
  rankdir=TB;
  "parseStatement()" -> "ParseInstruction()" [label="1"];
  "parseStatement()" -> "MatchAndEmitInstruction()" [label="2"];
  "MatchAndEmitInstruction()" -> "MatchInstructionImpl()";
  "ParseInstruction()" -> "ParseOperand()";
  "ParseOperand()" -> "MatchOperandParserImpl()";
  "MatchAndEmitInstruction()" -> "MCObjectStreamer::emitInstruction()";
  subgraph clusterAsm {
    label = "/lib/MC/AsmParser.cpp";
    "parseStatement()";
  }
  subgraph clusterCpu0Asm {
    label = "Cpu0AsmParser.cpp";
    "MatchAndEmitInstruction()";
    "ParseOperand()";
    "ParseInstruction()";
  }
  subgraph clusterAsmParserInc {
    label = "Cpu0GenAsmMatcher.inc";
    "MatchInstructionImpl()";
    "MatchOperandParserImpl()";
    "convertToMapAndConstraints()";
    "tryCustomParseOperand()";
    "MatchInstructionImpl()" -> "convertToMapAndConstraints()";
    "MatchOperandParserImpl()" -> "tryCustomParseOperand()";
  }
  subgraph clusterObj {
    label = "lib/MC/MCObjectStreamer.cpp";
    "MCObjectStreamer::emitInstruction()";
  }
}

Fig. 56 Flow of calling functions for Cpu0AsmParser.

  • After ParseInstruction() and MatchAndEmitInstruction() will produce class MCInst.

    • In MatchAndEmitInstruction(), assembly will call MCObjectStreamer::emitInstruction() for encoding to binary, reference Fig. 32.

  • Run llc with option “-debug” or “-debug-only=asm-matcher” will show debug message to check the flow of assembler as follows,

input % ~/llvm/test/build/bin/clang -target mips-unknown-linux-gnu -c
ch11_1.cpp -emit-llvm -o ch11_1.bc
input % ~/llvm/test/build/bin/llc -march=cpu0 -relocation-model=pic
-filetype=obj -debug-only=asm-matcher ch11_1.bc -o ch11_1.cpu0.o

AsmMatcher: found 1 encodings with mnemonic 'ld'
Trying to match opcode LD
  Matching formal operand class MCK_CPURegs against actual operand at index 1
  (Register<19>): match success using generic matcher
  Matching formal operand class MCK_Mem against actual operand at index 2
  (Mem<9, 8>): match success using generic matcher
  Matching formal operand class InvalidMatchClass against actual operand at
  index 3: actual operand index out of range Opcode result: complete match,
  selecting this opcode

The other functions in Cpu0AsmParser called as follows,

  • ParseDirective() -> parseDirectiveSet() -> parseSetReorderDirective(), parseSetNoReorderDirective(), parseSetMacroDirective(), parseSetNoMacroDirective() -> reportParseError()

  • ParseInstruction() -> ParseOperand() -> MatchOperandParserImpl() of Cpu0GenAsmMatcher.inc -> tryCustomParseOperand() of Cpu0GenAsmMatcher.inc -> parseMemOperand() -> parseMemOffset(), tryParseRegisterOperand()

  • MatchAndEmitInstruction() -> MatchInstructionImpl() of Cpu0GenAsmMatcher.inc, needsExpansion(), expandInstruction()

  • parseMemOffset() -> parseRelocOperand() -> getVariantKind()

  • tryParseRegisterOperand() -> tryParseRegister() -> matchRegisterName() -> getReg()), matchRegisterByNumber()

  • expandInstruction() -> expandLoadImm(), expandLoadAddressImm(), expandLoadAddressReg() -> EmitInstruction() of Cpu0AsmPrint.cpp

Inline assembly

Run Chapter11_1 with ch11_2 will get the following error.

lbdex/input/ch11_2.cpp

extern "C" int printf(const char *format, ...);
int inlineasm_addu(void)
{
  int foo = 10;
  const int bar = 15;

//  call i32 asm sideeffect "addu $0,$1,$2", "=r,r,r"(i32 %1, i32 %2) #1, !srcloc !1
  __asm__ __volatile__("addu %0,%1,%2"
                       :"=r"(foo) // 5
                       :"r"(foo), "r"(bar)
                       );

  return foo;
}

int inlineasm_longlong(void)
{
  int a, b;
  const long long bar = 0x0000000500000006;
  int* p = (int*)&bar;
//  int* q = (p+1); // Do not set q here.

//  call i32 asm sideeffect "ld $0,$1", "=r,*m"(i32* %2) #2, !srcloc !2
  __asm__ __volatile__("ld %0,%1"
                       :"=r"(a) // 0x700070007000700b
                       :"m"(*p)
                       );
  int* q = (p+1); // Set q just before inline asm refer to avoid register clobbered. 
//  call i32 asm sideeffect "ld $0,$1", "=r,*m"(i32* %6) #2, !srcloc !3
  __asm__ __volatile__("ld %0,%1"
                       :"=r"(b) // 11
                       :"m"(*q)
//              Or use :"m"(*(p+1)) to avoid register clobbered. 
                       );

  return (a+b);
}

int inlineasm_constraint(void)
{
  int foo = 10;
  const int n_5 = -5;
  const int n5 = 5;
  const int n0 = 0;
  const unsigned int un5 = 5;
  const int n65536 = 0x10000;
  const int n_65531 = -65531;

//   call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,I"(i32 %1, i32 15) #1, !srcloc !2
  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 15
                       :"r"(foo), "I"(n_5)
                       );

  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 15
                       :"r"(foo), "J"(n0)
                       );

  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 10
                       :"r"(foo), "K"(n5)
                       );

  __asm__ __volatile__("ori %0,%1,%2"
                       :"=r"(foo) // 10
                       :"r"(foo), "L"(n65536) // 0x10000 = 65536
                       );

  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 15
                       :"r"(foo), "N"(n_65531)
                       );

  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 10
                       :"r"(foo), "O"(n_5)
                       );

  __asm__ __volatile__("addiu %0,%1,%2"
                       :"=r"(foo) // 15
                       :"r"(foo), "P"(un5)
                       );

  return foo;
}

int inlineasm_arg(int u, int v)
{
  int w;

  __asm__ __volatile__("subu %0,%1,%2"
                       :"=r"(w)
                       :"r"(u), "r"(v)
                       );

  return w;
}

int g[3] = {1,2,3};

int inlineasm_global()
{
  int c, d;
  __asm__ __volatile__("ld %0,%1"
                       :"=r"(c) // c=3
                       :"m"(g[2])
                       );

  __asm__ __volatile__("addiu %0,%1,1"
                       :"=r"(d) // d=4
                       :"r"(c)
                       );

  return d;
}

#ifdef TESTSOFTFLOATLIB
// test_float() will call soft float library
int inlineasm_float()
{
  float a = 2.2;
  float b = 3.3;
  
  int c = (int)(a + b);

  int d;
  __asm__ __volatile__("addiu %0,%1,1"
                       :"=r"(d)
                       :"r"(c)
                       );

  return d;
}
#endif

int test_inlineasm()
{
  int a, b, c, d, e, f;

  a = inlineasm_addu(); // 25
  b = inlineasm_longlong(); // 11
  c = inlineasm_constraint(); // 15
  d = inlineasm_arg(1, 10); // -9
  e = inlineasm_arg(6, 3); // 3
  __asm__ __volatile__("addiu %0,%1,1"
                       :"=r"(f) // e=4
                       :"r"(e)
                       );

  return (a+b+c+d+e+f); // 25+11+15-9+3+4=49
}

1-160-129-73:input Jonathan$ ~/llvm/test/build/bin/llc
-march=cpu0 -relocation-model=static -filetype=asm ch11_2.bc -o -
  .section .mdebug.abi32
  .previous
  .file "ch11_2.bc"
error: couldn't allocate output register for constraint 'r'

The ch11_2.cpp is a inline assembly example. The clang supports inline assembly like gcc. The inline assembly used in C/C++ when program need to access the specific allocated register or memory for the C/C++ variable. For example, the variable foo of ch11_2.cpp may be allocated by compiler to register $2, $3 or any other register. The inline assembly fills the gap between high level language and assembly language. Reference here [2]. Chapter11_2 supports inline assembly as follows,

lbdex/chapters/Chapter11_2/Cpu0AsmPrinter.h

  bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
                       const char *ExtraCode, raw_ostream &O) override;
  bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
                             const char *ExtraCode, raw_ostream &O) override;
  void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O);

lbdex/chapters/Chapter11_2/Cpu0AsmPrinter.cpp

// Print out an operand for an inline asm expression.
bool Cpu0AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
                                     const char *ExtraCode, raw_ostream &O) {
  // Does this asm operand have a single letter operand modifier?
  if (ExtraCode && ExtraCode[0]) {
    if (ExtraCode[1] != 0) return true; // Unknown modifier.

    const MachineOperand &MO = MI->getOperand(OpNum);
    switch (ExtraCode[0]) {
    default:
      // See if this is a generic print operand
      return AsmPrinter::PrintAsmOperand(MI,OpNum, ExtraCode,O);
    case 'X': // hex const int
      if ((MO.getType()) != MachineOperand::MO_Immediate)
        return true;
      O << "0x" << StringRef(utohexstr(MO.getImm())).lower();
      return false;
    case 'x': // hex const int (low 16 bits)
      if ((MO.getType()) != MachineOperand::MO_Immediate)
        return true;
      O << "0x" << StringRef(utohexstr(MO.getImm() & 0xffff)).lower();
      return false;
    case 'd': // decimal const int
      if ((MO.getType()) != MachineOperand::MO_Immediate)
        return true;
      O << MO.getImm();
      return false;
    case 'm': // decimal const int minus 1
      if ((MO.getType()) != MachineOperand::MO_Immediate)
        return true;
      O << MO.getImm() - 1;
      return false;
    case 'z': {
      // $0 if zero, regular printing otherwise
      if (MO.getType() != MachineOperand::MO_Immediate)
        return true;
      int64_t Val = MO.getImm();
      if (Val)
        O << Val;
      else
        O << "$0";
      return false;
    }
    }
  }

  printOperand(MI, OpNum, O);
  return false;
}

bool Cpu0AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
                                           unsigned OpNum,
                                           const char *ExtraCode,
                                           raw_ostream &O) {
  int Offset = 0;
  // Currently we are expecting either no ExtraCode or 'D'
  if (ExtraCode) {
    return true; // Unknown modifier.
  }

  const MachineOperand &MO = MI->getOperand(OpNum);
  assert(MO.isReg() && "unexpected inline asm memory operand");
  O << Offset << "($" << Cpu0InstPrinter::getRegisterName(MO.getReg()) << ")";

  return false;
}

void Cpu0AsmPrinter::printOperand(const MachineInstr *MI, int opNum,
                                  raw_ostream &O) {
  const MachineOperand &MO = MI->getOperand(opNum);
  bool closeP = false;

  if (MO.getTargetFlags())
    closeP = true;

  switch(MO.getTargetFlags()) {
  case Cpu0II::MO_GPREL:    O << "%gp_rel("; break;
  case Cpu0II::MO_GOT_CALL: O << "%call16("; break;
  case Cpu0II::MO_GOT:      O << "%got(";    break;
  case Cpu0II::MO_ABS_HI:   O << "%hi(";     break;
  case Cpu0II::MO_ABS_LO:   O << "%lo(";     break;
  case Cpu0II::MO_GOT_HI16: O << "%got_hi16("; break;
  case Cpu0II::MO_GOT_LO16: O << "%got_lo16("; break;
  }

  switch (MO.getType()) {
    case MachineOperand::MO_Register:
      O << '$'
        << StringRef(Cpu0InstPrinter::getRegisterName(MO.getReg())).lower();
      break;

    case MachineOperand::MO_Immediate:
      O << MO.getImm();
      break;

    case MachineOperand::MO_MachineBasicBlock:
      O << *MO.getMBB()->getSymbol();
      return;

    case MachineOperand::MO_GlobalAddress:
      O << *getSymbol(MO.getGlobal());
      break;

    case MachineOperand::MO_BlockAddress: {
      MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
      O << BA->getName();
      break;
    }

    case MachineOperand::MO_ExternalSymbol:
      O << *GetExternalSymbolSymbol(MO.getSymbolName());
      break;

    case MachineOperand::MO_JumpTableIndex:
      O << MAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
        << '_' << MO.getIndex();
      break;

    case MachineOperand::MO_ConstantPoolIndex:
      O << MAI->getPrivateGlobalPrefix() << "CPI"
        << getFunctionNumber() << "_" << MO.getIndex();
      if (MO.getOffset())
        O << "+" << MO.getOffset();
      break;

    default:
      llvm_unreachable("<unknown operand type>");
  }

  if (closeP) O << ")";
}

lbdex/chapters/Chapter11_2/Cpu0InstrInfo.cpp

/// Return the number of bytes of code the specified instruction may be.
unsigned Cpu0InstrInfo::GetInstSizeInBytes(const MachineInstr &MI) const {
  case  TargetOpcode::INLINEASM: {       // Inline Asm: Variable size.
    const MachineFunction *MF = MI.getParent()->getParent();
    const char *AsmStr = MI.getOperand(0).getSymbolName();
    return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo());
  }

lbdex/chapters/Chapter11_2/Cpu0ISelDAGToDAG.h

  bool SelectInlineAsmMemoryOperand(const SDValue &Op,
                                    unsigned ConstraintID,
                                    std::vector<SDValue> &OutOps) override;

lbdex/chapters/Chapter11_2/Cpu0ISelDAGToDAG.cpp

// inlineasm begin
bool Cpu0DAGToDAGISel::
SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
                             std::vector<SDValue> &OutOps) {
  // All memory constraints can at least accept raw pointers.
  switch(ConstraintID) {
  default:
    llvm_unreachable("Unexpected asm memory constraint");
  case InlineAsm::Constraint_m:
    OutOps.push_back(Op);
    return false;
  }
  return true;
}
// inlineasm end

lbdex/chapters/Chapter11_2/Cpu0ISelLowering.h

    // Inline asm support
    ConstraintType getConstraintType(StringRef Constraint) const override;

    /// Examine constraint string and operand type and determine a weight value.
    /// The operand object must already have been set up with the operand type.
    ConstraintWeight getSingleConstraintMatchWeight(
      AsmOperandInfo &info, const char *constraint) const override;

    /// This function parses registers that appear in inline-asm constraints.
    /// It returns pair (0, 0) on failure.
    std::pair<unsigned, const TargetRegisterClass *>
    parseRegForInlineAsmConstraint(const StringRef &C, MVT VT) const;

    std::pair<unsigned, const TargetRegisterClass *>
    getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
                                 StringRef Constraint, MVT VT) const override;

    /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops
    /// vector.  If it is invalid, don't add anything to Ops. If hasMemory is
    /// true it means one of the asm constraint of the inline asm instruction
    /// being processed is 'm'.
    void LowerAsmOperandForConstraint(SDValue Op,
                                      std::string &Constraint,
                                      std::vector<SDValue> &Ops,
                                      SelectionDAG &DAG) const override;

    bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM,
                               Type *Ty, unsigned AS,
                               Instruction *I = nullptr) const override;

lbdex/chapters/Chapter11_2/Cpu0ISelLowering.cpp

//===----------------------------------------------------------------------===//
//                           Cpu0 Inline Assembly Support
//===----------------------------------------------------------------------===//

/// getConstraintType - Given a constraint letter, return the type of
/// constraint it is for this target.
Cpu0TargetLowering::ConstraintType 
Cpu0TargetLowering::getConstraintType(StringRef Constraint) const
{
  // Cpu0 specific constraints
  // GCC config/mips/constraints.md
  // 'c' : A register suitable for use in an indirect
  //       jump. This will always be $t9 for -mabicalls.
  if (Constraint.size() == 1) {
    switch (Constraint[0]) {
      default : break;
      case 'c':
        return C_RegisterClass;
      case 'R':
        return C_Memory;
    }
  }
  return TargetLowering::getConstraintType(Constraint);
}

/// Examine constraint type and operand type and determine a weight value.
/// This object must already have been set up with the operand type
/// and the current alternative constraint selected.
TargetLowering::ConstraintWeight
Cpu0TargetLowering::getSingleConstraintMatchWeight(
    AsmOperandInfo &info, const char *constraint) const {
  ConstraintWeight weight = CW_Invalid;
  Value *CallOperandVal = info.CallOperandVal;
    // If we don't have a value, we can't do a match,
    // but allow it at the lowest weight.
  if (!CallOperandVal)
    return CW_Default;
  Type *type = CallOperandVal->getType();
  // Look at the constraint type.
  switch (*constraint) {
  default:
    weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint);
    break;
  case 'c': // $t9 for indirect jumps
    if (type->isIntegerTy())
      weight = CW_SpecificReg;
    break;
  case 'I': // signed 16 bit immediate
  case 'J': // integer zero
  case 'K': // unsigned 16 bit immediate
  case 'L': // signed 32 bit immediate where lower 16 bits are 0
  case 'N': // immediate in the range of -65535 to -1 (inclusive)
  case 'O': // signed 15 bit immediate (+- 16383)
  case 'P': // immediate in the range of 65535 to 1 (inclusive)
    if (isa<ConstantInt>(CallOperandVal))
      weight = CW_Constant;
    break;
  case 'R':
    weight = CW_Memory;
    break;
  }
  return weight;
}

/// This is a helper function to parse a physical register string and split it
/// into non-numeric and numeric parts (Prefix and Reg). The first boolean flag
/// that is returned indicates whether parsing was successful. The second flag
/// is true if the numeric part exists.
static std::pair<bool, bool>
parsePhysicalReg(const StringRef &C, std::string &Prefix,
                 unsigned long long &Reg) {
  if (C.front() != '{' || C.back() != '}')
    return std::make_pair(false, false);

  // Search for the first numeric character.
  StringRef::const_iterator I, B = C.begin() + 1, E = C.end() - 1;
  I = std::find_if(B, E, isdigit);

  Prefix.assign(B, I - B);

  // The second flag is set to false if no numeric characters were found.
  if (I == E)
    return std::make_pair(true, false);

  // Parse the numeric characters.
  return std::make_pair(!getAsUnsignedInteger(StringRef(I, E - I), 10, Reg),
                        true);
}

std::pair<unsigned, const TargetRegisterClass *> Cpu0TargetLowering::
parseRegForInlineAsmConstraint(const StringRef &C, MVT VT) const {
  const TargetRegisterClass *RC;
  std::string Prefix;
  unsigned long long Reg;

  std::pair<bool, bool> R = parsePhysicalReg(C, Prefix, Reg);

  if (!R.first)
    return std::make_pair(0U, nullptr);
  if (!R.second)
    return std::make_pair(0U, nullptr);

 // Parse $0-$15.
  assert(Prefix == "$");
  RC = getRegClassFor((VT == MVT::Other) ? MVT::i32 : VT);

  assert(Reg < RC->getNumRegs());
  return std::make_pair(*(RC->begin() + Reg), RC);
}

/// Given a register class constraint, like 'r', if this corresponds directly
/// to an LLVM register class, return a register of 0 and the register class
/// pointer.
std::pair<unsigned, const TargetRegisterClass *>
Cpu0TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
                                                 StringRef Constraint,
                                                 MVT VT) const
{
  if (Constraint.size() == 1) {
    switch (Constraint[0]) {
    case 'r':
      if (VT == MVT::i32 || VT == MVT::i16 || VT == MVT::i8) {
        return std::make_pair(0U, &Cpu0::CPURegsRegClass);
      }
      if (VT == MVT::i64)
        return std::make_pair(0U, &Cpu0::CPURegsRegClass);
      // This will generate an error message
      return std::make_pair(0u, static_cast<const TargetRegisterClass*>(0));
    case 'c': // register suitable for indirect jump
      if (VT == MVT::i32)
        return std::make_pair((unsigned)Cpu0::T9, &Cpu0::CPURegsRegClass);
      assert(0 && "Unexpected type.");
    }
  }

  std::pair<unsigned, const TargetRegisterClass *> R;
  R = parseRegForInlineAsmConstraint(Constraint, VT);

  if (R.second)
    return R;

  return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
}

/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops
/// vector.  If it is invalid, don't add anything to Ops.
void Cpu0TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
                                                     std::string &Constraint,
                                                     std::vector<SDValue>&Ops,
                                                     SelectionDAG &DAG) const {
  SDLoc DL(Op);
  SDValue Result;

  // Only support length 1 constraints for now.
  if (Constraint.length() > 1) return;

  char ConstraintLetter = Constraint[0];
  switch (ConstraintLetter) {
  default: break; // This will fall through to the generic implementation
  case 'I': // Signed 16 bit constant
    // If this fails, the parent routine will give an error
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getSExtValue();
      if (isInt<16>(Val)) {
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  case 'J': // integer zero
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getZExtValue();
      if (Val == 0) {
        Result = DAG.getTargetConstant(0, DL, Type);
        break;
      }
    }
    return;
  case 'K': // unsigned 16 bit immediate
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      uint64_t Val = (uint64_t)C->getZExtValue();
      if (isUInt<16>(Val)) {
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  case 'L': // signed 32 bit immediate where lower 16 bits are 0
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getSExtValue();
      if ((isInt<32>(Val)) && ((Val & 0xffff) == 0)){
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  case 'N': // immediate in the range of -65535 to -1 (inclusive)
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getSExtValue();
      if ((Val >= -65535) && (Val <= -1)) {
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  case 'O': // signed 15 bit immediate
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getSExtValue();
      if ((isInt<15>(Val))) {
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  case 'P': // immediate in the range of 1 to 65535 (inclusive)
    if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op)) {
      EVT Type = Op.getValueType();
      int64_t Val = C->getSExtValue();
      if ((Val <= 65535) && (Val >= 1)) {
        Result = DAG.getTargetConstant(Val, DL, Type);
        break;
      }
    }
    return;
  }

  if (Result.getNode()) {
    Ops.push_back(Result);
    return;
  }

  TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
}

bool Cpu0TargetLowering::isLegalAddressingMode(const DataLayout &DL,
                                               const AddrMode &AM, Type *Ty,
                                               unsigned AS, Instruction *I) const {
  // No global is ever allowed as a base.
  if (AM.BaseGV)
    return false;

  switch (AM.Scale) {
  case 0: // "r+i" or just "i", depending on HasBaseReg.
    break;
  case 1:
    if (!AM.HasBaseReg) // allow "r+i".
      break;
    return false; // disallow "r+r" or "r+r+i".
  default:
    return false;
  }

  return true;
}

Same with backend structure, the structure of inline assembly can be divided by file name as Table: the structure of inline assembly.

Table 38 inline assembly functions

File

Function

Cpu0ISelLowering.cpp

inline asm DAG node create

Cpu0ISelDAGToDAG.cpp

save OP code

Cpu0AsmPrinter.cpp,

inline asm instructions printing

Cpu0InstrInfo.cpp

Except Cpu0ISelDAGToDAG.cpp, the other functions are same with backend’s compile code. The Cpu0ISelLowering.cpp inline asm is explained after the result of running with ch11_2.cpp. Cpu0ISelDAGToDAG.cpp just save OP code in SelectInlineAsmMemoryOperand(). Since the the OP code is Cpu0 inline assembly instruction, no llvm IR DAG translation needed further, just save OP directly and return false to notify llvm system that Cpu0 backend has finished processing this inline assembly instruction.

Run Chapter11_2 with ch11_2.cpp will get the following result.

1-160-129-73:input Jonathan$ clang -target mips-unknown-linux-gnu -c
ch11_2.cpp -emit-llvm -o ch11_2.bc

1-160-129-73:input Jonathan$ ~/llvm/test/build/bin/
llvm-dis ch11_2.bc -o -
...
target triple = "mips-unknown-linux-gnu"

@g = global [3 x i32] [i32 1, i32 2, i32 3], align 4

; Function Attrs: nounwind
define i32 @_Z14inlineasm_adduv() #0 {
  %foo = alloca i32, align 4
  %bar = alloca i32, align 4
  store i32 10, i32* %foo, align 4
  store i32 15, i32* %bar, align 4
  %1 = load i32* %foo, align 4
  %2 = call i32 asm sideeffect "addu $0,$1,$2", "=r,r,r"(i32 %1, i32 15) #1,
  !srcloc !1
  store i32 %2, i32* %foo, align 4
  %3 = load i32* %foo, align 4
  ret i32 %3
}

; Function Attrs: nounwind
define i32 @_Z18inlineasm_longlongv() #0 {
  %a = alloca i32, align 4
  %b = alloca i32, align 4
  %bar = alloca i64, align 8
  %p = alloca i32*, align 4
  %q = alloca i32*, align 4
  store i64 21474836486, i64* %bar, align 8
  %1 = bitcast i64* %bar to i32*
  store i32* %1, i32** %p, align 4
  %2 = load i32** %p, align 4
  %3 = call i32 asm sideeffect "ld $0,$1", "=r,*m"(i32* %2) #1, !srcloc !2
  store i32 %3, i32* %a, align 4
  %4 = load i32** %p, align 4
  %5 = getelementptr inbounds i32* %4, i32 1
  store i32* %5, i32** %q, align 4
  %6 = load i32** %q, align 4
  %7 = call i32 asm sideeffect "ld $0,$1", "=r,*m"(i32* %6) #1, !srcloc !3
  store i32 %7, i32* %b, align 4
  %8 = load i32* %a, align 4
  %9 = load i32* %b, align 4
  %10 = add nsw i32 %8, %9
  ret i32 %10
}

; Function Attrs: nounwind
define i32 @_Z20inlineasm_constraintv() #0 {
  %foo = alloca i32, align 4
  %n_5 = alloca i32, align 4
  %n5 = alloca i32, align 4
  %n0 = alloca i32, align 4
  %un5 = alloca i32, align 4
  %n65536 = alloca i32, align 4
  %n_65531 = alloca i32, align 4
  store i32 10, i32* %foo, align 4
  store i32 -5, i32* %n_5, align 4
  store i32 5, i32* %n5, align 4
  store i32 0, i32* %n0, align 4
  store i32 5, i32* %un5, align 4
  store i32 65536, i32* %n65536, align 4
  store i32 -65531, i32* %n_65531, align 4
  %1 = load i32* %foo, align 4
  %2 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,I"(i32 %1, i32 -5) #1,
  !srcloc !4
  store i32 %2, i32* %foo, align 4
  %3 = load i32* %foo, align 4
  %4 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,J"(i32 %3, i32 0) #1,
  !srcloc !5
  store i32 %4, i32* %foo, align 4
  %5 = load i32* %foo, align 4
  %6 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,K"(i32 %5, i32 5) #1,
  !srcloc !6
  store i32 %6, i32* %foo, align 4
  %7 = load i32* %foo, align 4
  %8 = call i32 asm sideeffect "ori $0,$1,$2", "=r,r,L"(i32 %7, i32 65536) #1,
  !srcloc !7
  store i32 %8, i32* %foo, align 4
  %9 = load i32* %foo, align 4
  %10 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,N"(i32 %9, i32 -65531)
  #1, !srcloc !8
  store i32 %10, i32* %foo, align 4
  %11 = load i32* %foo, align 4
  %12 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,O"(i32 %11, i32 -5) #1,
  !srcloc !9
  store i32 %12, i32* %foo, align 4
  %13 = load i32* %foo, align 4
  %14 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,P"(i32 %13, i32 5) #1,
  !srcloc !10
  store i32 %14, i32* %foo, align 4
  %15 = load i32* %foo, align 4
  ret i32 %15
}

; Function Attrs: nounwind
define i32 @_Z13inlineasm_argii(i32 %u, i32 %v) #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %w = alloca i32, align 4
  store i32 %u, i32* %1, align 4
  store i32 %v, i32* %2, align 4
  %3 = load i32* %1, align 4
  %4 = load i32* %2, align 4
  %5 = call i32 asm sideeffect "subu $0,$1,$2", "=r,r,r"(i32 %3, i32 %4) #1,
  !srcloc !11
  store i32 %5, i32* %w, align 4
  %6 = load i32* %w, align 4
  ret i32 %6
}

; Function Attrs: nounwind
define i32 @_Z16inlineasm_globalv() #0 {
  %c = alloca i32, align 4
  %d = alloca i32, align 4
  %1 = call i32 asm sideeffect "ld $0,$1", "=r,*m"(i32* getelementptr inbounds
  ([3 x i32]* @g, i32 0, i32 2)) #1, !srcloc !12
  store i32 %1, i32* %c, align 4
  %2 = load i32* %c, align 4
  %3 = call i32 asm sideeffect "addiu $0,$1,1", "=r,r"(i32 %2) #1, !srcloc !13
  store i32 %3, i32* %d, align 4
  %4 = load i32* %d, align 4
  ret i32 %4
}

; Function Attrs: nounwind
define i32 @_Z14test_inlineasmv() #0 {
  %a = alloca i32, align 4
  %b = alloca i32, align 4
  %c = alloca i32, align 4
  %d = alloca i32, align 4
  %e = alloca i32, align 4
  %f = alloca i32, align 4
  %g = alloca i32, align 4
  %1 = call i32 @_Z14inlineasm_adduv()
  store i32 %1, i32* %a, align 4
  %2 = call i32 @_Z18inlineasm_longlongv()
  store i32 %2, i32* %b, align 4
  %3 = call i32 @_Z20inlineasm_constraintv()
  store i32 %3, i32* %c, align 4
  %4 = call i32 @_Z13inlineasm_argii(i32 1, i32 10)
  store i32 %4, i32* %d, align 4
  %5 = call i32 @_Z13inlineasm_argii(i32 6, i32 3)
  store i32 %5, i32* %e, align 4
  %6 = load i32* %e, align 4
  %7 = call i32 asm sideeffect "addiu $0,$1,1", "=r,r"(i32 %6) #1, !srcloc !14
  store i32 %7, i32* %f, align 4
  %8 = call i32 @_Z16inlineasm_globalv()
  store i32 %8, i32* %g, align 4
  %9 = load i32* %a, align 4
  %10 = load i32* %b, align 4
  %11 = add nsw i32 %9, %10
  %12 = load i32* %c, align 4
  %13 = add nsw i32 %11, %12
  %14 = load i32* %d, align 4
  %15 = add nsw i32 %13, %14
  %16 = load i32* %e, align 4
  %17 = add nsw i32 %15, %16
  %18 = load i32* %f, align 4
  %19 = add nsw i32 %17, %18
  %20 = load i32* %g, align 4
  %21 = add nsw i32 %19, %20
  ret i32 %21
}
...
1-160-129-73:input Jonathan$ ~/llvm/test/build/bin/llc
  -march=cpu0 -relocation-model=static -filetype=asm ch11_2.bc -o -
  .section .mdebug.abi32
  .previous
  .file "ch11_2.bc"
  .text
  .globl  _Z14inlineasm_adduv
  .align  2
  .type _Z14inlineasm_adduv,@function
  .ent  _Z14inlineasm_adduv     # @_Z14inlineasm_adduv
_Z14inlineasm_adduv:
  .frame  $fp,16,$lr
  .mask   0x00001000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -16
  st  $fp, 12($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  addiu $2, $zero, 10
  st  $2, 8($fp)
  addiu $2, $zero, 15
  st  $2, 4($fp)
  ld  $3, 8($fp)
  #APP
  addu $2,$3,$2
  #NO_APP
  st  $2, 8($fp)
  addu  $sp, $fp, $zero
  ld  $fp, 12($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 16
  ret $lr
  nop
  .set  macro
  .set  reorder
  .end  _Z14inlineasm_adduv
$tmp3:
  .size _Z14inlineasm_adduv, ($tmp3)-_Z14inlineasm_adduv

  .globl  _Z18inlineasm_longlongv
  .align  2
  .type _Z18inlineasm_longlongv,@function
  .ent  _Z18inlineasm_longlongv # @_Z18inlineasm_longlongv
_Z18inlineasm_longlongv:
  .frame  $fp,32,$lr
  .mask   0x00001000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -32
  st  $fp, 28($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  addiu $2, $zero, 6
  st  $2, 12($fp)
  addiu $2, $zero, 5
  st  $2, 8($fp)
  addiu $2, $fp, 8
  st  $2, 4($fp)
  #APP
  ld $2,0($2)
  #NO_APP
  st  $2, 24($fp)
  ld  $2, 4($fp)
  addiu $2, $2, 4
  st  $2, 0($fp)
  #APP
  ld $2,0($2)
  #NO_APP
  st  $2, 20($fp)
  ld  $3, 24($fp)
  addu  $2, $3, $2
  addu  $sp, $fp, $zero
  ld  $fp, 28($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 32
  ret $lr
  .set  macro
  .set  reorder
  .end  _Z18inlineasm_longlongv
$tmp7:
  .size _Z18inlineasm_longlongv, ($tmp7)-_Z18inlineasm_longlongv

  .globl  _Z20inlineasm_constraintv
  .align  2
  .type _Z20inlineasm_constraintv,@function
  .ent  _Z20inlineasm_constraintv # @_Z20inlineasm_constraintv
_Z20inlineasm_constraintv:
  .frame  $fp,32,$lr
  .mask   0x00001000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -32
  st  $fp, 28($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  addiu $2, $zero, 10
  st  $2, 24($fp)
  addiu $2, $zero, -5
  st  $2, 20($fp)
  addiu $2, $zero, 5
  st  $2, 16($fp)
  addiu $3, $zero, 0
  st  $3, 12($fp)
  st  $2, 8($fp)
  lui $2, 1
  st  $2, 4($fp)
  lui $2, 65535
  ori $2, $2, 5
  st  $2, 0($fp)
  ld  $2, 24($fp)
  #APP
  addiu $2,$2,-5
  #NO_APP
  st  $2, 24($fp)
  #APP
  addiu $2,$2,0
  #NO_APP
  st  $2, 24($fp)
  #APP
  addiu $2,$2,5
  #NO_APP
  st  $2, 24($fp)
  #APP
  ori $2,$2,65536
  #NO_APP
  st  $2, 24($fp)
  #APP
  addiu $2,$2,-65531
  #NO_APP
  st  $2, 24($fp)
  #APP
  addiu $2,$2,-5
  #NO_APP
  st  $2, 24($fp)
  #APP
  addiu $2,$2,5
  #NO_APP
  st  $2, 24($fp)
  addu  $sp, $fp, $zero
  ld  $fp, 28($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 32
  ret $lr
  nop
  .set  macro
  .set  reorder
  .end  _Z20inlineasm_constraintv
$tmp11:
  .size _Z20inlineasm_constraintv, ($tmp11)-_Z20inlineasm_constraintv

  .globl  _Z13inlineasm_argii
  .align  2
  .type _Z13inlineasm_argii,@function
  .ent  _Z13inlineasm_argii     # @_Z13inlineasm_argii
_Z13inlineasm_argii:
  .frame  $fp,16,$lr
  .mask   0x00001000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -16
  st  $fp, 12($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  ld  $2, 16($fp)
  st  $2, 8($fp)
  ld  $2, 20($fp)
  st  $2, 4($fp)
  ld  $3, 8($fp)
  #APP
  subu $2,$3,$2
  #NO_APP
  st  $2, 0($fp)
  addu  $sp, $fp, $zero
  ld  $fp, 12($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 16
  ret $lr
  nop
  .set  macro
  .set  reorder
  .end  _Z13inlineasm_argii
$tmp15:
  .size _Z13inlineasm_argii, ($tmp15)-_Z13inlineasm_argii

  .globl  _Z16inlineasm_globalv
  .align  2
  .type _Z16inlineasm_globalv,@function
  .ent  _Z16inlineasm_globalv   # @_Z16inlineasm_globalv
_Z16inlineasm_globalv:
  .frame  $fp,16,$lr
  .mask   0x00001000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -16
  st  $fp, 12($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  lui $2, %hi(g)
  ori $2, $2, %lo(g)
  addiu $2, $2, 8
  #APP
  ld $2,0($2)
  #NO_APP
  st  $2, 8($fp)
  #APP
  addiu $2,$2,1
  #NO_APP
  st  $2, 4($fp)
  addu  $sp, $fp, $zero
  ld  $fp, 12($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 16
  ret $lr
  nop
  .set  macro
  .set  reorder
  .end  _Z16inlineasm_globalv
$tmp19:
  .size _Z16inlineasm_globalv, ($tmp19)-_Z16inlineasm_globalv

  .globl  _Z14test_inlineasmv
  .align  2
  .type _Z14test_inlineasmv,@function
  .ent  _Z14test_inlineasmv     # @_Z14test_inlineasmv
_Z14test_inlineasmv:
  .frame  $fp,48,$lr
  .mask   0x00005000,-4
  .set  noreorder
  .set  nomacro
# BB#0:
  addiu $sp, $sp, -48
  st  $lr, 44($sp)            # 4-byte Folded Spill
  st  $fp, 40($sp)            # 4-byte Folded Spill
  addu  $fp, $sp, $zero
  jsub  _Z14inlineasm_adduv
  nop
  st  $2, 36($fp)
  jsub  _Z18inlineasm_longlongv
  nop
  st  $2, 32($fp)
  jsub  _Z20inlineasm_constraintv
  nop
  st  $2, 28($fp)
  addiu $2, $zero, 10
  st  $2, 4($sp)
  addiu $2, $zero, 1
  st  $2, 0($sp)
  jsub  _Z13inlineasm_argii
  nop
  st  $2, 24($fp)
  addiu $2, $zero, 3
  st  $2, 4($sp)
  addiu $2, $zero, 6
  st  $2, 0($sp)
  jsub  _Z13inlineasm_argii
  nop
  st  $2, 20($fp)
  #APP
  addiu $2,$2,1
  #NO_APP
  st  $2, 16($fp)
  jsub  _Z16inlineasm_globalv
  nop
  st  $2, 12($fp)
  ld  $3, 32($fp)
  ld  $4, 36($fp)
  addu  $3, $4, $3
  ld  $4, 28($fp)
  addu  $3, $3, $4
  ld  $4, 24($fp)
  addu  $3, $3, $4
  ld  $4, 20($fp)
  addu  $3, $3, $4
  ld  $4, 16($fp)
  addu  $3, $3, $4
  addu  $2, $3, $2
  addu  $sp, $fp, $zero
  ld  $fp, 40($sp)            # 4-byte Folded Reload
  ld  $lr, 44($sp)            # 4-byte Folded Reload
  addiu $sp, $sp, 48
  ret $lr
  nop
  .set  macro
  .set  reorder
  .end  _Z14test_inlineasmv
$tmp23:
  .size _Z14test_inlineasmv, ($tmp23)-_Z14test_inlineasmv

  .type g,@object               # @g
  .data
  .globl  g
  .align  2
g:
  .4byte  1                       # 0x1
  .4byte  2                       # 0x2
  .4byte  3                       # 0x3
  .size g, 12

Clang translates gcc style inline assembly __asm__ into llvm IR Inline Assembler Expressions first [3], then replace the variable registers of SSA form to physical registers during llc register allocation stage. From above example, functions LowerAsmOperandForConstraint() and getSingleConstraintMatchWeight() of Cpu0ISelLowering.cpp will create different range of const operand by I, J, K, L, N, O, or P, and register operand by r . For instance, the following __asm__ will create the llvm asm immediately after it.

__asm__ __volatile__("addiu %0,%1,%2"
                     :"=r"(foo) // 15
                     :"r"(foo), "I"(n_5)
                     );
%2 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,I"(i32 %1, i32 -5) #0, !srcloc !1
__asm__ __volatile__("addiu %0,%1,%2"
                     :"=r"(foo) // 15
                     :"r"(foo), "N"(n_65531)
                     );
%10 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,N"(i32 %9, i32 -65531) #0, !srcloc !5
__asm__ __volatile__("addiu %0,%1,%2"
                     :"=r"(foo) // 15
                     :"r"(foo), "P"(un5)
                     );
%14 = call i32 asm sideeffect "addiu $0,$1,$2", "=r,r,P"(i32 %13, i32 5) #0, !srcloc !7

The r in __asm__ will generate register, %1, in llvm IR asm while I in __asm__ will generate const operand, -5, in llvm IR asm. Remind, the LowerAsmOperandForConstraint() limit the range of positive or negative const operand value to 16 bits since FL type immediate operand is 16 bits in Cpu0 instruction. So, the range of N is -65535 to -1 and the range of P is 65535 to 1. For any value out of the range, the code in LowerAsmOperandForConstraint() will treat it as error since FL instruction format has limitation of 16 bits.