//===------------- ArithTransformReuse.h - Expression reuse facility
//-----------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
/// Reuse an available expression by identifying its presence
/// in another by tranforming the new expression arithmatically
//
//===----------------------------------------------------------------------===//
//====  Copyright (c) 2015 Advanced Micro Devices, Inc.  All rights reserved.
//
//               Developed by: Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// with the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimers.
//
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimers in the documentation
// and/or other materials provided with the distribution.
//
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
// contributors may be used to endorse or promote products derived from this
// Software without specific prior written permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
// THE SOFTWARE.
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_SCALAR_ARITHTRANSFORMRULES_H
#define LLVM_TRANSFORMS_SCALAR_ARITHTRANSFORMRULES_H

#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/TargetFolder.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"

#include <cassert>

namespace llvm {
class MathExpression {
public:
  MathExpression() : Root(NULL), LST(NULL), RST(NULL), isLeaf(false) {}
  bool isLeafOfExpr() { return isLeaf; }
  void SetAsLeafOfExpr() { this->isLeaf = true; }
  Value *Root;
  MathExpression *LST;
  MathExpression *RST;
  bool isLeaf;
};
class ArithTransformRules {
public:
  BasicBlock *ParentBB;
  std::list<std::pair<bool, MathExpression *> (ArithTransformRules::*)(
      llvm::MathExpression *, BasicBlock *BB)>
      Rules;
  void PopulateRules(BasicBlock &BB);
  std::pair<bool, MathExpression *> VarRule(MathExpression *expr,
                                            BasicBlock *BB);
  std::pair<bool, MathExpression *> AddCommute(MathExpression *expr,
                                               BasicBlock *BB);
  std::pair<bool, MathExpression *> AddAssociate(MathExpression *expr,
                                                 BasicBlock *BB);
  std::pair<bool, MathExpression *> AddAssociateRev(MathExpression *expr,
                                                    BasicBlock *BB);
  std::pair<bool, MathExpression *> MulCommute(MathExpression *expr,
                                               BasicBlock *BB);
  std::pair<bool, MathExpression *> MulAssociate(MathExpression *expr,
                                                 BasicBlock *BB);
  std::pair<bool, MathExpression *> MulAssociateRev(MathExpression *expr,
                                                    BasicBlock *BB);
  std::pair<bool, MathExpression *> MulDistribute(MathExpression *expr,
                                                  BasicBlock *BB);
  std::pair<bool, MathExpression *> MulRevDistribute(MathExpression *expr,
                                                     BasicBlock *BB);
  std::pair<bool, MathExpression *> DivDistribute(MathExpression *expr,
                                                  BasicBlock *BB);
  std::pair<bool, MathExpression *> FDivDistribute(MathExpression *expr,
                                                   BasicBlock *BB);
  std::pair<bool, MathExpression *> DivRevDistribute(MathExpression *expr,
                                                     BasicBlock *BB);
  std::pair<bool, MathExpression *> FactorizeDiv(MathExpression *expr,
                                                 BasicBlock *BB);
  std::pair<bool, MathExpression *> DivMulReciprocal(MathExpression *expr,
                                                     BasicBlock *BB);
};

std::pair<bool, MathExpression *>
ArithTransformRules::VarRule(MathExpression *expr, BasicBlock *BB) {
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::AddCommute(MathExpression *expr, BasicBlock *BB) {
  if (dyn_cast<Instruction>(expr->Root) && expr->RST && expr->LST) {
    if ((dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::Add) {
      MathExpression *TransformedExpression = new MathExpression();
      IRBuilder<> Builder(BB);
      Value *newAdd;
      bool isInBB = false;
      for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE; ++I) {
        if ((*I).getOpcode() == Instruction::Add) {
          if (I->getOperand(0) == expr->RST->Root &&
              I->getOperand(1) == expr->LST->Root) {
            newAdd = dyn_cast<Value>(I);
            isInBB = true;
          }
        }
      }
      for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
           I != IE; ++I) {
        if ((*I).getOpcode() == Instruction::Add) {
          if (I->getOperand(0) == expr->RST->Root &&
              I->getOperand(1) == expr->LST->Root) {
            newAdd = dyn_cast<Value>(I);
            isInBB = true;
          }
        }
      }
      if (!isInBB) {
        newAdd =
            Builder.CreateAdd(expr->RST->Root, expr->LST->Root, "addnewone");
      }
      TransformedExpression->Root = newAdd;
      TransformedExpression->LST = expr->RST;
      TransformedExpression->RST = expr->LST;
      return std::make_pair(true, TransformedExpression);
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::AddAssociate(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if (dyn_cast<Instruction>(expr->Root)) {
    auto Opcode = (dyn_cast<Instruction>(expr->Root))->getOpcode();
    if ((Opcode == Instruction::Add || Opcode == Instruction::FAdd) &&
        expr->RST) {
      if ((dyn_cast<Instruction>(expr->RST->Root))) {
        auto rstOpcode = (dyn_cast<Instruction>(expr->RST->Root))->getOpcode();
        if (rstOpcode == Opcode) {
          if (expr->RST->LST && expr->RST->RST) {
            Value *newLSTAdd;
            bool isInBB = false;
            for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
                 ++I) {
              if ((*I).getOpcode() == rstOpcode) {
                if (I->getOperand(0) == expr->LST->Root &&
                    I->getOperand(1) == expr->RST->LST->Root) {
                  newLSTAdd = dyn_cast<Value>(I);
                  isInBB = true;
                }
              }
            }
            for (BasicBlock::iterator I = ParentBB->begin(),
                                      IE = ParentBB->end();
                 I != IE; ++I) {
              if ((*I).getOpcode() == rstOpcode) {
                if (I->getOperand(0) == expr->LST->Root &&
                    I->getOperand(1) == expr->RST->LST->Root) {
                  newLSTAdd = dyn_cast<Value>(I);
                  isInBB = true;
                }
              }
            }
            IRBuilder<> Builder(BB);
            if (!isInBB) {
              if (rstOpcode == Instruction::Add) {
                newLSTAdd = Builder.CreateAdd(
                    expr->LST->Root, expr->RST->LST->Root, "addnewtwo");
              }
              if (rstOpcode == Instruction::FAdd) {
                newLSTAdd = Builder.CreateFAdd(
                    expr->LST->Root, expr->RST->LST->Root, "Faddnewtwo");
              }
            }
            MathExpression *newLST = new MathExpression();
            newLST->Root = newLSTAdd;
            newLST->LST = expr->LST;
            newLST->RST = expr->RST->LST;
            Value *newExpr;
            bool isInsInBB = false;
            for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
                 ++I) {
              if ((*I).getOpcode() == rstOpcode) {
                if (I->getOperand(0) == newLSTAdd &&
                    I->getOperand(1) == expr->RST->RST->Root) {
                  newExpr = dyn_cast<Value>(I);
                  isInsInBB = true;
                }
              }
            }
            for (BasicBlock::iterator I = ParentBB->begin(),
                                      IE = ParentBB->end();
                 I != IE; ++I) {
              if ((*I).getOpcode() == rstOpcode) {
                if (I->getOperand(0) == newLSTAdd &&
                    I->getOperand(1) == expr->RST->RST->Root) {
                  newExpr = dyn_cast<Value>(I);
                  isInsInBB = true;
                }
              }
            }
            if (!isInsInBB) {
              if (rstOpcode == Instruction::Add) {
                newExpr = Builder.CreateAdd(newLSTAdd, expr->RST->RST->Root,
                                            "addnewthree");
              }
              if (rstOpcode == Instruction::FAdd) {
                newExpr = Builder.CreateFAdd(newLSTAdd, expr->RST->RST->Root,
                                             "Faddnewthree");
              }
            }
            TransformedExpression->Root = newExpr;
            TransformedExpression->LST = newLST;
            TransformedExpression->RST = expr->RST->RST;
            return std::make_pair(true, TransformedExpression);
          }
        }
      }
    }
  }
  return std::make_pair(false, TransformedExpression);
}
std::pair<bool, MathExpression *>
ArithTransformRules::AddAssociateRev(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) && expr->LST && !expr->LST->isLeaf) {
    auto Opcode = (dyn_cast<Instruction>(expr->Root))->getOpcode();
    if (Opcode == Instruction::Add || Opcode == Instruction::FAdd) {
      if (expr->LST->RST && expr->LST->LST) {
        if (dyn_cast<Instruction>(expr->LST->Root)) {
          auto LstOpcode =
              (dyn_cast<Instruction>(expr->LST->Root))->getOpcode();
          if (LstOpcode == Opcode) {
            Value *newRSTAdd;
            bool isInBB = false;
            for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
                 ++I) {
              if ((*I).getOpcode() == LstOpcode) {
                if (I->getOperand(0) == expr->LST->RST->Root &&
                    I->getOperand(1) == expr->RST->Root) {
                  newRSTAdd = dyn_cast<Value>(I);
                  isInBB = true;
                }
              }
            }
            for (BasicBlock::iterator I = ParentBB->begin(),
                                      IE = ParentBB->end();
                 I != IE; ++I) {
              if ((*I).getOpcode() == LstOpcode) {
                if (I->getOperand(0) == expr->LST->RST->Root &&
                    I->getOperand(1) == expr->RST->Root) {
                  newRSTAdd = dyn_cast<Value>(I);
                  isInBB = true;
                }
              }
            }
            IRBuilder<> Builder(BB);
            if (!isInBB) {
              if (LstOpcode == Instruction::Add) {
                newRSTAdd = Builder.CreateAdd(expr->LST->RST->Root,
                                              expr->RST->Root, "addnewfour");
              }
              if (LstOpcode == Instruction::FAdd) {
                newRSTAdd = Builder.CreateFAdd(expr->LST->RST->Root,
                                               expr->RST->Root, "Faddnewfour");
              }
            }
            MathExpression *newRST = new MathExpression();
            newRST->Root = newRSTAdd;
            newRST->LST = expr->LST->RST;
            newRST->RST = expr->RST;
            Value *newExpr;
            bool isInsInBB = false;
            for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
                 ++I) {
              if ((*I).getOpcode() == Opcode) {
                if (I->getOperand(0) == expr->LST->LST->Root &&
                    I->getOperand(1) == newRSTAdd) {
                  newExpr = dyn_cast<Value>(I);
                  isInsInBB = true;
                }
              }
            }
            for (BasicBlock::iterator I = ParentBB->begin(),
                                      IE = ParentBB->end();
                 I != IE; ++I) {
              if ((*I).getOpcode() == Opcode) {
                if (I->getOperand(0) == expr->LST->LST->Root &&
                    I->getOperand(1) == newRSTAdd) {
                  newExpr = dyn_cast<Value>(I);
                  isInsInBB = true;
                }
              }
            }
            if (!isInsInBB) {
              if (Opcode == Instruction::Add) {
                newExpr = Builder.CreateAdd(expr->LST->LST->Root, newRSTAdd,
                                            "addnewfive");
              }
              if (Opcode == Instruction::FAdd) {
                newExpr = Builder.CreateFAdd(expr->LST->LST->Root, newRSTAdd,
                                             "Faddnewfive");
              }
            }
            TransformedExpression->Root = newExpr;
            TransformedExpression->LST = expr->LST->LST;
            TransformedExpression->RST = newRST;
            return std::make_pair(true, TransformedExpression);
          }
        }
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::MulCommute(MathExpression *expr, BasicBlock *BB) {
  if (dyn_cast<Instruction>(expr->Root) && expr->RST && expr->LST) {
    if ((dyn_cast<Instruction>(expr->Root))) {
      auto OpCode = (dyn_cast<Instruction>(expr->Root))->getOpcode();
      if (OpCode == Instruction::Mul || OpCode == Instruction::FMul) {
        MathExpression *TransformedExpression = new MathExpression();
        IRBuilder<> Builder(BB);
        Value *newMul;
        bool isInBB = false;
        for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
             ++I) {
          if ((*I).getOpcode() == OpCode) {
            if (I->getOperand(0) == expr->RST->Root &&
                I->getOperand(1) == expr->LST->Root) {
              newMul = dyn_cast<Value>(I);
              isInBB = true;
            }
          }
        }
        for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
             I != IE; ++I) {
          if ((*I).getOpcode() == OpCode) {
            if (I->getOperand(0) == expr->RST->Root &&
                I->getOperand(1) == expr->LST->Root) {
              newMul = dyn_cast<Value>(I);
              isInBB = true;
            }
          }
        }
        if (!isInBB) {
          if (OpCode == Instruction::Mul) {
            newMul = Builder.CreateMul(expr->RST->Root, expr->LST->Root,
                                       "newmulone");
          } else if (OpCode == Instruction::FMul) {
            newMul = Builder.CreateFMul(expr->RST->Root, expr->LST->Root,
                                        "newfmulone");
          }
        }
        TransformedExpression->Root = newMul;
        TransformedExpression->LST = expr->RST;
        TransformedExpression->RST = expr->LST;
        return std::make_pair(true, TransformedExpression);
      }
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::MulAssociate(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) &&
      (dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::Mul &&
      expr->RST && !expr->RST->isLeaf) {
    if (dyn_cast<Instruction>(expr->RST->Root)) {
      if ((dyn_cast<Instruction>(expr->RST->Root))->getOpcode() ==
          Instruction::Mul) {
        Value *newLSTMul;
        bool isInBB = false;
        for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
             ++I) {
          if ((*I).getOpcode() == Instruction::Mul) {
            if (I->getOperand(0) == expr->LST->Root &&
                I->getOperand(1) == expr->RST->LST->Root) {
              newLSTMul = dyn_cast<Value>(I);
              isInBB = true;
            }
          }
        }
        for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
             I != IE; ++I) {
          if ((*I).getOpcode() == Instruction::Mul) {
            if (I->getOperand(0) == expr->LST->Root &&
                I->getOperand(1) == expr->RST->LST->Root) {
              newLSTMul = dyn_cast<Value>(I);
              isInBB = true;
            }
          }
        }
        IRBuilder<> Builder(BB);
        if (!isInBB) {
          newLSTMul = Builder.CreateMul(expr->LST->Root, expr->RST->LST->Root,
                                        "newmultwo");
        }
        MathExpression *newLST = new MathExpression();
        newLST->Root = newLSTMul;
        newLST->LST = expr->LST;
        newLST->RST = expr->RST->LST;
        Value *newExpr;
        bool isInsInBB = false;
        for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
             ++I) {
          if ((*I).getOpcode() == Instruction::Mul) {
            if (I->getOperand(0) == newLSTMul &&
                I->getOperand(1) == expr->RST->RST->Root) {
              newExpr = dyn_cast<Value>(I);
              isInsInBB = true;
            }
          }
        }
        for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
             I != IE; ++I) {
          if ((*I).getOpcode() == Instruction::Mul) {
            if (I->getOperand(0) == newLSTMul &&
                I->getOperand(1) == expr->RST->RST->Root) {
              newExpr = dyn_cast<Value>(I);
              isInsInBB = true;
            }
          }
        }
        if (!isInsInBB) {
          newExpr =
              Builder.CreateMul(newLSTMul, expr->RST->RST->Root, "newmulthree");
        }
        TransformedExpression->Root = newExpr;
        TransformedExpression->LST = newLST;
        TransformedExpression->RST = expr->RST->RST;
        return std::make_pair(true, TransformedExpression);
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::MulAssociateRev(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if (dyn_cast<Instruction>(expr->Root)) {
    if ((dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::Mul &&
        expr->LST && expr->RST && expr->LST->Root && expr->RST->Root &&
        !expr->LST->isLeaf) {
      if (dyn_cast<Instruction>(expr->LST->Root)) {
        if ((dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
            Instruction::Mul) {
          Value *newRSTMul;
          bool isInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->RST->Root &&
                  I->getOperand(1) == expr->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->RST->Root &&
                  I->getOperand(1) == expr->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isInBB = true;
              }
            }
          }
          IRBuilder<> Builder(BB);
          if (!isInBB) {
            newRSTMul = Builder.CreateMul(expr->LST->RST->Root, expr->RST->Root,
                                          "newmulfour");
          }
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTMul;
          newRST->LST = expr->LST->RST;
          newRST->RST = expr->RST;
          Value *newExpr;
          bool isInsInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->LST->Root &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInsInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->LST->Root &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInsInBB = true;
              }
            }
          }
          if (!isInsInBB) {
            newExpr = Builder.CreateMul(expr->LST->LST->Root, newRSTMul,
                                        "newmulfive");
          }
          TransformedExpression->Root = newExpr;
          TransformedExpression->LST = expr->LST->LST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::MulDistribute(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) &&
      (dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::Mul &&
      expr->LST && expr->RST && expr->LST->Root && expr->RST->Root) {
    if (expr->RST && !expr->RST->isLeaf) {
      if (dyn_cast<Instruction>(expr->RST->Root)) {
        if ((dyn_cast<Instruction>(expr->RST->Root))->getOpcode() ==
            Instruction::Add) {
          IRBuilder<> Builder(BB);
          Value *newLSTMul;
          Value *newRSTMul;
          bool isLSTinBB = false;
          bool isRSTinBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->Root &&
                  I->getOperand(1) == expr->RST->LST->Root) {
                newLSTMul = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(0) == expr->LST->Root &&
                  I->getOperand(1) == expr->RST->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(0) == expr->LST->Root &&
                  I->getOperand(1) == expr->RST->LST->Root) {
                newLSTMul = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(0) == expr->LST->Root &&
                  I->getOperand(1) == expr->RST->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          if (!isLSTinBB) {
            newLSTMul = Builder.CreateMul(expr->LST->Root, expr->RST->LST->Root,
                                          "newmulsix");
          }
          if (!isRSTinBB) {
            newRSTMul = Builder.CreateMul(expr->LST->Root, expr->RST->RST->Root,
                                          "newmulseven");
          }
          MathExpression *newLST = new MathExpression();
          newLST->Root = newLSTMul;
          newLST->LST = expr->LST;
          newLST->RST = expr->RST->LST;
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTMul;
          newRST->LST = expr->LST;
          newRST->RST = expr->RST->RST;
          Value *newExpr;
          bool isInsInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTMul &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInsInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTMul &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInsInBB = true;
              }
            }
          }
          if (!isInsInBB) {
            newExpr = Builder.CreateAdd(newLSTMul, newRSTMul, "newaddsix");
          }
          TransformedExpression->Root = newExpr;
          TransformedExpression->LST = newLST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
    if (expr->LST && !expr->LST->isLeaf) {
      if (dyn_cast<Instruction>(expr->LST->Root)) {
        if ((dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
            Instruction::Add) {
          IRBuilder<> Builder(BB);
          Value *newLSTMul;
          Value *newRSTMul;
          bool isLSTinBB = false;
          bool isRSTinBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTMul = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Mul) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTMul = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTMul = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          if (!isLSTinBB) {
            newLSTMul = Builder.CreateMul(expr->LST->LST->Root, expr->RST->Root,
                                          "newmul2six");
          }
          if (!isRSTinBB) {
            newRSTMul = Builder.CreateMul(expr->LST->RST->Root, expr->RST->Root,
                                          "newmul2seven");
          }
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTMul;
          newRST->RST = expr->RST;
          newRST->LST = expr->LST->RST;
          MathExpression *newLST = new MathExpression();
          newLST->Root = newLSTMul;
          newLST->RST = expr->RST;
          newLST->LST = expr->LST->LST;
          Value *newExpr;
          bool isInstInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTMul &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTMul &&
                  I->getOperand(1) == newRSTMul) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          if (!isInstInBB) {
            newExpr = Builder.CreateAdd(newLSTMul, newRSTMul, "newadd2six");
          }
          TransformedExpression->Root = newExpr;
          TransformedExpression->LST = newLST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::MulRevDistribute(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) && expr->RST && expr->LST &&
      expr->LST->LST && expr->RST->LST) {
    auto Opcode = (dyn_cast<Instruction>(expr->Root))->getOpcode();
    if (Opcode == Instruction::Add || Opcode == Instruction::FAdd ||
        Opcode == Instruction::Sub || Opcode == Instruction::FSub) {
      if ((dyn_cast<Instruction>(expr->RST->Root)) &&
          (dyn_cast<Instruction>(expr->LST->Root)) &&
          (dyn_cast<Instruction>(expr->LST->LST->Root)) &&
          (dyn_cast<Instruction>(expr->RST->LST->Root))) {
        if ((dyn_cast<Instruction>(expr->RST->Root))->getOpcode() ==
                Instruction::Mul &&
            (dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
                Instruction::Mul &&
            ((dyn_cast<Instruction>(expr->LST->LST->Root)) ==
             (dyn_cast<Instruction>(expr->RST->LST->Root)))) {
          IRBuilder<> Builder(BB);
          Value *newRSTAdd;
          switch (Opcode) {
            switch (Opcode) {
            case Instruction::Add:
              newRSTAdd = Builder.CreateAdd(
                  expr->LST->RST->Root, expr->RST->RST->Root, "addnewseven");
              break;
            case Instruction::FAdd:
              newRSTAdd = Builder.CreateFAdd(
                  expr->LST->RST->Root, expr->RST->RST->Root, "Faddnewseven");
              break;
            case Instruction::Sub:
              newRSTAdd = Builder.CreateSub(
                  expr->LST->RST->Root, expr->RST->RST->Root, "subnewseven");
              break;
            case Instruction::FSub:
              newRSTAdd = Builder.CreateFSub(
                  expr->LST->RST->Root, expr->RST->RST->Root, "fsubnewseven");
            }
          }
          Value *newExprMul =
              Builder.CreateMul(newRSTAdd, expr->LST->LST->Root, "newmuleight");
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTAdd;
          newRST->LST = expr->LST->RST;
          newRST->RST = expr->RST->RST;
          TransformedExpression->Root = newExprMul;
          TransformedExpression->LST = expr->LST->LST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::DivDistribute(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) &&
      (dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::UDiv &&
      expr->LST && expr->RST && expr->LST->Root && expr->RST->Root) {
    if (expr->LST && !expr->LST->isLeaf) {
      if (dyn_cast<Instruction>(expr->LST->Root)) {
        if ((dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
            Instruction::Add) {
          IRBuilder<> Builder(BB);
          Value *newLSTUDiv;
          Value *newRSTUDiv;
          bool isLSTinBB = false;
          bool isRSTinBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::UDiv) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTUDiv = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTUDiv = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::UDiv) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTUDiv = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTUDiv = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          if (!isLSTinBB) {
            newLSTUDiv = Builder.CreateUDiv(expr->LST->LST->Root,
                                            expr->RST->Root, "newUDiv2six");
          }
          if (!isRSTinBB) {
            newRSTUDiv = Builder.CreateUDiv(expr->LST->RST->Root,
                                            expr->RST->Root, "newUDiv2seven");
          }
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTUDiv;
          newRST->RST = expr->RST;
          newRST->LST = expr->LST->RST;
          MathExpression *newLST = new MathExpression();
          newLST->Root = newLSTUDiv;
          newLST->RST = expr->RST;
          newLST->LST = expr->LST->LST;
          Value *newExpr;
          bool isInstInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTUDiv &&
                  I->getOperand(1) == newRSTUDiv) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::Add) {
              if (I->getOperand(0) == newLSTUDiv &&
                  I->getOperand(1) == newRSTUDiv) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          if (!isInstInBB) {
            newExpr = Builder.CreateAdd(newLSTUDiv, newRSTUDiv, "newadd2six");
          }
          TransformedExpression->Root = newExpr;
          TransformedExpression->LST = newLST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::FDivDistribute(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) &&
      (dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::FDiv &&
      expr->LST && expr->RST && expr->LST->Root && expr->RST->Root) {
    if (expr->LST && !expr->LST->isLeaf) {
      if (dyn_cast<Instruction>(expr->LST->Root)) {
        if ((dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
            Instruction::FAdd) {
          IRBuilder<> Builder(BB);
          Value *newLSTFDiv;
          Value *newRSTFDiv;
          bool isLSTinBB = false;
          bool isRSTinBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::FDiv) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTFDiv = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTFDiv = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::FDiv) {
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->LST->Root) {
                newLSTFDiv = dyn_cast<Value>(I);
                isLSTinBB = true;
              }
              if (I->getOperand(1) == expr->RST->Root &&
                  I->getOperand(0) == expr->LST->RST->Root) {
                newRSTFDiv = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          if (!isLSTinBB) {
            newLSTFDiv = Builder.CreateFDiv(expr->LST->LST->Root,
                                            expr->RST->Root, "newFDiv2six");
          }
          if (!isRSTinBB) {
            newRSTFDiv = Builder.CreateFDiv(expr->LST->RST->Root,
                                            expr->RST->Root, "newFDiv2seven");
          }
          MathExpression *newRST = new MathExpression();
          newRST->Root = newRSTFDiv;
          newRST->RST = expr->RST;
          newRST->LST = expr->LST->RST;
          MathExpression *newLST = new MathExpression();
          newLST->Root = newLSTFDiv;
          newLST->RST = expr->RST;
          newLST->LST = expr->LST->LST;
          Value *newExpr;
          bool isInstInBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::FAdd) {
              if (I->getOperand(0) == newLSTFDiv &&
                  I->getOperand(1) == newRSTFDiv) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::FAdd) {
              if (I->getOperand(0) == newLSTFDiv &&
                  I->getOperand(1) == newRSTFDiv) {
                newExpr = dyn_cast<Value>(I);
                isInstInBB = true;
              }
            }
          }
          if (!isInstInBB) {
            newExpr = Builder.CreateFAdd(newLSTFDiv, newRSTFDiv, "newFAdd2six");
          }
          // newExpr->dump();
          TransformedExpression->Root = newExpr;
          TransformedExpression->LST = newLST;
          TransformedExpression->RST = newRST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::DivRevDistribute(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root)) &&
      (dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::Add &&
      expr->RST && expr->LST && expr->LST->LST && expr->RST->LST) {
    if ((dyn_cast<Instruction>(expr->RST->Root)) &&
        (dyn_cast<Instruction>(expr->LST->Root)) &&
        (dyn_cast<Instruction>(expr->LST->LST->Root)) &&
        (dyn_cast<Instruction>(expr->RST->LST->Root))) {
      if ((dyn_cast<Instruction>(expr->RST->Root))->getOpcode() ==
              Instruction::UDiv &&
          (dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
              Instruction::UDiv &&
          ((dyn_cast<Instruction>(expr->LST->LST->Root)) ==
           (dyn_cast<Instruction>(expr->RST->LST->Root)))) {
        IRBuilder<> Builder(BB);
        Value *newRSTAdd = Builder.CreateAdd(
            expr->LST->RST->Root, expr->RST->RST->Root, "newaddnine");
        Value *newExprDiv =
            Builder.CreateUDiv(newRSTAdd, expr->LST->LST->Root, "newdivthree");
        MathExpression *newRST = new MathExpression();
        newRST->Root = newRSTAdd;
        TransformedExpression->Root = newExprDiv;
        TransformedExpression->LST = expr->LST->LST;
        TransformedExpression->RST = newRST;
        return std::make_pair(true, TransformedExpression);
      }
    }
  }
  return std::make_pair(false, expr);
}
std::pair<bool, MathExpression *>
ArithTransformRules::FactorizeDiv(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root))) {
    if ((dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::FDiv &&
        expr->RST && expr->LST && expr->LST->Root && expr->RST->Root) {
      if ((dyn_cast<Instruction>(expr->LST->Root)) &&
          (dyn_cast<Instruction>(expr->LST->Root))->getOpcode() ==
              Instruction::FMul) {
        if (expr->LST->RST && expr->LST->LST) {
          Value *NewLST;
          Value *NewExpr;
          IRBuilder<> Builder(BB);
          bool isRSTinBB = false;
          bool isExprinBB = false;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::FDiv) {
              if (I->getOperand(0) == expr->LST->RST->Root &&
                  I->getOperand(1) == expr->RST->Root) {
                NewLST = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::FDiv) {
              if (I->getOperand(0) == expr->LST->RST->Root &&
                  I->getOperand(1) == expr->RST->Root) {
                NewLST = dyn_cast<Value>(I);
                isRSTinBB = true;
              }
            }
          }
          if (!isRSTinBB) {
            NewLST = Builder.CreateFDiv(expr->LST->RST->Root, expr->RST->Root,
                                        "newFact");
          }
          MathExpression *NewLSTExpr = new MathExpression();
          NewLSTExpr->Root = NewLST;
          NewLSTExpr->LST = expr->LST->RST;
          NewLSTExpr->RST = expr->RST;
          for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
               ++I) {
            if ((*I).getOpcode() == Instruction::FMul) {
              if (I->getOperand(0) == NewLST &&
                  I->getOperand(1) == expr->LST->LST->Root) {
                NewExpr = dyn_cast<Value>(I);
                isExprinBB = true;
              }
            }
          }
          for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
               I != IE; ++I) {
            if ((*I).getOpcode() == Instruction::FMul) {
              if (I->getOperand(0) == NewLST &&
                  I->getOperand(1) == expr->LST->LST->Root) {
                NewExpr = dyn_cast<Value>(I);
                isExprinBB = true;
              }
            }
          }
          if (!isExprinBB) {
            NewExpr =
                Builder.CreateFMul(NewLST, expr->LST->LST->Root, "newFactExpr");
          }
          TransformedExpression->Root = NewExpr;
          TransformedExpression->LST = NewLSTExpr;
          TransformedExpression->RST = expr->LST->LST;
          return std::make_pair(true, TransformedExpression);
        }
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}

std::pair<bool, MathExpression *>
ArithTransformRules::DivMulReciprocal(MathExpression *expr, BasicBlock *BB) {
  MathExpression *TransformedExpression = new MathExpression();
  if ((dyn_cast<Instruction>(expr->Root))) {
    if ((dyn_cast<Instruction>(expr->Root))->getOpcode() == Instruction::FDiv &&
        expr->RST && expr->LST && expr->RST->Root && expr->LST->Root) {
      if ((dyn_cast<Instruction>(expr->LST->Root)) &&
          (dyn_cast<Instruction>(expr->RST->Root))) {
        Value *NewRST;
        Value *NewExpr;
        Value *Numerator;
        IRBuilder<> Builder(BB);
        bool isRSTinBB = false;
        bool isExprinBB = false;
        Numerator = ConstantFP::get(expr->RST->Root->getType(), 1);
        for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
             ++I) {
          if ((*I).getOpcode() == Instruction::FDiv &&
              dyn_cast<ConstantFP>(I->getOperand(0))) {
            if (dyn_cast<ConstantFP>(I->getOperand(0))
                    ->isExactlyValue(1.000000e+00) &&
                I->getOperand(1) == expr->RST->Root) {
              NewRST = dyn_cast<Value>(I);
              isRSTinBB = true;
            }
          }
        }
        for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
             I != IE; ++I) {
          if ((*I).getOpcode() == Instruction::FDiv &&
              dyn_cast<ConstantFP>(I->getOperand(0))) {
            if (dyn_cast<ConstantFP>(I->getOperand(0))
                    ->isExactlyValue(1.000000e+00) &&
                I->getOperand(1) == expr->RST->Root) {
              NewRST = dyn_cast<Value>(I);
              isRSTinBB = true;
            }
          }
        }
        if (!isRSTinBB) {

          NewRST = Builder.CreateFDiv(Numerator, expr->RST->Root, "reciprocal");
        }
        MathExpression *NewRSTExpr = new MathExpression();
        MathExpression *NumeratorExpr = new MathExpression();
        NumeratorExpr->Root = Numerator;
        NumeratorExpr->isLeaf = true;
        NewRSTExpr->Root = NewRST;
        NewRSTExpr->LST = NumeratorExpr;
        NewRSTExpr->RST = expr->RST;
        for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE;
             ++I) {
          if ((*I).getOpcode() == Instruction::FMul) {
            if (I->getOperand(0) == expr->LST->Root &&
                I->getOperand(1) == NewRST) {
              NewExpr = dyn_cast<Value>(I);
              isExprinBB = true;
            }
          }
        }
        for (BasicBlock::iterator I = ParentBB->begin(), IE = ParentBB->end();
             I != IE; ++I) {
          if ((*I).getOpcode() == Instruction::FMul) {
            if (I->getOperand(0) == expr->LST->Root &&
                I->getOperand(1) == NewRST) {
              NewExpr = dyn_cast<Value>(I);
              isExprinBB = true;
            }
          }
        }
        if (!isExprinBB) {
          NewExpr =
              Builder.CreateFMul(expr->LST->Root, NewRST, "MulReciprocal");
        }
        TransformedExpression->Root = NewExpr;
        TransformedExpression->RST = NewRSTExpr;
        TransformedExpression->LST = expr->LST;
        return std::make_pair(true, TransformedExpression);
      }
    }
  }
  delete TransformedExpression;
  return std::make_pair(false, expr);
}

void ArithTransformRules::PopulateRules(BasicBlock &BB) {
  ParentBB = &BB;
  Rules.push_front(&ArithTransformRules::VarRule);
  //    Rules.push_front(&ArithTransformRules::AddCommute);
  //    Rules.push_front(&ArithTransformRules::AddAssociate);
  Rules.push_front(&ArithTransformRules::AddAssociateRev);
  //    Rules.push_front(&ArithTransformRules::MulCommute);
  // TODO: Enable support for FMul
  Rules.push_front(&ArithTransformRules::MulAssociate);
  //    Rules.push_front(&ArithTransformRules::MulAssociateRev);
  Rules.push_front(&ArithTransformRules::MulDistribute);
  //    Rules.push_front(&ArithTransformRules::MulRevDistribute);
  //    Rules.push_front(&ArithTransformRules::DivDistribute);
  // TODO: Enable support for UDiv, SDiv along with FDivDistribute
  Rules.push_front(&ArithTransformRules::FDivDistribute);
  Rules.push_front(&ArithTransformRules::FactorizeDiv);
  Rules.push_front(&ArithTransformRules::DivMulReciprocal);
  //    Rules.push_front(&ArithTransformRules::DivRevDistribute);
}
} // namespace llvm
#endif