//===- LowerPacked.cpp - Implementation of LowerPacked Transform ---------===// // // The LLVM Compiler Infrastructure // // This file was developed by Brad Jones and is distributed under // the University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements lowering Packed datatypes into more primitive // Packed datatypes, and finally to scalar operations. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Scalar.h" #include "llvm/Argument.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" #include "llvm/Function.h" #include "llvm/Instructions.h" #include "llvm/Pass.h" #include "llvm/Support/InstVisitor.h" #include "llvm/Support/Streams.h" #include "llvm/ADT/StringExtras.h" #include #include #include using namespace llvm; namespace { /// This pass converts packed operators to an /// equivalent operations on smaller packed data, to possibly /// scalar operations. Currently it supports lowering /// to scalar operations. /// /// @brief Transforms packed instructions to simpler instructions. /// class LowerPacked : public FunctionPass, public InstVisitor { public: /// @brief Lowers packed operations to scalar operations. /// @param F The fuction to process virtual bool runOnFunction(Function &F); /// @brief Lowers packed load instructions. /// @param LI the load instruction to convert void visitLoadInst(LoadInst& LI); /// @brief Lowers packed store instructions. /// @param SI the store instruction to convert void visitStoreInst(StoreInst& SI); /// @brief Lowers packed binary operations. /// @param BO the binary operator to convert void visitBinaryOperator(BinaryOperator& BO); /// @brief Lowers packed icmp operations. /// @param CI the icmp operator to convert void visitICmpInst(ICmpInst& IC); /// @brief Lowers packed select instructions. /// @param SELI the select operator to convert void visitSelectInst(SelectInst& SELI); /// @brief Lowers packed extractelement instructions. /// @param EI the extractelement operator to convert void visitExtractElementInst(ExtractElementInst& EE); /// @brief Lowers packed insertelement instructions. /// @param EI the insertelement operator to convert void visitInsertElementInst(InsertElementInst& IE); /// This function asserts if the instruction is a PackedType but /// is handled by another function. /// /// @brief Asserts if PackedType instruction is not handled elsewhere. /// @param I the unhandled instruction void visitInstruction(Instruction &I) { if (isa(I.getType())) cerr << "Unhandled Instruction with Packed ReturnType: " << I << '\n'; } private: /// @brief Retrieves lowered values for a packed value. /// @param val the packed value /// @return the lowered values std::vector& getValues(Value* val); /// @brief Sets lowered values for a packed value. /// @param val the packed value /// @param values the corresponding lowered values void setValues(Value* val,const std::vector& values); // Data Members /// @brief whether we changed the function or not bool Changed; /// @brief a map from old packed values to new smaller packed values std::map > packedToScalarMap; /// Instructions in the source program to get rid of /// after we do a pass (the old packed instructions) std::vector instrsToRemove; }; RegisterPass X("lower-packed", "lowers packed operations to operations on smaller packed datatypes"); } // end namespace FunctionPass *llvm::createLowerPackedPass() { return new LowerPacked(); } // This function sets lowered values for a corresponding // packed value. Note, in the case of a forward reference // getValues(Value*) will have already been called for // the packed parameter. This function will then replace // all references in the in the function of the "dummy" // value the previous getValues(Value*) call // returned with actual references. void LowerPacked::setValues(Value* value,const std::vector& values) { std::map >::iterator it = packedToScalarMap.lower_bound(value); if (it == packedToScalarMap.end() || it->first != value) { // there was not a forward reference to this element packedToScalarMap.insert(it,std::make_pair(value,values)); } else { // replace forward declarations with actual definitions assert(it->second.size() == values.size() && "Error forward refences and actual definition differ in size"); for (unsigned i = 0, e = values.size(); i != e; ++i) { // replace and get rid of old forward references it->second[i]->replaceAllUsesWith(values[i]); delete it->second[i]; it->second[i] = values[i]; } } } // This function will examine the packed value parameter // and if it is a packed constant or a forward reference // properly create the lowered values needed. Otherwise // it will simply retreive values from a // setValues(Value*,const std::vector&) // call. Failing both of these cases, it will abort // the program. std::vector& LowerPacked::getValues(Value* value) { assert(isa(value->getType()) && "Value must be PackedType"); // reject further processing if this one has // already been handled std::map >::iterator it = packedToScalarMap.lower_bound(value); if (it != packedToScalarMap.end() && it->first == value) { return it->second; } if (ConstantPacked* CP = dyn_cast(value)) { // non-zero constant case std::vector results; results.reserve(CP->getNumOperands()); for (unsigned i = 0, e = CP->getNumOperands(); i != e; ++i) { results.push_back(CP->getOperand(i)); } return packedToScalarMap.insert(it, std::make_pair(value,results))->second; } else if (ConstantAggregateZero* CAZ = dyn_cast(value)) { // zero constant const PackedType* PKT = cast(CAZ->getType()); std::vector results; results.reserve(PKT->getNumElements()); Constant* C = Constant::getNullValue(PKT->getElementType()); for (unsigned i = 0, e = PKT->getNumElements(); i != e; ++i) { results.push_back(C); } return packedToScalarMap.insert(it, std::make_pair(value,results))->second; } else if (isa(value)) { // foward reference const PackedType* PKT = cast(value->getType()); std::vector results; results.reserve(PKT->getNumElements()); for (unsigned i = 0, e = PKT->getNumElements(); i != e; ++i) { results.push_back(new Argument(PKT->getElementType())); } return packedToScalarMap.insert(it, std::make_pair(value,results))->second; } else { // we don't know what it is, and we are trying to retrieve // a value for it assert(false && "Unhandled PackedType value"); abort(); } } void LowerPacked::visitLoadInst(LoadInst& LI) { // Make sure what we are dealing with is a packed type if (const PackedType* PKT = dyn_cast(LI.getType())) { // Initialization, Idx is needed for getelementptr needed later std::vector Idx(2); Idx[0] = ConstantInt::get(Type::Int32Ty,0); ArrayType* AT = ArrayType::get(PKT->getContainedType(0), PKT->getNumElements()); PointerType* APT = PointerType::get(AT); // Cast the pointer to packed type to an equivalent array Value* array = new BitCastInst(LI.getPointerOperand(), APT, LI.getName() + ".a", &LI); // Convert this load into num elements number of loads std::vector values; values.reserve(PKT->getNumElements()); for (unsigned i = 0, e = PKT->getNumElements(); i != e; ++i) { // Calculate the second index we will need Idx[1] = ConstantInt::get(Type::Int32Ty,i); // Get the pointer Value* val = new GetElementPtrInst(array, Idx, LI.getName() + ".ge." + utostr(i), &LI); // generate the new load and save the result in packedToScalar map values.push_back(new LoadInst(val, LI.getName()+"."+utostr(i), LI.isVolatile(), &LI)); } setValues(&LI,values); Changed = true; instrsToRemove.push_back(&LI); } } void LowerPacked::visitBinaryOperator(BinaryOperator& BO) { // Make sure both operands are PackedTypes if (isa(BO.getOperand(0)->getType())) { std::vector& op0Vals = getValues(BO.getOperand(0)); std::vector& op1Vals = getValues(BO.getOperand(1)); std::vector result; assert((op0Vals.size() == op1Vals.size()) && "The two packed operand to scalar maps must be equal in size."); result.reserve(op0Vals.size()); // generate the new binary op and save the result for (unsigned i = 0; i != op0Vals.size(); ++i) { result.push_back(BinaryOperator::create(BO.getOpcode(), op0Vals[i], op1Vals[i], BO.getName() + "." + utostr(i), &BO)); } setValues(&BO,result); Changed = true; instrsToRemove.push_back(&BO); } } void LowerPacked::visitICmpInst(ICmpInst& IC) { // Make sure both operands are PackedTypes if (isa(IC.getOperand(0)->getType())) { std::vector& op0Vals = getValues(IC.getOperand(0)); std::vector& op1Vals = getValues(IC.getOperand(1)); std::vector result; assert((op0Vals.size() == op1Vals.size()) && "The two packed operand to scalar maps must be equal in size."); result.reserve(op0Vals.size()); // generate the new binary op and save the result for (unsigned i = 0; i != op0Vals.size(); ++i) { result.push_back(CmpInst::create(IC.getOpcode(), IC.getPredicate(), op0Vals[i], op1Vals[i], IC.getName() + "." + utostr(i), &IC)); } setValues(&IC,result); Changed = true; instrsToRemove.push_back(&IC); } } void LowerPacked::visitStoreInst(StoreInst& SI) { if (const PackedType* PKT = dyn_cast(SI.getOperand(0)->getType())) { // We will need this for getelementptr std::vector Idx(2); Idx[0] = ConstantInt::get(Type::Int32Ty,0); ArrayType* AT = ArrayType::get(PKT->getContainedType(0), PKT->getNumElements()); PointerType* APT = PointerType::get(AT); // Cast the pointer to packed to an array of equivalent type Value* array = new BitCastInst(SI.getPointerOperand(), APT, "store.ge.a.", &SI); std::vector& values = getValues(SI.getOperand(0)); assert((values.size() == PKT->getNumElements()) && "Scalar must have the same number of elements as Packed Type"); for (unsigned i = 0, e = PKT->getNumElements(); i != e; ++i) { // Generate the indices for getelementptr Idx[1] = ConstantInt::get(Type::Int32Ty,i); Value* val = new GetElementPtrInst(array, Idx, "store.ge." + utostr(i) + ".", &SI); new StoreInst(values[i], val, SI.isVolatile(),&SI); } Changed = true; instrsToRemove.push_back(&SI); } } void LowerPacked::visitSelectInst(SelectInst& SELI) { // Make sure both operands are PackedTypes if (isa(SELI.getType())) { std::vector& op0Vals = getValues(SELI.getTrueValue()); std::vector& op1Vals = getValues(SELI.getFalseValue()); std::vector result; assert((op0Vals.size() == op1Vals.size()) && "The two packed operand to scalar maps must be equal in size."); for (unsigned i = 0; i != op0Vals.size(); ++i) { result.push_back(new SelectInst(SELI.getCondition(), op0Vals[i], op1Vals[i], SELI.getName()+ "." + utostr(i), &SELI)); } setValues(&SELI,result); Changed = true; instrsToRemove.push_back(&SELI); } } void LowerPacked::visitExtractElementInst(ExtractElementInst& EI) { std::vector& op0Vals = getValues(EI.getOperand(0)); const PackedType *PTy = cast(EI.getOperand(0)->getType()); Value *op1 = EI.getOperand(1); if (ConstantInt *C = dyn_cast(op1)) { EI.replaceAllUsesWith(op0Vals[C->getZExtValue()]); } else { AllocaInst *alloca = new AllocaInst(PTy->getElementType(), ConstantInt::get(Type::Int32Ty, PTy->getNumElements()), EI.getName() + ".alloca", EI.getParent()->getParent()->getEntryBlock().begin()); for (unsigned i = 0; i < PTy->getNumElements(); ++i) { GetElementPtrInst *GEP = new GetElementPtrInst(alloca, ConstantInt::get(Type::Int32Ty, i), "store.ge", &EI); new StoreInst(op0Vals[i], GEP, &EI); } GetElementPtrInst *GEP = new GetElementPtrInst(alloca, op1, EI.getName() + ".ge", &EI); LoadInst *load = new LoadInst(GEP, EI.getName() + ".load", &EI); EI.replaceAllUsesWith(load); } Changed = true; instrsToRemove.push_back(&EI); } void LowerPacked::visitInsertElementInst(InsertElementInst& IE) { std::vector& Vals = getValues(IE.getOperand(0)); Value *Elt = IE.getOperand(1); Value *Idx = IE.getOperand(2); std::vector result; result.reserve(Vals.size()); if (ConstantInt *C = dyn_cast(Idx)) { unsigned idxVal = C->getZExtValue(); for (unsigned i = 0; i != Vals.size(); ++i) { result.push_back(i == idxVal ? Elt : Vals[i]); } } else { for (unsigned i = 0; i != Vals.size(); ++i) { ICmpInst *icmp = new ICmpInst(ICmpInst::ICMP_EQ, Idx, ConstantInt::get(Type::Int32Ty, i), "icmp", &IE); SelectInst *select = new SelectInst(icmp, Elt, Vals[i], "select", &IE); result.push_back(select); } } setValues(&IE, result); Changed = true; instrsToRemove.push_back(&IE); } bool LowerPacked::runOnFunction(Function& F) { // initialize Changed = false; // Does three passes: // Pass 1) Converts Packed Operations to // new Packed Operations on smaller // datatypes visit(F); // Pass 2) Drop all references std::for_each(instrsToRemove.begin(), instrsToRemove.end(), std::mem_fun(&Instruction::dropAllReferences)); // Pass 3) Delete the Instructions to remove aka packed instructions for (std::vector::iterator i = instrsToRemove.begin(), e = instrsToRemove.end(); i != e; ++i) { (*i)->getParent()->getInstList().erase(*i); } // clean-up packedToScalarMap.clear(); instrsToRemove.clear(); return Changed; }