//===- SandboxIR.cpp - A transactional overlay IR on top of LLVM IR -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/SandboxIR/SandboxIR.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/IR/Constants.h" #include "llvm/Support/Debug.h" #include using namespace llvm::sandboxir; Value *Use::get() const { return Ctx->getValue(LLVMUse->get()); } void Use::set(Value *V) { LLVMUse->set(V->Val); } unsigned Use::getOperandNo() const { return Usr->getUseOperandNo(*this); } #ifndef NDEBUG void Use::dump(raw_ostream &OS) const { Value *Def = nullptr; if (LLVMUse == nullptr) OS << " LLVM Use! "; else Def = Ctx->getValue(LLVMUse->get()); OS << "Def: "; if (Def == nullptr) OS << "NULL"; else OS << *Def; OS << "\n"; OS << "User: "; if (Usr == nullptr) OS << "NULL"; else OS << *Usr; OS << "\n"; OS << "OperandNo: "; if (Usr == nullptr) OS << "N/A"; else OS << getOperandNo(); OS << "\n"; } void Use::dump() const { dump(dbgs()); } #endif // NDEBUG Use OperandUseIterator::operator*() const { return Use; } OperandUseIterator &OperandUseIterator::operator++() { assert(Use.LLVMUse != nullptr && "Already at end!"); User *User = Use.getUser(); Use = User->getOperandUseInternal(Use.getOperandNo() + 1, /*Verify=*/false); return *this; } UserUseIterator &UserUseIterator::operator++() { // Get the corresponding llvm::Use, get the next in the list, and update the // sandboxir::Use. llvm::Use *&LLVMUse = Use.LLVMUse; assert(LLVMUse != nullptr && "Already at end!"); LLVMUse = LLVMUse->getNext(); if (LLVMUse == nullptr) { Use.Usr = nullptr; return *this; } auto *Ctx = Use.Ctx; auto *LLVMUser = LLVMUse->getUser(); Use.Usr = cast_or_null(Ctx->getValue(LLVMUser)); return *this; } Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx) : SubclassID(SubclassID), Val(Val), Ctx(Ctx) { #ifndef NDEBUG UID = Ctx.getNumValues(); #endif } Value::use_iterator Value::use_begin() { llvm::Use *LLVMUse = nullptr; if (Val->use_begin() != Val->use_end()) LLVMUse = &*Val->use_begin(); User *User = LLVMUse != nullptr ? cast_or_null(Ctx.getValue( Val->use_begin()->getUser())) : nullptr; return use_iterator(Use(LLVMUse, User, Ctx)); } Value::user_iterator Value::user_begin() { auto UseBegin = Val->use_begin(); auto UseEnd = Val->use_end(); bool AtEnd = UseBegin == UseEnd; llvm::Use *LLVMUse = AtEnd ? nullptr : &*UseBegin; User *User = AtEnd ? nullptr : cast_or_null(Ctx.getValue(&*LLVMUse->getUser())); return user_iterator(Use(LLVMUse, User, Ctx), UseToUser()); } unsigned Value::getNumUses() const { return range_size(Val->users()); } void Value::replaceUsesWithIf( Value *OtherV, llvm::function_ref ShouldReplace) { assert(getType() == OtherV->getType() && "Can't replace with different type"); llvm::Value *OtherVal = OtherV->Val; // We are delegating RUWIf to LLVM IR's RUWIf. Val->replaceUsesWithIf( OtherVal, [&ShouldReplace, this](llvm::Use &LLVMUse) -> bool { User *DstU = cast_or_null(Ctx.getValue(LLVMUse.getUser())); if (DstU == nullptr) return false; Use UseToReplace(&LLVMUse, DstU, Ctx); if (!ShouldReplace(UseToReplace)) return false; auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) Tracker.track(std::make_unique(UseToReplace, Tracker)); return true; }); } void Value::replaceAllUsesWith(Value *Other) { assert(getType() == Other->getType() && "Replacing with Value of different type!"); auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) { for (auto Use : uses()) Tracker.track(std::make_unique(Use, Tracker)); } // We are delegating RAUW to LLVM IR's RAUW. Val->replaceAllUsesWith(Other->Val); } #ifndef NDEBUG std::string Value::getUid() const { std::stringstream SS; SS << "SB" << UID << "."; return SS.str(); } void Value::dumpCommonHeader(raw_ostream &OS) const { OS << getUid() << " " << getSubclassIDStr(SubclassID) << " "; } void Value::dumpCommonFooter(raw_ostream &OS) const { OS.indent(2) << "Val: "; if (Val) OS << *Val; else OS << "NULL"; OS << "\n"; } void Value::dumpCommonPrefix(raw_ostream &OS) const { if (Val) OS << *Val; else OS << "NULL "; } void Value::dumpCommonSuffix(raw_ostream &OS) const { OS << " ; " << getUid() << " (" << getSubclassIDStr(SubclassID) << ")"; } void Value::printAsOperandCommon(raw_ostream &OS) const { if (Val) Val->printAsOperand(OS); else OS << "NULL "; } void Argument::printAsOperand(raw_ostream &OS) const { printAsOperandCommon(OS); } void Argument::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void Argument::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG Use User::getOperandUseDefault(unsigned OpIdx, bool Verify) const { assert((!Verify || OpIdx < getNumOperands()) && "Out of bounds!"); assert(isa(Val) && "Non-users have no operands!"); llvm::Use *LLVMUse; if (OpIdx != getNumOperands()) LLVMUse = &cast(Val)->getOperandUse(OpIdx); else LLVMUse = cast(Val)->op_end(); return Use(LLVMUse, const_cast(this), Ctx); } #ifndef NDEBUG void User::verifyUserOfLLVMUse(const llvm::Use &Use) const { assert(Ctx.getValue(Use.getUser()) == this && "Use not found in this SBUser's operands!"); } #endif bool User::classof(const Value *From) { switch (From->getSubclassID()) { #define DEF_VALUE(ID, CLASS) #define DEF_USER(ID, CLASS) \ case ClassID::ID: \ return true; #define DEF_INSTR(ID, OPC, CLASS) \ case ClassID::ID: \ return true; #include "llvm/SandboxIR/SandboxIRValues.def" default: return false; } } void User::setOperand(unsigned OperandIdx, Value *Operand) { assert(isa(Val) && "No operands!"); auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) Tracker.track(std::make_unique(getOperandUse(OperandIdx), Tracker)); // We are delegating to llvm::User::setOperand(). cast(Val)->setOperand(OperandIdx, Operand->Val); } bool User::replaceUsesOfWith(Value *FromV, Value *ToV) { auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) { for (auto OpIdx : seq(0, getNumOperands())) { auto Use = getOperandUse(OpIdx); if (Use.get() == FromV) Tracker.track(std::make_unique(Use, Tracker)); } } // We are delegating RUOW to LLVM IR's RUOW. return cast(Val)->replaceUsesOfWith(FromV->Val, ToV->Val); } #ifndef NDEBUG void User::dumpCommonHeader(raw_ostream &OS) const { Value::dumpCommonHeader(OS); // TODO: This is incomplete } #endif // NDEBUG BBIterator &BBIterator::operator++() { auto ItE = BB->end(); assert(It != ItE && "Already at end!"); ++It; if (It == ItE) return *this; Instruction &NextI = *cast(Ctx->getValue(&*It)); unsigned Num = NextI.getNumOfIRInstrs(); assert(Num > 0 && "Bad getNumOfIRInstrs()"); It = std::next(It, Num - 1); return *this; } BBIterator &BBIterator::operator--() { assert(It != BB->begin() && "Already at begin!"); if (It == BB->end()) { --It; return *this; } Instruction &CurrI = **this; unsigned Num = CurrI.getNumOfIRInstrs(); assert(Num > 0 && "Bad getNumOfIRInstrs()"); assert(std::prev(It, Num - 1) != BB->begin() && "Already at begin!"); It = std::prev(It, Num); return *this; } const char *Instruction::getOpcodeName(Opcode Opc) { switch (Opc) { #define DEF_VALUE(ID, CLASS) #define DEF_USER(ID, CLASS) #define OP(OPC) \ case Opcode::OPC: \ return #OPC; #define DEF_INSTR(ID, OPC, CLASS) OPC #include "llvm/SandboxIR/SandboxIRValues.def" } llvm_unreachable("Unknown Opcode"); } llvm::Instruction *Instruction::getTopmostLLVMInstruction() const { Instruction *Prev = getPrevNode(); if (Prev == nullptr) { // If at top of the BB, return the first BB instruction. return &*cast(getParent()->Val)->begin(); } // Else get the Previous sandbox IR instruction's bottom IR instruction and // return its successor. llvm::Instruction *PrevBotI = cast(Prev->Val); return PrevBotI->getNextNode(); } BBIterator Instruction::getIterator() const { auto *I = cast(Val); return BasicBlock::iterator(I->getParent(), I->getIterator(), &Ctx); } Instruction *Instruction::getNextNode() const { assert(getParent() != nullptr && "Detached!"); assert(getIterator() != getParent()->end() && "Already at end!"); // `Val` is the bottom-most LLVM IR instruction. Get the next in the chain, // and get the corresponding sandboxir Instruction that maps to it. This works // even for SandboxIR Instructions that map to more than one LLVM Instruction. auto *LLVMI = cast(Val); assert(LLVMI->getParent() != nullptr && "LLVM IR instr is detached!"); auto *NextLLVMI = LLVMI->getNextNode(); auto *NextI = cast_or_null(Ctx.getValue(NextLLVMI)); if (NextI == nullptr) return nullptr; return NextI; } Instruction *Instruction::getPrevNode() const { assert(getParent() != nullptr && "Detached!"); auto It = getIterator(); if (It != getParent()->begin()) return std::prev(getIterator()).get(); return nullptr; } void Instruction::removeFromParent() { auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) Tracker.track(std::make_unique(this, Tracker)); // Detach all the LLVM IR instructions from their parent BB. for (llvm::Instruction *I : getLLVMInstrs()) I->removeFromParent(); } void Instruction::eraseFromParent() { assert(users().empty() && "Still connected to users, can't erase!"); std::unique_ptr Detached = Ctx.detach(this); auto LLVMInstrs = getLLVMInstrs(); auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) { Tracker.track( std::make_unique(std::move(Detached), Tracker)); // We don't actually delete the IR instruction, because then it would be // impossible to bring it back from the dead at the same memory location. // Instead we remove it from its BB and track its current location. for (llvm::Instruction *I : LLVMInstrs) I->removeFromParent(); // TODO: Multi-instructions need special treatment because some of the // references are internal to the instruction. for (llvm::Instruction *I : LLVMInstrs) I->dropAllReferences(); } else { // Erase in reverse to avoid erasing nstructions with attached uses. for (llvm::Instruction *I : reverse(LLVMInstrs)) I->eraseFromParent(); } } void Instruction::moveBefore(BasicBlock &BB, const BBIterator &WhereIt) { if (std::next(getIterator()) == WhereIt) // Destination is same as origin, nothing to do. return; auto &Tracker = Ctx.getTracker(); if (Tracker.isTracking()) Tracker.track(std::make_unique(this, Tracker)); auto *LLVMBB = cast(BB.Val); llvm::BasicBlock::iterator It; if (WhereIt == BB.end()) { It = LLVMBB->end(); } else { Instruction *WhereI = &*WhereIt; It = WhereI->getTopmostLLVMInstruction()->getIterator(); } // TODO: Move this to the verifier of sandboxir::Instruction. assert(is_sorted(getLLVMInstrs(), [](auto *I1, auto *I2) { return I1->comesBefore(I2); }) && "Expected program order!"); // Do the actual move in LLVM IR. for (auto *I : getLLVMInstrs()) I->moveBefore(*LLVMBB, It); } void Instruction::insertBefore(Instruction *BeforeI) { llvm::Instruction *BeforeTopI = BeforeI->getTopmostLLVMInstruction(); // TODO: Move this to the verifier of sandboxir::Instruction. assert(is_sorted(getLLVMInstrs(), [](auto *I1, auto *I2) { return I1->comesBefore(I2); }) && "Expected program order!"); // Insert the LLVM IR Instructions in program order. for (llvm::Instruction *I : getLLVMInstrs()) I->insertBefore(BeforeTopI); } void Instruction::insertAfter(Instruction *AfterI) { insertInto(AfterI->getParent(), std::next(AfterI->getIterator())); } void Instruction::insertInto(BasicBlock *BB, const BBIterator &WhereIt) { llvm::BasicBlock *LLVMBB = cast(BB->Val); llvm::Instruction *LLVMBeforeI; llvm::BasicBlock::iterator LLVMBeforeIt; if (WhereIt != BB->end()) { Instruction *BeforeI = &*WhereIt; LLVMBeforeI = BeforeI->getTopmostLLVMInstruction(); LLVMBeforeIt = LLVMBeforeI->getIterator(); } else { LLVMBeforeI = nullptr; LLVMBeforeIt = LLVMBB->end(); } // Insert the LLVM IR Instructions in program order. for (llvm::Instruction *I : getLLVMInstrs()) I->insertInto(LLVMBB, LLVMBeforeIt); } BasicBlock *Instruction::getParent() const { // Get the LLVM IR Instruction that this maps to, get its parent, and get the // corresponding sandboxir::BasicBlock by looking it up in sandboxir::Context. auto *BB = cast(Val)->getParent(); if (BB == nullptr) return nullptr; return cast(Ctx.getValue(BB)); } bool Instruction::classof(const sandboxir::Value *From) { switch (From->getSubclassID()) { #define DEF_INSTR(ID, OPC, CLASS) \ case ClassID::ID: \ return true; #include "llvm/SandboxIR/SandboxIRValues.def" default: return false; } } #ifndef NDEBUG void Instruction::dump(raw_ostream &OS) const { OS << "Unimplemented! Please override dump()."; } void Instruction::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG Value *SelectInst::createCommon(Value *Cond, Value *True, Value *False, const Twine &Name, IRBuilder<> &Builder, Context &Ctx) { llvm::Value *NewV = Builder.CreateSelect(Cond->Val, True->Val, False->Val, Name); if (auto *NewSI = dyn_cast(NewV)) return Ctx.createSelectInst(NewSI); assert(isa(NewV) && "Expected constant"); return Ctx.getOrCreateConstant(cast(NewV)); } Value *SelectInst::create(Value *Cond, Value *True, Value *False, Instruction *InsertBefore, Context &Ctx, const Twine &Name) { llvm::Instruction *BeforeIR = InsertBefore->getTopmostLLVMInstruction(); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(BeforeIR); return createCommon(Cond, True, False, Name, Builder, Ctx); } Value *SelectInst::create(Value *Cond, Value *True, Value *False, BasicBlock *InsertAtEnd, Context &Ctx, const Twine &Name) { auto *IRInsertAtEnd = cast(InsertAtEnd->Val); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(IRInsertAtEnd); return createCommon(Cond, True, False, Name, Builder, Ctx); } bool SelectInst::classof(const Value *From) { return From->getSubclassID() == ClassID::Select; } #ifndef NDEBUG void SelectInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void SelectInst::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG LoadInst *LoadInst::create(Type *Ty, Value *Ptr, MaybeAlign Align, Instruction *InsertBefore, Context &Ctx, const Twine &Name) { llvm::Instruction *BeforeIR = InsertBefore->getTopmostLLVMInstruction(); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(BeforeIR); auto *NewLI = Builder.CreateAlignedLoad(Ty, Ptr->Val, Align, /*isVolatile=*/false, Name); auto *NewSBI = Ctx.createLoadInst(NewLI); return NewSBI; } LoadInst *LoadInst::create(Type *Ty, Value *Ptr, MaybeAlign Align, BasicBlock *InsertAtEnd, Context &Ctx, const Twine &Name) { auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(cast(InsertAtEnd->Val)); auto *NewLI = Builder.CreateAlignedLoad(Ty, Ptr->Val, Align, /*isVolatile=*/false, Name); auto *NewSBI = Ctx.createLoadInst(NewLI); return NewSBI; } bool LoadInst::classof(const Value *From) { return From->getSubclassID() == ClassID::Load; } Value *LoadInst::getPointerOperand() const { return Ctx.getValue(cast(Val)->getPointerOperand()); } #ifndef NDEBUG void LoadInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void LoadInst::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG StoreInst *StoreInst::create(Value *V, Value *Ptr, MaybeAlign Align, Instruction *InsertBefore, Context &Ctx) { llvm::Instruction *BeforeIR = InsertBefore->getTopmostLLVMInstruction(); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(BeforeIR); auto *NewSI = Builder.CreateAlignedStore(V->Val, Ptr->Val, Align, /*isVolatile=*/false); auto *NewSBI = Ctx.createStoreInst(NewSI); return NewSBI; } StoreInst *StoreInst::create(Value *V, Value *Ptr, MaybeAlign Align, BasicBlock *InsertAtEnd, Context &Ctx) { auto *InsertAtEndIR = cast(InsertAtEnd->Val); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(InsertAtEndIR); auto *NewSI = Builder.CreateAlignedStore(V->Val, Ptr->Val, Align, /*isVolatile=*/false); auto *NewSBI = Ctx.createStoreInst(NewSI); return NewSBI; } bool StoreInst::classof(const Value *From) { return From->getSubclassID() == ClassID::Store; } Value *StoreInst::getValueOperand() const { return Ctx.getValue(cast(Val)->getValueOperand()); } Value *StoreInst::getPointerOperand() const { return Ctx.getValue(cast(Val)->getPointerOperand()); } #ifndef NDEBUG void StoreInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void StoreInst::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG ReturnInst *ReturnInst::createCommon(Value *RetVal, IRBuilder<> &Builder, Context &Ctx) { llvm::ReturnInst *NewRI; if (RetVal != nullptr) NewRI = Builder.CreateRet(RetVal->Val); else NewRI = Builder.CreateRetVoid(); return Ctx.createReturnInst(NewRI); } ReturnInst *ReturnInst::create(Value *RetVal, Instruction *InsertBefore, Context &Ctx) { llvm::Instruction *BeforeIR = InsertBefore->getTopmostLLVMInstruction(); auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(BeforeIR); return createCommon(RetVal, Builder, Ctx); } ReturnInst *ReturnInst::create(Value *RetVal, BasicBlock *InsertAtEnd, Context &Ctx) { auto &Builder = Ctx.getLLVMIRBuilder(); Builder.SetInsertPoint(cast(InsertAtEnd->Val)); return createCommon(RetVal, Builder, Ctx); } Value *ReturnInst::getReturnValue() const { auto *LLVMRetVal = cast(Val)->getReturnValue(); return LLVMRetVal != nullptr ? Ctx.getValue(LLVMRetVal) : nullptr; } #ifndef NDEBUG void ReturnInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void ReturnInst::dump() const { dump(dbgs()); dbgs() << "\n"; } void OpaqueInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void OpaqueInst::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG Constant *Constant::createInt(Type *Ty, uint64_t V, Context &Ctx, bool IsSigned) { llvm::Constant *LLVMC = llvm::ConstantInt::get(Ty, V, IsSigned); return Ctx.getOrCreateConstant(LLVMC); } #ifndef NDEBUG void Constant::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); dumpCommonSuffix(OS); } void Constant::dump() const { dump(dbgs()); dbgs() << "\n"; } void Function::dumpNameAndArgs(raw_ostream &OS) const { auto *F = cast(Val); OS << *F->getReturnType() << " @" << F->getName() << "("; interleave( F->args(), [this, &OS](const llvm::Argument &LLVMArg) { auto *SBArg = cast_or_null(Ctx.getValue(&LLVMArg)); if (SBArg == nullptr) OS << "NULL"; else SBArg->printAsOperand(OS); }, [&] { OS << ", "; }); OS << ")"; } void Function::dump(raw_ostream &OS) const { dumpNameAndArgs(OS); OS << " {\n"; auto *LLVMF = cast(Val); interleave( *LLVMF, [this, &OS](const llvm::BasicBlock &LLVMBB) { auto *BB = cast_or_null(Ctx.getValue(&LLVMBB)); if (BB == nullptr) OS << "NULL"; else OS << *BB; }, [&OS] { OS << "\n"; }); OS << "}\n"; } void Function::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG BasicBlock::iterator::pointer BasicBlock::iterator::getInstr(llvm::BasicBlock::iterator It) const { return cast_or_null(Ctx->getValue(&*It)); } std::unique_ptr Context::detachLLVMValue(llvm::Value *V) { std::unique_ptr Erased; auto It = LLVMValueToValueMap.find(V); if (It != LLVMValueToValueMap.end()) { auto *Val = It->second.release(); Erased = std::unique_ptr(Val); LLVMValueToValueMap.erase(It); } return Erased; } std::unique_ptr Context::detach(Value *V) { assert(V->getSubclassID() != Value::ClassID::Constant && "Can't detach a constant!"); assert(V->getSubclassID() != Value::ClassID::User && "Can't detach a user!"); return detachLLVMValue(V->Val); } Value *Context::registerValue(std::unique_ptr &&VPtr) { assert(VPtr->getSubclassID() != Value::ClassID::User && "Can't register a user!"); Value *V = VPtr.get(); [[maybe_unused]] auto Pair = LLVMValueToValueMap.insert({VPtr->Val, std::move(VPtr)}); assert(Pair.second && "Already exists!"); return V; } Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) { auto Pair = LLVMValueToValueMap.insert({LLVMV, nullptr}); auto It = Pair.first; if (!Pair.second) return It->second.get(); if (auto *C = dyn_cast(LLVMV)) { It->second = std::unique_ptr(new Constant(C, *this)); auto *NewC = It->second.get(); for (llvm::Value *COp : C->operands()) getOrCreateValueInternal(COp, C); return NewC; } if (auto *Arg = dyn_cast(LLVMV)) { It->second = std::unique_ptr(new Argument(Arg, *this)); return It->second.get(); } if (auto *BB = dyn_cast(LLVMV)) { assert(isa(U) && "This won't create a SBBB, don't call this function directly!"); if (auto *SBBB = getValue(BB)) return SBBB; return nullptr; } assert(isa(LLVMV) && "Expected Instruction"); switch (cast(LLVMV)->getOpcode()) { case llvm::Instruction::Select: { auto *LLVMSel = cast(LLVMV); It->second = std::unique_ptr(new SelectInst(LLVMSel, *this)); return It->second.get(); } case llvm::Instruction::Load: { auto *LLVMLd = cast(LLVMV); It->second = std::unique_ptr(new LoadInst(LLVMLd, *this)); return It->second.get(); } case llvm::Instruction::Store: { auto *LLVMSt = cast(LLVMV); It->second = std::unique_ptr(new StoreInst(LLVMSt, *this)); return It->second.get(); } case llvm::Instruction::Ret: { auto *LLVMRet = cast(LLVMV); It->second = std::unique_ptr(new ReturnInst(LLVMRet, *this)); return It->second.get(); } default: break; } It->second = std::unique_ptr( new OpaqueInst(cast(LLVMV), *this)); return It->second.get(); } BasicBlock *Context::createBasicBlock(llvm::BasicBlock *LLVMBB) { assert(getValue(LLVMBB) == nullptr && "Already exists!"); auto NewBBPtr = std::unique_ptr(new BasicBlock(LLVMBB, *this)); auto *BB = cast(registerValue(std::move(NewBBPtr))); // Create SandboxIR for BB's body. BB->buildBasicBlockFromLLVMIR(LLVMBB); return BB; } SelectInst *Context::createSelectInst(llvm::SelectInst *SI) { auto NewPtr = std::unique_ptr(new SelectInst(SI, *this)); return cast(registerValue(std::move(NewPtr))); } LoadInst *Context::createLoadInst(llvm::LoadInst *LI) { auto NewPtr = std::unique_ptr(new LoadInst(LI, *this)); return cast(registerValue(std::move(NewPtr))); } StoreInst *Context::createStoreInst(llvm::StoreInst *SI) { auto NewPtr = std::unique_ptr(new StoreInst(SI, *this)); return cast(registerValue(std::move(NewPtr))); } ReturnInst *Context::createReturnInst(llvm::ReturnInst *I) { auto NewPtr = std::unique_ptr(new ReturnInst(I, *this)); return cast(registerValue(std::move(NewPtr))); } Value *Context::getValue(llvm::Value *V) const { auto It = LLVMValueToValueMap.find(V); if (It != LLVMValueToValueMap.end()) return It->second.get(); return nullptr; } Function *Context::createFunction(llvm::Function *F) { assert(getValue(F) == nullptr && "Already exists!"); auto NewFPtr = std::unique_ptr(new Function(F, *this)); // Create arguments. for (auto &Arg : F->args()) getOrCreateArgument(&Arg); // Create BBs. for (auto &BB : *F) createBasicBlock(&BB); auto *SBF = cast(registerValue(std::move(NewFPtr))); return SBF; } Function *BasicBlock::getParent() const { auto *BB = cast(Val); auto *F = BB->getParent(); if (F == nullptr) // Detached return nullptr; return cast_or_null(Ctx.getValue(F)); } void BasicBlock::buildBasicBlockFromLLVMIR(llvm::BasicBlock *LLVMBB) { for (llvm::Instruction &IRef : reverse(*LLVMBB)) { llvm::Instruction *I = &IRef; Ctx.getOrCreateValue(I); for (auto [OpIdx, Op] : enumerate(I->operands())) { // Skip instruction's label operands if (isa(Op)) continue; // Skip metadata if (isa(Op)) continue; // Skip asm if (isa(Op)) continue; Ctx.getOrCreateValue(Op); } } #if !defined(NDEBUG) && defined(SBVEC_EXPENSIVE_CHECKS) verify(); #endif } BasicBlock::iterator BasicBlock::begin() const { llvm::BasicBlock *BB = cast(Val); llvm::BasicBlock::iterator It = BB->begin(); if (!BB->empty()) { auto *V = Ctx.getValue(&*BB->begin()); assert(V != nullptr && "No SandboxIR for BB->begin()!"); auto *I = cast(V); unsigned Num = I->getNumOfIRInstrs(); assert(Num >= 1u && "Bad getNumOfIRInstrs()"); It = std::next(It, Num - 1); } return iterator(BB, It, &Ctx); } Instruction *BasicBlock::getTerminator() const { auto *TerminatorV = Ctx.getValue(cast(Val)->getTerminator()); return cast_or_null(TerminatorV); } Instruction &BasicBlock::front() const { auto *BB = cast(Val); assert(!BB->empty() && "Empty block!"); auto *SBI = cast(getContext().getValue(&*BB->begin())); assert(SBI != nullptr && "Expected Instr!"); return *SBI; } Instruction &BasicBlock::back() const { auto *BB = cast(Val); assert(!BB->empty() && "Empty block!"); auto *SBI = cast(getContext().getValue(&*BB->rbegin())); assert(SBI != nullptr && "Expected Instr!"); return *SBI; } #ifndef NDEBUG void BasicBlock::dump(raw_ostream &OS) const { llvm::BasicBlock *BB = cast(Val); const auto &Name = BB->getName(); OS << Name; if (!Name.empty()) OS << ":\n"; // If there are Instructions in the BB that are not mapped to SandboxIR, then // use a crash-proof dump. if (any_of(*BB, [this](llvm::Instruction &I) { return Ctx.getValue(&I) == nullptr; })) { OS << "\n"; DenseSet Visited; for (llvm::Instruction &IRef : *BB) { Value *SBV = Ctx.getValue(&IRef); if (SBV == nullptr) OS << IRef << " *** No SandboxIR ***\n"; else { auto *SBI = dyn_cast(SBV); if (SBI == nullptr) { OS << IRef << " *** Not a SBInstruction!!! ***\n"; } else { if (Visited.insert(SBI).second) OS << *SBI << "\n"; } } } } else { for (auto &SBI : *this) { SBI.dump(OS); OS << "\n"; } } } void BasicBlock::dump() const { dump(dbgs()); dbgs() << "\n"; } #endif // NDEBUG