Skip to content

[RISCV] Add symbol parsing support for Xqcilb long branch instructions #135044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
ParseStatus parseOperandWithSpecifier(OperandVector &Operands);
ParseStatus parseBareSymbol(OperandVector &Operands);
ParseStatus parseCallSymbol(OperandVector &Operands);
ParseStatus parsePseudoQCJumpSymbol(OperandVector &Operands);
ParseStatus parsePseudoJumpSymbol(OperandVector &Operands);
ParseStatus parseJALOffset(OperandVector &Operands);
ParseStatus parseVTypeI(OperandVector &Operands);
Expand Down Expand Up @@ -590,6 +591,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
(VK == RISCVMCExpr::VK_CALL || VK == RISCVMCExpr::VK_CALL_PLT);
}

bool isPseudoQCJumpSymbol() const {
int64_t Imm;
// Must be of 'immediate' type but not a constant.
if (!isImm() || evaluateConstantImm(getImm(), Imm))
return false;

RISCVMCExpr::Specifier VK = RISCVMCExpr::VK_None;
return RISCVAsmParser::classifySymbolRef(getImm(), VK) &&
VK == RISCVMCExpr::VK_QC_E_JUMP_PLT;
}

bool isPseudoJumpSymbol() const {
int64_t Imm;
// Must be of 'immediate' type but not a constant.
Expand Down Expand Up @@ -2136,6 +2148,38 @@ ParseStatus RISCVAsmParser::parseCallSymbol(OperandVector &Operands) {
return ParseStatus::Success;
}

ParseStatus RISCVAsmParser::parsePseudoQCJumpSymbol(OperandVector &Operands) {
SMLoc S = getLoc();
const MCExpr *Res;

if (getLexer().getKind() != AsmToken::Identifier)
return ParseStatus::NoMatch;

std::string Identifier(getTok().getIdentifier());
SMLoc E = getTok().getEndLoc();

if (getLexer().peekTok().is(AsmToken::At)) {
Lex();
Lex();
SMLoc PLTLoc = getLoc();
StringRef PLT;
E = getTok().getEndLoc();
if (getParser().parseIdentifier(PLT) || PLT != "plt")
return Error(PLTLoc,
"'@plt' is the only valid operand for this instruction");
} else {
Lex();
}

RISCVMCExpr::Specifier Kind = RISCVMCExpr::VK_QC_E_JUMP_PLT;

MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
Res = MCSymbolRefExpr::create(Sym, getContext());
Res = RISCVMCExpr::create(Res, Kind, getContext());
Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
return ParseStatus::Success;
}

ParseStatus RISCVAsmParser::parsePseudoJumpSymbol(OperandVector &Operands) {
SMLoc S = getLoc();
SMLoc E;
Expand Down
16 changes: 16 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_riscv_qc_e_branch", 0, 48, MCFixupKindInfo::FKF_IsPCRel},
{"fixup_riscv_qc_e_32", 16, 32, 0},
{"fixup_riscv_qc_abs20_u", 12, 20, 0},
{"fixup_riscv_qc_e_jump_plt", 0, 48, MCFixupKindInfo::FKF_IsPCRel},
};
static_assert((std::size(Infos)) == RISCV::NumTargetFixupKinds,
"Not all fixup kinds added to Infos array");
Expand Down Expand Up @@ -575,6 +576,21 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
Value = (Bit19 << 31) | (Bit14_0 << 16) | (Bit18_15 << 12);
return Value;
}
case RISCV::fixup_riscv_qc_e_jump_plt: {
if (!isInt<32>(Value))
Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
if (Value & 0x1)
Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned");
uint64_t Bit31_16 = (Value >> 16) & 0xffff;
uint64_t Bit12 = (Value >> 12) & 0x1;
uint64_t Bit10_5 = (Value >> 5) & 0x3f;
uint64_t Bit15_13 = (Value >> 13) & 0x7;
uint64_t Bit4_1 = (Value >> 1) & 0xf;
uint64_t Bit11 = (Value >> 11) & 0x1;
Value = (Bit31_16 << 32ull) | (Bit12 << 31) | (Bit10_5 << 25) |
(Bit15_13 << 17) | (Bit4_1 << 8) | (Bit11 << 7);
return Value;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
return ELF::R_RISCV_CALL_PLT;
case RISCV::fixup_riscv_qc_e_branch:
return ELF::R_RISCV_QC_E_BRANCH;
case RISCV::fixup_riscv_qc_e_jump_plt:
return ELF::R_RISCV_QC_E_JUMP_PLT;
}
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ enum Fixups {
fixup_riscv_qc_e_32,
// 20-bit fixup for symbol references in the 32-bit qc.li instruction
fixup_riscv_qc_abs20_u,
// 32-bit fixup for symbol references in the 48-bit qc.j/qc.jal instructions
fixup_riscv_qc_e_jump_plt,

// Used as a sentinel, must be the last
fixup_riscv_invalid,
Expand Down
32 changes: 32 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;

void expandQCJump(const MCInst &MI, SmallVectorImpl<char> &CB,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;

void expandTLSDESCCall(const MCInst &MI, SmallVectorImpl<char> &CB,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
Expand Down Expand Up @@ -169,6 +173,26 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
support::endian::write(CB, Binary, llvm::endianness::little);
}

void RISCVMCCodeEmitter::expandQCJump(const MCInst &MI,
SmallVectorImpl<char> &CB,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
MCOperand Func = MI.getOperand(0);
assert(Func.isExpr() && "Expected expression");

auto Opcode =
(MI.getOpcode() == RISCV::PseudoQC_E_J) ? RISCV::QC_E_J : RISCV::QC_E_JAL;
MCInst Jump = MCInstBuilder(Opcode).addExpr(Func.getExpr());

uint64_t Bits = getBinaryCodeForInstr(Jump, Fixups, STI) & 0xffff'ffff'ffffu;
SmallVector<char, 8> Encoding;
support::endian::write(Encoding, Bits, llvm::endianness::little);
assert(Encoding[6] == 0 && Encoding[7] == 0 &&
"Unexpected encoding for 48-bit instruction");
Encoding.truncate(6);
CB.append(Encoding);
}

void RISCVMCCodeEmitter::expandTLSDESCCall(const MCInst &MI,
SmallVectorImpl<char> &CB,
SmallVectorImpl<MCFixup> &Fixups,
Expand Down Expand Up @@ -440,6 +464,11 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
expandTLSDESCCall(MI, CB, Fixups, STI);
MCNumEmitted += 1;
return;
case RISCV::PseudoQC_E_J:
case RISCV::PseudoQC_E_JAL:
expandQCJump(MI, CB, Fixups, STI);
MCNumEmitted += 1;
return;
}

switch (Size) {
Expand Down Expand Up @@ -656,6 +685,9 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
case RISCVMCExpr::VK_QC_ABS20:
FixupKind = RISCV::fixup_riscv_qc_abs20_u;
break;
case RISCVMCExpr::VK_QC_E_JUMP_PLT:
FixupKind = RISCV::fixup_riscv_qc_e_jump_plt;
break;
}
} else if (Kind == MCExpr::SymbolRef || Kind == MCExpr::Binary) {
// FIXME: Sub kind binary exprs have chance of underflow.
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, Specifier S,

void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
Specifier S = getSpecifier();
bool HasVariant = ((S != VK_None) && (S != VK_CALL) && (S != VK_CALL_PLT));
bool HasVariant = ((S != VK_None) && (S != VK_CALL) && (S != VK_CALL_PLT) &&
(S != VK_QC_E_JUMP_PLT));

if (HasVariant)
OS << '%' << getSpecifierName(S) << '(';
Expand Down Expand Up @@ -167,6 +168,8 @@ StringRef RISCVMCExpr::getSpecifierName(Specifier S) {
return "pltpcrel";
case VK_QC_ABS20:
return "qc.abs20";
case VK_QC_E_JUMP_PLT:
return "qc_e_jump_plt";
}
llvm_unreachable("Invalid ELF symbol kind");
}
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class RISCVMCExpr : public MCTargetExpr {
VK_TLSDESC_ADD_LO,
VK_TLSDESC_CALL,
VK_QC_ABS20,
VK_QC_E_JUMP_PLT
};

private:
Expand Down
24 changes: 23 additions & 1 deletion llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ def simm32_lsb0 : Operand<OtherVT> {
let OperandType = "OPERAND_PCREL";
}

def PseudoQCJumpSymbol : AsmOperandClass {
let Name = "PseudoQCJumpSymbol";
let RenderMethod = "addImmOperands";
let DiagnosticType = "InvalidPseudoQCJumpSymbol";
let DiagnosticString = "operand must be a valid jump target";
let ParserMethod = "parsePseudoQCJumpSymbol";
}

def pseudo_qc_jump_symbol : Operand<XLenVT> {
let ParserMatchClass = PseudoQCJumpSymbol;
}

//===----------------------------------------------------------------------===//
// Instruction Formats
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -708,7 +720,7 @@ class QCIRVInstEI<bits<3> funct3, bits<2> funct2, string opcodestr>
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
class QCIRVInst48EJ<bits<2> func2, string opcodestr>
: RVInst48<(outs), (ins simm32_lsb0:$imm31),
opcodestr, "$imm31", [], InstFormatOther> {
opcodestr, "$imm31", [], InstFormatQC_EJ> {
bits<31> imm31;

let Inst{47-32} = imm31{30-15};
Expand Down Expand Up @@ -1219,6 +1231,16 @@ def PseudoQC_E_SH : PseudoStore<"qc.e.sh">;
def PseudoQC_E_SW : PseudoStore<"qc.e.sw">;
} // Predicates = [HasVendorXqcilo, IsRV32]

let isCall = 0, isBarrier = 1, isTerminator = 1,
isCodeGenOnly = 0, hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
def PseudoQC_E_J : Pseudo<(outs), (ins pseudo_qc_jump_symbol:$func), [],
"qc.e.j", "$func">;

let isCall = 1, Defs = [X1], isCodeGenOnly = 0, hasSideEffects = 0,
mayStore = 0, mayLoad = 0 in
def PseudoQC_E_JAL: Pseudo<(outs), (ins pseudo_qc_jump_symbol:$func), [],
"qc.e.jal", "$func">;

//===----------------------------------------------------------------------===//
// Code Gen Patterns
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions llvm/test/MC/RISCV/xqcilb-invalid.s
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ qc.e.jal 2147483649

# CHECK-MINUS: :[[@LINE+1]]:1: error: instruction requires the following: 'Xqcilb' (Qualcomm uC Long Branch Extension)
qc.e.jal 2147483640

# CHECK: :[[@LINE+1]]:12: error: '@plt' is the only valid operand for this instruction
qc.e.j foo@rlt
59 changes: 59 additions & 0 deletions llvm/test/MC/RISCV/xqcilb-relocations.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# RUN: llvm-mc -triple riscv32 -mattr=+experimental-xqcilb %s -show-encoding \
# RUN: | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s
# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %s -o %t.o
# RUN: llvm-readobj -r %t.o | FileCheck -check-prefix=RELOC %s

# Check prefixes:
# RELOC - Check the relocation in the object.
# FIXUP - Check the fixup on the instruction.
# INSTR - Check the instruction is handled properly by the ASMPrinter.

.text

qc.e.j foo
# RELOC: R_RISCV_CUSTOM195 foo 0x0
# INSTR: qc.e.j foo
# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt

qc.e.j foo@plt
# RELOC: R_RISCV_CUSTOM195 foo 0x0
# INSTR: qc.e.j foo
# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt

qc.e.jal foo@plt
# RELOC: R_RISCV_CUSTOM195 foo 0x0
# INSTR: qc.e.jal foo
# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt

qc.e.jal foo
# RELOC: R_RISCV_CUSTOM195 foo 0x0
# INSTR: qc.e.jal foo
# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt

# Check that a label in a different section is handled similar to an undefined symbol
qc.e.j .bar
# RELOC: R_RISCV_CUSTOM195 .bar 0x0
# INSTR: qc.e.j .bar
# FIXUP: fixup A - offset: 0, value: .bar, kind: fixup_riscv_qc_e_jump_plt

qc.e.jal .bar
# RELOC: R_RISCV_CUSTOM195 .bar 0x0
# INSTR: qc.e.jal .bar
# FIXUP: fixup A - offset: 0, value: .bar, kind: fixup_riscv_qc_e_jump_plt

# Check that jumps to a defined symbol are handled correctly
qc.e.j .L1
# INSTR:qc.e.j .L1
# FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_jump_plt

qc.e.jal .L1
# INSTR:qc.e.jal .L1
# FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_jump_plt

.L1:
ret

.section .t2

.bar:
ret