#include "SystemZ.h"
#include "SystemZSubtarget.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/IntrinsicsS390.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Target/TargetMachine.h"
#include <deque>
#include <set>
using namespace llvm;
namespace {
class SystemZTDCPass : public FunctionPass {
public:
static char ID;
SystemZTDCPass() : FunctionPass(ID) {
initializeSystemZTDCPassPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<TargetPassConfig>();
}
private:
MapVector<Instruction *, std::tuple<Value *, int, bool>> ConvertedInsts;
std::vector<BinaryOperator *> LogicOpsWorklist;
std::set<Instruction *> PossibleJunk;
void convertFCmp(CmpInst &I);
void convertICmp(CmpInst &I);
void convertLogicOp(BinaryOperator &I);
void converted(Instruction *I, Value *V, int Mask, bool Worthy) {
ConvertedInsts[I] = std::make_tuple(V, Mask, Worthy);
auto &M = *I->getFunction()->getParent();
auto &Ctx = M.getContext();
for (auto *U : I->users()) {
auto *LI = dyn_cast<BinaryOperator>(U);
if (LI && LI->getType() == Type::getInt1Ty(Ctx) &&
(LI->getOpcode() == Instruction::And ||
LI->getOpcode() == Instruction::Or ||
LI->getOpcode() == Instruction::Xor)) {
LogicOpsWorklist.push_back(LI);
}
}
}
};
}
char SystemZTDCPass::ID = 0;
INITIALIZE_PASS(SystemZTDCPass, "systemz-tdc",
"SystemZ Test Data Class optimization", false, false)
FunctionPass *llvm::createSystemZTDCPass() {
return new SystemZTDCPass();
}
void SystemZTDCPass::convertFCmp(CmpInst &I) {
Value *Op0 = I.getOperand(0);
auto *Const = dyn_cast<ConstantFP>(I.getOperand(1));
auto Pred = I.getPredicate();
if (!Const)
return;
auto &Sem = Op0->getType()->getFltSemantics();
APFloat Smallest = APFloat::getSmallestNormalized(Sem);
APFloat NegSmallest = Smallest;
NegSmallest.changeSign();
int WhichConst;
if (Const->isZero()) {
WhichConst = 0;
} else if (Const->isInfinity()) {
WhichConst = Const->isNegative() ? 2 : 1;
} else if (Const->isExactlyValue(Smallest)) {
if ((Pred & CmpInst::FCMP_OGE) != CmpInst::FCMP_OGE &&
(Pred & CmpInst::FCMP_OGE) != 0)
return;
WhichConst = 3;
} else if (Const->isExactlyValue(NegSmallest)) {
if ((Pred & CmpInst::FCMP_OLE) != CmpInst::FCMP_OLE &&
(Pred & CmpInst::FCMP_OLE) != 0)
return;
WhichConst = 4;
} else {
return;
}
static const int Masks[][4] = {
{ SystemZ::TDCMASK_ZERO, SystemZ::TDCMASK_POSITIVE, SystemZ::TDCMASK_NEGATIVE, SystemZ::TDCMASK_NAN, },
{ SystemZ::TDCMASK_INFINITY_PLUS, 0, (SystemZ::TDCMASK_ZERO |
SystemZ::TDCMASK_NEGATIVE |
SystemZ::TDCMASK_NORMAL_PLUS |
SystemZ::TDCMASK_SUBNORMAL_PLUS), SystemZ::TDCMASK_NAN, },
{ SystemZ::TDCMASK_INFINITY_MINUS, (SystemZ::TDCMASK_ZERO |
SystemZ::TDCMASK_POSITIVE |
SystemZ::TDCMASK_NORMAL_MINUS |
SystemZ::TDCMASK_SUBNORMAL_MINUS), 0, SystemZ::TDCMASK_NAN, },
{ 0, (SystemZ::TDCMASK_NORMAL_PLUS |
SystemZ::TDCMASK_INFINITY_PLUS), (SystemZ::TDCMASK_ZERO |
SystemZ::TDCMASK_NEGATIVE |
SystemZ::TDCMASK_SUBNORMAL_PLUS), SystemZ::TDCMASK_NAN, },
{ 0, (SystemZ::TDCMASK_ZERO |
SystemZ::TDCMASK_POSITIVE |
SystemZ::TDCMASK_SUBNORMAL_MINUS), (SystemZ::TDCMASK_NORMAL_MINUS |
SystemZ::TDCMASK_INFINITY_MINUS), SystemZ::TDCMASK_NAN, }
};
int Mask = 0;
if (Pred & CmpInst::FCMP_OEQ)
Mask |= Masks[WhichConst][0];
if (Pred & CmpInst::FCMP_OGT)
Mask |= Masks[WhichConst][1];
if (Pred & CmpInst::FCMP_OLT)
Mask |= Masks[WhichConst][2];
if (Pred & CmpInst::FCMP_UNO)
Mask |= Masks[WhichConst][3];
bool Worthy = false;
if (CallInst *CI = dyn_cast<CallInst>(Op0)) {
Function *F = CI->getCalledFunction();
if (F && F->getIntrinsicID() == Intrinsic::fabs) {
Mask &= SystemZ::TDCMASK_PLUS;
Mask |= Mask >> 1;
Op0 = CI->getArgOperand(0);
Worthy = WhichConst != 0;
PossibleJunk.insert(CI);
}
}
converted(&I, Op0, Mask, Worthy);
}
void SystemZTDCPass::convertICmp(CmpInst &I) {
Value *Op0 = I.getOperand(0);
auto *Const = dyn_cast<ConstantInt>(I.getOperand(1));
auto Pred = I.getPredicate();
if (!Const)
return;
if (auto *Cast = dyn_cast<BitCastInst>(Op0)) {
if (!Cast->getSrcTy()->isFloatTy() &&
!Cast->getSrcTy()->isDoubleTy() &&
!Cast->getSrcTy()->isFP128Ty())
return;
Value *V = Cast->getOperand(0);
int Mask;
if (Pred == CmpInst::ICMP_SLT && Const->isZero()) {
Mask = SystemZ::TDCMASK_MINUS;
} else if (Pred == CmpInst::ICMP_SGT && Const->isMinusOne()) {
Mask = SystemZ::TDCMASK_PLUS;
} else {
return;
}
PossibleJunk.insert(Cast);
converted(&I, V, Mask, true);
} else if (auto *CI = dyn_cast<CallInst>(Op0)) {
Function *F = CI->getCalledFunction();
if (!F || F->getIntrinsicID() != Intrinsic::s390_tdc)
return;
if (!Const->isZero())
return;
Value *V = CI->getArgOperand(0);
auto *MaskC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
if (!MaskC)
return;
int Mask = MaskC->getZExtValue();
Mask &= SystemZ::TDCMASK_ALL;
if (Pred == CmpInst::ICMP_NE) {
} else if (Pred == CmpInst::ICMP_EQ) {
Mask ^= SystemZ::TDCMASK_ALL;
} else {
return;
}
PossibleJunk.insert(CI);
converted(&I, V, Mask, false);
}
}
void SystemZTDCPass::convertLogicOp(BinaryOperator &I) {
Value *Op0, *Op1;
int Mask0, Mask1;
bool Worthy0, Worthy1;
std::tie(Op0, Mask0, Worthy0) = ConvertedInsts[cast<Instruction>(I.getOperand(0))];
std::tie(Op1, Mask1, Worthy1) = ConvertedInsts[cast<Instruction>(I.getOperand(1))];
if (Op0 != Op1)
return;
int Mask;
switch (I.getOpcode()) {
case Instruction::And:
Mask = Mask0 & Mask1;
break;
case Instruction::Or:
Mask = Mask0 | Mask1;
break;
case Instruction::Xor:
Mask = Mask0 ^ Mask1;
break;
default:
llvm_unreachable("Unknown op in convertLogicOp");
}
converted(&I, Op0, Mask, true);
}
bool SystemZTDCPass::runOnFunction(Function &F) {
auto &TPC = getAnalysis<TargetPassConfig>();
if (TPC.getTM<TargetMachine>()
.getSubtarget<SystemZSubtarget>(F)
.hasSoftFloat())
return false;
ConvertedInsts.clear();
LogicOpsWorklist.clear();
PossibleJunk.clear();
for (auto &I : instructions(F)) {
if (I.getOpcode() == Instruction::FCmp)
convertFCmp(cast<CmpInst>(I));
else if (I.getOpcode() == Instruction::ICmp)
convertICmp(cast<CmpInst>(I));
}
if (ConvertedInsts.empty())
return false;
while (!LogicOpsWorklist.empty()) {
BinaryOperator *Op = LogicOpsWorklist.back();
LogicOpsWorklist.pop_back();
if (ConvertedInsts.count(dyn_cast<Instruction>(Op->getOperand(0))) &&
ConvertedInsts.count(dyn_cast<Instruction>(Op->getOperand(1))) &&
!ConvertedInsts.count(Op))
convertLogicOp(*Op);
}
Module &M = *F.getParent();
auto &Ctx = M.getContext();
Value *Zero32 = ConstantInt::get(Type::getInt32Ty(Ctx), 0);
bool MadeChange = false;
for (auto &It : reverse(ConvertedInsts)) {
Instruction *I = It.first;
Value *V;
int Mask;
bool Worthy;
std::tie(V, Mask, Worthy) = It.second;
if (!I->user_empty()) {
if (!Worthy)
continue;
Function *TDCFunc =
Intrinsic::getDeclaration(&M, Intrinsic::s390_tdc, V->getType());
IRBuilder<> IRB(I);
Value *MaskVal = ConstantInt::get(Type::getInt64Ty(Ctx), Mask);
Instruction *TDC = IRB.CreateCall(TDCFunc, {V, MaskVal});
Value *ICmp = IRB.CreateICmp(CmpInst::ICMP_NE, TDC, Zero32);
I->replaceAllUsesWith(ICmp);
}
I->eraseFromParent();
MadeChange = true;
}
if (!MadeChange)
return false;
for (auto *I : PossibleJunk)
if (I->user_empty())
I->eraseFromParent();
return true;
}