From d5fe57d2f980c6bd1a61450f99c254a76d0f1683 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 21 Aug 2008 01:41:07 +0000 Subject: [PATCH] Basic fast-isel support for instructions with constant int operands. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@55099 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CodeGen/FastISel.h | 29 +++++++-- lib/CodeGen/SelectionDAG/FastISel.cpp | 90 +++++++++++++++++++++------ test/CodeGen/X86/fast-isel.ll | 3 +- utils/TableGen/FastISelEmitter.cpp | 53 ++++++++++++---- 4 files changed, 137 insertions(+), 38 deletions(-) diff --git a/include/llvm/CodeGen/FastISel.h b/include/llvm/CodeGen/FastISel.h index 12ae3a80e1b..cd6a6d61996 100644 --- a/include/llvm/CodeGen/FastISel.h +++ b/include/llvm/CodeGen/FastISel.h @@ -88,8 +88,15 @@ protected: /// virtual unsigned FastEmit_ri(MVT::SimpleValueType VT, ISD::NodeType Opcode, - unsigned Op0, uint64_t Imm, - MVT::SimpleValueType ImmType); + unsigned Op0, uint64_t Imm); + + /// FastEmit_rri - This method is called by target-independent code + /// to request that an instruction with the given type, opcode, and + /// register and immediate operands be emitted. + /// + virtual unsigned FastEmit_rri(MVT::SimpleValueType VT, + ISD::NodeType Opcode, + unsigned Op0, unsigned Op1, uint64_t Imm); /// FastEmit_ri_ - This method is a wrapper of FastEmit_ri. It first tries /// to emit an instruction with an immediate operand using FastEmit_ri. @@ -106,20 +113,34 @@ protected: unsigned FastEmitInst_(unsigned MachineInstOpcode, const TargetRegisterClass *RC); - /// FastEmitInst_ - Emit a MachineInstr with one register operand + /// FastEmitInst_r - Emit a MachineInstr with one register operand /// and a result register in the given register class. /// unsigned FastEmitInst_r(unsigned MachineInstOpcode, const TargetRegisterClass *RC, unsigned Op0); - /// FastEmitInst_ - Emit a MachineInstr with two register operands + /// FastEmitInst_rr - Emit a MachineInstr with two register operands /// and a result register in the given register class. /// unsigned FastEmitInst_rr(unsigned MachineInstOpcode, const TargetRegisterClass *RC, unsigned Op0, unsigned Op1); + /// FastEmitInst_ri - Emit a MachineInstr with two register operands + /// and a result register in the given register class. + /// + unsigned FastEmitInst_ri(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, uint64_t Imm); + + /// FastEmitInst_rri - Emit a MachineInstr with two register operands, + /// an immediate, and a result register in the given register class. + /// + unsigned FastEmitInst_rri(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, unsigned Op1, uint64_t Imm); + private: unsigned createResultReg(const TargetRegisterClass *RC); diff --git a/lib/CodeGen/SelectionDAG/FastISel.cpp b/lib/CodeGen/SelectionDAG/FastISel.cpp index dc54485a924..b4271fe6d2c 100644 --- a/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -26,17 +26,35 @@ using namespace llvm; /// bool FastISel::SelectBinaryOp(Instruction *I, ISD::NodeType ISDOpcode, DenseMap &ValueMap) { - unsigned Op0 = ValueMap[I->getOperand(0)]; - unsigned Op1 = ValueMap[I->getOperand(1)]; - if (Op0 == 0 || Op1 == 0) - // Unhandled operand. Halt "fast" selection and bail. - return false; - MVT VT = MVT::getMVT(I->getType(), /*HandleUnknown=*/true); if (VT == MVT::Other || !VT.isSimple()) // Unhandled type. Halt "fast" selection and bail. return false; + unsigned Op0 = ValueMap[I->getOperand(0)]; + if (Op0 == 0) + // Unhandled operand. Halt "fast" selection and bail. + return false; + + // Check if the second operand is a constant and handle it appropriately. + if (ConstantInt *CI = dyn_cast(I->getOperand(1))) { + unsigned ResultReg = FastEmit_ri_(VT.getSimpleVT(), ISDOpcode, Op0, + CI->getZExtValue(), VT.getSimpleVT()); + if (ResultReg == 0) + // Target-specific code wasn't able to find a machine opcode for + // the given ISD opcode and type. Halt "fast" selection and bail. + return false; + + // We successfully emitted code for the given LLVM Instruction. + ValueMap[I] = ResultReg; + return true; + } + + unsigned Op1 = ValueMap[I->getOperand(1)]; + if (Op1 == 0) + // Unhandled operand. Halt "fast" selection and bail. + return false; + unsigned ResultReg = FastEmit_rr(VT.getSimpleVT(), ISDOpcode, Op0, Op1); if (ResultReg == 0) // Target-specific code wasn't able to find a machine opcode for @@ -67,7 +85,7 @@ bool FastISel::SelectGetElementPtr(Instruction *I, uint64_t Offs = TD.getStructLayout(StTy)->getElementOffset(Field); // FIXME: This can be optimized by combining the add with a // subsequent one. - N = FastEmit_ri(VT.getSimpleVT(), ISD::ADD, N, Offs, VT.getSimpleVT()); + N = FastEmit_ri_(VT.getSimpleVT(), ISD::ADD, N, Offs, VT.getSimpleVT()); if (N == 0) // Unhandled operand. Halt "fast" selection and bail. return false; @@ -81,7 +99,7 @@ bool FastISel::SelectGetElementPtr(Instruction *I, if (CI->getZExtValue() == 0) continue; uint64_t Offs = TD.getABITypeSize(Ty)*cast(CI)->getSExtValue(); - N = FastEmit_ri(VT.getSimpleVT(), ISD::ADD, N, Offs, VT.getSimpleVT()); + N = FastEmit_ri_(VT.getSimpleVT(), ISD::ADD, N, Offs, VT.getSimpleVT()); if (N == 0) // Unhandled operand. Halt "fast" selection and bail. return false; @@ -108,8 +126,8 @@ bool FastISel::SelectGetElementPtr(Instruction *I, // FIXME: If multiple is power of two, turn it into a shift. The // optimization should be in FastEmit_ri? - IdxN = FastEmit_ri(VT.getSimpleVT(), ISD::MUL, IdxN, - ElementSize, VT.getSimpleVT()); + IdxN = FastEmit_ri_(VT.getSimpleVT(), ISD::MUL, IdxN, + ElementSize, VT.getSimpleVT()); if (IdxN == 0) // Unhandled operand. Halt "fast" selection and bail. return false; @@ -226,13 +244,18 @@ unsigned FastISel::FastEmit_rr(MVT::SimpleValueType, ISD::NodeType, return 0; } -unsigned FastISel::FastEmit_i(MVT::SimpleValueType, uint64_t) { +unsigned FastISel::FastEmit_i(MVT::SimpleValueType, uint64_t /*Imm*/) { return 0; } unsigned FastISel::FastEmit_ri(MVT::SimpleValueType, ISD::NodeType, - unsigned /*Op0*/, uint64_t Imm, - MVT::SimpleValueType ImmType) { + unsigned /*Op0*/, uint64_t /*Imm*/) { + return 0; +} + +unsigned FastISel::FastEmit_rri(MVT::SimpleValueType, ISD::NodeType, + unsigned /*Op0*/, unsigned /*Op1*/, + uint64_t /*Imm*/) { return 0; } @@ -241,20 +264,27 @@ unsigned FastISel::FastEmit_ri(MVT::SimpleValueType, ISD::NodeType, /// If that fails, it materializes the immediate into a register and try /// FastEmit_rr instead. unsigned FastISel::FastEmit_ri_(MVT::SimpleValueType VT, ISD::NodeType Opcode, - unsigned Op0, uint64_t Imm, - MVT::SimpleValueType ImmType) { + unsigned Op0, uint64_t Imm, + MVT::SimpleValueType ImmType) { unsigned ResultReg = 0; // First check if immediate type is legal. If not, we can't use the ri form. if (TLI.getOperationAction(ISD::Constant, ImmType) == TargetLowering::Legal) - ResultReg = FastEmit_ri(VT, Opcode, Op0, Imm, ImmType); + ResultReg = FastEmit_ri(VT, Opcode, Op0, Imm); if (ResultReg != 0) return ResultReg; - return FastEmit_rr(VT, Opcode, Op0, FastEmit_i(ImmType, Imm)); + unsigned MaterialReg = FastEmit_i(ImmType, Imm); + if (MaterialReg == 0) + return 0; + return FastEmit_rr(VT, Opcode, Op0, MaterialReg); +} + +unsigned FastISel::createResultReg(const TargetRegisterClass* RC) { + return MRI.createVirtualRegister(RC); } unsigned FastISel::FastEmitInst_(unsigned MachineInstOpcode, const TargetRegisterClass* RC) { - unsigned ResultReg = MRI.createVirtualRegister(RC); + unsigned ResultReg = createResultReg(RC); const TargetInstrDesc &II = TII.get(MachineInstOpcode); BuildMI(MBB, II, ResultReg); @@ -264,7 +294,7 @@ unsigned FastISel::FastEmitInst_(unsigned MachineInstOpcode, unsigned FastISel::FastEmitInst_r(unsigned MachineInstOpcode, const TargetRegisterClass *RC, unsigned Op0) { - unsigned ResultReg = MRI.createVirtualRegister(RC); + unsigned ResultReg = createResultReg(RC); const TargetInstrDesc &II = TII.get(MachineInstOpcode); BuildMI(MBB, II, ResultReg).addReg(Op0); @@ -274,9 +304,29 @@ unsigned FastISel::FastEmitInst_r(unsigned MachineInstOpcode, unsigned FastISel::FastEmitInst_rr(unsigned MachineInstOpcode, const TargetRegisterClass *RC, unsigned Op0, unsigned Op1) { - unsigned ResultReg = MRI.createVirtualRegister(RC); + unsigned ResultReg = createResultReg(RC); const TargetInstrDesc &II = TII.get(MachineInstOpcode); BuildMI(MBB, II, ResultReg).addReg(Op0).addReg(Op1); return ResultReg; } + +unsigned FastISel::FastEmitInst_ri(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, uint64_t Imm) { + unsigned ResultReg = createResultReg(RC); + const TargetInstrDesc &II = TII.get(MachineInstOpcode); + + BuildMI(MBB, II, ResultReg).addReg(Op0).addImm(Imm); + return ResultReg; +} + +unsigned FastISel::FastEmitInst_rri(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, unsigned Op1, uint64_t Imm) { + unsigned ResultReg = createResultReg(RC); + const TargetInstrDesc &II = TII.get(MachineInstOpcode); + + BuildMI(MBB, II, ResultReg).addReg(Op0).addReg(Op1).addImm(Imm); + return ResultReg; +} diff --git a/test/CodeGen/X86/fast-isel.ll b/test/CodeGen/X86/fast-isel.ll index 6eeffc523f4..ba7e861600a 100644 --- a/test/CodeGen/X86/fast-isel.ll +++ b/test/CodeGen/X86/fast-isel.ll @@ -16,10 +16,11 @@ fast: %t3 = and i32 %t2, %s %t4 = or i32 %t3, %s %t5 = xor i32 %t4, %s + %t6 = add i32 %t5, 2 br label %exit exit: - ret i32 %t5 + ret i32 %t6 } define double @bar(double* %p, double* %q) { diff --git a/utils/TableGen/FastISelEmitter.cpp b/utils/TableGen/FastISelEmitter.cpp index 406fb6edff3..cbe77f6d5df 100644 --- a/utils/TableGen/FastISelEmitter.cpp +++ b/utils/TableGen/FastISelEmitter.cpp @@ -66,16 +66,31 @@ struct OperandsSignature { const CodeGenRegisterClass *DstRC) { for (unsigned i = 0, e = InstPatNode->getNumChildren(); i != e; ++i) { TreePatternNode *Op = InstPatNode->getChild(i); - if (!Op->isLeaf()) - return false; // For now, filter out any operand with a predicate. if (!Op->getPredicateFn().empty()) return false; + // For now, filter out any operand with multiple values. + if (Op->getExtTypes().size() != 1) + return false; + // For now, all the operands must have the same type. + if (Op->getTypeNum(0) != VT) + return false; + if (!Op->isLeaf()) { + if (Op->getOperator()->getName() == "imm") { + Operands.push_back("i"); + return true; + } + // For now, ignore fpimm and other non-leaf nodes. + return false; + } DefInit *OpDI = dynamic_cast(Op->getLeafValue()); if (!OpDI) return false; Record *OpLeafRec = OpDI->getDef(); - // For now, only accept register operands. + // TODO: handle instructions which have physreg operands. + if (OpLeafRec->isSubClassOf("Register")) + return false; + // For now, the only other thing we accept is register operands. if (!OpLeafRec->isSubClassOf("RegisterClass")) return false; // For now, require the register operands' register classes to all @@ -86,9 +101,6 @@ struct OperandsSignature { // For now, all the operands must have the same register class. if (DstRC != RC) return false; - // For now, all the operands must have the same type. - if (Op->getTypeNum(0) != VT) - return false; Operands.push_back("r"); } return true; @@ -98,6 +110,8 @@ struct OperandsSignature { for (unsigned i = 0, e = Operands.size(); i != e; ++i) { if (Operands[i] == "r") { OS << "unsigned Op" << i; + } else if (Operands[i] == "i") { + OS << "uint64_t imm" << i; } else { assert("Unknown operand kind!"); abort(); @@ -111,6 +125,8 @@ struct OperandsSignature { for (unsigned i = 0, e = Operands.size(); i != e; ++i) { if (Operands[i] == "r") { OS << "Op" << i; + } else if (Operands[i] == "i") { + OS << "imm" << i; } else { assert("Unknown operand kind!"); abort(); @@ -239,13 +255,16 @@ void FastISelEmitter::run(std::ostream &OS) { MVT::SimpleValueType VT = TI->first; OS << " unsigned FastEmit_" << getLegalCName(Opcode) - << "_" << getLegalCName(getName(VT)) << "("; + << "_" << getLegalCName(getName(VT)) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "("; Operands.PrintParameters(OS); OS << ");\n"; } - OS << " unsigned FastEmit_" << getLegalCName(Opcode) - << "(MVT::SimpleValueType VT"; + OS << " unsigned FastEmit_" << getLegalCName(Opcode) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "(MVT::SimpleValueType VT"; if (!Operands.empty()) OS << ", "; Operands.PrintParameters(OS); @@ -293,7 +312,9 @@ void FastISelEmitter::run(std::ostream &OS) { OS << "unsigned FastISel::FastEmit_" << getLegalCName(Opcode) - << "_" << getLegalCName(getName(VT)) << "("; + << "_" << getLegalCName(getName(VT)) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "("; Operands.PrintParameters(OS); OS << ") {\n"; OS << " return FastEmitInst_"; @@ -310,7 +331,9 @@ void FastISelEmitter::run(std::ostream &OS) { // Emit one function for the opcode that demultiplexes based on the type. OS << "unsigned FastISel::FastEmit_" - << getLegalCName(Opcode) << "(MVT::SimpleValueType VT"; + << getLegalCName(Opcode) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "(MVT::SimpleValueType VT"; if (!Operands.empty()) OS << ", "; Operands.PrintParameters(OS); @@ -321,7 +344,9 @@ void FastISelEmitter::run(std::ostream &OS) { MVT::SimpleValueType VT = TI->first; std::string TypeName = getName(VT); OS << " case " << TypeName << ": return FastEmit_" - << getLegalCName(Opcode) << "_" << getLegalCName(TypeName) << "("; + << getLegalCName(Opcode) << "_" << getLegalCName(TypeName) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "("; Operands.PrintArguments(OS); OS << ");\n"; } @@ -346,7 +371,9 @@ void FastISelEmitter::run(std::ostream &OS) { const std::string &Opcode = I->first; OS << " case " << Opcode << ": return FastEmit_" - << getLegalCName(Opcode) << "(VT"; + << getLegalCName(Opcode) << "_"; + Operands.PrintManglingSuffix(OS); + OS << "(VT"; if (!Operands.empty()) OS << ", "; Operands.PrintArguments(OS);