//===-- BPFISelDAGToDAG.cpp - A dag to dag inst selector for BPF ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines a DAG pattern matching instruction selector for BPF, // converting from a legalized dag to a BPF dag. // //===----------------------------------------------------------------------===// #include "BPF.h" #include "BPFRegisterInfo.h" #include "BPFSubtarget.h" #include "BPFTargetMachine.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAGISel.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" using namespace llvm; #define DEBUG_TYPE "bpf-isel" // Instruction Selector Implementation namespace { class BPFDAGToDAGISel : public SelectionDAGISel { /// Subtarget - Keep a pointer to the BPFSubtarget around so that we can /// make the right decision when generating code for different subtargets. const BPFSubtarget *Subtarget; public: explicit BPFDAGToDAGISel(BPFTargetMachine &TM) : SelectionDAGISel(TM), Subtarget(nullptr) { curr_func_ = nullptr; } StringRef getPassName() const override { return "BPF DAG->DAG Pattern Instruction Selection"; } bool runOnMachineFunction(MachineFunction &MF) override { // Reset the subtarget each time through. Subtarget = &MF.getSubtarget<BPFSubtarget>(); return SelectionDAGISel::runOnMachineFunction(MF); } void PreprocessISelDAG() override; bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode, std::vector<SDValue> &OutOps) override; private: // Include the pieces autogenerated from the target description. #include "BPFGenDAGISel.inc" void Select(SDNode *N) override; // Complex Pattern for address selection. bool SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset); bool SelectFIAddr(SDValue Addr, SDValue &Base, SDValue &Offset); // Node preprocessing cases void PreprocessLoad(SDNode *Node, SelectionDAG::allnodes_iterator &I); void PreprocessCopyToReg(SDNode *Node); void PreprocessTrunc(SDNode *Node, SelectionDAG::allnodes_iterator &I); // Find constants from a constant structure typedef std::vector<unsigned char> val_vec_type; bool fillGenericConstant(const DataLayout &DL, const Constant *CV, val_vec_type &Vals, uint64_t Offset); bool fillConstantDataArray(const DataLayout &DL, const ConstantDataArray *CDA, val_vec_type &Vals, int Offset); bool fillConstantArray(const DataLayout &DL, const ConstantArray *CA, val_vec_type &Vals, int Offset); bool fillConstantStruct(const DataLayout &DL, const ConstantStruct *CS, val_vec_type &Vals, int Offset); bool getConstantFieldValue(const GlobalAddressSDNode *Node, uint64_t Offset, uint64_t Size, unsigned char *ByteSeq); bool checkLoadDef(unsigned DefReg, unsigned match_load_op); // Mapping from ConstantStruct global value to corresponding byte-list values std::map<const void *, val_vec_type> cs_vals_; // Mapping from vreg to load memory opcode std::map<unsigned, unsigned> load_to_vreg_; // Current function const Function *curr_func_; }; } // namespace // ComplexPattern used on BPF Load/Store instructions bool BPFDAGToDAGISel::SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset) { // if Address is FI, get the TargetFrameIndex. SDLoc DL(Addr); if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) { Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); Offset = CurDAG->getTargetConstant(0, DL, MVT::i64); return true; } if (Addr.getOpcode() == ISD::TargetExternalSymbol || Addr.getOpcode() == ISD::TargetGlobalAddress) return false; // Addresses of the form Addr+const or Addr|const if (CurDAG->isBaseWithConstantOffset(Addr)) { ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1)); if (isInt<16>(CN->getSExtValue())) { // If the first operand is a FI, get the TargetFI Node if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); else Base = Addr.getOperand(0); Offset = CurDAG->getTargetConstant(CN->getSExtValue(), DL, MVT::i64); return true; } } Base = Addr; Offset = CurDAG->getTargetConstant(0, DL, MVT::i64); return true; } // ComplexPattern used on BPF FI instruction bool BPFDAGToDAGISel::SelectFIAddr(SDValue Addr, SDValue &Base, SDValue &Offset) { SDLoc DL(Addr); if (!CurDAG->isBaseWithConstantOffset(Addr)) return false; // Addresses of the form Addr+const or Addr|const ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1)); if (isInt<16>(CN->getSExtValue())) { // If the first operand is a FI, get the TargetFI Node if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i64); else return false; Offset = CurDAG->getTargetConstant(CN->getSExtValue(), DL, MVT::i64); return true; } return false; } bool BPFDAGToDAGISel::SelectInlineAsmMemoryOperand( const SDValue &Op, unsigned ConstraintCode, std::vector<SDValue> &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(); // If we have a custom node, we already have selected! if (Node->isMachineOpcode()) { LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n'); return; } // tablegen selection should be handled here. switch (Opcode) { default: break; case ISD::SDIV: { DebugLoc Empty; const DebugLoc &DL = Node->getDebugLoc(); if (DL != Empty) errs() << "Error at line " << DL.getLine() << ": "; else errs() << "Error: "; errs() << "Unsupport signed division for DAG: "; Node->print(errs(), CurDAG); errs() << "Please convert to unsigned div/mod.\n"; break; } case ISD::INTRINSIC_W_CHAIN: { unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); switch (IntNo) { case Intrinsic::bpf_load_byte: case Intrinsic::bpf_load_half: case Intrinsic::bpf_load_word: { SDLoc DL(Node); SDValue Chain = Node->getOperand(0); SDValue N1 = Node->getOperand(1); SDValue Skb = Node->getOperand(2); SDValue N3 = Node->getOperand(3); SDValue R6Reg = CurDAG->getRegister(BPF::R6, MVT::i64); Chain = CurDAG->getCopyToReg(Chain, DL, R6Reg, Skb, SDValue()); Node = CurDAG->UpdateNodeOperands(Node, Chain, N1, R6Reg, N3); break; } } break; } case ISD::FrameIndex: { int FI = cast<FrameIndexSDNode>(Node)->getIndex(); EVT VT = Node->getValueType(0); SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); unsigned Opc = BPF::MOV_rr; if (Node->hasOneUse()) { CurDAG->SelectNodeTo(Node, Opc, VT, TFI); return; } ReplaceNode(Node, CurDAG->getMachineNode(Opc, SDLoc(Node), VT, TFI)); return; } } // Select the default instruction SelectCode(Node); } void BPFDAGToDAGISel::PreprocessLoad(SDNode *Node, SelectionDAG::allnodes_iterator &I) { union { uint8_t c[8]; uint16_t s; uint32_t i; uint64_t d; } new_val; // hold up the constant values replacing loads. bool to_replace = false; SDLoc DL(Node); const LoadSDNode *LD = cast<LoadSDNode>(Node); uint64_t size = LD->getMemOperand()->getSize(); if (!size || size > 8 || (size & (size - 1))) return; SDNode *LDAddrNode = LD->getOperand(1).getNode(); // Match LDAddr against either global_addr or (global_addr + offset) unsigned opcode = LDAddrNode->getOpcode(); if (opcode == ISD::ADD) { SDValue OP1 = LDAddrNode->getOperand(0); SDValue OP2 = LDAddrNode->getOperand(1); // We want to find the pattern global_addr + offset SDNode *OP1N = OP1.getNode(); if (OP1N->getOpcode() <= ISD::BUILTIN_OP_END || OP1N->getNumOperands() == 0) return; LLVM_DEBUG(dbgs() << "Check candidate load: "; LD->dump(); dbgs() << '\n'); const GlobalAddressSDNode *GADN = dyn_cast<GlobalAddressSDNode>(OP1N->getOperand(0).getNode()); const ConstantSDNode *CDN = dyn_cast<ConstantSDNode>(OP2.getNode()); if (GADN && CDN) to_replace = getConstantFieldValue(GADN, CDN->getZExtValue(), size, new_val.c); } else if (LDAddrNode->getOpcode() > ISD::BUILTIN_OP_END && LDAddrNode->getNumOperands() > 0) { LLVM_DEBUG(dbgs() << "Check candidate load: "; LD->dump(); dbgs() << '\n'); SDValue OP1 = LDAddrNode->getOperand(0); if (const GlobalAddressSDNode *GADN = dyn_cast<GlobalAddressSDNode>(OP1.getNode())) to_replace = getConstantFieldValue(GADN, 0, size, new_val.c); } if (!to_replace) return; // replacing the old with a new value uint64_t val; if (size == 1) val = new_val.c[0]; else if (size == 2) val = new_val.s; else if (size == 4) val = new_val.i; else { val = new_val.d; } LLVM_DEBUG(dbgs() << "Replacing load of size " << size << " with constant " << val << '\n'); SDValue NVal = CurDAG->getConstant(val, DL, MVT::i64); // After replacement, the current node is dead, we need to // go backward one step to make iterator still work I--; SDValue From[] = {SDValue(Node, 0), SDValue(Node, 1)}; SDValue To[] = {NVal, NVal}; CurDAG->ReplaceAllUsesOfValuesWith(From, To, 2); I++; // It is safe to delete node now CurDAG->DeleteNode(Node); } void BPFDAGToDAGISel::PreprocessISelDAG() { // Iterate through all nodes, interested in the following cases: // // . loads from ConstantStruct or ConstantArray of constructs // which can be turns into constant itself, with this we can // avoid reading from read-only section at runtime. // // . reg truncating is often the result of 8/16/32bit->64bit or // 8/16bit->32bit conversion. If the reg value is loaded with // masked byte width, the AND operation can be removed since // BPF LOAD already has zero extension. // // This also solved a correctness issue. // In BPF socket-related program, e.g., __sk_buff->{data, data_end} // are 32-bit registers, but later on, kernel verifier will rewrite // it with 64-bit value. Therefore, truncating the value after the // load will result in incorrect code. // clear the load_to_vreg_ map so that we have a clean start // for this function. if (!curr_func_) { curr_func_ = FuncInfo->Fn; } else if (curr_func_ != FuncInfo->Fn) { load_to_vreg_.clear(); curr_func_ = FuncInfo->Fn; } for (SelectionDAG::allnodes_iterator I = CurDAG->allnodes_begin(), E = CurDAG->allnodes_end(); I != E;) { SDNode *Node = &*I++; unsigned Opcode = Node->getOpcode(); if (Opcode == ISD::LOAD) PreprocessLoad(Node, I); else if (Opcode == ISD::CopyToReg) PreprocessCopyToReg(Node); else if (Opcode == ISD::AND) PreprocessTrunc(Node, I); } } bool BPFDAGToDAGISel::getConstantFieldValue(const GlobalAddressSDNode *Node, uint64_t Offset, uint64_t Size, unsigned char *ByteSeq) { const GlobalVariable *V = dyn_cast<GlobalVariable>(Node->getGlobal()); if (!V || !V->hasInitializer()) return false; const Constant *Init = V->getInitializer(); const DataLayout &DL = CurDAG->getDataLayout(); val_vec_type TmpVal; auto it = cs_vals_.find(static_cast<const void *>(Init)); if (it != cs_vals_.end()) { TmpVal = it->second; } else { uint64_t total_size = 0; if (const ConstantStruct *CS = dyn_cast<ConstantStruct>(Init)) total_size = DL.getStructLayout(cast<StructType>(CS->getType()))->getSizeInBytes(); else if (const ConstantArray *CA = dyn_cast<ConstantArray>(Init)) total_size = DL.getTypeAllocSize(CA->getType()->getElementType()) * CA->getNumOperands(); else return false; val_vec_type Vals(total_size, 0); if (fillGenericConstant(DL, Init, Vals, 0) == false) return false; cs_vals_[static_cast<const void *>(Init)] = Vals; TmpVal = std::move(Vals); } // test whether host endianness matches target union { uint8_t c[2]; uint16_t s; } test_buf; uint16_t test_val = 0x2345; if (DL.isLittleEndian()) support::endian::write16le(test_buf.c, test_val); else support::endian::write16be(test_buf.c, test_val); bool endian_match = test_buf.s == test_val; for (uint64_t i = Offset, j = 0; i < Offset + Size; i++, j++) ByteSeq[j] = endian_match ? TmpVal[i] : TmpVal[Offset + Size - 1 - j]; return true; } bool BPFDAGToDAGISel::fillGenericConstant(const DataLayout &DL, const Constant *CV, val_vec_type &Vals, uint64_t Offset) { uint64_t Size = DL.getTypeAllocSize(CV->getType()); if (isa<ConstantAggregateZero>(CV) || isa<UndefValue>(CV)) return true; // already done if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { uint64_t val = CI->getZExtValue(); LLVM_DEBUG(dbgs() << "Byte array at offset " << Offset << " with value " << val << '\n'); if (Size > 8 || (Size & (Size - 1))) return false; // Store based on target endian for (uint64_t i = 0; i < Size; ++i) { Vals[Offset + i] = DL.isLittleEndian() ? ((val >> (i * 8)) & 0xFF) : ((val >> ((Size - i - 1) * 8)) & 0xFF); } return true; } if (const ConstantDataArray *CDA = dyn_cast<ConstantDataArray>(CV)) return fillConstantDataArray(DL, CDA, Vals, Offset); if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) return fillConstantArray(DL, CA, Vals, Offset); if (const ConstantStruct *CVS = dyn_cast<ConstantStruct>(CV)) return fillConstantStruct(DL, CVS, Vals, Offset); return false; } bool BPFDAGToDAGISel::fillConstantDataArray(const DataLayout &DL, const ConstantDataArray *CDA, val_vec_type &Vals, int Offset) { for (unsigned i = 0, e = CDA->getNumElements(); i != e; ++i) { if (fillGenericConstant(DL, CDA->getElementAsConstant(i), Vals, Offset) == false) return false; Offset += DL.getTypeAllocSize(CDA->getElementAsConstant(i)->getType()); } return true; } bool BPFDAGToDAGISel::fillConstantArray(const DataLayout &DL, const ConstantArray *CA, val_vec_type &Vals, int Offset) { for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i) { if (fillGenericConstant(DL, CA->getOperand(i), Vals, Offset) == false) return false; Offset += DL.getTypeAllocSize(CA->getOperand(i)->getType()); } return true; } bool BPFDAGToDAGISel::fillConstantStruct(const DataLayout &DL, const ConstantStruct *CS, val_vec_type &Vals, int Offset) { const StructLayout *Layout = DL.getStructLayout(CS->getType()); for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) { const Constant *Field = CS->getOperand(i); uint64_t SizeSoFar = Layout->getElementOffset(i); if (fillGenericConstant(DL, Field, Vals, Offset + SizeSoFar) == false) return false; } return true; } void BPFDAGToDAGISel::PreprocessCopyToReg(SDNode *Node) { const RegisterSDNode *RegN = dyn_cast<RegisterSDNode>(Node->getOperand(1)); if (!RegN || !TargetRegisterInfo::isVirtualRegister(RegN->getReg())) return; const LoadSDNode *LD = dyn_cast<LoadSDNode>(Node->getOperand(2)); if (!LD) return; // Assign a load value to a virtual register. record its load width unsigned mem_load_op = 0; switch (LD->getMemOperand()->getSize()) { default: return; case 4: mem_load_op = BPF::LDW; break; case 2: mem_load_op = BPF::LDH; break; case 1: mem_load_op = BPF::LDB; break; } LLVM_DEBUG(dbgs() << "Find Load Value to VReg " << TargetRegisterInfo::virtReg2Index(RegN->getReg()) << '\n'); load_to_vreg_[RegN->getReg()] = mem_load_op; } void BPFDAGToDAGISel::PreprocessTrunc(SDNode *Node, SelectionDAG::allnodes_iterator &I) { ConstantSDNode *MaskN = dyn_cast<ConstantSDNode>(Node->getOperand(1)); if (!MaskN) return; // The Reg operand should be a virtual register, which is defined // outside the current basic block. DAG combiner has done a pretty // good job in removing truncating inside a single basic block except // when the Reg operand comes from bpf_load_[byte | half | word] for // which the generic optimizer doesn't understand their results are // zero extended. SDValue BaseV = Node->getOperand(0); if (BaseV.getOpcode() == ISD::INTRINSIC_W_CHAIN) { unsigned IntNo = cast<ConstantSDNode>(BaseV->getOperand(1))->getZExtValue(); uint64_t MaskV = MaskN->getZExtValue(); if (!((IntNo == Intrinsic::bpf_load_byte && MaskV == 0xFF) || (IntNo == Intrinsic::bpf_load_half && MaskV == 0xFFFF) || (IntNo == Intrinsic::bpf_load_word && MaskV == 0xFFFFFFFF))) return; LLVM_DEBUG(dbgs() << "Remove the redundant AND operation in: "; Node->dump(); dbgs() << '\n'); I--; CurDAG->ReplaceAllUsesWith(SDValue(Node, 0), BaseV); I++; CurDAG->DeleteNode(Node); return; } // Multiple basic blocks case. if (BaseV.getOpcode() != ISD::CopyFromReg) return; unsigned match_load_op = 0; switch (MaskN->getZExtValue()) { default: return; case 0xFFFFFFFF: match_load_op = BPF::LDW; break; case 0xFFFF: match_load_op = BPF::LDH; break; case 0xFF: match_load_op = BPF::LDB; break; } const RegisterSDNode *RegN = dyn_cast<RegisterSDNode>(BaseV.getNode()->getOperand(1)); if (!RegN || !TargetRegisterInfo::isVirtualRegister(RegN->getReg())) return; unsigned AndOpReg = RegN->getReg(); LLVM_DEBUG(dbgs() << "Examine " << printReg(AndOpReg) << '\n'); // Examine the PHI insns in the MachineBasicBlock to found out the // definitions of this virtual register. At this stage (DAG2DAG // transformation), only PHI machine insns are available in the machine basic // block. MachineBasicBlock *MBB = FuncInfo->MBB; MachineInstr *MII = nullptr; for (auto &MI : *MBB) { for (unsigned i = 0; i < MI.getNumOperands(); ++i) { const MachineOperand &MOP = MI.getOperand(i); if (!MOP.isReg() || !MOP.isDef()) continue; unsigned Reg = MOP.getReg(); if (TargetRegisterInfo::isVirtualRegister(Reg) && Reg == AndOpReg) { MII = &MI; break; } } } if (MII == nullptr) { // No phi definition in this block. if (!checkLoadDef(AndOpReg, match_load_op)) return; } else { // The PHI node looks like: // %2 = PHI %0, <%bb.1>, %1, <%bb.3> // Trace each incoming definition, e.g., (%0, %bb.1) and (%1, %bb.3) // The AND operation can be removed if both %0 in %bb.1 and %1 in // %bb.3 are defined with a load matching the MaskN. LLVM_DEBUG(dbgs() << "Check PHI Insn: "; MII->dump(); dbgs() << '\n'); unsigned PrevReg = -1; for (unsigned i = 0; i < MII->getNumOperands(); ++i) { const MachineOperand &MOP = MII->getOperand(i); if (MOP.isReg()) { if (MOP.isDef()) continue; PrevReg = MOP.getReg(); if (!TargetRegisterInfo::isVirtualRegister(PrevReg)) return; if (!checkLoadDef(PrevReg, match_load_op)) return; } } } LLVM_DEBUG(dbgs() << "Remove the redundant AND operation in: "; Node->dump(); dbgs() << '\n'); I--; CurDAG->ReplaceAllUsesWith(SDValue(Node, 0), BaseV); I++; CurDAG->DeleteNode(Node); } bool BPFDAGToDAGISel::checkLoadDef(unsigned DefReg, unsigned match_load_op) { auto it = load_to_vreg_.find(DefReg); if (it == load_to_vreg_.end()) return false; // The definition of register is not exported yet. return it->second == match_load_op; } FunctionPass *llvm::createBPFISelDag(BPFTargetMachine &TM) { return new BPFDAGToDAGISel(TM); }