diff --git a/lib/Target/BPF/BPFAsmPrinter.cpp b/lib/Target/BPF/BPFAsmPrinter.cpp index 9397c78f3dff8..705211b486bf7 100644 --- a/lib/Target/BPF/BPFAsmPrinter.cpp +++ b/lib/Target/BPF/BPFAsmPrinter.cpp @@ -40,11 +40,88 @@ class BPFAsmPrinter : public AsmPrinter { : AsmPrinter(TM, std::move(Streamer)) {} StringRef getPassName() const override { return "BPF Assembly Printer"; } + void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O); + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; void EmitInstruction(const MachineInstr *MI) override; }; } // namespace +void BPFAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, + raw_ostream &O) { + const MachineOperand &MO = MI->getOperand(OpNum); + + switch (MO.getType()) { + case MachineOperand::MO_Register: + O << BPFInstPrinter::getRegisterName(MO.getReg()); + break; + + case MachineOperand::MO_Immediate: + O << MO.getImm(); + break; + + case MachineOperand::MO_MachineBasicBlock: + O << *MO.getMBB()->getSymbol(); + break; + + 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: + case MachineOperand::MO_ConstantPoolIndex: + default: + llvm_unreachable(""); + } +} + +bool BPFAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned /*AsmVariant*/, + const char *ExtraCode, raw_ostream &O) { + if (ExtraCode && ExtraCode[0]) + return true; // BPF does not have special modifiers + + printOperand(MI, OpNo, O); + return false; +} + +bool BPFAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNum, unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &O) { + assert(OpNum + 1 < MI->getNumOperands() && "Insufficient operands"); + const MachineOperand &BaseMO = MI->getOperand(OpNum); + const MachineOperand &OffsetMO = MI->getOperand(OpNum + 1); + assert(BaseMO.isReg() && "Unexpected base pointer for inline asm memory operand."); + assert(OffsetMO.isImm() && "Unexpected offset for inline asm memory operand."); + int Offset = OffsetMO.getImm(); + + if (ExtraCode) + return true; // Unknown modifier. + + if (Offset < 0) + O << "(" << BPFInstPrinter::getRegisterName(BaseMO.getReg()) << " - " << -Offset << ")"; + else + O << "(" << BPFInstPrinter::getRegisterName(BaseMO.getReg()) << " + " << Offset << ")"; + + return false; +} + void BPFAsmPrinter::EmitInstruction(const MachineInstr *MI) { BPFMCInstLower MCInstLowering(OutContext, *this); diff --git a/lib/Target/BPF/BPFISelDAGToDAG.cpp b/lib/Target/BPF/BPFISelDAGToDAG.cpp index f48429ee57b0a..1f382f3e73f50 100644 --- a/lib/Target/BPF/BPFISelDAGToDAG.cpp +++ b/lib/Target/BPF/BPFISelDAGToDAG.cpp @@ -48,6 +48,10 @@ class BPFDAGToDAGISel : public SelectionDAGISel { void PreprocessISelDAG() override; + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode, + std::vector &OutOps) override; + + private: // Include the pieces autogenerated from the target description. #include "BPFGenDAGISel.inc" @@ -145,6 +149,26 @@ bool BPFDAGToDAGISel::SelectFIAddr(SDValue Addr, SDValue &Base, return false; } +bool BPFDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintCode, std::vector &OutOps) { + SDValue Op0, Op1; + switch (ConstraintCode) { + default: + return true; + case InlineAsm::Constraint_m: // memory + if (!SelectAddr(Op, Op0, Op1)) + return true; + break; + } + + SDLoc DL(Op); + SDValue AluOp = CurDAG->getTargetConstant(ISD::ADD, DL, MVT::i32);; + OutOps.push_back(Op0); + OutOps.push_back(Op1); + OutOps.push_back(AluOp); + return false; +} + void BPFDAGToDAGISel::Select(SDNode *Node) { unsigned Opcode = Node->getOpcode(); diff --git a/lib/Target/BPF/BPFISelLowering.cpp b/lib/Target/BPF/BPFISelLowering.cpp index 94b3c7a16aa5b..d4e06ddccafab 100644 --- a/lib/Target/BPF/BPFISelLowering.cpp +++ b/lib/Target/BPF/BPFISelLowering.cpp @@ -139,6 +139,22 @@ bool BPFTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) cons return false; } +std::pair +BPFTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + if (Constraint.size() == 1) + // GCC Constraint Letters + switch (Constraint[0]) { + case 'r': // GENERAL_REGS + return std::make_pair(0U, &BPF::GPRRegClass); + default: + break; + } + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { switch (Op.getOpcode()) { case ISD::BR_CC: diff --git a/lib/Target/BPF/BPFISelLowering.h b/lib/Target/BPF/BPFISelLowering.h index 35065f2649557..984843a987997 100644 --- a/lib/Target/BPF/BPFISelLowering.h +++ b/lib/Target/BPF/BPFISelLowering.h @@ -46,6 +46,10 @@ class BPFTargetLowering : public TargetLowering { // with the given GlobalAddress is legal. bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *BB) const override; diff --git a/test/CodeGen/BPF/inline_asm.ll b/test/CodeGen/BPF/inline_asm.ll new file mode 100644 index 0000000000000..7822ac49ed896 --- /dev/null +++ b/test/CodeGen/BPF/inline_asm.ll @@ -0,0 +1,54 @@ +; RUN: llc < %s -march=bpfel -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -march=bpfeb -verify-machineinstrs | FileCheck %s + +; Source code: +; int g[2]; +; +; int test(void *ctx) { +; int a = 4, b; +; unsigned long long c = 333333333333ULL; +; asm volatile("r0 = *(u16 *)skb[%0]" : : "i"(2)); +; asm volatile("r0 = *(u16 *)skb[%0]" : : "r"(a)); +; asm volatile("%0 = %1" : "=r"(b) : "i"(4)); +; asm volatile("%0 = %1 ll" : "=r"(b) : "i"(c)); +; asm volatile("%0 = *(u16 *) %1" : "=r"(b) : "m"(a)); +; asm volatile("%0 = *(u32 *) %1" : "=r"(b) : "m"(g[1])); +; return b; +; } +; + +@g = common global [2 x i32] zeroinitializer, align 4 + +; Function Attrs: nounwind +define i32 @test(i8* nocapture readnone %ctx) local_unnamed_addr #0 { +entry: + %a = alloca i32, align 4 + %0 = bitcast i32* %a to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #2 + store i32 4, i32* %a, align 4 + tail call void asm sideeffect "r0 = *(u16 *)skb[$0]", "i"(i32 2) #2 +; CHECK: r0 = *(u16 *)skb[2] + tail call void asm sideeffect "r0 = *(u16 *)skb[$0]", "r"(i32 4) #2 +; CHECK: r0 = *(u16 *)skb[r1] + %1 = tail call i32 asm sideeffect "$0 = $1", "=r,i"(i32 4) #2 +; CHECK: r1 = 4 + %2 = tail call i32 asm sideeffect "$0 = $1 ll", "=r,i"(i64 333333333333) #2 +; CHECK: r1 = 333333333333 ll + %3 = call i32 asm sideeffect "$0 = *(u16 *) $1", "=r,*m"(i32* nonnull %a) #2 +; CHECK: r1 = *(u16 *) (r10 - 4) + %4 = call i32 asm sideeffect "$0 = *(u32 *) $1", "=r,*m"(i32* getelementptr inbounds ([2 x i32], [2 x i32]* @g, i64 0, i64 1)) #2 +; CHECK: r1 = g ll +; CHECK: r0 = *(u32 *) (r1 + 4) + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #2 + ret i32 %4 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1 + +attributes #0 = { nounwind } +attributes #1 = { argmemonly nounwind } +attributes #2 = { nounwind }