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/JSBackend/JSBackend.cpp | |
parent | 9a21bed266588de64040a9ec3b3dafa912eb4c6a (diff) |
CPPBackend => JSBackend
Diffstat (limited to 'lib/Target/JSBackend/JSBackend.cpp')
-rw-r--r-- | lib/Target/JSBackend/JSBackend.cpp | 1738 |
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; |