TEST_F(MemorySSATest, RemoveMemoryAccess) { // We create a diamond where there is a store on one side, and then a load // after the merge point. This enables us to test a bunch of different // removal cases. F = Function::Create( FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), GlobalValue::ExternalLinkage, "F", &M); BasicBlock *Entry(BasicBlock::Create(C, "", F)); BasicBlock *Left(BasicBlock::Create(C, "", F)); BasicBlock *Right(BasicBlock::Create(C, "", F)); BasicBlock *Merge(BasicBlock::Create(C, "", F)); B.SetInsertPoint(Entry); B.CreateCondBr(B.getTrue(), Left, Right); B.SetInsertPoint(Left); Argument *PointerArg = &*F->arg_begin(); StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); BranchInst::Create(Merge, Left); BranchInst::Create(Merge, Right); B.SetInsertPoint(Merge); LoadInst *LoadInst = B.CreateLoad(PointerArg); setupAnalyses(); MemorySSA &MSSA = Analyses->MSSA; MemorySSAWalker *Walker = &*Analyses->Walker; // Before, the load will be a use of a phi<store, liveonentry>. It should be // the same after. MemoryUse *LoadAccess = cast<MemoryUse>(MSSA.getMemoryAccess(LoadInst)); MemoryDef *StoreAccess = cast<MemoryDef>(MSSA.getMemoryAccess(StoreInst)); MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); // The load is currently clobbered by one of the phi arguments, so the walker // should determine the clobbering access as the phi. EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst)); MSSA.removeMemoryAccess(StoreAccess); MSSA.verifyMemorySSA(); // After the removeaccess, let's see if we got the right accesses // The load should still point to the phi ... EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess()); // but we should now get live on entry for the clobbering definition of the // load, since it will walk past the phi node since every argument is the // same. EXPECT_TRUE( MSSA.isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); // The phi should now be a two entry phi with two live on entry defs. for (const auto &Op : DefiningAccess->operands()) { MemoryAccess *Operand = cast<MemoryAccess>(&*Op); EXPECT_TRUE(MSSA.isLiveOnEntryDef(Operand)); } // Now we try to remove the single valued phi MSSA.removeMemoryAccess(DefiningAccess); MSSA.verifyMemorySSA(); // Now the load should be a load of live on entry. EXPECT_TRUE(MSSA.isLiveOnEntryDef(LoadAccess->getDefiningAccess())); }
static unsigned getFlagsForCheck(const MemoryAccess &access) { unsigned flags = 0; if (access.getSize() > 1) flags |= MemoryCheck::CheckAlignment; flags |= MemoryCheck::CheckAddress; if (access.getIsStore()) flags |= MemoryCheck::CheckInvalidation; return flags; }
bool CallingConvention_x86_64_systemv::analyzeCallSite(ParameterRegistry ®istry, CallInformation &fillOut, CallSite cs) { fillOut.clear(); TargetInfo& targetInfo = registry.getTargetInfo(); Instruction& inst = *cs.getInstruction(); Function& caller = *inst.getParent()->getParent(); MemorySSA& mssa = *registry.getMemorySSA(caller); MemoryAccess* thisDef = mssa.getMemoryAccess(&inst); identifyParameterCandidates(targetInfo, mssa, thisDef->getDefiningAccess(), fillOut); identifyReturnCandidates(targetInfo, mssa, thisDef, fillOut); return true; }
void MemoryReservationDirective::execute( const ExecuteImmutableArguments& immutable, CompileErrorList& errors, FinalCommandVector& commandOutput, MemoryAccess& memoryAccess) { // Finally, we may put some zeros into memory. if (_size > 0) { memoryAccess.putMemoryValueAt(_absolutePosition, MemoryValue(_size)); } }
bool mssa::runOnFunction(Function &F) { MemorySSA *MSSA = &getAnalysis<MemorySSAWrapperPass>().getMSSA(); //MemorySSAWalker MAW = new MemorySSAWalker(MSSA); errs()<<"\n"; for (Function::iterator BB = F.begin(); BB != F.end(); BB++){ errs() << "Basic block (name=" << BB->getName() << ")\n"; //Get MemoryPhi and print it out MemoryPhi* MP = MSSA->getMemoryAccess(dyn_cast<BasicBlock>(BB)); if (MP != NULL) MP->dump(); for (BasicBlock::iterator itrIns = (*BB).begin(); itrIns != (*BB).end(); itrIns++) { //MemoryAccess* MA = MAW.getClobberingMemoryAccess(itrIns); //MemoryLocation Location; //MemoryAccess* MAR = MAW.getClobberingMemoryAccess(MA,&Location); //MAR->dump(); errs()<<"Instruction: "<< *itrIns <<"\n"; //Get MemoryDef or MemoryUse and print it out MemoryAccess *MA = MSSA->getMemoryAccess(dyn_cast<Value>(itrIns)); if (MA != NULL) { MA->dump(); //if(MSSA->isLiveOnEntryDef(MA)) //Get immediate MemoryDef of the instruction annotated MemoryDef/MemoryUse for (memoryaccess_def_iterator MAitr = MA->defs_begin(); MAitr != MA->defs_end(); MAitr++) { errs()<<"Def: "<<**MAitr<<"\n"; //Get the instruction the immediate Memory Def annotation represent Instruction* u = cast<MemoryUseOrDef>(*MAitr)->getMemoryInst(); if (u != NULL) errs()<<" Def Inst: "<<*u<<"\n"; } } } } return 0; }
TEST(MemorySSA, RemoveMemoryAccess) { LLVMContext C; std::unique_ptr<Module> M(new Module("Remove memory access", C)); IRBuilder<> B(C); DataLayout DL("e-i64:64-f80:128-n8:16:32:64-S128"); TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI(TLII); // We create a diamond where there is a store on one side, and then a load // after the merge point. This enables us to test a bunch of different // removal cases. Function *F = Function::Create( FunctionType::get(B.getVoidTy(), {B.getInt8PtrTy()}, false), GlobalValue::ExternalLinkage, "F", M.get()); BasicBlock *Entry(BasicBlock::Create(C, "", F)); BasicBlock *Left(BasicBlock::Create(C, "", F)); BasicBlock *Right(BasicBlock::Create(C, "", F)); BasicBlock *Merge(BasicBlock::Create(C, "", F)); B.SetInsertPoint(Entry); B.CreateCondBr(B.getTrue(), Left, Right); B.SetInsertPoint(Left); Argument *PointerArg = &*F->arg_begin(); StoreInst *StoreInst = B.CreateStore(B.getInt8(16), PointerArg); BranchInst::Create(Merge, Left); BranchInst::Create(Merge, Right); B.SetInsertPoint(Merge); LoadInst *LoadInst = B.CreateLoad(PointerArg); std::unique_ptr<MemorySSA> MSSA(new MemorySSA(*F)); std::unique_ptr<DominatorTree> DT(new DominatorTree(*F)); std::unique_ptr<AssumptionCache> AC(new AssumptionCache(*F)); AAResults AA(TLI); BasicAAResult BAA(DL, TLI, *AC, &*DT); AA.addAAResult(BAA); std::unique_ptr<MemorySSAWalker> Walker(MSSA->buildMemorySSA(&AA, &*DT)); // Before, the load will be a use of a phi<store, liveonentry>. It should be // the same after. MemoryUse *LoadAccess = cast<MemoryUse>(MSSA->getMemoryAccess(LoadInst)); MemoryDef *StoreAccess = cast<MemoryDef>(MSSA->getMemoryAccess(StoreInst)); MemoryAccess *DefiningAccess = LoadAccess->getDefiningAccess(); EXPECT_TRUE(isa<MemoryPhi>(DefiningAccess)); // The load is currently clobbered by one of the phi arguments, so the walker // should determine the clobbering access as the phi. EXPECT_EQ(DefiningAccess, Walker->getClobberingMemoryAccess(LoadInst)); MSSA->removeMemoryAccess(StoreAccess); MSSA->verifyMemorySSA(); // After the removeaccess, let's see if we got the right accesses // The load should still point to the phi ... EXPECT_EQ(DefiningAccess, LoadAccess->getDefiningAccess()); // but we should now get live on entry for the clobbering definition of the // load, since it will walk past the phi node since every argument is the // same. EXPECT_TRUE( MSSA->isLiveOnEntryDef(Walker->getClobberingMemoryAccess(LoadInst))); // The phi should now be a two entry phi with two live on entry defs. for (const auto &Op : DefiningAccess->operands()) { MemoryAccess *Operand = cast<MemoryAccess>(&*Op); EXPECT_TRUE(MSSA->isLiveOnEntryDef(Operand)); } // Now we try to remove the single valued phi MSSA->removeMemoryAccess(DefiningAccess); MSSA->verifyMemorySSA(); // Now the load should be a load of live on entry. EXPECT_TRUE(MSSA->isLiveOnEntryDef(LoadAccess->getDefiningAccess())); }
FinalRepresentation IntermediateRepresentator::transform(const TransformationParameters& parameters, const CompileErrorList& parsingErrors, MemoryAccess& memoryAccess) { auto errors = parsingErrors; if (_currentOutput) { errors.pushError(_currentOutput->positionInterval(), "Macro not closed. Missing a macro end directive?"); } PrecompileImmutableArguments precompileArguments(parameters.architecture(), parameters.generator()); SymbolGraph graph; MacroDirectiveTable macroTable; for (const auto& command : _commandList) { command->precompile(precompileArguments, errors, graph, macroTable); } IntermediateMacroInstruction::replaceWithMacros( _commandList.begin(), _commandList.end(), macroTable, errors); auto macroList = generateMacroInformation(); auto preliminaryEvaluation = graph.evaluate(); if (!evaluateGraph(preliminaryEvaluation, errors)) { return FinalRepresentation({}, errors, macroList); } SymbolReplacer preliminaryReplacer(preliminaryEvaluation); MemoryAllocator allocator(parameters.allocator()); SectionTracker tracker; auto allowedSize = memoryAccess.getMemorySize().get(); IntermediateOperationPointer firstMemoryExceedingOperation(nullptr); AllocateMemoryImmutableArguments allocateMemoryArguments(precompileArguments, preliminaryReplacer); for (const auto& command : _commandList) { command->allocateMemory( allocateMemoryArguments, errors, allocator, tracker); if (allocator.estimateSize() > allowedSize && !firstMemoryExceedingOperation) { firstMemoryExceedingOperation = command; } } auto allocatedSize = allocator.calculatePositions(); EnhanceSymbolTableImmutableArguments symbolTableArguments( allocateMemoryArguments, allocator); for (const auto& command : _commandList) { command->enhanceSymbolTable(symbolTableArguments, errors, graph); } auto graphEvaluation = graph.evaluate(); auto graphValid = evaluateGraph(graphEvaluation, errors); auto memoryValid = checkMemorySize( allocatedSize, allowedSize, firstMemoryExceedingOperation, errors); if (!(graphValid && memoryValid)) { return FinalRepresentation({}, errors, macroList); } SymbolReplacer replacer(graphEvaluation); ExecuteImmutableArguments executeArguments(symbolTableArguments, replacer); FinalCommandVector commandOutput; for (const auto& command : _commandList) { command->execute(executeArguments, errors, commandOutput, memoryAccess); } return FinalRepresentation(commandOutput, errors, macroList); }
void MemorySSAUpdater::fixupDefs(const SmallVectorImpl<WeakVH> &Vars) { SmallPtrSet<const BasicBlock *, 8> Seen; SmallVector<const BasicBlock *, 16> Worklist; for (auto &Var : Vars) { MemoryAccess *NewDef = dyn_cast_or_null<MemoryAccess>(Var); if (!NewDef) continue; // First, see if there is a local def after the operand. auto *Defs = MSSA->getWritableBlockDefs(NewDef->getBlock()); auto DefIter = NewDef->getDefsIterator(); // The temporary Phi is being fixed, unmark it for not to optimize. if (MemoryPhi *Phi = dyn_cast<MemoryPhi>(NewDef)) NonOptPhis.erase(Phi); // If there is a local def after us, we only have to rename that. if (++DefIter != Defs->end()) { cast<MemoryDef>(DefIter)->setDefiningAccess(NewDef); continue; } // Otherwise, we need to search down through the CFG. // For each of our successors, handle it directly if their is a phi, or // place on the fixup worklist. for (const auto *S : successors(NewDef->getBlock())) { if (auto *MP = MSSA->getMemoryAccess(S)) setMemoryPhiValueForBlock(MP, NewDef->getBlock(), NewDef); else Worklist.push_back(S); } while (!Worklist.empty()) { const BasicBlock *FixupBlock = Worklist.back(); Worklist.pop_back(); // Get the first def in the block that isn't a phi node. if (auto *Defs = MSSA->getWritableBlockDefs(FixupBlock)) { auto *FirstDef = &*Defs->begin(); // The loop above and below should have taken care of phi nodes assert(!isa<MemoryPhi>(FirstDef) && "Should have already handled phi nodes!"); // We are now this def's defining access, make sure we actually dominate // it assert(MSSA->dominates(NewDef, FirstDef) && "Should have dominated the new access"); // This may insert new phi nodes, because we are not guaranteed the // block we are processing has a single pred, and depending where the // store was inserted, it may require phi nodes below it. cast<MemoryDef>(FirstDef)->setDefiningAccess(getPreviousDef(FirstDef)); return; } // We didn't find a def, so we must continue. for (const auto *S : successors(FixupBlock)) { // If there is a phi node, handle it. // Otherwise, put the block on the worklist if (auto *MP = MSSA->getMemoryAccess(S)) setMemoryPhiValueForBlock(MP, FixupBlock, NewDef); else { // If we cycle, we should have ended up at a phi node that we already // processed. FIXME: Double check this if (!Seen.insert(S).second) continue; Worklist.push_back(S); } } } } }
// A brief description of the algorithm: // First, we compute what should define the new def, using the SSA // construction algorithm. // Then, we update the defs below us (and any new phi nodes) in the graph to // point to the correct new defs, to ensure we only have one variable, and no // disconnected stores. void MemorySSAUpdater::insertDef(MemoryDef *MD, bool RenameUses) { InsertedPHIs.clear(); // See if we had a local def, and if not, go hunting. MemoryAccess *DefBefore = getPreviousDef(MD); bool DefBeforeSameBlock = DefBefore->getBlock() == MD->getBlock(); // There is a def before us, which means we can replace any store/phi uses // of that thing with us, since we are in the way of whatever was there // before. // We now define that def's memorydefs and memoryphis if (DefBeforeSameBlock) { for (auto UI = DefBefore->use_begin(), UE = DefBefore->use_end(); UI != UE;) { Use &U = *UI++; // Leave the uses alone if (isa<MemoryUse>(U.getUser())) continue; U.set(MD); } } // and that def is now our defining access. // We change them in this order otherwise we will appear in the use list // above and reset ourselves. MD->setDefiningAccess(DefBefore); SmallVector<WeakVH, 8> FixupList(InsertedPHIs.begin(), InsertedPHIs.end()); if (!DefBeforeSameBlock) { // If there was a local def before us, we must have the same effect it // did. Because every may-def is the same, any phis/etc we would create, it // would also have created. If there was no local def before us, we // performed a global update, and have to search all successors and make // sure we update the first def in each of them (following all paths until // we hit the first def along each path). This may also insert phi nodes. // TODO: There are other cases we can skip this work, such as when we have a // single successor, and only used a straight line of single pred blocks // backwards to find the def. To make that work, we'd have to track whether // getDefRecursive only ever used the single predecessor case. These types // of paths also only exist in between CFG simplifications. FixupList.push_back(MD); } while (!FixupList.empty()) { unsigned StartingPHISize = InsertedPHIs.size(); fixupDefs(FixupList); FixupList.clear(); // Put any new phis on the fixup list, and process them FixupList.append(InsertedPHIs.begin() + StartingPHISize, InsertedPHIs.end()); } // Now that all fixups are done, rename all uses if we are asked. if (RenameUses) { SmallPtrSet<BasicBlock *, 16> Visited; BasicBlock *StartBlock = MD->getBlock(); // We are guaranteed there is a def in the block, because we just got it // handed to us in this function. MemoryAccess *FirstDef = &*MSSA->getWritableBlockDefs(StartBlock)->begin(); // Convert to incoming value if it's a memorydef. A phi *is* already an // incoming value. if (auto *MD = dyn_cast<MemoryDef>(FirstDef)) FirstDef = MD->getDefiningAccess(); MSSA->renamePass(MD->getBlock(), FirstDef, Visited); // We just inserted a phi into this block, so the incoming value will become // the phi anyway, so it does not matter what we pass. for (auto &MP : InsertedPHIs) { MemoryPhi *Phi = dyn_cast_or_null<MemoryPhi>(MP); if (Phi) MSSA->renamePass(Phi->getBlock(), nullptr, Visited); } } }
void axe:: placeMemoryChecks(std::vector<InstructionOpcode> &opcode, std::vector<Operands> &operands, std::queue<std::pair<uint32_t,MemoryCheck*> > &checks) { MemoryCheckState state; std::vector<MemoryCheckCandidate> candidates; // Gather expressions for memory accesses. for (unsigned i = 0, e = opcode.size(); i != e; ++i) { InstructionOpcode opc = opcode[i]; const Operands &ops = operands[i]; InstructionProperties &properties = instructionProperties[opc]; MemoryAccess access; if (properties.memCheckHoistingOptEnabled() && getInstructionMemoryAccess(opc, ops, access)) { assert(access.getOffsetImm() % access.getSize() == 0); // Compute the first offset where all registers are available. unsigned first = state.regDefs[access.getBaseReg()]; if (access.getScale()) first = std::min(first, state.regDefs[access.getOffsetReg()]); unsigned flags = getFlagsForCheck(access); bool updateBaseRegAlign = false; if (flags & MemoryCheck::CheckAlignment) { assert(access.getScale() == 0 || (access.getScale() % access.getSize()) == 0); assert((access.getOffsetImm() % access.getSize()) == 0); unsigned size = access.getSize(); if ((state.getMinAlignment(access.getBaseReg()) % size) == 0) { flags &= ~MemoryCheck::CheckAlignment; } else { updateBaseRegAlign = true; } } MemoryCheck *check = new MemoryCheck(access.getSize(), access.getBaseReg(), access.getScale(), access.getOffsetReg(), access.getOffsetImm(), flags); candidates.push_back(MemoryCheckCandidate(first, i, check)); if (updateBaseRegAlign) { state.setMinAlignment(access.getBaseReg(), access.getSize()); } } // Update regDefs. state.update(opc, ops, i + 1); } if (candidates.empty()) return; for (unsigned index = 0, size = candidates.size(); index < size; index++) { checks.push(std::make_pair(candidates[index].getInstructionIndex(), candidates[index].getMemoryCheck())); } }