diff options
author | Dan Gohman <sunfish@google.com> | 2014-02-24 08:49:19 -0800 |
---|---|---|
committer | Dan Gohman <sunfish@google.com> | 2014-02-25 11:58:56 -0800 |
commit | 338da97ed47659b9ef04f60067f84cafc93e3dd3 (patch) | |
tree | 2a80c78435712eafe02ea4afdc96c8d7713016cb | |
parent | 5653eb58d0b0068f0ef341c8928aa06d1d0ea3f7 (diff) |
Support GEP and ConstantExprs directly in the JSBackend.
This patch also lays the groundwork for the single-use instruction trick to
reduce the number of temporary variables.
-rw-r--r-- | lib/Target/JSBackend/CallHandlers.h | 72 | ||||
-rw-r--r-- | lib/Target/JSBackend/JSBackend.cpp | 516 | ||||
-rw-r--r-- | lib/Transforms/NaCl/ExpandI64.cpp | 55 | ||||
-rw-r--r-- | lib/Transforms/NaCl/ExpandVarArgs.cpp | 11 | ||||
-rw-r--r-- | lib/Transforms/NaCl/LowerEmExceptionsPass.cpp | 16 | ||||
-rw-r--r-- | lib/Transforms/NaCl/PNaClABISimplify.cpp | 4 | ||||
-rw-r--r-- | lib/Transforms/NaCl/PromoteIntegers.cpp | 10 |
7 files changed, 393 insertions, 291 deletions
diff --git a/lib/Target/JSBackend/CallHandlers.h b/lib/Target/JSBackend/CallHandlers.h index c70a37bc47..dae61b2054 100644 --- a/lib/Target/JSBackend/CallHandlers.h +++ b/lib/Target/JSBackend/CallHandlers.h @@ -78,7 +78,7 @@ DEF_CALL_HANDLER(__default__, { if (!NeedCasts) { text += getValueAsStr(CI->getOperand(i)); } else { - text += getValueAsCastStr(CI->getOperand(i), ASM_NONSPECIFIC | FFI_OUT); + text += getValueAsCastParenStr(CI->getOperand(i), ASM_NONSPECIFIC | FFI_OUT); } if (i < NumArgs - 1) text += ","; } @@ -89,11 +89,11 @@ DEF_CALL_HANDLER(__default__, { if (!InstRT->isVoidTy() && ActualRT->isVoidTy()) { // the function we are calling was cast to something returning a value, but it really // does not return a value - getAssign(getJSName(CI), InstRT); // ensure the variable is defined, but do not emit it here - // it should have 0 uses, but just to be safe + getAssignIfNeeded(CI); // ensure the variable is defined, but do not emit it here + // it should have 0 uses, but just to be safe } else if (!ActualRT->isVoidTy()) { unsigned FFI_IN = FFI ? ASM_FFI_IN : 0; - text = getAssign(getJSName(CI), ActualRT) + getCast(text, ActualRT, ASM_NONSPECIFIC | FFI_IN); + text = getAssignIfNeeded(CI) + "(" + getCast(text, ActualRT, ASM_NONSPECIFIC | FFI_IN) + ")"; } return text; }) @@ -107,10 +107,10 @@ DEF_CALL_HANDLER(emscripten_preinvoke, { DEF_CALL_HANDLER(emscripten_postinvoke, { assert(InvokeState == 1 || InvokeState == 2); // normally 2, but can be 1 if the call in between was optimized out InvokeState = 0; - return getAssign(getJSName(CI), CI->getType()) + "__THREW__; __THREW__ = 0"; + return getAssign(CI) + "__THREW__; __THREW__ = 0"; }) DEF_CALL_HANDLER(emscripten_landingpad, { - std::string Ret = getAssign(getJSName(CI), CI->getType()) + "___cxa_find_matching_catch(-1,-1"; + std::string Ret = getAssign(CI) + "___cxa_find_matching_catch(-1,-1"; unsigned Num = getNumArgOperands(CI); for (unsigned i = 1; i < Num-1; i++) { // ignore personality and cleanup XXX - we probably should not be doing that! Ret += ","; @@ -126,7 +126,7 @@ DEF_CALL_HANDLER(emscripten_resume, { // setjmp support DEF_CALL_HANDLER(emscripten_prep_setjmp, { - return getAssign("_setjmpTable", Type::getInt32Ty(CI->getContext())) + "STACKTOP; STACKTOP=(STACKTOP+168)|0;" + // XXX FIXME + return getAdHocAssign("_setjmpTable", Type::getInt32Ty(CI->getContext())) + "STACKTOP; STACKTOP=(STACKTOP+168)|0;" + // XXX FIXME "HEAP32[_setjmpTable>>2]=0"; }) DEF_CALL_HANDLER(emscripten_setjmp, { @@ -141,7 +141,7 @@ DEF_CALL_HANDLER(emscripten_longjmp, { DEF_CALL_HANDLER(emscripten_check_longjmp, { std::string Threw = getValueAsStr(CI->getOperand(0)); std::string Target = getJSName(CI); - std::string Assign = getAssign(Target, CI->getType()); + std::string Assign = getAssign(CI); return "if (((" + Threw + "|0) != 0) & ((threwValue|0) != 0)) { " + Assign + "_testSetjmp(HEAP32[" + Threw + ">>2]|0, _setjmpTable)|0; " + "if ((" + Target + "|0) == 0) { _longjmp(" + Threw + "|0, threwValue|0); } " + // rethrow @@ -150,13 +150,13 @@ DEF_CALL_HANDLER(emscripten_check_longjmp, { }) DEF_CALL_HANDLER(emscripten_get_longjmp_result, { std::string Threw = getValueAsStr(CI->getOperand(0)); - return getAssign(getJSName(CI), CI->getType()) + "tempRet0"; + return getAssign(CI) + "tempRet0"; }) // i64 support DEF_CALL_HANDLER(getHigh32, { - return getAssign(getJSName(CI), CI->getType()) + "tempRet0"; + return getAssign(CI) + "tempRet0"; }) DEF_CALL_HANDLER(setHigh32, { return "tempRet0 = " + getValueAsStr(CI->getOperand(0)); @@ -166,20 +166,20 @@ DEF_CALL_HANDLER(setHigh32, { DEF_CALL_HANDLER(low, { \ std::string Input = getValueAsStr(CI->getOperand(0)); \ if (PreciseF32 && CI->getOperand(0)->getType()->isFloatTy()) Input = "+" + Input; \ - return getAssign(getJSName(CI), CI->getType()) + "(~~" + Input + ")>>>0"; \ + return getAssign(CI) + "(~~" + Input + ")>>>0"; \ }) \ DEF_CALL_HANDLER(high, { \ std::string Input = getValueAsStr(CI->getOperand(0)); \ if (PreciseF32 && CI->getOperand(0)->getType()->isFloatTy()) Input = "+" + Input; \ - return getAssign(getJSName(CI), CI->getType()) + "+Math_abs(" + Input + ") >= +1 ? " + Input + " > +0 ? (Math_min(+Math_floor(" + Input + " / +4294967296), +4294967295) | 0) >>> 0 : ~~+Math_ceil((" + Input + " - +(~~" + Input + " >>> 0)) / +4294967296) >>> 0 : 0"; \ + return getAssign(CI) + "+Math_abs(" + Input + ") >= +1 ? " + Input + " > +0 ? (Math_min(+Math_floor(" + Input + " / +4294967296), +4294967295) | 0) >>> 0 : ~~+Math_ceil((" + Input + " - +(~~" + Input + " >>> 0)) / +4294967296) >>> 0 : 0"; \ }) TO_I(FtoILow, FtoIHigh); TO_I(DtoILow, DtoIHigh); DEF_CALL_HANDLER(BDtoILow, { - return "HEAPF64[tempDoublePtr>>3] = " + getValueAsStr(CI->getOperand(0)) + ";" + getAssign(getJSName(CI), CI->getType()) + "HEAP32[tempDoublePtr>>2]|0"; + return "HEAPF64[tempDoublePtr>>3] = " + getValueAsStr(CI->getOperand(0)) + ";" + getAssign(CI) + "HEAP32[tempDoublePtr>>2]|0"; }) DEF_CALL_HANDLER(BDtoIHigh, { - return getAssign(getJSName(CI), CI->getType()) + "HEAP32[tempDoublePtr+4>>2]|0"; + return getAssign(CI) + "HEAP32[tempDoublePtr+4>>2]|0"; }) DEF_CALL_HANDLER(SItoF, { std::string Ret = "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + @@ -187,7 +187,7 @@ DEF_CALL_HANDLER(SItoF, { if (PreciseF32 && CI->getType()->isFloatTy()) { Ret = "Math_fround(" + Ret + ")"; } - return getAssign(getJSName(CI), CI->getType()) + Ret; + return getAssign(CI) + Ret; }) DEF_CALL_HANDLER(UItoF, { std::string Ret = "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + @@ -195,20 +195,20 @@ DEF_CALL_HANDLER(UItoF, { if (PreciseF32 && CI->getType()->isFloatTy()) { Ret = "Math_fround(" + Ret + ")"; } - return getAssign(getJSName(CI), CI->getType()) + Ret; + return getAssign(CI) + Ret; }) DEF_CALL_HANDLER(SItoD, { - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + return getAssign(CI) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_SIGNED) + "))"; }) DEF_CALL_HANDLER(UItoD, { - return getAssign(getJSName(CI), CI->getType()) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + + return getAssign(CI) + "(+" + getValueAsCastParenStr(CI->getOperand(0), ASM_UNSIGNED) + ") + " + "(+4294967296*(+" + getValueAsCastParenStr(CI->getOperand(1), ASM_UNSIGNED) + "))"; }) DEF_CALL_HANDLER(BItoD, { return "HEAP32[tempDoublePtr>>2] = " + getValueAsStr(CI->getOperand(0)) + ";" + "HEAP32[tempDoublePtr+4>>2] = " + getValueAsStr(CI->getOperand(1)) + ";" + - getAssign(getJSName(CI), CI->getType()) + "+HEAPF64[tempDoublePtr>>3]"; + getAssign(CI) + "+HEAPF64[tempDoublePtr>>3]"; }) // misc @@ -323,7 +323,7 @@ DEF_CALL_HANDLER(llvm_memmove_p0i8_p0i8_i32, { }) DEF_CALL_HANDLER(llvm_expect_i32, { - return getAssign(getJSName(CI), CI->getType()) + getValueAsStr(CI->getOperand(0)); + return getAssign(CI) + getValueAsStr(CI->getOperand(0)); }) DEF_CALL_HANDLER(llvm_dbg_declare, { @@ -342,6 +342,18 @@ DEF_CALL_HANDLER(llvm_lifetime_end, { return ""; }) +DEF_CALL_HANDLER(llvm_invariant_start, { + return ""; +}) + +DEF_CALL_HANDLER(llvm_invariant_end, { + return ""; +}) + +DEF_CALL_HANDLER(llvm_prefetch, { + return ""; +}) + DEF_CALL_HANDLER(bitshift64Lshr, { return CH___default__(CI, "_bitshift64Lshr", 3); }) @@ -364,7 +376,7 @@ DEF_CALL_HANDLER(llvm_cttz_i32, { // vector ops DEF_CALL_HANDLER(emscripten_float32x4_signmask, { - return getAssign(getJSName(CI), CI->getType()) + "SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + ").signMask"; + return getAssign(CI) + "SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + ").signMask"; }) DEF_CALL_HANDLER(emscripten_float32x4_min, { return CH___default__(CI, "SIMD.float32x4.min"); @@ -391,19 +403,19 @@ DEF_CALL_HANDLER(emscripten_float32x4_greaterThan, { return CH___default__(CI, "SIMD.float32x4.greaterThan"); }) DEF_CALL_HANDLER(emscripten_float32x4_and, { - return getAssign(getJSName(CI), CI->getType()) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.float32x4.bitsToInt32x4(" + + return getAssign(CI) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + "),SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(1)) + ")))"; }) DEF_CALL_HANDLER(emscripten_float32x4_andNot, { - return getAssign(getJSName(CI), CI->getType()) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.int32x4.not(SIMD.float32x4.bitsToInt32x4(" + + return getAssign(CI) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(SIMD.int32x4.not(SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + ")),SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(1)) + ")))"; }) DEF_CALL_HANDLER(emscripten_float32x4_or, { - return getAssign(getJSName(CI), CI->getType()) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(SIMD.float32x4.bitsToInt32x4(" + + return getAssign(CI) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + "),SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(1)) + ")))"; }) DEF_CALL_HANDLER(emscripten_float32x4_xor, { - return getAssign(getJSName(CI), CI->getType()) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.xor(SIMD.float32x4.bitsToInt32x4(" + + return getAssign(CI) + "SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.xor(SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(0)) + "),SIMD.float32x4.bitsToInt32x4(" + getValueAsStr(CI->getOperand(1)) + ")))"; }) DEF_CALL_HANDLER(emscripten_int32x4_bitsToFloat32x4, { @@ -712,6 +724,9 @@ void setupCallHandlers() { SETUP_CALL_HANDLER(llvm_dbg_value); SETUP_CALL_HANDLER(llvm_lifetime_start); SETUP_CALL_HANDLER(llvm_lifetime_end); + SETUP_CALL_HANDLER(llvm_invariant_start); + SETUP_CALL_HANDLER(llvm_invariant_end); + SETUP_CALL_HANDLER(llvm_prefetch); SETUP_CALL_HANDLER(bitshift64Lshr); SETUP_CALL_HANDLER(bitshift64Ashr); SETUP_CALL_HANDLER(bitshift64Shl); @@ -977,7 +992,12 @@ void setupCallHandlers() { std::string handleCall(const Instruction *CI) { const Value *CV = getActuallyCalledValue(CI); assert(!isa<InlineAsm>(CV) && "asm() not supported, use EM_ASM() (see emscripten.h)"); - const std::string &Name = getJSName(CV); + + // Get the name to call this function by. If it's a direct call, meaning + // which know which Function we're calling, avoid calling getValueAsStr, as + // we don't need to use a function index. + const std::string &Name = isa<Function>(CV) ? getJSName(CV) : getValueAsStr(CV); + unsigned NumArgs = getNumArgOperands(CI); CallHandlerMap::iterator CH = CallHandlers->find("___default__"); if (isa<Function>(CV)) { diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp index a9d1aac129..a0c199e7db 100644 --- a/lib/Target/JSBackend/JSBackend.cpp +++ b/lib/Target/JSBackend/JSBackend.cpp @@ -15,8 +15,11 @@ //===----------------------------------------------------------------------===// #include "JSTargetMachine.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constants.h" @@ -24,7 +27,9 @@ #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -34,6 +39,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/DebugInfo.h" #include <algorithm> @@ -341,28 +347,6 @@ namespace { return false; } - std::string getPtrAsStr(const Value* Ptr) { - Ptr = Ptr->stripPointerCasts(); - if (isa<const ConstantPointerNull>(Ptr) || isa<UndefValue>(Ptr)) return "0"; - if (const Function *F = dyn_cast<Function>(Ptr)) { - return utostr(getFunctionIndex(F)); - } else if (const Constant *CV = dyn_cast<Constant>(Ptr)) { - if (const BlockAddress *BA = dyn_cast<const BlockAddress>(Ptr)) { - return utostr(getBlockAddress(BA)); - } - 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); - } - } - void checkVectorType(Type *T) { VectorType *VT = cast<VectorType>(T); assert(VT->getElementType()->getPrimitiveSizeInBits() == 32); @@ -387,7 +371,9 @@ namespace { void printType(Type* Ty); void printTypes(const Module* M); - std::string getAssign(const StringRef &, Type *); + std::string getAdHocAssign(const StringRef &, Type *); + std::string getAssign(const Instruction *I); + std::string getAssignIfNeeded(const Value *V); std::string getCast(const StringRef &, Type *, AsmCast sign=ASM_SIGNED); std::string getParenCast(const StringRef &, Type *, AsmCast sign=ASM_SIGNED); std::string getDoubleToInt(const StringRef &); @@ -397,8 +383,9 @@ namespace { void addBlock(const BasicBlock *BB, Relooper& R, LLVMToRelooperMap& LLVMToRelooper); void printFunctionBody(const Function *F); - bool generateSIMDInstruction(const std::string &iName, const Instruction *I, raw_string_ostream& Code); - void generateInstruction(const Instruction *I, raw_string_ostream& Code); + bool generateSIMDExpression(const User *I, raw_string_ostream& Code); + void generateExpression(const User *I, raw_string_ostream& Code); + std::string getOpName(const Value*); void processConstants(); @@ -522,7 +509,7 @@ std::string JSWriter::getPhiCode(const BasicBlock *From, const BasicBlock *To) { if (index < 0) continue; // we found it const std::string &name = getJSName(P); - assigns[name] = getAssign(name, P->getType()); + assigns[name] = getAssign(P); const Value *V = P->getIncomingValue(index); values[name] = V; std::string vname = getValueAsStr(V); @@ -550,7 +537,7 @@ std::string JSWriter::getPhiCode(const BasicBlock *From, const BasicBlock *To) { // break a cycle std::string depString = dep->second; std::string temp = curr + "$phi"; - pre += getAssign(temp, V->getType()) + CV + ';'; + pre += getAdHocAssign(temp, V->getType()) + CV + ';'; CV = temp; deps.erase(curr); undeps.erase(depString); @@ -584,16 +571,26 @@ const std::string &JSWriter::getJSName(const Value* val) { return ValueNames[val] = name; } -std::string JSWriter::getAssign(const StringRef &s, Type *t) { +std::string JSWriter::getAdHocAssign(const StringRef &s, Type *t) { UsedVars[s] = t->getTypeID(); return (s + " = ").str(); } +std::string JSWriter::getAssign(const Instruction *I) { + return getAdHocAssign(getJSName(I), I->getType()); +} + +std::string JSWriter::getAssignIfNeeded(const Value *V) { + if (const Instruction *I = dyn_cast<Instruction>(V)) + return getAssign(I); + return std::string(); +} + std::string JSWriter::getCast(const StringRef &s, Type *t, AsmCast sign) { switch (t->getTypeID()) { default: { // some types we cannot cast, like vectors - ignore - if (!t->isVectorTy()) assert(false && "Unsupported type"); + if (!t->isVectorTy()) { errs() << *t << "\n"; assert(false && "Unsupported type");} } case Type::FloatTyID: { if (PreciseF32 && !(sign & ASM_FFI_OUT)) { @@ -656,8 +653,8 @@ std::string JSWriter::getIMul(const Value *V1, const Value *V2) { } std::string JSWriter::getLoad(const Instruction *I, const Value *P, Type *T, unsigned Alignment, char sep) { - std::string Assign = getAssign(getJSName(I), I->getType()); - unsigned Bytes = T->getPrimitiveSizeInBits()/8; + std::string Assign = getAssign(I); + unsigned Bytes = DL->getTypeAllocSize(T); std::string text; if (Bytes <= Alignment || Alignment == 0) { text = Assign + getPtrLoad(P); @@ -672,7 +669,7 @@ std::string JSWriter::getLoad(const Instruction *I, const Value *P, Type *T, uns emitDebugInfo(errs(), I); errs() << "\n"; } - std::string PS = getOpName(P); + std::string PS = getValueAsStr(P); switch (Bytes) { case 8: { switch (Alignment) { @@ -767,7 +764,7 @@ std::string JSWriter::getStore(const Instruction *I, const Value *P, Type *T, co emitDebugInfo(errs(), I); errs() << "\n"; } - std::string PS = getOpName(P); + std::string PS = getValueAsStr(P); switch (Bytes) { case 8: { text = "HEAPF64[tempDoublePtr>>3]=" + VS + ';'; @@ -894,121 +891,108 @@ std::string JSWriter::getPtrUse(const Value* Ptr) { case 1: return "HEAP8[" + utostr(Addr) + "]"; } } else { - return getHeapAccess(getPtrAsStr(Ptr), Bytes, t->isIntegerTy() || t->isPointerTy()); + return getHeapAccess(getValueAsStr(Ptr), Bytes, t->isIntegerTy() || t->isPointerTy()); } } -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) { +static inline std::string ftostr(const ConstantFP *CFP) { const APFloat &flt = CFP->getValueAPF(); + + // Emscripten has its own spellings for infinity and NaN. if (flt.getCategory() == APFloat::fcInfinity) return flt.isNegative() ? "-inf" : "inf"; else if (flt.getCategory() == APFloat::fcNaN) return "nan"; - 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); - if (len&1) { - // uneven, need to add a 0 to make it be entire bytes - temp = std::string("0") + raw; - raw = temp.c_str(); - len++; - } - 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; + // Request 21 digits, aka DECIMAL_DIG, to avoid rounding errors. + SmallString<29> Str; + flt.toString(Str, 21); + + // asm.js considers literals to be floating-point literals when they contain a + // dot, however our output may be processed by UglifyJS, which doesn't + // currently preserve dots in all cases. Mark floating-point literals with + // unary plus to force them to floating-point. + if (APFloat(flt).roundToIntegral(APFloat::rmNearestTiesToEven) == APFloat::opOK) { + return '+' + Str.str().str(); + } + + return Str.str().str(); } std::string 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); - if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { - S = "Math_fround(+" + S + ")"; // FIXME: can avoid "+" for small enough constants - } else if (S[0] != '+') { - S = '+' + S; - } - return S; - } else if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { - if (sign != ASM_UNSIGNED && CI->getValue().getBitWidth() == 1) { - sign = ASM_UNSIGNED; // bools must always be unsigned: either 0 or 1 - } - return CI->getValue().toString(10, sign != ASM_UNSIGNED); - } else if (isa<UndefValue>(CV)) { - return CV->getType()->isIntegerTy() || CV->getType()->isPointerTy() ? "0" : getCast("0", CV->getType()); - } else if (isa<ConstantAggregateZero>(CV)) { - if (VectorType *VT = dyn_cast<VectorType>(CV->getType())) { - if (VT->getElementType()->isIntegerTy()) { - return "int32x4.splat(0)"; - } else { - return "float32x4.splat(0)"; - } + if (isa<ConstantPointerNull>(CV)) return "0"; + + if (const Function *F = dyn_cast<Function>(CV)) { + return utostr(getFunctionIndex(F)); + } + + if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) { + if (GV->isDeclaration()) { + std::string Name = getOpName(GV); + Externals.insert(Name); + return Name; + } + return utostr(getGlobalAddress(GV->getName().str())); + } + + if (const ConstantFP *CFP = dyn_cast<ConstantFP>(CV)) { + std::string S = ftostr(CFP); + if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { + S = "Math_fround(" + S + ")"; + } + return S; + } else if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) { + if (sign != ASM_UNSIGNED && CI->getValue().getBitWidth() == 1) { + sign = ASM_UNSIGNED; // bools must always be unsigned: either 0 or 1 + } + return CI->getValue().toString(10, sign != ASM_UNSIGNED); + } else if (isa<UndefValue>(CV)) { + std::string S = CV->getType()->isFloatingPointTy() ? "+0" : "0"; // XXX refactor this + if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { + S = "Math_fround(" + S + ")"; + } + return S; + } else if (isa<ConstantAggregateZero>(CV)) { + if (VectorType *VT = dyn_cast<VectorType>(CV->getType())) { + if (VT->getElementType()->isIntegerTy()) { + return "int32x4.splat(0)"; } else { - // something like [0 x i8*] zeroinitializer, which clang can emit for landingpads - return "0"; + return "float32x4.splat(0)"; } - } else if (const ConstantDataVector *DV = dyn_cast<ConstantDataVector>(CV)) { - const VectorType *VT = cast<VectorType>(CV->getType()); - if (VT->getElementType()->isIntegerTy()) { - return "int32x4(" + getConstant(DV->getElementAsConstant(0)) + ',' + + } else { + // something like [0 x i8*] zeroinitializer, which clang can emit for landingpads + return "0"; + } + } else if (const ConstantDataVector *DV = dyn_cast<ConstantDataVector>(CV)) { + const VectorType *VT = cast<VectorType>(CV->getType()); + if (VT->getElementType()->isIntegerTy()) { + return "int32x4(" + getConstant(DV->getElementAsConstant(0)) + ',' + + getConstant(DV->getElementAsConstant(1)) + ',' + + getConstant(DV->getElementAsConstant(2)) + ',' + + getConstant(DV->getElementAsConstant(3)) + ')'; + } else { + return "float32x4(" + getConstant(DV->getElementAsConstant(0)) + ',' + getConstant(DV->getElementAsConstant(1)) + ',' + getConstant(DV->getElementAsConstant(2)) + ',' + getConstant(DV->getElementAsConstant(3)) + ')'; - } else { - return "float32x4(" + getConstant(DV->getElementAsConstant(0)) + ',' + - getConstant(DV->getElementAsConstant(1)) + ',' + - getConstant(DV->getElementAsConstant(2)) + ',' + - getConstant(DV->getElementAsConstant(3)) + ')'; - } - } else if (const ConstantArray *CA = dyn_cast<const ConstantArray>(CV)) { - // handle things like [i8* bitcast (<{ i32, i32, i32 }>* @_ZTISt9bad_alloc to i8*)] which clang can emit for landingpads - assert(CA->getNumOperands() == 1); - CV = CA->getOperand(0); - const ConstantExpr *CE = cast<ConstantExpr>(CV); - CV = CE->getOperand(0); // ignore bitcast - return getPtrAsStr(CV); - } else { - CV->dump(); - llvm_unreachable("Unsupported constant kind"); } + } else if (const ConstantArray *CA = dyn_cast<const ConstantArray>(CV)) { + // handle things like [i8* bitcast (<{ i32, i32, i32 }>* @_ZTISt9bad_alloc to i8*)] which clang can emit for landingpads + assert(CA->getNumOperands() == 1); + CV = CA->getOperand(0); + const ConstantExpr *CE = cast<ConstantExpr>(CV); + CV = CE->getOperand(0); // ignore bitcast + return getConstant(CV); + } else if (const BlockAddress *BA = dyn_cast<const BlockAddress>(CV)) { + return utostr(getBlockAddress(BA)); + } else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { + std::string Code; + raw_string_ostream CodeStream(Code); + CodeStream << '('; + generateExpression(CE, CodeStream); + CodeStream << ')'; + return CodeStream.str(); + } else { + CV->dump(); + llvm_unreachable("Unsupported constant kind"); } } @@ -1021,10 +1005,10 @@ std::string JSWriter::getValueAsStr(const Value* V, AsmCast sign) { } std::string JSWriter::getValueAsCastStr(const Value* V, AsmCast sign) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV, sign); + if (isa<ConstantInt>(V) || isa<ConstantFP>(V)) { + return getConstant(cast<Constant>(V), sign); } else { - return getCast(getJSName(V), V->getType(), sign); + return getCast(getValueAsStr(V), V->getType(), sign); } } @@ -1032,39 +1016,38 @@ std::string JSWriter::getValueAsParenStr(const Value* V) { if (const Constant *CV = dyn_cast<Constant>(V)) { return getConstant(CV); } else { - return "(" + getJSName(V) + ")"; + return "(" + getValueAsStr(V) + ")"; } } std::string JSWriter::getValueAsCastParenStr(const Value* V, AsmCast sign) { - if (const Constant *CV = dyn_cast<Constant>(V)) { - return getConstant(CV, sign); + if (isa<ConstantInt>(V) || isa<ConstantFP>(V)) { + return getConstant(cast<Constant>(V), sign); } else { - return "(" + getCast(getJSName(V), V->getType(), sign) + ")"; + return "(" + getCast(getValueAsStr(V), V->getType(), sign) + ")"; } } -bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instruction *I, raw_string_ostream& Code) { +bool JSWriter::generateSIMDExpression(const User *I, raw_string_ostream& Code) { VectorType *VT; if ((VT = dyn_cast<VectorType>(I->getType()))) { // vector-producing instructions checkVectorType(VT); - Code << getAssign(iName, I->getType()); - - switch (I->getOpcode()) { + switch (Operator::getOpcode(I)) { default: I->dump(); error("invalid vector instr"); break; - case Instruction::FAdd: Code << "SIMD.float32x4.add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::FMul: Code << "SIMD.float32x4.mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::FDiv: Code << "SIMD.float32x4.div(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::Add: Code << "SIMD.int32x4.add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::Sub: Code << "SIMD.int32x4.sub(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::Mul: Code << "SIMD.int32x4.mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::And: Code << "SIMD.int32x4.and(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::Or: Code << "SIMD.int32x4.or(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; - case Instruction::Xor: Code << "SIMD.int32x4.xor(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::FAdd: Code << getAssignIfNeeded(I) << "SIMD.float32x4.add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::FMul: Code << getAssignIfNeeded(I) << "SIMD.float32x4.mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::FDiv: Code << getAssignIfNeeded(I) << "SIMD.float32x4.div(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Add: Code << getAssignIfNeeded(I) << "SIMD.int32x4.add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Sub: Code << getAssignIfNeeded(I) << "SIMD.int32x4.sub(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Mul: Code << getAssignIfNeeded(I) << "SIMD.int32x4.mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::And: Code << getAssignIfNeeded(I) << "SIMD.int32x4.and(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Or: Code << getAssignIfNeeded(I) << "SIMD.int32x4.or(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Xor: Code << getAssignIfNeeded(I) << "SIMD.int32x4.xor(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; case Instruction::FSub: // LLVM represents an fneg(x) as -0.0 - x. + Code << getAssignIfNeeded(I); if (BinaryOperator::isFNeg(I)) { Code << "SIMD.float32x4.neg(" << getValueAsStr(BinaryOperator::getFNegArgument(I)) << ")"; } else { @@ -1072,6 +1055,7 @@ bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instructi } break; case Instruction::BitCast: { + Code << getAssignIfNeeded(I); if (cast<VectorType>(I->getType())->getElementType()->isIntegerTy()) { Code << "SIMD.float32x4.bitsToInt32x4(" << getValueAsStr(I->getOperand(0)) << ')'; } else { @@ -1080,7 +1064,10 @@ bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instructi break; } case Instruction::Load: { - std::string PS = getOpName(I->getOperand(0)); + const LoadInst *LI = cast<LoadInst>(I); + const Value *P = LI->getPointerOperand(); + std::string PS = getValueAsStr(P); + Code << getAssignIfNeeded(I); if (VT->getElementType()->isIntegerTy()) { Code << "int32x4(HEAPU32[" << PS << ">>2],HEAPU32[" << PS << "+4>>2],HEAPU32[" << PS << "+8>>2],HEAPU32[" << PS << "+12>>2])"; } else { @@ -1093,6 +1080,7 @@ bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instructi const ConstantInt *IndexInt = cast<const ConstantInt>(III->getOperand(2)); unsigned Index = IndexInt->getZExtValue(); assert(Index <= 3); + Code << getAssignIfNeeded(I); if (VT->getElementType()->isIntegerTy()) { Code << "SIMD.int32x4.with"; } else { @@ -1103,6 +1091,7 @@ bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instructi break; } case Instruction::ShuffleVector: { + Code << getAssignIfNeeded(I); if (VT->getElementType()->isIntegerTy()) { Code << "int32x4("; } else { @@ -1130,24 +1119,27 @@ bool JSWriter::generateSIMDInstruction(const std::string &iName, const Instructi return true; } else { // vector-consuming instructions - if (I->getOpcode() == Instruction::Store && (VT = dyn_cast<VectorType>(I->getOperand(0)->getType())) && VT->isVectorTy()) { + if (Operator::getOpcode(I) == Instruction::Store && (VT = dyn_cast<VectorType>(I->getOperand(0)->getType())) && VT->isVectorTy()) { checkVectorType(VT); - std::string PS = getOpName(I->getOperand(1)); - std::string VS = getValueAsStr(I->getOperand(0)); + const StoreInst *SI = cast<StoreInst>(I); + const Value *P = SI->getPointerOperand(); + std::string PS = getOpName(P); + std::string VS = getValueAsStr(SI->getValueOperand()); + Code << getAdHocAssign(PS, P->getType()) << getValueAsStr(P) << ';'; if (VT->getElementType()->isIntegerTy()) { Code << "HEAPU32[" << PS << ">>2]=" << VS << ".x;HEAPU32[" << PS << "+4>>2]=" << VS << ".y;HEAPU32[" << PS << "+8>>2]=" << VS << ".z;HEAPU32[" << PS << "+12>>2]=" << VS << ".w"; } else { Code << "HEAPF32[" << PS << ">>2]=" << VS << ".x;HEAPF32[" << PS << "+4>>2]=" << VS << ".y;HEAPF32[" << PS << "+8>>2]=" << VS << ".z;HEAPF32[" << PS << "+12>>2]=" << VS << ".w"; } return true; - } else if (I->getOpcode() == Instruction::ExtractElement) { + } else if (Operator::getOpcode(I) == Instruction::ExtractElement) { const ExtractElementInst *EEI = cast<ExtractElementInst>(I); VT = cast<VectorType>(EEI->getVectorOperand()->getType()); checkVectorType(VT); const ConstantInt *IndexInt = cast<const ConstantInt>(EEI->getIndexOperand()); unsigned Index = IndexInt->getZExtValue(); assert(Index <= 3); - Code << getAssign(iName, I->getType()); + Code << getAssignIfNeeded(I); Code << getValueAsStr(EEI->getVectorOperand()) << '.' << simdLane[Index]; return true; } @@ -1159,17 +1151,15 @@ static uint64_t LSBMask(unsigned numBits) { return numBits >= 64 ? 0xFFFFFFFFFFFFFFFFULL : (1ULL << numBits) - 1; } -// generateInstruction - This member is called for each Instruction in a function. -void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Code) { - const std::string &iName(getJSName(I)); - +// Generate code for and operator, either an Instruction or a ConstantExpr. +void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { Type *T = I->getType(); if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { errs() << *I << "\n"; report_fatal_error("legalization problem"); } - if (!generateSIMDInstruction(iName, I, Code)) switch (I->getOpcode()) { + if (!generateSIMDExpression(I, Code)) switch (Operator::getOpcode(I)) { default: { I->dump(); error("Invalid instruction"); @@ -1180,16 +1170,14 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod const Value *RV = ret->getReturnValue(); Code << "STACKTOP = sp;"; Code << "return"; - if (RV == NULL) { - Code << ";"; - } else { - Code << " " << getValueAsCastStr(RV, ASM_NONSPECIFIC) << ";"; + if (RV != NULL) { + Code << " " << getValueAsCastParenStr(RV, ASM_NONSPECIFIC); } break; } case Instruction::Br: case Instruction::IndirectBr: - case Instruction::Switch: break; // handled while relooping + case Instruction::Switch: return; // handled while relooping case Instruction::Unreachable: { // Typically there should be an abort right before these, so we don't emit any code // TODO: when ASSERTIONS are on, emit abort(0) Code << "// unreachable"; @@ -1213,8 +1201,8 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod case Instruction::Shl: case Instruction::LShr: case Instruction::AShr:{ - Code << getAssign(iName, I->getType()); - unsigned opcode = I->getOpcode(); + Code << getAssignIfNeeded(I); + unsigned opcode = Operator::getOpcode(I); switch (opcode) { case Instruction::Add: Code << getParenCast( getValueAsParenStr(I->getOperand(0)) + @@ -1270,13 +1258,12 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod Code << ensureFloat(getValueAsStr(I->getOperand(0)) + " - " + getValueAsStr(I->getOperand(1)), I->getType()); } break; - default: error("bad icmp"); break; + default: error("bad binary opcode"); break; } - Code << ';'; break; } case Instruction::FCmp: { - Code << getAssign(iName, I->getType()); + Code << getAssignIfNeeded(I); switch (cast<FCmpInst>(I)->getPredicate()) { // Comparisons which are simple JS operators. case FCmpInst::FCMP_OEQ: Code << getValueAsStr(I->getOperand(0)) << " == " << getValueAsStr(I->getOperand(1)); break; @@ -1324,13 +1311,14 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod default: error("bad fcmp"); break; } - Code << ";"; break; } case Instruction::ICmp: { - unsigned predicate = cast<ICmpInst>(I)->getPredicate(); + unsigned predicate = isa<ConstantExpr>(I) ? + cast<ConstantExpr>(I)->getPredicate() : + cast<ICmpInst>(I)->getPredicate(); AsmCast sign = CmpInst::isUnsigned(predicate) ? ASM_UNSIGNED : ASM_SIGNED; - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) << "(" << + Code << getAssignIfNeeded(I) << "(" << getValueAsCastStr(I->getOperand(0), sign) << ")"; switch (predicate) { @@ -1348,23 +1336,23 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod } 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; + UsedVars[getJSName(I)] = cast<PointerType>(I->getType())->getElementType()->getTypeID(); + return; } const AllocaInst* AI = cast<AllocaInst>(I); AllocaIntMap::iterator AIMI = StackAllocs.find(AI); if (AIMI != StackAllocs.end()) { // fixed-size allocation that is already taken into account in the big initial allocation if (AIMI->second) { - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) << "sp + " << utostr(AIMI->second) << "|0;"; + Code << getAssign(AI) << "sp + " << utostr(AIMI->second) << "|0"; } else { - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) << "sp;"; + Code << getAssign(AI) << "sp"; } break; } @@ -1377,7 +1365,7 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod } else { Size = memAlignStr("((" + utostr(BaseSize) + '*' + getValueAsStr(AS) + ")|0)"); } - Code << getAssign(iName, Type::getInt32Ty(I->getContext())) << "STACKTOP; STACKTOP = STACKTOP + " << Size << "|0;"; + Code << getAssign(AI) << "STACKTOP; STACKTOP = STACKTOP + " << Size << "|0"; break; } case Instruction::Load: { @@ -1385,9 +1373,9 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod const Value *P = LI->getPointerOperand(); unsigned Alignment = LI->getAlignment(); if (NativizedVars.count(P)) { - Code << getAssign(iName, LI->getType()) << getValueAsStr(P) << ';'; + Code << getAssign(LI) << getValueAsStr(P); } else { - Code << getLoad(I, P, LI->getType(), Alignment) << ';'; + Code << getLoad(LI, P, LI->getType(), Alignment); } break; } @@ -1398,9 +1386,9 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod unsigned Alignment = SI->getAlignment(); std::string VS = getValueAsStr(V); if (NativizedVars.count(P)) { - Code << getValueAsStr(P) << " = " << VS << ';'; + Code << getValueAsStr(P) << " = " << VS; } else { - Code << getStore(I, P, V->getType(), VS, Alignment) << ';'; + Code << getStore(SI, P, V->getType(), VS, Alignment); } Type *T = V->getType(); @@ -1411,18 +1399,43 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod break; } case Instruction::GetElementPtr: { - report_fatal_error("Unlowered getelementptr"); + Code << getAssignIfNeeded(I); + const GEPOperator *GEP = cast<GEPOperator>(I); + gep_type_iterator GTI = gep_type_begin(GEP); + int32_t ConstantOffset = 0; + std::string text = getValueAsParenStr(GEP->getPointerOperand()); + for (GetElementPtrInst::const_op_iterator I = llvm::next(GEP->op_begin()), + E = GEP->op_end(); + I != E; ++I) { + const Value *Index = *I; + if (StructType *STy = dyn_cast<StructType>(*GTI++)) { + // For a struct, add the member offset. + unsigned FieldNo = cast<ConstantInt>(Index)->getZExtValue(); + uint32_t Offset = DL->getStructLayout(STy)->getElementOffset(FieldNo); + ConstantOffset = (uint32_t)ConstantOffset + Offset; + } else { + // For an array, add the element offset, explicitly scaled. + uint32_t ElementSize = DL->getTypeAllocSize(*GTI); + if (const ConstantInt *CI = dyn_cast<ConstantInt>(Index)) { + ConstantOffset = (uint32_t)ConstantOffset + (uint32_t)CI->getSExtValue() * ElementSize; + } else { + text = "(" + text + " + (" + getIMul(Index, ConstantInt::get(Type::getInt32Ty(GEP->getContext()), ElementSize)) + ")|0)"; + } + } + } + if (ConstantOffset != 0) { + text = "(" + text + " + " + itostr(ConstantOffset) + "|0)"; + } + Code << text; break; } case Instruction::PHI: { // handled separately - we push them back into the relooper branchings - break; + return; } 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)) << ";"; + Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)); break; case Instruction::Trunc: case Instruction::ZExt: @@ -1433,8 +1446,8 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod case Instruction::FPToSI: case Instruction::UIToFP: case Instruction::SIToFP: { - Code << getAssign(iName, I->getType()); - switch (I->getOpcode()) { + Code << getAssignIfNeeded(I); + switch (Operator::getOpcode(I)) { case Instruction::Trunc: { //unsigned inBits = V->getType()->getIntegerBitWidth(); unsigned outBits = I->getType()->getIntegerBitWidth(); @@ -1459,52 +1472,53 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod Code << ensureFloat(getValueAsStr(I->getOperand(0)), I->getType()); break; } - case Instruction::SIToFP: Code << getCast(getValueAsCastParenStr(I->getOperand(0), ASM_SIGNED), I->getType()); break; - case Instruction::UIToFP: Code << getCast(getValueAsCastParenStr(I->getOperand(0), ASM_UNSIGNED), I->getType()); break; - case Instruction::FPToSI: Code << getDoubleToInt(getValueAsParenStr(I->getOperand(0))); break; - case Instruction::FPToUI: Code << getCast(getDoubleToInt(getValueAsParenStr(I->getOperand(0))), I->getType(), ASM_UNSIGNED); break; - case Instruction::PtrToInt: Code << getValueAsStr(I->getOperand(0)); break; - case Instruction::IntToPtr: Code << getValueAsStr(I->getOperand(0)); break; + 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()); + Code << getAssignIfNeeded(I); // Most bitcasts are no-ops for us. However, the exception is int to float and float to int Type *InType = I->getOperand(0)->getType(); Type *OutType = I->getType(); std::string V = getValueAsStr(I->getOperand(0)); if (InType->isIntegerTy() && OutType->isFloatingPointTy()) { assert(InType->getIntegerBitWidth() == 32); - Code << "(HEAP32[tempDoublePtr>>2]=" << V << "," << getCast("HEAPF32[tempDoublePtr>>2]", Type::getFloatTy(TheModule->getContext())) + ");"; + Code << "(HEAP32[tempDoublePtr>>2]=" << V << "," << getCast("HEAPF32[tempDoublePtr>>2]", Type::getFloatTy(TheModule->getContext())) + ")"; } else if (OutType->isIntegerTy() && InType->isFloatingPointTy()) { assert(OutType->getIntegerBitWidth() == 32); - Code << "(HEAPF32[tempDoublePtr>>2]=" << V << "," << "HEAP32[tempDoublePtr>>2]|0);"; + Code << "(HEAPF32[tempDoublePtr>>2]=" << V << "," << "HEAP32[tempDoublePtr>>2]|0)"; } else { - Code << V << ";"; + Code << V; } break; } case Instruction::Call: { const CallInst *CI = cast<CallInst>(I); std::string Call = handleCall(CI); - if (Call.size() > 0) Code << Call << ';'; + if (Call.empty()) return; + Code << Call; break; } case Instruction::Select: { const SelectInst* SI = cast<SelectInst>(I); - Code << getAssign(iName, I->getType()) << getValueAsStr(SI->getCondition()) << " ? " << - getValueAsStr(SI->getTrueValue()) << " : " << - getValueAsStr(SI->getFalseValue()) << ';'; + Code << getAssignIfNeeded(I) << getValueAsStr(SI->getCondition()) << " ? " << + getValueAsStr(SI->getTrueValue()) << " : " << + getValueAsStr(SI->getFalseValue()); break; } case Instruction::AtomicCmpXchg: { + const AtomicCmpXchgInst *cxi = cast<AtomicCmpXchgInst>(I); const Value *P = I->getOperand(0); - Code << getLoad(I, P, I->getType(), 0) << ';' << - "if ((" << getCast(iName, I->getType()) << ") == " << getValueAsCastParenStr(I->getOperand(1)) << ") " << - getStore(I, P, I->getType(), getValueAsStr(I->getOperand(2)), 0) << ";"; + Code << getLoad(cxi, P, I->getType(), 0) << ';' << + "if ((" << getCast(getJSName(I), I->getType()) << ") == " << getValueAsCastParenStr(I->getOperand(1)) << ") " << + getStore(cxi, P, I->getType(), getValueAsStr(I->getOperand(2)), 0); break; } case Instruction::AtomicRMW: { @@ -1512,29 +1526,35 @@ void JSWriter::generateInstruction(const Instruction *I, raw_string_ostream& Cod const Value *P = rmwi->getOperand(0); const Value *V = rmwi->getOperand(1); std::string VS = getValueAsStr(V); - Code << getLoad(I, P, I->getType(), 0) << ';'; + Code << getLoad(rmwi, P, I->getType(), 0) << ';'; // Most bitcasts are no-ops for us. However, the exception is int to float and float to int switch (rmwi->getOperation()) { - case AtomicRMWInst::Xchg: Code << getStore(I, P, I->getType(), VS, 0); break; - case AtomicRMWInst::Add: Code << getStore(I, P, I->getType(), "((" + iName + '+' + VS + ")|0)", 0); break; - case AtomicRMWInst::Sub: Code << getStore(I, P, I->getType(), "((" + iName + '-' + VS + ")|0)", 0); break; - case AtomicRMWInst::And: Code << getStore(I, P, I->getType(), "(" + iName + '&' + VS + ")", 0); break; - case AtomicRMWInst::Nand: Code << getStore(I, P, I->getType(), "(~(" + iName + '&' + VS + "))", 0); break; - case AtomicRMWInst::Or: Code << getStore(I, P, I->getType(), "(" + iName + '|' + VS + ")", 0); break; - case AtomicRMWInst::Xor: Code << getStore(I, P, I->getType(), "(" + iName + '^' + VS + ")", 0); break; + case AtomicRMWInst::Xchg: Code << getStore(rmwi, P, I->getType(), VS, 0); break; + case AtomicRMWInst::Add: Code << getStore(rmwi, P, I->getType(), "((" + getJSName(I) + '+' + VS + ")|0)", 0); break; + case AtomicRMWInst::Sub: Code << getStore(rmwi, P, I->getType(), "((" + getJSName(I) + '-' + VS + ")|0)", 0); break; + case AtomicRMWInst::And: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '&' + VS + ")", 0); break; + case AtomicRMWInst::Nand: Code << getStore(rmwi, P, I->getType(), "(~(" + getJSName(I) + '&' + VS + "))", 0); break; + case AtomicRMWInst::Or: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '|' + VS + ")", 0); break; + case AtomicRMWInst::Xor: Code << getStore(rmwi, P, I->getType(), "(" + getJSName(I) + '^' + VS + ")", 0); break; case AtomicRMWInst::Max: case AtomicRMWInst::Min: case AtomicRMWInst::UMax: case AtomicRMWInst::UMin: case AtomicRMWInst::BAD_BINOP: llvm_unreachable("Bad atomic operation"); } - Code << ";"; break; } - case Instruction::Fence: break; // no threads, so nothing to do here + case Instruction::Fence: // no threads, so nothing to do here + Code << "/* fence */"; + break; + } + + if (const Instruction *Inst = dyn_cast<Instruction>(I)) { + Code << ';'; + // append debug info + emitDebugInfo(Code, Inst); + Code << '\n'; } - // append debug info - emitDebugInfo(Code, I); } // Checks whether to use a condition variable. We do so for switches and for indirectbrs @@ -1566,8 +1586,7 @@ void JSWriter::addBlock(const BasicBlock *BB, Relooper& R, LLVMToRelooperMap& LL raw_string_ostream CodeStream(Code); for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); I != E; ++I) { - generateInstruction(I, CodeStream); - CodeStream << '\n'; + generateExpression(I, CodeStream); } CodeStream.flush(); const Value* Condition = considerConditionVar(BB->getTerminator()); @@ -1726,7 +1745,7 @@ void JSWriter::printFunctionBody(const Function *F) { } // Emit stack entry - Out << " " << getAssign("sp", Type::getInt32Ty(F->getContext())) << "STACKTOP;"; + Out << " " << getAdHocAssign("sp", Type::getInt32Ty(F->getContext())) << "STACKTOP;"; if (TotalStackAllocs) { Out << "\n " << "STACKTOP = STACKTOP + " + utostr(TotalStackAllocs) + "|0;"; } @@ -1798,15 +1817,13 @@ void JSWriter::printFunction(const Function *F) { for (BasicBlock::const_iterator II = BI->begin(), E = BI->end(); II != E; ++II) { if (const AllocaInst* AI = dyn_cast<AllocaInst>(II)) { Type *T = AI->getAllocatedType(); - if (!T->isVectorTy()) { - const Value *AS = AI->getArraySize(); - unsigned BaseSize = DL->getTypeAllocSize(T); - if (const ConstantInt *CI = dyn_cast<ConstantInt>(AS)) { - // TODO: group by alignment to avoid unnecessary padding - unsigned Size = memAlign(BaseSize * CI->getZExtValue()); - StackAllocs[AI] = TotalStackAllocs; - TotalStackAllocs += Size; - } + const Value *AS = AI->getArraySize(); + unsigned BaseSize = DL->getTypeAllocSize(T); + if (const ConstantInt *CI = dyn_cast<ConstantInt>(AS)) { + // TODO: group by alignment to avoid unnecessary padding + unsigned Size = memAlign(BaseSize * CI->getZExtValue()); + StackAllocs[AI] = TotalStackAllocs; + TotalStackAllocs += Size; } } else { // stop after the first non-alloca - could alter the stack @@ -1847,6 +1864,21 @@ void JSWriter::printModuleBody() { nl(Out) << "// EMSCRIPTEN_START_FUNCTIONS"; nl(Out); for (Module::const_iterator I = TheModule->begin(), E = TheModule->end(); I != E; ++I) { + // Ignore intrinsics that are always no-ops. We don't emit any code for + // them, so we don't need to declare them. + if (I->isIntrinsic()) { + switch (I->getIntrinsicID()) { + case Intrinsic::dbg_declare: + case Intrinsic::dbg_value: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + case Intrinsic::invariant_start: + case Intrinsic::invariant_end: + case Intrinsic::prefetch: + continue; + } + } + if (!I->isDeclaration()) printFunction(I); } Out << "function runPostSets() {\n"; @@ -2168,12 +2200,22 @@ void JSWriter::parseConstant(const std::string& name, const Constant* CV, bool c } } else { unsigned Data = 0; + + // Deconstruct lowered getelementptrs. if (CE->getOpcode() == Instruction::Add) { Data = cast<ConstantInt>(CE->getOperand(1))->getZExtValue(); CE = cast<ConstantExpr>(CE->getOperand(0)); } - assert(CE->isCast()); - const Value *V = CE->getOperand(0); + const Value *V = CE; + if (CE->getOpcode() == Instruction::PtrToInt) { + V = CE->getOperand(0); + } + + // Deconstruct getelementptrs. + int64_t BaseOffset; + V = GetPointerBaseWithConstantOffset(V, BaseOffset, DL); + Data += (uint64_t)BaseOffset; + Data += getConstAsOffset(V, getGlobalAddress(name)); union { unsigned i; unsigned char b[sizeof(unsigned)]; } integer; integer.i = Data; diff --git a/lib/Transforms/NaCl/ExpandI64.cpp b/lib/Transforms/NaCl/ExpandI64.cpp index a4fbb8c709..911060ebfc 100644 --- a/lib/Transforms/NaCl/ExpandI64.cpp +++ b/lib/Transforms/NaCl/ExpandI64.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/InstructionSimplify.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" @@ -37,6 +38,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/CFG.h" +#include "llvm/Target/TargetLibraryInfo.h" #include "llvm/Transforms/NaCl.h" #include "llvm/Transforms/Utils/Local.h" #include <map> @@ -302,6 +304,23 @@ bool ExpandI64::splitInst(Instruction *I) { ChunksVec &Chunks = Splits[I]; switch (I->getOpcode()) { + case Instruction::GetElementPtr: { + GetElementPtrInst *GEP = cast<GetElementPtrInst>(I); + SmallVector<Value*, 2> NewOps; + for (unsigned i = 1, e = I->getNumOperands(); i != e; ++i) { + Value *Op = I->getOperand(i); + if (isIllegal(Op->getType())) { + // Truncate the operand down to one chunk. + NewOps.push_back(getChunks(Op)[0]); + } else { + NewOps.push_back(Op); + } + } + Value *NewGEP = CopyDebug(GetElementPtrInst::Create(GEP->getPointerOperand(), NewOps, "", GEP), GEP); + Chunks.push_back(NewGEP); + I->replaceAllUsesWith(NewGEP); + break; + } case Instruction::SExt: { ChunksVec InputChunks; Value *Op = I->getOperand(0); @@ -881,32 +900,32 @@ bool ExpandI64::splitInst(Instruction *I) { ChunksVec ExpandI64::getChunks(Value *V) { assert(isIllegal(V->getType())); + unsigned Num = getNumChunks(V->getType()); Type *i32 = Type::getInt32Ty(V->getContext()); - Value *Zero = ConstantInt::get(i32, 0); - unsigned Num = getNumChunks(V->getType()); + if (isa<UndefValue>(V)) + return ChunksVec(Num, UndefValue::get(i32)); - if (const ConstantInt *CI = dyn_cast<ConstantInt>(V)) { - APInt C = CI->getValue(); - ChunksVec Chunks; - for (unsigned i = 0; i < Num; i++) { - Chunks.push_back(ConstantInt::get(i32, C.trunc(32))); - C = C.lshr(32); - } - return Chunks; - } else if (Instruction *I = dyn_cast<Instruction>(V)) { - assert(Splits.find(I) != Splits.end()); - return Splits[I]; - } else if (isa<UndefValue>(V)) { + if (Constant *C = dyn_cast<Constant>(V)) { ChunksVec Chunks; for (unsigned i = 0; i < Num; i++) { - Chunks.push_back(Zero); + Constant *Count = ConstantInt::get(C->getType(), i * 32); + Constant *NewC = ConstantExpr::getTrunc(ConstantExpr::getLShr(C, Count), i32); + TargetLibraryInfo *TLI = 0; // TODO + if (ConstantExpr *NewCE = dyn_cast<ConstantExpr>(NewC)) { + if (Constant *FoldedC = ConstantFoldConstantExpression(NewCE, DL, TLI)) { + NewC = FoldedC; + } + } + + Chunks.push_back(NewC); } return Chunks; - } else { - assert(Splits.find(V) != Splits.end()); - return Splits[V]; } + + assert(Splits.find(V) != Splits.end()); + assert(Splits[V].size() == Num); + return Splits[V]; } void ExpandI64::ensureFuncs() { diff --git a/lib/Transforms/NaCl/ExpandVarArgs.cpp b/lib/Transforms/NaCl/ExpandVarArgs.cpp index 99c200c9f0..1b605b79ee 100644 --- a/lib/Transforms/NaCl/ExpandVarArgs.cpp +++ b/lib/Transforms/NaCl/ExpandVarArgs.cpp @@ -283,9 +283,14 @@ static bool ExpandVarArgCall(InstType *Call, DataLayout *DL) { ArgTypes.push_back(VarArgsTy->getPointerTo()); FunctionType *NFTy = FunctionType::get(FuncType->getReturnType(), ArgTypes, false); - Value *CastFunc = - CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), - "vararg_func", Call), Call); + /// XXX EMSCRIPTEN: Handle Constants as well as Instructions, since we + /// don't run the ConstantExpr lowering pass. + Value *CastFunc; + if (Constant *C = dyn_cast<Constant>(Call->getCalledValue())) + CastFunc = ConstantExpr::getBitCast(C, NFTy->getPointerTo()); + else + CastFunc = CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), + "vararg_func", Call), Call); // Create the converted function call. FixedArgs.push_back(Buf); diff --git a/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp b/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp index a562588c3a..2e3f0924cc 100644 --- a/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp +++ b/lib/Transforms/NaCl/LowerEmExceptionsPass.cpp @@ -231,7 +231,21 @@ bool LowerEmExceptions::runOnModule(Module &M) { unsigned Num = LP->getNumClauses(); SmallVector<Value*,16> NewLPArgs; NewLPArgs.push_back(LP->getPersonalityFn()); - for (unsigned i = 0; i < Num; i++) NewLPArgs.push_back(LP->getClause(i)); + for (unsigned i = 0; i < Num; i++) { + Value *Arg = LP->getClause(i); + // As a temporary workaround for the lack of aggregate varargs support + // in the varargs lowering code, break out filter operands into their + // component elements. + if (LP->isFilter(i)) { + ArrayType *ATy = cast<ArrayType>(Arg->getType()); + for (unsigned elem = 0, elemEnd = ATy->getNumElements(); elem != elemEnd; ++elem) { + Instruction *EE = ExtractValueInst::Create(Arg, makeArrayRef(elem), "", LP); + NewLPArgs.push_back(EE); + } + } else { + NewLPArgs.push_back(Arg); + } + } NewLPArgs.push_back(LP->isCleanup() ? ConstantInt::getTrue(i1) : ConstantInt::getFalse(i1)); CallInst *NewLP = CallInst::Create(LandingPad, NewLPArgs, "", LP); diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp index 520663152a..416c38dd17 100644 --- a/lib/Transforms/NaCl/PNaClABISimplify.cpp +++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp @@ -116,15 +116,19 @@ void llvm::PNaClABISimplifyAddPostOptPasses(PassManager &PM) { // are expanded out later. PM.add(createFlattenGlobalsPass()); +#if 0 // XXX EMSCRIPTEN: We can handle ConstantExprs in our backend. // We should not place arbitrary passes after ExpandConstantExpr // because they might reintroduce ConstantExprs. PM.add(createExpandConstantExprPass()); +#endif // PromoteIntegersPass does not handle constexprs and creates GEPs, // so it goes between those passes. PM.add(createPromoteIntegersPass()); +#if 0 // XXX EMSCRIPTEN: We can handle GEPs in our backend. // ExpandGetElementPtr must follow ExpandConstantExpr to expand the // getelementptr instructions it creates. PM.add(createExpandGetElementPtrPass()); +#endif // Rewrite atomic and volatile instructions with intrinsic calls. #if 0 // EMSCRIPTEN: we don't need to fix volatiles etc, and can use llvm intrinsics PM.add(createRewriteAtomicsPass()); diff --git a/lib/Transforms/NaCl/PromoteIntegers.cpp b/lib/Transforms/NaCl/PromoteIntegers.cpp index b8050b5ba2..af34faa7e5 100644 --- a/lib/Transforms/NaCl/PromoteIntegers.cpp +++ b/lib/Transforms/NaCl/PromoteIntegers.cpp @@ -233,14 +233,14 @@ class ConversionState { Value *PromoteIntegers::splitLoad(LoadInst *Inst, ConversionState &State) { if (Inst->isVolatile() || Inst->isAtomic()) report_fatal_error("Can't split volatile/atomic loads"); - if (cast<IntegerType>(Inst->getType())->getBitWidth() % 8 != 0) + if (DL->getTypeSizeInBits(Inst->getType()) % 8 != 0) report_fatal_error("Loads must be a multiple of 8 bits"); Value *OrigPtr = State.getConverted(Inst->getPointerOperand()); // OrigPtr is a placeholder in recursive calls, and so has no name if (OrigPtr->getName().empty()) OrigPtr->setName(Inst->getPointerOperand()->getName()); - unsigned Width = cast<IntegerType>(Inst->getType())->getBitWidth(); + unsigned Width = DL->getTypeSizeInBits(Inst->getType()); Type *NewType = getPromotedType(Inst->getType()); unsigned LoWidth = Width; @@ -293,8 +293,7 @@ Value *PromoteIntegers::splitLoad(LoadInst *Inst, ConversionState &State) { Value *PromoteIntegers::splitStore(StoreInst *Inst, ConversionState &State) { if (Inst->isVolatile() || Inst->isAtomic()) report_fatal_error("Can't split volatile/atomic stores"); - if (cast<IntegerType>(Inst->getValueOperand()->getType())->getBitWidth() % 8 - != 0) + if (DL->getTypeSizeInBits(Inst->getValueOperand()->getType()) % 8 != 0) report_fatal_error("Stores must be a multiple of 8 bits"); Value *OrigPtr = State.getConverted(Inst->getPointerOperand()); @@ -302,8 +301,7 @@ Value *PromoteIntegers::splitStore(StoreInst *Inst, ConversionState &State) { if (OrigPtr->getName().empty()) OrigPtr->setName(Inst->getPointerOperand()->getName()); Value *OrigVal = State.getConverted(Inst->getValueOperand()); - unsigned Width = cast<IntegerType>( - Inst->getValueOperand()->getType())->getBitWidth(); + unsigned Width = DL->getTypeSizeInBits(Inst->getValueOperand()->getType()); unsigned LoWidth = Width; while (!isLegalSize(LoWidth)) LoWidth -= 8; |