Skip to content

Commit 3faaa5c

Browse files
authored
[RISCV] Fix QC.E.LI -> C.LI with Bare Symbol Compression (#146763)
There's a long comment explaining this approach in RISCVInstrInfoXqci.td This change also fixes some problems when fixups are able to be resolved for `qc.e.li` and `qc.li`.
1 parent 4923313 commit 3faaa5c

File tree

11 files changed

+219
-6
lines changed

11 files changed

+219
-6
lines changed

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,10 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
16581658
case Match_InvalidSImm26:
16591659
return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 25),
16601660
(1 << 25) - 1);
1661+
// HACK: See comment before `BareSymbolQC_E_LI` in RISCVInstrInfoXqci.td.
1662+
case Match_InvalidBareSymbolQC_E_LI:
1663+
LLVM_FALLTHROUGH;
1664+
// END HACK
16611665
case Match_InvalidBareSImm32:
16621666
return generateImmOutOfRangeError(Operands, ErrorInfo,
16631667
std::numeric_limits<int32_t>::min(),

llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ MCFixupKindInfo RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
7676
{"fixup_riscv_branch", 0, 32, 0},
7777
{"fixup_riscv_rvc_jump", 2, 11, 0},
7878
{"fixup_riscv_rvc_branch", 0, 16, 0},
79+
{"fixup_riscv_rvc_imm", 0, 16, 0},
7980
{"fixup_riscv_call", 0, 64, 0},
8081
{"fixup_riscv_call_plt", 0, 64, 0},
8182

8283
{"fixup_riscv_qc_e_branch", 0, 48, 0},
8384
{"fixup_riscv_qc_e_32", 16, 32, 0},
84-
{"fixup_riscv_qc_abs20_u", 12, 20, 0},
85+
{"fixup_riscv_qc_abs20_u", 0, 32, 0},
8586
{"fixup_riscv_qc_e_call_plt", 0, 48, 0},
8687

8788
// Andes fixups
@@ -134,6 +135,10 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup,
134135
// For jump instructions the immediate must be in the range
135136
// [-1048576, 1048574]
136137
return Offset > 1048574 || Offset < -1048576;
138+
case RISCV::fixup_riscv_rvc_imm:
139+
// This fixup can never be emitted as a relocation, so always needs to be
140+
// relaxed.
141+
return true;
137142
}
138143
}
139144

@@ -152,6 +157,18 @@ static unsigned getRelaxedOpcode(unsigned Opcode, ArrayRef<MCOperand> Operands,
152157
// This only relaxes one "step" - i.e. from C.J to JAL, not from C.J to
153158
// QC.E.J, because we can always relax again if needed.
154159
return RISCV::JAL;
160+
case RISCV::C_LI:
161+
if (!STI.hasFeature(RISCV::FeatureVendorXqcili))
162+
break;
163+
// We only need this because `QC.E.LI` can be compressed into a `C.LI`. This
164+
// happens because the `simm6` MCOperandPredicate accepts bare symbols, and
165+
// `QC.E.LI` is the only instruction that accepts bare symbols at parse-time
166+
// and compresses to `C.LI`. `C.LI` does not itself accept bare symbols at
167+
// parse time.
168+
//
169+
// If we have a bare symbol, we need to turn this back to a `QC.E.LI`, as we
170+
// have no way to emit a relocation on a `C.LI` instruction.
171+
return RISCV::QC_E_LI;
155172
case RISCV::JAL: {
156173
// We can only relax JAL if we have Xqcilb
157174
if (!STI.hasFeature(RISCV::FeatureVendorXqcilb))
@@ -240,6 +257,23 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst,
240257
Res.addOperand(Inst.getOperand(1));
241258
break;
242259
}
260+
case RISCV::C_LI: {
261+
// This should only be hit when trying to relax a `C.LI` into a `QC.E.LI`
262+
// because the `C.LI` has a bare symbol. We cannot use
263+
// `RISCVRVC::uncompress` because it will use decompression patterns. The
264+
// `QC.E.LI` compression pattern to `C.LI` is compression-only (because we
265+
// don't want `c.li` ever printed as `qc.e.li`, which might be done if the
266+
// pattern applied to decompression), but that doesn't help much becuase
267+
// `C.LI` with a bare symbol will decompress to an `ADDI` anyway (because
268+
// `simm12`'s MCOperandPredicate accepts a bare symbol and that pattern
269+
// comes first), and we still cannot emit an `ADDI` with a bare symbol.
270+
assert(STI.hasFeature(RISCV::FeatureVendorXqcili) &&
271+
"C.LI is only relaxable with Xqcili");
272+
Res.setOpcode(getRelaxedOpcode(Inst.getOpcode(), Inst.getOperands(), STI));
273+
Res.addOperand(Inst.getOperand(0));
274+
Res.addOperand(Inst.getOperand(1));
275+
break;
276+
}
243277
case RISCV::BEQ:
244278
case RISCV::BNE:
245279
case RISCV::BLT:
@@ -539,10 +573,18 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
539573
(Bit5 << 2);
540574
return Value;
541575
}
576+
case RISCV::fixup_riscv_rvc_imm: {
577+
if (!isInt<6>(Value))
578+
Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
579+
unsigned Bit5 = (Value >> 5) & 0x1;
580+
unsigned Bit4_0 = Value & 0x1f;
581+
Value = (Bit5 << 12) | (Bit4_0 << 2);
582+
return Value;
583+
}
542584
case RISCV::fixup_riscv_qc_e_32: {
543585
if (!isInt<32>(Value))
544586
Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
545-
return ((Value & 0xffffffff) << 16);
587+
return Value & 0xffffffffu;
546588
}
547589
case RISCV::fixup_riscv_qc_abs20_u: {
548590
if (!isInt<20>(Value))

llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ unsigned RISCVELFObjectWriter::getRelocType(const MCFixup &Fixup,
135135
return ELF::R_RISCV_LO12_I;
136136
case RISCV::fixup_riscv_lo12_s:
137137
return ELF::R_RISCV_LO12_S;
138+
case RISCV::fixup_riscv_rvc_imm:
139+
reportError(Fixup.getLoc(), "No relocation for CI-type instructions");
140+
return ELF::R_RISCV_NONE;
138141
case RISCV::fixup_riscv_qc_e_32:
139142
return ELF::R_RISCV_QC_E_32;
140143
case RISCV::fixup_riscv_qc_abs20_u:

llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@ enum Fixups {
4040
fixup_riscv_rvc_jump,
4141
// 8-bit fixup for symbol references in the compressed branch instruction
4242
fixup_riscv_rvc_branch,
43+
// 6-bit fixup for symbol references in instructions like c.li
44+
fixup_riscv_rvc_imm,
4345
// Fixup representing a legacy no-pic function call attached to the auipc
4446
// instruction in a pair composed of adjacent auipc+jalr instructions.
4547
fixup_riscv_call,
4648
// Fixup representing a function call attached to the auipc instruction in a
4749
// pair composed of adjacent auipc+jalr instructions.
4850
fixup_riscv_call_plt,
51+
52+
// Qualcomm specific fixups
4953
// 12-bit fixup for symbol references in the 48-bit Xqcibi branch immediate
5054
// instructions
5155
fixup_riscv_qc_e_branch,

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
650650
FixupKind = RISCV::fixup_riscv_rvc_jump;
651651
} else if (MIFrm == RISCVII::InstFormatCB) {
652652
FixupKind = RISCV::fixup_riscv_rvc_branch;
653+
} else if (MIFrm == RISCVII::InstFormatCI) {
654+
FixupKind = RISCV::fixup_riscv_rvc_imm;
653655
} else if (MIFrm == RISCVII::InstFormatI) {
654656
FixupKind = RISCV::fixup_riscv_12_i;
655657
} else if (MIFrm == RISCVII::InstFormatQC_EB) {

llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,3 +1677,82 @@ def : CompressPat<(QC_E_BGEUI GPRNoX0:$rs1, uimm5nonzero:$imm5, bare_simm13_lsb0
16771677
def : CompressPat<(QC_E_BLTUI GPRNoX0:$rs1, uimm5nonzero:$imm5, bare_simm13_lsb0:$imm12),
16781678
(QC_BLTUI GPRNoX0:$rs1, uimm5nonzero:$imm5, bare_simm13_lsb0:$imm12)>;
16791679
} // let isCompressOnly = true, Predicates = [HasVendorXqcibi, IsRV32]
1680+
1681+
// HACKS
1682+
// -----
1683+
// The reasons for needing the definitions below are long and quite annoying. I'm writing
1684+
// this so they are explained in-line, rather than anywhere else.
1685+
//
1686+
// Emitting an instruction to an object proceeds as:
1687+
// - Compression (in emitInstruction)
1688+
// - Emit to Binary Code + Fixups
1689+
// - Assembler Relaxation
1690+
// - Fixup evaluation/application
1691+
// - If relaxed, re-emitted to Binary + Fixups
1692+
// - Relocation generation from Fixups
1693+
//
1694+
// Unfortunately, the `QC.E.LI` -> `C.LI` compression pattern has an edge case that has
1695+
// caused crashes in the past.
1696+
//
1697+
// How the bug happens is:
1698+
// - QC.E.LI is parsed with a bare symbol, which is valid + expected, and can
1699+
// be handled by fixups/relocations.
1700+
// - Compression turns this into a `C.LI` because the `simm6`
1701+
// MCOperandPredicate accepts bare symbols.
1702+
// - Binary Code emission didn't know how to create a fixup for a CI-type
1703+
// instruction containing a bare symbol.
1704+
//
1705+
// The solution to the last bullet is that we added the `fixup_riscv_rvc_imm`,
1706+
// so that we could proceed past the last error, and then use Assembler Relaxation
1707+
// to turn the `C.LI` with a bare symbol back into a `QC.E.LI`.
1708+
//
1709+
// This is good enough for emitting objects, but doesn't work for emitting
1710+
// assembly. Emitting assembly is why we need the following Hacks.
1711+
//
1712+
// Emitting an instruction to assembly proceeds as:
1713+
// - Compression (in emitInstruction)
1714+
// - Decompression (in RISCVInstPrinter::printInst)
1715+
// - InstAliases are applied
1716+
//
1717+
// So in the case of `QC.E.LI` with a bare symbol, first it is compressed to
1718+
// `C.LI` with a bare symbol, and then it is decompressed to `ADDI` with a bare
1719+
// symbol for printing, which is printed via an alias as `li <reg>, <symbol>`.
1720+
// Both the decompression and the alias use the MCOperandPredicate from
1721+
// `simm12`, which accepts bare symbols.
1722+
//
1723+
// The problem here is that `li <reg>, <symbol>` fails to parse, because the
1724+
// parsers do not accept bare symbols, they only accept symbols with specifiers
1725+
// or immediates.
1726+
//
1727+
// Our solution is to add another alias, which will be prioritised above the
1728+
// `li` alias, but only when `qc.e.li` is available. We originally intended to
1729+
// use the `bare_symbol` Operand type, but this had no MCOperandPredicate, and
1730+
// adding one changed the error messages when parsing `qc.e.li` with a
1731+
// too-large constant. So instead, we add a new `AsmOperand` and `Operand` type,
1732+
// just for the alias, which parse just like a BareSymbol, but they
1733+
// have both an MCOperandPredicate, and the error message that corresponds to
1734+
// the existing one on `qc.e.li` for too-large immediates (which fail to parse
1735+
// as both an immediate, and a bare symbol).
1736+
//
1737+
// This is fairly unpleasant, but it's the least disruptive thing we can do
1738+
// and keeps all the hacks confined to the RISC-V backend code.
1739+
1740+
def BareSymbolQC_E_LI : AsmOperandClass {
1741+
let Name = "BareSymbolQC_E_LI";
1742+
let PredicateMethod = "isBareSymbol";
1743+
let RenderMethod = "addImmOperands";
1744+
let DiagnosticType = "InvalidBareSymbolQC_E_LI";
1745+
let ParserMethod = "parseBareSymbol";
1746+
}
1747+
1748+
def hack_bare_symbol_qc_e_li : Operand<XLenVT> {
1749+
let ParserMatchClass = BareSymbolQC_E_LI;
1750+
let MCOperandPredicate = [{
1751+
return MCOp.isExpr() && MCOp.isBareSymbolRef();
1752+
}];
1753+
}
1754+
1755+
let Predicates = [HasVendorXqcili, IsRV32] in {
1756+
def : InstAlias<"qc.e.li $rd, $sym", (ADDI GPR:$rd, X0, hack_bare_symbol_qc_e_li:$sym), 3>;
1757+
} // Predicates = [HasVendorXqcili, IsRV32]
1758+
// END HACKS

llvm/test/MC/RISCV/Relocations/mc-dump.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# CHECK-NEXT: Symbol @0 .text
88
# CHECK-NEXT:0 Align Align:4 Fill:0 FillLen:1 MaxBytesToEmit:4 Nops
99
# CHECK-NEXT:0 Data LinkerRelaxable Size:8 [97,00,00,00,e7,80,00,00]
10-
# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4022
10+
# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4023
1111
# CHECK-NEXT: Symbol @0 $x
1212
# CHECK-NEXT:8 Align Align:8 Fill:0 FillLen:1 MaxBytesToEmit:8 Nops
1313
# CHECK-NEXT:12 Data Size:4 [13,05,30,00]

llvm/test/MC/RISCV/xqci-fixups.s

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \
2+
# RUN: --mattr=+experimental-xqcili,+experimental-xqcilb,+experimental-xqcibi \
3+
# RUN: -riscv-add-build-attributes \
4+
# RUN: | llvm-objdump --no-print-imm-hex -M no-aliases -d - \
5+
# RUN: | FileCheck -check-prefix=CHECK-INSTR %s
6+
# RUN: llvm-mc -filetype=obj -triple=riscv32 %s \
7+
# RUN: --mattr=+experimental-xqcili,+experimental-xqcilb,+experimental-xqcibi \
8+
# RUN: | llvm-readobj -r - | FileCheck %s -check-prefix=CHECK-REL
9+
10+
## This checks that, if the assembler can resolve the qc fixup, that the fixup
11+
## is applied correctly to the instruction.
12+
13+
.L0:
14+
# CHECK-INSTR: qc.e.beqi a0, 64, 0x0
15+
qc.e.beqi a0, 64, .L0
16+
# CHECK-INSTR: qc.e.j 0x10000016
17+
qc.e.j func
18+
# CHECK-INSTR: qc.e.li a0, 8
19+
qc.e.li a0, abs_sym
20+
# CHECK-INSTR: qc.li a0, 8
21+
qc.li a0, %qc.abs20(abs_sym)
22+
23+
24+
25+
# This has to come after the instructions that use it or it will
26+
# be evaluated at parse-time (avoiding fixups)
27+
abs_sym = 8
28+
29+
30+
.space 0x10000000
31+
func:
32+
ret
33+
34+
## All these fixups should be resolved by the assembler without emitting
35+
## relocations.
36+
# CHECK-REL-NOT: R_RISCV

llvm/test/MC/RISCV/xqcibi-relocations.s

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,20 @@ qc.bnei t3, 14, same_section
8484
# OBJ-NEXT: qc.e.bgeui s2, 0x18, 0x28 <same_section>
8585
qc.e.bgeui s2, 24, same_section
8686

87-
.option norelax
87+
## Enable compression/relaxation to check how symbols are handled.
88+
.option noexact
89+
90+
# ASM: qc.bnei t1, 10, undef
91+
# OBJ: qc.beqi t1, 0xa, 0x42 <same_section_extern+0x16>
92+
# OBJ-NEXT: j 0x3e <same_section_extern+0x12>
93+
# OBJ-NEXT: R_RISCV_JAL undef{{$}}
94+
qc.bnei t1, 10, undef
95+
96+
# ASM: qc.e.bgeui s0, 40, undef
97+
# OBJ-NEXT: qc.e.bltui s0, 0x28, 0x4c <same_section_extern+0x20>
98+
# OBJ-NEXT: j 0x48 <same_section_extern+0x1c>
99+
# OBJ-NEXT: R_RISCV_JAL undef{{$}}
100+
qc.e.bgeui s0, 40, undef
88101

89102
.section .text.second, "ax", @progbits
90103

llvm/test/MC/RISCV/xqcilb-relocations.s

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,22 @@ qc.e.j same_section
9292
# OBJ-NEXT: R_RISCV_RELAX
9393
qc.e.jal same_section
9494

95-
.option norelax
95+
## Enable compression/relaxation to check how symbols are handled.
96+
.option noexact
97+
98+
qc.e.j undef
99+
# ASM: j undef
100+
# OBJ: qc.e.j 0x44 <same_section_extern+0x10>
101+
# OBJ-NEXT: R_RISCV_VENDOR QUALCOMM{{$}}
102+
# OBJ-NEXT: R_RISCV_CUSTOM195 undef{{$}}
103+
# OBJ-NEXT: R_RISCV_RELAX
104+
105+
qc.e.jal undef
106+
# ASM: jal undef
107+
# OBJ: qc.e.jal 0x4a <same_section_extern+0x16>
108+
# OBJ-NEXT: R_RISCV_VENDOR QUALCOMM{{$}}
109+
# OBJ-NEXT: R_RISCV_CUSTOM195 undef{{$}}
110+
# OBJ-NEXT: R_RISCV_RELAX
96111

97112
.section .text.other, "ax", @progbits
98113

0 commit comments

Comments
 (0)