5515 lines
199 KiB
C++
5515 lines
199 KiB
C++
//===-- CBackend.cpp - Library for converting LLVM code to C --------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This library converts LLVM code to C code, compilable by GCC and other C
|
|
// compilers.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ispc.h"
|
|
#include "module.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sstream>
|
|
|
|
#ifndef _MSC_VER
|
|
#include <inttypes.h>
|
|
#define HAVE_PRINTF_A 1
|
|
#define ENABLE_CBE_PRINTF_A 1
|
|
#endif
|
|
|
|
#ifndef PRIx64
|
|
#define PRIx64 "llx"
|
|
#endif
|
|
|
|
#include "llvmutil.h"
|
|
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
#include "llvm/Constants.h"
|
|
#include "llvm/DerivedTypes.h"
|
|
#include "llvm/CallingConv.h"
|
|
#include "llvm/Module.h"
|
|
#include "llvm/Instructions.h"
|
|
#include "llvm/Intrinsics.h"
|
|
#include "llvm/IntrinsicInst.h"
|
|
#include "llvm/InlineAsm.h"
|
|
#else // LLVM 3.3+
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/CallingConv.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/InlineAsm.h"
|
|
#endif
|
|
#include "llvm/Pass.h"
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6 // <= 3.6
|
|
#include "llvm/PassManager.h"
|
|
#else // LLVM 3.7+
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#endif
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
#include "llvm/TypeFinder.h"
|
|
#else // LLVM_3_3+
|
|
#include "llvm/IR/TypeFinder.h"
|
|
#endif
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_4 // 3.2, 3.3, 3.4
|
|
#include "llvm/Support/InstIterator.h"
|
|
#else // 3.5+
|
|
#include "llvm/IR/InstIterator.h"
|
|
#endif
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
|
|
#include "llvm/Analysis/FindUsedTypes.h"
|
|
#endif
|
|
#include "llvm/Analysis/LoopInfo.h"
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5
|
|
#include "llvm/IR/Verifier.h"
|
|
#include <llvm/IR/IRPrintingPasses.h>
|
|
#include "llvm/IR/CallSite.h"
|
|
#include "llvm/IR/CFG.h"
|
|
#include "llvm/IR/GetElementPtrTypeIterator.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#else
|
|
#include "llvm/Analysis/Verifier.h"
|
|
#include <llvm/Assembly/PrintModulePass.h>
|
|
#include "llvm/Support/CallSite.h"
|
|
#include "llvm/Support/CFG.h"
|
|
#include "llvm/Support/GetElementPtrTypeIterator.h"
|
|
#endif
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/CodeGen/Passes.h"
|
|
#include "llvm/CodeGen/IntrinsicLowering.h"
|
|
//#include "llvm/Target/Mangler.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2 // 3.2
|
|
#include "llvm/DataLayout.h"
|
|
#else // LLVM 3.3+
|
|
#include "llvm/IR/DataLayout.h"
|
|
#endif
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2 // 3.2
|
|
#include "llvm/Support/InstVisitor.h"
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_3_4 // 3.3, 3.4
|
|
#include "llvm/InstVisitor.h"
|
|
#else // LLVM 3.5+
|
|
#include "llvm/IR/InstVisitor.h"
|
|
#endif
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_4 // 3.2, 3.3, 3.4
|
|
#include "llvm/Config/config.h"
|
|
#endif
|
|
|
|
#include <llvm/Transforms/IPO.h>
|
|
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
|
|
#include <llvm/Support/ToolOutputFile.h>
|
|
#include <algorithm>
|
|
// Some ms header decided to define setjmp as _setjmp, undo this for this file.
|
|
#ifdef _MSC_VER
|
|
#undef setjmp
|
|
#define snprintf _snprintf
|
|
#endif
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// This part of code was in LLVM's ConstantsScanner.h,
|
|
// but it was removed in revision #232397
|
|
|
|
namespace constant_scanner {
|
|
class constant_iterator : public std::iterator<std::forward_iterator_tag,
|
|
const llvm::Constant, ptrdiff_t> {
|
|
llvm::const_inst_iterator InstI; // Method instruction iterator
|
|
unsigned OpIdx; // Operand index
|
|
|
|
bool isAtConstant() const {
|
|
assert(!InstI.atEnd() && OpIdx < InstI->getNumOperands() &&
|
|
"isAtConstant called with invalid arguments!");
|
|
return llvm::isa<llvm::Constant>(InstI->getOperand(OpIdx));
|
|
}
|
|
|
|
public:
|
|
constant_iterator(const llvm::Function *F) : InstI(llvm::inst_begin(F)), OpIdx(0) {
|
|
// Advance to first constant... if we are not already at constant or end
|
|
if (InstI != llvm::inst_end(F) && // InstI is valid?
|
|
(InstI->getNumOperands() == 0 || !isAtConstant())) // Not at constant?
|
|
operator++();
|
|
}
|
|
|
|
constant_iterator(const llvm::Function *F, bool) // end ctor
|
|
: InstI(llvm::inst_end(F)),
|
|
OpIdx(0) {}
|
|
|
|
bool operator==(const constant_iterator &x) const {
|
|
return OpIdx == x.OpIdx && InstI == x.InstI;
|
|
}
|
|
bool operator!=(const constant_iterator &x) const { return !(*this == x); }
|
|
|
|
pointer operator*() const {
|
|
assert(isAtConstant() && "Dereferenced an iterator at the end!");
|
|
return llvm::cast<llvm::Constant>(InstI->getOperand(OpIdx));
|
|
}
|
|
|
|
constant_iterator &operator++() { // Preincrement implementation
|
|
++OpIdx;
|
|
do {
|
|
unsigned NumOperands = InstI->getNumOperands();
|
|
while (OpIdx < NumOperands && !isAtConstant()) {
|
|
++OpIdx;
|
|
}
|
|
|
|
if (OpIdx < NumOperands) return *this; // Found a constant!
|
|
++InstI;
|
|
OpIdx = 0;
|
|
} while (!InstI.atEnd());
|
|
|
|
return *this; // At the end of the method
|
|
}
|
|
};
|
|
|
|
inline constant_iterator constant_begin(const llvm::Function *F) {
|
|
return constant_iterator(F);
|
|
}
|
|
|
|
inline constant_iterator constant_end(const llvm::Function *F) {
|
|
return constant_iterator(F, true);
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FIXME:
|
|
namespace {
|
|
/// TypeFinder - Walk over a module, identifying all of the types that are
|
|
/// used by the module.
|
|
class TypeFinder {
|
|
// To avoid walking constant expressions multiple times and other IR
|
|
// objects, we keep several helper maps.
|
|
llvm::DenseSet<const llvm::Value*> VisitedConstants;
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_6 // LLVM 3.6+
|
|
llvm::DenseSet<const llvm::Metadata*> VisitedMDNodes;
|
|
#endif
|
|
llvm::DenseSet<llvm::Type*> VisitedTypes;
|
|
std::vector<llvm::ArrayType*> &ArrayTypes;
|
|
std::vector<llvm::IntegerType*> &IntegerTypes;
|
|
std::vector<bool> &IsVolatile;
|
|
std::vector<int> &Alignment;
|
|
public:
|
|
TypeFinder(std::vector<llvm::ArrayType*> &t, std::vector<llvm::IntegerType*> &i,
|
|
std::vector<bool> &v, std::vector<int> &a)
|
|
: ArrayTypes(t), IntegerTypes(i) , IsVolatile(v), Alignment(a){}
|
|
|
|
void run(const llvm::Module &M) {
|
|
// Get types from global variables.
|
|
for (llvm::Module::const_global_iterator I = M.global_begin(),
|
|
E = M.global_end(); I != E; ++I) {
|
|
incorporateType(I->getType());
|
|
if (I->hasInitializer())
|
|
incorporateValue(I->getInitializer());
|
|
}
|
|
|
|
// Get types from aliases.
|
|
for (llvm::Module::const_alias_iterator I = M.alias_begin(),
|
|
E = M.alias_end(); I != E; ++I) {
|
|
incorporateType(I->getType());
|
|
if (const llvm::Value *Aliasee = I->getAliasee())
|
|
incorporateValue(Aliasee);
|
|
}
|
|
|
|
llvm::SmallVector<std::pair<unsigned, llvm::MDNode*>, 4> MDForInst;
|
|
|
|
// Get types from functions.
|
|
for (llvm::Module::const_iterator FI = M.begin(), E = M.end(); FI != E; ++FI) {
|
|
incorporateType(FI->getType());
|
|
|
|
for (llvm::Function::const_iterator BB = FI->begin(), E = FI->end();
|
|
BB != E;++BB)
|
|
for (llvm::BasicBlock::const_iterator II = BB->begin(),
|
|
E = BB->end(); II != E; ++II) {
|
|
const llvm::Instruction &I = *II;
|
|
|
|
// Operands of SwitchInsts changed format after 3.1
|
|
// Seems like there ought to be better way to do what we
|
|
// want here. For now, punt on SwitchInsts.
|
|
if (llvm::isa<llvm::SwitchInst>(&I)) continue;
|
|
|
|
// Incorporate the type of the instruction and all its operands.
|
|
incorporateType(I.getType());
|
|
if (llvm::isa<llvm::StoreInst>(&I))
|
|
if (llvm::IntegerType *ITy = llvm::dyn_cast<llvm::IntegerType>(I.getType())) {
|
|
IntegerTypes.push_back(ITy);
|
|
const llvm::StoreInst *St = llvm::dyn_cast<llvm::StoreInst>(&I);
|
|
IsVolatile.push_back(St->isVolatile());
|
|
Alignment.push_back(St->getAlignment());
|
|
}
|
|
|
|
if (llvm::isa<llvm::LoadInst>(&I))
|
|
if (llvm::IntegerType *ITy = llvm::dyn_cast<llvm::IntegerType>(I.getType())) {
|
|
IntegerTypes.push_back(ITy);
|
|
const llvm::LoadInst *St = llvm::dyn_cast<llvm::LoadInst>(&I);
|
|
IsVolatile.push_back(St->isVolatile());
|
|
Alignment.push_back(St->getAlignment());
|
|
}
|
|
|
|
for (llvm::User::const_op_iterator OI = I.op_begin(), OE = I.op_end();
|
|
OI != OE; ++OI)
|
|
incorporateValue(*OI);
|
|
|
|
// Incorporate types hiding in metadata.
|
|
I.getAllMetadataOtherThanDebugLoc(MDForInst);
|
|
for (unsigned i = 0, e = MDForInst.size(); i != e; ++i)
|
|
incorporateMDNode(MDForInst[i].second);
|
|
|
|
MDForInst.clear();
|
|
}
|
|
}
|
|
|
|
for (llvm::Module::const_named_metadata_iterator I = M.named_metadata_begin(),
|
|
E = M.named_metadata_end(); I != E; ++I) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
const llvm::NamedMDNode *NMD = I;
|
|
#else /* LLVM 3.8+ */
|
|
const llvm::NamedMDNode *NMD = &*I;
|
|
#endif
|
|
for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i)
|
|
incorporateMDNode(NMD->getOperand(i));
|
|
}
|
|
}
|
|
|
|
private:
|
|
void incorporateType(llvm::Type *Ty) {
|
|
// Check to see if we're already visited this type.
|
|
if (!VisitedTypes.insert(Ty).second)
|
|
return;
|
|
|
|
if (llvm::ArrayType *ATy = llvm::dyn_cast<llvm::ArrayType>(Ty))
|
|
ArrayTypes.push_back(ATy);
|
|
|
|
// Recursively walk all contained types.
|
|
for (llvm::Type::subtype_iterator I = Ty->subtype_begin(),
|
|
E = Ty->subtype_end(); I != E; ++I)
|
|
incorporateType(*I);
|
|
}
|
|
|
|
/// incorporateValue - This method is used to walk operand lists finding
|
|
/// types hiding in constant expressions and other operands that won't be
|
|
/// walked in other ways. GlobalValues, basic blocks, instructions, and
|
|
/// inst operands are all explicitly enumerated.
|
|
void incorporateValue(const llvm::Value *V) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5 // 3.2, 3.3, 3.4, 3.5
|
|
if (const llvm::MDNode *M = llvm::dyn_cast<llvm::MDNode>(V)) {
|
|
incorporateMDNode(M);
|
|
return;
|
|
}
|
|
#else /* LLVN 3.6+ */
|
|
if (const llvm::MetadataAsValue *MV = llvm::dyn_cast<llvm::MetadataAsValue>(V)) {
|
|
incorporateMDNode(MV->getMetadata());
|
|
return;
|
|
}
|
|
#endif
|
|
if (!llvm::isa<llvm::Constant>(V) || llvm::isa<llvm::GlobalValue>(V)) return;
|
|
|
|
// Already visited?
|
|
if (!VisitedConstants.insert(V).second)
|
|
return;
|
|
|
|
// Check this type.
|
|
incorporateType(V->getType());
|
|
|
|
// Look in operands for types.
|
|
const llvm::User *U = llvm::cast<llvm::User>(V);
|
|
for (llvm::Constant::const_op_iterator I = U->op_begin(),
|
|
E = U->op_end(); I != E;++I)
|
|
incorporateValue(*I);
|
|
}
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5 // 3.2, 3.3, 3.4, 3.5
|
|
void incorporateMDNode(const llvm::MDNode *V) {
|
|
|
|
// Already visited?
|
|
if (!VisitedConstants.insert(V).second)
|
|
return;
|
|
|
|
// Look in operands for types.
|
|
for (unsigned i = 0, e = V->getNumOperands(); i != e; ++i)
|
|
if (llvm::Value *Op = V->getOperand(i))
|
|
incorporateValue(Op);
|
|
}
|
|
#else // LLVM 3.6+
|
|
void incorporateMDNode(const llvm::Metadata *M) {
|
|
|
|
// Already visited?
|
|
if (!VisitedMDNodes.insert(M).second)
|
|
return;
|
|
|
|
if (const llvm::MDNode* N = llvm::dyn_cast<llvm::MDNode>(M)) {
|
|
// Look in operands for types.
|
|
for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i)
|
|
if (const llvm::Metadata *O = N->getOperand(i))
|
|
incorporateMDNode(O);
|
|
} else if (llvm::isa<llvm::MDString>(M)) {
|
|
// Nothing to do with MDString.
|
|
} else if (const llvm::ValueAsMetadata* V = llvm::dyn_cast<llvm::ValueAsMetadata>(M)) {
|
|
incorporateValue(V->getValue());
|
|
} else {
|
|
// Some unknown Metadata subclass - has LLVM introduced something new?
|
|
llvm_unreachable("Unknown Metadata subclass");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static void findUsedArrayAndLongIntTypes(const llvm::Module *m, std::vector<llvm::ArrayType*> &t,
|
|
std::vector<llvm::IntegerType*> &i, std::vector<bool> &IsVolatile,
|
|
std::vector<int> &Alignment) {
|
|
TypeFinder(t, i, IsVolatile, Alignment).run(*m);
|
|
}
|
|
|
|
|
|
static bool is_vec16_i64_ty(llvm::Type *Ty) {
|
|
llvm::VectorType *VTy = llvm::dyn_cast<llvm::VectorType>(Ty);
|
|
if ((VTy != NULL) && (VTy->getElementType()->isIntegerTy()) &&
|
|
VTy->getElementType()->getPrimitiveSizeInBits() == 64)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
class CBEMCAsmInfo : public llvm::MCAsmInfo {
|
|
public:
|
|
CBEMCAsmInfo() {
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_4 // 3.2, 3.3, 3.4
|
|
GlobalPrefix = "";
|
|
#endif
|
|
PrivateGlobalPrefix = "";
|
|
}
|
|
};
|
|
|
|
/// CWriter - This class is the main chunk of code that converts an LLVM
|
|
/// module to a C translation unit.
|
|
class CWriter : public llvm::FunctionPass, public llvm::InstVisitor<CWriter> {
|
|
llvm::formatted_raw_ostream &Out;
|
|
llvm::IntrinsicLowering *IL;
|
|
//llvm::Mangler *Mang;
|
|
llvm::LoopInfo *LI;
|
|
const llvm::Module *TheModule;
|
|
const llvm::MCAsmInfo* TAsm;
|
|
const llvm::MCRegisterInfo *MRI;
|
|
const llvm::MCObjectFileInfo *MOFI;
|
|
llvm::MCContext *TCtx;
|
|
|
|
// FIXME: it's ugly to have the name be "TD" here, but it saves us
|
|
// lots of ifdefs in the below since the new DataLayout and the old
|
|
// TargetData have generally similar interfaces...
|
|
const llvm::DataLayout* TD;
|
|
|
|
std::map<const llvm::ConstantFP *, unsigned> FPConstantMap;
|
|
std::map<const llvm::ConstantDataVector *, unsigned> VectorConstantMap;
|
|
unsigned VectorConstantIndex;
|
|
std::set<llvm::Function*> intrinsicPrototypesAlreadyGenerated;
|
|
std::set<const llvm::Argument*> ByValParams;
|
|
unsigned FPCounter;
|
|
unsigned OpaqueCounter;
|
|
llvm::DenseMap<const llvm::Value*, unsigned> AnonValueNumbers;
|
|
unsigned NextAnonValueNumber;
|
|
|
|
std::string includeName;
|
|
int vectorWidth;
|
|
|
|
/// UnnamedStructIDs - This contains a unique ID for each struct that is
|
|
/// either anonymous or has no name.
|
|
llvm::DenseMap<llvm::StructType*, unsigned> UnnamedStructIDs;
|
|
llvm::DenseMap<llvm::ArrayType *, unsigned> ArrayIDs;
|
|
|
|
public:
|
|
static char ID;
|
|
explicit CWriter(llvm::formatted_raw_ostream &o, const char *incname,
|
|
int vecwidth)
|
|
: FunctionPass(ID), Out(o), IL(0), /* Mang(0), */ LI(0),
|
|
TheModule(0), TAsm(0), MRI(0), MOFI(0), TCtx(0), TD(0),
|
|
OpaqueCounter(0), NextAnonValueNumber(0),
|
|
includeName(incname ? incname : "generic_defs.h"),
|
|
vectorWidth(vecwidth) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6 // <= 3.6
|
|
initializeLoopInfoPass(*llvm::PassRegistry::getPassRegistry());
|
|
#else // LLVM 3.7+
|
|
initializeLoopInfoWrapperPassPass(*llvm::PassRegistry::getPassRegistry());
|
|
#endif
|
|
FPCounter = 0;
|
|
VectorConstantIndex = 0;
|
|
}
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
virtual const char *getPassName() const { return "C backend"; }
|
|
#else // LLVM 4.0+
|
|
virtual llvm::StringRef getPassName() const { return "C backend"; }
|
|
#endif
|
|
|
|
void getAnalysisUsage(llvm::AnalysisUsage &AU) const {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6 // <= 3.6
|
|
AU.addRequired<llvm::LoopInfo>();
|
|
#else // LLVM 3.7+
|
|
AU.addRequired<llvm::LoopInfoWrapperPass>();
|
|
#endif
|
|
AU.setPreservesAll();
|
|
}
|
|
|
|
virtual bool doInitialization(llvm::Module &M);
|
|
|
|
bool runOnFunction(llvm::Function &F) {
|
|
// Do not codegen any 'available_externally' functions at all, they have
|
|
// definitions outside the translation unit.
|
|
if (F.hasAvailableExternallyLinkage())
|
|
return false;
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6 // <= 3.6
|
|
LI = &getAnalysis<llvm::LoopInfo>();
|
|
#else // LLVM 3.7+
|
|
LI = &getAnalysis<llvm::LoopInfoWrapperPass>().getLoopInfo();
|
|
#endif
|
|
|
|
// Get rid of intrinsics we can't handle.
|
|
lowerIntrinsics(F);
|
|
|
|
// Output all floating point constants that cannot be printed accurately.
|
|
printFloatingPointConstants(F);
|
|
|
|
// Output all vector constants so they can be accessed with single
|
|
// vector loads
|
|
printVectorConstants(F);
|
|
|
|
printFunction(F);
|
|
return false;
|
|
}
|
|
|
|
virtual bool doFinalization(llvm::Module &M) {
|
|
// Free memory...
|
|
delete IL;
|
|
delete TD;
|
|
//delete Mang;
|
|
delete TCtx;
|
|
delete TAsm;
|
|
delete MRI;
|
|
delete MOFI;
|
|
FPConstantMap.clear();
|
|
VectorConstantMap.clear();
|
|
ByValParams.clear();
|
|
intrinsicPrototypesAlreadyGenerated.clear();
|
|
UnnamedStructIDs.clear();
|
|
ArrayIDs.clear();
|
|
return false;
|
|
}
|
|
|
|
llvm::raw_ostream &printType(llvm::raw_ostream &Out, llvm::Type *Ty,
|
|
bool isSigned = false,
|
|
const std::string &VariableName = "",
|
|
bool IgnoreName = false,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL = llvm::AttrListPtr()
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL = llvm::AttributeSet()
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL = llvm::AttributeList()
|
|
#endif
|
|
);
|
|
llvm::raw_ostream &printSimpleType(llvm::raw_ostream &Out, llvm::Type *Ty,
|
|
bool isSigned,
|
|
const std::string &NameSoFar = "");
|
|
|
|
void printStructReturnPointerFunctionType(llvm::raw_ostream &Out,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL,
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL,
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL,
|
|
#endif
|
|
llvm::PointerType *Ty);
|
|
|
|
std::string getStructName(llvm::StructType *ST);
|
|
std::string getArrayName(llvm::ArrayType *AT);
|
|
|
|
/// writeOperandDeref - Print the result of dereferencing the specified
|
|
/// operand with '*'. This is equivalent to printing '*' then using
|
|
/// writeOperand, but avoids excess syntax in some cases.
|
|
void writeOperandDeref(llvm::Value *Operand) {
|
|
if (isAddressExposed(Operand)) {
|
|
// Already something with an address exposed.
|
|
writeOperandInternal(Operand);
|
|
} else {
|
|
Out << "*(";
|
|
writeOperand(Operand);
|
|
Out << ")";
|
|
}
|
|
}
|
|
|
|
void writeOperand(llvm::Value *Operand, bool Static = false);
|
|
void writeInstComputationInline(llvm::Instruction &I);
|
|
void writeOperandInternal(llvm::Value *Operand, bool Static = false);
|
|
void writeOperandWithCast(llvm::Value* Operand, unsigned Opcode);
|
|
void writeOperandWithCast(llvm::Value* Operand, const llvm::ICmpInst &I);
|
|
bool writeInstructionCast(const llvm::Instruction &I);
|
|
|
|
void writeMemoryAccess(llvm::Value *Operand, llvm::Type *OperandType,
|
|
bool IsVolatile, unsigned Alignment);
|
|
|
|
private :
|
|
void lowerIntrinsics(llvm::Function &F);
|
|
/// Prints the definition of the intrinsic function F. Supports the
|
|
/// intrinsics which need to be explicitly defined in the CBackend.
|
|
void printIntrinsicDefinition(const llvm::Function &F, llvm::raw_ostream &Out);
|
|
|
|
void printModuleTypes();
|
|
void printContainedStructs(llvm::Type *Ty, llvm::SmallPtrSet<llvm::Type *, 16> &);
|
|
void printContainedArrays(llvm::ArrayType *ATy, llvm::SmallPtrSet<llvm::Type *, 16> &);
|
|
void printFloatingPointConstants(llvm::Function &F);
|
|
void printFloatingPointConstants(const llvm::Constant *C);
|
|
void printVectorConstants(llvm::Function &F);
|
|
void printFunctionSignature(const llvm::Function *F, bool Prototype);
|
|
|
|
void printFunction(llvm::Function &);
|
|
void printBasicBlock(llvm::BasicBlock *BB);
|
|
void printLoop(llvm::Loop *L);
|
|
|
|
bool printCast(unsigned opcode, llvm::Type *SrcTy, llvm::Type *DstTy);
|
|
void printConstant(llvm::Constant *CPV, bool Static);
|
|
void printConstantWithCast(llvm::Constant *CPV, unsigned Opcode);
|
|
bool printConstExprCast(const llvm::ConstantExpr *CE, bool Static);
|
|
void printConstantArray(llvm::ConstantArray *CPA, bool Static);
|
|
void printConstantVector(llvm::ConstantVector *CV, bool Static);
|
|
void printConstantDataSequential(llvm::ConstantDataSequential *CDS, bool Static);
|
|
|
|
/// isAddressExposed - Return true if the specified value's name needs to
|
|
/// have its address taken in order to get a C value of the correct type.
|
|
/// This happens for global variables, byval parameters, and direct allocas.
|
|
bool isAddressExposed(const llvm::Value *V) const {
|
|
if (const llvm::Argument *A = llvm::dyn_cast<llvm::Argument>(V))
|
|
return ByValParams.count(A);
|
|
return llvm::isa<llvm::GlobalVariable>(V) || isDirectAlloca(V);
|
|
}
|
|
|
|
// isInlinableInst - Attempt to inline instructions into their uses to build
|
|
// trees as much as possible. To do this, we have to consistently decide
|
|
// what is acceptable to inline, so that variable declarations don't get
|
|
// printed and an extra copy of the expr is not emitted.
|
|
//
|
|
static bool isInlinableInst(const llvm::Instruction &I) {
|
|
// Always inline cmp instructions, even if they are shared by multiple
|
|
// expressions. GCC generates horrible code if we don't.
|
|
if (llvm::isa<llvm::CmpInst>(I) && llvm::isa<llvm::VectorType>(I.getType()) == false)
|
|
return true;
|
|
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // 3.5+
|
|
// This instruction returns a struct on LLVM older than 3.4, and can not be inlined
|
|
if (llvm::isa<llvm::AtomicCmpXchgInst>(I))
|
|
return false;
|
|
#endif
|
|
|
|
// Must be an expression, must be used exactly once. If it is dead, we
|
|
// emit it inline where it would go.
|
|
if (I.getType() == llvm::Type::getVoidTy(I.getContext()) || !I.hasOneUse() ||
|
|
llvm::isa<llvm::TerminatorInst>(I) || llvm::isa<llvm::CallInst>(I) || llvm::isa<llvm::PHINode>(I) ||
|
|
llvm::isa<llvm::LoadInst>(I) || llvm::isa<llvm::VAArgInst>(I) || llvm::isa<llvm::InsertElementInst>(I) ||
|
|
llvm::isa<llvm::InsertValueInst>(I) || llvm::isa<llvm::ExtractValueInst>(I) || llvm::isa<llvm::SelectInst>(I))
|
|
// Don't inline a load across a store or other bad things!
|
|
return false;
|
|
|
|
// Must not be used in inline asm, extractelement, or shufflevector.
|
|
if (I.hasOneUse()) {
|
|
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // 3.5+
|
|
const llvm::Instruction &User = llvm::cast<llvm::Instruction>(*I.user_back());
|
|
#else
|
|
const llvm::Instruction &User = llvm::cast<llvm::Instruction>(*I.use_back());
|
|
#endif
|
|
if (isInlineAsm(User) || llvm::isa<llvm::ExtractElementInst>(User) ||
|
|
llvm::isa<llvm::ShuffleVectorInst>(User) || llvm::isa<llvm::AtomicRMWInst>(User) ||
|
|
llvm::isa<llvm::AtomicCmpXchgInst>(User))
|
|
return false;
|
|
}
|
|
|
|
// Only inline instruction it if it's use is in the same BB as the inst.
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // 3.5+
|
|
return I.getParent() == llvm::cast<llvm::Instruction>(I.user_back())->getParent();
|
|
#else
|
|
return I.getParent() == llvm::cast<llvm::Instruction>(I.use_back())->getParent();
|
|
#endif
|
|
}
|
|
|
|
// isDirectAlloca - Define fixed sized allocas in the entry block as direct
|
|
// variables which are accessed with the & operator. This causes GCC to
|
|
// generate significantly better code than to emit alloca calls directly.
|
|
//
|
|
static const llvm::AllocaInst *isDirectAlloca(const llvm::Value *V) {
|
|
const llvm::AllocaInst *AI = llvm::dyn_cast<llvm::AllocaInst>(V);
|
|
if (!AI) return 0;
|
|
if (AI->isArrayAllocation())
|
|
return 0; // FIXME: we can also inline fixed size array allocas!
|
|
if (AI->getParent() != &AI->getParent()->getParent()->getEntryBlock())
|
|
return 0;
|
|
return AI;
|
|
}
|
|
|
|
// isInlineAsm - Check if the instruction is a call to an inline asm chunk.
|
|
static bool isInlineAsm(const llvm::Instruction& I) {
|
|
if (const llvm::CallInst *CI = llvm::dyn_cast<llvm::CallInst>(&I))
|
|
return llvm::isa<llvm::InlineAsm>(CI->getCalledValue());
|
|
return false;
|
|
}
|
|
|
|
// Instruction visitation functions
|
|
friend class llvm::InstVisitor<CWriter>;
|
|
|
|
void visitReturnInst(llvm::ReturnInst &I);
|
|
void visitBranchInst(llvm::BranchInst &I);
|
|
void visitSwitchInst(llvm::SwitchInst &I);
|
|
void visitIndirectBrInst(llvm::IndirectBrInst &I);
|
|
void visitInvokeInst(llvm::InvokeInst &I) {
|
|
llvm_unreachable("Lowerinvoke pass didn't work!");
|
|
}
|
|
void visitResumeInst(llvm::ResumeInst &I) {
|
|
llvm_unreachable("DwarfEHPrepare pass didn't work!");
|
|
}
|
|
void visitUnreachableInst(llvm::UnreachableInst &I);
|
|
|
|
void visitPHINode(llvm::PHINode &I);
|
|
void visitBinaryOperator(llvm::Instruction &I);
|
|
void visitICmpInst(llvm::ICmpInst &I);
|
|
void visitFCmpInst(llvm::FCmpInst &I);
|
|
|
|
void visitCastInst (llvm::CastInst &I);
|
|
void visitSelectInst(llvm::SelectInst &I);
|
|
void visitCallInst (llvm::CallInst &I);
|
|
void visitInlineAsm(llvm::CallInst &I);
|
|
bool visitBuiltinCall(llvm::CallInst &I, llvm::Intrinsic::ID ID, bool &WroteCallee);
|
|
|
|
void visitAllocaInst(llvm::AllocaInst &I);
|
|
void visitLoadInst (llvm::LoadInst &I);
|
|
void visitStoreInst (llvm::StoreInst &I);
|
|
void visitGetElementPtrInst(llvm::GetElementPtrInst &I);
|
|
void visitVAArgInst (llvm::VAArgInst &I);
|
|
|
|
void visitInsertElementInst(llvm::InsertElementInst &I);
|
|
void visitExtractElementInst(llvm::ExtractElementInst &I);
|
|
void visitShuffleVectorInst(llvm::ShuffleVectorInst &SVI);
|
|
|
|
void visitInsertValueInst(llvm::InsertValueInst &I);
|
|
void visitExtractValueInst(llvm::ExtractValueInst &I);
|
|
|
|
void visitAtomicRMWInst(llvm::AtomicRMWInst &I);
|
|
void visitAtomicCmpXchgInst(llvm::AtomicCmpXchgInst &I);
|
|
|
|
void visitInstruction(llvm::Instruction &I) {
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "C Writer does not know about " << I;
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
|
|
void outputLValue(llvm::Instruction *I) {
|
|
Out << " " << GetValueName(I) << " = ";
|
|
}
|
|
|
|
bool isGotoCodeNecessary(llvm::BasicBlock *From, llvm::BasicBlock *To);
|
|
void printPHICopiesForSuccessor(llvm::BasicBlock *CurBlock,
|
|
llvm::BasicBlock *Successor, unsigned Indent);
|
|
void printBranchToBlock(llvm::BasicBlock *CurBlock, llvm::BasicBlock *SuccBlock,
|
|
unsigned Indent);
|
|
void printGEPExpression(llvm::Value *Ptr, llvm::gep_type_iterator I,
|
|
llvm::gep_type_iterator E, bool Static);
|
|
|
|
std::string GetValueName(const llvm::Value *Operand);
|
|
};
|
|
}
|
|
|
|
char CWriter::ID = 0;
|
|
|
|
|
|
|
|
static std::string CBEMangle(const std::string &S) {
|
|
std::string Result;
|
|
|
|
for (unsigned i = 0, e = S.size(); i != e; ++i) {
|
|
if (i+1 != e && ((S[i] == '>' && S[i+1] == '>') ||
|
|
(S[i] == '<' && S[i+1] == '<'))) {
|
|
Result += '_';
|
|
Result += 'A'+(S[i]&15);
|
|
Result += 'A'+((S[i]>>4)&15);
|
|
Result += '_';
|
|
i++;
|
|
} else if (isalnum(S[i]) || S[i] == '_' || S[i] == '<' || S[i] == '>') {
|
|
Result += S[i];
|
|
} else {
|
|
Result += '_';
|
|
Result += 'A'+(S[i]&15);
|
|
Result += 'A'+((S[i]>>4)&15);
|
|
Result += '_';
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
std::string CWriter::getStructName(llvm::StructType *ST) {
|
|
if (!ST->isLiteral() && !ST->getName().empty())
|
|
return CBEMangle("l_"+ST->getName().str());
|
|
|
|
return "l_unnamed_" + llvm::utostr(UnnamedStructIDs[ST]);
|
|
}
|
|
|
|
std::string CWriter::getArrayName(llvm::ArrayType *AT) {
|
|
return "l_array_" + llvm::utostr(ArrayIDs[AT]);
|
|
}
|
|
|
|
|
|
/// printStructReturnPointerFunctionType - This is like printType for a struct
|
|
/// return type, except, instead of printing the type as void (*)(Struct*, ...)
|
|
/// print it as "Struct (*)(...)", for struct return functions.
|
|
void CWriter::printStructReturnPointerFunctionType(llvm::raw_ostream &Out,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL,
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL,
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL,
|
|
#endif
|
|
llvm::PointerType *TheTy) {
|
|
llvm::FunctionType *FTy = llvm::cast<llvm::FunctionType>(TheTy->getElementType());
|
|
std::string tstr;
|
|
llvm::raw_string_ostream FunctionInnards(tstr);
|
|
FunctionInnards << " (*) (";
|
|
bool PrintedType = false;
|
|
|
|
llvm::FunctionType::param_iterator I = FTy->param_begin(), E = FTy->param_end();
|
|
llvm::Type *RetTy = llvm::cast<llvm::PointerType>(*I)->getElementType();
|
|
unsigned Idx = 1;
|
|
for (++I, ++Idx; I != E; ++I, ++Idx) {
|
|
if (PrintedType)
|
|
FunctionInnards << ", ";
|
|
llvm::Type *ArgTy = *I;
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::ByVal)) {
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#else // LLVM 5.0+
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#endif
|
|
assert(ArgTy->isPointerTy());
|
|
ArgTy = llvm::cast<llvm::PointerType>(ArgTy)->getElementType();
|
|
}
|
|
printType(FunctionInnards, ArgTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
"");
|
|
PrintedType = true;
|
|
}
|
|
if (FTy->isVarArg()) {
|
|
if (!PrintedType)
|
|
FunctionInnards << " int"; //dummy argument for empty vararg functs
|
|
FunctionInnards << ", ...";
|
|
} else if (!PrintedType) {
|
|
FunctionInnards << "void";
|
|
}
|
|
FunctionInnards << ')';
|
|
printType(Out, RetTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeSet::ReturnIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeList::ReturnIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
FunctionInnards.str());
|
|
}
|
|
|
|
llvm::raw_ostream &
|
|
CWriter::printSimpleType(llvm::raw_ostream &Out, llvm::Type *Ty, bool isSigned,
|
|
const std::string &NameSoFar) {
|
|
assert((Ty->isFloatingPointTy() || Ty->isX86_MMXTy() || Ty->isIntegerTy() || Ty->isVectorTy() || Ty->isVoidTy()) &&
|
|
"Invalid type for printSimpleType");
|
|
switch (Ty->getTypeID()) {
|
|
case llvm::Type::VoidTyID: return Out << "void " << NameSoFar;
|
|
case llvm::Type::IntegerTyID: {
|
|
unsigned NumBits = llvm::cast<llvm::IntegerType>(Ty)->getBitWidth();
|
|
if (NumBits == 1)
|
|
return Out << "bool " << NameSoFar;
|
|
else if (NumBits <= 8)
|
|
return Out << (isSigned?"":"u") << "int8_t " << NameSoFar;
|
|
else if (NumBits <= 16)
|
|
return Out << (isSigned?"":"u") << "int16_t " << NameSoFar;
|
|
else if (NumBits <= 32)
|
|
return Out << (isSigned?"":"u") << "int32_t " << NameSoFar;
|
|
else if (NumBits <= 64)
|
|
return Out << (isSigned?"":"u") << "int64_t "<< NameSoFar;
|
|
else
|
|
return Out << "iN<" << NumBits << "> " << NameSoFar;
|
|
}
|
|
case llvm::Type::FloatTyID: return Out << "float " << NameSoFar;
|
|
case llvm::Type::DoubleTyID: return Out << "double " << NameSoFar;
|
|
// Lacking emulation of FP80 on PPC, etc., we assume whichever of these is
|
|
// present matches host 'long double'.
|
|
case llvm::Type::X86_FP80TyID:
|
|
case llvm::Type::PPC_FP128TyID:
|
|
case llvm::Type::FP128TyID: return Out << "long double " << NameSoFar;
|
|
|
|
case llvm::Type::X86_MMXTyID:
|
|
return printSimpleType(Out, llvm::Type::getInt32Ty(Ty->getContext()), isSigned,
|
|
" __attribute__((vector_size(64))) " + NameSoFar);
|
|
|
|
case llvm::Type::VectorTyID: {
|
|
llvm::VectorType *VTy = llvm::cast<llvm::VectorType>(Ty);
|
|
#if 1
|
|
const char *suffix = NULL;
|
|
const llvm::Type *eltTy = VTy->getElementType();
|
|
if (eltTy->isFloatTy())
|
|
suffix = "f";
|
|
else if (eltTy->isDoubleTy())
|
|
suffix = "d";
|
|
else {
|
|
assert(eltTy->isIntegerTy());
|
|
switch (eltTy->getPrimitiveSizeInBits()) {
|
|
case 1:
|
|
suffix = "i1";
|
|
break;
|
|
case 8:
|
|
suffix = "i8";
|
|
break;
|
|
case 16:
|
|
suffix = "i16";
|
|
break;
|
|
case 32:
|
|
suffix = "i32";
|
|
break;
|
|
case 64:
|
|
suffix = "i64";
|
|
break;
|
|
default:
|
|
suffix = "iN";
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Out << "__vec" << VTy->getNumElements() << "_" << suffix << " " <<
|
|
NameSoFar;
|
|
#else
|
|
return printSimpleType(Out, VTy->getElementType(), isSigned,
|
|
" __attribute__((vector_size(" +
|
|
utostr(TD->getTypeAllocSize(VTy)) + " ))) " + NameSoFar);
|
|
#endif
|
|
}
|
|
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "Unknown primitive type: " << *Ty << "\n";
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
}
|
|
|
|
// Pass the Type* and the variable name and this prints out the variable
|
|
// declaration.
|
|
//
|
|
llvm::raw_ostream &CWriter::printType(llvm::raw_ostream &Out, llvm::Type *Ty,
|
|
bool isSigned, const std::string &NameSoFar,
|
|
bool IgnoreName,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL
|
|
#endif
|
|
) {
|
|
|
|
if (Ty->isFloatingPointTy() || Ty->isX86_MMXTy() || Ty->isIntegerTy() || Ty->isVectorTy() || Ty->isVoidTy()) {
|
|
printSimpleType(Out, Ty, isSigned, NameSoFar);
|
|
return Out;
|
|
}
|
|
|
|
switch (Ty->getTypeID()) {
|
|
case llvm::Type::FunctionTyID: {
|
|
llvm::FunctionType *FTy = llvm::cast<llvm::FunctionType>(Ty);
|
|
std::string tstr;
|
|
llvm::raw_string_ostream FunctionInnards(tstr);
|
|
FunctionInnards << " (" << NameSoFar << ") (";
|
|
unsigned Idx = 1;
|
|
for (llvm::FunctionType::param_iterator I = FTy->param_begin(),
|
|
E = FTy->param_end(); I != E; ++I) {
|
|
llvm::Type *ArgTy = *I;
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::ByVal)) {
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#else // LLVM 5.0+
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#endif
|
|
assert(ArgTy->isPointerTy());
|
|
ArgTy = llvm::cast<llvm::PointerType>(ArgTy)->getElementType();
|
|
}
|
|
if (I != FTy->param_begin())
|
|
FunctionInnards << ", ";
|
|
printType(FunctionInnards, ArgTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
"");
|
|
++Idx;
|
|
}
|
|
if (FTy->isVarArg()) {
|
|
if (!FTy->getNumParams())
|
|
FunctionInnards << " int"; //dummy argument for empty vaarg functs
|
|
FunctionInnards << ", ...";
|
|
} else if (!FTy->getNumParams()) {
|
|
FunctionInnards << "void";
|
|
}
|
|
FunctionInnards << ')';
|
|
printType(Out, FTy->getReturnType(),
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeSet::ReturnIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeList::ReturnIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
FunctionInnards.str());
|
|
return Out;
|
|
}
|
|
case llvm::Type::StructTyID: {
|
|
llvm::StructType *STy = llvm::cast<llvm::StructType>(Ty);
|
|
|
|
// Check to see if the type is named.
|
|
if (!IgnoreName)
|
|
return Out << getStructName(STy) << ' ' << NameSoFar;
|
|
|
|
Out << "struct " << NameSoFar << " {\n";
|
|
|
|
// print initialization func
|
|
if (STy->getNumElements() > 0) {
|
|
Out << " static " << NameSoFar << " init(";
|
|
unsigned Idx = 0;
|
|
for (llvm::StructType::element_iterator I = STy->element_begin(),
|
|
E = STy->element_end(); I != E; ++I, ++Idx) {
|
|
char buf[64];
|
|
sprintf(buf, "v%d", Idx);
|
|
printType(Out, *I, false, buf);
|
|
if (Idx + 1 < STy->getNumElements())
|
|
Out << ", ";
|
|
}
|
|
Out << ") {\n";
|
|
Out << " " << NameSoFar << " ret;\n";
|
|
for (Idx = 0; Idx < STy->getNumElements(); ++Idx)
|
|
Out << " ret.field" << Idx << " = v" << Idx << ";\n";
|
|
Out << " return ret;\n";
|
|
Out << " }\n";
|
|
}
|
|
|
|
unsigned Idx = 0;
|
|
for (llvm::StructType::element_iterator I = STy->element_begin(),
|
|
E = STy->element_end(); I != E; ++I) {
|
|
Out << " ";
|
|
printType(Out, *I, false, "field" + llvm::utostr(Idx++));
|
|
Out << ";\n";
|
|
}
|
|
Out << '}';
|
|
if (STy->isPacked())
|
|
Out << " __attribute__ ((packed))";
|
|
return Out;
|
|
}
|
|
|
|
case llvm::Type::PointerTyID: {
|
|
llvm::PointerType *PTy = llvm::cast<llvm::PointerType>(Ty);
|
|
std::string ptrName = "*" + NameSoFar;
|
|
|
|
if (PTy->getElementType()->isArrayTy() ||
|
|
PTy->getElementType()->isVectorTy())
|
|
ptrName = "(" + ptrName + ")";
|
|
|
|
if (!PAL.isEmpty())
|
|
// Must be a function ptr cast!
|
|
return printType(Out, PTy->getElementType(), false, ptrName, true, PAL);
|
|
return printType(Out, PTy->getElementType(), false, ptrName);
|
|
}
|
|
|
|
case llvm::Type::ArrayTyID: {
|
|
llvm::ArrayType *ATy = llvm::cast<llvm::ArrayType>(Ty);
|
|
|
|
// Check to see if the type is named.
|
|
if (!IgnoreName)
|
|
return Out << getArrayName(ATy) << ' ' << NameSoFar;
|
|
|
|
unsigned NumElements = (unsigned)ATy->getNumElements();
|
|
if (NumElements == 0) NumElements = 1;
|
|
// Arrays are wrapped in structs to allow them to have normal
|
|
// value semantics (avoiding the array "decay").
|
|
Out << "struct " << NameSoFar << " {\n";
|
|
// init func
|
|
Out << " static " << NameSoFar << " init(";
|
|
for (unsigned Idx = 0; Idx < NumElements; ++Idx) {
|
|
char buf[64];
|
|
sprintf(buf, "v%d", Idx);
|
|
printType(Out, ATy->getElementType(), false, buf);
|
|
if (Idx + 1 < NumElements)
|
|
Out << ", ";
|
|
}
|
|
Out << ") {\n";
|
|
Out << " " << NameSoFar << " ret;\n";
|
|
for (unsigned Idx = 0; Idx < NumElements; ++Idx)
|
|
Out << " ret.array[" << Idx << "] = v" << Idx << ";\n";
|
|
Out << " return ret;\n";
|
|
Out << " }\n ";
|
|
|
|
// if it's an array of i8s, also provide a version that takes a const
|
|
// char *
|
|
if (ATy->getElementType() == LLVMTypes::Int8Type) {
|
|
Out << " static " << NameSoFar << " init(const char *p) {\n";
|
|
Out << " " << NameSoFar << " ret;\n";
|
|
Out << " memcpy((uint8_t *)ret.array, (uint8_t *)p, " << NumElements << ");\n";
|
|
Out << " return ret;\n";
|
|
Out << " }\n";
|
|
}
|
|
|
|
printType(Out, ATy->getElementType(), false,
|
|
"array[" + llvm::utostr(NumElements) + "]");
|
|
return Out << ";\n} ";
|
|
}
|
|
|
|
default:
|
|
llvm_unreachable("Unhandled case in getTypeProps!");
|
|
}
|
|
}
|
|
|
|
void CWriter::printConstantArray(llvm::ConstantArray *CPA, bool Static) {
|
|
// vec16_i64 should be handled separately
|
|
|
|
if (is_vec16_i64_ty(CPA->getOperand(0)->getType())) {
|
|
Out << "/* vec16_i64 should be loaded carefully on knc */";
|
|
Out << "\n#if defined(KNC)\n";
|
|
Out << "hilo2zmm";
|
|
Out << "\n#endif\n";
|
|
}
|
|
Out << "(";
|
|
printConstant(llvm::cast<llvm::Constant>(CPA->getOperand(0)), Static);
|
|
Out << ")";
|
|
|
|
for (unsigned i = 1, e = CPA->getNumOperands(); i != e; ++i) {
|
|
Out << ", ";
|
|
|
|
if (is_vec16_i64_ty(CPA->getOperand(i)->getType())) {
|
|
Out << "/* vec16_i64 should be loaded carefully on knc */";
|
|
Out << "\n#if defined(KNC) \n";
|
|
Out << "hilo2zmm";
|
|
Out << "\n#endif \n";
|
|
}
|
|
Out << "(";
|
|
printConstant(llvm::cast<llvm::Constant>(CPA->getOperand(i)), Static);
|
|
Out << ")";
|
|
}
|
|
}
|
|
|
|
void CWriter::printConstantVector(llvm::ConstantVector *CP, bool Static) {
|
|
printConstant(llvm::cast<llvm::Constant>(CP->getOperand(0)), Static);
|
|
for (unsigned i = 1, e = CP->getNumOperands(); i != e; ++i) {
|
|
Out << ", ";
|
|
printConstant(llvm::cast<llvm::Constant>(CP->getOperand(i)), Static);
|
|
}
|
|
}
|
|
|
|
void CWriter::printConstantDataSequential(llvm::ConstantDataSequential *CDS,
|
|
bool Static) {
|
|
// As a special case, print the array as a string if it is an array of
|
|
// ubytes or an array of sbytes with positive values.
|
|
//
|
|
if (CDS->isCString()) {
|
|
Out << '\"';
|
|
// Keep track of whether the last number was a hexadecimal escape.
|
|
bool LastWasHex = false;
|
|
|
|
llvm::StringRef Bytes = CDS->getAsCString();
|
|
|
|
// Do not include the last character, which we know is null
|
|
for (unsigned i = 0, e = Bytes.size(); i != e; ++i) {
|
|
unsigned char C = Bytes[i];
|
|
|
|
// Print it out literally if it is a printable character. The only thing
|
|
// to be careful about is when the last letter output was a hex escape
|
|
// code, in which case we have to be careful not to print out hex digits
|
|
// explicitly (the C compiler thinks it is a continuation of the previous
|
|
// character, sheesh...)
|
|
//
|
|
if (isprint(C) && (!LastWasHex || !isxdigit(C))) {
|
|
LastWasHex = false;
|
|
if (C == '"' || C == '\\')
|
|
Out << "\\" << (char)C;
|
|
else
|
|
Out << (char)C;
|
|
} else {
|
|
LastWasHex = false;
|
|
switch (C) {
|
|
case '\n': Out << "\\n"; break;
|
|
case '\t': Out << "\\t"; break;
|
|
case '\r': Out << "\\r"; break;
|
|
case '\v': Out << "\\v"; break;
|
|
case '\a': Out << "\\a"; break;
|
|
case '\"': Out << "\\\""; break;
|
|
case '\'': Out << "\\\'"; break;
|
|
default:
|
|
Out << "\\x";
|
|
Out << (char)(( C/16 < 10) ? ( C/16 +'0') : ( C/16 -10+'A'));
|
|
Out << (char)(((C&15) < 10) ? ((C&15)+'0') : ((C&15)-10+'A'));
|
|
LastWasHex = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Out << '\"';
|
|
} else {
|
|
printConstant(CDS->getElementAsConstant(0), Static);
|
|
for (unsigned i = 1, e = CDS->getNumElements(); i != e; ++i) {
|
|
Out << ", ";
|
|
printConstant(CDS->getElementAsConstant(i), Static);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline std::string ftostr(const llvm::APFloat& V) {
|
|
std::string Buf;
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
|
|
if (&V.getSemantics() == &llvm::APFloat::IEEEdouble) {
|
|
llvm::raw_string_ostream(Buf) << V.convertToDouble();
|
|
return Buf;
|
|
} else if (&V.getSemantics() == &llvm::APFloat::IEEEsingle) {
|
|
llvm::raw_string_ostream(Buf) << (double)V.convertToFloat();
|
|
return Buf;
|
|
}
|
|
#else // LLVM 4.0+
|
|
if (&V.getSemantics() == &llvm::APFloat::IEEEdouble()) {
|
|
llvm::raw_string_ostream(Buf) << V.convertToDouble();
|
|
return Buf;
|
|
} else if (&V.getSemantics() == &llvm::APFloat::IEEEsingle()) {
|
|
llvm::raw_string_ostream(Buf) << (double)V.convertToFloat();
|
|
return Buf;
|
|
}
|
|
#endif
|
|
return "<unknown format in ftostr>"; // error
|
|
}
|
|
|
|
// isFPCSafeToPrint - Returns true if we may assume that CFP may be written out
|
|
// textually as a double (rather than as a reference to a stack-allocated
|
|
// variable). We decide this by converting CFP to a string and back into a
|
|
// double, and then checking whether the conversion results in a bit-equal
|
|
// double to the original value of CFP. This depends on us and the target C
|
|
// compiler agreeing on the conversion process (which is pretty likely since we
|
|
// only deal in IEEE FP).
|
|
//
|
|
static bool isFPCSafeToPrint(const llvm::ConstantFP *CFP) {
|
|
bool ignored;
|
|
// Do long doubles in hex for now.
|
|
if (CFP->getType() != llvm::Type::getFloatTy(CFP->getContext()) &&
|
|
CFP->getType() != llvm::Type::getDoubleTy(CFP->getContext()))
|
|
return false;
|
|
llvm::APFloat APF = llvm::APFloat(CFP->getValueAPF()); // copy
|
|
if (CFP->getType() == llvm::Type::getFloatTy(CFP->getContext()))
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
APF.convert(llvm::APFloat::IEEEdouble, llvm::APFloat::rmNearestTiesToEven, &ignored);
|
|
#else // LLVM 4.0+
|
|
APF.convert(llvm::APFloat::IEEEdouble(), llvm::APFloat::rmNearestTiesToEven, &ignored);
|
|
#endif
|
|
#if HAVE_PRINTF_A && ENABLE_CBE_PRINTF_A
|
|
char Buffer[100];
|
|
sprintf(Buffer, "%a", APF.convertToDouble());
|
|
if (!strncmp(Buffer, "0x", 2) ||
|
|
!strncmp(Buffer, "-0x", 3) ||
|
|
!strncmp(Buffer, "+0x", 3))
|
|
return APF.bitwiseIsEqual(llvm::APFloat(atof(Buffer)));
|
|
return false;
|
|
#else
|
|
std::string StrVal = ftostr(APF);
|
|
|
|
while (StrVal[0] == ' ')
|
|
StrVal.erase(StrVal.begin());
|
|
|
|
// Check to make sure that the stringized number is not some string like "Inf"
|
|
// or NaN. Check that the string matches the "[-+]?[0-9]" regex.
|
|
if ((StrVal[0] >= '0' && StrVal[0] <= '9') ||
|
|
((StrVal[0] == '-' || StrVal[0] == '+') &&
|
|
(StrVal[1] >= '0' && StrVal[1] <= '9')))
|
|
// Reparse stringized version!
|
|
return APF.bitwiseIsEqual(llvm::APFloat(atof(StrVal.c_str())));
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/// Print out the casting for a cast operation. This does the double casting
|
|
/// necessary for conversion to the destination type, if necessary.
|
|
/// Return value indicates whether a closing paren is needed.
|
|
/// @brief Print a cast
|
|
bool CWriter::printCast(unsigned opc, llvm::Type *SrcTy, llvm::Type *DstTy) {
|
|
if (llvm::isa<const llvm::VectorType>(DstTy)) {
|
|
assert(llvm::isa<const llvm::VectorType>(SrcTy));
|
|
switch (opc) {
|
|
case llvm::Instruction::UIToFP: Out << "__cast_uitofp("; break;
|
|
case llvm::Instruction::SIToFP: Out << "__cast_sitofp("; break;
|
|
case llvm::Instruction::IntToPtr: llvm_unreachable("Invalid vector cast");
|
|
case llvm::Instruction::Trunc: Out << "__cast_trunc("; break;
|
|
case llvm::Instruction::BitCast: Out << "__cast_bits("; break;
|
|
case llvm::Instruction::FPExt: Out << "__cast_fpext("; break;
|
|
case llvm::Instruction::FPTrunc: Out << "__cast_fptrunc("; break;
|
|
case llvm::Instruction::ZExt: Out << "__cast_zext("; break;
|
|
case llvm::Instruction::PtrToInt: llvm_unreachable("Invalid vector cast");
|
|
case llvm::Instruction::FPToUI: Out << "__cast_fptoui("; break;
|
|
case llvm::Instruction::SExt: Out << "__cast_sext("; break;
|
|
case llvm::Instruction::FPToSI: Out << "__cast_fptosi("; break;
|
|
default:
|
|
llvm_unreachable("Invalid cast opcode");
|
|
}
|
|
|
|
// print a call to the constructor for the destination type for the
|
|
// first arg; this bogus first parameter is only used to convey the
|
|
// desired return type to the callee.
|
|
printType(Out, DstTy);
|
|
Out << "(), ";
|
|
|
|
return true;
|
|
}
|
|
|
|
// Print the destination type cast
|
|
switch (opc) {
|
|
case llvm::Instruction::BitCast: {
|
|
if (DstTy->isPointerTy()) {
|
|
Out << '(';
|
|
printType(Out, DstTy);
|
|
Out << ')';
|
|
break;
|
|
}
|
|
else {
|
|
Out << "__cast_bits((";
|
|
printType(Out, DstTy);
|
|
Out << ")0, ";
|
|
return true;
|
|
}
|
|
}
|
|
case llvm::Instruction::UIToFP:
|
|
case llvm::Instruction::SIToFP:
|
|
case llvm::Instruction::IntToPtr:
|
|
case llvm::Instruction::Trunc:
|
|
case llvm::Instruction::FPExt:
|
|
case llvm::Instruction::FPTrunc: // For these the DstTy sign doesn't matter
|
|
Out << '(';
|
|
printType(Out, DstTy);
|
|
Out << ')';
|
|
break;
|
|
case llvm::Instruction::ZExt:
|
|
case llvm::Instruction::PtrToInt:
|
|
case llvm::Instruction::FPToUI: // For these, make sure we get an unsigned dest
|
|
Out << '(';
|
|
printSimpleType(Out, DstTy, false);
|
|
Out << ')';
|
|
break;
|
|
case llvm::Instruction::SExt:
|
|
case llvm::Instruction::FPToSI: // For these, make sure we get a signed dest
|
|
Out << '(';
|
|
printSimpleType(Out, DstTy, true);
|
|
Out << ')';
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid cast opcode");
|
|
}
|
|
|
|
// Print the source type cast
|
|
switch (opc) {
|
|
case llvm::Instruction::UIToFP:
|
|
case llvm::Instruction::ZExt:
|
|
Out << '(';
|
|
printSimpleType(Out, SrcTy, false);
|
|
Out << ')';
|
|
break;
|
|
case llvm::Instruction::SIToFP:
|
|
case llvm::Instruction::SExt:
|
|
Out << '(';
|
|
printSimpleType(Out, SrcTy, true);
|
|
Out << ')';
|
|
break;
|
|
case llvm::Instruction::IntToPtr:
|
|
case llvm::Instruction::PtrToInt:
|
|
// Avoid "cast to pointer from integer of different size" warnings
|
|
Out << "(unsigned long)";
|
|
break;
|
|
case llvm::Instruction::Trunc:
|
|
case llvm::Instruction::BitCast:
|
|
case llvm::Instruction::FPExt:
|
|
case llvm::Instruction::FPTrunc:
|
|
case llvm::Instruction::FPToSI:
|
|
case llvm::Instruction::FPToUI:
|
|
break; // These don't need a source cast.
|
|
default:
|
|
llvm_unreachable("Invalid cast opcode");
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/** Construct the name of a function with the given base and returning a
|
|
vector of a given type, of the specified idth. For example, if base
|
|
is "foo" and matchType is i32 and width is 16, this will return the
|
|
string "__foo_i32<__vec16_i32>".
|
|
*/
|
|
static const char *
|
|
lGetTypedFunc(const char *base, llvm::Type *matchType, int width) {
|
|
static const char *ty_desc_str[] = {"f", "d", "i1", "i8", "i16", "i32", "i64"};
|
|
static const char *fn_desc_str[] = {"float", "double", "i1", "i8", "i16", "i32", "i64"};
|
|
enum {DESC_FLOAT, DESC_DOUBLE, DESC_I1, DESC_I8, DESC_I16, DESC_I32, DESC_I64} desc;
|
|
|
|
switch (matchType->getTypeID()) {
|
|
case llvm::Type::FloatTyID: desc = DESC_FLOAT; break;
|
|
case llvm::Type::DoubleTyID: desc = DESC_DOUBLE; break;
|
|
case llvm::Type::IntegerTyID: {
|
|
switch (llvm::cast<llvm::IntegerType>(matchType)->getBitWidth()) {
|
|
case 1: desc = DESC_I1; break;
|
|
case 8: desc = DESC_I8; break;
|
|
case 16: desc = DESC_I16; break;
|
|
case 32: desc = DESC_I32; break;
|
|
case 64: desc = DESC_I64; break;
|
|
default: return NULL;
|
|
}
|
|
break;
|
|
}
|
|
default: return NULL;
|
|
}
|
|
|
|
char buf[64];
|
|
snprintf(buf, 64, "__%s_%s<__vec%d_%s>", base, fn_desc_str[desc], width, ty_desc_str[desc]);
|
|
return strdup(buf);
|
|
}
|
|
|
|
|
|
// printConstant - The LLVM Constant to C Constant converter.
|
|
void CWriter::printConstant(llvm::Constant *CPV, bool Static) {
|
|
if (const llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(CPV)) {
|
|
if (llvm::isa<llvm::VectorType>(CPV->getType())) {
|
|
assert(CE->getOpcode() == llvm::Instruction::BitCast);
|
|
llvm::ConstantExpr *Op = llvm::dyn_cast<llvm::ConstantExpr>(CE->getOperand(0));
|
|
assert(Op && Op->getOpcode() == llvm::Instruction::BitCast);
|
|
assert(llvm::isa<llvm::VectorType>(Op->getOperand(0)->getType()));
|
|
|
|
Out << "(__cast_bits(";
|
|
printType(Out, CE->getType());
|
|
Out << "(), ";
|
|
printConstant(Op->getOperand(0), Static);
|
|
Out << "))";
|
|
return;
|
|
}
|
|
switch (CE->getOpcode()) {
|
|
case llvm::Instruction::Trunc:
|
|
case llvm::Instruction::ZExt:
|
|
case llvm::Instruction::SExt:
|
|
case llvm::Instruction::FPTrunc:
|
|
case llvm::Instruction::FPExt:
|
|
case llvm::Instruction::UIToFP:
|
|
case llvm::Instruction::SIToFP:
|
|
case llvm::Instruction::FPToUI:
|
|
case llvm::Instruction::FPToSI:
|
|
case llvm::Instruction::PtrToInt:
|
|
case llvm::Instruction::IntToPtr:
|
|
case llvm::Instruction::BitCast: {
|
|
if (CE->getOpcode() == llvm::Instruction::BitCast &&
|
|
CE->getType()->isPointerTy() == false) {
|
|
Out << "__cast_bits((";
|
|
printType(Out, CE->getType());
|
|
Out << ")0, ";
|
|
printConstant(CE->getOperand(0), Static);
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
Out << "(";
|
|
bool closeParen = printCast(CE->getOpcode(), CE->getOperand(0)->getType(),
|
|
CE->getType());
|
|
if (CE->getOpcode() == llvm::Instruction::SExt &&
|
|
CE->getOperand(0)->getType() == llvm::Type::getInt1Ty(CPV->getContext())) {
|
|
// Make sure we really sext from bool here by subtracting from 0
|
|
Out << "0-";
|
|
}
|
|
printConstant(CE->getOperand(0), Static);
|
|
if (CE->getType() == llvm::Type::getInt1Ty(CPV->getContext()) &&
|
|
(CE->getOpcode() == llvm::Instruction::Trunc ||
|
|
CE->getOpcode() == llvm::Instruction::FPToUI ||
|
|
CE->getOpcode() == llvm::Instruction::FPToSI ||
|
|
CE->getOpcode() == llvm::Instruction::PtrToInt)) {
|
|
// Make sure we really truncate to bool here by anding with 1
|
|
Out << "&1u";
|
|
}
|
|
Out << ')';
|
|
if (closeParen)
|
|
Out << ')';
|
|
return;
|
|
}
|
|
case llvm::Instruction::GetElementPtr:
|
|
assert(!llvm::isa<llvm::VectorType>(CPV->getType()));
|
|
Out << "(";
|
|
printGEPExpression(CE->getOperand(0), gep_type_begin(CPV),
|
|
gep_type_end(CPV), Static);
|
|
Out << ")";
|
|
return;
|
|
case llvm::Instruction::Select:
|
|
assert(!llvm::isa<llvm::VectorType>(CPV->getType()));
|
|
Out << '(';
|
|
printConstant(CE->getOperand(0), Static);
|
|
Out << '?';
|
|
printConstant(CE->getOperand(1), Static);
|
|
Out << ':';
|
|
printConstant(CE->getOperand(2), Static);
|
|
Out << ')';
|
|
return;
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::FAdd:
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::FSub:
|
|
case llvm::Instruction::Mul:
|
|
case llvm::Instruction::FMul:
|
|
case llvm::Instruction::SDiv:
|
|
case llvm::Instruction::UDiv:
|
|
case llvm::Instruction::FDiv:
|
|
case llvm::Instruction::URem:
|
|
case llvm::Instruction::SRem:
|
|
case llvm::Instruction::FRem:
|
|
case llvm::Instruction::And:
|
|
case llvm::Instruction::Or:
|
|
case llvm::Instruction::Xor:
|
|
case llvm::Instruction::ICmp:
|
|
case llvm::Instruction::Shl:
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::AShr:
|
|
{
|
|
assert(!llvm::isa<llvm::VectorType>(CPV->getType()));
|
|
Out << '(';
|
|
bool NeedsClosingParens = printConstExprCast(CE, Static);
|
|
printConstantWithCast(CE->getOperand(0), CE->getOpcode());
|
|
switch (CE->getOpcode()) {
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::FAdd: Out << " + "; break;
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::FSub: Out << " - "; break;
|
|
case llvm::Instruction::Mul:
|
|
case llvm::Instruction::FMul: Out << " * "; break;
|
|
case llvm::Instruction::URem:
|
|
case llvm::Instruction::SRem:
|
|
case llvm::Instruction::FRem: Out << " % "; break;
|
|
case llvm::Instruction::UDiv:
|
|
case llvm::Instruction::SDiv:
|
|
case llvm::Instruction::FDiv: Out << " / "; break;
|
|
case llvm::Instruction::And: Out << " & "; break;
|
|
case llvm::Instruction::Or: Out << " | "; break;
|
|
case llvm::Instruction::Xor: Out << " ^ "; break;
|
|
case llvm::Instruction::Shl: Out << " << "; break;
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::AShr: Out << " >> "; break;
|
|
case llvm::Instruction::ICmp:
|
|
switch (CE->getPredicate()) {
|
|
case llvm::ICmpInst::ICMP_EQ: Out << " == "; break;
|
|
case llvm::ICmpInst::ICMP_NE: Out << " != "; break;
|
|
case llvm::ICmpInst::ICMP_SLT:
|
|
case llvm::ICmpInst::ICMP_ULT: Out << " < "; break;
|
|
case llvm::ICmpInst::ICMP_SLE:
|
|
case llvm::ICmpInst::ICMP_ULE: Out << " <= "; break;
|
|
case llvm::ICmpInst::ICMP_SGT:
|
|
case llvm::ICmpInst::ICMP_UGT: Out << " > "; break;
|
|
case llvm::ICmpInst::ICMP_SGE:
|
|
case llvm::ICmpInst::ICMP_UGE: Out << " >= "; break;
|
|
default: llvm_unreachable("Illegal ICmp predicate");
|
|
}
|
|
break;
|
|
default: llvm_unreachable("Illegal opcode here!");
|
|
}
|
|
printConstantWithCast(CE->getOperand(1), CE->getOpcode());
|
|
if (NeedsClosingParens)
|
|
Out << "))";
|
|
Out << ')';
|
|
return;
|
|
}
|
|
case llvm::Instruction::FCmp: {
|
|
assert(!llvm::isa<llvm::VectorType>(CPV->getType()));
|
|
Out << '(';
|
|
bool NeedsClosingParens = printConstExprCast(CE, Static);
|
|
if (CE->getPredicate() == llvm::FCmpInst::FCMP_FALSE)
|
|
Out << "0";
|
|
else if (CE->getPredicate() == llvm::FCmpInst::FCMP_TRUE)
|
|
Out << "1";
|
|
else {
|
|
const char* op = 0;
|
|
switch (CE->getPredicate()) {
|
|
default: llvm_unreachable("Illegal FCmp predicate");
|
|
case llvm::FCmpInst::FCMP_ORD: op = "ord"; break;
|
|
case llvm::FCmpInst::FCMP_UNO: op = "uno"; break;
|
|
case llvm::FCmpInst::FCMP_UEQ: op = "ueq"; break;
|
|
case llvm::FCmpInst::FCMP_UNE: op = "une"; break;
|
|
case llvm::FCmpInst::FCMP_ULT: op = "ult"; break;
|
|
case llvm::FCmpInst::FCMP_ULE: op = "ule"; break;
|
|
case llvm::FCmpInst::FCMP_UGT: op = "ugt"; break;
|
|
case llvm::FCmpInst::FCMP_UGE: op = "uge"; break;
|
|
case llvm::FCmpInst::FCMP_OEQ: op = "oeq"; break;
|
|
case llvm::FCmpInst::FCMP_ONE: op = "one"; break;
|
|
case llvm::FCmpInst::FCMP_OLT: op = "olt"; break;
|
|
case llvm::FCmpInst::FCMP_OLE: op = "ole"; break;
|
|
case llvm::FCmpInst::FCMP_OGT: op = "ogt"; break;
|
|
case llvm::FCmpInst::FCMP_OGE: op = "oge"; break;
|
|
}
|
|
Out << "llvm_fcmp_" << op << "(";
|
|
printConstantWithCast(CE->getOperand(0), CE->getOpcode());
|
|
Out << ", ";
|
|
printConstantWithCast(CE->getOperand(1), CE->getOpcode());
|
|
Out << ")";
|
|
}
|
|
if (NeedsClosingParens)
|
|
Out << "))";
|
|
Out << ')';
|
|
return;
|
|
}
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "CWriter Error: Unhandled constant expression: "
|
|
<< *CE << "\n";
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
} else if (llvm::isa<llvm::UndefValue>(CPV) && CPV->getType()->isSingleValueType()) {
|
|
if (CPV->getType()->isVectorTy()) {
|
|
printType(Out, CPV->getType());
|
|
Out << "( /* UNDEF */)";
|
|
return;
|
|
}
|
|
|
|
Out << "((";
|
|
printType(Out, CPV->getType()); // sign doesn't matter
|
|
Out << ")/*UNDEF*/";
|
|
Out << "0)";
|
|
return;
|
|
}
|
|
|
|
if (llvm::ConstantInt *CI = llvm::dyn_cast<llvm::ConstantInt>(CPV)) {
|
|
llvm::Type* Ty = CI->getType();
|
|
if (Ty == llvm::Type::getInt1Ty(CPV->getContext()))
|
|
Out << (CI->getZExtValue() ? '1' : '0');
|
|
else if (Ty == llvm::Type::getInt32Ty(CPV->getContext()))
|
|
Out << CI->getZExtValue() << 'u';
|
|
else if (Ty == llvm::Type::getInt64Ty(CPV->getContext()))
|
|
Out << CI->getZExtValue() << "ull";
|
|
else if (Ty->getPrimitiveSizeInBits() > 64) {
|
|
Out << "\"";
|
|
//const uint64_t *Ptr64 = CPV->getUniqueInteger().getRawData();
|
|
const uint64_t *Ptr64 = CI->getValue().getRawData();
|
|
for (int i = 0; i < Ty->getPrimitiveSizeInBits(); i++) {
|
|
Out << ((Ptr64[i / (sizeof (uint64_t) * 8)] >> (i % (sizeof (uint64_t) * 8))) & 1);
|
|
}
|
|
Out << "\"";
|
|
}
|
|
else {
|
|
Out << "((";
|
|
printSimpleType(Out, Ty, false) << ')';
|
|
if (CI->isMinValue(true))
|
|
Out << CI->getZExtValue() << 'u';
|
|
else
|
|
Out << CI->getSExtValue();
|
|
Out << ')';
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (CPV->getType()->getTypeID()) {
|
|
case llvm::Type::FloatTyID:
|
|
case llvm::Type::DoubleTyID:
|
|
case llvm::Type::X86_FP80TyID:
|
|
case llvm::Type::PPC_FP128TyID:
|
|
case llvm::Type::FP128TyID: {
|
|
llvm::ConstantFP *FPC = llvm::cast<llvm::ConstantFP>(CPV);
|
|
std::map<const llvm::ConstantFP*, unsigned>::iterator I = FPConstantMap.find(FPC);
|
|
if (I != FPConstantMap.end()) {
|
|
// Because of FP precision problems we must load from a stack allocated
|
|
// value that holds the value in hex.
|
|
Out << "(*(" << (FPC->getType() == llvm::Type::getFloatTy(CPV->getContext()) ?
|
|
"float" :
|
|
FPC->getType() == llvm::Type::getDoubleTy(CPV->getContext()) ?
|
|
"double" :
|
|
"long double")
|
|
<< "*)&FPConstant" << I->second << ')';
|
|
} else {
|
|
double V;
|
|
if (FPC->getType() == llvm::Type::getFloatTy(CPV->getContext()))
|
|
V = FPC->getValueAPF().convertToFloat();
|
|
else if (FPC->getType() == llvm::Type::getDoubleTy(CPV->getContext()))
|
|
V = FPC->getValueAPF().convertToDouble();
|
|
else {
|
|
// Long double. Convert the number to double, discarding precision.
|
|
// This is not awesome, but it at least makes the CBE output somewhat
|
|
// useful.
|
|
llvm::APFloat Tmp = FPC->getValueAPF();
|
|
bool LosesInfo;
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
Tmp.convert(llvm::APFloat::IEEEdouble, llvm::APFloat::rmTowardZero, &LosesInfo);
|
|
#else // LLVM 4.0+
|
|
Tmp.convert(llvm::APFloat::IEEEdouble(), llvm::APFloat::rmTowardZero, &LosesInfo);
|
|
#endif
|
|
V = Tmp.convertToDouble();
|
|
}
|
|
|
|
if (std::isnan(V)) {
|
|
// The value is NaN
|
|
|
|
// FIXME the actual NaN bits should be emitted.
|
|
// The prefix for a quiet NaN is 0x7FF8. For a signalling NaN,
|
|
// it's 0x7ff4.
|
|
const unsigned long QuietNaN = 0x7ff8UL;
|
|
//const unsigned long SignalNaN = 0x7ff4UL;
|
|
|
|
// We need to grab the first part of the FP #
|
|
char Buffer[100];
|
|
|
|
uint64_t ll = llvm::DoubleToBits(V);
|
|
sprintf(Buffer, "0x%" PRIx64, ll);
|
|
|
|
std::string Num(&Buffer[0], &Buffer[6]);
|
|
unsigned long Val = strtoul(Num.c_str(), 0, 16);
|
|
|
|
if (FPC->getType() == llvm::Type::getFloatTy(FPC->getContext()))
|
|
Out << "LLVM_NAN" << (Val == QuietNaN ? "" : "S") << "F(\""
|
|
<< Buffer << "\") /*nan*/ ";
|
|
else
|
|
Out << "LLVM_NAN" << (Val == QuietNaN ? "" : "S") << "(\""
|
|
<< Buffer << "\") /*nan*/ ";
|
|
} else if (std::isinf(V)) {
|
|
// The value is Inf
|
|
if (V < 0) Out << '-';
|
|
Out << "LLVM_INF" <<
|
|
(FPC->getType() == llvm::Type::getFloatTy(FPC->getContext()) ? "F" : "")
|
|
<< " /*inf*/ ";
|
|
} else {
|
|
std::string Num;
|
|
#if HAVE_PRINTF_A && ENABLE_CBE_PRINTF_A
|
|
// Print out the constant as a floating point number.
|
|
char Buffer[100];
|
|
sprintf(Buffer, "%a", V);
|
|
Num = Buffer;
|
|
#else
|
|
Num = ftostr(FPC->getValueAPF());
|
|
#endif
|
|
Out << Num;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case llvm::Type::ArrayTyID: {
|
|
llvm::ArrayType *AT = llvm::cast<llvm::ArrayType>(CPV->getType());
|
|
if (Static)
|
|
// arrays are wrapped in structs...
|
|
Out << "{ ";
|
|
else {
|
|
// call init func of the struct it's wrapped in...
|
|
printType(Out, CPV->getType());
|
|
Out << "::init (";
|
|
}
|
|
if (llvm::ConstantArray *CA = llvm::dyn_cast<llvm::ConstantArray>(CPV)) {
|
|
printConstantArray(CA, Static);
|
|
} else if (llvm::ConstantDataSequential *CDS =
|
|
llvm::dyn_cast<llvm::ConstantDataSequential>(CPV)) {
|
|
printConstantDataSequential(CDS, Static);
|
|
} else {
|
|
assert(llvm::isa<llvm::ConstantAggregateZero>(CPV) || llvm::isa<llvm::UndefValue>(CPV));
|
|
if (AT->getNumElements()) {
|
|
Out << ' ';
|
|
llvm::Constant *CZ = llvm::Constant::getNullValue(AT->getElementType());
|
|
printConstant(CZ, Static);
|
|
for (unsigned i = 1, e = (unsigned)AT->getNumElements(); i != e; ++i) {
|
|
Out << ", ";
|
|
printConstant(CZ, Static);
|
|
}
|
|
}
|
|
}
|
|
if (Static)
|
|
Out << " }";
|
|
else
|
|
Out << ")";
|
|
break;
|
|
}
|
|
case llvm::Type::VectorTyID: {
|
|
llvm::VectorType *VT = llvm::dyn_cast<llvm::VectorType>(CPV->getType());
|
|
|
|
if (llvm::isa<llvm::ConstantAggregateZero>(CPV)) {
|
|
// All zeros; call the __setzero_* function.
|
|
const char *setZeroFunc = lGetTypedFunc("setzero", VT->getElementType(), vectorWidth);
|
|
assert(setZeroFunc != NULL);
|
|
Out << setZeroFunc << "()";
|
|
}
|
|
else if (llvm::isa<llvm::UndefValue>(CPV)) {
|
|
// Undefined value; call __undef_* so that we can potentially pass
|
|
// this information along..
|
|
const char *undefFunc = lGetTypedFunc("undef", VT->getElementType(), vectorWidth);
|
|
assert(undefFunc != NULL);
|
|
Out << undefFunc << "()";
|
|
}
|
|
else {
|
|
const char *smearFunc = lGetTypedFunc("smear", VT->getElementType(), vectorWidth);
|
|
|
|
if (llvm::ConstantVector *CV = llvm::dyn_cast<llvm::ConstantVector>(CPV)) {
|
|
llvm::Constant *splatValue = CV->getSplatValue();
|
|
if (splatValue != NULL && smearFunc != NULL) {
|
|
// If it's a basic type and has a __smear_* function, then
|
|
// call that.
|
|
Out << smearFunc << "(";
|
|
printConstant(splatValue, Static);
|
|
Out << ")";
|
|
}
|
|
else {
|
|
// Otherwise call the constructor for the type
|
|
printType(Out, CPV->getType());
|
|
Out << "(";
|
|
printConstantVector(CV, Static);
|
|
Out << ")";
|
|
}
|
|
}
|
|
else if (llvm::ConstantDataVector *CDV =
|
|
llvm::dyn_cast<llvm::ConstantDataVector>(CPV)) {
|
|
llvm::Constant *splatValue = CDV->getSplatValue();
|
|
if (splatValue != NULL && smearFunc != NULL) {
|
|
Out << smearFunc << "(";
|
|
printConstant(splatValue, Static);
|
|
Out << ")";
|
|
}
|
|
else if (VectorConstantMap.find(CDV) != VectorConstantMap.end()) {
|
|
// If we have emitted an static const array with the
|
|
// vector's values, just load from it.
|
|
unsigned index = VectorConstantMap[CDV];
|
|
int alignment = 4 * std::min(vectorWidth, 16);
|
|
|
|
Out << "__load<" << alignment << ">(";
|
|
|
|
// Cast the pointer to the array of element values to a
|
|
// pointer to the vector type.
|
|
Out << "(const ";
|
|
printSimpleType(Out, CDV->getType(), true, "");
|
|
Out << " *)";
|
|
|
|
Out << "(VectorConstant" << index << "))";
|
|
}
|
|
else {
|
|
printType(Out, CPV->getType());
|
|
Out << "(";
|
|
printConstantDataSequential(CDV, Static);
|
|
Out << ")";
|
|
}
|
|
}
|
|
else {
|
|
llvm::report_fatal_error("Unexpected vector type");
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case llvm::Type::StructTyID:
|
|
if (!Static) {
|
|
// call init func...
|
|
printType(Out, CPV->getType());
|
|
Out << "::init";
|
|
}
|
|
if (llvm::isa<llvm::ConstantAggregateZero>(CPV) || llvm::isa<llvm::UndefValue>(CPV)) {
|
|
llvm::StructType *ST = llvm::cast<llvm::StructType>(CPV->getType());
|
|
Out << '(';
|
|
if (ST->getNumElements()) {
|
|
Out << ' ';
|
|
printConstant(llvm::Constant::getNullValue(ST->getElementType(0)), Static);
|
|
for (unsigned i = 1, e = ST->getNumElements(); i != e; ++i) {
|
|
Out << ", ";
|
|
printConstant(llvm::Constant::getNullValue(ST->getElementType(i)), Static);
|
|
}
|
|
}
|
|
Out << ')';
|
|
} else {
|
|
Out << '(';
|
|
if (CPV->getNumOperands()) {
|
|
// It is a kludge. It is needed because we cannot support short vectors
|
|
// when generating code for knl-generic in multitarget mode.
|
|
// Short vectors are mapped to "native" vectors and cause AVX-512 code
|
|
// generation in static block initialization (__vec16_* in ::init function).
|
|
bool isGenericKNL = g->target->getISA() == Target::GENERIC &&
|
|
!g->target->getTreatGenericAsSmth().empty() &&
|
|
g->mangleFunctionsWithTarget;
|
|
if (isGenericKNL && CPV->getOperand(0)->getType()->isVectorTy())
|
|
llvm::report_fatal_error("knl-generic-* target doesn's support short vectors");
|
|
Out << ' ';
|
|
printConstant(llvm::cast<llvm::Constant>(CPV->getOperand(0)), Static);
|
|
for (unsigned i = 1, e = CPV->getNumOperands(); i != e; ++i) {
|
|
Out << ", ";
|
|
if (isGenericKNL && CPV->getOperand(i)->getType()->isVectorTy())
|
|
llvm::report_fatal_error("knl-generic-* target doesn's support short vectors");
|
|
printConstant(llvm::cast<llvm::Constant>(CPV->getOperand(i)), Static);
|
|
}
|
|
}
|
|
Out << ')';
|
|
}
|
|
break;
|
|
|
|
case llvm::Type::PointerTyID:
|
|
if (llvm::isa<llvm::ConstantPointerNull>(CPV)) {
|
|
Out << "((";
|
|
printType(Out, CPV->getType()); // sign doesn't matter
|
|
Out << ")/*NULL*/0)";
|
|
break;
|
|
} else if (llvm::GlobalValue *GV = llvm::dyn_cast<llvm::GlobalValue>(CPV)) {
|
|
writeOperand(GV, Static);
|
|
break;
|
|
}
|
|
// FALL THROUGH
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "Unknown constant type: " << *CPV << "\n";
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
}
|
|
|
|
// Some constant expressions need to be casted back to the original types
|
|
// because their operands were casted to the expected type. This function takes
|
|
// care of detecting that case and printing the cast for the ConstantExpr.
|
|
bool CWriter::printConstExprCast(const llvm::ConstantExpr* CE, bool Static) {
|
|
bool NeedsExplicitCast = false;
|
|
llvm::Type *Ty = CE->getOperand(0)->getType();
|
|
bool TypeIsSigned = false;
|
|
switch (CE->getOpcode()) {
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::Mul:
|
|
// We need to cast integer arithmetic so that it is always performed
|
|
// as unsigned, to avoid undefined behavior on overflow.
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::URem:
|
|
case llvm::Instruction::UDiv: NeedsExplicitCast = true; break;
|
|
case llvm::Instruction::AShr:
|
|
case llvm::Instruction::SRem:
|
|
case llvm::Instruction::SDiv: NeedsExplicitCast = true; TypeIsSigned = true; break;
|
|
case llvm::Instruction::SExt:
|
|
Ty = CE->getType();
|
|
NeedsExplicitCast = true;
|
|
TypeIsSigned = true;
|
|
break;
|
|
case llvm::Instruction::ZExt:
|
|
case llvm::Instruction::Trunc:
|
|
case llvm::Instruction::FPTrunc:
|
|
case llvm::Instruction::FPExt:
|
|
case llvm::Instruction::UIToFP:
|
|
case llvm::Instruction::SIToFP:
|
|
case llvm::Instruction::FPToUI:
|
|
case llvm::Instruction::FPToSI:
|
|
case llvm::Instruction::PtrToInt:
|
|
case llvm::Instruction::IntToPtr:
|
|
case llvm::Instruction::BitCast:
|
|
Ty = CE->getType();
|
|
NeedsExplicitCast = true;
|
|
break;
|
|
default: break;
|
|
}
|
|
if (NeedsExplicitCast) {
|
|
Out << "((";
|
|
if (Ty->isIntegerTy() && Ty != llvm::Type::getInt1Ty(Ty->getContext()))
|
|
printSimpleType(Out, Ty, TypeIsSigned);
|
|
else
|
|
printType(Out, Ty); // not integer, sign doesn't matter
|
|
Out << ")(";
|
|
}
|
|
return NeedsExplicitCast;
|
|
}
|
|
|
|
// Print a constant assuming that it is the operand for a given Opcode. The
|
|
// opcodes that care about sign need to cast their operands to the expected
|
|
// type before the operation proceeds. This function does the casting.
|
|
void CWriter::printConstantWithCast(llvm::Constant* CPV, unsigned Opcode) {
|
|
|
|
// Extract the operand's type, we'll need it.
|
|
llvm::Type* OpTy = CPV->getType();
|
|
|
|
// Indicate whether to do the cast or not.
|
|
bool shouldCast = false;
|
|
bool typeIsSigned = false;
|
|
|
|
// Based on the Opcode for which this Constant is being written, determine
|
|
// the new type to which the operand should be casted by setting the value
|
|
// of OpTy. If we change OpTy, also set shouldCast to true so it gets
|
|
// casted below.
|
|
switch (Opcode) {
|
|
default:
|
|
// for most instructions, it doesn't matter
|
|
break;
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::Mul:
|
|
// We need to cast integer arithmetic so that it is always performed
|
|
// as unsigned, to avoid undefined behavior on overflow.
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::UDiv:
|
|
case llvm::Instruction::URem:
|
|
shouldCast = true;
|
|
break;
|
|
case llvm::Instruction::AShr:
|
|
case llvm::Instruction::SDiv:
|
|
case llvm::Instruction::SRem:
|
|
shouldCast = true;
|
|
typeIsSigned = true;
|
|
break;
|
|
}
|
|
|
|
// Write out the casted constant if we should, otherwise just write the
|
|
// operand.
|
|
if (shouldCast) {
|
|
Out << "((";
|
|
printSimpleType(Out, OpTy, typeIsSigned);
|
|
Out << ")";
|
|
printConstant(CPV, false);
|
|
Out << ")";
|
|
} else
|
|
printConstant(CPV, false);
|
|
}
|
|
|
|
std::string CWriter::GetValueName(const llvm::Value *Operand) {
|
|
|
|
// Resolve potential alias.
|
|
if (const llvm::GlobalAlias *GA = llvm::dyn_cast<llvm::GlobalAlias>(Operand)) {
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 /* LLVM 3.5+ */
|
|
if (const llvm::Value *V = GA->getAliasee())
|
|
#else /* <= LLVM 3.4 */
|
|
if (const llvm::Value *V = GA->resolveAliasedGlobal(false))
|
|
#endif
|
|
Operand = V;
|
|
}
|
|
|
|
// Mangle globals with the standard mangler interface for LLC compatibility.
|
|
if (const llvm::GlobalValue *GV = llvm::dyn_cast<llvm::GlobalValue>(Operand)) {
|
|
(void)GV;
|
|
//llvm::SmallString<128> Str;
|
|
//Mang->getNameWithPrefix(Str, GV, false);
|
|
//return CBEMangle(Str.str().str());
|
|
return CBEMangle(Operand->getName().str().c_str());
|
|
}
|
|
|
|
std::string Name = Operand->getName();
|
|
|
|
if (Name.empty()) { // Assign unique names to local temporaries.
|
|
unsigned &No = AnonValueNumbers[Operand];
|
|
if (No == 0)
|
|
No = ++NextAnonValueNumber;
|
|
Name = "tmp__" + llvm::utostr(No);
|
|
}
|
|
|
|
std::string VarName;
|
|
VarName.reserve(Name.capacity());
|
|
|
|
for (std::string::iterator I = Name.begin(), E = Name.end();
|
|
I != E; ++I) {
|
|
char ch = *I;
|
|
|
|
if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
|
|
(ch >= '0' && ch <= '9') || ch == '_')) {
|
|
char buffer[5];
|
|
sprintf(buffer, "_%x_", ch);
|
|
VarName += buffer;
|
|
} else
|
|
VarName += ch;
|
|
}
|
|
|
|
if (llvm::isa<llvm::BasicBlock>(Operand))
|
|
VarName += "_label";
|
|
else
|
|
VarName += "_";
|
|
|
|
return VarName;
|
|
}
|
|
|
|
/// writeInstComputationInline - Emit the computation for the specified
|
|
/// instruction inline, with no destination provided.
|
|
void CWriter::writeInstComputationInline(llvm::Instruction &I) {
|
|
// If this is a non-trivial bool computation, make sure to truncate down to
|
|
// a 1 bit value. This is important because we want "add i1 x, y" to return
|
|
// "0" when x and y are true, not "2" for example.
|
|
bool NeedBoolTrunc = false;
|
|
if (I.getType() == llvm::Type::getInt1Ty(I.getContext()) &&
|
|
!llvm::isa<llvm::ICmpInst>(I) && !llvm::isa<llvm::FCmpInst>(I))
|
|
NeedBoolTrunc = true;
|
|
|
|
if (NeedBoolTrunc)
|
|
Out << "((";
|
|
|
|
visit(I);
|
|
|
|
if (NeedBoolTrunc)
|
|
Out << ")&1)";
|
|
}
|
|
|
|
|
|
void CWriter::writeOperandInternal(llvm::Value *Operand, bool Static) {
|
|
if (llvm::Instruction *I = llvm::dyn_cast<llvm::Instruction>(Operand))
|
|
// Should we inline this instruction to build a tree?
|
|
if (isInlinableInst(*I) && !isDirectAlloca(I)) {
|
|
Out << '(';
|
|
writeInstComputationInline(*I);
|
|
Out << ')';
|
|
return;
|
|
}
|
|
|
|
llvm::Constant* CPV = llvm::dyn_cast<llvm::Constant>(Operand);
|
|
|
|
if (CPV && !llvm::isa<llvm::GlobalValue>(CPV))
|
|
printConstant(CPV, Static);
|
|
else
|
|
Out << GetValueName(Operand);
|
|
}
|
|
|
|
void CWriter::writeOperand(llvm::Value *Operand, bool Static) {
|
|
bool isAddressImplicit = isAddressExposed(Operand);
|
|
if (isAddressImplicit)
|
|
Out << "(&"; // Global variables are referenced as their addresses by llvm
|
|
|
|
writeOperandInternal(Operand, Static);
|
|
|
|
if (isAddressImplicit)
|
|
Out << ')';
|
|
}
|
|
|
|
// Some instructions need to have their result value casted back to the
|
|
// original types because their operands were casted to the expected type.
|
|
// This function takes care of detecting that case and printing the cast
|
|
// for the Instruction.
|
|
bool CWriter::writeInstructionCast(const llvm::Instruction &I) {
|
|
llvm::Type *Ty = I.getOperand(0)->getType();
|
|
switch (I.getOpcode()) {
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::Mul:
|
|
// We need to cast integer arithmetic so that it is always performed
|
|
// as unsigned, to avoid undefined behavior on overflow.
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::URem:
|
|
case llvm::Instruction::UDiv:
|
|
Out << "((";
|
|
printSimpleType(Out, Ty, false);
|
|
Out << ")(";
|
|
return true;
|
|
case llvm::Instruction::AShr:
|
|
case llvm::Instruction::SRem:
|
|
case llvm::Instruction::SDiv:
|
|
Out << "((";
|
|
printSimpleType(Out, Ty, true);
|
|
Out << ")(";
|
|
return true;
|
|
default: break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Write the operand with a cast to another type based on the Opcode being used.
|
|
// This will be used in cases where an instruction has specific type
|
|
// requirements (usually signedness) for its operands.
|
|
void CWriter::writeOperandWithCast(llvm::Value* Operand, unsigned Opcode) {
|
|
|
|
// Extract the operand's type, we'll need it.
|
|
llvm::Type* OpTy = Operand->getType();
|
|
|
|
// Indicate whether to do the cast or not.
|
|
bool shouldCast = false;
|
|
|
|
// Indicate whether the cast should be to a signed type or not.
|
|
bool castIsSigned = false;
|
|
|
|
// Based on the Opcode for which this Operand is being written, determine
|
|
// the new type to which the operand should be casted by setting the value
|
|
// of OpTy. If we change OpTy, also set shouldCast to true.
|
|
switch (Opcode) {
|
|
default:
|
|
// for most instructions, it doesn't matter
|
|
break;
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::Mul:
|
|
// We need to cast integer arithmetic so that it is always performed
|
|
// as unsigned, to avoid undefined behavior on overflow.
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::UDiv:
|
|
case llvm::Instruction::URem: // Cast to unsigned first
|
|
shouldCast = true;
|
|
castIsSigned = false;
|
|
break;
|
|
case llvm::Instruction::GetElementPtr:
|
|
case llvm::Instruction::AShr:
|
|
case llvm::Instruction::SDiv:
|
|
case llvm::Instruction::SRem: // Cast to signed first
|
|
shouldCast = true;
|
|
castIsSigned = true;
|
|
break;
|
|
}
|
|
|
|
// Write out the casted operand if we should, otherwise just write the
|
|
// operand.
|
|
if (shouldCast) {
|
|
Out << "((";
|
|
printSimpleType(Out, OpTy, castIsSigned);
|
|
Out << ")";
|
|
writeOperand(Operand);
|
|
Out << ")";
|
|
} else
|
|
writeOperand(Operand);
|
|
}
|
|
|
|
// Write the operand with a cast to another type based on the icmp predicate
|
|
// being used.
|
|
void CWriter::writeOperandWithCast(llvm::Value* Operand, const llvm::ICmpInst &Cmp) {
|
|
// This has to do a cast to ensure the operand has the right signedness.
|
|
// Also, if the operand is a pointer, we make sure to cast to an integer when
|
|
// doing the comparison both for signedness and so that the C compiler doesn't
|
|
// optimize things like "p < NULL" to false (p may contain an integer value
|
|
// f.e.).
|
|
bool shouldCast = Cmp.isRelational();
|
|
|
|
// Write out the casted operand if we should, otherwise just write the
|
|
// operand.
|
|
if (!shouldCast) {
|
|
writeOperand(Operand);
|
|
return;
|
|
}
|
|
|
|
// Should this be a signed comparison? If so, convert to signed.
|
|
bool castIsSigned = Cmp.isSigned();
|
|
|
|
// If the operand was a pointer, convert to a large integer type.
|
|
llvm::Type* OpTy = Operand->getType();
|
|
if (OpTy->isPointerTy())
|
|
OpTy = TD->getIntPtrType(Operand->getContext());
|
|
|
|
Out << "((";
|
|
printSimpleType(Out, OpTy, castIsSigned);
|
|
Out << ")";
|
|
writeOperand(Operand);
|
|
Out << ")";
|
|
}
|
|
|
|
// generateCompilerSpecificCode - This is where we add conditional compilation
|
|
// directives to cater to specific compilers as need be.
|
|
//
|
|
static void generateCompilerSpecificCode(llvm::formatted_raw_ostream& Out,
|
|
const llvm::DataLayout *TD) {
|
|
// We output GCC specific attributes to preserve 'linkonce'ness on globals.
|
|
// If we aren't being compiled with GCC, just drop these attributes.
|
|
Out << "#ifndef __GNUC__ /* Can only support \"linkonce\" vars with GCC */\n"
|
|
<< "#define __attribute__(X)\n"
|
|
<< "#endif\n\n";
|
|
|
|
// On Mac OS X, "external weak" is spelled "__attribute__((weak_import))".
|
|
Out << "#if defined(__GNUC__) && defined(__APPLE_CC__)\n"
|
|
<< "#define __EXTERNAL_WEAK__ __attribute__((weak_import))\n"
|
|
<< "#elif defined(__GNUC__)\n"
|
|
<< "#define __EXTERNAL_WEAK__ __attribute__((weak))\n"
|
|
<< "#else\n"
|
|
<< "#define __EXTERNAL_WEAK__\n"
|
|
<< "#endif\n\n";
|
|
|
|
// For now, turn off the weak linkage attribute on Mac OS X. (See above.)
|
|
Out << "#if defined(__GNUC__) && defined(__APPLE_CC__)\n"
|
|
<< "#define __ATTRIBUTE_WEAK__\n"
|
|
<< "#elif defined(__GNUC__)\n"
|
|
<< "#define __ATTRIBUTE_WEAK__ __attribute__((weak))\n"
|
|
<< "#else\n"
|
|
<< "#define __ATTRIBUTE_WEAK__\n"
|
|
<< "#endif\n\n";
|
|
|
|
// Add hidden visibility support. FIXME: APPLE_CC?
|
|
Out << "#if defined(__GNUC__)\n"
|
|
<< "#define __HIDDEN__ __attribute__((visibility(\"hidden\")))\n"
|
|
<< "#endif\n\n";
|
|
|
|
// Define NaN and Inf as GCC builtins if using GCC, as 0 otherwise
|
|
// From the GCC documentation:
|
|
//
|
|
// double __builtin_nan (const char *str)
|
|
//
|
|
// This is an implementation of the ISO C99 function nan.
|
|
//
|
|
// Since ISO C99 defines this function in terms of strtod, which we do
|
|
// not implement, a description of the parsing is in order. The string is
|
|
// parsed as by strtol; that is, the base is recognized by leading 0 or
|
|
// 0x prefixes. The number parsed is placed in the significand such that
|
|
// the least significant bit of the number is at the least significant
|
|
// bit of the significand. The number is truncated to fit the significand
|
|
// field provided. The significand is forced to be a quiet NaN.
|
|
//
|
|
// This function, if given a string literal, is evaluated early enough
|
|
// that it is considered a compile-time constant.
|
|
//
|
|
// float __builtin_nanf (const char *str)
|
|
//
|
|
// Similar to __builtin_nan, except the return type is float.
|
|
//
|
|
// double __builtin_inf (void)
|
|
//
|
|
// Similar to __builtin_huge_val, except a warning is generated if the
|
|
// target floating-point format does not support infinities. This
|
|
// function is suitable for implementing the ISO C99 macro INFINITY.
|
|
//
|
|
// float __builtin_inff (void)
|
|
//
|
|
// Similar to __builtin_inf, except the return type is float.
|
|
Out << "#if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER)\n"
|
|
<< "#define LLVM_NAN(NanStr) __builtin_nan(NanStr) /* Double */\n"
|
|
<< "#define LLVM_NANF(NanStr) __builtin_nanf(NanStr) /* Float */\n"
|
|
<< "#define LLVM_NANS(NanStr) __builtin_nans(NanStr) /* Double */\n"
|
|
<< "#define LLVM_NANSF(NanStr) __builtin_nansf(NanStr) /* Float */\n"
|
|
<< "#define LLVM_INF __builtin_inf() /* Double */\n"
|
|
<< "#define LLVM_INFF __builtin_inff() /* Float */\n"
|
|
<< "//#define LLVM_PREFETCH(addr,rw,locality) "
|
|
"__builtin_prefetch(addr,rw,locality)\n"
|
|
<< "//#define __ATTRIBUTE_CTOR__ __attribute__((constructor))\n"
|
|
<< "//#define __ATTRIBUTE_DTOR__ __attribute__((destructor))\n"
|
|
<< "#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)\n"
|
|
<< "#include <limits>\n"
|
|
<< "#define LLVM_NAN(NanStr) std::numeric_limits<double>::quiet_NaN()\n"
|
|
<< "#define LLVM_NANF(NanStr) std::numeric_limits<float>::quiet_NaN()\n"
|
|
<< "#define LLVM_NANS(NanStr) std::numeric_limits<double>::signaling_NaN()\n"
|
|
<< "#define LLVM_NANSF(NanStr) std::numeric_limits<float>::signaling_NaN()\n"
|
|
<< "#define LLVM_INF std::numeric_limits<double>::infinity()\n"
|
|
<< "#define LLVM_INFF std::numeric_limits<float>::infinity()\n"
|
|
<< "//#define LLVM_PREFETCH(addr,rw,locality) /* PREFETCH */\n"
|
|
<< "//#define __ATTRIBUTE_CTOR__\n"
|
|
<< "//#define __ATTRIBUTE_DTOR__\n"
|
|
<< "#else\n"
|
|
<< "#error \"Not MSVC, clang, or g++?\"\n"
|
|
<< "#endif\n\n";
|
|
|
|
// LLVM_ASM() is used to define mapping of the symbol to a different name,
|
|
// this is expected to be MacOS-only feature. So defining it only for
|
|
// gcc and clang (Intel Compiler on Linux/MacOS is also ok).
|
|
// For example, this feature is required to translate symbols described in
|
|
// "Symbol Variants Release Notes" document (on Apple website).
|
|
Out << "#if (defined(__GNUC__) || defined(__clang__))\n"
|
|
<< "#define LLVM_ASM(X) __asm(X)\n"
|
|
<< "#endif\n\n";
|
|
|
|
Out << "#if defined(__clang__) || defined(__INTEL_COMPILER) || "
|
|
"(__GNUC__ < 4) /* Old GCCs, or compilers not GCC */ \n"
|
|
<< "#define __builtin_stack_save() 0 /* not implemented */\n"
|
|
<< "#define __builtin_stack_restore(X) /* noop */\n"
|
|
<< "#endif\n\n";
|
|
|
|
#if 0
|
|
// Output typedefs for 128-bit integers. If these are needed with a
|
|
// 32-bit target or with a C compiler that doesn't support mode(TI),
|
|
// more drastic measures will be needed.
|
|
Out << "#if __GNUC__ && __LP64__ /* 128-bit integer types */\n"
|
|
<< "typedef int __attribute__((mode(TI))) llvmInt128;\n"
|
|
<< "typedef unsigned __attribute__((mode(TI))) llvmUInt128;\n"
|
|
<< "#endif\n\n";
|
|
#endif
|
|
|
|
// Output target-specific code that should be inserted into main.
|
|
Out << "#define CODE_FOR_MAIN() /* Any target-specific code for main()*/\n";
|
|
}
|
|
|
|
/// FindStaticTors - Given a static ctor/dtor list, unpack its contents into
|
|
/// the StaticTors set.
|
|
static void FindStaticTors(llvm::GlobalVariable *GV, std::set<llvm::Function*> &StaticTors){
|
|
llvm::ConstantArray *InitList = llvm::dyn_cast<llvm::ConstantArray>(GV->getInitializer());
|
|
if (!InitList) return;
|
|
|
|
for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i)
|
|
if (llvm::ConstantStruct *CS = llvm::dyn_cast<llvm::ConstantStruct>(InitList->getOperand(i))){
|
|
if (CS->getNumOperands() != 2) return; // Not array of 2-element structs.
|
|
|
|
if (CS->getOperand(1)->isNullValue())
|
|
return; // Found a null terminator, exit printing.
|
|
llvm::Constant *FP = CS->getOperand(1);
|
|
if (llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(FP))
|
|
if (CE->isCast())
|
|
FP = CE->getOperand(0);
|
|
if (llvm::Function *F = llvm::dyn_cast<llvm::Function>(FP))
|
|
StaticTors.insert(F);
|
|
}
|
|
}
|
|
|
|
enum SpecialGlobalClass {
|
|
NotSpecial = 0,
|
|
GlobalCtors, GlobalDtors,
|
|
NotPrinted
|
|
};
|
|
|
|
/// getGlobalVariableClass - If this is a global that is specially recognized
|
|
/// by LLVM, return a code that indicates how we should handle it.
|
|
static SpecialGlobalClass getGlobalVariableClass(const llvm::GlobalVariable *GV) {
|
|
// If this is a global ctors/dtors list, handle it now.
|
|
if (GV->hasAppendingLinkage() && GV->use_empty()) {
|
|
if (GV->getName() == "llvm.global_ctors")
|
|
return GlobalCtors;
|
|
else if (GV->getName() == "llvm.global_dtors")
|
|
return GlobalDtors;
|
|
}
|
|
|
|
// Otherwise, if it is other metadata, don't print it. This catches things
|
|
// like debug information.
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_8 /* LLVM 3.5-3.8 */
|
|
// Here we compare char *
|
|
if (!strcmp(GV->getSection(), "llvm.metadata"))
|
|
#else
|
|
// Here we compare strings
|
|
if (GV->getSection() == "llvm.metadata")
|
|
#endif
|
|
return NotPrinted;
|
|
|
|
return NotSpecial;
|
|
}
|
|
|
|
// PrintEscapedString - Print each character of the specified string, escaping
|
|
// it if it is not printable or if it is an escape char.
|
|
static void PrintEscapedString(const char *Str, unsigned Length,
|
|
llvm::raw_ostream &Out) {
|
|
for (unsigned i = 0; i != Length; ++i) {
|
|
unsigned char C = Str[i];
|
|
if (isprint(C) && C != '\\' && C != '"')
|
|
Out << C;
|
|
else if (C == '\\')
|
|
Out << "\\\\";
|
|
else if (C == '\"')
|
|
Out << "\\\"";
|
|
else if (C == '\t')
|
|
Out << "\\t";
|
|
else
|
|
Out << "\\x" << llvm::hexdigit(C >> 4) << llvm::hexdigit(C & 0x0F);
|
|
}
|
|
}
|
|
|
|
// PrintEscapedString - Print each character of the specified string, escaping
|
|
// it if it is not printable or if it is an escape char.
|
|
static void PrintEscapedString(const std::string &Str, llvm::raw_ostream &Out) {
|
|
PrintEscapedString(Str.c_str(), Str.size(), Out);
|
|
}
|
|
|
|
bool CWriter::doInitialization(llvm::Module &M) {
|
|
llvm::FunctionPass::doInitialization(M);
|
|
|
|
// Initialize
|
|
TheModule = &M;
|
|
|
|
TD = new llvm::DataLayout(&M);
|
|
IL = new llvm::IntrinsicLowering(*TD);
|
|
IL->AddPrototypes(M);
|
|
|
|
#if 0
|
|
std::string Triple = TheModule->getTargetTriple();
|
|
if (Triple.empty())
|
|
Triple = llvm::sys::getDefaultTargetTriple();
|
|
|
|
std::string E;
|
|
if (const llvm::Target *Match = llvm::TargetRegistry::lookupTarget(Triple, E))
|
|
TAsm = Match->createMCAsmInfo(Triple);
|
|
#endif
|
|
TAsm = new CBEMCAsmInfo();
|
|
MRI = new llvm::MCRegisterInfo();
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_4 // LLVM 3.4+
|
|
TCtx = new llvm::MCContext(TAsm, MRI, NULL);
|
|
#else
|
|
TCtx = new llvm::MCContext(*TAsm, *MRI, NULL);
|
|
#endif
|
|
//Mang = new llvm::Mangler(*TCtx, *TD);
|
|
|
|
// Keep track of which functions are static ctors/dtors so they can have
|
|
// an attribute added to their prototypes.
|
|
std::set<llvm::Function*> StaticCtors, StaticDtors;
|
|
for (llvm::Module::global_iterator I = M.global_begin(), E = M.global_end();
|
|
I != E; ++I) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
switch (getGlobalVariableClass(I)) {
|
|
#else /* LLVM 3.8+ */
|
|
switch (getGlobalVariableClass(&*I)) {
|
|
#endif
|
|
default: break;
|
|
case GlobalCtors:
|
|
FindStaticTors(&*I, StaticCtors);
|
|
break;
|
|
case GlobalDtors:
|
|
FindStaticTors(&*I, StaticDtors);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Out << "/*******************************************************************\n";
|
|
Out << " This file has been automatically generated by ispc\n";
|
|
Out << " DO NOT EDIT THIS FILE DIRECTLY\n";
|
|
Out << " *******************************************************************/\n\n";
|
|
|
|
Out << "/* Provide Declarations */\n";
|
|
Out << "#include <stdarg.h>\n"; // Varargs support
|
|
Out << "#include <setjmp.h>\n"; // Unwind support
|
|
Out << "#include <limits.h>\n"; // With overflow intrinsics support.
|
|
Out << "#include <stdlib.h>\n";
|
|
Out << "#ifdef _MSC_VER\n";
|
|
Out << " #define NOMINMAX\n";
|
|
Out << " #include <windows.h>\n";
|
|
Out << "#endif // _MSC_VER\n";
|
|
Out << "#include <stdlib.h>\n";
|
|
Out << "#include <stdint.h>\n";
|
|
Out << "/* get a declaration for alloca */\n";
|
|
Out << "#ifdef _MSC_VER\n";
|
|
Out << " #include <malloc.h>\n";
|
|
Out << " #define alloca _alloca\n";
|
|
Out << "#else\n";
|
|
Out << " #include <alloca.h>\n";
|
|
Out << "#endif\n\n";
|
|
|
|
if (g->opt.fastMath) {
|
|
Out << "#define ISPC_FAST_MATH 1\n";
|
|
} else {
|
|
Out << "#undef ISPC_FAST_MATH\n";
|
|
}
|
|
|
|
if (g->opt.forceAlignedMemory) {
|
|
Out << "#define ISPC_FORCE_ALIGNED_MEMORY\n";
|
|
}
|
|
|
|
Out << "#include \"" << includeName << "\"\n";
|
|
|
|
Out << "\n/* Basic Library Function Declarations */\n";
|
|
Out << "extern \"C\" {\n";
|
|
Out << "int puts(unsigned char *);\n";
|
|
Out << "unsigned int putchar(unsigned int);\n";
|
|
Out << "int fflush(void *);\n";
|
|
Out << "int printf(const unsigned char *, ...);\n";
|
|
Out << "uint8_t *memcpy(uint8_t *, uint8_t *, uint64_t );\n";
|
|
Out << "uint8_t *memset(uint8_t *, uint8_t, uint64_t );\n";
|
|
Out << "void memset_pattern16(void *, const void *, uint64_t );\n";
|
|
Out << "}\n\n";
|
|
|
|
generateCompilerSpecificCode(Out, TD);
|
|
|
|
// Provide a definition for `bool' if not compiling with a C++ compiler.
|
|
Out << "\n"
|
|
<< "#ifndef __cplusplus\ntypedef unsigned char bool;\n#endif\n"
|
|
|
|
<< "\n\n/* Support for floating point constants */\n"
|
|
<< "typedef uint64_t ConstantDoubleTy;\n"
|
|
<< "typedef uint32_t ConstantFloatTy;\n"
|
|
<< "typedef struct { unsigned long long f1; unsigned short f2; "
|
|
"unsigned short pad[3]; } ConstantFP80Ty;\n"
|
|
// This is used for both kinds of 128-bit long double; meaning differs.
|
|
<< "typedef struct { uint64_t f1, f2; } ConstantFP128Ty;\n"
|
|
<< "\n\n/* Global Declarations */\n\n";
|
|
|
|
// First output all the declarations for the program, because C requires
|
|
// Functions & globals to be declared before they are used.
|
|
//
|
|
if (!M.getModuleInlineAsm().empty()) {
|
|
Out << "/* Module asm statements */\n"
|
|
<< "asm(";
|
|
|
|
// Split the string into lines, to make it easier to read the .ll file.
|
|
std::string Asm = M.getModuleInlineAsm();
|
|
size_t CurPos = 0;
|
|
size_t NewLine = Asm.find_first_of('\n', CurPos);
|
|
while (NewLine != std::string::npos) {
|
|
// We found a newline, print the portion of the asm string from the
|
|
// last newline up to this newline.
|
|
Out << "\"";
|
|
PrintEscapedString(std::string(Asm.begin()+CurPos, Asm.begin()+NewLine),
|
|
Out);
|
|
Out << "\\n\"\n";
|
|
CurPos = NewLine+1;
|
|
NewLine = Asm.find_first_of('\n', CurPos);
|
|
}
|
|
Out << "\"";
|
|
PrintEscapedString(std::string(Asm.begin()+CurPos, Asm.end()), Out);
|
|
Out << "\");\n"
|
|
<< "/* End Module asm statements */\n";
|
|
}
|
|
|
|
// Loop over the symbol table, emitting all named constants.
|
|
printModuleTypes();
|
|
|
|
// Global variable declarations...
|
|
if (!M.global_empty()) {
|
|
Out << "\n/* External Global Variable Declarations */\n";
|
|
for (llvm::Module::global_iterator I = M.global_begin(), E = M.global_end();
|
|
I != E; ++I) {
|
|
|
|
if (I->hasExternalLinkage() || I->hasExternalWeakLinkage() ||
|
|
I->hasCommonLinkage())
|
|
Out << "extern ";
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
else if (I->hasDLLImportStorageClass())
|
|
#else
|
|
else if (I->hasDLLImportLinkage())
|
|
#endif
|
|
Out << "__declspec(dllimport) ";
|
|
else
|
|
continue; // Internal Global
|
|
|
|
// Thread Local Storage
|
|
if (I->isThreadLocal())
|
|
Out << "__thread ";
|
|
|
|
printType(Out, I->getType()->getElementType(), false, GetValueName(&*I));
|
|
|
|
if (I->hasExternalWeakLinkage())
|
|
Out << " __EXTERNAL_WEAK__";
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
|
|
// Output the global variable declarations
|
|
if (!M.global_empty()) {
|
|
Out << "\n\n/* Global Variable Declarations */\n";
|
|
for (llvm::Module::global_iterator I = M.global_begin(), E = M.global_end();
|
|
I != E; ++I)
|
|
if (!I->isDeclaration()) {
|
|
// Ignore special globals, such as debug info.
|
|
if (getGlobalVariableClass(&*I))
|
|
continue;
|
|
|
|
if (I->hasLocalLinkage())
|
|
continue;
|
|
else
|
|
Out << "extern ";
|
|
|
|
// Thread Local Storage
|
|
if (I->isThreadLocal())
|
|
Out << "__thread ";
|
|
|
|
printType(Out, I->getType()->getElementType(), false,
|
|
GetValueName(&*I));
|
|
|
|
if (I->hasLinkOnceLinkage())
|
|
Out << " __attribute__((common))";
|
|
else if (I->hasCommonLinkage()) // FIXME is this right?
|
|
Out << " __ATTRIBUTE_WEAK__";
|
|
else if (I->hasWeakLinkage())
|
|
Out << " __ATTRIBUTE_WEAK__";
|
|
else if (I->hasExternalWeakLinkage())
|
|
Out << " __EXTERNAL_WEAK__";
|
|
if (I->hasHiddenVisibility())
|
|
Out << " __HIDDEN__";
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
|
|
// Function declarations
|
|
Out << "\n/* Function Declarations */\n";
|
|
Out << "extern \"C\" {\n";
|
|
|
|
// Store the intrinsics which will be declared/defined below.
|
|
llvm::SmallVector<const llvm::Function*, 8> intrinsicsToDefine;
|
|
|
|
for (llvm::Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
|
|
// Don't print declarations for intrinsic functions.
|
|
// Store the used intrinsics, which need to be explicitly defined.
|
|
if (I->isIntrinsic()) {
|
|
switch (I->getIntrinsicID()) {
|
|
default:
|
|
break;
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
intrinsicsToDefine.push_back(I);
|
|
#else /* LLVM 3.8+ */
|
|
intrinsicsToDefine.push_back(&*I);
|
|
#endif
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (I->getName() == "setjmp" || I->getName() == "abort" ||
|
|
I->getName() == "longjmp" || I->getName() == "_setjmp" ||
|
|
I->getName() == "memset" || I->getName() == "memset_pattern16" ||
|
|
I->getName() == "puts" ||
|
|
I->getName() == "printf" || I->getName() == "putchar" ||
|
|
I->getName() == "fflush" ||
|
|
// Memory allocation
|
|
I->getName() == "malloc" ||
|
|
I->getName() == "posix_memalign" ||
|
|
I->getName() == "free" ||
|
|
I->getName() == "_aligned_malloc" ||
|
|
I->getName() == "_aligned_free"
|
|
)
|
|
continue;
|
|
|
|
// Don't redeclare ispc's own intrinsics
|
|
std::string name = I->getName();
|
|
if (name.size() > 2 && name[0] == '_' && name[1] == '_')
|
|
continue;
|
|
|
|
if (I->hasExternalWeakLinkage())
|
|
Out << "extern ";
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
printFunctionSignature(I, true);
|
|
#else /* LLVM 3.8+ */
|
|
printFunctionSignature(&*I, true);
|
|
#endif
|
|
if (I->hasWeakLinkage() || I->hasLinkOnceLinkage())
|
|
Out << " __ATTRIBUTE_WEAK__";
|
|
if (I->hasExternalWeakLinkage())
|
|
Out << " __EXTERNAL_WEAK__";
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
if (StaticCtors.count(I))
|
|
Out << " __ATTRIBUTE_CTOR__";
|
|
if (StaticDtors.count(I))
|
|
#else /* LLVM 3.8+ */
|
|
if (StaticCtors.count(&*I))
|
|
Out << " __ATTRIBUTE_CTOR__";
|
|
if (StaticDtors.count(&*I))
|
|
#endif
|
|
Out << " __ATTRIBUTE_DTOR__";
|
|
if (I->hasHiddenVisibility())
|
|
Out << " __HIDDEN__";
|
|
|
|
// This is MacOS specific feature, this should not appear on other platforms.
|
|
if (I->hasName() && I->getName()[0] == 1)
|
|
Out << " LLVM_ASM(\"" << I->getName().substr(1) << "\")";
|
|
|
|
Out << ";\n";
|
|
}
|
|
Out << "}\n\n";
|
|
|
|
if (!M.empty())
|
|
Out << "\n\n/* Function Bodies */\n";
|
|
|
|
// Emit some helper functions for dealing with FCMP instruction's
|
|
// predicates
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ord(A X, B Y) { ";
|
|
Out << "return X == X && Y == Y; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_uno(A X, B Y) { ";
|
|
Out << "return X != X || Y != Y; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ueq(A X, B Y) { ";
|
|
Out << "return X == Y || llvm_fcmp_uno(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_une(A X, B Y) { ";
|
|
Out << "return X != Y; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ult(A X, B Y) { ";
|
|
Out << "return X < Y || llvm_fcmp_uno(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ugt(A X, B Y) { ";
|
|
Out << "return X > Y || llvm_fcmp_uno(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ule(A X, B Y) { ";
|
|
Out << "return X <= Y || llvm_fcmp_uno(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_uge(A X, B Y) { ";
|
|
Out << "return X >= Y || llvm_fcmp_uno(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_oeq(A X, B Y) { ";
|
|
Out << "return X == Y ; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_one(A X, B Y) { ";
|
|
Out << "return X != Y && llvm_fcmp_ord(X, Y); }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_olt(A X, B Y) { ";
|
|
Out << "return X < Y ; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ogt(A X, B Y) { ";
|
|
Out << "return X > Y ; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_ole(A X, B Y) { ";
|
|
Out << "return X <= Y ; }\n";
|
|
Out << "template <typename A, typename B> static inline int llvm_fcmp_oge(A X, B Y) { ";
|
|
Out << "return X >= Y ; }\n";
|
|
Out << "template <typename A> A *Memset(A *ptr, int count, size_t len) { ";
|
|
Out << "return (A *)memset(ptr, count, len); }\n";
|
|
|
|
// Emit definitions of the intrinsics.
|
|
for (llvm::SmallVector<const llvm::Function*, 8>::const_iterator
|
|
I = intrinsicsToDefine.begin(),
|
|
E = intrinsicsToDefine.end(); I != E; ++I) {
|
|
printIntrinsicDefinition(**I, Out);
|
|
}
|
|
|
|
// Output the global variable definitions and contents...
|
|
if (!M.global_empty()) {
|
|
Out << "\n\n/* Global Variable Definitions and Initialization */\n";
|
|
for (llvm::Module::global_iterator I = M.global_begin(), E = M.global_end();
|
|
I != E; ++I)
|
|
if (!I->isDeclaration()) {
|
|
// Ignore special globals, such as debug info.
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
if (getGlobalVariableClass(I))
|
|
#else /* LLVM 3.8+ */
|
|
if (getGlobalVariableClass(&*I))
|
|
#endif
|
|
continue;
|
|
|
|
if (I->hasLocalLinkage())
|
|
Out << "static ";
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
else if (I->hasDLLImportStorageClass()) Out << "__declspec(dllimport) ";
|
|
else if (I->hasDLLExportStorageClass()) Out << "__declspec(dllexport) ";
|
|
#else
|
|
else if (I->hasDLLImportLinkage()) Out << "__declspec(dllimport) ";
|
|
else if (I->hasDLLExportLinkage()) Out << "__declspec(dllexport) ";
|
|
#endif
|
|
// Thread Local Storage
|
|
if (I->isThreadLocal())
|
|
Out << "__thread ";
|
|
|
|
printType(Out, I->getType()->getElementType(), false,
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
GetValueName(I));
|
|
#else /* LLVM 3.8+ */
|
|
GetValueName(&*I));
|
|
#endif
|
|
|
|
if (I->hasLinkOnceLinkage())
|
|
Out << " __attribute__((common))";
|
|
else if (I->hasWeakLinkage())
|
|
Out << " __ATTRIBUTE_WEAK__";
|
|
else if (I->hasCommonLinkage())
|
|
Out << " __ATTRIBUTE_WEAK__";
|
|
|
|
if (I->hasHiddenVisibility())
|
|
Out << " __HIDDEN__";
|
|
|
|
// If the initializer is not null, emit the initializer. If it is null,
|
|
// we try to avoid emitting large amounts of zeros. The problem with
|
|
// this, however, occurs when the variable has weak linkage. In this
|
|
// case, the assembler will complain about the variable being both weak
|
|
// and common, so we disable this optimization.
|
|
// FIXME common linkage should avoid this problem.
|
|
if (!I->getInitializer()->isNullValue()) {
|
|
Out << " = " ;
|
|
|
|
// vec16_i64 should be handled separately
|
|
if (is_vec16_i64_ty(I->getType()->getElementType())) {
|
|
Out << "/* vec16_i64 should be loaded carefully on knc */\n";
|
|
Out << "\n#if defined(KNC) \n";
|
|
Out << "hilo2zmm";
|
|
Out << "\n#endif \n";
|
|
}
|
|
|
|
Out << "(";
|
|
writeOperand(I->getInitializer(), false);
|
|
Out << ")";
|
|
} else if (I->hasWeakLinkage()) {
|
|
// We have to specify an initializer, but it doesn't have to be
|
|
// complete. If the value is an aggregate, print out { 0 }, and let
|
|
// the compiler figure out the rest of the zeros.
|
|
Out << " = " ;
|
|
if (I->getInitializer()->getType()->isStructTy() ||
|
|
I->getInitializer()->getType()->isVectorTy()) {
|
|
Out << "{ 0 }";
|
|
} else if (I->getInitializer()->getType()->isArrayTy()) {
|
|
// As with structs and vectors, but with an extra set of braces
|
|
// because arrays are wrapped in structs.
|
|
Out << "{ { 0 } }";
|
|
} else {
|
|
// Just print it out normally.
|
|
writeOperand(I->getInitializer(), false);
|
|
}
|
|
}
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/// Output all floating point constants that cannot be printed accurately...
|
|
void CWriter::printFloatingPointConstants(llvm::Function &F) {
|
|
// Scan the module for floating point constants. If any FP constant is used
|
|
// in the function, we want to redirect it here so that we do not depend on
|
|
// the precision of the printed form, unless the printed form preserves
|
|
// precision.
|
|
//
|
|
for (constant_scanner::constant_iterator I = constant_scanner::constant_begin(&F),
|
|
E = constant_scanner::constant_end(&F); I != E; ++I)
|
|
printFloatingPointConstants(*I);
|
|
|
|
Out << '\n';
|
|
}
|
|
|
|
void CWriter::printFloatingPointConstants(const llvm::Constant *C) {
|
|
// If this is a constant expression, recursively check for constant fp values.
|
|
if (const llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(C)) {
|
|
for (unsigned i = 0, e = CE->getNumOperands(); i != e; ++i)
|
|
printFloatingPointConstants(CE->getOperand(i));
|
|
return;
|
|
}
|
|
|
|
// Otherwise, check for a FP constant that we need to print.
|
|
const llvm::ConstantFP *FPC = llvm::dyn_cast<llvm::ConstantFP>(C);
|
|
if (FPC == 0 ||
|
|
// Do not put in FPConstantMap if safe.
|
|
isFPCSafeToPrint(FPC) ||
|
|
// Already printed this constant?
|
|
FPConstantMap.count(FPC))
|
|
return;
|
|
|
|
FPConstantMap[FPC] = FPCounter; // Number the FP constants
|
|
|
|
if (FPC->getType() == llvm::Type::getDoubleTy(FPC->getContext())) {
|
|
double Val = FPC->getValueAPF().convertToDouble();
|
|
uint64_t i = FPC->getValueAPF().bitcastToAPInt().getZExtValue();
|
|
Out << "static const ConstantDoubleTy FPConstant" << FPCounter++
|
|
<< " = 0x" << llvm::utohexstr(i)
|
|
<< "ULL; /* " << Val << " */\n";
|
|
} else if (FPC->getType() == llvm::Type::getFloatTy(FPC->getContext())) {
|
|
float Val = FPC->getValueAPF().convertToFloat();
|
|
uint32_t i = (uint32_t)FPC->getValueAPF().bitcastToAPInt().
|
|
getZExtValue();
|
|
Out << "static const ConstantFloatTy FPConstant" << FPCounter++
|
|
<< " = 0x" << llvm::utohexstr(i)
|
|
<< "U; /* " << Val << " */\n";
|
|
} else if (FPC->getType() == llvm::Type::getX86_FP80Ty(FPC->getContext())) {
|
|
// api needed to prevent premature destruction
|
|
llvm::APInt api = FPC->getValueAPF().bitcastToAPInt();
|
|
const uint64_t *p = api.getRawData();
|
|
Out << "static const ConstantFP80Ty FPConstant" << FPCounter++
|
|
<< " = { 0x" << llvm::utohexstr(p[0])
|
|
<< "ULL, 0x" << llvm::utohexstr((uint16_t)p[1]) << ",{0,0,0}"
|
|
<< "}; /* Long double constant */\n";
|
|
} else if (FPC->getType() == llvm::Type::getPPC_FP128Ty(FPC->getContext()) ||
|
|
FPC->getType() == llvm::Type::getFP128Ty(FPC->getContext())) {
|
|
llvm::APInt api = FPC->getValueAPF().bitcastToAPInt();
|
|
const uint64_t *p = api.getRawData();
|
|
Out << "static const ConstantFP128Ty FPConstant" << FPCounter++
|
|
<< " = { 0x"
|
|
<< llvm::utohexstr(p[0]) << ", 0x" << llvm::utohexstr(p[1])
|
|
<< "}; /* Long double constant */\n";
|
|
|
|
} else {
|
|
llvm_unreachable("Unknown float type!");
|
|
}
|
|
}
|
|
|
|
|
|
// For any vector constants, generate code to declare static const arrays
|
|
// with their element values. Doing so allows us to emit aligned vector
|
|
// loads to get their values, rather than tediously inserting the
|
|
// individual values into the vector.
|
|
void CWriter::printVectorConstants(llvm::Function &F) {
|
|
for (constant_scanner::constant_iterator I = constant_scanner::constant_begin(&F),
|
|
E = constant_scanner::constant_end(&F); I != E; ++I) {
|
|
const llvm::ConstantDataVector *CDV = llvm::dyn_cast<llvm::ConstantDataVector>(*I);
|
|
if (CDV == NULL)
|
|
continue;
|
|
|
|
// Don't bother if this is a splat of the same value; a (more
|
|
// efficient?) __splat_* call will be generated for these.
|
|
if (CDV->getSplatValue() != NULL)
|
|
continue;
|
|
|
|
// Don't align to anything more than 64 bytes
|
|
int alignment = 4 * std::min(vectorWidth, 16);
|
|
|
|
Out << "static const ";
|
|
printSimpleType(Out, CDV->getElementType(), true, "");
|
|
Out << "__attribute__ ((aligned(" << alignment << "))) ";
|
|
Out << "VectorConstant" << VectorConstantIndex << "[] = { ";
|
|
for (int i = 0; i < (int)CDV->getNumElements(); ++i) {
|
|
printConstant(CDV->getElementAsConstant(i), false);
|
|
Out << ", ";
|
|
}
|
|
Out << " };\n";
|
|
|
|
VectorConstantMap[CDV] = VectorConstantIndex++;
|
|
}
|
|
Out << "\n";
|
|
}
|
|
|
|
/// printSymbolTable - Run through symbol table looking for type names. If a
|
|
/// type name is found, emit its declaration...
|
|
///
|
|
void CWriter::printModuleTypes() {
|
|
Out << "\n/* Helper union for bitcasts */\n";
|
|
Out << "typedef union {\n";
|
|
Out << " unsigned int Int32;\n";
|
|
Out << " unsigned long long Int64;\n";
|
|
Out << " float Float;\n";
|
|
Out << " double Double;\n";
|
|
Out << "} llvmBitCastUnion;\n";
|
|
Out << "\n/* This is special class, designed for operations with long int.*/ \n";
|
|
Out << "namespace { \n";
|
|
Out << "template <int num_bits> \n";
|
|
Out << "struct iN { \n";
|
|
Out << " int num[num_bits / (sizeof (int) * 8)]; \n";
|
|
Out << " \n";
|
|
Out << " iN () {} \n";
|
|
Out << " \n";
|
|
Out << " iN (const char *val) { \n";
|
|
Out << " if (val == NULL) \n";
|
|
Out << " return; \n";
|
|
Out << " int length = num_bits / (sizeof (int) * 8); \n";
|
|
Out << " int val_len = 0; \n";
|
|
Out << " for (val_len = 0; val[val_len]; (val_len)++); \n";
|
|
Out << " for (int i = 0; (i < val_len && i < num_bits); i++) \n";
|
|
Out << " num[i / (sizeof (int) * 8)] = (num[i / (sizeof (int) * 8)] << 1) | (val[i] - '0'); \n";
|
|
Out << " } \n";
|
|
Out << " \n";
|
|
Out << " ~iN () {} \n";
|
|
Out << " \n";
|
|
Out << " iN operator >> (const iN rhs) { \n";
|
|
Out << " iN res; \n";
|
|
Out << " int length = num_bits / (sizeof (int) * 8); \n";
|
|
Out << " int cells_shift = rhs.num[0] / (sizeof(int) * 8); \n";
|
|
Out << " int small_shift = rhs.num[0] % (sizeof(int) * 8); \n";
|
|
Out << " for (int i = 0; i < (length - cells_shift); i++) \n";
|
|
Out << " res.num[i] = this->num[cells_shift + i]; \n";
|
|
Out << " for (int i = 0; i < length - 1; i++) { \n";
|
|
Out << " res.num[i] = this->num[i] >> small_shift; \n";
|
|
Out << " res.num[i] = ((this->num[i + 1] << ((sizeof(int) * 8) - small_shift))) | res.num[i];\n";
|
|
Out << " } \n";
|
|
Out << " res.num[length - 1] = res.num[length - 1] >> small_shift; \n";
|
|
Out << " return res; \n";
|
|
Out << " } \n";
|
|
Out << " \n";
|
|
Out << " iN operator & (iN rhs) { \n";
|
|
Out << " iN res; \n";
|
|
Out << " int length = num_bits / (sizeof (int) * 8); \n";
|
|
Out << " for (int i = 0; i < length; i++) \n";
|
|
Out << " res.num[i] = (this->num[i]) & (rhs.num[i]); \n";
|
|
Out << " return res; \n";
|
|
Out << " } \n";
|
|
Out << " \n";
|
|
Out << " operator uint32_t() { return this->num[0]; } \n";
|
|
Out << " \n";
|
|
Out << " template <class T> \n";
|
|
Out << " friend iN<num_bits> __cast_bits(iN<num_bits> to, T from) { \n";
|
|
Out << " for (int i = 0; i <" << vectorWidth << "; i++) \n";
|
|
Out << " to.num[i] = ((int*)(&from))[i]; \n";
|
|
Out << " return to; \n";
|
|
Out << " } \n";
|
|
Out << " \n";
|
|
Out << " template <class T> \n";
|
|
Out << " friend T __cast_bits(T to, iN<num_bits> from) { \n";
|
|
Out << " for (int i = 0; i <" << vectorWidth << "; i++) \n";
|
|
Out << " ((int*)(&to))[i] = from.num[i]; \n";
|
|
Out << " return to; \n";
|
|
Out << " } \n";
|
|
Out << " \n";
|
|
Out << " template <int ALIGN, class T> \n";
|
|
Out << " friend void __store(T *p, iN<num_bits> val) { \n";
|
|
Out << " for (int i = 0; i <" << vectorWidth << "; i++) \n";
|
|
Out << " ((int*)p)[i] = val.num[i]; \n";
|
|
Out << " } \n";
|
|
Out << "}; \n";
|
|
Out << "};\n";
|
|
Out << "\n";
|
|
|
|
// Get all of the struct types used in the module.
|
|
std::vector<llvm::StructType*> StructTypes;
|
|
llvm::TypeFinder typeFinder;
|
|
typeFinder.run(*TheModule, false);
|
|
for (llvm::TypeFinder::iterator iter = typeFinder.begin();
|
|
iter != typeFinder.end(); ++iter)
|
|
StructTypes.push_back(*iter);
|
|
|
|
// Get all of the array types used in the module
|
|
std::vector<llvm::ArrayType*> ArrayTypes;
|
|
std::vector<llvm::IntegerType*> IntegerTypes;
|
|
std::vector<bool> IsVolatile;
|
|
std::vector<int> Alignment;
|
|
|
|
findUsedArrayAndLongIntTypes(TheModule, ArrayTypes, IntegerTypes, IsVolatile, Alignment);
|
|
|
|
if (StructTypes.empty() && ArrayTypes.empty())
|
|
return;
|
|
|
|
Out << "/* Structure and array forward declarations */\n";
|
|
|
|
unsigned NextTypeID = 0;
|
|
|
|
// If any of them are missing names, add a unique ID to UnnamedStructIDs.
|
|
// Print out forward declarations for structure types.
|
|
for (unsigned i = 0, e = StructTypes.size(); i != e; ++i) {
|
|
llvm::StructType *ST = StructTypes[i];
|
|
|
|
if (ST->isLiteral() || ST->getName().empty())
|
|
UnnamedStructIDs[ST] = NextTypeID++;
|
|
|
|
std::string Name = getStructName(ST);
|
|
|
|
Out << "struct " << Name << ";\n";
|
|
}
|
|
|
|
Out << "namespace {\n";
|
|
for (unsigned i = 0, e = ArrayTypes.size(); i != e; ++i) {
|
|
llvm::ArrayType *AT = ArrayTypes[i];
|
|
ArrayIDs[AT] = NextTypeID++;
|
|
std::string Name = getArrayName(AT);
|
|
Out << " struct " << Name << ";\n";
|
|
}
|
|
Out << "};\n";
|
|
|
|
for (unsigned i = 0, e = IntegerTypes.size(); i != e; ++i) {
|
|
llvm::IntegerType *IT = IntegerTypes[i];
|
|
if (IT->getIntegerBitWidth() <= 64 || Alignment[i] == 0)
|
|
continue;
|
|
|
|
Out << "typedef struct __attribute__ ((packed, aligned(" << Alignment[i] << "))) {\n ";
|
|
IsVolatile[i] ? Out << " volatile " : Out << " ";
|
|
printType(Out, IT, false, "data");
|
|
Out << ";\n";
|
|
Out << "} iN_" << IT->getIntegerBitWidth() << "_align_" << Alignment[i] << ";\n";
|
|
}
|
|
|
|
Out << '\n';
|
|
|
|
// Keep track of which types have been printed so far.
|
|
llvm::SmallPtrSet<llvm::Type *, 16> StructArrayPrinted;
|
|
|
|
// Loop over all structures then push them into the stack so they are
|
|
// printed in the correct order.
|
|
//
|
|
Out << "/* Structure and array contents */\n";
|
|
for (unsigned i = 0, e = StructTypes.size(); i != e; ++i) {
|
|
if (StructTypes[i]->isStructTy())
|
|
// Only print out used types!
|
|
printContainedStructs(StructTypes[i], StructArrayPrinted);
|
|
}
|
|
|
|
Out << "namespace {\n";
|
|
for (unsigned i = 0, e = ArrayTypes.size(); i != e; ++i)
|
|
printContainedArrays(ArrayTypes[i], StructArrayPrinted);
|
|
|
|
Out << "};\n";
|
|
Out << '\n';
|
|
}
|
|
|
|
// Push the struct onto the stack and recursively push all structs
|
|
// this one depends on.
|
|
//
|
|
// TODO: Make this work properly with vector types
|
|
//
|
|
void CWriter::printContainedStructs(llvm::Type *Ty,
|
|
llvm::SmallPtrSet<llvm::Type *, 16> &Printed) {
|
|
// Don't walk through pointers.
|
|
if (!(Ty->isStructTy() || Ty->isArrayTy()))
|
|
return;
|
|
|
|
// Print all contained types first.
|
|
for (llvm::Type::subtype_iterator I = Ty->subtype_begin(),
|
|
E = Ty->subtype_end(); I != E; ++I)
|
|
printContainedStructs(*I, Printed);
|
|
|
|
if (llvm::StructType *ST = llvm::dyn_cast<llvm::StructType>(Ty)) {
|
|
// Check to see if we have already printed this struct.
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_6 // LLVM 3.6+
|
|
if (!Printed.insert(Ty).second) return;
|
|
#else
|
|
if (!Printed.insert(Ty)) return;
|
|
#endif
|
|
|
|
// Print structure type out.
|
|
printType(Out, ST, false, getStructName(ST), true);
|
|
Out << ";\n\n";
|
|
}
|
|
if (llvm::ArrayType *AT = llvm::dyn_cast<llvm::ArrayType>(Ty)) {
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_6 // LLVM 3.6+
|
|
if (!Printed.insert(Ty).second) return;
|
|
#else
|
|
if (!Printed.insert(Ty)) return;
|
|
#endif
|
|
|
|
Out << "namespace {\n";
|
|
printType(Out, AT, false, getArrayName(AT), true);
|
|
Out << ";\n}\n\n";
|
|
}
|
|
}
|
|
|
|
void CWriter::printContainedArrays(llvm::ArrayType *ATy,
|
|
llvm::SmallPtrSet<llvm::Type *, 16> &Printed) {
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_6 // LLVM 3.6+
|
|
if (!Printed.insert(ATy).second)
|
|
return;
|
|
#else
|
|
if (!Printed.insert(ATy))
|
|
return;
|
|
#endif
|
|
|
|
llvm::ArrayType *ChildTy = llvm::dyn_cast<llvm::ArrayType>(ATy->getElementType());
|
|
if (ChildTy != NULL)
|
|
printContainedArrays(ChildTy, Printed);
|
|
|
|
printType(Out, ATy, false, getArrayName(ATy), true);
|
|
Out << ";\n\n";
|
|
}
|
|
|
|
void CWriter::printFunctionSignature(const llvm::Function *F, bool Prototype) {
|
|
/// isStructReturn - Should this function actually return a struct by-value?
|
|
bool isStructReturn = F->hasStructRetAttr();
|
|
|
|
if (F->hasLocalLinkage()) Out << "static ";
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
if (F->hasDLLImportStorageClass()) Out << "__declspec(dllimport) ";
|
|
if (F->hasDLLExportStorageClass()) Out << "__declspec(dllexport) ";
|
|
#else
|
|
if (F->hasDLLImportLinkage()) Out << "__declspec(dllimport) ";
|
|
if (F->hasDLLExportLinkage()) Out << "__declspec(dllexport) ";
|
|
#endif
|
|
switch (F->getCallingConv()) {
|
|
case llvm::CallingConv::X86_StdCall:
|
|
Out << "__attribute__((stdcall)) ";
|
|
break;
|
|
case llvm::CallingConv::X86_FastCall:
|
|
Out << "__attribute__((fastcall)) ";
|
|
break;
|
|
case llvm::CallingConv::X86_ThisCall:
|
|
Out << "__attribute__((thiscall)) ";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Loop over the arguments, printing them...
|
|
llvm::FunctionType *FT = llvm::cast<llvm::FunctionType>(F->getFunctionType());
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL = F->getAttributes();
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL = F->getAttributes();
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL = F->getAttributes();
|
|
#endif
|
|
|
|
std::string tstr;
|
|
llvm::raw_string_ostream FunctionInnards(tstr);
|
|
|
|
// Print out the name...
|
|
FunctionInnards << GetValueName(F) << '(';
|
|
|
|
bool PrintedArg = false;
|
|
if (!F->isDeclaration()) {
|
|
if (!F->arg_empty()) {
|
|
llvm::Function::const_arg_iterator I = F->arg_begin(), E = F->arg_end();
|
|
unsigned Idx = 1;
|
|
|
|
// If this is a struct-return function, don't print the hidden
|
|
// struct-return argument.
|
|
if (isStructReturn) {
|
|
assert(I != E && "Invalid struct return function!");
|
|
++I;
|
|
++Idx;
|
|
}
|
|
|
|
std::string ArgName;
|
|
for (; I != E; ++I) {
|
|
if (PrintedArg) FunctionInnards << ", ";
|
|
if (I->hasName() || !Prototype)
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
ArgName = GetValueName(I);
|
|
#else /* LLVM 3.8+ */
|
|
ArgName = GetValueName(&*I);
|
|
#endif
|
|
else
|
|
ArgName = "";
|
|
llvm::Type *ArgTy = I->getType();
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::ByVal)) {
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#else // LLVM 5.0+
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#endif
|
|
ArgTy = llvm::cast<llvm::PointerType>(ArgTy)->getElementType();
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
ByValParams.insert(I);
|
|
#else /* LLVM 3.8+ */
|
|
ByValParams.insert(&*I);
|
|
#endif
|
|
}
|
|
printType(FunctionInnards, ArgTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
ArgName);
|
|
PrintedArg = true;
|
|
++Idx;
|
|
}
|
|
}
|
|
} else {
|
|
// Loop over the arguments, printing them.
|
|
llvm::FunctionType::param_iterator I = FT->param_begin(), E = FT->param_end();
|
|
unsigned Idx = 1;
|
|
|
|
// If this is a struct-return function, don't print the hidden
|
|
// struct-return argument.
|
|
if (isStructReturn) {
|
|
assert(I != E && "Invalid struct return function!");
|
|
++I;
|
|
++Idx;
|
|
}
|
|
|
|
for (; I != E; ++I) {
|
|
if (PrintedArg) FunctionInnards << ", ";
|
|
llvm::Type *ArgTy = *I;
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::ByVal)) {
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#else // LLVM 5.0+
|
|
if (PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::ByVal)) {
|
|
#endif
|
|
assert(ArgTy->isPointerTy());
|
|
ArgTy = llvm::cast<llvm::PointerType>(ArgTy)->getElementType();
|
|
}
|
|
printType(FunctionInnards, ArgTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::Attributes::SExt)
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::SExt)
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(Idx).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::SExt)
|
|
#endif
|
|
);
|
|
PrintedArg = true;
|
|
++Idx;
|
|
}
|
|
}
|
|
|
|
if (!PrintedArg && FT->isVarArg()) {
|
|
FunctionInnards << "int vararg_dummy_arg";
|
|
PrintedArg = true;
|
|
}
|
|
|
|
// Finish printing arguments... if this is a vararg function, print the ...,
|
|
// unless there are no known types, in which case, we just emit ().
|
|
//
|
|
if (FT->isVarArg() && PrintedArg) {
|
|
FunctionInnards << ",..."; // Output varargs portion of signature!
|
|
} else if (!FT->isVarArg() && !PrintedArg) {
|
|
FunctionInnards << "void"; // ret() -> ret(void) in C.
|
|
}
|
|
FunctionInnards << ')';
|
|
|
|
// Get the return tpe for the function.
|
|
llvm::Type *RetTy;
|
|
if (!isStructReturn)
|
|
RetTy = F->getReturnType();
|
|
else {
|
|
// If this is a struct-return function, print the struct-return type.
|
|
RetTy = llvm::cast<llvm::PointerType>(FT->getParamType(0))->getElementType();
|
|
}
|
|
|
|
// Print out the return type and the signature built above.
|
|
printType(Out, RetTy,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::Attributes::SExt),
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeSet::ReturnIndex, llvm::Attribute::SExt),
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(0).hasAttribute(llvm::AttributeList::ReturnIndex, llvm::Attribute::SExt),
|
|
#endif
|
|
FunctionInnards.str());
|
|
}
|
|
|
|
static inline bool isFPIntBitCast(const llvm::Instruction &I) {
|
|
if (!llvm::isa<llvm::BitCastInst>(I))
|
|
return false;
|
|
llvm::Type *SrcTy = I.getOperand(0)->getType();
|
|
llvm::Type *DstTy = I.getType();
|
|
return (SrcTy->isFloatingPointTy() && DstTy->isIntegerTy()) ||
|
|
(DstTy->isFloatingPointTy() && SrcTy->isIntegerTy());
|
|
}
|
|
|
|
void CWriter::printFunction(llvm::Function &F) {
|
|
/// isStructReturn - Should this function actually return a struct by-value?
|
|
bool isStructReturn = F.hasStructRetAttr();
|
|
|
|
printFunctionSignature(&F, false);
|
|
Out << " {\n";
|
|
|
|
// If this is a struct return function, handle the result with magic.
|
|
if (isStructReturn) {
|
|
llvm::Type *StructTy =
|
|
llvm::cast<llvm::PointerType>(F.arg_begin()->getType())->getElementType();
|
|
Out << " ";
|
|
printType(Out, StructTy, false, "StructReturn");
|
|
Out << "; /* Struct return temporary */\n";
|
|
|
|
Out << " ";
|
|
printType(Out, F.arg_begin()->getType(), false,
|
|
GetValueName(&*(F.arg_begin())));
|
|
Out << " = &StructReturn;\n";
|
|
}
|
|
|
|
bool PrintedVar = false;
|
|
|
|
// print local variable information for the function
|
|
for (llvm::inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) {
|
|
if (const llvm::AllocaInst *AI = isDirectAlloca(&*I)) {
|
|
Out << " ";
|
|
printType(Out, AI->getAllocatedType(), false, GetValueName(AI));
|
|
Out << "; /* Address-exposed local */\n";
|
|
PrintedVar = true;
|
|
} else if (I->getType() != llvm::Type::getVoidTy(F.getContext()) &&
|
|
!isInlinableInst(*I)) {
|
|
Out << " ";
|
|
printType(Out, I->getType(), false, GetValueName(&*I));
|
|
Out << ";\n";
|
|
|
|
if (llvm::isa<llvm::PHINode>(*I)) { // Print out PHI node temporaries as well...
|
|
Out << " ";
|
|
printType(Out, I->getType(), false,
|
|
GetValueName(&*I)+"__PHI");
|
|
Out << ";\n";
|
|
}
|
|
PrintedVar = true;
|
|
}
|
|
// We need a temporary for the BitCast to use so it can pluck a value out
|
|
// of a union to do the BitCast. This is separate from the need for a
|
|
// variable to hold the result of the BitCast.
|
|
if (isFPIntBitCast(*I)) {
|
|
Out << " llvmBitCastUnion " << GetValueName(&*I)
|
|
<< "__BITCAST_TEMPORARY;\n";
|
|
PrintedVar = true;
|
|
}
|
|
}
|
|
|
|
if (PrintedVar)
|
|
Out << '\n';
|
|
|
|
if (F.hasExternalLinkage() && F.getName() == "main")
|
|
Out << " CODE_FOR_MAIN();\n";
|
|
|
|
// print the basic blocks
|
|
for (llvm::Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) {
|
|
if (llvm::Loop *L = LI->getLoopFor(&*BB)) {
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_9 // LLVM 3.9+
|
|
if (L->getHeader()->getIterator() == BB && L->getParentLoop() == 0)
|
|
#else
|
|
if (L->getHeader() == BB && L->getParentLoop() == 0)
|
|
#endif
|
|
printLoop(L);
|
|
} else {
|
|
printBasicBlock(&*BB);
|
|
}
|
|
}
|
|
|
|
Out << "}\n\n";
|
|
}
|
|
|
|
void CWriter::printLoop(llvm::Loop *L) {
|
|
Out << " do { /* Syntactic loop '" << L->getHeader()->getName()
|
|
<< "' to make GCC happy */\n";
|
|
for (unsigned i = 0, e = L->getBlocks().size(); i != e; ++i) {
|
|
llvm::BasicBlock *BB = L->getBlocks()[i];
|
|
llvm::Loop *BBLoop = LI->getLoopFor(BB);
|
|
if (BBLoop == L)
|
|
printBasicBlock(BB);
|
|
else if (BB == BBLoop->getHeader() && BBLoop->getParentLoop() == L)
|
|
printLoop(BBLoop);
|
|
}
|
|
Out << " } while (1); /* end of syntactic loop '"
|
|
<< L->getHeader()->getName() << "' */\n";
|
|
}
|
|
|
|
void CWriter::printBasicBlock(llvm::BasicBlock *BB) {
|
|
|
|
// Don't print the label for the basic block if there are no uses, or if
|
|
// the only terminator use is the predecessor basic block's terminator.
|
|
// We have to scan the use list because PHI nodes use basic blocks too but
|
|
// do not require a label to be generated.
|
|
//
|
|
bool NeedsLabel = false;
|
|
for (llvm::pred_iterator PI = pred_begin(BB), E = pred_end(BB); PI != E; ++PI)
|
|
if (isGotoCodeNecessary(*PI, BB)) {
|
|
NeedsLabel = true;
|
|
break;
|
|
}
|
|
|
|
if (NeedsLabel) Out << GetValueName(BB) << ": {\n";
|
|
|
|
// Output all of the instructions in the basic block...
|
|
for (llvm::BasicBlock::iterator II = BB->begin(), E = --BB->end(); II != E;
|
|
++II) {
|
|
if (!isInlinableInst(*II) && !isDirectAlloca(&*II)) {
|
|
if (II->getType() != llvm::Type::getVoidTy(BB->getContext()) &&
|
|
!isInlineAsm(*II))
|
|
outputLValue(&*II);
|
|
else
|
|
Out << " ";
|
|
writeInstComputationInline(*II);
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
|
|
// Don't emit prefix or suffix for the terminator.
|
|
visit(*BB->getTerminator());
|
|
if (NeedsLabel) Out << "}\n"; // workaround g++ bug
|
|
}
|
|
|
|
|
|
// Specific Instruction type classes... note that all of the casts are
|
|
// necessary because we use the instruction classes as opaque types...
|
|
//
|
|
void CWriter::visitReturnInst(llvm::ReturnInst &I) {
|
|
// If this is a struct return function, return the temporary struct.
|
|
bool isStructReturn = I.getParent()->getParent()->hasStructRetAttr();
|
|
|
|
if (isStructReturn) {
|
|
Out << " return StructReturn;\n";
|
|
return;
|
|
}
|
|
|
|
// Don't output a void return if this is the last basic block in the function
|
|
if (I.getNumOperands() == 0 &&
|
|
&*--I.getParent()->getParent()->end() == I.getParent() &&
|
|
(!I.getParent()->size()) == 1) {
|
|
return;
|
|
}
|
|
|
|
Out << " return";
|
|
if (I.getNumOperands()) {
|
|
Out << ' ';
|
|
writeOperand(I.getOperand(0));
|
|
}
|
|
Out << ";\n";
|
|
}
|
|
|
|
void CWriter::visitSwitchInst(llvm::SwitchInst &SI) {
|
|
|
|
llvm::Value* Cond = SI.getCondition();
|
|
|
|
Out << " switch (";
|
|
writeOperand(Cond);
|
|
Out << ") {\n default:\n";
|
|
printPHICopiesForSuccessor (SI.getParent(), SI.getDefaultDest(), 2);
|
|
printBranchToBlock(SI.getParent(), SI.getDefaultDest(), 2);
|
|
Out << ";\n";
|
|
|
|
for (llvm::SwitchInst::CaseIt i = SI.case_begin(), e = SI.case_end(); i != e; ++i) {
|
|
llvm::ConstantInt* CaseVal = i.getCaseValue();
|
|
llvm::BasicBlock* Succ = i.getCaseSuccessor();
|
|
Out << " case ";
|
|
writeOperand(CaseVal);
|
|
Out << ":\n";
|
|
printPHICopiesForSuccessor (SI.getParent(), Succ, 2);
|
|
printBranchToBlock(SI.getParent(), Succ, 2);
|
|
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
if (llvm::Function::iterator(Succ) == std::next(llvm::Function::iterator(SI.getParent())))
|
|
#else
|
|
if (llvm::Function::iterator(Succ) == llvm::next(llvm::Function::iterator(SI.getParent())))
|
|
#endif
|
|
Out << " break;\n";
|
|
}
|
|
|
|
Out << " }\n";
|
|
}
|
|
|
|
void CWriter::visitIndirectBrInst(llvm::IndirectBrInst &IBI) {
|
|
Out << " goto *(void*)(";
|
|
writeOperand(IBI.getOperand(0));
|
|
Out << ");\n";
|
|
}
|
|
|
|
void CWriter::visitUnreachableInst(llvm::UnreachableInst &I) {
|
|
Out << " /*UNREACHABLE*/;\n";
|
|
}
|
|
|
|
bool CWriter::isGotoCodeNecessary(llvm::BasicBlock *From, llvm::BasicBlock *To) {
|
|
/// FIXME: This should be reenabled, but loop reordering safe!!
|
|
return true;
|
|
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
if (std::next(llvm::Function::iterator(From)) != llvm::Function::iterator(To))
|
|
#else
|
|
if (llvm::next(llvm::Function::iterator(From)) != llvm::Function::iterator(To))
|
|
#endif
|
|
return true; // Not the direct successor, we need a goto.
|
|
|
|
//llvm::isa<llvm::SwitchInst>(From->getTerminator())
|
|
|
|
if (LI->getLoopFor(From) != LI->getLoopFor(To))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void CWriter::printPHICopiesForSuccessor (llvm::BasicBlock *CurBlock,
|
|
llvm::BasicBlock *Successor,
|
|
unsigned Indent) {
|
|
for (llvm::BasicBlock::iterator I = Successor->begin(); llvm::isa<llvm::PHINode>(I); ++I) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
llvm::PHINode *PN = llvm::cast<llvm::PHINode>(I);
|
|
#else /* LLVM 3.8+ */
|
|
llvm::PHINode *PN = llvm::cast<llvm::PHINode>(&*I);
|
|
#endif
|
|
// Now we have to do the printing.
|
|
llvm::Value *IV = PN->getIncomingValueForBlock(CurBlock);
|
|
if (!llvm::isa<llvm::UndefValue>(IV)) {
|
|
Out << std::string(Indent, ' ');
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
Out << " " << GetValueName(I) << "__PHI = ";
|
|
#else /* LLVM 3.8+ */
|
|
Out << " " << GetValueName(&*I) << "__PHI = ";
|
|
#endif
|
|
writeOperand(IV);
|
|
Out << "; /* for PHI node */\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void CWriter::printBranchToBlock(llvm::BasicBlock *CurBB, llvm::BasicBlock *Succ,
|
|
unsigned Indent) {
|
|
if (isGotoCodeNecessary(CurBB, Succ)) {
|
|
Out << std::string(Indent, ' ') << " goto ";
|
|
writeOperand(Succ);
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
|
|
// Branch instruction printing - Avoid printing out a branch to a basic block
|
|
// that immediately succeeds the current one.
|
|
//
|
|
void CWriter::visitBranchInst(llvm::BranchInst &I) {
|
|
|
|
if (I.isConditional()) {
|
|
if (isGotoCodeNecessary(I.getParent(), I.getSuccessor(0))) {
|
|
Out << " if (";
|
|
writeOperand(I.getCondition());
|
|
Out << ") {\n";
|
|
|
|
printPHICopiesForSuccessor (I.getParent(), I.getSuccessor(0), 2);
|
|
printBranchToBlock(I.getParent(), I.getSuccessor(0), 2);
|
|
|
|
if (isGotoCodeNecessary(I.getParent(), I.getSuccessor(1))) {
|
|
Out << " } else {\n";
|
|
printPHICopiesForSuccessor (I.getParent(), I.getSuccessor(1), 2);
|
|
printBranchToBlock(I.getParent(), I.getSuccessor(1), 2);
|
|
}
|
|
} else {
|
|
// First goto not necessary, assume second one is...
|
|
Out << " if (!";
|
|
writeOperand(I.getCondition());
|
|
Out << ") {\n";
|
|
|
|
printPHICopiesForSuccessor (I.getParent(), I.getSuccessor(1), 2);
|
|
printBranchToBlock(I.getParent(), I.getSuccessor(1), 2);
|
|
}
|
|
|
|
Out << " }\n";
|
|
} else {
|
|
printPHICopiesForSuccessor (I.getParent(), I.getSuccessor(0), 0);
|
|
printBranchToBlock(I.getParent(), I.getSuccessor(0), 0);
|
|
}
|
|
Out << "\n";
|
|
}
|
|
|
|
// PHI nodes get copied into temporary values at the end of predecessor basic
|
|
// blocks. We now need to copy these temporary values into the REAL value for
|
|
// the PHI.
|
|
void CWriter::visitPHINode(llvm::PHINode &I) {
|
|
writeOperand(&I);
|
|
Out << "__PHI";
|
|
}
|
|
|
|
|
|
void CWriter::visitBinaryOperator(llvm::Instruction &I) {
|
|
// binary instructions, shift instructions, setCond instructions.
|
|
assert(!I.getType()->isPointerTy());
|
|
|
|
if (llvm::isa<const llvm::VectorType>(I.getOperand(0)->getType())) {
|
|
const char *intrinsic = NULL;
|
|
switch (I.getOpcode()) {
|
|
case llvm::Instruction::Add: intrinsic = "__add"; break;
|
|
case llvm::Instruction::FAdd: intrinsic = "__add"; break;
|
|
case llvm::Instruction::Sub: intrinsic = "__sub"; break;
|
|
case llvm::Instruction::FSub: intrinsic = "__sub"; break;
|
|
case llvm::Instruction::Mul: intrinsic = "__mul"; break;
|
|
case llvm::Instruction::FMul: intrinsic = "__mul"; break;
|
|
case llvm::Instruction::URem: intrinsic = "__urem"; break;
|
|
case llvm::Instruction::SRem: intrinsic = "__srem"; break;
|
|
case llvm::Instruction::FRem: intrinsic = "__frem"; break;
|
|
case llvm::Instruction::UDiv: intrinsic = "__udiv"; break;
|
|
case llvm::Instruction::SDiv: intrinsic = "__sdiv"; break;
|
|
case llvm::Instruction::FDiv: intrinsic = "__div"; break;
|
|
case llvm::Instruction::And: intrinsic = "__and"; break;
|
|
case llvm::Instruction::Or: intrinsic = "__or"; break;
|
|
case llvm::Instruction::Xor: intrinsic = "__xor"; break;
|
|
case llvm::Instruction::Shl : intrinsic = "__shl"; break;
|
|
case llvm::Instruction::LShr: intrinsic = "__lshr"; break;
|
|
case llvm::Instruction::AShr: intrinsic = "__ashr"; break;
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "Invalid operator type!" << I;
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
Out << intrinsic;
|
|
Out << "(";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
if ((I.getOpcode() == llvm::Instruction::Shl ||
|
|
I.getOpcode() == llvm::Instruction::LShr ||
|
|
I.getOpcode() == llvm::Instruction::AShr)) {
|
|
llvm::Value *splat = NULL;
|
|
if (LLVMVectorValuesAllEqual(I.getOperand(1), &splat)) {
|
|
if (splat) {
|
|
// Avoid __extract_element(splat(value), 0), if possible.
|
|
writeOperand(splat);
|
|
} else {
|
|
Out << "__extract_element(";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ", 0) ";
|
|
}
|
|
}
|
|
else
|
|
writeOperand(I.getOperand(1));
|
|
}
|
|
else
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
// We must cast the results of binary operations which might be promoted.
|
|
bool needsCast = false;
|
|
if ((I.getType() == llvm::Type::getInt8Ty(I.getContext())) ||
|
|
(I.getType() == llvm::Type::getInt16Ty(I.getContext()))
|
|
|| (I.getType() == llvm::Type::getFloatTy(I.getContext()))) {
|
|
needsCast = true;
|
|
Out << "((";
|
|
printType(Out, I.getType(), false);
|
|
Out << ")(";
|
|
}
|
|
|
|
// If this is a negation operation, print it out as such. For FP, we don't
|
|
// want to print "-0.0 - X".
|
|
if (llvm::BinaryOperator::isNeg(&I)) {
|
|
Out << "-(";
|
|
writeOperand(llvm::BinaryOperator::getNegArgument(llvm::cast<llvm::BinaryOperator>(&I)));
|
|
Out << ")";
|
|
} else if (llvm::BinaryOperator::isFNeg(&I)) {
|
|
Out << "-(";
|
|
writeOperand(llvm::BinaryOperator::getFNegArgument(llvm::cast<llvm::BinaryOperator>(&I)));
|
|
Out << ")";
|
|
} else if (I.getOpcode() == llvm::Instruction::FRem) {
|
|
// Output a call to fmod/fmodf instead of emitting a%b
|
|
if (I.getType() == llvm::Type::getFloatTy(I.getContext()))
|
|
Out << "fmodf(";
|
|
else if (I.getType() == llvm::Type::getDoubleTy(I.getContext()))
|
|
Out << "fmod(";
|
|
else // all 3 flavors of long double
|
|
Out << "fmodl(";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
} else {
|
|
|
|
// Write out the cast of the instruction's value back to the proper type
|
|
// if necessary.
|
|
bool NeedsClosingParens = writeInstructionCast(I);
|
|
|
|
// Certain instructions require the operand to be forced to a specific type
|
|
// so we use writeOperandWithCast here instead of writeOperand. Similarly
|
|
// below for operand 1
|
|
writeOperandWithCast(I.getOperand(0), I.getOpcode());
|
|
|
|
switch (I.getOpcode()) {
|
|
case llvm::Instruction::Add:
|
|
case llvm::Instruction::FAdd: Out << " + "; break;
|
|
case llvm::Instruction::Sub:
|
|
case llvm::Instruction::FSub: Out << " - "; break;
|
|
case llvm::Instruction::Mul:
|
|
case llvm::Instruction::FMul: Out << " * "; break;
|
|
case llvm::Instruction::URem:
|
|
case llvm::Instruction::SRem:
|
|
case llvm::Instruction::FRem: Out << " % "; break;
|
|
case llvm::Instruction::UDiv:
|
|
case llvm::Instruction::SDiv:
|
|
case llvm::Instruction::FDiv: Out << " / "; break;
|
|
case llvm::Instruction::And: Out << " & "; break;
|
|
case llvm::Instruction::Or: Out << " | "; break;
|
|
case llvm::Instruction::Xor: Out << " ^ "; break;
|
|
case llvm::Instruction::Shl : Out << " << "; break;
|
|
case llvm::Instruction::LShr:
|
|
case llvm::Instruction::AShr: Out << " >> "; break;
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "Invalid operator type!" << I;
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
|
|
writeOperandWithCast(I.getOperand(1), I.getOpcode());
|
|
if (NeedsClosingParens)
|
|
Out << "))";
|
|
}
|
|
|
|
if (needsCast) {
|
|
Out << "))";
|
|
}
|
|
}
|
|
|
|
|
|
static const char *
|
|
lPredicateToString(llvm::CmpInst::Predicate p) {
|
|
switch (p) {
|
|
case llvm::ICmpInst::ICMP_EQ: return "__equal";
|
|
case llvm::ICmpInst::ICMP_NE: return "__not_equal";
|
|
case llvm::ICmpInst::ICMP_ULE: return "__unsigned_less_equal";
|
|
case llvm::ICmpInst::ICMP_SLE: return "__signed_less_equal";
|
|
case llvm::ICmpInst::ICMP_UGE: return "__unsigned_greater_equal";
|
|
case llvm::ICmpInst::ICMP_SGE: return "__signed_greater_equal";
|
|
case llvm::ICmpInst::ICMP_ULT: return "__unsigned_less_than";
|
|
case llvm::ICmpInst::ICMP_SLT: return "__signed_less_than";
|
|
case llvm::ICmpInst::ICMP_UGT: return "__unsigned_greater_than";
|
|
case llvm::ICmpInst::ICMP_SGT: return "__signed_greater_than";
|
|
|
|
case llvm::FCmpInst::FCMP_ORD: return "__ordered";
|
|
case llvm::FCmpInst::FCMP_UNO: return "__unordered";
|
|
case llvm::FCmpInst::FCMP_UEQ: return "__equal";
|
|
case llvm::FCmpInst::FCMP_UNE: return "__not_equal";
|
|
case llvm::FCmpInst::FCMP_ULT: return "__less_than";
|
|
case llvm::FCmpInst::FCMP_ULE: return "__less_equal";
|
|
case llvm::FCmpInst::FCMP_UGT: return "__greater_than";
|
|
case llvm::FCmpInst::FCMP_UGE: return "__greater_equal";
|
|
case llvm::FCmpInst::FCMP_OEQ: return "__equal";
|
|
case llvm::FCmpInst::FCMP_ONE: return "__not_equal";
|
|
case llvm::FCmpInst::FCMP_OLT: return "__less_than";
|
|
case llvm::FCmpInst::FCMP_OLE: return "__less_equal";
|
|
case llvm::FCmpInst::FCMP_OGT: return "__greater_than";
|
|
case llvm::FCmpInst::FCMP_OGE: return "__greater_equal";
|
|
|
|
default: llvm_unreachable(0); return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static const char *
|
|
lTypeToSuffix(llvm::Type *t) {
|
|
llvm::VectorType *vt = llvm::dyn_cast<llvm::VectorType>(t);
|
|
Assert(vt != NULL);
|
|
t = vt->getElementType();
|
|
|
|
switch (t->getTypeID()) {
|
|
case llvm::Type::FloatTyID: return "float";
|
|
case llvm::Type::DoubleTyID: return "double";
|
|
case llvm::Type::IntegerTyID: {
|
|
switch (llvm::cast<llvm::IntegerType>(t)->getBitWidth()) {
|
|
case 1: return "i1";
|
|
case 8: return "i8";
|
|
case 16: return "i16";
|
|
case 32: return "i32";
|
|
case 64: return "i64";
|
|
}
|
|
}
|
|
default: llvm_unreachable(0); return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void CWriter::visitICmpInst(llvm::ICmpInst &I) {
|
|
bool isVector = llvm::isa<llvm::VectorType>(I.getOperand(0)->getType());
|
|
|
|
if (isVector) {
|
|
Out << lPredicateToString(I.getPredicate());
|
|
Out << "_";
|
|
Out << lTypeToSuffix(I.getOperand(0)->getType());
|
|
Out << "(";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
// Write out the cast of the instruction's value back to the proper type
|
|
// if necessary.
|
|
bool NeedsClosingParens = writeInstructionCast(I);
|
|
|
|
// Certain icmp predicate require the operand to be forced to a specific type
|
|
// so we use writeOperandWithCast here instead of writeOperand. Similarly
|
|
// below for operand 1
|
|
writeOperandWithCast(I.getOperand(0), I);
|
|
|
|
switch (I.getPredicate()) {
|
|
case llvm::ICmpInst::ICMP_EQ: Out << " == "; break;
|
|
case llvm::ICmpInst::ICMP_NE: Out << " != "; break;
|
|
case llvm::ICmpInst::ICMP_ULE:
|
|
case llvm::ICmpInst::ICMP_SLE: Out << " <= "; break;
|
|
case llvm::ICmpInst::ICMP_UGE:
|
|
case llvm::ICmpInst::ICMP_SGE: Out << " >= "; break;
|
|
case llvm::ICmpInst::ICMP_ULT:
|
|
case llvm::ICmpInst::ICMP_SLT: Out << " < "; break;
|
|
case llvm::ICmpInst::ICMP_UGT:
|
|
case llvm::ICmpInst::ICMP_SGT: Out << " > "; break;
|
|
default:
|
|
#ifndef NDEBUG
|
|
llvm::errs() << "Invalid icmp predicate!" << I;
|
|
#endif
|
|
llvm_unreachable(0);
|
|
}
|
|
|
|
writeOperandWithCast(I.getOperand(1), I);
|
|
if (NeedsClosingParens)
|
|
Out << "))";
|
|
}
|
|
|
|
void CWriter::visitFCmpInst(llvm::FCmpInst &I) {
|
|
bool isVector = llvm::isa<llvm::VectorType>(I.getOperand(0)->getType());
|
|
|
|
if (I.getPredicate() == llvm::FCmpInst::FCMP_FALSE) {
|
|
if (isVector)
|
|
llvm::report_fatal_error("FIXME: vector FCMP_FALSE");
|
|
else
|
|
Out << "0";
|
|
return;
|
|
}
|
|
if (I.getPredicate() == llvm::FCmpInst::FCMP_TRUE) {
|
|
if (isVector)
|
|
llvm::report_fatal_error("FIXME: vector FCMP_TRUE");
|
|
else
|
|
Out << "1";
|
|
return;
|
|
}
|
|
|
|
if (isVector) {
|
|
Out << lPredicateToString(I.getPredicate());
|
|
Out << "_";
|
|
Out << lTypeToSuffix(I.getOperand(0)->getType());
|
|
Out << "(";
|
|
}
|
|
else {
|
|
const char* op = 0;
|
|
switch (I.getPredicate()) {
|
|
default: llvm_unreachable("Illegal FCmp predicate");
|
|
case llvm::FCmpInst::FCMP_ORD: op = "ord"; break;
|
|
case llvm::FCmpInst::FCMP_UNO: op = "uno"; break;
|
|
|
|
case llvm::FCmpInst::FCMP_UEQ: op = "ueq"; break;
|
|
case llvm::FCmpInst::FCMP_UNE: op = "une"; break;
|
|
case llvm::FCmpInst::FCMP_ULT: op = "ult"; break;
|
|
case llvm::FCmpInst::FCMP_ULE: op = "ule"; break;
|
|
case llvm::FCmpInst::FCMP_UGT: op = "ugt"; break;
|
|
case llvm::FCmpInst::FCMP_UGE: op = "uge"; break;
|
|
|
|
case llvm::FCmpInst::FCMP_OEQ: op = "oeq"; break;
|
|
case llvm::FCmpInst::FCMP_ONE: op = "one"; break;
|
|
case llvm::FCmpInst::FCMP_OLT: op = "olt"; break;
|
|
case llvm::FCmpInst::FCMP_OLE: op = "ole"; break;
|
|
case llvm::FCmpInst::FCMP_OGT: op = "ogt"; break;
|
|
case llvm::FCmpInst::FCMP_OGE: op = "oge"; break;
|
|
}
|
|
|
|
Out << "llvm_fcmp_" << op << "(";
|
|
}
|
|
|
|
// Write the first operand
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
// Write the second operand
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
}
|
|
|
|
static const char * getFloatBitCastField(llvm::Type *Ty) {
|
|
switch (Ty->getTypeID()) {
|
|
default: llvm_unreachable("Invalid Type");
|
|
case llvm::Type::FloatTyID: return "Float";
|
|
case llvm::Type::DoubleTyID: return "Double";
|
|
case llvm::Type::IntegerTyID: {
|
|
unsigned NumBits = llvm::cast<llvm::IntegerType>(Ty)->getBitWidth();
|
|
if (NumBits <= 32)
|
|
return "Int32";
|
|
else
|
|
return "Int64";
|
|
}
|
|
}
|
|
}
|
|
|
|
void CWriter::visitCastInst(llvm::CastInst &I) {
|
|
llvm::Type *DstTy = I.getType();
|
|
llvm::Type *SrcTy = I.getOperand(0)->getType();
|
|
if (isFPIntBitCast(I)) {
|
|
Out << '(';
|
|
// These int<->float and long<->double casts need to be handled specially
|
|
Out << GetValueName(&I) << "__BITCAST_TEMPORARY."
|
|
<< getFloatBitCastField(I.getOperand(0)->getType()) << " = ";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", " << GetValueName(&I) << "__BITCAST_TEMPORARY."
|
|
<< getFloatBitCastField(I.getType());
|
|
Out << ')';
|
|
return;
|
|
}
|
|
|
|
if ((llvm::isa<llvm::VectorType>(DstTy)) && (!llvm::isa<llvm::VectorType>(SrcTy))) {
|
|
writeOperand(I.getOperand(0));
|
|
return;
|
|
}
|
|
|
|
Out << '(';
|
|
bool closeParen = printCast(I.getOpcode(), SrcTy, DstTy);
|
|
|
|
// Make a sext from i1 work by subtracting the i1 from 0 (an int).
|
|
if (SrcTy == llvm::Type::getInt1Ty(I.getContext()) &&
|
|
I.getOpcode() == llvm::Instruction::SExt)
|
|
Out << "0-";
|
|
|
|
writeOperand(I.getOperand(0));
|
|
|
|
if (DstTy == llvm::Type::getInt1Ty(I.getContext()) &&
|
|
(I.getOpcode() == llvm::Instruction::Trunc ||
|
|
I.getOpcode() == llvm::Instruction::FPToUI ||
|
|
I.getOpcode() == llvm::Instruction::FPToSI ||
|
|
I.getOpcode() == llvm::Instruction::PtrToInt)) {
|
|
// Make sure we really get a trunc to bool by anding the operand with 1
|
|
Out << "&1u";
|
|
}
|
|
Out << ')';
|
|
if (closeParen)
|
|
Out << ')';
|
|
}
|
|
|
|
void CWriter::visitSelectInst(llvm::SelectInst &I) {
|
|
if (llvm::isa<llvm::VectorType>(I.getType())) {
|
|
Out << "__select(";
|
|
writeOperand(I.getCondition());
|
|
Out << ", ";
|
|
writeOperand(I.getTrueValue());
|
|
Out << ", ";
|
|
writeOperand(I.getFalseValue());
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
Out << "((";
|
|
writeOperand(I.getCondition());
|
|
Out << ") ? (";
|
|
writeOperand(I.getTrueValue());
|
|
Out << ") : (";
|
|
writeOperand(I.getFalseValue());
|
|
Out << "))";
|
|
}
|
|
|
|
// Returns the macro name or value of the max or min of an integer type
|
|
// (as defined in limits.h).
|
|
static void printLimitValue(llvm::IntegerType &Ty, bool isSigned, bool isMax,
|
|
llvm::raw_ostream &Out) {
|
|
const char* type;
|
|
const char* sprefix = "";
|
|
|
|
unsigned NumBits = Ty.getBitWidth();
|
|
if (NumBits <= 8) {
|
|
type = "CHAR";
|
|
sprefix = "S";
|
|
} else if (NumBits <= 16) {
|
|
type = "SHRT";
|
|
} else if (NumBits <= 32) {
|
|
type = "INT";
|
|
} else if (NumBits <= 64) {
|
|
type = "LLONG";
|
|
} else {
|
|
llvm_unreachable("Bit widths > 64 not implemented yet");
|
|
}
|
|
|
|
if (isSigned)
|
|
Out << sprefix << type << (isMax ? "_MAX" : "_MIN");
|
|
else
|
|
Out << "U" << type << (isMax ? "_MAX" : "0");
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
static bool isSupportedIntegerSize(llvm::IntegerType &T) {
|
|
return T.getBitWidth() == 8 || T.getBitWidth() == 16 ||
|
|
T.getBitWidth() == 32 || T.getBitWidth() == 64;
|
|
}
|
|
#endif
|
|
|
|
void CWriter::printIntrinsicDefinition(const llvm::Function &F, llvm::raw_ostream &Out) {
|
|
llvm::FunctionType *funT = F.getFunctionType();
|
|
llvm::Type *retT = F.getReturnType();
|
|
llvm::IntegerType *elemT = llvm::cast<llvm::IntegerType>(funT->getParamType(1));
|
|
|
|
assert(isSupportedIntegerSize(*elemT) &&
|
|
"CBackend does not support arbitrary size integers.");
|
|
assert(llvm::cast<llvm::StructType>(retT)->getElementType(0) == elemT &&
|
|
elemT == funT->getParamType(0) && funT->getNumParams() == 2);
|
|
|
|
switch (F.getIntrinsicID()) {
|
|
default:
|
|
llvm_unreachable("Unsupported Intrinsic.");
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
// static inline Rty uadd_ixx(unsigned ixx a, unsigned ixx b) {
|
|
// Rty r;
|
|
// r.field0 = a + b;
|
|
// r.field1 = (r.field0 < a);
|
|
// return r;
|
|
// }
|
|
Out << "static inline ";
|
|
printType(Out, retT);
|
|
Out << GetValueName(&F);
|
|
Out << "(";
|
|
printSimpleType(Out, elemT, false);
|
|
Out << "a,";
|
|
printSimpleType(Out, elemT, false);
|
|
Out << "b) {\n ";
|
|
printType(Out, retT);
|
|
Out << "r;\n";
|
|
Out << " r.field0 = a + b;\n";
|
|
Out << " r.field1 = (r.field0 < a);\n";
|
|
Out << " return r;\n}\n";
|
|
break;
|
|
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
// static inline Rty sadd_ixx(ixx a, ixx b) {
|
|
// Rty r;
|
|
// r.field1 = (b > 0 && a > XX_MAX - b) ||
|
|
// (b < 0 && a < XX_MIN - b);
|
|
// r.field0 = r.field1 ? 0 : a + b;
|
|
// return r;
|
|
// }
|
|
Out << "static ";
|
|
printType(Out, retT);
|
|
Out << GetValueName(&F);
|
|
Out << "(";
|
|
printSimpleType(Out, elemT, true);
|
|
Out << "a,";
|
|
printSimpleType(Out, elemT, true);
|
|
Out << "b) {\n ";
|
|
printType(Out, retT);
|
|
Out << "r;\n";
|
|
Out << " r.field1 = (b > 0 && a > ";
|
|
printLimitValue(*elemT, true, true, Out);
|
|
Out << " - b) || (b < 0 && a < ";
|
|
printLimitValue(*elemT, true, false, Out);
|
|
Out << " - b);\n";
|
|
Out << " r.field0 = r.field1 ? 0 : a + b;\n";
|
|
Out << " return r;\n}\n";
|
|
break;
|
|
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
Out << "static inline ";
|
|
printType(Out, retT);
|
|
Out << GetValueName(&F);
|
|
Out << "(";
|
|
printSimpleType(Out, elemT, false);
|
|
Out << "a,";
|
|
printSimpleType(Out, elemT, false);
|
|
Out << "b) {\n ";
|
|
|
|
printType(Out, retT);
|
|
Out << "r;\n";
|
|
|
|
unsigned NumBits = llvm::cast<llvm::IntegerType>(elemT)->getBitWidth();
|
|
std::stringstream str_type;
|
|
if (NumBits <= 32)
|
|
str_type << "uint" << 2 * NumBits << "_t";
|
|
else {
|
|
assert(NumBits <= 64 && "Bit widths > 128 not implemented yet");
|
|
str_type << "llvmUInt128";
|
|
}
|
|
|
|
Out << " " << str_type.str() << " result = (" << str_type.str() << ") a * (" << str_type.str() << ") b;\n";
|
|
Out << " r.field0 = result;\n";
|
|
Out << " r.field1 = result >> " << NumBits << ";\n";
|
|
Out << " return r;\n}\n";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CWriter::lowerIntrinsics(llvm::Function &F) {
|
|
// This is used to keep track of intrinsics that get generated to a lowered
|
|
// function. We must generate the prototypes before the function body which
|
|
// will only be expanded on first use (by the loop below).
|
|
std::vector<llvm::Function*> prototypesToGen;
|
|
|
|
// Examine all the instructions in this function to find the intrinsics that
|
|
// need to be lowered.
|
|
for (llvm::Function::iterator BB = F.begin(), EE = F.end(); BB != EE; ++BB)
|
|
for (llvm::BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; )
|
|
if (llvm::CallInst *CI = llvm::dyn_cast<llvm::CallInst>(I++))
|
|
if (llvm::Function *F = CI->getCalledFunction())
|
|
switch (F->getIntrinsicID()) {
|
|
// We directly implement these intrinsics
|
|
case llvm::Intrinsic::not_intrinsic:
|
|
case llvm::Intrinsic::vastart:
|
|
case llvm::Intrinsic::vacopy:
|
|
case llvm::Intrinsic::vaend:
|
|
case llvm::Intrinsic::returnaddress:
|
|
case llvm::Intrinsic::frameaddress:
|
|
case llvm::Intrinsic::setjmp:
|
|
case llvm::Intrinsic::longjmp:
|
|
case llvm::Intrinsic::memset:
|
|
case llvm::Intrinsic::prefetch:
|
|
case llvm::Intrinsic::powi:
|
|
case llvm::Intrinsic::fabs:
|
|
case llvm::Intrinsic::x86_sse_cmp_ss:
|
|
case llvm::Intrinsic::x86_sse_cmp_ps:
|
|
case llvm::Intrinsic::x86_sse2_cmp_sd:
|
|
case llvm::Intrinsic::x86_sse2_cmp_pd:
|
|
case llvm::Intrinsic::ppc_altivec_lvsl:
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
case llvm::Intrinsic::trap:
|
|
case llvm::Intrinsic::objectsize:
|
|
case llvm::Intrinsic::readcyclecounter:
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
// Or we just ignore them because of their uselessness in C++ source
|
|
case llvm::Intrinsic::dbg_value:
|
|
case llvm::Intrinsic::dbg_declare:
|
|
break;
|
|
default:
|
|
// If this is an intrinsic that directly corresponds to a GCC
|
|
// builtin, we handle it.
|
|
const char *BuiltinName = "";
|
|
#define GET_GCC_BUILTIN_NAME
|
|
#define Intrinsic llvm::Intrinsic
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
#include "llvm/Intrinsics.gen"
|
|
#else /* LLVM 3.3+ */
|
|
#include "llvm/IR/Intrinsics.gen"
|
|
#endif
|
|
#undef Intrinsic
|
|
#undef GET_GCC_BUILTIN_NAME
|
|
// If we handle it, don't lower it.
|
|
if (BuiltinName[0]) break;
|
|
|
|
// All other intrinsic calls we must lower.
|
|
llvm::Instruction *Before = 0;
|
|
if (CI != &BB->front())
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
Before = &*std::prev(llvm::BasicBlock::iterator(CI));
|
|
#else
|
|
Before = prior(llvm::BasicBlock::iterator(CI));
|
|
#endif
|
|
|
|
IL->LowerIntrinsicCall(CI);
|
|
if (Before) { // Move iterator to instruction after call
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_7 /* 3.2, 3.3, 3.4, 3.5, 3.6, 3.7 */
|
|
I = Before; ++I;
|
|
#else /* LLVM 3.8+ */
|
|
I = Before->getIterator(); ++I;
|
|
#endif
|
|
} else {
|
|
I = BB->begin();
|
|
}
|
|
// If the intrinsic got lowered to another call, and that call has
|
|
// a definition then we need to make sure its prototype is emitted
|
|
// before any calls to it.
|
|
if (llvm::CallInst *Call = llvm::dyn_cast<llvm::CallInst>(I))
|
|
if (llvm::Function *NewF = Call->getCalledFunction())
|
|
if (!NewF->isDeclaration())
|
|
prototypesToGen.push_back(NewF);
|
|
|
|
break;
|
|
}
|
|
|
|
// We may have collected some prototypes to emit in the loop above.
|
|
// Emit them now, before the function that uses them is emitted. But,
|
|
// be careful not to emit them twice.
|
|
std::vector<llvm::Function*>::iterator I = prototypesToGen.begin();
|
|
std::vector<llvm::Function*>::iterator E = prototypesToGen.end();
|
|
for ( ; I != E; ++I) {
|
|
if (intrinsicPrototypesAlreadyGenerated.insert(*I).second) {
|
|
Out << '\n';
|
|
printFunctionSignature(*I, true);
|
|
Out << ";\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void CWriter::visitCallInst(llvm::CallInst &I) {
|
|
if (llvm::isa<llvm::InlineAsm>(I.getCalledValue()))
|
|
return visitInlineAsm(I);
|
|
|
|
bool WroteCallee = false;
|
|
|
|
// Handle intrinsic function calls first...
|
|
if (llvm::Function *F = I.getCalledFunction())
|
|
if (llvm::Intrinsic::ID ID = (llvm::Intrinsic::ID)F->getIntrinsicID())
|
|
if (visitBuiltinCall(I, ID, WroteCallee))
|
|
return;
|
|
|
|
llvm::Value *Callee = I.getCalledValue();
|
|
|
|
llvm::PointerType *PTy = llvm::cast<llvm::PointerType>(Callee->getType());
|
|
llvm::FunctionType *FTy = llvm::cast<llvm::FunctionType>(PTy->getElementType());
|
|
|
|
// If this is a call to a struct-return function, assign to the first
|
|
// parameter instead of passing it to the call.
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
const llvm::AttrListPtr &PAL = I.getAttributes();
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
const llvm::AttributeSet &PAL = I.getAttributes();
|
|
#else // LLVM 5.0+
|
|
const llvm::AttributeList &PAL = I.getAttributes();
|
|
#endif
|
|
|
|
bool hasByVal = I.hasByValArgument();
|
|
bool isStructRet = (I.getNumArgOperands() > 0) && I.hasStructRetAttr();
|
|
if (isStructRet) {
|
|
writeOperandDeref(I.getArgOperand(0));
|
|
Out << " = ";
|
|
}
|
|
|
|
if (I.isTailCall()) Out << " /*tail*/ ";
|
|
|
|
if (!WroteCallee) {
|
|
// If this is an indirect call to a struct return function, we need to cast
|
|
// the pointer. Ditto for indirect calls with byval arguments.
|
|
bool NeedsCast = (hasByVal || isStructRet) && !llvm::isa<llvm::Function>(Callee);
|
|
|
|
// GCC is a real PITA. It does not permit codegening casts of functions to
|
|
// function pointers if they are in a call (it generates a trap instruction
|
|
// instead!). We work around this by inserting a cast to void* in between
|
|
// the function and the function pointer cast. Unfortunately, we can't just
|
|
// form the constant expression here, because the folder will immediately
|
|
// nuke it.
|
|
//
|
|
// Note finally, that this is completely unsafe. ANSI C does not guarantee
|
|
// that void* and function pointers have the same size. :( To deal with this
|
|
// in the common case, we handle casts where the number of arguments passed
|
|
// match exactly.
|
|
//
|
|
if (llvm::ConstantExpr *CE = llvm::dyn_cast<llvm::ConstantExpr>(Callee))
|
|
if (CE->isCast())
|
|
if (llvm::Function *RF = llvm::dyn_cast<llvm::Function>(CE->getOperand(0))) {
|
|
NeedsCast = true;
|
|
Callee = RF;
|
|
}
|
|
|
|
if (Callee->getName() == "malloc" ||
|
|
Callee->getName() == "_aligned_malloc")
|
|
Out << "(uint8_t *)";
|
|
|
|
// This 'if' will fix 'soa-18.ispc' test (fails with optimizations off)
|
|
// Yet the way the case is fixed is quite dirty and leads to many other fails
|
|
|
|
//if (Callee->getName() == "__masked_store_i64") {
|
|
// llvm::CallSite CS(&I);
|
|
// llvm::CallSite::arg_iterator AI = CS.arg_begin();
|
|
// if (is_vec16_i64_ty(llvm::cast<llvm::PointerType>((*AI)->getType())->getElementType())) {
|
|
// Out << "/* Replacing store of vec16_i64 val into &vec16_i64 pointer with a simple copy */\n";
|
|
// // If we are trying to get a pointer to from a vec16_i64 var
|
|
// // It would be better to replace this instruction with a masked copy
|
|
// if (llvm::isa<llvm::GetElementPtrInst>(*AI)) {
|
|
// writeOperandDeref(*AI);
|
|
// Out << " = __select(";
|
|
// writeOperand(*(AI+2));
|
|
// Out << ", ";
|
|
// writeOperand(*(AI+1));
|
|
// Out << ", ";
|
|
// writeOperandDeref(*AI);
|
|
// Out << ")";
|
|
// return;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
if (NeedsCast) {
|
|
// Ok, just cast the pointer type.
|
|
Out << "((";
|
|
if (isStructRet)
|
|
printStructReturnPointerFunctionType(Out, PAL,
|
|
llvm::cast<llvm::PointerType>(I.getCalledValue()->getType()));
|
|
else if (hasByVal)
|
|
printType(Out, I.getCalledValue()->getType(), false, "", true, PAL);
|
|
else
|
|
printType(Out, I.getCalledValue()->getType());
|
|
Out << ")(void*)";
|
|
}
|
|
writeOperand(Callee);
|
|
if (NeedsCast) Out << ')';
|
|
}
|
|
|
|
Out << '(';
|
|
|
|
bool PrintedArg = false;
|
|
if(FTy->isVarArg() && !FTy->getNumParams()) {
|
|
Out << "0 /*dummy arg*/";
|
|
PrintedArg = true;
|
|
}
|
|
|
|
unsigned NumDeclaredParams = FTy->getNumParams();
|
|
llvm::CallSite CS(&I);
|
|
llvm::CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
|
|
unsigned ArgNo = 0;
|
|
if (isStructRet) { // Skip struct return argument.
|
|
++AI;
|
|
++ArgNo;
|
|
}
|
|
|
|
|
|
for (; AI != AE; ++AI, ++ArgNo) {
|
|
if (PrintedArg) Out << ", ";
|
|
if (ArgNo == 0 &&
|
|
Callee->getName() == "posix_memalign") {
|
|
// uint8_t** is incompatible with void** without explicit cast.
|
|
// Should be do this any other functions?
|
|
Out << "(void **)";
|
|
}
|
|
else if (ArgNo < NumDeclaredParams &&
|
|
(*AI)->getType() != FTy->getParamType(ArgNo)) {
|
|
Out << '(';
|
|
printType(Out, FTy->getParamType(ArgNo),
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
PAL.getParamAttributes(ArgNo+1).hasAttribute(llvm::Attributes::SExt)
|
|
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_4_0
|
|
PAL.getParamAttributes(ArgNo+1).hasAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::SExt)
|
|
#else // LLVM 5.0+
|
|
PAL.getParamAttributes(ArgNo+1).hasAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::SExt)
|
|
#endif
|
|
);
|
|
Out << ')';
|
|
}
|
|
// Check if the argument is expected to be passed by value.
|
|
if (I.paramHasAttr(ArgNo+1,
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
llvm::Attributes::ByVal
|
|
#else /* LLVM 3.3+ */
|
|
llvm::Attribute::ByVal
|
|
#endif
|
|
)) {
|
|
writeOperandDeref(*AI);
|
|
}
|
|
else {
|
|
writeOperand(*AI);
|
|
}
|
|
PrintedArg = true;
|
|
}
|
|
Out << ')';
|
|
}
|
|
|
|
/// visitBuiltinCall - Handle the call to the specified builtin. Returns true
|
|
/// if the entire call is handled, return false if it wasn't handled, and
|
|
/// optionally set 'WroteCallee' if the callee has already been printed out.
|
|
bool CWriter::visitBuiltinCall(llvm::CallInst &I, llvm::Intrinsic::ID ID,
|
|
bool &WroteCallee) {
|
|
switch (ID) {
|
|
default: {
|
|
// If this is an intrinsic that directly corresponds to a GCC
|
|
// builtin, we emit it here.
|
|
const char *BuiltinName = "";
|
|
#define GET_GCC_BUILTIN_NAME
|
|
#define Intrinsic llvm::Intrinsic
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
#include "llvm/Intrinsics.gen"
|
|
#else /* LLVM 3.3+ */
|
|
#include "llvm/IR/Intrinsics.gen"
|
|
#endif
|
|
#undef Intrinsic
|
|
#undef GET_GCC_BUILTIN_NAME
|
|
assert(BuiltinName[0] && "Unknown LLVM intrinsic!");
|
|
|
|
Out << BuiltinName;
|
|
WroteCallee = true;
|
|
return false;
|
|
}
|
|
// Ignoring debug intrinsics
|
|
case llvm::Intrinsic::dbg_value:
|
|
case llvm::Intrinsic::dbg_declare:
|
|
return true;
|
|
case llvm::Intrinsic::vastart:
|
|
Out << "0; ";
|
|
|
|
Out << "va_start(*(va_list*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
// Output the last argument to the enclosing function.
|
|
if (I.getParent()->getParent()->arg_empty())
|
|
Out << "vararg_dummy_arg";
|
|
else
|
|
writeOperand(&*(std::prev(I.getParent()->getParent()->arg_end())));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::vaend:
|
|
if (!llvm::isa<llvm::ConstantPointerNull>(I.getArgOperand(0))) {
|
|
Out << "0; va_end(*(va_list*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ')';
|
|
} else {
|
|
Out << "va_end(*(va_list*)0)";
|
|
}
|
|
return true;
|
|
case llvm::Intrinsic::vacopy:
|
|
Out << "0; ";
|
|
Out << "va_copy(*(va_list*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", *(va_list*)";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::returnaddress:
|
|
Out << "__builtin_return_address(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::frameaddress:
|
|
Out << "__builtin_frame_address(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::powi:
|
|
Out << "__builtin_powi(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::fabs:
|
|
Out << "__builtin_fabs(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::setjmp:
|
|
Out << "setjmp(*(jmp_buf*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::longjmp:
|
|
Out << "longjmp(*(jmp_buf*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::memset:
|
|
Out << "Memset(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(2));
|
|
Out << ')';
|
|
return true;
|
|
case llvm::Intrinsic::prefetch:
|
|
Out << "LLVM_PREFETCH((const void *)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(2));
|
|
Out << ")";
|
|
return true;
|
|
case llvm::Intrinsic::stacksave:
|
|
// Emit this as: Val = 0; *((void**)&Val) = __builtin_stack_save()
|
|
// to work around GCC bugs (see PR1809).
|
|
Out << "0; *((void**)&" << GetValueName(&I)
|
|
<< ") = __builtin_stack_save()";
|
|
return true;
|
|
case llvm::Intrinsic::x86_sse_cmp_ss:
|
|
case llvm::Intrinsic::x86_sse_cmp_ps:
|
|
case llvm::Intrinsic::x86_sse2_cmp_sd:
|
|
case llvm::Intrinsic::x86_sse2_cmp_pd:
|
|
Out << '(';
|
|
printType(Out, I.getType());
|
|
Out << ')';
|
|
// Multiple GCC builtins multiplex onto this intrinsic.
|
|
switch (llvm::cast<llvm::ConstantInt>(I.getArgOperand(2))->getZExtValue()) {
|
|
default: llvm_unreachable("Invalid llvm.x86.sse.cmp!");
|
|
case 0: Out << "__builtin_ia32_cmpeq"; break;
|
|
case 1: Out << "__builtin_ia32_cmplt"; break;
|
|
case 2: Out << "__builtin_ia32_cmple"; break;
|
|
case 3: Out << "__builtin_ia32_cmpunord"; break;
|
|
case 4: Out << "__builtin_ia32_cmpneq"; break;
|
|
case 5: Out << "__builtin_ia32_cmpnlt"; break;
|
|
case 6: Out << "__builtin_ia32_cmpnle"; break;
|
|
case 7: Out << "__builtin_ia32_cmpord"; break;
|
|
}
|
|
if (ID == llvm::Intrinsic::x86_sse_cmp_ps || ID == llvm::Intrinsic::x86_sse2_cmp_pd)
|
|
Out << 'p';
|
|
else
|
|
Out << 's';
|
|
if (ID == llvm::Intrinsic::x86_sse_cmp_ss || ID == llvm::Intrinsic::x86_sse_cmp_ps)
|
|
Out << 's';
|
|
else
|
|
Out << 'd';
|
|
|
|
Out << "(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ")";
|
|
return true;
|
|
case llvm::Intrinsic::ppc_altivec_lvsl:
|
|
Out << '(';
|
|
printType(Out, I.getType());
|
|
Out << ')';
|
|
Out << "__builtin_altivec_lvsl(0, (void*)";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ")";
|
|
return true;
|
|
case llvm::Intrinsic::uadd_with_overflow:
|
|
case llvm::Intrinsic::sadd_with_overflow:
|
|
case llvm::Intrinsic::umul_with_overflow:
|
|
Out << GetValueName(I.getCalledFunction()) << "(";
|
|
writeOperand(I.getArgOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getArgOperand(1));
|
|
Out << ")";
|
|
return true;
|
|
case llvm::Intrinsic::trap:
|
|
Out << "abort()";
|
|
return true;
|
|
case llvm::Intrinsic::objectsize:
|
|
return true;
|
|
case llvm::Intrinsic::readcyclecounter:
|
|
Out << "__clock()";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
//TODO: assumptions about what consume arguments from the call are likely wrong
|
|
// handle communitivity
|
|
void CWriter::visitInlineAsm(llvm::CallInst &CI) {
|
|
assert(!"Inline assembly not supported");
|
|
}
|
|
|
|
void CWriter::visitAllocaInst(llvm::AllocaInst &I) {
|
|
Out << '(';
|
|
printType(Out, I.getType());
|
|
Out << ") alloca(sizeof(";
|
|
printType(Out, I.getType()->getElementType());
|
|
Out << ')';
|
|
if (I.isArrayAllocation()) {
|
|
Out << " * " ;
|
|
writeOperand(I.getOperand(0));
|
|
}
|
|
Out << ')';
|
|
}
|
|
|
|
void CWriter::printGEPExpression(llvm::Value *Ptr, llvm::gep_type_iterator I,
|
|
llvm::gep_type_iterator E, bool Static) {
|
|
|
|
// If there are no indices, just print out the pointer.
|
|
if (I == E) {
|
|
writeOperand(Ptr);
|
|
return;
|
|
}
|
|
|
|
// Find out if the last index is into a vector. If so, we have to print this
|
|
// specially. Since vectors can't have elements of indexable type, only the
|
|
// last index could possibly be of a vector element.
|
|
llvm::VectorType *LastIndexIsVector = 0;
|
|
{
|
|
for (llvm::gep_type_iterator TmpI = I; TmpI != E; ++TmpI)
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
|
|
LastIndexIsVector = llvm::dyn_cast<llvm::VectorType>(*TmpI);
|
|
#else // LLVM 4.0+
|
|
LastIndexIsVector = llvm::dyn_cast<llvm::VectorType>(TmpI.getIndexedType());
|
|
#endif
|
|
}
|
|
|
|
Out << "(";
|
|
|
|
// If the last index is into a vector, we can't print it as &a[i][j] because
|
|
// we can't index into a vector with j in GCC. Instead, emit this as
|
|
// (((float*)&a[i])+j)
|
|
if (LastIndexIsVector) {
|
|
Out << "((";
|
|
printType(Out, llvm::PointerType::getUnqual(LastIndexIsVector->getElementType()));
|
|
Out << ")(";
|
|
}
|
|
|
|
Out << '&';
|
|
|
|
// If the first index is 0 (very typical) we can do a number of
|
|
// simplifications to clean up the code.
|
|
llvm::Value *FirstOp = I.getOperand();
|
|
if (!llvm::isa<llvm::Constant>(FirstOp) || !llvm::cast<llvm::Constant>(FirstOp)->isNullValue()) {
|
|
// First index isn't simple, print it the hard way.
|
|
writeOperand(Ptr);
|
|
} else {
|
|
++I; // Skip the zero index.
|
|
|
|
// Okay, emit the first operand. If Ptr is something that is already address
|
|
// exposed, like a global, avoid emitting (&foo)[0], just emit foo instead.
|
|
if (isAddressExposed(Ptr)) {
|
|
writeOperandInternal(Ptr, Static);
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
|
|
} else if (I != E && (*I)->isStructTy()) {
|
|
#else // LLVM 4.0+
|
|
} else if (I != E && I.isStruct()) {
|
|
#endif
|
|
// If we didn't already emit the first operand, see if we can print it as
|
|
// P->f instead of "P[0].f"
|
|
writeOperand(Ptr);
|
|
Out << "->field" << llvm::cast<llvm::ConstantInt>(I.getOperand())->getZExtValue();
|
|
++I; // eat the struct index as well.
|
|
} else {
|
|
// Instead of emitting P[0][1], emit (*P)[1], which is more idiomatic.
|
|
Out << "(*";
|
|
writeOperand(Ptr);
|
|
Out << ")";
|
|
}
|
|
}
|
|
|
|
for (; I != E; ++I) {
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
|
|
llvm::Type *type = *I;
|
|
#else // LLVM 4.0+
|
|
llvm::Type *type = I.getIndexedType();
|
|
#endif
|
|
if (type->isStructTy()) {
|
|
Out << ".field" << llvm::cast<llvm::ConstantInt>(I.getOperand())->getZExtValue();
|
|
} else if (type->isArrayTy()) {
|
|
Out << ".array[";
|
|
writeOperandWithCast(I.getOperand(), llvm::Instruction::GetElementPtr);
|
|
Out << ']';
|
|
} else if (!type->isVectorTy()) {
|
|
Out << '[';
|
|
writeOperandWithCast(I.getOperand(), llvm::Instruction::GetElementPtr);
|
|
Out << ']';
|
|
} else {
|
|
// If the last index is into a vector, then print it out as "+j)". This
|
|
// works with the 'LastIndexIsVector' code above.
|
|
if (llvm::isa<llvm::Constant>(I.getOperand()) &&
|
|
llvm::cast<llvm::Constant>(I.getOperand())->isNullValue()) {
|
|
Out << "))"; // avoid "+0".
|
|
} else {
|
|
Out << ")+(";
|
|
writeOperandWithCast(I.getOperand(), llvm::Instruction::GetElementPtr);
|
|
Out << "))";
|
|
}
|
|
}
|
|
}
|
|
Out << ")";
|
|
}
|
|
|
|
void CWriter::writeMemoryAccess(llvm::Value *Operand, llvm::Type *OperandType,
|
|
bool IsVolatile, unsigned Alignment) {
|
|
assert(!llvm::isa<llvm::VectorType>(OperandType));
|
|
bool IsUnaligned = Alignment &&
|
|
Alignment < TD->getABITypeAlignment(OperandType);
|
|
|
|
llvm::IntegerType *ITy = llvm::dyn_cast<llvm::IntegerType>(OperandType);
|
|
if (!IsUnaligned)
|
|
Out << '*';
|
|
if (IsVolatile || IsUnaligned) {
|
|
Out << "((";
|
|
if (IsUnaligned && ITy && (ITy->getBitWidth() > 64))
|
|
Out << "iN_" << ITy->getBitWidth() << "_align_" << Alignment << " *)";
|
|
else {
|
|
if (IsUnaligned)
|
|
Out << "struct __attribute__ ((packed, aligned(" << Alignment << "))) {";
|
|
printType(Out, OperandType, false, IsUnaligned ? "data" : "volatile*");
|
|
if (IsUnaligned) {
|
|
Out << "; } ";
|
|
if (IsVolatile) Out << "volatile ";
|
|
Out << "*";
|
|
}
|
|
Out << ")";
|
|
}
|
|
}
|
|
|
|
writeOperand(Operand);
|
|
|
|
if (IsVolatile || IsUnaligned) {
|
|
Out << ')';
|
|
if (IsUnaligned)
|
|
Out << "->data";
|
|
}
|
|
}
|
|
|
|
void CWriter::visitLoadInst(llvm::LoadInst &I) {
|
|
llvm::VectorType *VT = llvm::dyn_cast<llvm::VectorType>(I.getType());
|
|
if (VT != NULL) {
|
|
Out << "__load<" << I.getAlignment() << ">(";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
writeMemoryAccess(I.getOperand(0), I.getType(), I.isVolatile(),
|
|
I.getAlignment());
|
|
}
|
|
|
|
void CWriter::visitStoreInst(llvm::StoreInst &I) {
|
|
llvm::VectorType *VT = llvm::dyn_cast<llvm::VectorType>(I.getOperand(0)->getType());
|
|
if (VT != NULL) {
|
|
Out << "__store<" << I.getAlignment() << ">(";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ", ";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ")";
|
|
return;
|
|
}
|
|
|
|
writeMemoryAccess(I.getPointerOperand(), I.getOperand(0)->getType(),
|
|
I.isVolatile(), I.getAlignment());
|
|
Out << " = ";
|
|
llvm::Value *Operand = I.getOperand(0);
|
|
llvm::Constant *BitMask = 0;
|
|
if (llvm::IntegerType* ITy = llvm::dyn_cast<llvm::IntegerType>(Operand->getType()))
|
|
if (!ITy->isPowerOf2ByteWidth())
|
|
// We have a bit width that doesn't match an even power-of-2 byte
|
|
// size. Consequently we must & the value with the type's bit mask
|
|
BitMask = llvm::ConstantInt::get(ITy, ITy->getBitMask());
|
|
if (BitMask)
|
|
Out << "((";
|
|
writeOperand(Operand);
|
|
if (BitMask) {
|
|
Out << ") & ";
|
|
printConstant(BitMask, false);
|
|
Out << ")";
|
|
}
|
|
}
|
|
|
|
void CWriter::visitGetElementPtrInst(llvm::GetElementPtrInst &I) {
|
|
printGEPExpression(I.getPointerOperand(), gep_type_begin(I),
|
|
gep_type_end(I), false);
|
|
}
|
|
|
|
void CWriter::visitVAArgInst(llvm::VAArgInst &I) {
|
|
Out << "va_arg(*(va_list*)";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
printType(Out, I.getType());
|
|
Out << ");\n ";
|
|
}
|
|
|
|
void CWriter::visitInsertElementInst(llvm::InsertElementInst &I) {
|
|
#if 0
|
|
Type *EltTy = I.getType()->getElementType();
|
|
writeOperand(I.getOperand(0));
|
|
Out << ";\n ";
|
|
Out << "((";
|
|
printType(Out, llvm::PointerType::getUnqual(EltTy));
|
|
Out << ")(&" << GetValueName(&I) << "))[";
|
|
writeOperand(I.getOperand(2));
|
|
Out << "] = (";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
#else
|
|
writeOperand(I.getOperand(0));
|
|
Out << ";\n ";
|
|
Out << "__insert_element(&" << GetValueName(&I) << ", ";
|
|
writeOperand(I.getOperand(2));
|
|
Out << ", ";
|
|
writeOperand(I.getOperand(1));
|
|
Out << ")";
|
|
#endif
|
|
}
|
|
|
|
void CWriter::visitExtractElementInst(llvm::ExtractElementInst &I) {
|
|
// We know that our operand is not inlined.
|
|
#if 0
|
|
Out << "((";
|
|
Type *EltTy =
|
|
llvm::cast<llvm::VectorType>(I.getOperand(0)->getType())->getElementType();
|
|
printType(Out, llvm::PointerType::getUnqual(EltTy));
|
|
Out << ")(&" << GetValueName(I.getOperand(0)) << "))[";
|
|
writeOperand(I.getOperand(1));
|
|
Out << "]";
|
|
#else
|
|
Out << "(__extract_element(";
|
|
writeOperand(I.getOperand(0));
|
|
Out << ", ";
|
|
writeOperand(I.getOperand(1));
|
|
Out << "))";
|
|
#endif
|
|
}
|
|
|
|
void CWriter::visitShuffleVectorInst(llvm::ShuffleVectorInst &SVI) {
|
|
printType(Out, SVI.getType());
|
|
Out << "(";
|
|
llvm::VectorType *VT = SVI.getType();
|
|
unsigned NumElts = VT->getNumElements();
|
|
llvm::Type *EltTy = VT->getElementType();
|
|
llvm::VectorType *OpTy = llvm::dyn_cast<llvm::VectorType>(SVI.getOperand(0)->getType());
|
|
unsigned OpElts = OpTy->getNumElements();
|
|
|
|
for (unsigned i = 0; i != NumElts; ++i) {
|
|
if (i) Out << ", ";
|
|
int SrcVal = SVI.getMaskValue(i);
|
|
if ((unsigned)SrcVal >= 2*OpElts) {
|
|
Out << " 0/*undef*/ ";
|
|
} else {
|
|
llvm::Value *Op = SVI.getOperand((unsigned)SrcVal >= OpElts);
|
|
SrcVal &= OpElts - 1;
|
|
|
|
if (llvm::isa<llvm::ConstantVector>(Op)) {
|
|
printConstant(llvm::cast<llvm::ConstantVector>(Op)->getOperand(SrcVal),
|
|
false);
|
|
} else if (llvm::isa<llvm::ConstantAggregateZero>(Op) || llvm::isa<llvm::UndefValue>(Op)) {
|
|
Out << "0";
|
|
}
|
|
else {
|
|
// Do an extractelement of this value from the appropriate input.
|
|
Out << " \n#if defined(KNC) \n";
|
|
if (OpElts != 1) { // all __vec16_* have overloaded operator []
|
|
Out << "(" << GetValueName(Op)
|
|
<< ")[" << SrcVal << "]";
|
|
}
|
|
else { // but __vec1_* don't have it
|
|
Out << "((";
|
|
printType(Out, llvm::PointerType::getUnqual(EltTy));
|
|
Out << ")(&" << GetValueName(Op)
|
|
<< "))[" << SrcVal << "]";
|
|
}
|
|
Out << " \n#else \n";
|
|
Out << "((";
|
|
printType(Out, llvm::PointerType::getUnqual(EltTy));
|
|
Out << ")(&" << GetValueName(Op)
|
|
<< "))[" << SrcVal << "]";
|
|
Out << " \n#endif \n";
|
|
}
|
|
}
|
|
}
|
|
Out << ")";
|
|
}
|
|
|
|
void CWriter::visitInsertValueInst(llvm::InsertValueInst &IVI) {
|
|
// Start by copying the entire aggregate value into the result variable.
|
|
writeOperand(IVI.getOperand(0));
|
|
Out << ";\n ";
|
|
|
|
// Then do the insert to update the field.
|
|
Out << GetValueName(&IVI);
|
|
for (const unsigned *b = IVI.idx_begin(), *i = b, *e = IVI.idx_end();
|
|
i != e; ++i) {
|
|
llvm::Type *IndexedTy = (b == i) ? IVI.getOperand(0)->getType() :
|
|
llvm::ExtractValueInst::getIndexedType(IVI.getOperand(0)->getType(),
|
|
llvm::makeArrayRef(b, i));
|
|
if (IndexedTy->isArrayTy())
|
|
Out << ".array[" << *i << "]";
|
|
else
|
|
Out << ".field" << *i;
|
|
}
|
|
Out << " = ";
|
|
writeOperand(IVI.getOperand(1));
|
|
}
|
|
|
|
void CWriter::visitExtractValueInst(llvm::ExtractValueInst &EVI) {
|
|
Out << "(";
|
|
if (llvm::isa<llvm::UndefValue>(EVI.getOperand(0))) {
|
|
// FIXME: need to handle these--a 0 initializer won't do...
|
|
assert(!llvm::isa<llvm::VectorType>(EVI.getType()));
|
|
Out << "(";
|
|
printType(Out, EVI.getType());
|
|
Out << ") 0/*UNDEF*/";
|
|
} else {
|
|
Out << GetValueName(EVI.getOperand(0));
|
|
for (const unsigned *b = EVI.idx_begin(), *i = b, *e = EVI.idx_end();
|
|
i != e; ++i) {
|
|
llvm::Type *IndexedTy = (b == i) ? EVI.getOperand(0)->getType() :
|
|
llvm::ExtractValueInst::getIndexedType(EVI.getOperand(0)->getType(),
|
|
llvm::makeArrayRef(b, i));
|
|
if (IndexedTy->isArrayTy())
|
|
Out << ".array[" << *i << "]";
|
|
else
|
|
Out << ".field" << *i;
|
|
}
|
|
}
|
|
Out << ")";
|
|
}
|
|
|
|
void CWriter::visitAtomicRMWInst(llvm::AtomicRMWInst &AI) {
|
|
Out << "(";
|
|
Out << "__atomic_";
|
|
switch (AI.getOperation()) {
|
|
default: llvm_unreachable("Unhandled case in visitAtomicRMWInst!");
|
|
case llvm::AtomicRMWInst::Add: Out << "add"; break;
|
|
case llvm::AtomicRMWInst::Sub: Out << "sub"; break;
|
|
case llvm::AtomicRMWInst::Xchg: Out << "xchg"; break;
|
|
case llvm::AtomicRMWInst::And: Out << "and"; break;
|
|
case llvm::AtomicRMWInst::Nand: Out << "nand"; break;
|
|
case llvm::AtomicRMWInst::Or: Out << "or"; break;
|
|
case llvm::AtomicRMWInst::Xor: Out << "xor"; break;
|
|
case llvm::AtomicRMWInst::Min: Out << "min"; break;
|
|
case llvm::AtomicRMWInst::Max: Out << "max"; break;
|
|
case llvm::AtomicRMWInst::UMin: Out << "umin"; break;
|
|
case llvm::AtomicRMWInst::UMax: Out << "umax"; break;
|
|
}
|
|
Out << "(";
|
|
writeOperand(AI.getOperand(0));
|
|
Out << ", ";
|
|
writeOperand(AI.getOperand(1));
|
|
Out << "))";
|
|
}
|
|
|
|
void CWriter::visitAtomicCmpXchgInst(llvm::AtomicCmpXchgInst &ACXI) {
|
|
Out << "(";
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
printType(Out, ACXI.getType(), false);
|
|
Out << "::init("; // LLVM cmpxchg returns a struct, so we need make an assighment properly
|
|
#endif
|
|
Out << "__atomic_cmpxchg(";
|
|
writeOperand(ACXI.getPointerOperand());
|
|
Out << ", ";
|
|
writeOperand(ACXI.getCompareOperand());
|
|
Out << ", ";
|
|
writeOperand(ACXI.getNewValOperand());
|
|
Out << ")";
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
|
|
Out << ", true /* There is no way to learn the value of this bit inside ISPC, so making it constant */)";
|
|
#endif
|
|
Out << ")";
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// SmearCleanupPass
|
|
|
|
class SmearCleanupPass : public llvm::BasicBlockPass {
|
|
public:
|
|
SmearCleanupPass(llvm::Module *m, int width)
|
|
: BasicBlockPass(ID) { module = m; vectorWidth = width; }
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
const char *getPassName() const { return "Smear Cleanup Pass"; }
|
|
#else // LLVM 4.0+
|
|
llvm::StringRef getPassName() const { return "Smear Cleanup Pass"; }
|
|
#endif
|
|
bool runOnBasicBlock(llvm::BasicBlock &BB);
|
|
|
|
static char ID;
|
|
llvm::Module *module;
|
|
unsigned int vectorWidth;
|
|
|
|
private:
|
|
unsigned int ChainLength(llvm::InsertElementInst *inst) const;
|
|
llvm::Value *getInsertChainSmearValue(llvm::Instruction* inst) const;
|
|
llvm::Value *getShuffleSmearValue(llvm::Instruction* inst) const;
|
|
};
|
|
|
|
|
|
char SmearCleanupPass::ID = 0;
|
|
|
|
|
|
unsigned int
|
|
SmearCleanupPass::ChainLength(llvm::InsertElementInst *inst) const {
|
|
unsigned int length = 0;
|
|
while (inst != NULL) {
|
|
++length;
|
|
inst = llvm::dyn_cast<llvm::InsertElementInst>(inst->getOperand(0));
|
|
}
|
|
return length;
|
|
}
|
|
|
|
|
|
llvm::Value *
|
|
SmearCleanupPass::getInsertChainSmearValue(llvm::Instruction* inst) const {
|
|
// TODO: we don't check indexes where we do insertion, so we may trigger
|
|
// transformation for a wrong chain.
|
|
// This way of doing broadcast is obsolete and should be probably removed
|
|
// some day.
|
|
|
|
llvm::InsertElementInst *insertInst =
|
|
llvm::dyn_cast<llvm::InsertElementInst>(inst);
|
|
if (!insertInst) {
|
|
return NULL;
|
|
}
|
|
|
|
// We consider only chians of vectorWidth length.
|
|
if (ChainLength(insertInst) != vectorWidth) {
|
|
return NULL;
|
|
}
|
|
|
|
// FIXME: we only want to do this to vectors with width equal to
|
|
// the target vector width. But we can't easily get that here, so
|
|
// for now we at least avoid one case where we definitely don't
|
|
// want to do this.
|
|
llvm::VectorType *vt = llvm::dyn_cast<llvm::VectorType>(insertInst->getType());
|
|
if (vt->getNumElements() == 1) {
|
|
return NULL;
|
|
}
|
|
|
|
llvm::Value *smearValue = NULL;
|
|
while (insertInst != NULL) {
|
|
// operand 1 is inserted value
|
|
llvm::Value *insertValue = insertInst->getOperand(1);
|
|
if (smearValue == NULL) {
|
|
smearValue = insertValue;
|
|
}
|
|
else if (smearValue != insertValue) {
|
|
return NULL;
|
|
}
|
|
|
|
// operand 0 is a vector to insert into.
|
|
insertInst =
|
|
llvm::dyn_cast<llvm::InsertElementInst>(insertInst->getOperand(0));
|
|
}
|
|
assert(smearValue != NULL);
|
|
|
|
return smearValue;
|
|
}
|
|
|
|
|
|
llvm::Value *
|
|
SmearCleanupPass::getShuffleSmearValue(llvm::Instruction* inst) const {
|
|
llvm::ShuffleVectorInst *shuffleInst =
|
|
llvm::dyn_cast<llvm::ShuffleVectorInst>(inst);
|
|
if (!shuffleInst) {
|
|
return NULL;
|
|
}
|
|
|
|
llvm::Constant* mask =
|
|
llvm::dyn_cast<llvm::Constant>(shuffleInst->getOperand(2));
|
|
|
|
// Check that the shuffle is a broadcast of the element of the first vector,
|
|
// i.e. mask vector is vector with equal elements of expected size.
|
|
if (!(mask &&
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
(mask->isNullValue() || (shuffleInst->getMask()->getType()->isVectorTy() && llvm::dyn_cast<llvm::ConstantVector>(shuffleInst->getMask())->getSplatValue() != 0 ) ) &&
|
|
#else
|
|
(mask->isNullValue() || (shuffleInst->getMask()->getSplatValue() != 0))&&
|
|
#endif
|
|
llvm::dyn_cast<llvm::VectorType>(mask->getType())->getNumElements() == vectorWidth)) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
llvm::InsertElementInst *insertInst =
|
|
llvm::dyn_cast<llvm::InsertElementInst>(shuffleInst->getOperand(0));
|
|
|
|
// Check that it's an InsertElementInst that inserts a value to first element.
|
|
if (!(insertInst &&
|
|
llvm::isa<llvm::Constant>(insertInst->getOperand(2)) &&
|
|
llvm::dyn_cast<llvm::Constant>(insertInst->getOperand(2))->isNullValue())) {
|
|
|
|
// We can't extract element from vec1
|
|
llvm::VectorType *operandVec = llvm::dyn_cast<llvm::VectorType>(shuffleInst->getOperand(0)->getType());
|
|
if (operandVec && operandVec->getNumElements() == 1)
|
|
return NULL;
|
|
|
|
// Insert ExtractElementInstr to get value for smear
|
|
|
|
llvm::Function *extractFunc = module->getFunction("__extract_element");
|
|
|
|
if (extractFunc == NULL) {
|
|
// Declare the __extract_element function if needed; it takes a vector and
|
|
// a scalar parameter and returns a scalar of the vector parameter type.
|
|
llvm::Constant *ef =
|
|
module->getOrInsertFunction("__extract_element",
|
|
shuffleInst->getOperand(0)->getType()->getVectorElementType(),
|
|
shuffleInst->getOperand(0)->getType(),
|
|
llvm::IntegerType::get(module->getContext(), 32), NULL);
|
|
extractFunc = llvm::dyn_cast<llvm::Function>(ef);
|
|
assert(extractFunc != NULL);
|
|
extractFunc->setDoesNotThrow();
|
|
extractFunc->setOnlyReadsMemory();
|
|
}
|
|
|
|
if (extractFunc == NULL) {
|
|
return NULL;
|
|
}
|
|
llvm::Instruction *extractCall =
|
|
llvm::ExtractElementInst::Create(shuffleInst->getOperand(0),
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
// mask is of VectorType
|
|
llvm::dyn_cast<llvm::ConstantVector>(mask)->getSplatValue(),
|
|
#else
|
|
mask->getSplatValue(),
|
|
#endif
|
|
"__extract_element", inst);
|
|
return extractCall;
|
|
}
|
|
|
|
llvm::Value *result = insertInst->getOperand(1);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
bool
|
|
SmearCleanupPass::runOnBasicBlock(llvm::BasicBlock &bb) {
|
|
bool modifiedAny = false;
|
|
|
|
restart:
|
|
for (llvm::BasicBlock::iterator iter = bb.begin(), e = bb.end(); iter != e; ++iter) {
|
|
llvm::Value *smearValue = NULL;
|
|
|
|
if (!(smearValue = getInsertChainSmearValue(&*iter)) &&
|
|
!(smearValue = getShuffleSmearValue(&*iter))) {
|
|
continue;
|
|
}
|
|
|
|
llvm::Type *smearType = smearValue->getType();
|
|
const char *smearFuncName = lGetTypedFunc("smear", smearType, vectorWidth);
|
|
if (smearFuncName != NULL) {
|
|
llvm::Function *smearFunc = module->getFunction(smearFuncName);
|
|
if (smearFunc == NULL) {
|
|
// Declare the smear function if needed; it takes a single
|
|
// scalar parameter and returns a vector of the same
|
|
// parameter type.
|
|
llvm::Constant *sf =
|
|
module->getOrInsertFunction(smearFuncName, iter->getType(),
|
|
smearType, NULL);
|
|
smearFunc = llvm::dyn_cast<llvm::Function>(sf);
|
|
assert(smearFunc != NULL);
|
|
smearFunc->setDoesNotThrow();
|
|
smearFunc->setDoesNotAccessMemory();
|
|
}
|
|
|
|
assert(smearFunc != NULL);
|
|
llvm::Value *args[1] = { smearValue };
|
|
llvm::ArrayRef<llvm::Value *> argArray(&args[0], &args[1]);
|
|
llvm::Instruction *smearCall =
|
|
llvm::CallInst::Create(smearFunc, argArray, LLVMGetName(smearValue, "_smear"),
|
|
(llvm::Instruction *)NULL);
|
|
|
|
ReplaceInstWithInst(&*iter, smearCall);
|
|
|
|
modifiedAny = true;
|
|
goto restart;
|
|
}
|
|
}
|
|
|
|
return modifiedAny;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// AndCmpCleanupPass
|
|
|
|
class AndCmpCleanupPass : public llvm::BasicBlockPass {
|
|
public:
|
|
AndCmpCleanupPass()
|
|
: BasicBlockPass(ID) { }
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
const char *getPassName() const { return "AndCmp Cleanup Pass"; }
|
|
#else // LLVM 4.0+
|
|
llvm::StringRef getPassName() const { return "AndCmp Cleanup Pass"; }
|
|
#endif
|
|
bool runOnBasicBlock(llvm::BasicBlock &BB);
|
|
|
|
static char ID;
|
|
};
|
|
|
|
char AndCmpCleanupPass::ID = 0;
|
|
|
|
// Look for ANDs of masks where one of the operands is a vector compare; we
|
|
// can turn these into specialized calls to masked vector compares and
|
|
// thence eliminate the AND. For example, rather than emitting
|
|
// __and(__less(a, b), c), we will emit __less_and_mask(a, b, c).
|
|
bool
|
|
AndCmpCleanupPass::runOnBasicBlock(llvm::BasicBlock &bb) {
|
|
bool modifiedAny = false;
|
|
|
|
restart:
|
|
for (llvm::BasicBlock::iterator iter = bb.begin(), e = bb.end(); iter != e; ++iter) {
|
|
// See if we have an AND instruction
|
|
llvm::BinaryOperator *bop = llvm::dyn_cast<llvm::BinaryOperator>(&*iter);
|
|
if (bop == NULL || bop->getOpcode() != llvm::Instruction::And)
|
|
continue;
|
|
|
|
// Make sure it's a vector AND
|
|
if (llvm::isa<llvm::VectorType>(bop->getType()) == false)
|
|
continue;
|
|
|
|
// We only care about ANDs of the mask type, not, e.g. ANDs of
|
|
// int32s vectors.
|
|
if (bop->getType() != LLVMTypes::MaskType)
|
|
continue;
|
|
|
|
// Now see if either of the operands to the AND is a comparison
|
|
for (int i = 0; i < 2; ++i) {
|
|
llvm::Value *op = bop->getOperand(i);
|
|
llvm::CmpInst *opCmp = llvm::dyn_cast<llvm::CmpInst>(op);
|
|
if (opCmp == NULL)
|
|
continue;
|
|
|
|
// We have a comparison. However, we also need to make sure
|
|
// that it's not comparing two mask values; those can't be
|
|
// simplified to something simpler.
|
|
if (opCmp->getOperand(0)->getType() == LLVMTypes::MaskType)
|
|
break;
|
|
|
|
// Success! Go ahead and replace the AND with a call to the
|
|
// "__and_mask" variant of the comparison function for this
|
|
// operand.
|
|
std::string funcName = lPredicateToString(opCmp->getPredicate());
|
|
funcName += "_";
|
|
funcName += lTypeToSuffix(opCmp->getOperand(0)->getType());
|
|
funcName += "_and_mask";
|
|
|
|
llvm::Function *andCmpFunc = m->module->getFunction(funcName);
|
|
if (andCmpFunc == NULL) {
|
|
// Declare the function if needed; the first two arguments
|
|
// are the same as the two arguments to the compare we're
|
|
// replacing and the third argument is the mask type.
|
|
llvm::Type *cmpOpType = opCmp->getOperand(0)->getType();
|
|
llvm::Constant *acf =
|
|
m->module->getOrInsertFunction(funcName, LLVMTypes::MaskType,
|
|
cmpOpType, cmpOpType,
|
|
LLVMTypes::MaskType, NULL);
|
|
andCmpFunc = llvm::dyn_cast<llvm::Function>(acf);
|
|
Assert(andCmpFunc != NULL);
|
|
andCmpFunc->setDoesNotThrow();
|
|
andCmpFunc->setDoesNotAccessMemory();
|
|
}
|
|
|
|
// Set up the function call to the *_and_mask function; the
|
|
// mask value passed in is the other operand to the AND.
|
|
llvm::Value *args[3] = { opCmp->getOperand(0), opCmp->getOperand(1),
|
|
bop->getOperand(i ^ 1) };
|
|
llvm::ArrayRef<llvm::Value *> argArray(&args[0], &args[3]);
|
|
llvm::Instruction *cmpCall =
|
|
llvm::CallInst::Create(andCmpFunc, argArray,
|
|
LLVMGetName(bop, "_and_mask"),
|
|
(llvm::Instruction *)NULL);
|
|
|
|
// And replace the original AND instruction with it.
|
|
llvm::ReplaceInstWithInst(&*iter, cmpCall);
|
|
|
|
modifiedAny = true;
|
|
goto restart;
|
|
}
|
|
}
|
|
|
|
return modifiedAny;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// MaskOpsCleanupPass
|
|
|
|
/** This pass does various peephole improvements to mask modification
|
|
operations. In particular, it converts mask XORs with "all true" to
|
|
calls to __not() and replaces operations like and(not(a), b) to
|
|
__and_not1(a, b) (and similarly if the second operand has not applied
|
|
to it...)
|
|
*/
|
|
class MaskOpsCleanupPass : public llvm::BasicBlockPass {
|
|
public:
|
|
MaskOpsCleanupPass(llvm::Module *m)
|
|
: BasicBlockPass(ID) {
|
|
llvm::Type *mt = LLVMTypes::MaskType;
|
|
|
|
// Declare the __not, __and_not1, and __and_not2 functions that we
|
|
// expect the target to end up providing.
|
|
notFunc =
|
|
llvm::dyn_cast<llvm::Function>(m->getOrInsertFunction("__not", mt, mt, NULL));
|
|
assert(notFunc != NULL);
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
notFunc->addFnAttr(llvm::Attributes::NoUnwind);
|
|
notFunc->addFnAttr(llvm::Attributes::ReadNone);
|
|
#else /* LLVM 3.3+ */
|
|
notFunc->addFnAttr(llvm::Attribute::NoUnwind);
|
|
notFunc->addFnAttr(llvm::Attribute::ReadNone);
|
|
#endif
|
|
|
|
andNotFuncs[0] =
|
|
llvm::dyn_cast<llvm::Function>(m->getOrInsertFunction("__and_not1", mt, mt, mt,
|
|
NULL));
|
|
assert(andNotFuncs[0] != NULL);
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
andNotFuncs[0]->addFnAttr(llvm::Attributes::NoUnwind);
|
|
andNotFuncs[0]->addFnAttr(llvm::Attributes::ReadNone);
|
|
#else /* LLVM 3.3+ */
|
|
andNotFuncs[0]->addFnAttr(llvm::Attribute::NoUnwind);
|
|
andNotFuncs[0]->addFnAttr(llvm::Attribute::ReadNone);
|
|
#endif
|
|
andNotFuncs[1] =
|
|
llvm::dyn_cast<llvm::Function>(m->getOrInsertFunction("__and_not2", mt, mt, mt,
|
|
NULL));
|
|
assert(andNotFuncs[1] != NULL);
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
andNotFuncs[1]->addFnAttr(llvm::Attributes::NoUnwind);
|
|
andNotFuncs[1]->addFnAttr(llvm::Attributes::ReadNone);
|
|
#else /* LLVM 3.3+ */
|
|
andNotFuncs[1]->addFnAttr(llvm::Attribute::NoUnwind);
|
|
andNotFuncs[1]->addFnAttr(llvm::Attribute::ReadNone);
|
|
#endif
|
|
}
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9 // <= 3.9
|
|
const char *getPassName() const { return "MaskOps Cleanup Pass"; }
|
|
#else // LLVM 4.0+
|
|
llvm::StringRef getPassName() const { return "MaskOps Cleanup Pass"; }
|
|
#endif
|
|
bool runOnBasicBlock(llvm::BasicBlock &BB);
|
|
|
|
private:
|
|
llvm::Value *lGetNotOperand(llvm::Value *v) const;
|
|
|
|
llvm::Function *notFunc, *andNotFuncs[2];
|
|
|
|
static char ID;
|
|
};
|
|
|
|
char MaskOpsCleanupPass::ID = 0;
|
|
|
|
|
|
/** Returns true if the given value is a compile-time constant vector of
|
|
i1s with all elements 'true'.
|
|
*/
|
|
static bool
|
|
lIsAllTrue(llvm::Value *v) {
|
|
if (llvm::ConstantVector *cv = llvm::dyn_cast<llvm::ConstantVector>(v)) {
|
|
llvm::ConstantInt *ci;
|
|
return (cv->getSplatValue() != NULL &&
|
|
(ci = llvm::dyn_cast<llvm::ConstantInt>(cv->getSplatValue())) != NULL &&
|
|
ci->isOne());
|
|
}
|
|
|
|
if (llvm::ConstantDataVector *cdv = llvm::dyn_cast<llvm::ConstantDataVector>(v)) {
|
|
llvm::ConstantInt *ci;
|
|
return (cdv->getSplatValue() != NULL &&
|
|
(ci = llvm::dyn_cast<llvm::ConstantInt>(cdv->getSplatValue())) != NULL &&
|
|
ci->isOne());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/** Checks to see if the given value is the NOT of some other value. If
|
|
so, it returns the operand of the NOT; otherwise returns NULL.
|
|
*/
|
|
llvm::Value *
|
|
MaskOpsCleanupPass::lGetNotOperand(llvm::Value *v) const {
|
|
if (llvm::CallInst *ci = llvm::dyn_cast<llvm::CallInst>(v))
|
|
if (ci->getCalledFunction() == notFunc)
|
|
// Direct call to __not()
|
|
return ci->getArgOperand(0);
|
|
|
|
if (llvm::BinaryOperator *bop = llvm::dyn_cast<llvm::BinaryOperator>(v))
|
|
if (bop->getOpcode() == llvm::Instruction::Xor &&
|
|
lIsAllTrue(bop->getOperand(1)))
|
|
// XOR of all-true vector.
|
|
return bop->getOperand(0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
MaskOpsCleanupPass::runOnBasicBlock(llvm::BasicBlock &bb) {
|
|
bool modifiedAny = false;
|
|
|
|
restart:
|
|
for (llvm::BasicBlock::iterator iter = bb.begin(), e = bb.end(); iter != e; ++iter) {
|
|
llvm::BinaryOperator *bop = llvm::dyn_cast<llvm::BinaryOperator>(&*iter);
|
|
if (bop == NULL)
|
|
continue;
|
|
|
|
if (bop->getType() != LLVMTypes::MaskType)
|
|
continue;
|
|
|
|
if (bop->getOpcode() == llvm::Instruction::Xor) {
|
|
// Check for XOR with all-true values
|
|
if (lIsAllTrue(bop->getOperand(1))) {
|
|
llvm::Value *val = bop->getOperand(0);
|
|
// Note that ArrayRef takes reference to an object, which must live
|
|
// long enough, so passing return value of getOperand directly is
|
|
// incorrect and it actually causes crashes with gcc 4.7 and later.
|
|
llvm::ArrayRef<llvm::Value *> arg(val);
|
|
llvm::CallInst *notCall = llvm::CallInst::Create(notFunc, arg,
|
|
bop->getName());
|
|
ReplaceInstWithInst(&*iter, notCall);
|
|
modifiedAny = true;
|
|
goto restart;
|
|
}
|
|
}
|
|
else if (bop->getOpcode() == llvm::Instruction::And) {
|
|
// Check each of the operands to see if they have NOT applied
|
|
// to them.
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (llvm::Value *notOp = lGetNotOperand(bop->getOperand(i))) {
|
|
// In notOp we have the target of the NOT operation;
|
|
// put it in its appropriate spot in the operand array.
|
|
// Copy in the other operand directly.
|
|
llvm::Value *args[2];
|
|
args[i] = notOp;
|
|
args[i ^ 1] = bop->getOperand(i ^ 1);
|
|
llvm::ArrayRef<llvm::Value *> argsRef(&args[0], 2);
|
|
|
|
// Call the appropriate __and_not* function.
|
|
llvm::CallInst *andNotCall =
|
|
llvm::CallInst::Create(andNotFuncs[i], argsRef, bop->getName());
|
|
|
|
ReplaceInstWithInst(&*iter, andNotCall);
|
|
modifiedAny = true;
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return modifiedAny;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// External Interface declaration
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool
|
|
WriteCXXFile(llvm::Module *module, const char *fn, int vectorWidth,
|
|
const char *includeName) {
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6 // 3.2, 3.3, 3.4, 3.5, 3.6
|
|
llvm::PassManager pm;
|
|
#else // LLVM 3.7+
|
|
llvm::legacy::PassManager pm;
|
|
#endif
|
|
#if 0
|
|
if (const llvm::TargetData *td = targetMachine->getTargetData())
|
|
pm.add(new llvm::TargetData(*td));
|
|
else
|
|
pm.add(new llvm::TargetData(module));
|
|
#endif
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_3 // 3.2, 3.3
|
|
int flags = 0;
|
|
#else // LLVM 3.4+
|
|
llvm::sys::fs::OpenFlags flags = llvm::sys::fs::F_None;
|
|
#endif
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5 // 3.2, 3.3, 3.4, 3.5
|
|
std::string error;
|
|
#else // LLVM 3.6+
|
|
std::error_code error;
|
|
#endif
|
|
|
|
llvm::tool_output_file *of = new llvm::tool_output_file(fn, error, flags);
|
|
|
|
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5 // 3.2, 3.3, 3.4, 3.5
|
|
if (error.size()) {
|
|
#else // LLVM 3.6+
|
|
if (error) {
|
|
#endif
|
|
fprintf(stderr, "Error opening output file \"%s\".\n", fn);
|
|
return false;
|
|
}
|
|
|
|
llvm::formatted_raw_ostream fos(of->os());
|
|
|
|
pm.add(llvm::createGCLoweringPass());
|
|
pm.add(llvm::createLowerInvokePass());
|
|
pm.add(llvm::createCFGSimplificationPass()); // clean up after lower invoke.
|
|
pm.add(new SmearCleanupPass(module, vectorWidth));
|
|
pm.add(new AndCmpCleanupPass());
|
|
pm.add(new MaskOpsCleanupPass(module));
|
|
pm.add(llvm::createDeadCodeEliminationPass()); // clean up after smear pass
|
|
//CO pm.add(llvm::createPrintModulePass(&fos));
|
|
pm.add(new CWriter(fos, includeName, vectorWidth));
|
|
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
|
|
// This interface is depricated for 3.3+
|
|
pm.add(llvm::createGCInfoDeleter());
|
|
#endif
|
|
//CO pm.add(llvm::createVerifierPass());
|
|
|
|
pm.run(*module);
|
|
|
|
return true;
|
|
}
|