diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-12-27 19:57:19 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-12-27 19:57:19 -0800 |
commit | 81afe65c6fa734101c96dc99ec10b1aed3f43602 (patch) | |
tree | 7af4be660811c17f77cefe25c9be2b7df36df980 /lib/Target/CppBackend/CPPBackend.cpp | |
parent | 9a21bed266588de64040a9ec3b3dafa912eb4c6a (diff) |
CPPBackend => JSBackend
Diffstat (limited to 'lib/Target/CppBackend/CPPBackend.cpp')
-rw-r--r-- | lib/Target/CppBackend/CPPBackend.cpp | 1738 |
1 files changed, 0 insertions, 1738 deletions
diff --git a/lib/Target/CppBackend/CPPBackend.cpp b/lib/Target/CppBackend/CPPBackend.cpp deleted file mode 100644 index 1253d8a622..0000000000 --- a/lib/Target/CppBackend/CPPBackend.cpp +++ /dev/null @@ -1,1738 +0,0 @@ -//===-- CPPBackend.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 "CPPTargetMachine.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Config/config.h" -#include "llvm/IR/CallingConv.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/Module.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/Pass.h" -#include "llvm/PassManager.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/DebugInfo.h" -#include <algorithm> -#include <cstdio> -#include <map> -#include <set> // TODO: unordered_set? -using namespace llvm; - -#include <OptPasses.h> -#include <Relooper.h> - -#define dump(x) fprintf(stderr, x "\n") -#define dumpv(x, ...) fprintf(stderr, x "\n", __VA_ARGS__) -#define dumpfail(x) { fprintf(stderr, x "\n"); fprintf(stderr, "%s : %d\n", __FILE__, __LINE__); report_fatal_error("fail"); } -#define dumpfailv(x, ...) { fprintf(stderr, x "\n", __VA_ARGS__); fprintf(stderr, "%s : %d\n", __FILE__, __LINE__); report_fatal_error("fail"); } -#define dumpIR(value) { \ - std::string temp; \ - raw_string_ostream stream(temp); \ - stream << *(value); \ - fprintf(stderr, "%s\n", temp.c_str()); \ -} -#undef assert -#define assert(x) { if (!(x)) dumpfail(#x); } - -extern "C" void LLVMInitializeCppBackendTarget() { - // Register the target. - RegisterTargetMachine<CPPTargetMachine> X(TheCppBackendTarget); -} - -namespace { - enum AsmCast { - ASM_SIGNED = 0, - ASM_UNSIGNED = 1, - ASM_NONSPECIFIC = 2 // nonspecific means to not differentiate ints. |0 for all, regardless of size and sign - }; - - 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::TypeID> 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; - - /// JSWriter - This class is the main chunk of code that converts an LLVM - /// module to JavaScript. - class JSWriter : public ModulePass { - formatted_raw_ostream &Out; - const Module *TheModule; - unsigned UniqueNum; - ValueMap ValueNames; - NameSet UsedNames; - VarMap UsedVars; - 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; - std::map<std::string, unsigned> IndexedFunctions; // name -> index - FunctionTableMap FunctionTables; // sig => list of functions - std::vector<std::string> GlobalInitializers; - - #include "CallHandlers.h" - - public: - static char ID; - explicit JSWriter(formatted_raw_ostream &o) : - ModulePass(ID), Out(o), UniqueNum(0) {} - - virtual const char *getPassName() const { return "JavaScript backend"; } - - bool runOnModule(Module &M); - - void printProgram(const std::string& fname, const std::string& modName ); - void printModule(const std::string& fname, const std::string& modName ); - void printContents(const std::string& fname, const std::string& modName ); - void printFunction(const std::string& fname, const std::string& funcName ); - void printFunctions(); - void printInline(const std::string& fname, const std::string& funcName ); - void printVariable(const std::string& fname, const std::string& varName ); - - void error(const std::string& msg); - - formatted_raw_ostream& nl(formatted_raw_ostream &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 - - unsigned memAlign(unsigned x) { - return x + (x%MEM_ALIGN != 0 ? MEM_ALIGN - x%MEM_ALIGN : 0); - } - - 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: assert(false); - } - while (GlobalData->size() % (Bits/8) != 0) GlobalData->push_back(0); - GlobalAddresses[Name] = Address(GlobalData->size(), Bits); - return GlobalData; - } - - #define GLOBAL_BASE 8 - - // return the absolute offset of a global - unsigned getGlobalAddress(const std::string &s) { - if (GlobalAddresses.find(s) == GlobalAddresses.end()) dumpfailv("cannot find global address %s", s.c_str()); - Address a = GlobalAddresses[s]; - assert(a.second == 64); // FIXME when we use optimal alignments - switch (a.second) { - case 64: - assert((a.first + GLOBAL_BASE)%8 == 0); - return a.first + GLOBAL_BASE; - case 32: - assert((a.first + GLOBAL_BASE)%4 == 0); - return a.first + GLOBAL_BASE + GlobalData64.size(); - case 8: - return a.first + GLOBAL_BASE + GlobalData64.size() + GlobalData32.size(); - default: - dumpfailv("bad global address %s %d %d\n", s.c_str(), a.first, a.second); - } - } - // returns the internal offset inside the proper block: GlobalData8, 32, 64 - unsigned getRelativeGlobalAddress(const std::string &s) { - if (GlobalAddresses.find(s) == GlobalAddresses.end()) dumpfailv("cannot find global address %s", s.c_str()); - Address a = GlobalAddresses[s]; - return a.first; - } - char getFunctionSignatureLetter(Type *T) { - if (T->isVoidTy()) return 'v'; - else if (T->isFloatTy() || T->isDoubleTy()) return 'd'; // TODO: float - else return 'i'; - } - std::string getFunctionSignature(const FunctionType *F) { - 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; - } - unsigned getFunctionIndex(const Function *F) { - std::string Name = getCppName(F); - if (IndexedFunctions.find(Name) != IndexedFunctions.end()) return IndexedFunctions[Name]; - std::string Sig = getFunctionSignature(F->getFunctionType()); - FunctionTable &Table = FunctionTables[Sig]; - // use alignment info to avoid unnecessary holes. This is not optimal though, - // (1) depends on order of appearance, and (2) really just need align for &class::method, see test_polymorph - unsigned Alignment = F->getAlignment() || 1; - while (Table.size() == 0 || Table.size() % Alignment) Table.push_back("0"); - unsigned Index = Table.size(); - Table.push_back(Name); - IndexedFunctions[Name] = Index; - - // invoke the callHandler for this, if there is one. the function may only be indexed but never called directly, we need to catch it in the handler - CallHandlerMap::iterator CH = CallHandlers->find(Name); - if (CH != CallHandlers->end()) { - (this->*(CH->second))(NULL, Name, -1); - } - - return Index; - } - void ensureFunctionTable(const FunctionType *F) { - FunctionTables[getFunctionSignature(F)]; - } - // 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) { - if (const Function *F = dyn_cast<const Function>(V)) { - return getFunctionIndex(F); - } else { - if (const GlobalValue *GV = dyn_cast<GlobalValue>(V)) { - if (GV->hasExternalLinkage()) { - // 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()); - } - } - std::string getPtrAsStr(const Value* Ptr) { - if (const Function *F = dyn_cast<Function>(Ptr)) { - return utostr(getFunctionIndex(F)); - } else if (const Constant *CV = dyn_cast<Constant>(Ptr)) { - if (const GlobalValue *GV = dyn_cast<GlobalValue>(Ptr)) { - if (GV->isDeclaration()) { - std::string Name = getOpName(Ptr); - Externals.insert(Name); - return Name; - } - } - return utostr(getGlobalAddress(CV->getName().str())); - } else { - return getOpName(Ptr); - } - } - - std::string getPtrLoad(const Value* Ptr); - std::string getPtrUse(const Value* Ptr); - std::string getConstant(const Constant*, AsmCast sign=ASM_SIGNED); - 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); - - std::string getCppName(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 getAssign(const StringRef &, const Type *); - std::string getCast(const StringRef &, const Type *, AsmCast sign=ASM_SIGNED); - std::string getParenCast(const StringRef &, const Type *, AsmCast sign=ASM_SIGNED); - std::string getDoubleToInt(const StringRef &); - std::string getIMul(const Value *, const Value *); - std::string getLoad(const std::string& Assign, const Value *P, const Type *T, unsigned Alignment, char sep=';'); - std::string getStore(const Value *P, const Type *T, const std::string& VS, unsigned Alignment, char sep=';'); - - void printFunctionBody(const Function *F); - void generateInstruction(const Instruction *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. - -formatted_raw_ostream &JSWriter::nl(formatted_raw_ostream &Out, int delta) { - Out << '\n'; - return Out; -} - -static inline void sanitize(std::string& str) { - for (size_t i = 1; i < str.length(); ++i) - if (!isalnum(str[i]) && str[i] != '_' && str[i] != '$') - str[i] = '_'; -} - -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 - typedef std::map<std::string, std::string> StringMap; - StringMap assigns; // variable -> assign statement - std::map<std::string, 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 - std::string name = getCppName(P); - assigns[name] = getAssign(name, P->getType()); - Value *V = P->getIncomingValue(index); - values[name] = V; - std::string vname = getValueAsStr(V); - if (const Instruction *VI = dyn_cast<const Instruction>(V)) { - if (VI->getParent() == To) { - 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; - 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::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 += getAssign(temp, V->getType()) + CV + ';'; - CV = temp; - deps.erase(curr); - undeps.erase(depString); - } - post += assigns[curr] + CV + ';'; - assigns.erase(last); - emitted = true; - } - } - } - return pre + post; -} - -std::string JSWriter::getCppName(const Value* val) { - std::string name; - ValueMap::iterator I = ValueNames.find(val); - if (I != ValueNames.end() && I->first == val) - return I->second; - - if (val->hasName()) { - if (isa<Function>(val) || isa<Constant>(val)) { - name = std::string("_") + val->getName().str(); - } else { - name = std::string("$") + val->getName().str(); - } - sanitize(name); - } else { - name = "unique$" + utostr(UniqueNum++); - } - return ValueNames[val] = name; -} - -std::string JSWriter::getAssign(const StringRef &s, const Type *t) { - UsedVars[s] = t->getTypeID(); - return (s + " = ").str(); -} - -std::string JSWriter::getCast(const StringRef &s, const Type *t, AsmCast sign) { - switch (t->getTypeID()) { - default: assert(false && "Unsupported type"); - case Type::FloatTyID: // TODO return ("Math_fround(" + s + ")").str(); - 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 (s + "&1").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: assert(0); - } - } - case Type::PointerTyID: return (s + "|0").str(); - } -} - -std::string JSWriter::getParenCast(const StringRef &s, const 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 std::string& Assign, const Value *P, const Type *T, unsigned Alignment, char sep) { - unsigned Bytes = T->getPrimitiveSizeInBits()/8; - std::string text; - if (Bytes <= Alignment || Alignment == 0) { - text = Assign + getPtrLoad(P); - if (Alignment == 536870912) text += "; abort() /* segfault */"; - } else { - // unaligned in some manner - std::string PS = getOpName(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]=HEAP8[" + PS + "]" + sep + - "HEAP8[tempDoublePtr+1]=HEAP8[" + PS + "+1|0]" + sep + - "HEAP8[tempDoublePtr+2]=HEAP8[" + PS + "+2|0]" + sep + - "HEAP8[tempDoublePtr+3]=HEAP8[" + PS + "+3|0]" + sep + - "HEAP8[tempDoublePtr+4]=HEAP8[" + PS + "+4|0]" + sep + - "HEAP8[tempDoublePtr+5]=HEAP8[" + PS + "+5|0]" + sep + - "HEAP8[tempDoublePtr+6]=HEAP8[" + PS + "+6|0]" + sep + - "HEAP8[tempDoublePtr+7]=HEAP8[" + PS + "+7|0]"; - break; - } - default: assert(0 && "bad 8 store"); - } - text += sep + Assign + "+HEAPF64[tempDoublePtr>>3]"; - break; - } - case 4: { - if (T->isIntegerTy()) { - switch (Alignment) { - case 2: { - text = Assign + "HEAPU16[" + PS + ">>1]|" + - "(HEAPU16[" + PS + "+2>>1]<<16)"; - break; - } - case 1: { - text = Assign + "HEAPU8[" + PS + "]|" + - "(HEAPU8[" + PS + "+1|0]<<8)|" + - "(HEAPU8[" + PS + "+2|0]<<16)|" + - "(HEAPU8[" + PS + "+3|0]<<24)"; - break; - } - default: assert(0 && "bad 4i store"); - } - } else { // float - 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]=HEAP8[" + PS + "]" + 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 += Assign + "+HEAPF32[tempDoublePtr>>2]"; - } - break; - } - case 2: { - text = Assign + "HEAPU8[" + PS + "]|" + - "(HEAPU8[" + PS + "+1|0]<<8)"; - break; - } - default: assert(0 && "bad store"); - } - } - return text; -} - -std::string JSWriter::getStore(const Value *P, const Type *T, const std::string& VS, unsigned Alignment, char sep) { - assert(sep == ';'); // FIXME when we need that - unsigned Bytes = T->getPrimitiveSizeInBits()/8; - std::string text; - if (Bytes <= Alignment || Alignment == 0) { - text = getPtrUse(P) + " = " + VS; - if (Alignment == 536870912) text += "; abort() /* segfault */"; - } else { - // unaligned in some manner - std::string PS = getOpName(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 + "]=HEAP8[tempDoublePtr];" + - "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()) { - switch (Alignment) { - case 2: { - text = "HEAP16[" + PS + ">>1]=" + VS + "&65535;" + - "HEAP16[" + PS + "+2>>1]=" + VS + ">>>16"; - break; - } - case 1: { - text = "HEAP8[" + PS + "]=" + 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 - 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 + "]=HEAP8[tempDoublePtr];" + - "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 + "]=" + VS + "&255;" + - "HEAP8[" + PS + "+1|0]=" + VS + ">>8"; - break; - } - default: assert(0 && "bad store"); - } - } - return text; -} - -std::string JSWriter::getOpName(const Value* V) { // TODO: remove this - return getCppName(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::getPtrUse(const Value* Ptr) { - Type *t = cast<PointerType>(Ptr->getType())->getElementType(); - unsigned Bytes = t->getPrimitiveSizeInBits()/8; - if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) { - std::string text = ""; - unsigned Addr = getGlobalAddress(GV->getName().str()); - switch (Bytes) { - default: assert(false && "Unsupported type"); - case 8: return "HEAPF64[" + utostr(Addr >> 3) + "]"; - case 4: { - if (t->isIntegerTy()) { - return "HEAP32[" + utostr(Addr >> 2) + "]"; - } else { - return "HEAPF32[" + utostr(Addr >> 2) + "]"; - } - } - case 2: return "HEAP16[" + utostr(Addr >> 1) + "]"; - case 1: return "HEAP8[" + utostr(Addr) + "]"; - } - } else { - std::string Name = getOpName(Ptr); - switch (Bytes) { - default: assert(false && "Unsupported type"); - case 8: return "HEAPF64[" + Name + ">>3]"; - case 4: { - if (t->isIntegerTy()) { - return "HEAP32[" + Name + ">>2]"; - } else { - return "HEAPF32[" + Name + ">>2]"; - } - } - case 2: return "HEAP16[" + Name + ">>1]"; - case 1: return "HEAP8[" + Name + "]"; - } - } -} - -static int hexToInt(char x) { - if (x <= '9') { - assert(x >= '0'); - return x - '0'; - } else { - assert('A' <= x && x <= 'F'); - return x - 'A' + 10; - } -} - -/* static inline std::string ftostr(const APFloat& V) { - std::string Buf; - if (&V.getSemantics() == &APFloat::IEEEdouble) { - raw_string_ostream(Buf) << V.convertToDouble(); - return Buf; - } else if (&V.getSemantics() == &APFloat::IEEEsingle) { - raw_string_ostream(Buf) << (double)V.convertToFloat(); - return Buf; - } - return "<unknown format in ftostr>"; // error -} */ - -static inline std::string ftostr_exact(const ConstantFP *CFP) { - std::string temp; - raw_string_ostream stream(temp); - stream << *CFP; // bitcast on APF produces odd results, so do it this horrible way - const char *raw = temp.c_str(); - if (CFP->getType()->isFloatTy()) { - raw += 6; // skip "float " - } else { - raw += 7; // skip "double " - } - if (raw[1] != 'x') return raw; // number has already been printed out - raw += 2; // skip "0x" - unsigned len = strlen(raw); - assert((len&1) == 0); // must be complete bytes, so an even number of chars - unsigned missing = 8 - len/2; - union dbl { double d; unsigned char b[sizeof(double)]; } dbl; - dbl.d = 0; - for (unsigned i = 0; i < 8 - missing; i++) { - dbl.b[7-i] = (hexToInt(raw[2*i]) << 4) | - hexToInt(raw[2*i+1]); - } - char buffer[101]; - snprintf(buffer, 100, "%.30g", dbl.d); - return buffer; -} - -std::string JSWriter::getConstant(const Constant* CV, AsmCast sign) { - if (isa<PointerType>(CV->getType())) { - return getPtrAsStr(CV); - } else { - if (const ConstantFP *CFP = dyn_cast<ConstantFP>(CV)) { - std::string S = ftostr_exact(CFP); - S = '+' + S; - //if (S.find('.') == S.npos) { TODO: do this when necessary, but it is necessary even for 0.0001 - return S; - } else if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { - if (sign == ASM_SIGNED && CI->getValue().getBitWidth() == 1) sign = ASM_UNSIGNED; // booleans cannot be signed in a meaningful way - return CI->getValue().toString(10, sign != ASM_UNSIGNED); - } else if (isa<UndefValue>(CV)) { - return CV->getType()->isIntegerTy() ? "0" : "+0"; // XXX fround, refactor this - } else { - dumpIR(CV); - assert(false); - } - } -} - -std::string JSWriter::getValueAsStr(const Value* V, AsmCast sign) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV, sign); - } else { - return getCppName(V); - } -} - -std::string JSWriter::getValueAsCastStr(const Value* V, AsmCast sign) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV, sign); - } else { - return getCast(getCppName(V), V->getType(), sign); - } -} - -std::string JSWriter::getValueAsParenStr(const Value* V) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV); - } else { - return "(" + getCppName(V) + ")"; - } -} - -std::string JSWriter::getValueAsCastParenStr(const Value* V, AsmCast sign) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV, sign); - } else { - return "(" + getCast(getCppName(V), V->getType(), sign) + ")"; - } -} - -// generateInstruction - This member is called for each Instruction in a function. -void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Code) { - std::string iName(getCppName(I)); - - Type *T = I->getType(); - if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { - dumpIR(I); - assert(0 && "FIXME: finish legalization"); // FIXME - } - - switch (I->getOpcode()) { - default: - error("Invalid instruction"); - break; - - case Instruction::Ret: { - const ReturnInst* ret = cast<ReturnInst>(I); - Value *RV = ret->getReturnValue(); - Code << "STACKTOP = sp;"; - Code << "return"; - if (RV == NULL) { - Code << ";"; - } else { - Code << " " + getValueAsCastStr(RV, ASM_NONSPECIFIC) + ";"; - } - break; - } - case Instruction::Br: - case Instruction::Switch: break; // handled while relooping - case Instruction::Unreachable: { - // No need to emit anything, as there should be an abort right before these - // Code << "abort();"; - 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 << getAssign(iName, I->getType()); - unsigned opcode = I->getOpcode(); - 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 << getValueAsStr(I->getOperand(0)) + " + " + getValueAsStr(I->getOperand(1)); break; // TODO: ensurefloat here - case Instruction::FSub: Code << getValueAsStr(I->getOperand(0)) + " - " + getValueAsStr(I->getOperand(1)); break; - case Instruction::FMul: Code << getValueAsStr(I->getOperand(0)) + " * " + getValueAsStr(I->getOperand(1)); break; - case Instruction::FDiv: Code << getValueAsStr(I->getOperand(0)) + " / " + getValueAsStr(I->getOperand(1)); break; - case Instruction::FRem: Code << getValueAsStr(I->getOperand(0)) + " % " + getValueAsStr(I->getOperand(1)); break; - default: error("bad icmp"); break; - } - Code << ';'; - break; - } - case Instruction::FCmp: { - Code << getAssign(iName, I->getType()); - switch (cast<FCmpInst>(I)->getPredicate()) { - case FCmpInst::FCMP_OEQ: - case FCmpInst::FCMP_UEQ: Code << getValueAsStr(I->getOperand(0)) + " == " + getValueAsStr(I->getOperand(1)); break; - case FCmpInst::FCMP_ONE: - case FCmpInst::FCMP_UNE: Code << getValueAsStr(I->getOperand(0)) + " != " + getValueAsStr(I->getOperand(1)); break; - case FCmpInst::FCMP_OGT: - case FCmpInst::FCMP_UGT: Code << getValueAsStr(I->getOperand(0)) + " > " + getValueAsStr(I->getOperand(1)); break; - case FCmpInst::FCMP_OGE: - case FCmpInst::FCMP_UGE: Code << getValueAsStr(I->getOperand(0)) + " >= " + getValueAsStr(I->getOperand(1)); break; - case FCmpInst::FCMP_OLT: - case FCmpInst::FCMP_ULT: Code << getValueAsStr(I->getOperand(0)) + " < " + getValueAsStr(I->getOperand(1)); break; - case FCmpInst::FCMP_OLE: - case FCmpInst::FCMP_ULE: Code << getValueAsStr(I->getOperand(0)) + " <= " + getValueAsStr(I->getOperand(1)); break; - 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; - case FCmpInst::FCMP_FALSE: Code << "0"; break; - case FCmpInst::FCMP_TRUE : Code << "1"; break; - default: error("bad fcmp"); break; - } - Code << ";"; - break; - } - case Instruction::ICmp: { - unsigned predicate = cast<ICmpInst>(I)->getPredicate(); - AsmCast sign = (predicate == ICmpInst::ICMP_ULE || - predicate == ICmpInst::ICMP_UGE || - predicate == ICmpInst::ICMP_ULT || - predicate == ICmpInst::ICMP_UGT) ? ASM_UNSIGNED : ASM_SIGNED; - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) + "(" + - 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: assert(0); - } - Code << "(" + - getValueAsCastStr(I->getOperand(1), sign) + - ");"; - break; - } - case Instruction::Alloca: { - if (NativizedVars.count(I)) { - // nativized stack variable, we just need a 'var' definition - UsedVars[iName] = cast<PointerType>(I->getType())->getElementType()->getTypeID(); - break; - } - const AllocaInst* AI = cast<AllocaInst>(I); - Type *T = AI->getAllocatedType(); - assert(!isa<ArrayType>(T)); - const Value *AS = AI->getArraySize(); - unsigned BaseSize = T->getScalarSizeInBits()/8; - std::string Size; - if (const ConstantInt *CI = dyn_cast<ConstantInt>(AS)) { - Size = Twine(memAlign(BaseSize * CI->getZExtValue())).str(); - } else { - Size = "((" + utostr(BaseSize) + '*' + getValueAsStr(AS) + ")|0)"; - } - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) + "STACKTOP; STACKTOP = STACKTOP + " + Size + "|0;"; - break; - } - case Instruction::Load: { - const LoadInst *LI = cast<LoadInst>(I); - const Value *P = LI->getPointerOperand(); - unsigned Alignment = LI->getAlignment(); - std::string Assign = getAssign(iName, LI->getType()); - if (NativizedVars.count(P)) { - Code << Assign + getValueAsStr(P) + ';'; - } else { - Code << getLoad(Assign, 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(P, V->getType(), VS, Alignment) + ';'; - } - - Type *T = V->getType(); - if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { - assert(0 && "FIXME: finish legalization"); // FIXME - } - break; - } - case Instruction::GetElementPtr: { - assert(false && "Unhandled instruction"); - break; - } - case Instruction::PHI: { - // handled separately - we push them back into the relooper branchings - break; - } - case Instruction::PtrToInt: - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) + getPtrAsStr(I->getOperand(0)) + ';'; - break; - case Instruction::IntToPtr: - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) + 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 << getAssign(iName, I->getType()); - switch (I->getOpcode()) { - case Instruction::Trunc: { - //unsigned inBits = V->getType()->getIntegerBitWidth(); - unsigned outBits = I->getType()->getIntegerBitWidth(); - Code << getValueAsStr(I->getOperand(0)) + "&" + utostr(pow(2, outBits)-1); - 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: Code << getValueAsStr(I->getOperand(0)); break; // TODO: fround - case Instruction::FPTrunc: Code << getValueAsStr(I->getOperand(0)); break; // TODO: fround - 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"); - } - Code << ";"; - break; - } - case Instruction::BitCast: { - Code << getAssign(iName, I->getType()); - // 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 + "," + "+HEAPF32[tempDoublePtr>>2]);"; - } 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); - Code << handleCall(CI) + ';'; - break; - } - case Instruction::Select: { - const SelectInst* SI = cast<SelectInst>(I); - Code << getAssign(iName, I->getType()) + getValueAsStr(SI->getCondition()) + " ? " + - getValueAsStr(SI->getTrueValue()) + " : " + - getValueAsStr(SI->getFalseValue()) + ';'; - break; - } - case Instruction::AtomicCmpXchg: { - std::string Assign = getAssign(iName, I->getType()); - const Value *P = I->getOperand(0); - Code << getLoad(Assign, P, I->getType(), 0) + ';' + - "if ((" + getCast(iName, I->getType()) + ") == " + getValueAsCastParenStr(I->getOperand(1)) + ") " + - getStore(P, I->getType(), getValueAsStr(I->getOperand(2)), 0) + ";"; - break; - } - case Instruction::AtomicRMW: { - const AtomicRMWInst *rmwi = cast<AtomicRMWInst>(I); - const Value *P = rmwi->getOperand(0); - const Value *V = rmwi->getOperand(1); - std::string Assign = getAssign(iName, I->getType()); - std::string VS = getValueAsStr(V); - Code << getLoad(Assign, 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(P, I->getType(), VS, 0); break; - case AtomicRMWInst::Add: Code << getStore(P, I->getType(), "((" + VS + '+' + iName + ")|0)", 0); break; - case AtomicRMWInst::Sub: Code << getStore(P, I->getType(), "((" + VS + '-' + iName + ")|0)", 0); break; - case AtomicRMWInst::And: Code << getStore(P, I->getType(), "(" + VS + '&' + iName + ")", 0); break; - case AtomicRMWInst::Nand: Code << getStore(P, I->getType(), "(~(" + VS + '&' + iName + "))", 0); break; - case AtomicRMWInst::Or: Code << getStore(P, I->getType(), "(" + VS + '|' + iName + ")", 0); break; - case AtomicRMWInst::Xor: Code << getStore(P, I->getType(), "(" + VS + '^' + iName + ")", 0); break; - case AtomicRMWInst::Max: - case AtomicRMWInst::Min: - case AtomicRMWInst::UMax: - case AtomicRMWInst::UMin: - case AtomicRMWInst::BAD_BINOP: llvm_unreachable("Bad atomic operation"); - } - Code << ";"; - break; - } - } - // append debug info - if (MDNode *N = I->getMetadata("dbg")) { - DILocation Loc(N); - unsigned Line = Loc.getLineNumber(); - StringRef File = Loc.getFilename(); - Code << " //@line " + utostr(Line) + " \"" + File.str() + "\""; - } -} - -static const SwitchInst *considerSwitch(const Instruction *I) { - const SwitchInst *SI = dyn_cast<SwitchInst>(I); - if (!SI) return NULL; - // use a switch if the range is not too big or sparse - int Minn = INT_MAX, Maxx = INT_MIN, Num = 0; - for (SwitchInst::ConstCaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { - const IntegersSubset CaseVal = i.getCaseValueEx(); - assert(CaseVal.isSingleNumbersOnly()); - std::string Condition = ""; - for (unsigned Index = 0; Index < CaseVal.getNumItems(); Index++) { - int Curr = CaseVal.getSingleNumber(Index).toConstantInt()->getZExtValue(); - if (Curr < Minn) Minn = Curr; - if (Curr > Maxx) Maxx = Curr; - } - Num++; - } - int Range = Maxx - Minn; - return Num < 5 || Range > 10*1024 || (Range/Num) > 1024 ? NULL : SI; // heuristics -} - -void JSWriter::printFunctionBody(const Function *F) { - assert(!F->isDeclaration()); - - // Prepare relooper TODO: resize buffer as needed - #define RELOOPER_BUFFER 10*1024*1024 - static char *buffer = new char[RELOOPER_BUFFER]; - Relooper::SetOutputBuffer(buffer, RELOOPER_BUFFER); - Relooper R; - //if (!canReloop(F)) R.SetEmulate(true); - R.SetAsmJSMode(1); - Block *Entry = NULL; - std::map<const BasicBlock*, Block*> LLVMToRelooper; - - // Create relooper blocks with their contents - for (Function::const_iterator BI = F->begin(), BE = F->end(); - BI != BE; ++BI) { - std::string Code; - raw_string_ostream CodeStream(Code); - for (BasicBlock::const_iterator I = BI->begin(), E = BI->end(); - I != E; ++I) { - generateInstruction(I, CodeStream); - CodeStream << '\n'; - } - CodeStream.flush(); - const SwitchInst* SI = considerSwitch(BI->getTerminator()); - Block *Curr = new Block(Code.c_str(), SI ? getValueAsCastStr(SI->getCondition()).c_str() : NULL); - const BasicBlock *BB = &*BI; - LLVMToRelooper[BB] = Curr; - R.AddBlock(Curr); - if (!Entry) Entry = Curr; - } - - // Create branchings - for (Function::const_iterator BI = F->begin(), BE = F->end(); - BI != BE; ++BI) { - const TerminatorInst *TI = BI->getTerminator(); - switch (TI->getOpcode()) { - default: { - dumpfailv("invalid branch instr %s\n", 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::Switch: { - const SwitchInst* SI = cast<SwitchInst>(TI); - bool UseSwitch = !!considerSwitch(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(); - const IntegersSubset CaseVal = i.getCaseValueEx(); - assert(CaseVal.isSingleNumbersOnly()); - std::string Condition = ""; - for (unsigned Index = 0; Index < CaseVal.getNumItems(); Index++) { - std::string Curr = CaseVal.getSingleNumber(Index).toConstantInt()->getValue().toString(10, true); - if (UseSwitch) { - Condition += "case " + Curr + ": "; - } else { - if (Condition.size() > 0) Condition += " | "; - Condition += "(" + getValueAsCastParenStr(SI->getCondition()) + " == " + Curr + ")"; - } - } - BlocksToConditions[BB] = Condition + (!UseSwitch && BlocksToConditions[BB].size() > 0 ? " | " : "") + BlocksToConditions[BB]; - } - for (BlockCondMap::iterator I = BlocksToConditions.begin(), E = BlocksToConditions.end(); I != E; ++I) { - const BasicBlock *BB = I->first; - 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())->getTypeID(); - UsedVars["label"] = Type::getInt32Ty(F->getContext())->getTypeID(); - if (!UsedVars.empty()) { - unsigned Count = 0; - for (VarMap::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) { - default: - assert(false); - case Type::PointerTyID: - case Type::IntegerTyID: - Out << "0"; - break; - case Type::FloatTyID: - // TODO Out << "Math_fround(0)"; - case Type::DoubleTyID: - Out << "+0"; // FIXME - break; - } - } - Out << ";"; - nl(Out); - } - - // Emit stack entry - Out << " " + getAssign("sp", Type::getInt32Ty(F->getContext())) + "STACKTOP;"; - - // Emit (relooped) code - 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 " + getCast("0", 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::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()) { - // Ensure all arguments and locals are named (we assume used values need names, which might be false if the optimizer did not run) - unsigned Next = 1; - for (Function::const_arg_iterator AI = I->arg_begin(), AE = I->arg_end(); - AI != AE; ++AI) { - if (!AI->hasName() && AI->hasNUsesOrMore(1)) { - ValueNames[AI] = "$" + utostr(Next++); - } - } - for (Function::const_iterator BI = I->begin(), BE = I->end(); - BI != BE; ++BI) { - for (BasicBlock::const_iterator II = BI->begin(), E = BI->end(); - II != E; ++II) { - if (!II->hasName() && II->hasNUsesOrMore(1)) { - ValueNames[II] = "$" + utostr(Next++); - } - } - } - - // Prepare and analyze function - - UsedVars.clear(); - UniqueNum = 0; - calculateNativizedVars(I); - - // Emit the function - - Out << "function _" << I->getName() << "("; - for (Function::const_arg_iterator AI = I->arg_begin(), AE = I->arg_end(); - AI != AE; ++AI) { - if (AI != I->arg_begin()) Out << ","; - Out << getCppName(AI); - } - Out << ") {"; - nl(Out); - for (Function::const_arg_iterator AI = I->arg_begin(), AE = I->arg_end(); - AI != AE; ++AI) { - std::string name = getCppName(AI); - Out << " " << name << " = " << getCast(name, AI->getType(), ASM_NONSPECIFIC) << ";"; - nl(Out); - } - printFunctionBody(I); - Out << "}"; - nl(Out); - } - } - 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()) { - if (first) { - first = false; - } else { - Out << ", "; - } - Out << "\"" + I->getName() + "\""; - } - } - for (NameSet::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::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::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 << ", "; - } - Out << "\"_" << I->getName() << '"'; - } - } - 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 << "\n}\n"; -} - -void JSWriter::parseConstant(const std::string& name, const Constant* CV, bool calculate) { - if (isa<GlobalValue>(CV)) - return; - //dumpv("parsing constant %s\n", name.c_str()); - // 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); - } - } 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); - } else if (isa<ConstantAggregateZero>(CV)) { - if (calculate) { - DataLayout DL(TheModule); - unsigned Bytes = DL.getTypeStoreSize(CV->getType()); - // FIXME: assume full 64-bit alignment for now - Bytes = memAlign(Bytes); - 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 (isa<ConstantArray>(CV)) { - assert(false); - } 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(getCppName(C)); - } - } - } else if (calculate) { - HeapData *GlobalData = allocateAddress(name); - DataLayout DL(TheModule); - 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)) { - DataLayout DL(TheModule); - unsigned Bytes = DL.getTypeStoreSize(C->getType()); - Offset += Bytes; // zeros, so just skip - } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) { - 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 = dyn_cast<ConstantExpr>(V)->getOperand(0); - Data = getConstAsOffset(V, Absolute + Offset - OffsetStart); - ConstantInt *CI = dyn_cast<ConstantInt>(CE->getOperand(1)); - Data += *CI->getValue().getRawData(); - } else { - dumpIR(CE); - assert(0); - } - 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 { - dumpIR(C); - assert(0); - } - } - } - } else if (isa<ConstantVector>(CV)) { - assert(false); - } else if (isa<BlockAddress>(CV)) { - assert(false); - } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { - if (name == "__init_array_start") { - // this is the global static initializer - if (calculate) { - Value *V = CE->getOperand(0); - GlobalInitializers.push_back(getCppName(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; - if (CE->getOpcode() == Instruction::Add) { - Data = cast<ConstantInt>(CE->getOperand(1))->getZExtValue(); - CE = dyn_cast<ConstantExpr>(CE->getOperand(0)); - } - assert(CE->isCast()); - Value *V = CE->getOperand(0); - 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); - } else { - assert(false); - } -} - -// 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 (I->getOpcode() == Instruction::Alloca) { - // this is on the stack. if its address is never used nor escaped, we can nativize it - bool Fail = false; - for (Instruction::const_use_iterator UI = I->use_begin(), UE = I->use_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; - - setupCallHandlers(); - - printProgram("", ""); - - return false; -} - -char JSWriter::ID = 0; - -//===----------------------------------------------------------------------===// -// External Interface declaration -//===----------------------------------------------------------------------===// - -bool CPPTargetMachine::addPassesToEmitFile(PassManagerBase &PM, - formatted_raw_ostream &o, - CodeGenFileType FileType, - bool DisableVerify, - AnalysisID StartAfter, - AnalysisID StopAfter) { - assert(FileType == TargetMachine::CGFT_AssemblyFile); - - PM.add(createSimplifyAllocasPass()); - PM.add(new JSWriter(o)); - - return false; -} - |