Index: lib/Target/JSBackend/JSBackend.cpp |
diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp |
deleted file mode 100644 |
index b0c374e101d00ed1aa7eaa4c0b3f7868ac3540c3..0000000000000000000000000000000000000000 |
--- a/lib/Target/JSBackend/JSBackend.cpp |
+++ /dev/null |
@@ -1,2984 +0,0 @@ |
-//===-- JSBackend.cpp - Library for converting LLVM code to JS -----===// |
-// |
-// The LLVM Compiler Infrastructure |
-// |
-// This file is distributed under the University of Illinois Open Source |
-// License. See LICENSE.TXT for details. |
-// |
-//===----------------------------------------------------------------------===// |
-// |
-// This file implements compiling of LLVM IR, which is assumed to have been |
-// simplified using the PNaCl passes, i64 legalization, and other necessary |
-// transformations, into JavaScript in asm.js format, suitable for passing |
-// to emscripten for final processing. |
-// |
-//===----------------------------------------------------------------------===// |
- |
-#include "JSTargetMachine.h" |
-#include "MCTargetDesc/JSBackendMCTargetDesc.h" |
-#include "AllocaManager.h" |
-#include "llvm/Analysis/ValueTracking.h" |
-#include "llvm/ADT/SmallPtrSet.h" |
-#include "llvm/ADT/SmallString.h" |
-#include "llvm/ADT/StringExtras.h" |
-#include "llvm/ADT/STLExtras.h" |
-#include "llvm/Config/config.h" |
-#include "llvm/IR/Constants.h" |
-#include "llvm/IR/DerivedTypes.h" |
-#include "llvm/IR/InlineAsm.h" |
-#include "llvm/IR/Instruction.h" |
-#include "llvm/IR/Instructions.h" |
-#include "llvm/IR/Intrinsics.h" |
-#include "llvm/IR/Module.h" |
-#include "llvm/IR/Operator.h" |
-#include "llvm/Pass.h" |
-#include "llvm/IR/LegacyPassManager.h" |
-#include "llvm/IR/CallSite.h" |
-#include "llvm/Support/CommandLine.h" |
-#include "llvm/Support/ErrorHandling.h" |
-#include "llvm/IR/GetElementPtrTypeIterator.h" |
-#include "llvm/Support/MathExtras.h" |
-#include "llvm/Support/TargetRegistry.h" |
-#include "llvm/IR/DebugInfo.h" |
-#include "llvm/Transforms/NaCl.h" |
-#include <algorithm> |
-#include <cstdio> |
-#include <map> |
-#include <set> // TODO: unordered_set? |
-using namespace llvm; |
- |
-#include <OptPasses.h> |
-#include <Relooper.h> |
- |
-#ifdef NDEBUG |
-#undef assert |
-#define assert(x) { if (!(x)) report_fatal_error(#x); } |
-#endif |
- |
-raw_ostream &prettyWarning() { |
- errs().changeColor(raw_ostream::YELLOW); |
- errs() << "warning:"; |
- errs().resetColor(); |
- errs() << " "; |
- return errs(); |
-} |
- |
-static cl::opt<bool> |
-PreciseF32("emscripten-precise-f32", |
- cl::desc("Enables Math.fround usage to implement precise float32 semantics and performance (see emscripten PRECISE_F32 option)"), |
- cl::init(false)); |
- |
-static cl::opt<bool> |
-WarnOnUnaligned("emscripten-warn-unaligned", |
- cl::desc("Warns about unaligned loads and stores (which can negatively affect performance)"), |
- cl::init(false)); |
- |
-static cl::opt<int> |
-ReservedFunctionPointers("emscripten-reserved-function-pointers", |
- cl::desc("Number of reserved slots in function tables for functions to be added at runtime (see emscripten RESERVED_FUNCTION_POINTERS option)"), |
- cl::init(0)); |
- |
-static cl::opt<int> |
-EmscriptenAssertions("emscripten-assertions", |
- cl::desc("Additional JS-specific assertions (see emscripten ASSERTIONS)"), |
- cl::init(0)); |
- |
-static cl::opt<bool> |
-NoAliasingFunctionPointers("emscripten-no-aliasing-function-pointers", |
- cl::desc("Forces function pointers to not alias (this is more correct, but rarely needed, and has the cost of much larger function tables; it is useful for debugging though; see emscripten ALIASING_FUNCTION_POINTERS option)"), |
- cl::init(false)); |
- |
-static cl::opt<int> |
-GlobalBase("emscripten-global-base", |
- cl::desc("Where global variables start out in memory (see emscripten GLOBAL_BASE option)"), |
- cl::init(8)); |
- |
- |
-extern "C" void LLVMInitializeJSBackendTarget() { |
- // Register the target. |
- RegisterTargetMachine<JSTargetMachine> X(TheJSBackendTarget); |
-} |
- |
-namespace { |
- #define ASM_SIGNED 0 |
- #define ASM_UNSIGNED 1 |
- #define ASM_NONSPECIFIC 2 // nonspecific means to not differentiate ints. |0 for all, regardless of size and sign |
- #define ASM_FFI_IN 4 // FFI return values are limited to things that work in ffis |
- #define ASM_FFI_OUT 8 // params to FFIs are limited to things that work in ffis |
- #define ASM_MUST_CAST 16 // this value must be explicitly cast (or be an integer constant) |
- typedef unsigned AsmCast; |
- |
- const char *const SIMDLane = "XYZW"; |
- const char *const simdLane = "xyzw"; |
- |
- typedef std::map<const Value*,std::string> ValueMap; |
- typedef std::set<std::string> NameSet; |
- typedef std::vector<unsigned char> HeapData; |
- typedef std::pair<unsigned, unsigned> Address; |
- typedef std::map<std::string, Type *> VarMap; |
- typedef std::map<std::string, Address> GlobalAddressMap; |
- typedef std::vector<std::string> FunctionTable; |
- typedef std::map<std::string, FunctionTable> FunctionTableMap; |
- typedef std::map<std::string, std::string> StringMap; |
- typedef std::map<std::string, unsigned> NameIntMap; |
- typedef std::map<const BasicBlock*, unsigned> BlockIndexMap; |
- typedef std::map<const Function*, BlockIndexMap> BlockAddressMap; |
- typedef std::map<const BasicBlock*, Block*> LLVMToRelooperMap; |
- |
- /// JSWriter - This class is the main chunk of code that converts an LLVM |
- /// module to JavaScript. |
- class JSWriter : public ModulePass { |
- raw_pwrite_stream &Out; |
- const Module *TheModule; |
- unsigned UniqueNum; |
- unsigned NextFunctionIndex; // used with NoAliasingFunctionPointers |
- ValueMap ValueNames; |
- VarMap UsedVars; |
- AllocaManager Allocas; |
- HeapData GlobalData8; |
- HeapData GlobalData32; |
- HeapData GlobalData64; |
- GlobalAddressMap GlobalAddresses; |
- NameSet Externals; // vars |
- NameSet Declares; // funcs |
- StringMap Redirects; // library function redirects actually used, needed for wrapper funcs in tables |
- std::string PostSets; |
- NameIntMap NamedGlobals; // globals that we export as metadata to JS, so it can access them by name |
- std::map<std::string, unsigned> IndexedFunctions; // name -> index |
- FunctionTableMap FunctionTables; // sig => list of functions |
- std::vector<std::string> GlobalInitializers; |
- std::vector<std::string> Exports; // additional exports |
- BlockAddressMap BlockAddresses; |
- |
- std::string CantValidate; |
- bool UsesSIMD; |
- int InvokeState; // cycles between 0, 1 after preInvoke, 2 after call, 0 again after postInvoke. hackish, no argument there. |
- CodeGenOpt::Level OptLevel; |
- const DataLayout *DL; |
- bool StackBumped; |
- |
- #include "CallHandlers.h" |
- |
- public: |
- static char ID; |
- JSWriter(raw_pwrite_stream &o, CodeGenOpt::Level OptLevel) |
- : ModulePass(ID), Out(o), UniqueNum(0), NextFunctionIndex(0), CantValidate(""), UsesSIMD(false), InvokeState(0), |
- OptLevel(OptLevel), StackBumped(false) {} |
- |
- virtual const char *getPassName() const { return "JavaScript backend"; } |
- |
- virtual bool runOnModule(Module &M); |
- |
- virtual void getAnalysisUsage(AnalysisUsage &AU) const { |
- AU.setPreservesAll(); |
- ModulePass::getAnalysisUsage(AU); |
- } |
- |
- void printProgram(const std::string& fname, const std::string& modName ); |
- void printModule(const std::string& fname, const std::string& modName ); |
- void printFunction(const Function *F); |
- |
- LLVM_ATTRIBUTE_NORETURN void error(const std::string& msg); |
- |
- raw_pwrite_stream& nl(raw_pwrite_stream &Out, int delta = 0); |
- |
- private: |
- void printCommaSeparated(const HeapData v); |
- |
- // parsing of constants has two phases: calculate, and then emit |
- void parseConstant(const std::string& name, const Constant* CV, bool calculate); |
- |
- #define MEM_ALIGN 8 |
- #define MEM_ALIGN_BITS 64 |
- #define STACK_ALIGN 16 |
- #define STACK_ALIGN_BITS 128 |
- |
- unsigned stackAlign(unsigned x) { |
- return RoundUpToAlignment(x, STACK_ALIGN); |
- } |
- std::string stackAlignStr(std::string x) { |
- return "((" + x + "+" + utostr(STACK_ALIGN-1) + ")&-" + utostr(STACK_ALIGN) + ")"; |
- } |
- |
- HeapData *allocateAddress(const std::string& Name, unsigned Bits = MEM_ALIGN_BITS) { |
- assert(Bits == 64); // FIXME when we use optimal alignments |
- HeapData *GlobalData = NULL; |
- switch (Bits) { |
- case 8: GlobalData = &GlobalData8; break; |
- case 32: GlobalData = &GlobalData32; break; |
- case 64: GlobalData = &GlobalData64; break; |
- default: llvm_unreachable("Unsupported data element size"); |
- } |
- while (GlobalData->size() % (Bits/8) != 0) GlobalData->push_back(0); |
- GlobalAddresses[Name] = Address(GlobalData->size(), Bits); |
- return GlobalData; |
- } |
- |
- // return the absolute offset of a global |
- unsigned getGlobalAddress(const std::string &s) { |
- GlobalAddressMap::const_iterator I = GlobalAddresses.find(s); |
- if (I == GlobalAddresses.end()) { |
- report_fatal_error("cannot find global address " + Twine(s)); |
- } |
- Address a = I->second; |
- assert(a.second == 64); // FIXME when we use optimal alignments |
- unsigned Ret; |
- switch (a.second) { |
- case 64: |
- assert((a.first + GlobalBase)%8 == 0); |
- Ret = a.first + GlobalBase; |
- break; |
- case 32: |
- assert((a.first + GlobalBase)%4 == 0); |
- Ret = a.first + GlobalBase + GlobalData64.size(); |
- break; |
- case 8: |
- Ret = a.first + GlobalBase + GlobalData64.size() + GlobalData32.size(); |
- break; |
- default: |
- report_fatal_error("bad global address " + Twine(s) + ": " |
- "count=" + Twine(a.first) + " " |
- "elementsize=" + Twine(a.second)); |
- } |
- return Ret; |
- } |
- // returns the internal offset inside the proper block: GlobalData8, 32, 64 |
- unsigned getRelativeGlobalAddress(const std::string &s) { |
- GlobalAddressMap::const_iterator I = GlobalAddresses.find(s); |
- if (I == GlobalAddresses.end()) { |
- report_fatal_error("cannot find global address " + Twine(s)); |
- } |
- Address a = I->second; |
- return a.first; |
- } |
- char getFunctionSignatureLetter(Type *T) { |
- if (T->isVoidTy()) return 'v'; |
- else if (T->isFloatingPointTy()) { |
- if (PreciseF32 && T->isFloatTy()) { |
- return 'f'; |
- } else { |
- return 'd'; |
- } |
- } else if (VectorType *VT = dyn_cast<VectorType>(T)) { |
- checkVectorType(VT); |
- if (VT->getElementType()->isIntegerTy()) { |
- return 'I'; |
- } else { |
- return 'F'; |
- } |
- } else { |
- return 'i'; |
- } |
- } |
- std::string getFunctionSignature(const FunctionType *F, const std::string *Name=NULL) { |
- std::string Ret; |
- Ret += getFunctionSignatureLetter(F->getReturnType()); |
- for (FunctionType::param_iterator AI = F->param_begin(), |
- AE = F->param_end(); AI != AE; ++AI) { |
- Ret += getFunctionSignatureLetter(*AI); |
- } |
- return Ret; |
- } |
- FunctionTable& ensureFunctionTable(const FunctionType *FT) { |
- FunctionTable &Table = FunctionTables[getFunctionSignature(FT)]; |
- unsigned MinSize = ReservedFunctionPointers ? 2*(ReservedFunctionPointers+1) : 1; // each reserved slot must be 2-aligned |
- while (Table.size() < MinSize) Table.push_back("0"); |
- return Table; |
- } |
- unsigned getFunctionIndex(const Function *F) { |
- const std::string &Name = getJSName(F); |
- if (IndexedFunctions.find(Name) != IndexedFunctions.end()) return IndexedFunctions[Name]; |
- std::string Sig = getFunctionSignature(F->getFunctionType(), &Name); |
- FunctionTable& Table = ensureFunctionTable(F->getFunctionType()); |
- if (NoAliasingFunctionPointers) { |
- while (Table.size() < NextFunctionIndex) Table.push_back("0"); |
- } |
- // XXX this is wrong, it's always 1. but, that's fine in the ARM-like ABI |
- // we have which allows unaligned func the one risk is if someone forces a |
- // function to be aligned, and relies on that. Could do F->getAlignment() |
- // instead. |
- unsigned Alignment = 1; |
- while (Table.size() % Alignment) Table.push_back("0"); |
- unsigned Index = Table.size(); |
- Table.push_back(Name); |
- IndexedFunctions[Name] = Index; |
- if (NoAliasingFunctionPointers) { |
- NextFunctionIndex = Index+1; |
- } |
- |
- // invoke the callHandler for this, if there is one. the function may only be indexed but never called directly, and we may need to do things in the handler |
- CallHandlerMap::const_iterator CH = CallHandlers.find(Name); |
- if (CH != CallHandlers.end()) { |
- (this->*(CH->second))(NULL, Name, -1); |
- } |
- |
- return Index; |
- } |
- |
- unsigned getBlockAddress(const Function *F, const BasicBlock *BB) { |
- BlockIndexMap& Blocks = BlockAddresses[F]; |
- if (Blocks.find(BB) == Blocks.end()) { |
- Blocks[BB] = Blocks.size(); // block addresses start from 0 |
- } |
- return Blocks[BB]; |
- } |
- |
- unsigned getBlockAddress(const BlockAddress *BA) { |
- return getBlockAddress(BA->getFunction(), BA->getBasicBlock()); |
- } |
- |
- const Value *resolveFully(const Value *V) { |
- bool More = true; |
- while (More) { |
- More = false; |
- if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) { |
- V = GA->getAliasee(); |
- More = true; |
- } |
- if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(V)) { |
- V = CE->getOperand(0); // ignore bitcasts |
- More = true; |
- } |
- } |
- return V; |
- } |
- |
- // Return a constant we are about to write into a global as a numeric offset. If the |
- // value is not known at compile time, emit a postSet to that location. |
- unsigned getConstAsOffset(const Value *V, unsigned AbsoluteTarget) { |
- V = resolveFully(V); |
- if (const Function *F = dyn_cast<const Function>(V)) { |
- return getFunctionIndex(F); |
- } else if (const BlockAddress *BA = dyn_cast<const BlockAddress>(V)) { |
- return getBlockAddress(BA); |
- } else { |
- if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) { |
- if (!GV->hasInitializer()) { |
- // We don't have a constant to emit here, so we must emit a postSet |
- // All postsets are of external values, so they are pointers, hence 32-bit |
- std::string Name = getOpName(V); |
- Externals.insert(Name); |
- PostSets += "HEAP32[" + utostr(AbsoluteTarget>>2) + "] = " + Name + ';'; |
- return 0; // emit zero in there for now, until the postSet |
- } |
- } |
- return getGlobalAddress(V->getName().str()); |
- } |
- } |
- |
- // Test whether the given value is known to be an absolute value or one we turn into an absolute value |
- bool isAbsolute(const Value *P) { |
- if (const IntToPtrInst *ITP = dyn_cast<IntToPtrInst>(P)) { |
- return isa<ConstantInt>(ITP->getOperand(0)); |
- } |
- if (isa<ConstantPointerNull>(P) || isa<UndefValue>(P)) { |
- return true; |
- } |
- return false; |
- } |
- |
- void checkVectorType(Type *T) { |
- VectorType *VT = cast<VectorType>(T); |
- // LLVM represents the results of vector comparison as vectors of i1. We |
- // represent them as vectors of integers the size of the vector elements |
- // of the compare that produced them. |
- assert(VT->getElementType()->getPrimitiveSizeInBits() == 32 || |
- VT->getElementType()->getPrimitiveSizeInBits() == 1); |
- assert(VT->getBitWidth() <= 128); |
- assert(VT->getNumElements() <= 4); |
- UsesSIMD = true; |
- } |
- |
- std::string ensureCast(std::string S, Type *T, AsmCast sign) { |
- if (sign & ASM_MUST_CAST) return getCast(S, T); |
- return S; |
- } |
- |
- std::string ftostr(const ConstantFP *CFP, AsmCast sign) { |
- const APFloat &flt = CFP->getValueAPF(); |
- |
- // Emscripten has its own spellings for infinity and NaN. |
- if (flt.getCategory() == APFloat::fcInfinity) return ensureCast(flt.isNegative() ? "-inf" : "inf", CFP->getType(), sign); |
- else if (flt.getCategory() == APFloat::fcNaN) return ensureCast("nan", CFP->getType(), sign); |
- |
- // Request 9 or 17 digits, aka FLT_DECIMAL_DIG or DBL_DECIMAL_DIG (our |
- // long double is the the same as our double), to avoid rounding errors. |
- SmallString<29> Str; |
- flt.toString(Str, PreciseF32 && CFP->getType()->isFloatTy() ? 9 : 17); |
- |
- // asm.js considers literals to be floating-point literals when they contain a |
- // dot, however our output may be processed by UglifyJS, which doesn't |
- // currently preserve dots in all cases. Mark floating-point literals with |
- // unary plus to force them to floating-point. |
- if (APFloat(flt).roundToIntegral(APFloat::rmNearestTiesToEven) == APFloat::opOK) { |
- return '+' + Str.str().str(); |
- } |
- |
- return Str.str().str(); |
- } |
- |
- std::string getPtrLoad(const Value* Ptr); |
- std::string getHeapAccess(const std::string& Name, unsigned Bytes, bool Integer=true); |
- std::string getPtrUse(const Value* Ptr); |
- std::string getConstant(const Constant*, AsmCast sign=ASM_SIGNED); |
- std::string getConstantVector(Type *ElementType, std::string x, std::string y, std::string z, std::string w); |
- std::string getValueAsStr(const Value*, AsmCast sign=ASM_SIGNED); |
- std::string getValueAsCastStr(const Value*, AsmCast sign=ASM_SIGNED); |
- std::string getValueAsParenStr(const Value*); |
- std::string getValueAsCastParenStr(const Value*, AsmCast sign=ASM_SIGNED); |
- |
- const std::string &getJSName(const Value* val); |
- |
- std::string getPhiCode(const BasicBlock *From, const BasicBlock *To); |
- |
- void printAttributes(const AttributeSet &PAL, const std::string &name); |
- void printType(Type* Ty); |
- void printTypes(const Module* M); |
- |
- std::string getAdHocAssign(const StringRef &, Type *); |
- std::string getAssign(const Instruction *I); |
- std::string getAssignIfNeeded(const Value *V); |
- std::string getCast(const StringRef &, Type *, AsmCast sign=ASM_SIGNED); |
- std::string getParenCast(const StringRef &, Type *, AsmCast sign=ASM_SIGNED); |
- std::string getDoubleToInt(const StringRef &); |
- std::string getIMul(const Value *, const Value *); |
- std::string getLoad(const Instruction *I, const Value *P, Type *T, unsigned Alignment, char sep=';'); |
- std::string getStore(const Instruction *I, const Value *P, Type *T, const std::string& VS, unsigned Alignment, char sep=';'); |
- std::string getStackBump(unsigned Size); |
- std::string getStackBump(const std::string &Size); |
- |
- void addBlock(const BasicBlock *BB, Relooper& R, LLVMToRelooperMap& LLVMToRelooper); |
- void printFunctionBody(const Function *F); |
- void generateInsertElementExpression(const InsertElementInst *III, raw_string_ostream& Code); |
- void generateExtractElementExpression(const ExtractElementInst *EEI, raw_string_ostream& Code); |
- void generateShuffleVectorExpression(const ShuffleVectorInst *SVI, raw_string_ostream& Code); |
- void generateICmpExpression(const ICmpInst *I, raw_string_ostream& Code); |
- void generateFCmpExpression(const FCmpInst *I, raw_string_ostream& Code); |
- void generateShiftExpression(const BinaryOperator *I, raw_string_ostream& Code); |
- void generateUnrolledExpression(const User *I, raw_string_ostream& Code); |
- bool generateSIMDExpression(const User *I, raw_string_ostream& Code); |
- void generateExpression(const User *I, raw_string_ostream& Code); |
- |
- std::string getOpName(const Value*); |
- |
- void processConstants(); |
- |
- // nativization |
- |
- typedef std::set<const Value*> NativizedVarsMap; |
- NativizedVarsMap NativizedVars; |
- |
- void calculateNativizedVars(const Function *F); |
- |
- // special analyses |
- |
- bool canReloop(const Function *F); |
- |
- // main entry point |
- |
- void printModuleBody(); |
- }; |
-} // end anonymous namespace. |
- |
-raw_pwrite_stream &JSWriter::nl(raw_pwrite_stream &Out, int delta) { |
- Out << '\n'; |
- return Out; |
-} |
- |
-static inline char halfCharToHex(unsigned char half) { |
- assert(half <= 15); |
- if (half <= 9) { |
- return '0' + half; |
- } else { |
- return 'A' + half - 10; |
- } |
-} |
- |
-static inline void sanitizeGlobal(std::string& str) { |
- // Global names are prefixed with "_" to prevent them from colliding with |
- // names of things in normal JS. |
- str = "_" + str; |
- |
- // functions and globals should already be in C-style format, |
- // in addition to . for llvm intrinsics and possibly $ and so forth. |
- // There is a risk of collisions here, we just lower all these |
- // invalid characters to _, but this should not happen in practice. |
- // TODO: in debug mode, check for such collisions. |
- size_t OriginalSize = str.size(); |
- for (size_t i = 1; i < OriginalSize; ++i) { |
- unsigned char c = str[i]; |
- if (!isalnum(c) && c != '_') str[i] = '_'; |
- } |
-} |
- |
-static inline void sanitizeLocal(std::string& str) { |
- // Local names are prefixed with "$" to prevent them from colliding with |
- // global names. |
- str = "$" + str; |
- |
- // We need to convert every string that is not a valid JS identifier into |
- // a valid one, without collisions - we cannot turn "x.a" into "x_a" while |
- // also leaving "x_a" as is, for example. |
- // |
- // We leave valid characters 0-9a-zA-Z and _ unchanged. Anything else |
- // we replace with $ and append a hex representation of that value, |
- // so for example x.a turns into x$a2e, x..a turns into x$$a2e2e. |
- // |
- // As an optimization, we replace . with $ without appending anything, |
- // unless there is another illegal character. The reason is that . is |
- // a common illegal character, and we want to avoid resizing strings |
- // for perf reasons, and we If we do see we need to append something, then |
- // for . we just append Z (one character, instead of the hex code). |
- // |
- |
- size_t OriginalSize = str.size(); |
- int Queued = 0; |
- for (size_t i = 1; i < OriginalSize; ++i) { |
- unsigned char c = str[i]; |
- if (!isalnum(c) && c != '_') { |
- str[i] = '$'; |
- if (c == '.') { |
- Queued++; |
- } else { |
- size_t s = str.size(); |
- str.resize(s+2+Queued); |
- for (int i = 0; i < Queued; i++) { |
- str[s++] = 'Z'; |
- } |
- Queued = 0; |
- str[s] = halfCharToHex(c >> 4); |
- str[s+1] = halfCharToHex(c & 0xf); |
- } |
- } |
- } |
-} |
- |
-static inline std::string ensureFloat(const std::string &S, Type *T) { |
- if (PreciseF32 && T->isFloatTy()) { |
- return "Math_fround(" + S + ")"; |
- } |
- return S; |
-} |
- |
-static void emitDebugInfo(raw_ostream& Code, const Instruction *I) { |
- auto &Loc = I->getDebugLoc(); |
- if (Loc) { |
- unsigned Line = Loc.getLine(); |
- StringRef File = cast<MDLocation>(Loc.getScope())->getFilename(); |
- Code << " //@line " << utostr(Line) << " \"" << (File.size() > 0 ? File.str() : "?") << "\""; |
- } |
-} |
- |
-void JSWriter::error(const std::string& msg) { |
- report_fatal_error(msg); |
-} |
- |
-std::string JSWriter::getPhiCode(const BasicBlock *From, const BasicBlock *To) { |
- // FIXME this is all quite inefficient, and also done once per incoming to each phi |
- |
- // Find the phis, and generate assignments and dependencies |
- std::set<std::string> PhiVars; |
- for (BasicBlock::const_iterator I = To->begin(), E = To->end(); |
- I != E; ++I) { |
- const PHINode* P = dyn_cast<PHINode>(I); |
- if (!P) break; |
- PhiVars.insert(getJSName(P)); |
- } |
- typedef std::map<std::string, std::string> StringMap; |
- StringMap assigns; // variable -> assign statement |
- std::map<std::string, const Value*> values; // variable -> Value |
- StringMap deps; // variable -> dependency |
- StringMap undeps; // reverse: dependency -> variable |
- for (BasicBlock::const_iterator I = To->begin(), E = To->end(); |
- I != E; ++I) { |
- const PHINode* P = dyn_cast<PHINode>(I); |
- if (!P) break; |
- int index = P->getBasicBlockIndex(From); |
- if (index < 0) continue; |
- // we found it |
- const std::string &name = getJSName(P); |
- assigns[name] = getAssign(P); |
- // Get the operand, and strip pointer casts, since normal expression |
- // translation also strips pointer casts, and we want to see the same |
- // thing so that we can detect any resulting dependencies. |
- const Value *V = P->getIncomingValue(index)->stripPointerCasts(); |
- values[name] = V; |
- std::string vname = getValueAsStr(V); |
- if (const Instruction *VI = dyn_cast<const Instruction>(V)) { |
- if (VI->getParent() == To && PhiVars.find(vname) != PhiVars.end()) { |
- deps[name] = vname; |
- undeps[vname] = name; |
- } |
- } |
- } |
- // Emit assignments+values, taking into account dependencies, and breaking cycles |
- std::string pre = "", post = ""; |
- while (assigns.size() > 0) { |
- bool emitted = false; |
- for (StringMap::iterator I = assigns.begin(); I != assigns.end();) { |
- StringMap::iterator last = I; |
- std::string curr = last->first; |
- const Value *V = values[curr]; |
- std::string CV = getValueAsStr(V); |
- I++; // advance now, as we may erase |
- // if we have no dependencies, or we found none to emit and are at the end (so there is a cycle), emit |
- StringMap::const_iterator dep = deps.find(curr); |
- if (dep == deps.end() || (!emitted && I == assigns.end())) { |
- if (dep != deps.end()) { |
- // break a cycle |
- std::string depString = dep->second; |
- std::string temp = curr + "$phi"; |
- pre += getAdHocAssign(temp, V->getType()) + CV + ';'; |
- CV = temp; |
- deps.erase(curr); |
- undeps.erase(depString); |
- } |
- post += assigns[curr] + CV + ';'; |
- assigns.erase(last); |
- emitted = true; |
- } |
- } |
- } |
- return pre + post; |
-} |
- |
-const std::string &JSWriter::getJSName(const Value* val) { |
- ValueMap::const_iterator I = ValueNames.find(val); |
- if (I != ValueNames.end() && I->first == val) |
- return I->second; |
- |
- // If this is an alloca we've replaced with another, use the other name. |
- if (const AllocaInst *AI = dyn_cast<AllocaInst>(val)) { |
- if (AI->isStaticAlloca()) { |
- const AllocaInst *Rep = Allocas.getRepresentative(AI); |
- if (Rep != AI) { |
- return getJSName(Rep); |
- } |
- } |
- } |
- |
- std::string name; |
- if (val->hasName()) { |
- name = val->getName().str(); |
- } else { |
- name = utostr(UniqueNum++); |
- } |
- |
- if (isa<Constant>(val)) { |
- sanitizeGlobal(name); |
- } else { |
- sanitizeLocal(name); |
- } |
- |
- return ValueNames[val] = name; |
-} |
- |
-std::string JSWriter::getAdHocAssign(const StringRef &s, Type *t) { |
- UsedVars[s] = t; |
- return (s + " = ").str(); |
-} |
- |
-std::string JSWriter::getAssign(const Instruction *I) { |
- return getAdHocAssign(getJSName(I), I->getType()); |
-} |
- |
-std::string JSWriter::getAssignIfNeeded(const Value *V) { |
- if (const Instruction *I = dyn_cast<Instruction>(V)) { |
- if (!I->use_empty()) return getAssign(I); |
- } |
- return std::string(); |
-} |
- |
-std::string JSWriter::getCast(const StringRef &s, Type *t, AsmCast sign) { |
- switch (t->getTypeID()) { |
- default: { |
- errs() << *t << "\n"; |
- assert(false && "Unsupported type"); |
- } |
- case Type::VectorTyID: |
- return (cast<VectorType>(t)->getElementType()->isIntegerTy() ? |
- "SIMD_int32x4_check(" + s + ")" : |
- "SIMD_float32x4_check(" + s + ")").str(); |
- case Type::FloatTyID: { |
- if (PreciseF32 && !(sign & ASM_FFI_OUT)) { |
- if (sign & ASM_FFI_IN) { |
- return ("Math_fround(+(" + s + "))").str(); |
- } else { |
- return ("Math_fround(" + s + ")").str(); |
- } |
- } |
- // otherwise fall through to double |
- } |
- case Type::DoubleTyID: return ("+" + s).str(); |
- case Type::IntegerTyID: { |
- // fall through to the end for nonspecific |
- switch (t->getIntegerBitWidth()) { |
- case 1: if (!(sign & ASM_NONSPECIFIC)) return sign == ASM_UNSIGNED ? (s + "&1").str() : (s + "<<31>>31").str(); |
- case 8: if (!(sign & ASM_NONSPECIFIC)) return sign == ASM_UNSIGNED ? (s + "&255").str() : (s + "<<24>>24").str(); |
- case 16: if (!(sign & ASM_NONSPECIFIC)) return sign == ASM_UNSIGNED ? (s + "&65535").str() : (s + "<<16>>16").str(); |
- case 32: return (sign == ASM_SIGNED || (sign & ASM_NONSPECIFIC) ? s + "|0" : s + ">>>0").str(); |
- default: llvm_unreachable("Unsupported integer cast bitwidth"); |
- } |
- } |
- case Type::PointerTyID: |
- return (sign == ASM_SIGNED || (sign & ASM_NONSPECIFIC) ? s + "|0" : s + ">>>0").str(); |
- } |
-} |
- |
-std::string JSWriter::getParenCast(const StringRef &s, Type *t, AsmCast sign) { |
- return getCast(("(" + s + ")").str(), t, sign); |
-} |
- |
-std::string JSWriter::getDoubleToInt(const StringRef &s) { |
- return ("~~(" + s + ")").str(); |
-} |
- |
-std::string JSWriter::getIMul(const Value *V1, const Value *V2) { |
- const ConstantInt *CI = NULL; |
- const Value *Other = NULL; |
- if ((CI = dyn_cast<ConstantInt>(V1))) { |
- Other = V2; |
- } else if ((CI = dyn_cast<ConstantInt>(V2))) { |
- Other = V1; |
- } |
- // we ignore optimizing the case of multiplying two constants - optimizer would have removed those |
- if (CI) { |
- std::string OtherStr = getValueAsStr(Other); |
- unsigned C = CI->getZExtValue(); |
- if (C == 0) return "0"; |
- if (C == 1) return OtherStr; |
- unsigned Orig = C, Shifts = 0; |
- while (C) { |
- if ((C & 1) && (C != 1)) break; // not power of 2 |
- C >>= 1; |
- Shifts++; |
- if (C == 0) return OtherStr + "<<" + utostr(Shifts-1); // power of 2, emit shift |
- } |
- if (Orig < (1<<20)) return "(" + OtherStr + "*" + utostr(Orig) + ")|0"; // small enough, avoid imul |
- } |
- return "Math_imul(" + getValueAsStr(V1) + ", " + getValueAsStr(V2) + ")|0"; // unknown or too large, emit imul |
-} |
- |
-std::string JSWriter::getLoad(const Instruction *I, const Value *P, Type *T, unsigned Alignment, char sep) { |
- std::string Assign = getAssign(I); |
- unsigned Bytes = DL->getTypeAllocSize(T); |
- std::string text; |
- if (Bytes <= Alignment || Alignment == 0) { |
- text = Assign + getPtrLoad(P); |
- if (isAbsolute(P)) { |
- // loads from an absolute constants are either intentional segfaults (int x = *((int*)0)), or code problems |
- text += "; abort() /* segfault, load from absolute addr */"; |
- } |
- } else { |
- // unaligned in some manner |
- if (WarnOnUnaligned) { |
- errs() << "emcc: warning: unaligned load in " << I->getParent()->getParent()->getName() << ":" << *I << " | "; |
- emitDebugInfo(errs(), I); |
- errs() << "\n"; |
- } |
- std::string PS = getValueAsStr(P); |
- switch (Bytes) { |
- case 8: { |
- switch (Alignment) { |
- case 4: { |
- text = "HEAP32[tempDoublePtr>>2]=HEAP32[" + PS + ">>2]" + sep + |
- "HEAP32[tempDoublePtr+4>>2]=HEAP32[" + PS + "+4>>2]"; |
- break; |
- } |
- case 2: { |
- text = "HEAP16[tempDoublePtr>>1]=HEAP16[" + PS + ">>1]" + sep + |
- "HEAP16[tempDoublePtr+2>>1]=HEAP16[" + PS + "+2>>1]" + sep + |
- "HEAP16[tempDoublePtr+4>>1]=HEAP16[" + PS + "+4>>1]" + sep + |
- "HEAP16[tempDoublePtr+6>>1]=HEAP16[" + PS + "+6>>1]"; |
- break; |
- } |
- case 1: { |
- text = "HEAP8[tempDoublePtr>>0]=HEAP8[" + PS + ">>0]" + sep + |
- "HEAP8[tempDoublePtr+1>>0]=HEAP8[" + PS + "+1>>0]" + sep + |
- "HEAP8[tempDoublePtr+2>>0]=HEAP8[" + PS + "+2>>0]" + sep + |
- "HEAP8[tempDoublePtr+3>>0]=HEAP8[" + PS + "+3>>0]" + sep + |
- "HEAP8[tempDoublePtr+4>>0]=HEAP8[" + PS + "+4>>0]" + sep + |
- "HEAP8[tempDoublePtr+5>>0]=HEAP8[" + PS + "+5>>0]" + sep + |
- "HEAP8[tempDoublePtr+6>>0]=HEAP8[" + PS + "+6>>0]" + sep + |
- "HEAP8[tempDoublePtr+7>>0]=HEAP8[" + PS + "+7>>0]"; |
- break; |
- } |
- default: assert(0 && "bad 8 store"); |
- } |
- text += sep + Assign + "+HEAPF64[tempDoublePtr>>3]"; |
- break; |
- } |
- case 4: { |
- if (T->isIntegerTy() || T->isPointerTy()) { |
- switch (Alignment) { |
- case 2: { |
- text = Assign + "HEAPU16[" + PS + ">>1]|" + |
- "(HEAPU16[" + PS + "+2>>1]<<16)"; |
- break; |
- } |
- case 1: { |
- text = Assign + "HEAPU8[" + PS + ">>0]|" + |
- "(HEAPU8[" + PS + "+1>>0]<<8)|" + |
- "(HEAPU8[" + PS + "+2>>0]<<16)|" + |
- "(HEAPU8[" + PS + "+3>>0]<<24)"; |
- break; |
- } |
- default: assert(0 && "bad 4i store"); |
- } |
- } else { // float |
- assert(T->isFloatingPointTy()); |
- switch (Alignment) { |
- case 2: { |
- text = "HEAP16[tempDoublePtr>>1]=HEAP16[" + PS + ">>1]" + sep + |
- "HEAP16[tempDoublePtr+2>>1]=HEAP16[" + PS + "+2>>1]"; |
- break; |
- } |
- case 1: { |
- text = "HEAP8[tempDoublePtr>>0]=HEAP8[" + PS + ">>0]" + sep + |
- "HEAP8[tempDoublePtr+1>>0]=HEAP8[" + PS + "+1>>0]" + sep + |
- "HEAP8[tempDoublePtr+2>>0]=HEAP8[" + PS + "+2>>0]" + sep + |
- "HEAP8[tempDoublePtr+3>>0]=HEAP8[" + PS + "+3>>0]"; |
- break; |
- } |
- default: assert(0 && "bad 4f store"); |
- } |
- text += sep + Assign + getCast("HEAPF32[tempDoublePtr>>2]", Type::getFloatTy(TheModule->getContext())); |
- } |
- break; |
- } |
- case 2: { |
- text = Assign + "HEAPU8[" + PS + ">>0]|" + |
- "(HEAPU8[" + PS + "+1>>0]<<8)"; |
- break; |
- } |
- default: assert(0 && "bad store"); |
- } |
- } |
- return text; |
-} |
- |
-std::string JSWriter::getStore(const Instruction *I, const Value *P, Type *T, const std::string& VS, unsigned Alignment, char sep) { |
- assert(sep == ';'); // FIXME when we need that |
- unsigned Bytes = DL->getTypeAllocSize(T); |
- std::string text; |
- if (Bytes <= Alignment || Alignment == 0) { |
- text = getPtrUse(P) + " = " + VS; |
- if (Alignment == 536870912) text += "; abort() /* segfault */"; |
- } else { |
- // unaligned in some manner |
- if (WarnOnUnaligned) { |
- errs() << "emcc: warning: unaligned store in " << I->getParent()->getParent()->getName() << ":" << *I << " | "; |
- emitDebugInfo(errs(), I); |
- errs() << "\n"; |
- } |
- std::string PS = getValueAsStr(P); |
- switch (Bytes) { |
- case 8: { |
- text = "HEAPF64[tempDoublePtr>>3]=" + VS + ';'; |
- switch (Alignment) { |
- case 4: { |
- text += "HEAP32[" + PS + ">>2]=HEAP32[tempDoublePtr>>2];" + |
- "HEAP32[" + PS + "+4>>2]=HEAP32[tempDoublePtr+4>>2]"; |
- break; |
- } |
- case 2: { |
- text += "HEAP16[" + PS + ">>1]=HEAP16[tempDoublePtr>>1];" + |
- "HEAP16[" + PS + "+2>>1]=HEAP16[tempDoublePtr+2>>1];" + |
- "HEAP16[" + PS + "+4>>1]=HEAP16[tempDoublePtr+4>>1];" + |
- "HEAP16[" + PS + "+6>>1]=HEAP16[tempDoublePtr+6>>1]"; |
- break; |
- } |
- case 1: { |
- text += "HEAP8[" + PS + ">>0]=HEAP8[tempDoublePtr>>0];" + |
- "HEAP8[" + PS + "+1>>0]=HEAP8[tempDoublePtr+1>>0];" + |
- "HEAP8[" + PS + "+2>>0]=HEAP8[tempDoublePtr+2>>0];" + |
- "HEAP8[" + PS + "+3>>0]=HEAP8[tempDoublePtr+3>>0];" + |
- "HEAP8[" + PS + "+4>>0]=HEAP8[tempDoublePtr+4>>0];" + |
- "HEAP8[" + PS + "+5>>0]=HEAP8[tempDoublePtr+5>>0];" + |
- "HEAP8[" + PS + "+6>>0]=HEAP8[tempDoublePtr+6>>0];" + |
- "HEAP8[" + PS + "+7>>0]=HEAP8[tempDoublePtr+7>>0]"; |
- break; |
- } |
- default: assert(0 && "bad 8 store"); |
- } |
- break; |
- } |
- case 4: { |
- if (T->isIntegerTy() || T->isPointerTy()) { |
- switch (Alignment) { |
- case 2: { |
- text = "HEAP16[" + PS + ">>1]=" + VS + "&65535;" + |
- "HEAP16[" + PS + "+2>>1]=" + VS + ">>>16"; |
- break; |
- } |
- case 1: { |
- text = "HEAP8[" + PS + ">>0]=" + VS + "&255;" + |
- "HEAP8[" + PS + "+1>>0]=(" + VS + ">>8)&255;" + |
- "HEAP8[" + PS + "+2>>0]=(" + VS + ">>16)&255;" + |
- "HEAP8[" + PS + "+3>>0]=" + VS + ">>24"; |
- break; |
- } |
- default: assert(0 && "bad 4i store"); |
- } |
- } else { // float |
- assert(T->isFloatingPointTy()); |
- text = "HEAPF32[tempDoublePtr>>2]=" + VS + ';'; |
- switch (Alignment) { |
- case 2: { |
- text += "HEAP16[" + PS + ">>1]=HEAP16[tempDoublePtr>>1];" + |
- "HEAP16[" + PS + "+2>>1]=HEAP16[tempDoublePtr+2>>1]"; |
- break; |
- } |
- case 1: { |
- text += "HEAP8[" + PS + ">>0]=HEAP8[tempDoublePtr>>0];" + |
- "HEAP8[" + PS + "+1>>0]=HEAP8[tempDoublePtr+1>>0];" + |
- "HEAP8[" + PS + "+2>>0]=HEAP8[tempDoublePtr+2>>0];" + |
- "HEAP8[" + PS + "+3>>0]=HEAP8[tempDoublePtr+3>>0]"; |
- break; |
- } |
- default: assert(0 && "bad 4f store"); |
- } |
- } |
- break; |
- } |
- case 2: { |
- text = "HEAP8[" + PS + ">>0]=" + VS + "&255;" + |
- "HEAP8[" + PS + "+1>>0]=" + VS + ">>8"; |
- break; |
- } |
- default: assert(0 && "bad store"); |
- } |
- } |
- return text; |
-} |
- |
-std::string JSWriter::getStackBump(unsigned Size) { |
- return getStackBump(utostr(Size)); |
-} |
- |
-std::string JSWriter::getStackBump(const std::string &Size) { |
- std::string ret = "STACKTOP = STACKTOP + " + Size + "|0;"; |
- if (EmscriptenAssertions) { |
- ret += " if ((STACKTOP|0) >= (STACK_MAX|0)) abort();"; |
- } |
- return ret; |
-} |
- |
-std::string JSWriter::getOpName(const Value* V) { // TODO: remove this |
- return getJSName(V); |
-} |
- |
-std::string JSWriter::getPtrLoad(const Value* Ptr) { |
- Type *t = cast<PointerType>(Ptr->getType())->getElementType(); |
- return getCast(getPtrUse(Ptr), t, ASM_NONSPECIFIC); |
-} |
- |
-std::string JSWriter::getHeapAccess(const std::string& Name, unsigned Bytes, bool Integer) { |
- switch (Bytes) { |
- default: llvm_unreachable("Unsupported type"); |
- case 8: return "HEAPF64[" + Name + ">>3]"; |
- case 4: { |
- if (Integer) { |
- return "HEAP32[" + Name + ">>2]"; |
- } else { |
- return "HEAPF32[" + Name + ">>2]"; |
- } |
- } |
- case 2: return "HEAP16[" + Name + ">>1]"; |
- case 1: return "HEAP8[" + Name + ">>0]"; |
- } |
-} |
- |
-std::string JSWriter::getPtrUse(const Value* Ptr) { |
- Type *t = cast<PointerType>(Ptr->getType())->getElementType(); |
- unsigned Bytes = DL->getTypeAllocSize(t); |
- if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) { |
- std::string text = ""; |
- unsigned Addr = getGlobalAddress(GV->getName().str()); |
- switch (Bytes) { |
- default: llvm_unreachable("Unsupported type"); |
- case 8: return "HEAPF64[" + utostr(Addr >> 3) + "]"; |
- case 4: { |
- if (t->isIntegerTy() || t->isPointerTy()) { |
- return "HEAP32[" + utostr(Addr >> 2) + "]"; |
- } else { |
- assert(t->isFloatingPointTy()); |
- return "HEAPF32[" + utostr(Addr >> 2) + "]"; |
- } |
- } |
- case 2: return "HEAP16[" + utostr(Addr >> 1) + "]"; |
- case 1: return "HEAP8[" + utostr(Addr) + "]"; |
- } |
- } else { |
- return getHeapAccess(getValueAsStr(Ptr), Bytes, t->isIntegerTy() || t->isPointerTy()); |
- } |
-} |
- |
-std::string JSWriter::getConstant(const Constant* CV, AsmCast sign) { |
- if (isa<ConstantPointerNull>(CV)) return "0"; |
- |
- if (const Function *F = dyn_cast<Function>(CV)) { |
- return utostr(getFunctionIndex(F)); |
- } |
- |
- if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) { |
- if (GV->isDeclaration()) { |
- std::string Name = getOpName(GV); |
- Externals.insert(Name); |
- return Name; |
- } |
- if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(CV)) { |
- // Since we don't currently support linking of our output, we don't need |
- // to worry about weak or other kinds of aliases. |
- return getConstant(GA->getAliasee(), sign); |
- } |
- return utostr(getGlobalAddress(GV->getName().str())); |
- } |
- |
- if (const ConstantFP *CFP = dyn_cast<ConstantFP>(CV)) { |
- std::string S = ftostr(CFP, sign); |
- if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { |
- S = "Math_fround(" + S + ")"; |
- } |
- return S; |
- } else if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { |
- if (sign != ASM_UNSIGNED && CI->getValue().getBitWidth() == 1) { |
- sign = ASM_UNSIGNED; // bools must always be unsigned: either 0 or 1 |
- } |
- return CI->getValue().toString(10, sign != ASM_UNSIGNED); |
- } else if (isa<UndefValue>(CV)) { |
- std::string S; |
- if (VectorType *VT = dyn_cast<VectorType>(CV->getType())) { |
- checkVectorType(VT); |
- if (VT->getElementType()->isIntegerTy()) { |
- S = "SIMD_int32x4_splat(0)"; |
- } else { |
- S = "SIMD_float32x4_splat(Math_fround(0))"; |
- } |
- } else { |
- S = CV->getType()->isFloatingPointTy() ? "+0" : "0"; // XXX refactor this |
- if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { |
- S = "Math_fround(" + S + ")"; |
- } |
- } |
- return S; |
- } else if (isa<ConstantAggregateZero>(CV)) { |
- if (VectorType *VT = dyn_cast<VectorType>(CV->getType())) { |
- checkVectorType(VT); |
- if (VT->getElementType()->isIntegerTy()) { |
- return "SIMD_int32x4_splat(0)"; |
- } else { |
- return "SIMD_float32x4_splat(Math_fround(0))"; |
- } |
- } else { |
- // something like [0 x i8*] zeroinitializer, which clang can emit for landingpads |
- return "0"; |
- } |
- } else if (const ConstantDataVector *DV = dyn_cast<ConstantDataVector>(CV)) { |
- checkVectorType(DV->getType()); |
- unsigned NumElts = cast<VectorType>(DV->getType())->getNumElements(); |
- Type *EltTy = cast<VectorType>(DV->getType())->getElementType(); |
- Constant *Undef = UndefValue::get(EltTy); |
- return getConstantVector(EltTy, |
- getConstant(NumElts > 0 ? DV->getElementAsConstant(0) : Undef), |
- getConstant(NumElts > 1 ? DV->getElementAsConstant(1) : Undef), |
- getConstant(NumElts > 2 ? DV->getElementAsConstant(2) : Undef), |
- getConstant(NumElts > 3 ? DV->getElementAsConstant(3) : Undef)); |
- } else if (const ConstantVector *V = dyn_cast<ConstantVector>(CV)) { |
- checkVectorType(V->getType()); |
- unsigned NumElts = cast<VectorType>(CV->getType())->getNumElements(); |
- Type *EltTy = cast<VectorType>(CV->getType())->getElementType(); |
- Constant *Undef = UndefValue::get(EltTy); |
- return getConstantVector(cast<VectorType>(V->getType())->getElementType(), |
- getConstant(NumElts > 0 ? V->getOperand(0) : Undef), |
- getConstant(NumElts > 1 ? V->getOperand(1) : Undef), |
- getConstant(NumElts > 2 ? V->getOperand(2) : Undef), |
- getConstant(NumElts > 3 ? V->getOperand(3) : Undef)); |
- } else if (const ConstantArray *CA = dyn_cast<const ConstantArray>(CV)) { |
- // handle things like [i8* bitcast (<{ i32, i32, i32 }>* @_ZTISt9bad_alloc to i8*)] which clang can emit for landingpads |
- assert(CA->getNumOperands() == 1); |
- CV = CA->getOperand(0); |
- const ConstantExpr *CE = cast<ConstantExpr>(CV); |
- CV = CE->getOperand(0); // ignore bitcast |
- return getConstant(CV); |
- } else if (const BlockAddress *BA = dyn_cast<const BlockAddress>(CV)) { |
- return utostr(getBlockAddress(BA)); |
- } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { |
- std::string Code; |
- raw_string_ostream CodeStream(Code); |
- CodeStream << '('; |
- generateExpression(CE, CodeStream); |
- CodeStream << ')'; |
- return CodeStream.str(); |
- } else { |
- CV->dump(); |
- llvm_unreachable("Unsupported constant kind"); |
- } |
-} |
- |
-std::string JSWriter::getConstantVector(Type *ElementType, std::string x, std::string y, std::string z, std::string w) { |
- // Check for a splat. |
- if (x == y && x == z && x == w) { |
- if (ElementType->isIntegerTy()) { |
- return "SIMD_int32x4_splat(" + x + ')'; |
- } else { |
- return "SIMD_float32x4_splat(Math_fround(" + x + "))"; |
- } |
- } |
- |
- if (ElementType->isIntegerTy()) { |
- return "SIMD_int32x4(" + x + ',' + y + ',' + z + ',' + w + ')'; |
- } else { |
- return "SIMD_float32x4(Math_fround(" + x + "),Math_fround(" + y + "),Math_fround(" + z + "),Math_fround(" + w + "))"; |
- } |
-} |
- |
-std::string JSWriter::getValueAsStr(const Value* V, AsmCast sign) { |
- // Skip past no-op bitcasts and zero-index geps. |
- V = V->stripPointerCasts(); |
- |
- if (const Constant *CV = dyn_cast<Constant>(V)) { |
- return getConstant(CV, sign); |
- } else { |
- return getJSName(V); |
- } |
-} |
- |
-std::string JSWriter::getValueAsCastStr(const Value* V, AsmCast sign) { |
- // Skip past no-op bitcasts and zero-index geps. |
- V = V->stripPointerCasts(); |
- |
- if (isa<ConstantInt>(V) || isa<ConstantFP>(V)) { |
- return getConstant(cast<Constant>(V), sign); |
- } else { |
- return getCast(getValueAsStr(V), V->getType(), sign); |
- } |
-} |
- |
-std::string JSWriter::getValueAsParenStr(const Value* V) { |
- // Skip past no-op bitcasts and zero-index geps. |
- V = V->stripPointerCasts(); |
- |
- if (const Constant *CV = dyn_cast<Constant>(V)) { |
- return getConstant(CV); |
- } else { |
- return "(" + getValueAsStr(V) + ")"; |
- } |
-} |
- |
-std::string JSWriter::getValueAsCastParenStr(const Value* V, AsmCast sign) { |
- // Skip past no-op bitcasts and zero-index geps. |
- V = V->stripPointerCasts(); |
- |
- if (isa<ConstantInt>(V) || isa<ConstantFP>(V) || isa<UndefValue>(V)) { |
- return getConstant(cast<Constant>(V), sign); |
- } else { |
- return "(" + getCast(getValueAsStr(V), V->getType(), sign) + ")"; |
- } |
-} |
- |
-void JSWriter::generateInsertElementExpression(const InsertElementInst *III, raw_string_ostream& Code) { |
- // LLVM has no vector type constructor operator; it uses chains of |
- // insertelement instructions instead. It also has no splat operator; it |
- // uses an insertelement followed by a shuffle instead. If this insertelement |
- // is part of either such sequence, skip it for now; we'll process it when we |
- // reach the end. |
- if (III->hasOneUse()) { |
- const User *U = *III->user_begin(); |
- if (isa<InsertElementInst>(U)) |
- return; |
- if (isa<ShuffleVectorInst>(U) && |
- isa<ConstantAggregateZero>(cast<ShuffleVectorInst>(U)->getMask()) && |
- !isa<InsertElementInst>(III->getOperand(0)) && |
- isa<ConstantInt>(III->getOperand(2)) && |
- cast<ConstantInt>(III->getOperand(2))->isZero()) |
- { |
- return; |
- } |
- } |
- |
- // This insertelement is at the base of a chain of single-user insertelement |
- // instructions. Collect all the inserted elements so that we can categorize |
- // the chain as either a splat, a constructor, or an actual series of inserts. |
- VectorType *VT = III->getType(); |
- unsigned NumElems = VT->getNumElements(); |
- unsigned NumInserted = 0; |
- SmallVector<const Value *, 8> Operands(NumElems, NULL); |
- const Value *Splat = III->getOperand(1); |
- const Value *Base = III; |
- do { |
- const InsertElementInst *BaseIII = cast<InsertElementInst>(Base); |
- const ConstantInt *IndexInt = cast<ConstantInt>(BaseIII->getOperand(2)); |
- unsigned Index = IndexInt->getZExtValue(); |
- if (Operands[Index] == NULL) |
- ++NumInserted; |
- Value *Op = BaseIII->getOperand(1); |
- if (Operands[Index] == NULL) { |
- Operands[Index] = Op; |
- if (Op != Splat) |
- Splat = NULL; |
- } |
- Base = BaseIII->getOperand(0); |
- } while (Base->hasOneUse() && isa<InsertElementInst>(Base)); |
- |
- // Emit code for the chain. |
- Code << getAssignIfNeeded(III); |
- if (NumInserted == NumElems) { |
- if (Splat) { |
- // Emit splat code. |
- if (VT->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_splat(" << getValueAsStr(Splat) << ")"; |
- } else { |
- std::string operand = getValueAsStr(Splat); |
- if (!PreciseF32) { |
- // SIMD_float32x4_splat requires an actual float32 even if we're |
- // otherwise not being precise about it. |
- operand = "Math_fround(" + operand + ")"; |
- } |
- Code << "SIMD_float32x4_splat(" << operand << ")"; |
- } |
- } else { |
- // Emit constructor code. |
- if (VT->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4("; |
- } else { |
- Code << "SIMD_float32x4("; |
- } |
- for (unsigned Index = 0; Index < NumElems; ++Index) { |
- if (Index != 0) |
- Code << ", "; |
- std::string operand = getValueAsStr(Operands[Index]); |
- if (!PreciseF32 && VT->getElementType()->isFloatTy()) { |
- // SIMD_float32x4_splat requires an actual float32 even if we're |
- // otherwise not being precise about it. |
- operand = "Math_fround(" + operand + ")"; |
- } |
- Code << operand; |
- } |
- Code << ")"; |
- } |
- } else { |
- // Emit a series of inserts. |
- std::string Result = getValueAsStr(Base); |
- for (unsigned Index = 0; Index < NumElems; ++Index) { |
- std::string with; |
- if (!Operands[Index]) |
- continue; |
- if (VT->getElementType()->isIntegerTy()) { |
- with = "SIMD_int32x4_with"; |
- } else { |
- with = "SIMD_float32x4_with"; |
- } |
- std::string operand = getValueAsStr(Operands[Index]); |
- if (!PreciseF32) { |
- operand = "Math_fround(" + operand + ")"; |
- } |
- Result = with + SIMDLane[Index] + "(" + Result + ',' + operand + ')'; |
- } |
- Code << Result; |
- } |
-} |
- |
-void JSWriter::generateExtractElementExpression(const ExtractElementInst *EEI, raw_string_ostream& Code) { |
- VectorType *VT = cast<VectorType>(EEI->getVectorOperand()->getType()); |
- checkVectorType(VT); |
- const ConstantInt *IndexInt = dyn_cast<const ConstantInt>(EEI->getIndexOperand()); |
- if (IndexInt) { |
- unsigned Index = IndexInt->getZExtValue(); |
- assert(Index <= 3); |
- Code << getAssignIfNeeded(EEI); |
- std::string OperandCode; |
- raw_string_ostream CodeStream(OperandCode); |
- CodeStream << getValueAsStr(EEI->getVectorOperand()) << '.' << simdLane[Index]; |
- Code << getCast(CodeStream.str(), EEI->getType()); |
- return; |
- } |
- |
- error("SIMD extract element with non-constant index not implemented yet"); |
-} |
- |
-void JSWriter::generateShuffleVectorExpression(const ShuffleVectorInst *SVI, raw_string_ostream& Code) { |
- Code << getAssignIfNeeded(SVI); |
- |
- // LLVM has no splat operator, so it makes do by using an insert and a |
- // shuffle. If that's what this shuffle is doing, the code in |
- // generateInsertElementExpression will have also detected it and skipped |
- // emitting the insert, so we can just emit a splat here. |
- if (isa<ConstantAggregateZero>(SVI->getMask()) && |
- isa<InsertElementInst>(SVI->getOperand(0))) |
- { |
- InsertElementInst *IEI = cast<InsertElementInst>(SVI->getOperand(0)); |
- if (ConstantInt *CI = dyn_cast<ConstantInt>(IEI->getOperand(2))) { |
- if (CI->isZero()) { |
- std::string operand = getValueAsStr(IEI->getOperand(1)); |
- if (!PreciseF32) { |
- // SIMD_float32x4_splat requires an actual float32 even if we're |
- // otherwise not being precise about it. |
- operand = "Math_fround(" + operand + ")"; |
- } |
- if (SVI->getType()->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_splat("; |
- } else { |
- Code << "SIMD_float32x4_splat("; |
- } |
- Code << operand << ")"; |
- return; |
- } |
- } |
- } |
- |
- // Check whether can generate SIMD.js swizzle or shuffle. |
- std::string A = getValueAsStr(SVI->getOperand(0)); |
- std::string B = getValueAsStr(SVI->getOperand(1)); |
- int OpNumElements = cast<VectorType>(SVI->getOperand(0)->getType())->getNumElements(); |
- int ResultNumElements = SVI->getType()->getNumElements(); |
- int Mask0 = ResultNumElements > 0 ? SVI->getMaskValue(0) : -1; |
- int Mask1 = ResultNumElements > 1 ? SVI->getMaskValue(1) : -1; |
- int Mask2 = ResultNumElements > 2 ? SVI->getMaskValue(2) : -1; |
- int Mask3 = ResultNumElements > 3 ? SVI->getMaskValue(3) : -1; |
- bool swizzleA = false; |
- bool swizzleB = false; |
- if ((Mask0 < OpNumElements) && (Mask1 < OpNumElements) && |
- (Mask2 < OpNumElements) && (Mask3 < OpNumElements)) { |
- swizzleA = true; |
- } |
- if ((Mask0 < 0 || (Mask0 >= OpNumElements && Mask0 < OpNumElements * 2)) && |
- (Mask1 < 0 || (Mask1 >= OpNumElements && Mask1 < OpNumElements * 2)) && |
- (Mask2 < 0 || (Mask2 >= OpNumElements && Mask2 < OpNumElements * 2)) && |
- (Mask3 < 0 || (Mask3 >= OpNumElements && Mask3 < OpNumElements * 2))) { |
- swizzleB = true; |
- } |
- assert(!(swizzleA && swizzleB)); |
- if (swizzleA || swizzleB) { |
- std::string T = (swizzleA ? A : B); |
- if (SVI->getType()->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_swizzle(" << T; |
- } else { |
- Code << "SIMD_float32x4_swizzle(" << T; |
- } |
- int i = 0; |
- for (; i < ResultNumElements; ++i) { |
- Code << ", "; |
- int Mask = SVI->getMaskValue(i); |
- if (Mask < 0) { |
- Code << 0; |
- } else if (Mask < OpNumElements) { |
- Code << Mask; |
- } else { |
- assert(Mask < OpNumElements * 2); |
- Code << (Mask-OpNumElements); |
- } |
- } |
- for (; i < 4; ++i) { |
- Code << ", 0"; |
- } |
- Code << ")"; |
- return; |
- } |
- |
- // Emit a fully-general shuffle. |
- if (SVI->getType()->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_shuffle("; |
- } else { |
- Code << "SIMD_float32x4_shuffle("; |
- } |
- |
- Code << A << ", " << B << ", "; |
- |
- SmallVector<int, 16> Indices; |
- SVI->getShuffleMask(Indices); |
- for (unsigned int i = 0; i < Indices.size(); ++i) { |
- if (i != 0) |
- Code << ", "; |
- int Mask = Indices[i]; |
- if (Mask >= OpNumElements) |
- Mask = Mask - OpNumElements + 4; |
- if (Mask < 0) |
- Code << 0; |
- else |
- Code << Mask; |
- } |
- |
- Code << ")"; |
-} |
- |
-void JSWriter::generateICmpExpression(const ICmpInst *I, raw_string_ostream& Code) { |
- bool Invert = false; |
- const char *Name; |
- switch (cast<ICmpInst>(I)->getPredicate()) { |
- case ICmpInst::ICMP_EQ: Name = "equal"; break; |
- case ICmpInst::ICMP_NE: Name = "equal"; Invert = true; break; |
- case ICmpInst::ICMP_SLE: Name = "greaterThan"; Invert = true; break; |
- case ICmpInst::ICMP_SGE: Name = "lessThan"; Invert = true; break; |
- case ICmpInst::ICMP_ULE: Name = "unsignedLessThanOrEqual"; break; |
- case ICmpInst::ICMP_UGE: Name = "unsignedGreaterThanOrEqual"; break; |
- case ICmpInst::ICMP_ULT: Name = "unsignedLessThan"; break; |
- case ICmpInst::ICMP_SLT: Name = "lessThan"; break; |
- case ICmpInst::ICMP_UGT: Name = "unsignedGreaterThan"; break; |
- case ICmpInst::ICMP_SGT: Name = "greaterThan"; break; |
- default: I->dump(); error("invalid vector icmp"); break; |
- } |
- |
- if (Invert) |
- Code << "SIMD_int32x4_not("; |
- |
- Code << getAssignIfNeeded(I) << "SIMD_int32x4_" << Name << "(" |
- << getValueAsStr(I->getOperand(0)) << ", " << getValueAsStr(I->getOperand(1)) << ")"; |
- |
- if (Invert) |
- Code << ")"; |
-} |
- |
-void JSWriter::generateFCmpExpression(const FCmpInst *I, raw_string_ostream& Code) { |
- const char *Name; |
- bool Invert = false; |
- switch (cast<FCmpInst>(I)->getPredicate()) { |
- case ICmpInst::FCMP_FALSE: |
- Code << "SIMD_int32x4_splat(0)"; |
- return; |
- case ICmpInst::FCMP_TRUE: |
- Code << "SIMD_int32x4_splat(-1)"; |
- return; |
- case ICmpInst::FCMP_ONE: |
- Code << "SIMD_float32x4_and(SIMD_float32x4_and(" |
- "SIMD_float32x4_equal(" << getValueAsStr(I->getOperand(0)) << ", " |
- << getValueAsStr(I->getOperand(0)) << "), " << |
- "SIMD_float32x4_equal(" << getValueAsStr(I->getOperand(1)) << ", " |
- << getValueAsStr(I->getOperand(1)) << ")), " << |
- "SIMD_float32x4_notEqual(" << getValueAsStr(I->getOperand(0)) << ", " |
- << getValueAsStr(I->getOperand(1)) << "))"; |
- return; |
- case ICmpInst::FCMP_UEQ: |
- Code << "SIMD_float32x4_or(SIMD_float32x4_or(" |
- "SIMD_float32x4_notEqual(" << getValueAsStr(I->getOperand(0)) << ", " |
- << getValueAsStr(I->getOperand(0)) << "), " << |
- "SIMD_float32x4_notEqual(" << getValueAsStr(I->getOperand(1)) << ", " |
- << getValueAsStr(I->getOperand(1)) << ")), " << |
- "SIMD_float32x4_equal(" << getValueAsStr(I->getOperand(0)) << ", " |
- << getValueAsStr(I->getOperand(1)) << "))"; |
- return; |
- case FCmpInst::FCMP_ORD: |
- Code << "SIMD_float32x4_and(" |
- "SIMD_float32x4_equal(" << getValueAsStr(I->getOperand(0)) << ", " << getValueAsStr(I->getOperand(0)) << "), " << |
- "SIMD_float32x4_equal(" << getValueAsStr(I->getOperand(1)) << ", " << getValueAsStr(I->getOperand(1)) << "))"; |
- return; |
- |
- case FCmpInst::FCMP_UNO: |
- Code << "SIMD_float32x4_or(" |
- "SIMD_float32x4_notEqual(" << getValueAsStr(I->getOperand(0)) << ", " << getValueAsStr(I->getOperand(0)) << "), " << |
- "SIMD_float32x4_notEqual(" << getValueAsStr(I->getOperand(1)) << ", " << getValueAsStr(I->getOperand(1)) << "))"; |
- return; |
- |
- case ICmpInst::FCMP_OEQ: Name = "equal"; break; |
- case ICmpInst::FCMP_OGT: Name = "greaterThan"; break; |
- case ICmpInst::FCMP_OGE: Name = "greaterThanOrEqual"; break; |
- case ICmpInst::FCMP_OLT: Name = "lessThan"; break; |
- case ICmpInst::FCMP_OLE: Name = "lessThanOrEqual"; break; |
- case ICmpInst::FCMP_UGT: Name = "lessThanOrEqual"; Invert = true; break; |
- case ICmpInst::FCMP_UGE: Name = "lessThan"; Invert = true; break; |
- case ICmpInst::FCMP_ULT: Name = "greaterThanOrEqual"; Invert = true; break; |
- case ICmpInst::FCMP_ULE: Name = "greaterThan"; Invert = true; break; |
- case ICmpInst::FCMP_UNE: Name = "notEqual"; break; |
- default: I->dump(); error("invalid vector fcmp"); break; |
- } |
- |
- if (Invert) |
- Code << "SIMD_int32x4_not("; |
- |
- Code << getAssignIfNeeded(I) << "SIMD_float32x4_" << Name << "(" |
- << getValueAsStr(I->getOperand(0)) << ", " << getValueAsStr(I->getOperand(1)) << ")"; |
- |
- if (Invert) |
- Code << ")"; |
-} |
- |
-static const Value *getElement(const Value *V, unsigned i) { |
- if (const InsertElementInst *II = dyn_cast<InsertElementInst>(V)) { |
- if (ConstantInt *CI = dyn_cast<ConstantInt>(II->getOperand(2))) { |
- if (CI->equalsInt(i)) |
- return II->getOperand(1); |
- } |
- return getElement(II->getOperand(0), i); |
- } |
- return NULL; |
-} |
- |
-static const Value *getSplatValue(const Value *V) { |
- if (const Constant *C = dyn_cast<Constant>(V)) |
- return C->getSplatValue(); |
- |
- VectorType *VTy = cast<VectorType>(V->getType()); |
- const Value *Result = NULL; |
- for (unsigned i = 0; i < VTy->getNumElements(); ++i) { |
- const Value *E = getElement(V, i); |
- if (!E) |
- return NULL; |
- if (!Result) |
- Result = E; |
- else if (Result != E) |
- return NULL; |
- } |
- return Result; |
- |
-} |
- |
-void JSWriter::generateShiftExpression(const BinaryOperator *I, raw_string_ostream& Code) { |
- // If we're shifting every lane by the same amount (shifting by a splat value |
- // then we can use a ByScalar shift. |
- const Value *Count = I->getOperand(1); |
- if (const Value *Splat = getSplatValue(Count)) { |
- Code << getAssignIfNeeded(I) << "SIMD_int32x4_"; |
- if (I->getOpcode() == Instruction::AShr) |
- Code << "shiftRightArithmeticByScalar"; |
- else if (I->getOpcode() == Instruction::LShr) |
- Code << "shiftRightLogicalByScalar"; |
- else |
- Code << "shiftLeftByScalar"; |
- Code << "(" << getValueAsStr(I->getOperand(0)) << ", " << getValueAsStr(Splat) << ")"; |
- return; |
- } |
- |
- // SIMD.js does not currently have vector-vector shifts. |
- generateUnrolledExpression(I, Code); |
-} |
- |
-void JSWriter::generateUnrolledExpression(const User *I, raw_string_ostream& Code) { |
- VectorType *VT = cast<VectorType>(I->getType()); |
- |
- Code << getAssignIfNeeded(I); |
- |
- if (VT->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4("; |
- } else { |
- Code << "SIMD_float32x4("; |
- } |
- |
- for (unsigned Index = 0; Index < VT->getNumElements(); ++Index) { |
- if (Index != 0) |
- Code << ", "; |
- if (!PreciseF32 && VT->getElementType()->isFloatTy()) { |
- Code << "Math_fround("; |
- } |
- std::string Lane = VT->getNumElements() <= 4 ? |
- std::string(".") + simdLane[Index] : |
- ".s" + utostr(Index); |
- switch (Operator::getOpcode(I)) { |
- case Instruction::SDiv: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << "|0) / (" |
- << getValueAsStr(I->getOperand(1)) << Lane << "|0)|0"; |
- break; |
- case Instruction::UDiv: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << ">>>0) / (" |
- << getValueAsStr(I->getOperand(1)) << Lane << ">>>0)>>>0"; |
- break; |
- case Instruction::SRem: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << "|0) / (" |
- << getValueAsStr(I->getOperand(1)) << Lane << "|0)|0"; |
- break; |
- case Instruction::URem: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << ">>>0) / (" |
- << getValueAsStr(I->getOperand(1)) << Lane << ">>>0)>>>0"; |
- break; |
- case Instruction::AShr: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << "|0) >> (" |
- << getValueAsStr(I->getOperand(1)) << Lane << "|0)|0"; |
- break; |
- case Instruction::LShr: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << "|0) >>> (" |
- << getValueAsStr(I->getOperand(1)) << Lane << "|0)|0"; |
- break; |
- case Instruction::Shl: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << Lane << "|0) << (" |
- << getValueAsStr(I->getOperand(1)) << Lane << "|0)|0"; |
- break; |
- default: I->dump(); error("invalid unrolled vector instr"); break; |
- } |
- if (!PreciseF32 && VT->getElementType()->isFloatTy()) { |
- Code << ")"; |
- } |
- } |
- |
- Code << ")"; |
-} |
- |
-bool JSWriter::generateSIMDExpression(const User *I, raw_string_ostream& Code) { |
- VectorType *VT; |
- if ((VT = dyn_cast<VectorType>(I->getType()))) { |
- // vector-producing instructions |
- checkVectorType(VT); |
- |
- switch (Operator::getOpcode(I)) { |
- default: I->dump(); error("invalid vector instr"); break; |
- case Instruction::Call: // return value is just a SIMD value, no special handling |
- return false; |
- case Instruction::PHI: // handled separately - we push them back into the relooper branchings |
- break; |
- case Instruction::ICmp: |
- generateICmpExpression(cast<ICmpInst>(I), Code); |
- break; |
- case Instruction::FCmp: |
- generateFCmpExpression(cast<FCmpInst>(I), Code); |
- break; |
- case Instruction::SExt: |
- assert(cast<VectorType>(I->getOperand(0)->getType())->getElementType()->isIntegerTy(1) && |
- "sign-extension from vector of other than i1 not yet supported"); |
- // Since we represent vectors of i1 as vectors of sign extended wider integers, |
- // sign extending them is a no-op. |
- Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)); |
- break; |
- case Instruction::Select: |
- // Since we represent vectors of i1 as vectors of sign extended wider integers, |
- // selecting on them is just an elementwise select. |
- if (isa<VectorType>(I->getOperand(0)->getType())) { |
- if (cast<VectorType>(I->getType())->getElementType()->isIntegerTy()) { |
- Code << getAssignIfNeeded(I) << "SIMD_int32x4_select(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << "," << getValueAsStr(I->getOperand(2)) << ")"; break; |
- } else { |
- Code << getAssignIfNeeded(I) << "SIMD_float32x4_select(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << "," << getValueAsStr(I->getOperand(2)) << ")"; break; |
- } |
- return true; |
- } |
- // Otherwise we have a scalar condition, so it's a ?: operator. |
- return false; |
- case Instruction::FAdd: Code << getAssignIfNeeded(I) << "SIMD_float32x4_add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::FMul: Code << getAssignIfNeeded(I) << "SIMD_float32x4_mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::FDiv: Code << getAssignIfNeeded(I) << "SIMD_float32x4_div(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::Add: Code << getAssignIfNeeded(I) << "SIMD_int32x4_add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::Sub: Code << getAssignIfNeeded(I) << "SIMD_int32x4_sub(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::Mul: Code << getAssignIfNeeded(I) << "SIMD_int32x4_mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::And: Code << getAssignIfNeeded(I) << "SIMD_int32x4_and(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::Or: Code << getAssignIfNeeded(I) << "SIMD_int32x4_or(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case Instruction::Xor: |
- // LLVM represents a not(x) as -1 ^ x |
- Code << getAssignIfNeeded(I); |
- if (BinaryOperator::isNot(I)) { |
- Code << "SIMD_int32x4_not(" << getValueAsStr(BinaryOperator::getNotArgument(I)) << ")"; break; |
- } else { |
- Code << "SIMD_int32x4_xor(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; |
- } |
- break; |
- case Instruction::FSub: |
- // LLVM represents an fneg(x) as -0.0 - x. |
- Code << getAssignIfNeeded(I); |
- if (BinaryOperator::isFNeg(I)) { |
- Code << "SIMD_float32x4_neg(" << getValueAsStr(BinaryOperator::getFNegArgument(I)) << ")"; |
- } else { |
- Code << "SIMD_float32x4_sub(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; |
- } |
- break; |
- case Instruction::BitCast: { |
- Code << getAssignIfNeeded(I); |
- if (cast<VectorType>(I->getType())->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_fromFloat32x4Bits(" << getValueAsStr(I->getOperand(0)) << ')'; |
- } else { |
- Code << "SIMD_float32x4_fromInt32x4Bits(" << getValueAsStr(I->getOperand(0)) << ')'; |
- } |
- break; |
- } |
- case Instruction::Load: { |
- const LoadInst *LI = cast<LoadInst>(I); |
- const Value *P = LI->getPointerOperand(); |
- std::string PS = getValueAsStr(P); |
- |
- // Determine if this is a partial load. |
- static const std::string partialAccess[4] = { "X", "XY", "XYZ", "" }; |
- if (VT->getNumElements() < 1 || VT->getNumElements() > 4) { |
- error("invalid number of lanes in SIMD operation!"); |
- break; |
- } |
- const std::string &Part = partialAccess[VT->getNumElements() - 1]; |
- |
- Code << getAssignIfNeeded(I); |
- if (VT->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_load" << Part << "(HEAPU8, " << PS << ")"; |
- } else { |
- Code << "SIMD_float32x4_load" << Part << "(HEAPU8, " << PS << ")"; |
- } |
- break; |
- } |
- case Instruction::InsertElement: |
- generateInsertElementExpression(cast<InsertElementInst>(I), Code); |
- break; |
- case Instruction::ShuffleVector: |
- generateShuffleVectorExpression(cast<ShuffleVectorInst>(I), Code); |
- break; |
- case Instruction::SDiv: |
- case Instruction::UDiv: |
- case Instruction::SRem: |
- case Instruction::URem: |
- // The SIMD API does not currently support these operations directly. |
- // Emulate them using scalar operations (which is essentially the same |
- // as what would happen if the API did support them, since hardware |
- // doesn't support them). |
- generateUnrolledExpression(I, Code); |
- break; |
- case Instruction::AShr: |
- case Instruction::LShr: |
- case Instruction::Shl: |
- generateShiftExpression(cast<BinaryOperator>(I), Code); |
- break; |
- } |
- return true; |
- } else { |
- // vector-consuming instructions |
- if (Operator::getOpcode(I) == Instruction::Store && (VT = dyn_cast<VectorType>(I->getOperand(0)->getType())) && VT->isVectorTy()) { |
- checkVectorType(VT); |
- const StoreInst *SI = cast<StoreInst>(I); |
- const Value *P = SI->getPointerOperand(); |
- std::string PS = getOpName(P); |
- std::string VS = getValueAsStr(SI->getValueOperand()); |
- Code << getAdHocAssign(PS, P->getType()) << getValueAsStr(P) << ';'; |
- |
- // Determine if this is a partial store. |
- static const std::string partialAccess[4] = { "X", "XY", "XYZ", "" }; |
- if (VT->getNumElements() < 1 || VT->getNumElements() > 4) { |
- error("invalid number of lanes in SIMD operation!"); |
- return false; |
- } |
- const std::string &Part = partialAccess[VT->getNumElements() - 1]; |
- |
- if (VT->getElementType()->isIntegerTy()) { |
- Code << "SIMD_int32x4_store" << Part << "(HEAPU8, " << PS << ", " << VS << ")"; |
- } else { |
- Code << "SIMD_float32x4_store" << Part << "(HEAPU8, " << PS << ", " << VS << ")"; |
- } |
- return true; |
- } else if (Operator::getOpcode(I) == Instruction::ExtractElement) { |
- generateExtractElementExpression(cast<ExtractElementInst>(I), Code); |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-static uint64_t LSBMask(unsigned numBits) { |
- return numBits >= 64 ? 0xFFFFFFFFFFFFFFFFULL : (1ULL << numBits) - 1; |
-} |
- |
-// Generate code for and operator, either an Instruction or a ConstantExpr. |
-void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { |
- // To avoid emiting code and variables for the no-op pointer bitcasts |
- // and all-zero-index geps that LLVM needs to satisfy its type system, we |
- // call stripPointerCasts() on all values before translating them. This |
- // includes bitcasts whose only use is lifetime marker intrinsics. |
- assert(I == I->stripPointerCasts()); |
- |
- Type *T = I->getType(); |
- if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { |
- errs() << *I << "\n"; |
- report_fatal_error("legalization problem"); |
- } |
- |
- if (!generateSIMDExpression(I, Code)) switch (Operator::getOpcode(I)) { |
- default: { |
- I->dump(); |
- error("Invalid instruction"); |
- break; |
- } |
- case Instruction::Ret: { |
- const ReturnInst* ret = cast<ReturnInst>(I); |
- const Value *RV = ret->getReturnValue(); |
- if (StackBumped) { |
- Code << "STACKTOP = sp;"; |
- } |
- Code << "return"; |
- if (RV != NULL) { |
- Code << " " << getValueAsCastParenStr(RV, ASM_NONSPECIFIC | ASM_MUST_CAST); |
- } |
- break; |
- } |
- case Instruction::Br: |
- case Instruction::IndirectBr: |
- case Instruction::Switch: return; // handled while relooping |
- case Instruction::Unreachable: { |
- // Typically there should be an abort right before these, so we don't emit any code // TODO: when ASSERTIONS are on, emit abort(0) |
- Code << "// unreachable"; |
- break; |
- } |
- case Instruction::Add: |
- case Instruction::FAdd: |
- case Instruction::Sub: |
- case Instruction::FSub: |
- case Instruction::Mul: |
- case Instruction::FMul: |
- case Instruction::UDiv: |
- case Instruction::SDiv: |
- case Instruction::FDiv: |
- case Instruction::URem: |
- case Instruction::SRem: |
- case Instruction::FRem: |
- case Instruction::And: |
- case Instruction::Or: |
- case Instruction::Xor: |
- case Instruction::Shl: |
- case Instruction::LShr: |
- case Instruction::AShr:{ |
- Code << getAssignIfNeeded(I); |
- unsigned opcode = Operator::getOpcode(I); |
- switch (opcode) { |
- case Instruction::Add: Code << getParenCast( |
- getValueAsParenStr(I->getOperand(0)) + |
- " + " + |
- getValueAsParenStr(I->getOperand(1)), |
- I->getType() |
- ); break; |
- case Instruction::Sub: Code << getParenCast( |
- getValueAsParenStr(I->getOperand(0)) + |
- " - " + |
- getValueAsParenStr(I->getOperand(1)), |
- I->getType() |
- ); break; |
- case Instruction::Mul: Code << getIMul(I->getOperand(0), I->getOperand(1)); break; |
- case Instruction::UDiv: |
- case Instruction::SDiv: |
- case Instruction::URem: |
- case Instruction::SRem: Code << "(" << |
- getValueAsCastParenStr(I->getOperand(0), (opcode == Instruction::SDiv || opcode == Instruction::SRem) ? ASM_SIGNED : ASM_UNSIGNED) << |
- ((opcode == Instruction::UDiv || opcode == Instruction::SDiv) ? " / " : " % ") << |
- getValueAsCastParenStr(I->getOperand(1), (opcode == Instruction::SDiv || opcode == Instruction::SRem) ? ASM_SIGNED : ASM_UNSIGNED) << |
- ")&-1"; break; |
- case Instruction::And: Code << getValueAsStr(I->getOperand(0)) << " & " << getValueAsStr(I->getOperand(1)); break; |
- case Instruction::Or: Code << getValueAsStr(I->getOperand(0)) << " | " << getValueAsStr(I->getOperand(1)); break; |
- case Instruction::Xor: Code << getValueAsStr(I->getOperand(0)) << " ^ " << getValueAsStr(I->getOperand(1)); break; |
- case Instruction::Shl: { |
- std::string Shifted = getValueAsStr(I->getOperand(0)) + " << " + getValueAsStr(I->getOperand(1)); |
- if (I->getType()->getIntegerBitWidth() < 32) { |
- Shifted = getParenCast(Shifted, I->getType(), ASM_UNSIGNED); // remove bits that are shifted beyond the size of this value |
- } |
- Code << Shifted; |
- break; |
- } |
- case Instruction::AShr: |
- case Instruction::LShr: { |
- std::string Input = getValueAsStr(I->getOperand(0)); |
- if (I->getType()->getIntegerBitWidth() < 32) { |
- Input = '(' + getCast(Input, I->getType(), opcode == Instruction::AShr ? ASM_SIGNED : ASM_UNSIGNED) + ')'; // fill in high bits, as shift needs those and is done in 32-bit |
- } |
- Code << Input << (opcode == Instruction::AShr ? " >> " : " >>> ") << getValueAsStr(I->getOperand(1)); |
- break; |
- } |
- |
- case Instruction::FAdd: Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " + " + getValueAsStr(I->getOperand(1)), I->getType()); break; |
- case Instruction::FMul: Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " * " + getValueAsStr(I->getOperand(1)), I->getType()); break; |
- case Instruction::FDiv: Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " / " + getValueAsStr(I->getOperand(1)), I->getType()); break; |
- case Instruction::FRem: Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " % " + getValueAsStr(I->getOperand(1)), I->getType()); break; |
- case Instruction::FSub: |
- // LLVM represents an fneg(x) as -0.0 - x. |
- if (BinaryOperator::isFNeg(I)) { |
- Code << ensureFloat("-" + getValueAsStr(BinaryOperator::getFNegArgument(I)), I->getType()); |
- } else { |
- Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " - " + getValueAsStr(I->getOperand(1)), I->getType()); |
- } |
- break; |
- default: error("bad binary opcode"); break; |
- } |
- break; |
- } |
- case Instruction::FCmp: { |
- Code << getAssignIfNeeded(I); |
- switch (cast<FCmpInst>(I)->getPredicate()) { |
- // Comparisons which are simple JS operators. |
- case FCmpInst::FCMP_OEQ: Code << getValueAsStr(I->getOperand(0)) << " == " << getValueAsStr(I->getOperand(1)); break; |
- case FCmpInst::FCMP_UNE: Code << getValueAsStr(I->getOperand(0)) << " != " << getValueAsStr(I->getOperand(1)); break; |
- case FCmpInst::FCMP_OGT: Code << getValueAsStr(I->getOperand(0)) << " > " << getValueAsStr(I->getOperand(1)); break; |
- case FCmpInst::FCMP_OGE: Code << getValueAsStr(I->getOperand(0)) << " >= " << getValueAsStr(I->getOperand(1)); break; |
- case FCmpInst::FCMP_OLT: Code << getValueAsStr(I->getOperand(0)) << " < " << getValueAsStr(I->getOperand(1)); break; |
- case FCmpInst::FCMP_OLE: Code << getValueAsStr(I->getOperand(0)) << " <= " << getValueAsStr(I->getOperand(1)); break; |
- |
- // Comparisons which are inverses of JS operators. |
- case FCmpInst::FCMP_UGT: |
- Code << "!(" << getValueAsStr(I->getOperand(0)) << " <= " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- case FCmpInst::FCMP_UGE: |
- Code << "!(" << getValueAsStr(I->getOperand(0)) << " < " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- case FCmpInst::FCMP_ULT: |
- Code << "!(" << getValueAsStr(I->getOperand(0)) << " >= " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- case FCmpInst::FCMP_ULE: |
- Code << "!(" << getValueAsStr(I->getOperand(0)) << " > " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- |
- // Comparisons which require explicit NaN checks. |
- case FCmpInst::FCMP_UEQ: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << " != " << getValueAsStr(I->getOperand(0)) << ") | " << |
- "(" << getValueAsStr(I->getOperand(1)) << " != " << getValueAsStr(I->getOperand(1)) << ") |" << |
- "(" << getValueAsStr(I->getOperand(0)) << " == " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- case FCmpInst::FCMP_ONE: |
- Code << "(" << getValueAsStr(I->getOperand(0)) << " == " << getValueAsStr(I->getOperand(0)) << ") & " << |
- "(" << getValueAsStr(I->getOperand(1)) << " == " << getValueAsStr(I->getOperand(1)) << ") &" << |
- "(" << getValueAsStr(I->getOperand(0)) << " != " << getValueAsStr(I->getOperand(1)) << ")"; |
- break; |
- |
- // Simple NaN checks. |
- case FCmpInst::FCMP_ORD: Code << "(" << getValueAsStr(I->getOperand(0)) << " == " << getValueAsStr(I->getOperand(0)) << ") & " << |
- "(" << getValueAsStr(I->getOperand(1)) << " == " << getValueAsStr(I->getOperand(1)) << ")"; break; |
- case FCmpInst::FCMP_UNO: Code << "(" << getValueAsStr(I->getOperand(0)) << " != " << getValueAsStr(I->getOperand(0)) << ") | " << |
- "(" << getValueAsStr(I->getOperand(1)) << " != " << getValueAsStr(I->getOperand(1)) << ")"; break; |
- |
- // Simple constants. |
- case FCmpInst::FCMP_FALSE: Code << "0"; break; |
- case FCmpInst::FCMP_TRUE : Code << "1"; break; |
- |
- default: error("bad fcmp"); break; |
- } |
- break; |
- } |
- case Instruction::ICmp: { |
- unsigned predicate = isa<ConstantExpr>(I) ? |
- cast<ConstantExpr>(I)->getPredicate() : |
- cast<ICmpInst>(I)->getPredicate(); |
- AsmCast sign = CmpInst::isUnsigned(predicate) ? ASM_UNSIGNED : ASM_SIGNED; |
- Code << getAssignIfNeeded(I) << "(" << |
- getValueAsCastStr(I->getOperand(0), sign) << |
- ")"; |
- switch (predicate) { |
- case ICmpInst::ICMP_EQ: Code << "=="; break; |
- case ICmpInst::ICMP_NE: Code << "!="; break; |
- case ICmpInst::ICMP_ULE: Code << "<="; break; |
- case ICmpInst::ICMP_SLE: Code << "<="; break; |
- case ICmpInst::ICMP_UGE: Code << ">="; break; |
- case ICmpInst::ICMP_SGE: Code << ">="; break; |
- case ICmpInst::ICMP_ULT: Code << "<"; break; |
- case ICmpInst::ICMP_SLT: Code << "<"; break; |
- case ICmpInst::ICMP_UGT: Code << ">"; break; |
- case ICmpInst::ICMP_SGT: Code << ">"; break; |
- default: llvm_unreachable("Invalid ICmp predicate"); |
- } |
- Code << "(" << |
- getValueAsCastStr(I->getOperand(1), sign) << |
- ")"; |
- break; |
- } |
- case Instruction::Alloca: { |
- const AllocaInst* AI = cast<AllocaInst>(I); |
- |
- // We've done an alloca, so we'll have bumped the stack and will |
- // need to restore it. |
- // Yes, we shouldn't have to bump it for nativized vars, however |
- // they are included in the frame offset, so the restore is still |
- // needed until that is fixed. |
- StackBumped = true; |
- |
- if (NativizedVars.count(AI)) { |
- // nativized stack variable, we just need a 'var' definition |
- UsedVars[getJSName(AI)] = AI->getType()->getElementType(); |
- return; |
- } |
- |
- // Fixed-size entry-block allocations are allocated all at once in the |
- // function prologue. |
- if (AI->isStaticAlloca()) { |
- uint64_t Offset; |
- if (Allocas.getFrameOffset(AI, &Offset)) { |
- Code << getAssign(AI); |
- if (Allocas.getMaxAlignment() <= STACK_ALIGN) { |
- Code << "sp"; |
- } else { |
- Code << "sp_a"; // aligned base of stack is different, use that |
- } |
- if (Offset != 0) { |
- Code << " + " << Offset << "|0"; |
- } |
- break; |
- } |
- // Otherwise, this alloca is being represented by another alloca, so |
- // there's nothing to print. |
- return; |
- } |
- |
- assert(AI->getAlignment() <= STACK_ALIGN); // TODO |
- |
- Type *T = AI->getAllocatedType(); |
- std::string Size; |
- uint64_t BaseSize = DL->getTypeAllocSize(T); |
- const Value *AS = AI->getArraySize(); |
- if (const ConstantInt *CI = dyn_cast<ConstantInt>(AS)) { |
- Size = Twine(stackAlign(BaseSize * CI->getZExtValue())).str(); |
- } else { |
- Size = stackAlignStr("((" + utostr(BaseSize) + '*' + getValueAsStr(AS) + ")|0)"); |
- } |
- Code << getAssign(AI) << "STACKTOP; " << getStackBump(Size); |
- break; |
- } |
- case Instruction::Load: { |
- const LoadInst *LI = cast<LoadInst>(I); |
- const Value *P = LI->getPointerOperand(); |
- unsigned Alignment = LI->getAlignment(); |
- if (NativizedVars.count(P)) { |
- Code << getAssign(LI) << getValueAsStr(P); |
- } else { |
- Code << getLoad(LI, P, LI->getType(), Alignment); |
- } |
- break; |
- } |
- case Instruction::Store: { |
- const StoreInst *SI = cast<StoreInst>(I); |
- const Value *P = SI->getPointerOperand(); |
- const Value *V = SI->getValueOperand(); |
- unsigned Alignment = SI->getAlignment(); |
- std::string VS = getValueAsStr(V); |
- if (NativizedVars.count(P)) { |
- Code << getValueAsStr(P) << " = " << VS; |
- } else { |
- Code << getStore(SI, P, V->getType(), VS, Alignment); |
- } |
- |
- Type *T = V->getType(); |
- if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { |
- errs() << *I << "\n"; |
- report_fatal_error("legalization problem"); |
- } |
- break; |
- } |
- case Instruction::GetElementPtr: { |
- Code << getAssignIfNeeded(I); |
- const GEPOperator *GEP = cast<GEPOperator>(I); |
- gep_type_iterator GTI = gep_type_begin(GEP); |
- int32_t ConstantOffset = 0; |
- std::string text = getValueAsParenStr(GEP->getPointerOperand()); |
- |
- GetElementPtrInst::const_op_iterator I = GEP->op_begin(); |
- I++; |
- for (GetElementPtrInst::const_op_iterator E = GEP->op_end(); |
- I != E; ++I) { |
- const Value *Index = *I; |
- if (StructType *STy = dyn_cast<StructType>(*GTI++)) { |
- // For a struct, add the member offset. |
- unsigned FieldNo = cast<ConstantInt>(Index)->getZExtValue(); |
- uint32_t Offset = DL->getStructLayout(STy)->getElementOffset(FieldNo); |
- ConstantOffset = (uint32_t)ConstantOffset + Offset; |
- } else { |
- // For an array, add the element offset, explicitly scaled. |
- uint32_t ElementSize = DL->getTypeAllocSize(*GTI); |
- if (const ConstantInt *CI = dyn_cast<ConstantInt>(Index)) { |
- ConstantOffset = (uint32_t)ConstantOffset + (uint32_t)CI->getSExtValue() * ElementSize; |
- } else { |
- text = "(" + text + " + (" + getIMul(Index, ConstantInt::get(Type::getInt32Ty(GEP->getContext()), ElementSize)) + ")|0)"; |
- } |
- } |
- } |
- if (ConstantOffset != 0) { |
- text = "(" + text + " + " + itostr(ConstantOffset) + "|0)"; |
- } |
- Code << text; |
- break; |
- } |
- case Instruction::PHI: { |
- // handled separately - we push them back into the relooper branchings |
- return; |
- } |
- case Instruction::PtrToInt: |
- case Instruction::IntToPtr: |
- Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)); |
- break; |
- case Instruction::Trunc: |
- case Instruction::ZExt: |
- case Instruction::SExt: |
- case Instruction::FPTrunc: |
- case Instruction::FPExt: |
- case Instruction::FPToUI: |
- case Instruction::FPToSI: |
- case Instruction::UIToFP: |
- case Instruction::SIToFP: { |
- Code << getAssignIfNeeded(I); |
- switch (Operator::getOpcode(I)) { |
- case Instruction::Trunc: { |
- //unsigned inBits = V->getType()->getIntegerBitWidth(); |
- unsigned outBits = I->getType()->getIntegerBitWidth(); |
- Code << getValueAsStr(I->getOperand(0)) << "&" << utostr(LSBMask(outBits)); |
- break; |
- } |
- case Instruction::SExt: { |
- std::string bits = utostr(32 - I->getOperand(0)->getType()->getIntegerBitWidth()); |
- Code << getValueAsStr(I->getOperand(0)) << " << " << bits << " >> " << bits; |
- break; |
- } |
- case Instruction::ZExt: { |
- Code << getValueAsCastStr(I->getOperand(0), ASM_UNSIGNED); |
- break; |
- } |
- case Instruction::FPExt: { |
- if (PreciseF32) { |
- Code << "+" << getValueAsStr(I->getOperand(0)); break; |
- } else { |
- Code << getValueAsStr(I->getOperand(0)); break; |
- } |
- break; |
- } |
- case Instruction::FPTrunc: { |
- Code << ensureFloat(getValueAsStr(I->getOperand(0)), I->getType()); |
- break; |
- } |
- case Instruction::SIToFP: Code << '(' << getCast(getValueAsCastParenStr(I->getOperand(0), ASM_SIGNED), I->getType()) << ')'; break; |
- case Instruction::UIToFP: Code << '(' << getCast(getValueAsCastParenStr(I->getOperand(0), ASM_UNSIGNED), I->getType()) << ')'; break; |
- case Instruction::FPToSI: Code << '(' << getDoubleToInt(getValueAsParenStr(I->getOperand(0))) << ')'; break; |
- case Instruction::FPToUI: Code << '(' << getCast(getDoubleToInt(getValueAsParenStr(I->getOperand(0))), I->getType(), ASM_UNSIGNED) << ')'; break; |
- case Instruction::PtrToInt: Code << '(' << getValueAsStr(I->getOperand(0)) << ')'; break; |
- case Instruction::IntToPtr: Code << '(' << getValueAsStr(I->getOperand(0)) << ')'; break; |
- default: llvm_unreachable("Unreachable"); |
- } |
- break; |
- } |
- case Instruction::BitCast: { |
- Code << getAssignIfNeeded(I); |
- // Most bitcasts are no-ops for us. However, the exception is int to float and float to int |
- Type *InType = I->getOperand(0)->getType(); |
- Type *OutType = I->getType(); |
- std::string V = getValueAsStr(I->getOperand(0)); |
- if (InType->isIntegerTy() && OutType->isFloatingPointTy()) { |
- assert(InType->getIntegerBitWidth() == 32); |
- Code << "(HEAP32[tempDoublePtr>>2]=" << V << "," << getCast("HEAPF32[tempDoublePtr>>2]", Type::getFloatTy(TheModule->getContext())) << ")"; |
- } else if (OutType->isIntegerTy() && InType->isFloatingPointTy()) { |
- assert(OutType->getIntegerBitWidth() == 32); |
- Code << "(HEAPF32[tempDoublePtr>>2]=" << V << "," "HEAP32[tempDoublePtr>>2]|0)"; |
- } else { |
- Code << V; |
- } |
- break; |
- } |
- case Instruction::Call: { |
- const CallInst *CI = cast<CallInst>(I); |
- std::string Call = handleCall(CI); |
- if (Call.empty()) return; |
- Code << Call; |
- break; |
- } |
- case Instruction::Select: { |
- Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)) << " ? " << |
- getValueAsStr(I->getOperand(1)) << " : " << |
- getValueAsStr(I->getOperand(2)); |
- break; |
- } |
- case Instruction::AtomicRMW: { |
- const AtomicRMWInst *rmwi = cast<AtomicRMWInst>(I); |
- const Value *P = rmwi->getOperand(0); |
- const Value *V = rmwi->getOperand(1); |
- std::string VS = getValueAsStr(V); |
- Code << getLoad(rmwi, P, I->getType(), 0) << ';'; |
- // Most bitcasts are no-ops for us. However, the exception is int to float and float to int |
- switch (rmwi->getOperation()) { |
- case AtomicRMWInst::Xchg: Code << getStore(rmwi, P, I->getType(), VS, 0); break; |
- case AtomicRMWInst::Add: Code << getStore(rmwi, P, I->getType(), "((" + getJSName(I) + '+' + VS + ")|0)", 0); break; |
- case AtomicRMWInst::Sub: Code << getStore(rmwi, P, I->getType(), "((" + getJSName(I) + '-' + VS + ")|0)", 0); break; |
- case AtomicRMWInst::And: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '&' + VS + ")", 0); break; |
- case AtomicRMWInst::Nand: Code << getStore(rmwi, P, I->getType(), "(~(" + getJSName(I) + '&' + VS + "))", 0); break; |
- case AtomicRMWInst::Or: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '|' + VS + ")", 0); break; |
- case AtomicRMWInst::Xor: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '^' + VS + ")", 0); break; |
- case AtomicRMWInst::Max: |
- case AtomicRMWInst::Min: |
- case AtomicRMWInst::UMax: |
- case AtomicRMWInst::UMin: |
- case AtomicRMWInst::BAD_BINOP: llvm_unreachable("Bad atomic operation"); |
- } |
- break; |
- } |
- case Instruction::Fence: // no threads, so nothing to do here |
- Code << "/* fence */"; |
- break; |
- } |
- |
- if (const Instruction *Inst = dyn_cast<Instruction>(I)) { |
- Code << ';'; |
- // append debug info |
- emitDebugInfo(Code, Inst); |
- Code << '\n'; |
- } |
-} |
- |
-// Checks whether to use a condition variable. We do so for switches and for indirectbrs |
-static const Value *considerConditionVar(const Instruction *I) { |
- if (const IndirectBrInst *IB = dyn_cast<const IndirectBrInst>(I)) { |
- return IB->getAddress(); |
- } |
- const SwitchInst *SI = dyn_cast<SwitchInst>(I); |
- if (!SI) return NULL; |
- // use a switch if the range is not too big or sparse |
- int64_t Minn = INT64_MAX, Maxx = INT64_MIN; |
- for (SwitchInst::ConstCaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { |
- int64_t Curr = i.getCaseValue()->getSExtValue(); |
- if (Curr < Minn) Minn = Curr; |
- if (Curr > Maxx) Maxx = Curr; |
- } |
- int64_t Range = Maxx - Minn; |
- int Num = SI->getNumCases(); |
- return Num < 5 || Range > 10*1024 || (Range/Num) > 1024 ? NULL : SI->getCondition(); // heuristics |
-} |
- |
-void JSWriter::addBlock(const BasicBlock *BB, Relooper& R, LLVMToRelooperMap& LLVMToRelooper) { |
- std::string Code; |
- raw_string_ostream CodeStream(Code); |
- for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); |
- I != E; ++I) { |
- if (I->stripPointerCasts() == I) { |
- generateExpression(I, CodeStream); |
- } |
- } |
- CodeStream.flush(); |
- const Value* Condition = considerConditionVar(BB->getTerminator()); |
- Block *Curr = new Block(Code.c_str(), Condition ? getValueAsCastStr(Condition).c_str() : NULL); |
- LLVMToRelooper[BB] = Curr; |
- R.AddBlock(Curr); |
-} |
- |
-void JSWriter::printFunctionBody(const Function *F) { |
- assert(!F->isDeclaration()); |
- |
- // Prepare relooper |
- Relooper::MakeOutputBuffer(1024*1024); |
- Relooper R; |
- //if (!canReloop(F)) R.SetEmulate(true); |
- if (F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, Attribute::MinSize) || |
- F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, Attribute::OptimizeForSize)) { |
- R.SetMinSize(true); |
- } |
- R.SetAsmJSMode(1); |
- Block *Entry = NULL; |
- LLVMToRelooperMap LLVMToRelooper; |
- |
- // Create relooper blocks with their contents. TODO: We could optimize |
- // indirectbr by emitting indexed blocks first, so their indexes |
- // match up with the label index. |
- for (Function::const_iterator BI = F->begin(), BE = F->end(); |
- BI != BE; ++BI) { |
- InvokeState = 0; // each basic block begins in state 0; the previous may not have cleared it, if e.g. it had a throw in the middle and the rest of it was decapitated |
- addBlock(BI, R, LLVMToRelooper); |
- if (!Entry) Entry = LLVMToRelooper[BI]; |
- } |
- assert(Entry); |
- |
- // Create branchings |
- for (Function::const_iterator BI = F->begin(), BE = F->end(); |
- BI != BE; ++BI) { |
- const TerminatorInst *TI = BI->getTerminator(); |
- switch (TI->getOpcode()) { |
- default: { |
- report_fatal_error("invalid branch instr " + Twine(TI->getOpcodeName())); |
- break; |
- } |
- case Instruction::Br: { |
- const BranchInst* br = cast<BranchInst>(TI); |
- if (br->getNumOperands() == 3) { |
- BasicBlock *S0 = br->getSuccessor(0); |
- BasicBlock *S1 = br->getSuccessor(1); |
- std::string P0 = getPhiCode(&*BI, S0); |
- std::string P1 = getPhiCode(&*BI, S1); |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S0], getValueAsStr(TI->getOperand(0)).c_str(), P0.size() > 0 ? P0.c_str() : NULL); |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S1], NULL, P1.size() > 0 ? P1.c_str() : NULL); |
- } else if (br->getNumOperands() == 1) { |
- BasicBlock *S = br->getSuccessor(0); |
- std::string P = getPhiCode(&*BI, S); |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S], NULL, P.size() > 0 ? P.c_str() : NULL); |
- } else { |
- error("Branch with 2 operands?"); |
- } |
- break; |
- } |
- case Instruction::IndirectBr: { |
- const IndirectBrInst* br = cast<IndirectBrInst>(TI); |
- unsigned Num = br->getNumDestinations(); |
- std::set<const BasicBlock*> Seen; // sadly llvm allows the same block to appear multiple times |
- bool SetDefault = false; // pick the first and make it the default, llvm gives no reasonable default here |
- for (unsigned i = 0; i < Num; i++) { |
- const BasicBlock *S = br->getDestination(i); |
- if (Seen.find(S) != Seen.end()) continue; |
- Seen.insert(S); |
- std::string P = getPhiCode(&*BI, S); |
- std::string Target; |
- if (!SetDefault) { |
- SetDefault = true; |
- } else { |
- Target = "case " + utostr(getBlockAddress(F, S)) + ": "; |
- } |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*S], Target.size() > 0 ? Target.c_str() : NULL, P.size() > 0 ? P.c_str() : NULL); |
- } |
- break; |
- } |
- case Instruction::Switch: { |
- const SwitchInst* SI = cast<SwitchInst>(TI); |
- bool UseSwitch = !!considerConditionVar(SI); |
- BasicBlock *DD = SI->getDefaultDest(); |
- std::string P = getPhiCode(&*BI, DD); |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*DD], NULL, P.size() > 0 ? P.c_str() : NULL); |
- typedef std::map<const BasicBlock*, std::string> BlockCondMap; |
- BlockCondMap BlocksToConditions; |
- for (SwitchInst::ConstCaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { |
- const BasicBlock *BB = i.getCaseSuccessor(); |
- std::string Curr = i.getCaseValue()->getValue().toString(10, true); |
- std::string Condition; |
- if (UseSwitch) { |
- Condition = "case " + Curr + ": "; |
- } else { |
- Condition = "(" + getValueAsCastParenStr(SI->getCondition()) + " == " + Curr + ")"; |
- } |
- BlocksToConditions[BB] = Condition + (!UseSwitch && BlocksToConditions[BB].size() > 0 ? " | " : "") + BlocksToConditions[BB]; |
- } |
- for (BlockCondMap::const_iterator I = BlocksToConditions.begin(), E = BlocksToConditions.end(); I != E; ++I) { |
- const BasicBlock *BB = I->first; |
- if (BB == DD) continue; // ok to eliminate this, default dest will get there anyhow |
- std::string P = getPhiCode(&*BI, BB); |
- LLVMToRelooper[&*BI]->AddBranchTo(LLVMToRelooper[&*BB], I->second.c_str(), P.size() > 0 ? P.c_str() : NULL); |
- } |
- break; |
- } |
- case Instruction::Ret: |
- case Instruction::Unreachable: break; |
- } |
- } |
- |
- // Calculate relooping and print |
- R.Calculate(Entry); |
- R.Render(); |
- |
- // Emit local variables |
- UsedVars["sp"] = Type::getInt32Ty(F->getContext()); |
- unsigned MaxAlignment = Allocas.getMaxAlignment(); |
- if (MaxAlignment > STACK_ALIGN) { |
- UsedVars["sp_a"] = Type::getInt32Ty(F->getContext()); |
- } |
- UsedVars["label"] = Type::getInt32Ty(F->getContext()); |
- if (!UsedVars.empty()) { |
- unsigned Count = 0; |
- for (VarMap::const_iterator VI = UsedVars.begin(); VI != UsedVars.end(); ++VI) { |
- if (Count == 20) { |
- Out << ";\n"; |
- Count = 0; |
- } |
- if (Count == 0) Out << " var "; |
- if (Count > 0) { |
- Out << ", "; |
- } |
- Count++; |
- Out << VI->first << " = "; |
- switch (VI->second->getTypeID()) { |
- default: |
- llvm_unreachable("unsupported variable initializer type"); |
- case Type::PointerTyID: |
- case Type::IntegerTyID: |
- Out << "0"; |
- break; |
- case Type::FloatTyID: |
- if (PreciseF32) { |
- Out << "Math_fround(0)"; |
- break; |
- } |
- // otherwise fall through to double |
- case Type::DoubleTyID: |
- Out << "+0"; |
- break; |
- case Type::VectorTyID: |
- if (cast<VectorType>(VI->second)->getElementType()->isIntegerTy()) { |
- Out << "SIMD_int32x4(0,0,0,0)"; |
- } else { |
- Out << "SIMD_float32x4(0,0,0,0)"; |
- } |
- break; |
- } |
- } |
- Out << ";"; |
- nl(Out); |
- } |
- |
- { |
- static bool Warned = false; |
- if (!Warned && OptLevel < 2 && UsedVars.size() > 2000) { |
- prettyWarning() << "emitted code will contain very large numbers of local variables, which is bad for performance (build to JS with -O2 or above to avoid this - make sure to do so both on source files, and during 'linking')\n"; |
- Warned = true; |
- } |
- } |
- |
- // Emit stack entry |
- Out << " " << getAdHocAssign("sp", Type::getInt32Ty(F->getContext())) << "STACKTOP;"; |
- if (uint64_t FrameSize = Allocas.getFrameSize()) { |
- if (MaxAlignment > STACK_ALIGN) { |
- // We must align this entire stack frame to something higher than the default |
- Out << "\n "; |
- Out << "sp_a = STACKTOP = (STACKTOP + " << utostr(MaxAlignment-1) << ")&-" << utostr(MaxAlignment) << ";"; |
- } |
- Out << "\n "; |
- Out << getStackBump(FrameSize); |
- } |
- |
- // Emit (relooped) code |
- char *buffer = Relooper::GetOutputBuffer(); |
- nl(Out) << buffer; |
- |
- // Ensure a final return if necessary |
- Type *RT = F->getFunctionType()->getReturnType(); |
- if (!RT->isVoidTy()) { |
- char *LastCurly = strrchr(buffer, '}'); |
- if (!LastCurly) LastCurly = buffer; |
- char *FinalReturn = strstr(LastCurly, "return "); |
- if (!FinalReturn) { |
- Out << " return " << getParenCast(getConstant(UndefValue::get(RT)), RT, ASM_NONSPECIFIC) << ";\n"; |
- } |
- } |
-} |
- |
-void JSWriter::processConstants() { |
- // First, calculate the address of each constant |
- for (Module::const_global_iterator I = TheModule->global_begin(), |
- E = TheModule->global_end(); I != E; ++I) { |
- if (I->hasInitializer()) { |
- parseConstant(I->getName().str(), I->getInitializer(), true); |
- } |
- } |
- // Second, allocate their contents |
- for (Module::const_global_iterator I = TheModule->global_begin(), |
- E = TheModule->global_end(); I != E; ++I) { |
- if (I->hasInitializer()) { |
- parseConstant(I->getName().str(), I->getInitializer(), false); |
- } |
- } |
-} |
- |
-void JSWriter::printFunction(const Function *F) { |
- ValueNames.clear(); |
- |
- // Prepare and analyze function |
- |
- UsedVars.clear(); |
- UniqueNum = 0; |
- |
- // When optimizing, the regular optimizer (mem2reg, SROA, GVN, and others) |
- // will have already taken all the opportunities for nativization. |
- if (OptLevel == CodeGenOpt::None) |
- calculateNativizedVars(F); |
- |
- // Do alloca coloring at -O1 and higher. |
- Allocas.analyze(*F, *DL, OptLevel != CodeGenOpt::None); |
- |
- // Emit the function |
- |
- std::string Name = F->getName(); |
- sanitizeGlobal(Name); |
- Out << "function " << Name << "("; |
- for (Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end(); |
- AI != AE; ++AI) { |
- if (AI != F->arg_begin()) Out << ","; |
- Out << getJSName(AI); |
- } |
- Out << ") {"; |
- nl(Out); |
- for (Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end(); |
- AI != AE; ++AI) { |
- std::string name = getJSName(AI); |
- Out << " " << name << " = " << getCast(name, AI->getType(), ASM_NONSPECIFIC) << ";"; |
- nl(Out); |
- } |
- printFunctionBody(F); |
- Out << "}"; |
- nl(Out); |
- |
- Allocas.clear(); |
- StackBumped = false; |
-} |
- |
-void JSWriter::printModuleBody() { |
- processConstants(); |
- |
- // Emit function bodies. |
- nl(Out) << "// EMSCRIPTEN_START_FUNCTIONS"; nl(Out); |
- for (Module::const_iterator I = TheModule->begin(), E = TheModule->end(); |
- I != E; ++I) { |
- if (!I->isDeclaration()) printFunction(I); |
- } |
- Out << "function runPostSets() {\n"; |
- Out << " " << PostSets << "\n"; |
- Out << "}\n"; |
- PostSets = ""; |
- Out << "// EMSCRIPTEN_END_FUNCTIONS\n\n"; |
- |
- assert(GlobalData32.size() == 0 && GlobalData8.size() == 0); // FIXME when we use optimal constant alignments |
- |
- // TODO fix commas |
- Out << "/* memory initializer */ allocate(["; |
- printCommaSeparated(GlobalData64); |
- if (GlobalData64.size() > 0 && GlobalData32.size() + GlobalData8.size() > 0) { |
- Out << ","; |
- } |
- printCommaSeparated(GlobalData32); |
- if (GlobalData32.size() > 0 && GlobalData8.size() > 0) { |
- Out << ","; |
- } |
- printCommaSeparated(GlobalData8); |
- Out << "], \"i8\", ALLOC_NONE, Runtime.GLOBAL_BASE);"; |
- |
- // Emit metadata for emcc driver |
- Out << "\n\n// EMSCRIPTEN_METADATA\n"; |
- Out << "{\n"; |
- |
- Out << "\"declares\": ["; |
- bool first = true; |
- for (Module::const_iterator I = TheModule->begin(), E = TheModule->end(); |
- I != E; ++I) { |
- if (I->isDeclaration() && !I->use_empty()) { |
- // Ignore intrinsics that are always no-ops or expanded into other code |
- // which doesn't require the intrinsic function itself to be declared. |
- if (I->isIntrinsic()) { |
- switch (I->getIntrinsicID()) { |
- case Intrinsic::dbg_declare: |
- case Intrinsic::dbg_value: |
- case Intrinsic::lifetime_start: |
- case Intrinsic::lifetime_end: |
- case Intrinsic::invariant_start: |
- case Intrinsic::invariant_end: |
- case Intrinsic::prefetch: |
- case Intrinsic::memcpy: |
- case Intrinsic::memset: |
- case Intrinsic::memmove: |
- case Intrinsic::expect: |
- case Intrinsic::flt_rounds: |
- continue; |
- } |
- } |
- |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"" << I->getName() << "\""; |
- } |
- } |
- for (NameSet::const_iterator I = Declares.begin(), E = Declares.end(); |
- I != E; ++I) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"" << *I << "\""; |
- } |
- Out << "],"; |
- |
- Out << "\"redirects\": {"; |
- first = true; |
- for (StringMap::const_iterator I = Redirects.begin(), E = Redirects.end(); |
- I != E; ++I) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"_" << I->first << "\": \"" << I->second << "\""; |
- } |
- Out << "},"; |
- |
- Out << "\"externs\": ["; |
- first = true; |
- for (NameSet::const_iterator I = Externals.begin(), E = Externals.end(); |
- I != E; ++I) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"" << *I << "\""; |
- } |
- Out << "],"; |
- |
- Out << "\"implementedFunctions\": ["; |
- first = true; |
- for (Module::const_iterator I = TheModule->begin(), E = TheModule->end(); |
- I != E; ++I) { |
- if (!I->isDeclaration()) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- std::string name = I->getName(); |
- sanitizeGlobal(name); |
- Out << "\"" << name << '"'; |
- } |
- } |
- Out << "],"; |
- |
- Out << "\"tables\": {"; |
- unsigned Num = FunctionTables.size(); |
- for (FunctionTableMap::iterator I = FunctionTables.begin(), E = FunctionTables.end(); I != E; ++I) { |
- Out << " \"" << I->first << "\": \"var FUNCTION_TABLE_" << I->first << " = ["; |
- FunctionTable &Table = I->second; |
- // ensure power of two |
- unsigned Size = 1; |
- while (Size < Table.size()) Size <<= 1; |
- while (Table.size() < Size) Table.push_back("0"); |
- for (unsigned i = 0; i < Table.size(); i++) { |
- Out << Table[i]; |
- if (i < Table.size()-1) Out << ","; |
- } |
- Out << "];\""; |
- if (--Num > 0) Out << ","; |
- Out << "\n"; |
- } |
- Out << "},"; |
- |
- Out << "\"initializers\": ["; |
- first = true; |
- for (unsigned i = 0; i < GlobalInitializers.size(); i++) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"" << GlobalInitializers[i] << "\""; |
- } |
- Out << "],"; |
- |
- Out << "\"exports\": ["; |
- first = true; |
- for (unsigned i = 0; i < Exports.size(); i++) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"" << Exports[i] << "\""; |
- } |
- Out << "],"; |
- |
- Out << "\"cantValidate\": \"" << CantValidate << "\","; |
- |
- Out << "\"simd\": "; |
- Out << (UsesSIMD ? "1" : "0"); |
- Out << ","; |
- |
- Out << "\"namedGlobals\": {"; |
- first = true; |
- for (NameIntMap::const_iterator I = NamedGlobals.begin(), E = NamedGlobals.end(); I != E; ++I) { |
- if (first) { |
- first = false; |
- } else { |
- Out << ", "; |
- } |
- Out << "\"_" << I->first << "\": \"" << utostr(I->second) << "\""; |
- } |
- Out << "}"; |
- |
- Out << "\n}\n"; |
-} |
- |
-void JSWriter::parseConstant(const std::string& name, const Constant* CV, bool calculate) { |
- if (isa<GlobalValue>(CV)) |
- return; |
- //errs() << "parsing constant " << name << "\n"; |
- // TODO: we repeat some work in both calculate and emit phases here |
- // FIXME: use the proper optimal alignments |
- if (const ConstantDataSequential *CDS = |
- dyn_cast<ConstantDataSequential>(CV)) { |
- assert(CDS->isString()); |
- if (calculate) { |
- HeapData *GlobalData = allocateAddress(name); |
- StringRef Str = CDS->getAsString(); |
- for (unsigned int i = 0; i < Str.size(); i++) { |
- GlobalData->push_back(Str.data()[i]); |
- } |
- } |
- } else if (const ConstantFP *CFP = dyn_cast<ConstantFP>(CV)) { |
- APFloat APF = CFP->getValueAPF(); |
- if (CFP->getType() == Type::getFloatTy(CFP->getContext())) { |
- if (calculate) { |
- HeapData *GlobalData = allocateAddress(name); |
- union flt { float f; unsigned char b[sizeof(float)]; } flt; |
- flt.f = APF.convertToFloat(); |
- for (unsigned i = 0; i < sizeof(float); ++i) { |
- GlobalData->push_back(flt.b[i]); |
- } |
- } |
- } else if (CFP->getType() == Type::getDoubleTy(CFP->getContext())) { |
- if (calculate) { |
- HeapData *GlobalData = allocateAddress(name); |
- union dbl { double d; unsigned char b[sizeof(double)]; } dbl; |
- dbl.d = APF.convertToDouble(); |
- for (unsigned i = 0; i < sizeof(double); ++i) { |
- GlobalData->push_back(dbl.b[i]); |
- } |
- } |
- } else { |
- assert(false && "Unsupported floating-point type"); |
- } |
- } else if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { |
- if (calculate) { |
- union { uint64_t i; unsigned char b[sizeof(uint64_t)]; } integer; |
- integer.i = *CI->getValue().getRawData(); |
- unsigned BitWidth = 64; // CI->getValue().getBitWidth(); |
- assert(BitWidth == 32 || BitWidth == 64); |
- HeapData *GlobalData = allocateAddress(name); |
- // assuming compiler is little endian |
- for (unsigned i = 0; i < BitWidth / 8; ++i) { |
- GlobalData->push_back(integer.b[i]); |
- } |
- } |
- } else if (isa<ConstantPointerNull>(CV)) { |
- assert(false && "Unlowered ConstantPointerNull"); |
- } else if (isa<ConstantAggregateZero>(CV)) { |
- if (calculate) { |
- unsigned Bytes = DL->getTypeStoreSize(CV->getType()); |
- HeapData *GlobalData = allocateAddress(name); |
- for (unsigned i = 0; i < Bytes; ++i) { |
- GlobalData->push_back(0); |
- } |
- // FIXME: create a zero section at the end, avoid filling meminit with zeros |
- } |
- } else if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) { |
- if (calculate) { |
- for (Constant::const_user_iterator UI = CV->user_begin(), UE = CV->user_end(); UI != UE; ++UI) { |
- if ((*UI)->getName() == "llvm.used") { |
- // export the kept-alives |
- for (unsigned i = 0; i < CA->getNumOperands(); i++) { |
- const Constant *C = CA->getOperand(i); |
- if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { |
- C = CE->getOperand(0); // ignore bitcasts |
- } |
- Exports.push_back(getJSName(C)); |
- } |
- } else if ((*UI)->getName() == "llvm.global.annotations") { |
- // llvm.global.annotations can be ignored. |
- } else { |
- llvm_unreachable("Unexpected constant array"); |
- } |
- break; // we assume one use here |
- } |
- } |
- } else if (const ConstantStruct *CS = dyn_cast<ConstantStruct>(CV)) { |
- if (name == "__init_array_start") { |
- // this is the global static initializer |
- if (calculate) { |
- unsigned Num = CS->getNumOperands(); |
- for (unsigned i = 0; i < Num; i++) { |
- const Value* C = CS->getOperand(i); |
- if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { |
- C = CE->getOperand(0); // ignore bitcasts |
- } |
- GlobalInitializers.push_back(getJSName(C)); |
- } |
- } |
- } else if (calculate) { |
- HeapData *GlobalData = allocateAddress(name); |
- unsigned Bytes = DL->getTypeStoreSize(CV->getType()); |
- for (unsigned i = 0; i < Bytes; ++i) { |
- GlobalData->push_back(0); |
- } |
- } else { |
- // Per the PNaCl abi, this must be a packed struct of a very specific type |
- // https://chromium.googlesource.com/native_client/pnacl-llvm/+/7287c45c13dc887cebe3db6abfa2f1080186bb97/lib/Transforms/NaCl/FlattenGlobals.cpp |
- assert(CS->getType()->isPacked()); |
- // This is the only constant where we cannot just emit everything during the first phase, 'calculate', as we may refer to other globals |
- unsigned Num = CS->getNumOperands(); |
- unsigned Offset = getRelativeGlobalAddress(name); |
- unsigned OffsetStart = Offset; |
- unsigned Absolute = getGlobalAddress(name); |
- for (unsigned i = 0; i < Num; i++) { |
- const Constant* C = CS->getOperand(i); |
- if (isa<ConstantAggregateZero>(C)) { |
- unsigned Bytes = DL->getTypeStoreSize(C->getType()); |
- Offset += Bytes; // zeros, so just skip |
- } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { |
- const Value *V = CE->getOperand(0); |
- unsigned Data = 0; |
- if (CE->getOpcode() == Instruction::PtrToInt) { |
- Data = getConstAsOffset(V, Absolute + Offset - OffsetStart); |
- } else if (CE->getOpcode() == Instruction::Add) { |
- V = cast<ConstantExpr>(V)->getOperand(0); |
- Data = getConstAsOffset(V, Absolute + Offset - OffsetStart); |
- ConstantInt *CI = cast<ConstantInt>(CE->getOperand(1)); |
- Data += *CI->getValue().getRawData(); |
- } else { |
- CE->dump(); |
- llvm_unreachable("Unexpected constant expr kind"); |
- } |
- union { unsigned i; unsigned char b[sizeof(unsigned)]; } integer; |
- integer.i = Data; |
- assert(Offset+4 <= GlobalData64.size()); |
- for (unsigned i = 0; i < 4; ++i) { |
- GlobalData64[Offset++] = integer.b[i]; |
- } |
- } else if (const ConstantDataSequential *CDS = dyn_cast<ConstantDataSequential>(C)) { |
- assert(CDS->isString()); |
- StringRef Str = CDS->getAsString(); |
- assert(Offset+Str.size() <= GlobalData64.size()); |
- for (unsigned int i = 0; i < Str.size(); i++) { |
- GlobalData64[Offset++] = Str.data()[i]; |
- } |
- } else { |
- C->dump(); |
- llvm_unreachable("Unexpected constant kind"); |
- } |
- } |
- } |
- } else if (isa<ConstantVector>(CV)) { |
- assert(false && "Unlowered ConstantVector"); |
- } else if (isa<BlockAddress>(CV)) { |
- assert(false && "Unlowered BlockAddress"); |
- } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { |
- if (name == "__init_array_start") { |
- // this is the global static initializer |
- if (calculate) { |
- const Value *V = CE->getOperand(0); |
- GlobalInitializers.push_back(getJSName(V)); |
- // is the func |
- } |
- } else if (name == "__fini_array_start") { |
- // nothing to do |
- } else { |
- // a global equal to a ptrtoint of some function, so a 32-bit integer for us |
- if (calculate) { |
- HeapData *GlobalData = allocateAddress(name); |
- for (unsigned i = 0; i < 4; ++i) { |
- GlobalData->push_back(0); |
- } |
- } else { |
- unsigned Data = 0; |
- |
- // Deconstruct lowered getelementptrs. |
- if (CE->getOpcode() == Instruction::Add) { |
- Data = cast<ConstantInt>(CE->getOperand(1))->getZExtValue(); |
- CE = cast<ConstantExpr>(CE->getOperand(0)); |
- } |
- const Value *V = CE; |
- if (CE->getOpcode() == Instruction::PtrToInt) { |
- V = CE->getOperand(0); |
- } |
- |
- // Deconstruct getelementptrs. |
- int64_t BaseOffset; |
- V = GetPointerBaseWithConstantOffset(V, BaseOffset, *DL); |
- Data += (uint64_t)BaseOffset; |
- |
- Data += getConstAsOffset(V, getGlobalAddress(name)); |
- union { unsigned i; unsigned char b[sizeof(unsigned)]; } integer; |
- integer.i = Data; |
- unsigned Offset = getRelativeGlobalAddress(name); |
- assert(Offset+4 <= GlobalData64.size()); |
- for (unsigned i = 0; i < 4; ++i) { |
- GlobalData64[Offset++] = integer.b[i]; |
- } |
- } |
- } |
- } else if (isa<UndefValue>(CV)) { |
- assert(false && "Unlowered UndefValue"); |
- } else { |
- CV->dump(); |
- assert(false && "Unsupported constant kind"); |
- } |
-} |
- |
-// nativization |
- |
-void JSWriter::calculateNativizedVars(const Function *F) { |
- NativizedVars.clear(); |
- |
- for (Function::const_iterator BI = F->begin(), BE = F->end(); BI != BE; ++BI) { |
- for (BasicBlock::const_iterator II = BI->begin(), E = BI->end(); II != E; ++II) { |
- const Instruction *I = &*II; |
- if (const AllocaInst *AI = dyn_cast<const AllocaInst>(I)) { |
- if (AI->getAllocatedType()->isVectorTy()) continue; // we do not nativize vectors, we rely on the LLVM optimizer to avoid load/stores on them |
- if (AI->getAllocatedType()->isAggregateType()) continue; // we do not nativize aggregates either |
- // this is on the stack. if its address is never used nor escaped, we can nativize it |
- bool Fail = false; |
- for (Instruction::const_user_iterator UI = I->user_begin(), UE = I->user_end(); UI != UE && !Fail; ++UI) { |
- const Instruction *U = dyn_cast<Instruction>(*UI); |
- if (!U) { Fail = true; break; } // not an instruction, not cool |
- switch (U->getOpcode()) { |
- case Instruction::Load: break; // load is cool |
- case Instruction::Store: { |
- if (U->getOperand(0) == I) Fail = true; // store *of* it is not cool; store *to* it is fine |
- break; |
- } |
- default: { Fail = true; break; } // anything that is "not" "cool", is "not cool" |
- } |
- } |
- if (!Fail) NativizedVars.insert(I); |
- } |
- } |
- } |
-} |
- |
-// special analyses |
- |
-bool JSWriter::canReloop(const Function *F) { |
- return true; |
-} |
- |
-// main entry |
- |
-void JSWriter::printCommaSeparated(const HeapData data) { |
- for (HeapData::const_iterator I = data.begin(); |
- I != data.end(); ++I) { |
- if (I != data.begin()) { |
- Out << ","; |
- } |
- Out << (int)*I; |
- } |
-} |
- |
-void JSWriter::printProgram(const std::string& fname, |
- const std::string& mName) { |
- printModule(fname,mName); |
-} |
- |
-void JSWriter::printModule(const std::string& fname, |
- const std::string& mName) { |
- printModuleBody(); |
-} |
- |
-bool JSWriter::runOnModule(Module &M) { |
- TheModule = &M; |
- DL = &M.getDataLayout(); |
- |
- setupCallHandlers(); |
- |
- printProgram("", ""); |
- |
- return false; |
-} |
- |
-char JSWriter::ID = 0; |
- |
-class CheckTriple : public ModulePass { |
-public: |
- static char ID; |
- CheckTriple() : ModulePass(ID) {} |
- virtual bool runOnModule(Module &M) { |
- if (M.getTargetTriple() != "asmjs-unknown-emscripten") { |
- prettyWarning() << "incorrect target triple '" << M.getTargetTriple() << "' (did you use emcc/em++ on all source files and not clang directly?)\n"; |
- } |
- return false; |
- } |
-}; |
- |
-char CheckTriple::ID; |
- |
-Pass *createCheckTriplePass() { |
- return new CheckTriple(); |
-} |
- |
-//===----------------------------------------------------------------------===// |
-// External Interface declaration |
-//===----------------------------------------------------------------------===// |
- |
-bool JSTargetMachine::addPassesToEmitFile(PassManagerBase &PM, |
- raw_pwrite_stream &o, |
- CodeGenFileType FileType, |
- bool DisableVerify, |
- AnalysisID StartAfter, |
- AnalysisID StopAfter) { |
- assert(FileType == TargetMachine::CGFT_AssemblyFile); |
- |
- PM.add(createCheckTriplePass()); |
- PM.add(createExpandInsertExtractElementPass()); |
- PM.add(createExpandI64Pass()); |
- |
- CodeGenOpt::Level OptLevel = getOptLevel(); |
- |
- // When optimizing, there shouldn't be any opportunities for SimplifyAllocas |
- // because the regular optimizer should have taken them all (GVN, and possibly |
- // also SROA). |
- if (OptLevel == CodeGenOpt::None) |
- PM.add(createEmscriptenSimplifyAllocasPass()); |
- |
- PM.add(new JSWriter(o, OptLevel)); |
- |
- return false; |
-} |