- Notifications
You must be signed in to change notification settings - Fork 15.1k
[MC][Dwarf] Add custom CFI pseudo-ops for use in AMDGPU #164721
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
base: users/slinder1/amdgpu-cfi-1
Are you sure you want to change the base?
Conversation
| Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
| @llvm/pr-subscribers-debuginfo @llvm/pr-subscribers-backend-amdgpu Author: Scott Linder (slinder1) ChangesWhile these can be represented with .cfi_escape, using these pseudo-cfi Patch is 49.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/164721.diff 18 Files Affected:
diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h index 640d2eeb68d61..e602f03de5ebf 100644 --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -530,6 +530,10 @@ class MCCFIInstruction { OpGnuArgsSize, OpLabel, OpValOffset, + OpLLVMRegisterPair, + OpLLVMVectorRegisters, + OpLLVMVectorOffset, + OpLLVMVectorRegisterMask, }; // Held in ExtraFields for most common OpTypes, exceptions follow. @@ -548,10 +552,45 @@ class MCCFIInstruction { struct LabelFields { MCSymbol *CfiLabel = nullptr; }; + /// Held in ExtraFields when OpLLVMRegisterPair. + struct RegisterPairFields { + unsigned Register; + unsigned Reg1, Reg2; + unsigned Reg1SizeInBits, Reg2SizeInBits; + }; + struct VectorRegisterWithLane { + unsigned Register; + unsigned Lane; + unsigned SizeInBits; + }; + /// Held in ExtraFields when OpLLVMVectorRegisters. + struct VectorRegistersFields { + unsigned Register; + std::vector<VectorRegisterWithLane> VectorRegisters; + }; + /// Held in ExtraFields when OpLLVMVectorOffset. + struct VectorOffsetFields { + unsigned Register; + unsigned RegisterSizeInBits; + int64_t Offset; + unsigned MaskRegister; + unsigned MaskRegisterSizeInBits; + }; + /// Held in ExtraFields when OpLLVMVectorRegisterMask. + struct VectorRegisterMaskFields { + unsigned Register; + unsigned SpillRegister; + unsigned SpillRegisterLaneSizeInBits; + unsigned MaskRegister; + unsigned MaskRegisterSizeInBits; + }; private: MCSymbol *Label; - std::variant<CommonFields, EscapeFields, LabelFields> ExtraFields; + std::variant<CommonFields, EscapeFields, LabelFields, RegisterPairFields, + VectorRegistersFields, VectorOffsetFields, + VectorRegisterMaskFields> + ExtraFields; OpType Operation; SMLoc Loc; @@ -694,6 +733,57 @@ class MCCFIInstruction { return {OpLabel, L, LabelFields{CfiLabel}, Loc}; } + /// .cfi_llvm_register_pair Previous value of Register is saved in R1:R2. + static MCCFIInstruction + createLLVMRegisterPair(MCSymbol *L, unsigned Register, unsigned R1, + unsigned R1SizeInBits, unsigned R2, + unsigned R2SizeInBits, SMLoc Loc = {}) { + RegisterPairFields Extra{Register, R1, R2, R1SizeInBits, R2SizeInBits}; + return {OpLLVMRegisterPair, L, Extra, Loc}; + } + + /// .cfi_llvm_vector_registers Previous value of Register is saved in lanes of + /// vector registers. + static MCCFIInstruction + createLLVMVectorRegisters(MCSymbol *L, unsigned Register, + std::vector<VectorRegisterWithLane> VectorRegisters, + SMLoc Loc = {}) { + VectorRegistersFields Extra{Register, std::move(VectorRegisters)}; + return {OpLLVMVectorRegisters, L, std::move(Extra), Loc}; + } + + /// .cfi_llvm_vector_offset Previous value of Register is saved at Offset from + /// CFA. MaskRegister specifies the active lanes of register. + static MCCFIInstruction + createLLVMVectorOffset(MCSymbol *L, unsigned Register, + unsigned RegisterSizeInBits, unsigned MaskRegister, + unsigned MaskRegisterSizeInBits, int64_t Offset, + SMLoc Loc = {}) { + VectorOffsetFields Extra{Register, RegisterSizeInBits, Offset, MaskRegister, + MaskRegisterSizeInBits}; + return MCCFIInstruction(OpLLVMVectorOffset, L, Extra, Loc); + } + + /// .cfi_llvm_vector_register_mask Previous value of Register is saved in + /// SpillRegister, predicated on the value of MaskRegister. + static MCCFIInstruction createLLVMVectorRegisterMask( + MCSymbol *L, unsigned Register, unsigned SpillRegister, + unsigned SpillRegisterLaneSizeInBits, unsigned MaskRegister, + unsigned MaskRegisterSizeInBits, SMLoc Loc = {}) { + VectorRegisterMaskFields Extra{ + Register, SpillRegister, SpillRegisterLaneSizeInBits, + MaskRegister, MaskRegisterSizeInBits, + }; + return MCCFIInstruction(OpLLVMVectorRegisterMask, L, Extra, Loc); + } + + template <class ExtraFieldsTy> ExtraFieldsTy &getExtraFields() { + return std::get<ExtraFieldsTy>(ExtraFields); + } + + template <class ExtraFieldsTy> const ExtraFieldsTy &getExtraFields() const { + return std::get<ExtraFieldsTy>(ExtraFields); + } /// .cfi_val_offset Previous value of Register is offset Offset from the /// current CFA register. static MCCFIInstruction createValOffset(MCSymbol *L, unsigned Register, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 79c715e3820a6..4e76aa323eb30 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -1012,6 +1012,24 @@ class LLVM_ABI MCStreamer { SMLoc Loc = {}); virtual void emitCFIWindowSave(SMLoc Loc = {}); virtual void emitCFINegateRAState(SMLoc Loc = {}); + virtual void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, + int64_t R1SizeInBits, int64_t R2, + int64_t R2SizeInBits, SMLoc Loc = {}); + virtual void emitCFILLVMVectorRegisters( + int64_t Register, + std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs, + SMLoc Loc = {}); + virtual void emitCFILLVMVectorOffset(int64_t Register, + int64_t RegisterSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, + int64_t Offset, SMLoc Loc = {}); + virtual void + emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister, + int64_t SpillRegisterLaneSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, SMLoc Loc = {}); + virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {}); virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name); virtual void emitCFIValOffset(int64_t Register, int64_t Offset, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp index 2a146eb15f709..895c18abc56f9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -260,6 +260,39 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const { case MCCFIInstruction::OpRestoreState: OutStreamer->emitCFIRestoreState(Loc); break; + case MCCFIInstruction::OpLLVMRegisterPair: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::RegisterPairFields>(); + OutStreamer->emitCFILLVMRegisterPair(Fields.Register, Fields.Reg1, + Fields.Reg1SizeInBits, Fields.Reg2, + Fields.Reg2SizeInBits, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorRegisters: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorRegistersFields>(); + OutStreamer->emitCFILLVMVectorRegisters(Fields.Register, + Fields.VectorRegisters, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorOffset: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorOffsetFields>(); + OutStreamer->emitCFILLVMVectorOffset( + Fields.Register, Fields.RegisterSizeInBits, Fields.MaskRegister, + Fields.MaskRegisterSizeInBits, Fields.Offset, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorRegisterMask: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>(); + OutStreamer->emitCFILLVMVectorRegisterMask( + Fields.Register, Fields.SpillRegister, + Fields.SpillRegisterLaneSizeInBits, Fields.MaskRegister, + Fields.MaskRegisterSizeInBits); + break; + } + case MCCFIInstruction::OpValOffset: OutStreamer->emitCFIValOffset(Inst.getRegister(), Inst.getOffset(), Loc); break; diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp index 14098bc821617..0d60d17da0cf7 100644 --- a/llvm/lib/CodeGen/CFIInstrInserter.cpp +++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp @@ -262,6 +262,10 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) { case MCCFIInstruction::OpNegateRAState: case MCCFIInstruction::OpNegateRAStateWithPC: case MCCFIInstruction::OpGnuArgsSize: + case MCCFIInstruction::OpLLVMRegisterPair: + case MCCFIInstruction::OpLLVMVectorRegisters: + case MCCFIInstruction::OpLLVMVectorOffset: + case MCCFIInstruction::OpLLVMVectorRegisterMask: case MCCFIInstruction::OpLabel: case MCCFIInstruction::OpValOffset: break; diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp index 8b72c295416a2..8ed590669a3b0 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp +++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp @@ -240,6 +240,11 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) { .Case("window_save", MIToken::kw_cfi_window_save) .Case("negate_ra_sign_state", MIToken::kw_cfi_aarch64_negate_ra_sign_state) + .Case("llvm_register_pair", MIToken::kw_cfi_llvm_register_pair) + .Case("llvm_vector_registers", MIToken::kw_cfi_llvm_vector_registers) + .Case("llvm_vector_offset", MIToken::kw_cfi_llvm_vector_offset) + .Case("llvm_vector_register_mask", + MIToken::kw_cfi_llvm_vector_register_mask) .Case("negate_ra_sign_state_with_pc", MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc) .Case("blockaddress", MIToken::kw_blockaddress) diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h index 0627f176b9e00..abac1880f94e0 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.h +++ b/llvm/lib/CodeGen/MIRParser/MILexer.h @@ -98,6 +98,10 @@ struct MIToken { kw_cfi_undefined, kw_cfi_window_save, kw_cfi_aarch64_negate_ra_sign_state, + kw_cfi_llvm_register_pair, + kw_cfi_llvm_vector_registers, + kw_cfi_llvm_vector_offset, + kw_cfi_llvm_vector_register_mask, kw_cfi_aarch64_negate_ra_sign_state_with_pc, kw_blockaddress, kw_intrinsic, diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp index 6a464d9dd6886..3618022d89bed 100644 --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -484,6 +484,7 @@ class MIParser { bool parseDILocation(MDNode *&Expr); bool parseMetadataOperand(MachineOperand &Dest); bool parseCFIOffset(int &Offset); + bool parseCFIUnsigned(unsigned &Value); bool parseCFIRegister(unsigned &Reg); bool parseCFIAddressSpace(unsigned &AddressSpace); bool parseCFIEscapeValues(std::string& Values); @@ -2475,6 +2476,13 @@ bool MIParser::parseCFIOffset(int &Offset) { return false; } +bool MIParser::parseCFIUnsigned(unsigned &Value) { + if (getUnsigned(Value)) + return true; + lex(); + return false; +} + bool MIParser::parseCFIRegister(unsigned &Reg) { if (Token.isNot(MIToken::NamedRegister)) return error("expected a cfi register"); @@ -2608,6 +2616,69 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) { case MIToken::kw_cfi_aarch64_negate_ra_sign_state: CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); break; + case MIToken::kw_cfi_llvm_register_pair: { + unsigned Reg, R1, R2; + unsigned R1Size, R2Size; + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIRegister(R1) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(R1Size) || expectAndConsume(MIToken::comma) || + parseCFIRegister(R2) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(R2Size)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMRegisterPair( + nullptr, Reg, R1, R1Size, R2, R2Size)); + break; + } + case MIToken::kw_cfi_llvm_vector_registers: { + std::vector<MCCFIInstruction::VectorRegisterWithLane> VectorRegisters; + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma)) + return true; + do { + unsigned VR; + unsigned Lane, Size; + if (parseCFIRegister(VR) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(Lane) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(Size)) + return true; + VectorRegisters.push_back({VR, Lane, Size}); + } while (consumeIfPresent(MIToken::comma)); + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisters( + nullptr, Reg, std::move(VectorRegisters))); + break; + } + case MIToken::kw_cfi_llvm_vector_offset: { + unsigned Reg, MaskReg; + unsigned RegSize, MaskRegSize; + int Offset = 0; + + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(RegSize) || expectAndConsume(MIToken::comma) || + parseCFIRegister(MaskReg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(MaskRegSize) || expectAndConsume(MIToken::comma) || + parseCFIOffset(Offset)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorOffset( + nullptr, Reg, RegSize, MaskReg, MaskRegSize, Offset)); + break; + } + case MIToken::kw_cfi_llvm_vector_register_mask: { + unsigned Reg, SpillReg, MaskReg; + unsigned SpillRegLaneSize, MaskRegSize; + + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIRegister(SpillReg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(SpillRegLaneSize) || + expectAndConsume(MIToken::comma) || parseCFIRegister(MaskReg) || + expectAndConsume(MIToken::comma) || parseCFIUnsigned(MaskRegSize)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisterMask( + nullptr, Reg, SpillReg, SpillRegLaneSize, MaskReg, MaskRegSize)); + break; + } case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc: CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr)); @@ -2962,6 +3033,10 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx, case MIToken::kw_cfi_undefined: case MIToken::kw_cfi_window_save: case MIToken::kw_cfi_aarch64_negate_ra_sign_state: + case MIToken::kw_cfi_llvm_register_pair: + case MIToken::kw_cfi_llvm_vector_registers: + case MIToken::kw_cfi_llvm_vector_offset: + case MIToken::kw_cfi_llvm_vector_register_mask: case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc: return parseCFIOperand(Dest); case MIToken::kw_blockaddress: diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index bb9c76ff0c729..db802cc6e9024 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -778,6 +778,64 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI, if (MCSymbol *Label = CFI.getLabel()) MachineOperand::printSymbol(OS, *Label); break; + case MCCFIInstruction::OpLLVMRegisterPair: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::RegisterPairFields>(); + + OS << "llvm_register_pair "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", "; + printCFIRegister(Fields.Reg1, OS, TRI); + OS << ", " << Fields.Reg1SizeInBits << ", "; + printCFIRegister(Fields.Reg2, OS, TRI); + OS << ", " << Fields.Reg2SizeInBits; + break; + } + case MCCFIInstruction::OpLLVMVectorRegisters: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorRegistersFields>(); + + OS << "llvm_vector_registers "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + for (auto [Reg, Lane, Size] : Fields.VectorRegisters) { + OS << ", "; + printCFIRegister(Reg, OS, TRI); + OS << ", " << Lane << ", " << Size; + } + break; + } + case MCCFIInstruction::OpLLVMVectorOffset: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorOffsetFields>(); + + OS << "llvm_vector_offset "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", " << Fields.RegisterSizeInBits << ", "; + printCFIRegister(Fields.MaskRegister, OS, TRI); + OS << ", " << Fields.MaskRegisterSizeInBits << ", " << Fields.Offset; + break; + } + case MCCFIInstruction::OpLLVMVectorRegisterMask: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>(); + + OS << "llvm_vector_register_mask "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", "; + printCFIRegister(Fields.SpillRegister, OS, TRI); + OS << ", " << Fields.SpillRegisterLaneSizeInBits << ", "; + printCFIRegister(Fields.MaskRegister, OS, TRI); + OS << ", " << Fields.MaskRegisterSizeInBits; + break; + } case MCCFIInstruction::OpNegateRAStateWithPC: OS << "negate_ra_sign_state_with_pc "; if (MCSymbol *Label = CFI.getLabel()) diff --git a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp index bca820fa807c8..4d2d2da8a4445 100644 --- a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp +++ b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp @@ -161,6 +161,16 @@ dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) { CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(), Directive.getOffset()); break; + case MCCFIInstruction::OpLLVMRegisterPair: + case MCCFIInstruction::OpLLVMVectorRegisters: + case MCCFIInstruction::OpLLVMVectorOffset: + case MCCFIInstruction::OpLLVMVectorRegisterMask: + // TODO: These should be pretty straightforward to support, but is low + // priority. Similarly the implementation of OpLLVMDefAspaceCfa above + // seem incomplete and should be fixed. + Context->reportWarning(Directive.getLoc(), + "this directive is not supported, ignoring it"); + break; } return CFIP; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index be8c022f39ad1..6c54a9efbf2c6 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -369,6 +369,21 @@ class MCAsmStreamer final : public MCStreamer { void emitCFINegateRAState(SMLoc Loc) override; void emitCFINegateRAStateWithPC(SMLoc Loc) override; void emitCFIReturnColumn(int64_t Register) override; + void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, int64_t R1Size, + int64_t R2, int64_t R2Size, SMLoc Loc) override; + void emitCFILLVMVectorRegisters( + int64_t Register, + std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs, + SMLoc Loc) override; + void emitCFILLVMVectorOffset(int64_t Register, int64_t RegisterSize, + int64_t MaskRegister, int64_t MaskRegisterSize, + int64_t Offset, SMLoc Loc) override; + void emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister, + int64_t SpillRegisterLaneSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, + SMLoc Loc) override; + void emitCFILabelDirective(SMLoc Loc, StringRef Name) override; void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; @@ -2101,6 +2116,67 @@ void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2, EmitEOL(); } +void MCAsmStreamer::emitCFILLVMRegisterPair(int64_t Register, int64_t R1, + int64_t R1Size, int64_t R2, + int64_... [truncated] |
| @llvm/pr-subscribers-llvm-mc Author: Scott Linder (slinder1) ChangesWhile these can be represented with .cfi_escape, using these pseudo-cfi Patch is 49.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/164721.diff 18 Files Affected:
diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h index 640d2eeb68d61..e602f03de5ebf 100644 --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -530,6 +530,10 @@ class MCCFIInstruction { OpGnuArgsSize, OpLabel, OpValOffset, + OpLLVMRegisterPair, + OpLLVMVectorRegisters, + OpLLVMVectorOffset, + OpLLVMVectorRegisterMask, }; // Held in ExtraFields for most common OpTypes, exceptions follow. @@ -548,10 +552,45 @@ class MCCFIInstruction { struct LabelFields { MCSymbol *CfiLabel = nullptr; }; + /// Held in ExtraFields when OpLLVMRegisterPair. + struct RegisterPairFields { + unsigned Register; + unsigned Reg1, Reg2; + unsigned Reg1SizeInBits, Reg2SizeInBits; + }; + struct VectorRegisterWithLane { + unsigned Register; + unsigned Lane; + unsigned SizeInBits; + }; + /// Held in ExtraFields when OpLLVMVectorRegisters. + struct VectorRegistersFields { + unsigned Register; + std::vector<VectorRegisterWithLane> VectorRegisters; + }; + /// Held in ExtraFields when OpLLVMVectorOffset. + struct VectorOffsetFields { + unsigned Register; + unsigned RegisterSizeInBits; + int64_t Offset; + unsigned MaskRegister; + unsigned MaskRegisterSizeInBits; + }; + /// Held in ExtraFields when OpLLVMVectorRegisterMask. + struct VectorRegisterMaskFields { + unsigned Register; + unsigned SpillRegister; + unsigned SpillRegisterLaneSizeInBits; + unsigned MaskRegister; + unsigned MaskRegisterSizeInBits; + }; private: MCSymbol *Label; - std::variant<CommonFields, EscapeFields, LabelFields> ExtraFields; + std::variant<CommonFields, EscapeFields, LabelFields, RegisterPairFields, + VectorRegistersFields, VectorOffsetFields, + VectorRegisterMaskFields> + ExtraFields; OpType Operation; SMLoc Loc; @@ -694,6 +733,57 @@ class MCCFIInstruction { return {OpLabel, L, LabelFields{CfiLabel}, Loc}; } + /// .cfi_llvm_register_pair Previous value of Register is saved in R1:R2. + static MCCFIInstruction + createLLVMRegisterPair(MCSymbol *L, unsigned Register, unsigned R1, + unsigned R1SizeInBits, unsigned R2, + unsigned R2SizeInBits, SMLoc Loc = {}) { + RegisterPairFields Extra{Register, R1, R2, R1SizeInBits, R2SizeInBits}; + return {OpLLVMRegisterPair, L, Extra, Loc}; + } + + /// .cfi_llvm_vector_registers Previous value of Register is saved in lanes of + /// vector registers. + static MCCFIInstruction + createLLVMVectorRegisters(MCSymbol *L, unsigned Register, + std::vector<VectorRegisterWithLane> VectorRegisters, + SMLoc Loc = {}) { + VectorRegistersFields Extra{Register, std::move(VectorRegisters)}; + return {OpLLVMVectorRegisters, L, std::move(Extra), Loc}; + } + + /// .cfi_llvm_vector_offset Previous value of Register is saved at Offset from + /// CFA. MaskRegister specifies the active lanes of register. + static MCCFIInstruction + createLLVMVectorOffset(MCSymbol *L, unsigned Register, + unsigned RegisterSizeInBits, unsigned MaskRegister, + unsigned MaskRegisterSizeInBits, int64_t Offset, + SMLoc Loc = {}) { + VectorOffsetFields Extra{Register, RegisterSizeInBits, Offset, MaskRegister, + MaskRegisterSizeInBits}; + return MCCFIInstruction(OpLLVMVectorOffset, L, Extra, Loc); + } + + /// .cfi_llvm_vector_register_mask Previous value of Register is saved in + /// SpillRegister, predicated on the value of MaskRegister. + static MCCFIInstruction createLLVMVectorRegisterMask( + MCSymbol *L, unsigned Register, unsigned SpillRegister, + unsigned SpillRegisterLaneSizeInBits, unsigned MaskRegister, + unsigned MaskRegisterSizeInBits, SMLoc Loc = {}) { + VectorRegisterMaskFields Extra{ + Register, SpillRegister, SpillRegisterLaneSizeInBits, + MaskRegister, MaskRegisterSizeInBits, + }; + return MCCFIInstruction(OpLLVMVectorRegisterMask, L, Extra, Loc); + } + + template <class ExtraFieldsTy> ExtraFieldsTy &getExtraFields() { + return std::get<ExtraFieldsTy>(ExtraFields); + } + + template <class ExtraFieldsTy> const ExtraFieldsTy &getExtraFields() const { + return std::get<ExtraFieldsTy>(ExtraFields); + } /// .cfi_val_offset Previous value of Register is offset Offset from the /// current CFA register. static MCCFIInstruction createValOffset(MCSymbol *L, unsigned Register, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 79c715e3820a6..4e76aa323eb30 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -1012,6 +1012,24 @@ class LLVM_ABI MCStreamer { SMLoc Loc = {}); virtual void emitCFIWindowSave(SMLoc Loc = {}); virtual void emitCFINegateRAState(SMLoc Loc = {}); + virtual void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, + int64_t R1SizeInBits, int64_t R2, + int64_t R2SizeInBits, SMLoc Loc = {}); + virtual void emitCFILLVMVectorRegisters( + int64_t Register, + std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs, + SMLoc Loc = {}); + virtual void emitCFILLVMVectorOffset(int64_t Register, + int64_t RegisterSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, + int64_t Offset, SMLoc Loc = {}); + virtual void + emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister, + int64_t SpillRegisterLaneSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, SMLoc Loc = {}); + virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {}); virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name); virtual void emitCFIValOffset(int64_t Register, int64_t Offset, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp index 2a146eb15f709..895c18abc56f9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -260,6 +260,39 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const { case MCCFIInstruction::OpRestoreState: OutStreamer->emitCFIRestoreState(Loc); break; + case MCCFIInstruction::OpLLVMRegisterPair: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::RegisterPairFields>(); + OutStreamer->emitCFILLVMRegisterPair(Fields.Register, Fields.Reg1, + Fields.Reg1SizeInBits, Fields.Reg2, + Fields.Reg2SizeInBits, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorRegisters: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorRegistersFields>(); + OutStreamer->emitCFILLVMVectorRegisters(Fields.Register, + Fields.VectorRegisters, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorOffset: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorOffsetFields>(); + OutStreamer->emitCFILLVMVectorOffset( + Fields.Register, Fields.RegisterSizeInBits, Fields.MaskRegister, + Fields.MaskRegisterSizeInBits, Fields.Offset, Loc); + break; + } + case MCCFIInstruction::OpLLVMVectorRegisterMask: { + const auto &Fields = + Inst.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>(); + OutStreamer->emitCFILLVMVectorRegisterMask( + Fields.Register, Fields.SpillRegister, + Fields.SpillRegisterLaneSizeInBits, Fields.MaskRegister, + Fields.MaskRegisterSizeInBits); + break; + } + case MCCFIInstruction::OpValOffset: OutStreamer->emitCFIValOffset(Inst.getRegister(), Inst.getOffset(), Loc); break; diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp index 14098bc821617..0d60d17da0cf7 100644 --- a/llvm/lib/CodeGen/CFIInstrInserter.cpp +++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp @@ -262,6 +262,10 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) { case MCCFIInstruction::OpNegateRAState: case MCCFIInstruction::OpNegateRAStateWithPC: case MCCFIInstruction::OpGnuArgsSize: + case MCCFIInstruction::OpLLVMRegisterPair: + case MCCFIInstruction::OpLLVMVectorRegisters: + case MCCFIInstruction::OpLLVMVectorOffset: + case MCCFIInstruction::OpLLVMVectorRegisterMask: case MCCFIInstruction::OpLabel: case MCCFIInstruction::OpValOffset: break; diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp index 8b72c295416a2..8ed590669a3b0 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp +++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp @@ -240,6 +240,11 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) { .Case("window_save", MIToken::kw_cfi_window_save) .Case("negate_ra_sign_state", MIToken::kw_cfi_aarch64_negate_ra_sign_state) + .Case("llvm_register_pair", MIToken::kw_cfi_llvm_register_pair) + .Case("llvm_vector_registers", MIToken::kw_cfi_llvm_vector_registers) + .Case("llvm_vector_offset", MIToken::kw_cfi_llvm_vector_offset) + .Case("llvm_vector_register_mask", + MIToken::kw_cfi_llvm_vector_register_mask) .Case("negate_ra_sign_state_with_pc", MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc) .Case("blockaddress", MIToken::kw_blockaddress) diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h index 0627f176b9e00..abac1880f94e0 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.h +++ b/llvm/lib/CodeGen/MIRParser/MILexer.h @@ -98,6 +98,10 @@ struct MIToken { kw_cfi_undefined, kw_cfi_window_save, kw_cfi_aarch64_negate_ra_sign_state, + kw_cfi_llvm_register_pair, + kw_cfi_llvm_vector_registers, + kw_cfi_llvm_vector_offset, + kw_cfi_llvm_vector_register_mask, kw_cfi_aarch64_negate_ra_sign_state_with_pc, kw_blockaddress, kw_intrinsic, diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp index 6a464d9dd6886..3618022d89bed 100644 --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -484,6 +484,7 @@ class MIParser { bool parseDILocation(MDNode *&Expr); bool parseMetadataOperand(MachineOperand &Dest); bool parseCFIOffset(int &Offset); + bool parseCFIUnsigned(unsigned &Value); bool parseCFIRegister(unsigned &Reg); bool parseCFIAddressSpace(unsigned &AddressSpace); bool parseCFIEscapeValues(std::string& Values); @@ -2475,6 +2476,13 @@ bool MIParser::parseCFIOffset(int &Offset) { return false; } +bool MIParser::parseCFIUnsigned(unsigned &Value) { + if (getUnsigned(Value)) + return true; + lex(); + return false; +} + bool MIParser::parseCFIRegister(unsigned &Reg) { if (Token.isNot(MIToken::NamedRegister)) return error("expected a cfi register"); @@ -2608,6 +2616,69 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) { case MIToken::kw_cfi_aarch64_negate_ra_sign_state: CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); break; + case MIToken::kw_cfi_llvm_register_pair: { + unsigned Reg, R1, R2; + unsigned R1Size, R2Size; + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIRegister(R1) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(R1Size) || expectAndConsume(MIToken::comma) || + parseCFIRegister(R2) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(R2Size)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMRegisterPair( + nullptr, Reg, R1, R1Size, R2, R2Size)); + break; + } + case MIToken::kw_cfi_llvm_vector_registers: { + std::vector<MCCFIInstruction::VectorRegisterWithLane> VectorRegisters; + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma)) + return true; + do { + unsigned VR; + unsigned Lane, Size; + if (parseCFIRegister(VR) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(Lane) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(Size)) + return true; + VectorRegisters.push_back({VR, Lane, Size}); + } while (consumeIfPresent(MIToken::comma)); + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisters( + nullptr, Reg, std::move(VectorRegisters))); + break; + } + case MIToken::kw_cfi_llvm_vector_offset: { + unsigned Reg, MaskReg; + unsigned RegSize, MaskRegSize; + int Offset = 0; + + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(RegSize) || expectAndConsume(MIToken::comma) || + parseCFIRegister(MaskReg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(MaskRegSize) || expectAndConsume(MIToken::comma) || + parseCFIOffset(Offset)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorOffset( + nullptr, Reg, RegSize, MaskReg, MaskRegSize, Offset)); + break; + } + case MIToken::kw_cfi_llvm_vector_register_mask: { + unsigned Reg, SpillReg, MaskReg; + unsigned SpillRegLaneSize, MaskRegSize; + + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIRegister(SpillReg) || expectAndConsume(MIToken::comma) || + parseCFIUnsigned(SpillRegLaneSize) || + expectAndConsume(MIToken::comma) || parseCFIRegister(MaskReg) || + expectAndConsume(MIToken::comma) || parseCFIUnsigned(MaskRegSize)) + return true; + + CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisterMask( + nullptr, Reg, SpillReg, SpillRegLaneSize, MaskReg, MaskRegSize)); + break; + } case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc: CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr)); @@ -2962,6 +3033,10 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx, case MIToken::kw_cfi_undefined: case MIToken::kw_cfi_window_save: case MIToken::kw_cfi_aarch64_negate_ra_sign_state: + case MIToken::kw_cfi_llvm_register_pair: + case MIToken::kw_cfi_llvm_vector_registers: + case MIToken::kw_cfi_llvm_vector_offset: + case MIToken::kw_cfi_llvm_vector_register_mask: case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc: return parseCFIOperand(Dest); case MIToken::kw_blockaddress: diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index bb9c76ff0c729..db802cc6e9024 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -778,6 +778,64 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI, if (MCSymbol *Label = CFI.getLabel()) MachineOperand::printSymbol(OS, *Label); break; + case MCCFIInstruction::OpLLVMRegisterPair: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::RegisterPairFields>(); + + OS << "llvm_register_pair "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", "; + printCFIRegister(Fields.Reg1, OS, TRI); + OS << ", " << Fields.Reg1SizeInBits << ", "; + printCFIRegister(Fields.Reg2, OS, TRI); + OS << ", " << Fields.Reg2SizeInBits; + break; + } + case MCCFIInstruction::OpLLVMVectorRegisters: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorRegistersFields>(); + + OS << "llvm_vector_registers "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + for (auto [Reg, Lane, Size] : Fields.VectorRegisters) { + OS << ", "; + printCFIRegister(Reg, OS, TRI); + OS << ", " << Lane << ", " << Size; + } + break; + } + case MCCFIInstruction::OpLLVMVectorOffset: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorOffsetFields>(); + + OS << "llvm_vector_offset "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", " << Fields.RegisterSizeInBits << ", "; + printCFIRegister(Fields.MaskRegister, OS, TRI); + OS << ", " << Fields.MaskRegisterSizeInBits << ", " << Fields.Offset; + break; + } + case MCCFIInstruction::OpLLVMVectorRegisterMask: { + const auto &Fields = + CFI.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>(); + + OS << "llvm_vector_register_mask "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(Fields.Register, OS, TRI); + OS << ", "; + printCFIRegister(Fields.SpillRegister, OS, TRI); + OS << ", " << Fields.SpillRegisterLaneSizeInBits << ", "; + printCFIRegister(Fields.MaskRegister, OS, TRI); + OS << ", " << Fields.MaskRegisterSizeInBits; + break; + } case MCCFIInstruction::OpNegateRAStateWithPC: OS << "negate_ra_sign_state_with_pc "; if (MCSymbol *Label = CFI.getLabel()) diff --git a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp index bca820fa807c8..4d2d2da8a4445 100644 --- a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp +++ b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp @@ -161,6 +161,16 @@ dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) { CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(), Directive.getOffset()); break; + case MCCFIInstruction::OpLLVMRegisterPair: + case MCCFIInstruction::OpLLVMVectorRegisters: + case MCCFIInstruction::OpLLVMVectorOffset: + case MCCFIInstruction::OpLLVMVectorRegisterMask: + // TODO: These should be pretty straightforward to support, but is low + // priority. Similarly the implementation of OpLLVMDefAspaceCfa above + // seem incomplete and should be fixed. + Context->reportWarning(Directive.getLoc(), + "this directive is not supported, ignoring it"); + break; } return CFIP; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index be8c022f39ad1..6c54a9efbf2c6 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -369,6 +369,21 @@ class MCAsmStreamer final : public MCStreamer { void emitCFINegateRAState(SMLoc Loc) override; void emitCFINegateRAStateWithPC(SMLoc Loc) override; void emitCFIReturnColumn(int64_t Register) override; + void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, int64_t R1Size, + int64_t R2, int64_t R2Size, SMLoc Loc) override; + void emitCFILLVMVectorRegisters( + int64_t Register, + std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs, + SMLoc Loc) override; + void emitCFILLVMVectorOffset(int64_t Register, int64_t RegisterSize, + int64_t MaskRegister, int64_t MaskRegisterSize, + int64_t Offset, SMLoc Loc) override; + void emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister, + int64_t SpillRegisterLaneSizeInBits, + int64_t MaskRegister, + int64_t MaskRegisterSizeInBits, + SMLoc Loc) override; + void emitCFILabelDirective(SMLoc Loc, StringRef Name) override; void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; @@ -2101,6 +2116,67 @@ void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2, EmitEOL(); } +void MCAsmStreamer::emitCFILLVMRegisterPair(int64_t Register, int64_t R1, + int64_t R1Size, int64_t R2, + int64_... [truncated] |
| return false; | ||
| } | ||
| | ||
| bool MIParser::parseCFIUnsigned(unsigned &Value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing mir print/parse tests
While these can be represented with .cfi_escape, using these pseudo-cfi instructions makes .s/.mir files more readable, and it is necessary to support updating registers in CFI instructions (something that the AMDGPU backend requires).
15a73c6 to 6f6d434 Compare b3d52d3 to 638fa37 Compare | # RUN: llc -mtriple=amdgcn -mcpu=gfx908 -x=mir -run-pass none -o - | \ | ||
| # RUN: FileCheck %s | ||
| | ||
| # Verify we can parse and emit these CFI pseudos. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This belongs in test/CodeGen/MIR/

While these can be represented with .cfi_escape, using these pseudo-cfi
instructions makes .s/.mir files more readable, and it is necessary to
support updating registers in CFI instructions (something that the
AMDGPU backend requires).