//===- Instruction.cpp - The Instructions of Sandbox 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/Instruction.h"
#include "llvm/SandboxIR/Function.h"

namespace llvm::sandboxir {

const char *Instruction::getOpcodeName(Opcode Opc) {
  switch (Opc) {
#define OP(OPC)                                                                \
  case Opcode::OPC:                                                            \
    return #OPC;
#define OPCODES(...) __VA_ARGS__
#define DEF_INSTR(ID, OPC, CLASS) OPC
#include "llvm/SandboxIR/Values.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<llvm::BasicBlock>(getParent()->Val)->begin();
  }
  // Else get the Previous sandbox IR instruction's bottom IR instruction and
  // return its successor.
  llvm::Instruction *PrevBotI = cast<llvm::Instruction>(Prev->Val);
  return PrevBotI->getNextNode();
}

BBIterator Instruction::getIterator() const {
  auto *I = cast<llvm::Instruction>(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<llvm::Instruction>(Val);
  assert(LLVMI->getParent() != nullptr && "LLVM IR instr is detached!");
  auto *NextLLVMI = LLVMI->getNextNode();
  auto *NextI = cast_or_null<Instruction>(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() {
  Ctx.getTracker().emplaceIfTracking<RemoveFromParent>(this);

  // 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!");

  Ctx.runEraseInstrCallbacks(this);
  std::unique_ptr<Value> Detached = Ctx.detach(this);
  auto LLVMInstrs = getLLVMInstrs();

  auto &Tracker = Ctx.getTracker();
  if (Tracker.isTracking()) {
    Tracker.track(std::make_unique<EraseFromParent>(std::move(Detached)));
    // 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;

  Ctx.runMoveInstrCallbacks(this, WhereIt);
  Ctx.getTracker().emplaceIfTracking<MoveInstr>(this);

  auto *LLVMBB = cast<llvm::BasicBlock>(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();

  Ctx.getTracker().emplaceIfTracking<InsertIntoBB>(this);

  // Insert the LLVM IR Instructions in program order.
  for (llvm::Instruction *I : getLLVMInstrs())
    I->insertBefore(BeforeTopI->getIterator());
}

void Instruction::insertAfter(Instruction *AfterI) {
  insertInto(AfterI->getParent(), std::next(AfterI->getIterator()));
}

void Instruction::insertInto(BasicBlock *BB, const BBIterator &WhereIt) {
  llvm::BasicBlock *LLVMBB = cast<llvm::BasicBlock>(BB->Val);
  llvm::Instruction *LLVMBeforeI;
  llvm::BasicBlock::iterator LLVMBeforeIt;
  Instruction *BeforeI;
  if (WhereIt != BB->end()) {
    BeforeI = &*WhereIt;
    LLVMBeforeI = BeforeI->getTopmostLLVMInstruction();
    LLVMBeforeIt = LLVMBeforeI->getIterator();
  } else {
    BeforeI = nullptr;
    LLVMBeforeI = nullptr;
    LLVMBeforeIt = LLVMBB->end();
  }

  Ctx.getTracker().emplaceIfTracking<InsertIntoBB>(this);

  // 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<llvm::Instruction>(Val)->getParent();
  if (BB == nullptr)
    return nullptr;
  return cast<BasicBlock>(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/Values.def"
  default:
    return false;
  }
}

void Instruction::setHasNoUnsignedWrap(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasNoUnsignedWrap,
                                       &Instruction::setHasNoUnsignedWrap>>(
          this);
  cast<llvm::Instruction>(Val)->setHasNoUnsignedWrap(B);
}

void Instruction::setHasNoSignedWrap(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasNoSignedWrap,
                                       &Instruction::setHasNoSignedWrap>>(this);
  cast<llvm::Instruction>(Val)->setHasNoSignedWrap(B);
}

void Instruction::setFast(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&Instruction::isFast, &Instruction::setFast>>(this);
  cast<llvm::Instruction>(Val)->setFast(B);
}

void Instruction::setIsExact(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&Instruction::isExact, &Instruction::setIsExact>>(this);
  cast<llvm::Instruction>(Val)->setIsExact(B);
}

void Instruction::setHasAllowReassoc(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasAllowReassoc,
                                       &Instruction::setHasAllowReassoc>>(this);
  cast<llvm::Instruction>(Val)->setHasAllowReassoc(B);
}

void Instruction::setHasNoNaNs(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&Instruction::hasNoNaNs, &Instruction::setHasNoNaNs>>(
          this);
  cast<llvm::Instruction>(Val)->setHasNoNaNs(B);
}

void Instruction::setHasNoInfs(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&Instruction::hasNoInfs, &Instruction::setHasNoInfs>>(
          this);
  cast<llvm::Instruction>(Val)->setHasNoInfs(B);
}

void Instruction::setHasNoSignedZeros(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasNoSignedZeros,
                                       &Instruction::setHasNoSignedZeros>>(
          this);
  cast<llvm::Instruction>(Val)->setHasNoSignedZeros(B);
}

void Instruction::setHasAllowReciprocal(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasAllowReciprocal,
                                       &Instruction::setHasAllowReciprocal>>(
          this);
  cast<llvm::Instruction>(Val)->setHasAllowReciprocal(B);
}

void Instruction::setHasAllowContract(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasAllowContract,
                                       &Instruction::setHasAllowContract>>(
          this);
  cast<llvm::Instruction>(Val)->setHasAllowContract(B);
}

void Instruction::setFastMathFlags(FastMathFlags FMF) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::getFastMathFlags,
                                       &Instruction::copyFastMathFlags>>(this);
  cast<llvm::Instruction>(Val)->setFastMathFlags(FMF);
}

void Instruction::copyFastMathFlags(FastMathFlags FMF) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::getFastMathFlags,
                                       &Instruction::copyFastMathFlags>>(this);
  cast<llvm::Instruction>(Val)->copyFastMathFlags(FMF);
}

Type *Instruction::getAccessType() const {
  return Ctx.getType(cast<llvm::Instruction>(Val)->getAccessType());
}

void Instruction::setHasApproxFunc(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&Instruction::hasApproxFunc,
                                       &Instruction::setHasApproxFunc>>(this);
  cast<llvm::Instruction>(Val)->setHasApproxFunc(B);
}

#ifndef NDEBUG
void Instruction::dumpOS(raw_ostream &OS) const {
  OS << "Unimplemented! Please override dump().";
}
#endif // NDEBUG

VAArgInst *VAArgInst::create(Value *List, Type *Ty, InsertPosition Pos,
                             Context &Ctx, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMI =
      cast<llvm::VAArgInst>(Builder.CreateVAArg(List->Val, Ty->LLVMTy, Name));
  return Ctx.createVAArgInst(LLVMI);
}

Value *VAArgInst::getPointerOperand() {
  return Ctx.getValue(cast<llvm::VAArgInst>(Val)->getPointerOperand());
}

FreezeInst *FreezeInst::create(Value *V, InsertPosition Pos, Context &Ctx,
                               const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMI = cast<llvm::FreezeInst>(Builder.CreateFreeze(V->Val, Name));
  return Ctx.createFreezeInst(LLVMI);
}

FenceInst *FenceInst::create(AtomicOrdering Ordering, InsertPosition Pos,
                             Context &Ctx, SyncScope::ID SSID) {
  auto &Builder = Instruction::setInsertPos(Pos);
  llvm::FenceInst *LLVMI = Builder.CreateFence(Ordering, SSID);
  return Ctx.createFenceInst(LLVMI);
}

void FenceInst::setOrdering(AtomicOrdering Ordering) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&FenceInst::getOrdering, &FenceInst::setOrdering>>(
          this);
  cast<llvm::FenceInst>(Val)->setOrdering(Ordering);
}

void FenceInst::setSyncScopeID(SyncScope::ID SSID) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&FenceInst::getSyncScopeID,
                                       &FenceInst::setSyncScopeID>>(this);
  cast<llvm::FenceInst>(Val)->setSyncScopeID(SSID);
}

Value *SelectInst::create(Value *Cond, Value *True, Value *False,
                          InsertPosition Pos, Context &Ctx, const Twine &Name) {
  auto &Builder = Instruction::setInsertPos(Pos);
  llvm::Value *NewV =
      Builder.CreateSelect(Cond->Val, True->Val, False->Val, Name);
  if (auto *NewSI = dyn_cast<llvm::SelectInst>(NewV))
    return Ctx.createSelectInst(NewSI);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

void SelectInst::swapValues() {
  Ctx.getTracker().emplaceIfTracking<UseSwap>(getOperandUse(1),
                                              getOperandUse(2));
  cast<llvm::SelectInst>(Val)->swapValues();
}

bool SelectInst::classof(const Value *From) {
  return From->getSubclassID() == ClassID::Select;
}

BranchInst *BranchInst::create(BasicBlock *IfTrue, InsertPosition Pos,
                               Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  llvm::BranchInst *NewBr =
      Builder.CreateBr(cast<llvm::BasicBlock>(IfTrue->Val));
  return Ctx.createBranchInst(NewBr);
}

BranchInst *BranchInst::create(BasicBlock *IfTrue, BasicBlock *IfFalse,
                               Value *Cond, InsertPosition Pos, Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  llvm::BranchInst *NewBr =
      Builder.CreateCondBr(Cond->Val, cast<llvm::BasicBlock>(IfTrue->Val),
                           cast<llvm::BasicBlock>(IfFalse->Val));
  return Ctx.createBranchInst(NewBr);
}

bool BranchInst::classof(const Value *From) {
  return From->getSubclassID() == ClassID::Br;
}

Value *BranchInst::getCondition() const {
  assert(isConditional() && "Cannot get condition of an uncond branch!");
  return Ctx.getValue(cast<llvm::BranchInst>(Val)->getCondition());
}

BasicBlock *BranchInst::getSuccessor(unsigned SuccIdx) const {
  assert(SuccIdx < getNumSuccessors() &&
         "Successor # out of range for Branch!");
  return cast_or_null<BasicBlock>(
      Ctx.getValue(cast<llvm::BranchInst>(Val)->getSuccessor(SuccIdx)));
}

void BranchInst::setSuccessor(unsigned Idx, BasicBlock *NewSucc) {
  assert((Idx == 0 || Idx == 1) && "Out of bounds!");
  setOperand(2u - Idx, NewSucc);
}

BasicBlock *BranchInst::LLVMBBToSBBB::operator()(llvm::BasicBlock *BB) const {
  return cast<BasicBlock>(Ctx.getValue(BB));
}
const BasicBlock *
BranchInst::ConstLLVMBBToSBBB::operator()(const llvm::BasicBlock *BB) const {
  return cast<BasicBlock>(Ctx.getValue(BB));
}

void LoadInst::setVolatile(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&LoadInst::isVolatile, &LoadInst::setVolatile>>(this);
  cast<llvm::LoadInst>(Val)->setVolatile(V);
}

LoadInst *LoadInst::create(Type *Ty, Value *Ptr, MaybeAlign Align,
                           InsertPosition Pos, bool IsVolatile, Context &Ctx,
                           const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *NewLI =
      Builder.CreateAlignedLoad(Ty->LLVMTy, Ptr->Val, Align, IsVolatile, 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<llvm::LoadInst>(Val)->getPointerOperand());
}

void StoreInst::setVolatile(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&StoreInst::isVolatile, &StoreInst::setVolatile>>(this);
  cast<llvm::StoreInst>(Val)->setVolatile(V);
}

StoreInst *StoreInst::create(Value *V, Value *Ptr, MaybeAlign Align,
                             InsertPosition Pos, bool IsVolatile,
                             Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  auto *NewSI = Builder.CreateAlignedStore(V->Val, Ptr->Val, Align, IsVolatile);
  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<llvm::StoreInst>(Val)->getValueOperand());
}

Value *StoreInst::getPointerOperand() const {
  return Ctx.getValue(cast<llvm::StoreInst>(Val)->getPointerOperand());
}

UnreachableInst *UnreachableInst::create(InsertPosition Pos, Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  llvm::UnreachableInst *NewUI = Builder.CreateUnreachable();
  return Ctx.createUnreachableInst(NewUI);
}

bool UnreachableInst::classof(const Value *From) {
  return From->getSubclassID() == ClassID::Unreachable;
}

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, InsertPosition Pos,
                               Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  return createCommon(RetVal, Builder, Ctx);
}

Value *ReturnInst::getReturnValue() const {
  auto *LLVMRetVal = cast<llvm::ReturnInst>(Val)->getReturnValue();
  return LLVMRetVal != nullptr ? Ctx.getValue(LLVMRetVal) : nullptr;
}

FunctionType *CallBase::getFunctionType() const {
  return cast<FunctionType>(
      Ctx.getType(cast<llvm::CallBase>(Val)->getFunctionType()));
}

Value *CallBase::getCalledOperand() const {
  return Ctx.getValue(cast<llvm::CallBase>(Val)->getCalledOperand());
}

Use CallBase::getCalledOperandUse() const {
  llvm::Use *LLVMUse = &cast<llvm::CallBase>(Val)->getCalledOperandUse();
  return Use(LLVMUse, cast<User>(Ctx.getValue(LLVMUse->getUser())), Ctx);
}

Function *CallBase::getCalledFunction() const {
  return cast_or_null<Function>(
      Ctx.getValue(cast<llvm::CallBase>(Val)->getCalledFunction()));
}
Function *CallBase::getCaller() {
  return cast<Function>(Ctx.getValue(cast<llvm::CallBase>(Val)->getCaller()));
}

void CallBase::setCalledFunction(Function *F) {
  // F's function type is private, so we rely on `setCalledFunction()` to update
  // it. But even though we are calling `setCalledFunction()` we also need to
  // track this change at the SandboxIR level, which is why we call
  // `setCalledOperand()` here.
  // Note: This may break if `setCalledFunction()` early returns if `F`
  // is already set, but we do have a unit test for it.
  setCalledOperand(F);
  cast<llvm::CallBase>(Val)->setCalledFunction(
      cast<llvm::FunctionType>(F->getFunctionType()->LLVMTy),
      cast<llvm::Function>(F->Val));
}

CallInst *CallInst::create(FunctionType *FTy, Value *Func,
                           ArrayRef<Value *> Args, InsertPosition Pos,
                           Context &Ctx, const Twine &NameStr) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::Value *> LLVMArgs;
  LLVMArgs.reserve(Args.size());
  for (Value *Arg : Args)
    LLVMArgs.push_back(Arg->Val);
  llvm::CallInst *NewCI = Builder.CreateCall(
      cast<llvm::FunctionType>(FTy->LLVMTy), Func->Val, LLVMArgs, NameStr);
  return Ctx.createCallInst(NewCI);
}

InvokeInst *InvokeInst::create(FunctionType *FTy, Value *Func,
                               BasicBlock *IfNormal, BasicBlock *IfException,
                               ArrayRef<Value *> Args, InsertPosition Pos,
                               Context &Ctx, const Twine &NameStr) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::Value *> LLVMArgs;
  LLVMArgs.reserve(Args.size());
  for (Value *Arg : Args)
    LLVMArgs.push_back(Arg->Val);
  llvm::InvokeInst *Invoke = Builder.CreateInvoke(
      cast<llvm::FunctionType>(FTy->LLVMTy), Func->Val,
      cast<llvm::BasicBlock>(IfNormal->Val),
      cast<llvm::BasicBlock>(IfException->Val), LLVMArgs, NameStr);
  return Ctx.createInvokeInst(Invoke);
}

BasicBlock *InvokeInst::getNormalDest() const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::InvokeInst>(Val)->getNormalDest()));
}
BasicBlock *InvokeInst::getUnwindDest() const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::InvokeInst>(Val)->getUnwindDest()));
}
void InvokeInst::setNormalDest(BasicBlock *BB) {
  setOperand(1, BB);
  assert(getNormalDest() == BB && "LLVM IR uses a different operan index!");
}
void InvokeInst::setUnwindDest(BasicBlock *BB) {
  setOperand(2, BB);
  assert(getUnwindDest() == BB && "LLVM IR uses a different operan index!");
}
LandingPadInst *InvokeInst::getLandingPadInst() const {
  return cast<LandingPadInst>(
      Ctx.getValue(cast<llvm::InvokeInst>(Val)->getLandingPadInst()));
  ;
}
BasicBlock *InvokeInst::getSuccessor(unsigned SuccIdx) const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::InvokeInst>(Val)->getSuccessor(SuccIdx)));
}

CallBrInst *CallBrInst::create(FunctionType *FTy, Value *Func,
                               BasicBlock *DefaultDest,
                               ArrayRef<BasicBlock *> IndirectDests,
                               ArrayRef<Value *> Args, InsertPosition Pos,
                               Context &Ctx, const Twine &NameStr) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::BasicBlock *> LLVMIndirectDests;
  LLVMIndirectDests.reserve(IndirectDests.size());
  for (BasicBlock *IndDest : IndirectDests)
    LLVMIndirectDests.push_back(cast<llvm::BasicBlock>(IndDest->Val));

  SmallVector<llvm::Value *> LLVMArgs;
  LLVMArgs.reserve(Args.size());
  for (Value *Arg : Args)
    LLVMArgs.push_back(Arg->Val);

  llvm::CallBrInst *CallBr =
      Builder.CreateCallBr(cast<llvm::FunctionType>(FTy->LLVMTy), Func->Val,
                           cast<llvm::BasicBlock>(DefaultDest->Val),
                           LLVMIndirectDests, LLVMArgs, NameStr);
  return Ctx.createCallBrInst(CallBr);
}

Value *CallBrInst::getIndirectDestLabel(unsigned Idx) const {
  return Ctx.getValue(cast<llvm::CallBrInst>(Val)->getIndirectDestLabel(Idx));
}
Value *CallBrInst::getIndirectDestLabelUse(unsigned Idx) const {
  return Ctx.getValue(
      cast<llvm::CallBrInst>(Val)->getIndirectDestLabelUse(Idx));
}
BasicBlock *CallBrInst::getDefaultDest() const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::CallBrInst>(Val)->getDefaultDest()));
}
BasicBlock *CallBrInst::getIndirectDest(unsigned Idx) const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::CallBrInst>(Val)->getIndirectDest(Idx)));
}
llvm::SmallVector<BasicBlock *, 16> CallBrInst::getIndirectDests() const {
  SmallVector<BasicBlock *, 16> BBs;
  for (llvm::BasicBlock *LLVMBB :
       cast<llvm::CallBrInst>(Val)->getIndirectDests())
    BBs.push_back(cast<BasicBlock>(Ctx.getValue(LLVMBB)));
  return BBs;
}
void CallBrInst::setDefaultDest(BasicBlock *BB) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CallBrInst::getDefaultDest,
                                       &CallBrInst::setDefaultDest>>(this);
  cast<llvm::CallBrInst>(Val)->setDefaultDest(cast<llvm::BasicBlock>(BB->Val));
}
void CallBrInst::setIndirectDest(unsigned Idx, BasicBlock *BB) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetterWithIdx<&CallBrInst::getIndirectDest,
                                              &CallBrInst::setIndirectDest>>(
          this, Idx);
  cast<llvm::CallBrInst>(Val)->setIndirectDest(Idx,
                                               cast<llvm::BasicBlock>(BB->Val));
}
BasicBlock *CallBrInst::getSuccessor(unsigned Idx) const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::CallBrInst>(Val)->getSuccessor(Idx)));
}

LandingPadInst *LandingPadInst::create(Type *RetTy, unsigned NumReservedClauses,
                                       InsertPosition Pos, Context &Ctx,
                                       const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::LandingPadInst *LLVMI =
      Builder.CreateLandingPad(RetTy->LLVMTy, NumReservedClauses, Name);
  return Ctx.createLandingPadInst(LLVMI);
}

void LandingPadInst::setCleanup(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&LandingPadInst::isCleanup,
                                       &LandingPadInst::setCleanup>>(this);
  cast<llvm::LandingPadInst>(Val)->setCleanup(V);
}

Constant *LandingPadInst::getClause(unsigned Idx) const {
  return cast<Constant>(
      Ctx.getValue(cast<llvm::LandingPadInst>(Val)->getClause(Idx)));
}

Value *FuncletPadInst::getParentPad() const {
  return Ctx.getValue(cast<llvm::FuncletPadInst>(Val)->getParentPad());
}

void FuncletPadInst::setParentPad(Value *ParentPad) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&FuncletPadInst::getParentPad,
                                       &FuncletPadInst::setParentPad>>(this);
  cast<llvm::FuncletPadInst>(Val)->setParentPad(ParentPad->Val);
}

Value *FuncletPadInst::getArgOperand(unsigned Idx) const {
  return Ctx.getValue(cast<llvm::FuncletPadInst>(Val)->getArgOperand(Idx));
}

void FuncletPadInst::setArgOperand(unsigned Idx, Value *V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetterWithIdx<&FuncletPadInst::getArgOperand,
                                              &FuncletPadInst::setArgOperand>>(
          this, Idx);
  cast<llvm::FuncletPadInst>(Val)->setArgOperand(Idx, V->Val);
}

CatchSwitchInst *CatchPadInst::getCatchSwitch() const {
  return cast<CatchSwitchInst>(
      Ctx.getValue(cast<llvm::CatchPadInst>(Val)->getCatchSwitch()));
}

CatchPadInst *CatchPadInst::create(Value *ParentPad, ArrayRef<Value *> Args,
                                   InsertPosition Pos, Context &Ctx,
                                   const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::Value *> LLVMArgs;
  LLVMArgs.reserve(Args.size());
  for (auto *Arg : Args)
    LLVMArgs.push_back(Arg->Val);
  llvm::CatchPadInst *LLVMI =
      Builder.CreateCatchPad(ParentPad->Val, LLVMArgs, Name);
  return Ctx.createCatchPadInst(LLVMI);
}

CleanupPadInst *CleanupPadInst::create(Value *ParentPad, ArrayRef<Value *> Args,
                                       InsertPosition Pos, Context &Ctx,
                                       const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::Value *> LLVMArgs;
  LLVMArgs.reserve(Args.size());
  for (auto *Arg : Args)
    LLVMArgs.push_back(Arg->Val);
  llvm::CleanupPadInst *LLVMI =
      Builder.CreateCleanupPad(ParentPad->Val, LLVMArgs, Name);
  return Ctx.createCleanupPadInst(LLVMI);
}

CatchReturnInst *CatchReturnInst::create(CatchPadInst *CatchPad, BasicBlock *BB,
                                         InsertPosition Pos, Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  llvm::CatchReturnInst *LLVMI = Builder.CreateCatchRet(
      cast<llvm::CatchPadInst>(CatchPad->Val), cast<llvm::BasicBlock>(BB->Val));
  return Ctx.createCatchReturnInst(LLVMI);
}

CatchPadInst *CatchReturnInst::getCatchPad() const {
  return cast<CatchPadInst>(
      Ctx.getValue(cast<llvm::CatchReturnInst>(Val)->getCatchPad()));
}

void CatchReturnInst::setCatchPad(CatchPadInst *CatchPad) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CatchReturnInst::getCatchPad,
                                       &CatchReturnInst::setCatchPad>>(this);
  cast<llvm::CatchReturnInst>(Val)->setCatchPad(
      cast<llvm::CatchPadInst>(CatchPad->Val));
}

BasicBlock *CatchReturnInst::getSuccessor() const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::CatchReturnInst>(Val)->getSuccessor()));
}

void CatchReturnInst::setSuccessor(BasicBlock *NewSucc) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CatchReturnInst::getSuccessor,
                                       &CatchReturnInst::setSuccessor>>(this);
  cast<llvm::CatchReturnInst>(Val)->setSuccessor(
      cast<llvm::BasicBlock>(NewSucc->Val));
}

Value *CatchReturnInst::getCatchSwitchParentPad() const {
  return Ctx.getValue(
      cast<llvm::CatchReturnInst>(Val)->getCatchSwitchParentPad());
}

CleanupReturnInst *CleanupReturnInst::create(CleanupPadInst *CleanupPad,
                                             BasicBlock *UnwindBB,
                                             InsertPosition Pos, Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMUnwindBB =
      UnwindBB != nullptr ? cast<llvm::BasicBlock>(UnwindBB->Val) : nullptr;
  llvm::CleanupReturnInst *LLVMI = Builder.CreateCleanupRet(
      cast<llvm::CleanupPadInst>(CleanupPad->Val), LLVMUnwindBB);
  return Ctx.createCleanupReturnInst(LLVMI);
}

CleanupPadInst *CleanupReturnInst::getCleanupPad() const {
  return cast<CleanupPadInst>(
      Ctx.getValue(cast<llvm::CleanupReturnInst>(Val)->getCleanupPad()));
}

void CleanupReturnInst::setCleanupPad(CleanupPadInst *CleanupPad) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CleanupReturnInst::getCleanupPad,
                                       &CleanupReturnInst::setCleanupPad>>(
          this);
  cast<llvm::CleanupReturnInst>(Val)->setCleanupPad(
      cast<llvm::CleanupPadInst>(CleanupPad->Val));
}

BasicBlock *CleanupReturnInst::getUnwindDest() const {
  return cast_or_null<BasicBlock>(
      Ctx.getValue(cast<llvm::CleanupReturnInst>(Val)->getUnwindDest()));
}

void CleanupReturnInst::setUnwindDest(BasicBlock *NewDest) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CleanupReturnInst::getUnwindDest,
                                       &CleanupReturnInst::setUnwindDest>>(
          this);
  cast<llvm::CleanupReturnInst>(Val)->setUnwindDest(
      cast<llvm::BasicBlock>(NewDest->Val));
}

Value *GetElementPtrInst::create(Type *Ty, Value *Ptr,
                                 ArrayRef<Value *> IdxList, InsertPosition Pos,
                                 Context &Ctx, const Twine &NameStr) {
  auto &Builder = setInsertPos(Pos);
  SmallVector<llvm::Value *> LLVMIdxList;
  LLVMIdxList.reserve(IdxList.size());
  for (Value *Idx : IdxList)
    LLVMIdxList.push_back(Idx->Val);
  llvm::Value *NewV =
      Builder.CreateGEP(Ty->LLVMTy, Ptr->Val, LLVMIdxList, NameStr);
  if (auto *NewGEP = dyn_cast<llvm::GetElementPtrInst>(NewV))
    return Ctx.createGetElementPtrInst(NewGEP);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Type *GetElementPtrInst::getSourceElementType() const {
  return Ctx.getType(
      cast<llvm::GetElementPtrInst>(Val)->getSourceElementType());
}

Type *GetElementPtrInst::getResultElementType() const {
  return Ctx.getType(
      cast<llvm::GetElementPtrInst>(Val)->getResultElementType());
}

Value *GetElementPtrInst::getPointerOperand() const {
  return Ctx.getValue(cast<llvm::GetElementPtrInst>(Val)->getPointerOperand());
}

Type *GetElementPtrInst::getPointerOperandType() const {
  return Ctx.getType(
      cast<llvm::GetElementPtrInst>(Val)->getPointerOperandType());
}

BasicBlock *PHINode::LLVMBBToBB::operator()(llvm::BasicBlock *LLVMBB) const {
  return cast<BasicBlock>(Ctx.getValue(LLVMBB));
}

PHINode *PHINode::create(Type *Ty, unsigned NumReservedValues,
                         InsertPosition Pos, Context &Ctx, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::PHINode *NewPHI =
      Builder.CreatePHI(Ty->LLVMTy, NumReservedValues, Name);
  return Ctx.createPHINode(NewPHI);
}

bool PHINode::classof(const Value *From) {
  return From->getSubclassID() == ClassID::PHI;
}

Value *PHINode::getIncomingValue(unsigned Idx) const {
  return Ctx.getValue(cast<llvm::PHINode>(Val)->getIncomingValue(Idx));
}
void PHINode::setIncomingValue(unsigned Idx, Value *V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetterWithIdx<&PHINode::getIncomingValue,
                                              &PHINode::setIncomingValue>>(this,
                                                                           Idx);
  cast<llvm::PHINode>(Val)->setIncomingValue(Idx, V->Val);
}
BasicBlock *PHINode::getIncomingBlock(unsigned Idx) const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::PHINode>(Val)->getIncomingBlock(Idx)));
}
BasicBlock *PHINode::getIncomingBlock(const Use &U) const {
  llvm::Use *LLVMUse = U.LLVMUse;
  llvm::BasicBlock *BB = cast<llvm::PHINode>(Val)->getIncomingBlock(*LLVMUse);
  return cast<BasicBlock>(Ctx.getValue(BB));
}
void PHINode::setIncomingBlock(unsigned Idx, BasicBlock *BB) {
  // Helper to disambiguate PHINode::getIncomingBlock(unsigned).
  constexpr BasicBlock *(PHINode::*GetIncomingBlockFn)(unsigned) const =
      &PHINode::getIncomingBlock;
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetterWithIdx<GetIncomingBlockFn, &PHINode::setIncomingBlock>>(
          this, Idx);
  cast<llvm::PHINode>(Val)->setIncomingBlock(Idx,
                                             cast<llvm::BasicBlock>(BB->Val));
}
void PHINode::addIncoming(Value *V, BasicBlock *BB) {
  auto &Tracker = Ctx.getTracker();
  Tracker.emplaceIfTracking<PHIAddIncoming>(this);

  cast<llvm::PHINode>(Val)->addIncoming(V->Val,
                                        cast<llvm::BasicBlock>(BB->Val));
}
Value *PHINode::removeIncomingValue(unsigned Idx) {
  auto &Tracker = Ctx.getTracker();
  Tracker.emplaceIfTracking<PHIRemoveIncoming>(this, Idx);
  llvm::Value *LLVMV =
      cast<llvm::PHINode>(Val)->removeIncomingValue(Idx,
                                                    /*DeletePHIIfEmpty=*/false);
  return Ctx.getValue(LLVMV);
}
Value *PHINode::removeIncomingValue(BasicBlock *BB) {
  auto &Tracker = Ctx.getTracker();
  Tracker.emplaceIfTracking<PHIRemoveIncoming>(this, getBasicBlockIndex(BB));

  auto *LLVMBB = cast<llvm::BasicBlock>(BB->Val);
  llvm::Value *LLVMV =
      cast<llvm::PHINode>(Val)->removeIncomingValue(LLVMBB,
                                                    /*DeletePHIIfEmpty=*/false);
  return Ctx.getValue(LLVMV);
}
int PHINode::getBasicBlockIndex(const BasicBlock *BB) const {
  auto *LLVMBB = cast<llvm::BasicBlock>(BB->Val);
  return cast<llvm::PHINode>(Val)->getBasicBlockIndex(LLVMBB);
}
Value *PHINode::getIncomingValueForBlock(const BasicBlock *BB) const {
  auto *LLVMBB = cast<llvm::BasicBlock>(BB->Val);
  llvm::Value *LLVMV =
      cast<llvm::PHINode>(Val)->getIncomingValueForBlock(LLVMBB);
  return Ctx.getValue(LLVMV);
}
Value *PHINode::hasConstantValue() const {
  llvm::Value *LLVMV = cast<llvm::PHINode>(Val)->hasConstantValue();
  return LLVMV != nullptr ? Ctx.getValue(LLVMV) : nullptr;
}
void PHINode::replaceIncomingBlockWith(const BasicBlock *Old, BasicBlock *New) {
  assert(New && Old && "Sandbox IR PHI node got a null basic block!");
  for (unsigned Idx = 0, NumOps = cast<llvm::PHINode>(Val)->getNumOperands();
       Idx != NumOps; ++Idx)
    if (getIncomingBlock(Idx) == Old)
      setIncomingBlock(Idx, New);
}
void PHINode::removeIncomingValueIf(function_ref<bool(unsigned)> Predicate) {
  // Avoid duplicate tracking by going through this->removeIncomingValue here at
  // the expense of some performance. Copy PHI::removeIncomingValueIf more
  // directly if performance becomes an issue.

  // Removing the element at index X, moves the element previously at X + 1
  // to X. Working from the end avoids complications from that.
  unsigned Idx = getNumIncomingValues();
  while (Idx > 0) {
    if (Predicate(Idx - 1))
      removeIncomingValue(Idx - 1);
    --Idx;
  }
}

Value *CmpInst::create(Predicate P, Value *S1, Value *S2, InsertPosition Pos,
                       Context &Ctx, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMV = Builder.CreateCmp(P, S1->Val, S2->Val, Name);
  // It may have been folded into a constant.
  if (auto *LLVMC = dyn_cast<llvm::Constant>(LLVMV))
    return Ctx.getOrCreateConstant(LLVMC);
  if (isa<llvm::ICmpInst>(LLVMV))
    return Ctx.createICmpInst(cast<llvm::ICmpInst>(LLVMV));
  return Ctx.createFCmpInst(cast<llvm::FCmpInst>(LLVMV));
}

Value *CmpInst::createWithCopiedFlags(Predicate P, Value *S1, Value *S2,
                                      const Instruction *F, InsertPosition Pos,
                                      Context &Ctx, const Twine &Name) {
  Value *V = create(P, S1, S2, Pos, Ctx, Name);
  if (auto *C = dyn_cast<Constant>(V))
    return C;
  cast<llvm::CmpInst>(V->Val)->copyIRFlags(F->Val);
  return V;
}

Type *CmpInst::makeCmpResultType(Type *OpndType) {
  if (auto *VT = dyn_cast<VectorType>(OpndType)) {
    // TODO: Cleanup when we have more complete support for
    // sandboxir::VectorType
    return OpndType->getContext().getType(llvm::VectorType::get(
        llvm::Type::getInt1Ty(OpndType->getContext().LLVMCtx),
        cast<llvm::VectorType>(VT->LLVMTy)->getElementCount()));
  }
  return Type::getInt1Ty(OpndType->getContext());
}

void CmpInst::setPredicate(Predicate P) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&CmpInst::getPredicate, &CmpInst::setPredicate>>(this);
  cast<llvm::CmpInst>(Val)->setPredicate(P);
}

void CmpInst::swapOperands() {
  if (ICmpInst *IC = dyn_cast<ICmpInst>(this))
    IC->swapOperands();
  else
    cast<FCmpInst>(this)->swapOperands();
}

void ICmpInst::swapOperands() {
  Ctx.getTracker().emplaceIfTracking<CmpSwapOperands>(this);
  cast<llvm::ICmpInst>(Val)->swapOperands();
}

void FCmpInst::swapOperands() {
  Ctx.getTracker().emplaceIfTracking<CmpSwapOperands>(this);
  cast<llvm::FCmpInst>(Val)->swapOperands();
}

#ifndef NDEBUG
void CmpInst::dumpOS(raw_ostream &OS) const {
  dumpCommonPrefix(OS);
  dumpCommonSuffix(OS);
}

void CmpInst::dump() const {
  dumpOS(dbgs());
  dbgs() << "\n";
}
#endif // NDEBUG

static llvm::Instruction::CastOps getLLVMCastOp(Instruction::Opcode Opc) {
  switch (Opc) {
  case Instruction::Opcode::ZExt:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::ZExt);
  case Instruction::Opcode::SExt:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::SExt);
  case Instruction::Opcode::FPToUI:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::FPToUI);
  case Instruction::Opcode::FPToSI:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::FPToSI);
  case Instruction::Opcode::FPExt:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::FPExt);
  case Instruction::Opcode::PtrToInt:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::PtrToInt);
  case Instruction::Opcode::IntToPtr:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::IntToPtr);
  case Instruction::Opcode::SIToFP:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::SIToFP);
  case Instruction::Opcode::UIToFP:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::UIToFP);
  case Instruction::Opcode::Trunc:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::Trunc);
  case Instruction::Opcode::FPTrunc:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::FPTrunc);
  case Instruction::Opcode::BitCast:
    return static_cast<llvm::Instruction::CastOps>(llvm::Instruction::BitCast);
  case Instruction::Opcode::AddrSpaceCast:
    return static_cast<llvm::Instruction::CastOps>(
        llvm::Instruction::AddrSpaceCast);
  default:
    llvm_unreachable("Opcode not suitable for CastInst!");
  }
}

/// \Returns the LLVM opcode that corresponds to \p Opc.
static llvm::Instruction::UnaryOps getLLVMUnaryOp(Instruction::Opcode Opc) {
  switch (Opc) {
  case Instruction::Opcode::FNeg:
    return static_cast<llvm::Instruction::UnaryOps>(llvm::Instruction::FNeg);
  default:
    llvm_unreachable("Not a unary op!");
  }
}

CatchSwitchInst *CatchSwitchInst::create(Value *ParentPad, BasicBlock *UnwindBB,
                                         unsigned NumHandlers,
                                         InsertPosition Pos, Context &Ctx,
                                         const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::CatchSwitchInst *LLVMCSI = Builder.CreateCatchSwitch(
      ParentPad->Val, cast<llvm::BasicBlock>(UnwindBB->Val), NumHandlers, Name);
  return Ctx.createCatchSwitchInst(LLVMCSI);
}

Value *CatchSwitchInst::getParentPad() const {
  return Ctx.getValue(cast<llvm::CatchSwitchInst>(Val)->getParentPad());
}

void CatchSwitchInst::setParentPad(Value *ParentPad) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CatchSwitchInst::getParentPad,
                                       &CatchSwitchInst::setParentPad>>(this);
  cast<llvm::CatchSwitchInst>(Val)->setParentPad(ParentPad->Val);
}

BasicBlock *CatchSwitchInst::getUnwindDest() const {
  return cast_or_null<BasicBlock>(
      Ctx.getValue(cast<llvm::CatchSwitchInst>(Val)->getUnwindDest()));
}

void CatchSwitchInst::setUnwindDest(BasicBlock *UnwindDest) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&CatchSwitchInst::getUnwindDest,
                                       &CatchSwitchInst::setUnwindDest>>(this);
  cast<llvm::CatchSwitchInst>(Val)->setUnwindDest(
      cast<llvm::BasicBlock>(UnwindDest->Val));
}

void CatchSwitchInst::addHandler(BasicBlock *Dest) {
  Ctx.getTracker().emplaceIfTracking<CatchSwitchAddHandler>(this);
  cast<llvm::CatchSwitchInst>(Val)->addHandler(
      cast<llvm::BasicBlock>(Dest->Val));
}

ResumeInst *ResumeInst::create(Value *Exn, InsertPosition Pos, Context &Ctx) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMI = cast<llvm::ResumeInst>(Builder.CreateResume(Exn->Val));
  return Ctx.createResumeInst(LLVMI);
}

Value *ResumeInst::getValue() const {
  return Ctx.getValue(cast<llvm::ResumeInst>(Val)->getValue());
}

SwitchInst *SwitchInst::create(Value *V, BasicBlock *Dest, unsigned NumCases,
                               InsertPosition Pos, Context &Ctx,
                               const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::SwitchInst *LLVMSwitch =
      Builder.CreateSwitch(V->Val, cast<llvm::BasicBlock>(Dest->Val), NumCases);
  return Ctx.createSwitchInst(LLVMSwitch);
}

Value *SwitchInst::getCondition() const {
  return Ctx.getValue(cast<llvm::SwitchInst>(Val)->getCondition());
}

void SwitchInst::setCondition(Value *V) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&SwitchInst::getCondition, &SwitchInst::setCondition>>(
          this);
  cast<llvm::SwitchInst>(Val)->setCondition(V->Val);
}

BasicBlock *SwitchInst::getDefaultDest() const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::SwitchInst>(Val)->getDefaultDest()));
}

void SwitchInst::setDefaultDest(BasicBlock *DefaultCase) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&SwitchInst::getDefaultDest,
                                       &SwitchInst::setDefaultDest>>(this);
  cast<llvm::SwitchInst>(Val)->setDefaultDest(
      cast<llvm::BasicBlock>(DefaultCase->Val));
}
ConstantInt *SwitchInst::findCaseDest(BasicBlock *BB) {
  auto *LLVMC = cast<llvm::SwitchInst>(Val)->findCaseDest(
      cast<llvm::BasicBlock>(BB->Val));
  return LLVMC != nullptr ? cast<ConstantInt>(Ctx.getValue(LLVMC)) : nullptr;
}

void SwitchInst::addCase(ConstantInt *OnVal, BasicBlock *Dest) {
  Ctx.getTracker().emplaceIfTracking<SwitchAddCase>(this, OnVal);
  // TODO: Track this!
  cast<llvm::SwitchInst>(Val)->addCase(cast<llvm::ConstantInt>(OnVal->Val),
                                       cast<llvm::BasicBlock>(Dest->Val));
}

SwitchInst::CaseIt SwitchInst::removeCase(CaseIt It) {
  Ctx.getTracker().emplaceIfTracking<SwitchRemoveCase>(this);

  auto *LLVMSwitch = cast<llvm::SwitchInst>(Val);
  unsigned CaseNum = It - case_begin();
  llvm::SwitchInst::CaseIt LLVMIt(LLVMSwitch, CaseNum);
  auto LLVMCaseIt = LLVMSwitch->removeCase(LLVMIt);
  unsigned Num = LLVMCaseIt - LLVMSwitch->case_begin();
  return CaseIt(this, Num);
}

BasicBlock *SwitchInst::getSuccessor(unsigned Idx) const {
  return cast<BasicBlock>(
      Ctx.getValue(cast<llvm::SwitchInst>(Val)->getSuccessor(Idx)));
}

void SwitchInst::setSuccessor(unsigned Idx, BasicBlock *NewSucc) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetterWithIdx<&SwitchInst::getSuccessor,
                                              &SwitchInst::setSuccessor>>(this,
                                                                          Idx);
  cast<llvm::SwitchInst>(Val)->setSuccessor(
      Idx, cast<llvm::BasicBlock>(NewSucc->Val));
}

Value *UnaryOperator::create(Instruction::Opcode Op, Value *OpV,
                             InsertPosition Pos, Context &Ctx,
                             const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *NewLLVMV = Builder.CreateUnOp(getLLVMUnaryOp(Op), OpV->Val, Name);
  if (auto *NewUnOpV = dyn_cast<llvm::UnaryOperator>(NewLLVMV)) {
    return Ctx.createUnaryOperator(NewUnOpV);
  }
  assert(isa<llvm::Constant>(NewLLVMV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewLLVMV));
}

Value *UnaryOperator::createWithCopiedFlags(Instruction::Opcode Op, Value *OpV,
                                            Value *CopyFrom, InsertPosition Pos,
                                            Context &Ctx, const Twine &Name) {
  auto *NewV = create(Op, OpV, Pos, Ctx, Name);
  if (auto *UnI = dyn_cast<llvm::UnaryOperator>(NewV->Val))
    UnI->copyIRFlags(CopyFrom->Val);
  return NewV;
}

/// \Returns the LLVM opcode that corresponds to \p Opc.
static llvm::Instruction::BinaryOps getLLVMBinaryOp(Instruction::Opcode Opc) {
  switch (Opc) {
  case Instruction::Opcode::Add:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Add);
  case Instruction::Opcode::FAdd:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::FAdd);
  case Instruction::Opcode::Sub:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Sub);
  case Instruction::Opcode::FSub:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::FSub);
  case Instruction::Opcode::Mul:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Mul);
  case Instruction::Opcode::FMul:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::FMul);
  case Instruction::Opcode::UDiv:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::UDiv);
  case Instruction::Opcode::SDiv:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::SDiv);
  case Instruction::Opcode::FDiv:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::FDiv);
  case Instruction::Opcode::URem:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::URem);
  case Instruction::Opcode::SRem:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::SRem);
  case Instruction::Opcode::FRem:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::FRem);
  case Instruction::Opcode::Shl:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Shl);
  case Instruction::Opcode::LShr:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::LShr);
  case Instruction::Opcode::AShr:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::AShr);
  case Instruction::Opcode::And:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::And);
  case Instruction::Opcode::Or:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Or);
  case Instruction::Opcode::Xor:
    return static_cast<llvm::Instruction::BinaryOps>(llvm::Instruction::Xor);
  default:
    llvm_unreachable("Not a binary op!");
  }
}
Value *BinaryOperator::create(Instruction::Opcode Op, Value *LHS, Value *RHS,
                              InsertPosition Pos, Context &Ctx,
                              const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV =
      Builder.CreateBinOp(getLLVMBinaryOp(Op), LHS->Val, RHS->Val, Name);
  if (auto *NewBinOp = dyn_cast<llvm::BinaryOperator>(NewV))
    return Ctx.createBinaryOperator(NewBinOp);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Value *BinaryOperator::createWithCopiedFlags(Instruction::Opcode Op, Value *LHS,
                                             Value *RHS, Value *CopyFrom,
                                             InsertPosition Pos, Context &Ctx,
                                             const Twine &Name) {

  Value *NewV = create(Op, LHS, RHS, Pos, Ctx, Name);
  if (auto *NewBO = dyn_cast<BinaryOperator>(NewV))
    cast<llvm::BinaryOperator>(NewBO->Val)->copyIRFlags(CopyFrom->Val);
  return NewV;
}

void PossiblyDisjointInst::setIsDisjoint(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&PossiblyDisjointInst::isDisjoint,
                                       &PossiblyDisjointInst::setIsDisjoint>>(
          this);
  cast<llvm::PossiblyDisjointInst>(Val)->setIsDisjoint(B);
}

void AtomicRMWInst::setAlignment(Align Align) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicRMWInst::getAlign,
                                       &AtomicRMWInst::setAlignment>>(this);
  cast<llvm::AtomicRMWInst>(Val)->setAlignment(Align);
}

void AtomicRMWInst::setVolatile(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicRMWInst::isVolatile,
                                       &AtomicRMWInst::setVolatile>>(this);
  cast<llvm::AtomicRMWInst>(Val)->setVolatile(V);
}

void AtomicRMWInst::setOrdering(AtomicOrdering Ordering) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicRMWInst::getOrdering,
                                       &AtomicRMWInst::setOrdering>>(this);
  cast<llvm::AtomicRMWInst>(Val)->setOrdering(Ordering);
}

void AtomicRMWInst::setSyncScopeID(SyncScope::ID SSID) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicRMWInst::getSyncScopeID,
                                       &AtomicRMWInst::setSyncScopeID>>(this);
  cast<llvm::AtomicRMWInst>(Val)->setSyncScopeID(SSID);
}

Value *AtomicRMWInst::getPointerOperand() {
  return Ctx.getValue(cast<llvm::AtomicRMWInst>(Val)->getPointerOperand());
}

Value *AtomicRMWInst::getValOperand() {
  return Ctx.getValue(cast<llvm::AtomicRMWInst>(Val)->getValOperand());
}

AtomicRMWInst *AtomicRMWInst::create(BinOp Op, Value *Ptr, Value *Val,
                                     MaybeAlign Align, AtomicOrdering Ordering,
                                     InsertPosition Pos, Context &Ctx,
                                     SyncScope::ID SSID, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMAtomicRMW =
      Builder.CreateAtomicRMW(Op, Ptr->Val, Val->Val, Align, Ordering, SSID);
  LLVMAtomicRMW->setName(Name);
  return Ctx.createAtomicRMWInst(LLVMAtomicRMW);
}

void AtomicCmpXchgInst::setSyncScopeID(SyncScope::ID SSID) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::getSyncScopeID,
                                       &AtomicCmpXchgInst::setSyncScopeID>>(
          this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setSyncScopeID(SSID);
}

Value *AtomicCmpXchgInst::getPointerOperand() {
  return Ctx.getValue(cast<llvm::AtomicCmpXchgInst>(Val)->getPointerOperand());
}

Value *AtomicCmpXchgInst::getCompareOperand() {
  return Ctx.getValue(cast<llvm::AtomicCmpXchgInst>(Val)->getCompareOperand());
}

Value *AtomicCmpXchgInst::getNewValOperand() {
  return Ctx.getValue(cast<llvm::AtomicCmpXchgInst>(Val)->getNewValOperand());
}

AtomicCmpXchgInst *
AtomicCmpXchgInst::create(Value *Ptr, Value *Cmp, Value *New, MaybeAlign Align,
                          AtomicOrdering SuccessOrdering,
                          AtomicOrdering FailureOrdering, InsertPosition Pos,
                          Context &Ctx, SyncScope::ID SSID, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *LLVMAtomicCmpXchg =
      Builder.CreateAtomicCmpXchg(Ptr->Val, Cmp->Val, New->Val, Align,
                                  SuccessOrdering, FailureOrdering, SSID);
  LLVMAtomicCmpXchg->setName(Name);
  return Ctx.createAtomicCmpXchgInst(LLVMAtomicCmpXchg);
}

void AtomicCmpXchgInst::setAlignment(Align Align) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::getAlign,
                                       &AtomicCmpXchgInst::setAlignment>>(this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setAlignment(Align);
}

void AtomicCmpXchgInst::setVolatile(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::isVolatile,
                                       &AtomicCmpXchgInst::setVolatile>>(this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setVolatile(V);
}

void AtomicCmpXchgInst::setWeak(bool IsWeak) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::isWeak,
                                       &AtomicCmpXchgInst::setWeak>>(this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setWeak(IsWeak);
}

void AtomicCmpXchgInst::setSuccessOrdering(AtomicOrdering Ordering) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::getSuccessOrdering,
                                       &AtomicCmpXchgInst::setSuccessOrdering>>(
          this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setSuccessOrdering(Ordering);
}

void AtomicCmpXchgInst::setFailureOrdering(AtomicOrdering Ordering) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AtomicCmpXchgInst::getFailureOrdering,
                                       &AtomicCmpXchgInst::setFailureOrdering>>(
          this);
  cast<llvm::AtomicCmpXchgInst>(Val)->setFailureOrdering(Ordering);
}

AllocaInst *AllocaInst::create(Type *Ty, unsigned AddrSpace, InsertPosition Pos,
                               Context &Ctx, Value *ArraySize,
                               const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  auto *NewAlloca =
      Builder.CreateAlloca(Ty->LLVMTy, AddrSpace, ArraySize->Val, Name);
  return Ctx.createAllocaInst(NewAlloca);
}

Type *AllocaInst::getAllocatedType() const {
  return Ctx.getType(cast<llvm::AllocaInst>(Val)->getAllocatedType());
}

void AllocaInst::setAllocatedType(Type *Ty) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AllocaInst::getAllocatedType,
                                       &AllocaInst::setAllocatedType>>(this);
  cast<llvm::AllocaInst>(Val)->setAllocatedType(Ty->LLVMTy);
}

void AllocaInst::setAlignment(Align Align) {
  Ctx.getTracker()
      .emplaceIfTracking<
          GenericSetter<&AllocaInst::getAlign, &AllocaInst::setAlignment>>(
          this);
  cast<llvm::AllocaInst>(Val)->setAlignment(Align);
}

void AllocaInst::setUsedWithInAlloca(bool V) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&AllocaInst::isUsedWithInAlloca,
                                       &AllocaInst::setUsedWithInAlloca>>(this);
  cast<llvm::AllocaInst>(Val)->setUsedWithInAlloca(V);
}

Value *AllocaInst::getArraySize() {
  return Ctx.getValue(cast<llvm::AllocaInst>(Val)->getArraySize());
}

PointerType *AllocaInst::getType() const {
  return cast<PointerType>(Ctx.getType(cast<llvm::AllocaInst>(Val)->getType()));
}

Value *CastInst::create(Type *DestTy, Opcode Op, Value *Operand,
                        InsertPosition Pos, Context &Ctx, const Twine &Name) {
  assert(getLLVMCastOp(Op) && "Opcode not suitable for CastInst!");
  auto &Builder = setInsertPos(Pos);
  auto *NewV =
      Builder.CreateCast(getLLVMCastOp(Op), Operand->Val, DestTy->LLVMTy, Name);
  if (auto *NewCI = dyn_cast<llvm::CastInst>(NewV))
    return Ctx.createCastInst(NewCI);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

bool CastInst::classof(const Value *From) {
  return From->getSubclassID() == ClassID::Cast;
}

Type *CastInst::getSrcTy() const {
  return Ctx.getType(cast<llvm::CastInst>(Val)->getSrcTy());
}

Type *CastInst::getDestTy() const {
  return Ctx.getType(cast<llvm::CastInst>(Val)->getDestTy());
}

void PossiblyNonNegInst::setNonNeg(bool B) {
  Ctx.getTracker()
      .emplaceIfTracking<GenericSetter<&PossiblyNonNegInst::hasNonNeg,
                                       &PossiblyNonNegInst::setNonNeg>>(this);
  cast<llvm::PossiblyNonNegInst>(Val)->setNonNeg(B);
}

Value *InsertElementInst::create(Value *Vec, Value *NewElt, Value *Idx,
                                 InsertPosition Pos, Context &Ctx,
                                 const Twine &Name) {
  auto &Builder = Instruction::setInsertPos(Pos);
  llvm::Value *NewV =
      Builder.CreateInsertElement(Vec->Val, NewElt->Val, Idx->Val, Name);
  if (auto *NewInsert = dyn_cast<llvm::InsertElementInst>(NewV))
    return Ctx.createInsertElementInst(NewInsert);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Value *ExtractElementInst::create(Value *Vec, Value *Idx, InsertPosition Pos,
                                  Context &Ctx, const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV = Builder.CreateExtractElement(Vec->Val, Idx->Val, Name);
  if (auto *NewExtract = dyn_cast<llvm::ExtractElementInst>(NewV))
    return Ctx.createExtractElementInst(NewExtract);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Value *ShuffleVectorInst::create(Value *V1, Value *V2, Value *Mask,
                                 InsertPosition Pos, Context &Ctx,
                                 const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV =
      Builder.CreateShuffleVector(V1->Val, V2->Val, Mask->Val, Name);
  if (auto *NewShuffle = dyn_cast<llvm::ShuffleVectorInst>(NewV))
    return Ctx.createShuffleVectorInst(NewShuffle);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Value *ShuffleVectorInst::create(Value *V1, Value *V2, ArrayRef<int> Mask,
                                 InsertPosition Pos, Context &Ctx,
                                 const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV = Builder.CreateShuffleVector(V1->Val, V2->Val, Mask, Name);
  if (auto *NewShuffle = dyn_cast<llvm::ShuffleVectorInst>(NewV))
    return Ctx.createShuffleVectorInst(NewShuffle);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

void ShuffleVectorInst::setShuffleMask(ArrayRef<int> Mask) {
  Ctx.getTracker().emplaceIfTracking<ShuffleVectorSetMask>(this);
  cast<llvm::ShuffleVectorInst>(Val)->setShuffleMask(Mask);
}

VectorType *ShuffleVectorInst::getType() const {
  return cast<VectorType>(
      Ctx.getType(cast<llvm::ShuffleVectorInst>(Val)->getType()));
}

void ShuffleVectorInst::commute() {
  Ctx.getTracker().emplaceIfTracking<ShuffleVectorSetMask>(this);
  Ctx.getTracker().emplaceIfTracking<UseSwap>(getOperandUse(0),
                                              getOperandUse(1));
  cast<llvm::ShuffleVectorInst>(Val)->commute();
}

Constant *ShuffleVectorInst::getShuffleMaskForBitcode() const {
  return Ctx.getOrCreateConstant(
      cast<llvm::ShuffleVectorInst>(Val)->getShuffleMaskForBitcode());
}

Constant *ShuffleVectorInst::convertShuffleMaskForBitcode(ArrayRef<int> Mask,
                                                          Type *ResultTy) {
  return ResultTy->getContext().getOrCreateConstant(
      llvm::ShuffleVectorInst::convertShuffleMaskForBitcode(Mask,
                                                            ResultTy->LLVMTy));
}

VectorType *ExtractElementInst::getVectorOperandType() const {
  return cast<VectorType>(Ctx.getType(getVectorOperand()->getType()->LLVMTy));
}

Value *ExtractValueInst::create(Value *Agg, ArrayRef<unsigned> Idxs,
                                InsertPosition Pos, Context &Ctx,
                                const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV = Builder.CreateExtractValue(Agg->Val, Idxs, Name);
  if (auto *NewExtractValueInst = dyn_cast<llvm::ExtractValueInst>(NewV))
    return Ctx.createExtractValueInst(NewExtractValueInst);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

Type *ExtractValueInst::getIndexedType(Type *Agg, ArrayRef<unsigned> Idxs) {
  auto *LLVMTy = llvm::ExtractValueInst::getIndexedType(Agg->LLVMTy, Idxs);
  return Agg->getContext().getType(LLVMTy);
}

Value *InsertValueInst::create(Value *Agg, Value *Val, ArrayRef<unsigned> Idxs,
                               InsertPosition Pos, Context &Ctx,
                               const Twine &Name) {
  auto &Builder = setInsertPos(Pos);
  llvm::Value *NewV = Builder.CreateInsertValue(Agg->Val, Val->Val, Idxs, Name);
  if (auto *NewInsertValueInst = dyn_cast<llvm::InsertValueInst>(NewV))
    return Ctx.createInsertValueInst(NewInsertValueInst);
  assert(isa<llvm::Constant>(NewV) && "Expected constant");
  return Ctx.getOrCreateConstant(cast<llvm::Constant>(NewV));
}

ConstantTokenNone *ConstantTokenNone::get(Context &Ctx) {
  auto *LLVMC = llvm::ConstantTokenNone::get(Ctx.LLVMCtx);
  return cast<ConstantTokenNone>(Ctx.getOrCreateConstant(LLVMC));
}

} // namespace llvm::sandboxir
