PseudoInstExpansion Pseudo result 'JIRL' operand count mismatch

environment: LLVM 10.0.0, Ubuntu 20.04

Input Command:
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug -DLLVM_USE_LINKER=lld -G "Ninja" ../llvm
ninja -j4

error info:

home/dyy/llvm-project-llvmorg-10.0.0/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td:175:1: error: Pseudo result 'JIRL' operand count mismatch
def RET     : RetBase<GPROut>;
^

Description:
The new backend LoongArch doesn’t have instruction like “JR $ra” as indirect branch instruction, so I tried to use “JIRL $zero $rj 0” instead.
“JIRL” 's formation is like:

jirl    rd,  rj,  offs16
JIRL:
rd = PC+4
PC = rj + offs16

The question is, the “JR” instruction has only one parameter, but “JIRL” has 3. I use PseudoInstExpansion to solve this problem but it seems like I screwed up something. Here is my code in InstrInfo.td:


class JumpFR<bits<6> op, string instr_asm, RegisterClass RC>:
    Fmt2RI16<op, (outs), (ins RC:$rj),
     !strconcat(instr_asm, "\t$rj"), [(brind RC:$rj)], IIBranch> {
  let rd = 0;
  let imm16 = 0;
}

def JIRL      : JumpFR<0b010011, "jirl", GPROut>;

/// Return instruction
class RetBase<RegisterClass RC>
        : JumpFR<0b010011, "ret", RC>,
          PseudoInstExpansion<(JIRL ZERO, RA, 0)>{ 
                                                   // ZERO and RA are defined in RegisterInfo.td 
                                                   // ZERO represents r0 which is always 0, 
                                                   //RA represents r1 used as return address register
let isReturn = 1;
let isCodeGenOnly = 1;
let hasCtrlDep = 1;
let hasExtraSrcRegAllocReq = 1;
}

def RET     : RetBase<GPROut>;

Above, Fmt2RI16 is a LoongArch instruction formation which match with 2 Register and 1 Immediate number:

class Fmt2RI16<bits<6> op, dag outs, dag ins, string asmstr,
        list<dag> pattern, InstrItinClass itin>
: LAInst<outs, ins, asmstr, pattern, itin> {
bits<16> imm16;
bits<5> rj;
bits<5> rd;

let Inst{31-26} = op;
let Inst{25-10} = imm16;
let Inst{9-5} = rj;
let Inst{4-0} = rd;
}

Your JIRL instruction clearly has only single argument: (ins RC:$rj) hence the error. You need to have separate instruction descriptions: one for JIRL (complete form) and another one – Pseudo for RET.

1 Like

Do you actually want a new pseudo instruction or just an instruction alias for assembly? Normally you would just have the one instruction and alias everything else (see RISCV’s handling of jr and ret, which are aliases of the real jalr instruction).

1 Like

Thank you for reply.
Am I misunderstand the meaning of pseudo instruction? All I want is, you see, the mips has “jr $ra” instruction, so when it meets LLVM IR node like “ret i32 0”, it will use “jr $ra” to handle(return value stored in $ra), but my backend doesn’t have somethin like “jr”, it only has “jirl $rd, $rj, simm12” , so I have to use "jirl $zero, $ra, 0 " to pretend to be “jr $ra” :joy:.

Am I misunderstand the meaning of pseudo instruction?

Maybe

So, you’d essentially need to expand your pseudo RET into jirl $zero, $ra, 0. The latter instruction obviously have 3 operands, so you’d just need to provide those.

1 Like

A pseudo instruction gets used when you can’t use a real instruction, such as because you need to do a late expansion of a specific operation into a sequence of instructions, or because of something special with the operands (this is deliberately vague because getting into the details of that is a bit too deep a dive for here). For code generation purposes you often do want a pseudo instruction for return so that you can constrain the register and set isReturn, but for assembly you just want an InstAlias that maps directly to JIRL.

I’d suggest looking at RISCVInstrInfo.td’s PseudoRET (used for code generation) and ret InstAlias (used for assembly).

1 Like

Thank you so much ! That exactly what I want. :star_struck:

But I still have two question, could you please help me with them?
I read the RISCVInstrInfo.td, code as follow:

def : InstAlias<"jr $rs",                (JALR      X0, GPR:$rs, 0), 3>;

def : InstAlias<"ret",                   (JALR      X0,      X1, 0), 4>;
class InstAlias<string Asm, dag Result, bit Emit = 0b1> {
  string AsmString = Asm;      // The .s format to match the instruction with.
  dag ResultInst = Result;     // The MCInst to generate.
  bit EmitAlias = Emit;        // Emit the alias instead of what's aliased.
  // Predicates - Predicates that must be true for this to match.
  list<Predicate> Predicates = [];
}

Q1 : InstAlias class’ first parameter is the .s format to match the instruction with.
In def : InstAlias<"jr $rs", (JALR X0, GPR:$rs, 0), 3>; it passes a “jr $rs”, but llvm IR doesn’t have “jr”(only br, switch or ret),why is that?

Q2: InstAlias class’ third parameter is a bit flag which indicate that if emit the alias instead of what’s aliased or not. the value should be 1 or 0
In def : InstAlias<"jr $rs", (JALR X0, GPR:$rs, 0), 3>; it passes a “3” :tired_face: why is that?

Thank you for your patient explanation, sir, really appreciate that.

That string is assembly, not IR.

From llvm/include/llvm/Target/Target.td:

/// InstAlias - This defines an alternate assembly syntax that is allowed to
/// match an instruction that has a different (more canonical) assembly
/// representation.
class InstAlias<string Asm, dag Result, int Emit = 1, string VariantName = ""> {
  ...
  // This determines which order the InstPrinter detects aliases for
  // printing. A larger value makes the alias more likely to be
  // emitted. The Instruction's own definition is notionally 0.5, so 0
  // disables printing and 1 enables it if there are no conflicting aliases.
  int EmitPriority = Emit;

It’s to control which aliases take priority over others when multiple match.

1 Like

Thanks, again.
I really think nice people like you who are willing to answer beginner’s crap are gospel to us.