aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/JSBackend/JSBackend.cpp
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-12-27 19:57:19 -0800
committerAlon Zakai <alonzakai@gmail.com>2013-12-27 19:57:19 -0800
commit81afe65c6fa734101c96dc99ec10b1aed3f43602 (patch)
tree7af4be660811c17f77cefe25c9be2b7df36df980 /lib/Target/JSBackend/JSBackend.cpp
parent9a21bed266588de64040a9ec3b3dafa912eb4c6a (diff)
CPPBackend => JSBackend
Diffstat (limited to 'lib/Target/JSBackend/JSBackend.cpp')
-rw-r--r--lib/Target/JSBackend/JSBackend.cpp1738
1 files changed, 1738 insertions, 0 deletions
diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp
new file mode 100644
index 0000000000..656e40753f
--- /dev/null
+++ b/lib/Target/JSBackend/JSBackend.cpp
@@ -0,0 +1,1738 @@
+//===-- 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 "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 LLVMInitializeJSBackendTarget() {
+ // Register the target.
+ RegisterTargetMachine<JSTargetMachine> X(TheJSBackendTarget);
+}
+
+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 = getJSName(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 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 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 = getJSName(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::getJSName(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 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::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 getJSName(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(getJSName(V), V->getType(), sign);
+ }
+}
+
+std::string JSWriter::getValueAsParenStr(const Value* V) {
+ if (const Constant *CV = dyn_cast<Constant>(V)) {
+ return getConstant(CV);
+ } else {
+ return "(" + getJSName(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(getJSName(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(getJSName(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;