diff --git a/emscripten-version.txt b/emscripten-version.txt index 3f7f5ca3407..6b92c86119a 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -"1.36.11" +"1.36.12" diff --git a/lib/Target/JSBackend/CallHandlers.h b/lib/Target/JSBackend/CallHandlers.h index 8c9eb7125fc..73e01c858c4 100644 --- a/lib/Target/JSBackend/CallHandlers.h +++ b/lib/Target/JSBackend/CallHandlers.h @@ -132,12 +132,15 @@ DEF_CALL_HANDLER(__default__, { } if (NumArgs > 0) text += ","; } - // this is an ffi call if we need casts, and it is not a special Math_ builtin + // this is an ffi call if we need casts, and it is not a special Math_ builtin or wasm-only intrinsic bool FFI = NeedCasts; - if (FFI && IsMath) { - if (Name == "Math_ceil" || Name == "Math_floor" || Name == "Math_min" || Name == "Math_max" || Name == "Math_sqrt" || Name == "Math_abs") { + if (FFI) { + if (IsMath && (Name == "Math_ceil" || Name == "Math_floor" || Name == "Math_min" || Name == "Math_max" || Name == "Math_sqrt" || Name == "Math_abs")) { // This special Math builtin is optimizable with all types, including floats, so can treat it as non-ffi FFI = false; + } else if (OnlyWebAssembly && Name == "f32_copysign") { + // f32_copysign doesn't need to use a +() coercion which an ffi would need, it's a simple f32 operation + FFI = false; } } unsigned FFI_OUT = FFI ? ASM_FFI_OUT : 0; @@ -360,58 +363,32 @@ DEF_CALL_HANDLER(llvm_memcpy_p0i8_p0i8_i32, { unsigned Len = LenInt->getZExtValue(); if (Len <= WRITE_LOOP_MAX) { unsigned Align = AlignInt->getZExtValue(); - if (Align > 4) Align = 4; - else if (Align == 0) Align = 1; // align 0 means 1 in memcpy and memset (unlike other places where it means 'default/4') - if (Align == 1 && Len > 1 && WarnOnUnaligned) { - errs() << "emcc: warning: unaligned memcpy in " << CI->getParent()->getParent()->getName() << ":" << *CI << " (compiler's fault?)\n"; - } - unsigned Pos = 0; - std::string Ret; - std::string Dest = getValueAsStr(CI->getOperand(0)); - std::string Src = getValueAsStr(CI->getOperand(1)); - while (Len > 0) { - // handle as much as we can in the current alignment - unsigned CurrLen = Align*(Len/Align); - unsigned Factor = CurrLen/Align; - if (Factor <= UNROLL_LOOP_MAX) { - // unroll - for (unsigned Offset = 0; Offset < CurrLen; Offset += Align) { + if (OnlyWebAssembly) { + // wasm + if (Align > 8) Align = 8; + else if (Align == 0) Align = 1; // align 0 means 1 in memcpy and memset (unlike other places where it means 'default') + unsigned Pos = 0; + std::string Ret; + std::string Dest = getValueAsStr(CI->getOperand(0)); + std::string Src = getValueAsStr(CI->getOperand(1)); + unsigned Size = 8; // start by writing out i64 copies + while (Len > 0) { + // handle as much as we can in the current size + unsigned CurrLen = Size*(Len/Size); + for (unsigned Offset = 0; Offset < CurrLen; Offset += Size) { unsigned PosOffset = Pos + Offset; - std::string Add = PosOffset == 0 ? "" : ("+" + utostr(PosOffset)); - Ret += ";" + getHeapAccess(Dest + Add, Align) + "=" + getHeapAccess(Src + Add, Align) + "|0"; + std::string Add = PosOffset == 0 ? "" : ("+" + utostr(PosOffset) + " | 0"); + Ret += "; store" + utostr(Size) + "(" + Dest + Add + + ",load" + utostr(Size) + "(" + Src + Add + "," + utostr(std::min(Align, Size)) + ")" + + "," + utostr(std::min(Align, Size)) + ")"; } - } else { - // emit a loop - UsedVars["dest"] = UsedVars["src"] = UsedVars["stop"] = Type::getInt32Ty(TheModule->getContext()); - std::string Add = Pos == 0 ? "" : ("+" + utostr(Pos) + "|0"); - Ret += "dest=" + Dest + Add + "; src=" + Src + Add + "; stop=dest+" + utostr(CurrLen) + "|0; do { " + getHeapAccess("dest", Align) + "=" + getHeapAccess("src", Align) + "|0; dest=dest+" + utostr(Align) + "|0; src=src+" + utostr(Align) + "|0; } while ((dest|0) < (stop|0))"; + Pos += CurrLen; + Len -= CurrLen; + Size /= 2; } - Pos += CurrLen; - Len -= CurrLen; - Align /= 2; - } - return Ret; - } - } - } - } - Declares.insert("memcpy"); - return CH___default__(CI, "_memcpy", 3) + "|0"; -}) - -DEF_CALL_HANDLER(llvm_memset_p0i8_i32, { - if (CI) { - ConstantInt *AlignInt = dyn_cast(CI->getOperand(3)); - if (AlignInt) { - ConstantInt *LenInt = dyn_cast(CI->getOperand(2)); - if (LenInt) { - ConstantInt *ValInt = dyn_cast(CI->getOperand(1)); - if (ValInt) { - // we can emit inline code for this - unsigned Len = LenInt->getZExtValue(); - if (Len <= WRITE_LOOP_MAX) { - unsigned Align = AlignInt->getZExtValue(); - unsigned Val = ValInt->getZExtValue(); + return Ret; + } else { + // asm.js if (Align > 4) Align = 4; else if (Align == 0) Align = 1; // align 0 means 1 in memcpy and memset (unlike other places where it means 'default/4') if (Align == 1 && Len > 1 && WarnOnUnaligned) { @@ -420,27 +397,23 @@ DEF_CALL_HANDLER(llvm_memset_p0i8_i32, { unsigned Pos = 0; std::string Ret; std::string Dest = getValueAsStr(CI->getOperand(0)); + std::string Src = getValueAsStr(CI->getOperand(1)); while (Len > 0) { // handle as much as we can in the current alignment unsigned CurrLen = Align*(Len/Align); - unsigned FullVal = 0; - for (unsigned i = 0; i < Align; i++) { - FullVal <<= 8; - FullVal |= Val; - } unsigned Factor = CurrLen/Align; if (Factor <= UNROLL_LOOP_MAX) { // unroll for (unsigned Offset = 0; Offset < CurrLen; Offset += Align) { unsigned PosOffset = Pos + Offset; std::string Add = PosOffset == 0 ? "" : ("+" + utostr(PosOffset)); - Ret += ";" + getHeapAccess(Dest + Add, Align) + "=" + utostr(FullVal) + "|0"; + Ret += ";" + getHeapAccess(Dest + Add, Align) + "=" + getHeapAccess(Src + Add, Align) + "|0"; } } else { // emit a loop - UsedVars["dest"] = UsedVars["stop"] = Type::getInt32Ty(TheModule->getContext()); + UsedVars["dest"] = UsedVars["src"] = UsedVars["stop"] = Type::getInt32Ty(TheModule->getContext()); std::string Add = Pos == 0 ? "" : ("+" + utostr(Pos) + "|0"); - Ret += "dest=" + Dest + Add + "; stop=dest+" + utostr(CurrLen) + "|0; do { " + getHeapAccess("dest", Align) + "=" + utostr(FullVal) + "|0; dest=dest+" + utostr(Align) + "|0; } while ((dest|0) < (stop|0))"; + Ret += "dest=" + Dest + Add + "; src=" + Src + Add + "; stop=dest+" + utostr(CurrLen) + "|0; do { " + getHeapAccess("dest", Align) + "=" + getHeapAccess("src", Align) + "|0; dest=dest+" + utostr(Align) + "|0; src=src+" + utostr(Align) + "|0; } while ((dest|0) < (stop|0))"; } Pos += CurrLen; Len -= CurrLen; @@ -452,6 +425,95 @@ DEF_CALL_HANDLER(llvm_memset_p0i8_i32, { } } } + Declares.insert("memcpy"); + return CH___default__(CI, "_memcpy", 3) + "|0"; +}) + +DEF_CALL_HANDLER(llvm_memset_p0i8_i32, { + if (CI) { + ConstantInt *AlignInt = dyn_cast(CI->getOperand(3)); + if (AlignInt) { + ConstantInt *LenInt = dyn_cast(CI->getOperand(2)); + if (LenInt) { + ConstantInt *ValInt = dyn_cast(CI->getOperand(1)); + if (ValInt) { + // we can emit inline code for this + unsigned Len = LenInt->getZExtValue(); + if (Len <= WRITE_LOOP_MAX) { + unsigned Align = AlignInt->getZExtValue(); + if (OnlyWebAssembly) { + // wasm + uint64_t Val64 = ValInt->getZExtValue(); + if (Align > 8) Align = 8; + else if (Align == 0) Align = 1; // align 0 means 1 in memcpy and memset (unlike other places where it means 'default') + unsigned Pos = 0; + std::string Ret; + std::string Dest = getValueAsStr(CI->getOperand(0)); + std::string Src = getValueAsStr(CI->getOperand(1)); + unsigned Size = 8; // start by writing out i64 copies + while (Len > 0) { + // handle as much as we can in the current size + unsigned CurrLen = Size*(Len/Size); + uint64_t FullVal = 0; + for (unsigned i = 0; i < Size; i++) { + FullVal <<= 8; + FullVal |= Val64; + } + std::string ValStr = Size < 8 ? utostr(FullVal) : emitI64Const(FullVal); + for (unsigned Offset = 0; Offset < CurrLen; Offset += Size) { + unsigned PosOffset = Pos + Offset; + std::string Add = PosOffset == 0 ? "" : ("+" + utostr(PosOffset) + "|0"); + Ret += "; store" + utostr(Size) + "(" + Dest + Add + "," + ValStr + "," + utostr(std::min(Align, Size)) + ")"; + } + Pos += CurrLen; + Len -= CurrLen; + Size /= 2; + } + return Ret; + } else { + // asm.js + unsigned Val = ValInt->getZExtValue(); + if (Align > 4) Align = 4; + else if (Align == 0) Align = 1; // align 0 means 1 in memcpy and memset (unlike other places where it means 'default/4') + if (Align == 1 && Len > 1 && WarnOnUnaligned) { + errs() << "emcc: warning: unaligned memcpy in " << CI->getParent()->getParent()->getName() << ":" << *CI << " (compiler's fault?)\n"; + } + unsigned Pos = 0; + std::string Ret; + std::string Dest = getValueAsStr(CI->getOperand(0)); + while (Len > 0) { + // handle as much as we can in the current alignment + unsigned CurrLen = Align*(Len/Align); + unsigned FullVal = 0; + for (unsigned i = 0; i < Align; i++) { + FullVal <<= 8; + FullVal |= Val; + } + unsigned Factor = CurrLen/Align; + if (Factor <= UNROLL_LOOP_MAX) { + // unroll + for (unsigned Offset = 0; Offset < CurrLen; Offset += Align) { + unsigned PosOffset = Pos + Offset; + std::string Add = PosOffset == 0 ? "" : ("+" + utostr(PosOffset)); + Ret += ";" + getHeapAccess(Dest + Add, Align) + "=" + utostr(FullVal) + "|0"; + } + } else { + // emit a loop + UsedVars["dest"] = UsedVars["stop"] = Type::getInt32Ty(TheModule->getContext()); + std::string Add = Pos == 0 ? "" : ("+" + utostr(Pos) + "|0"); + Ret += "dest=" + Dest + Add + "; stop=dest+" + utostr(CurrLen) + "|0; do { " + getHeapAccess("dest", Align) + "=" + utostr(FullVal) + "|0; dest=dest+" + utostr(Align) + "|0; } while ((dest|0) < (stop|0))"; + } + Pos += CurrLen; + Len -= CurrLen; + Align /= 2; + } + return Ret; + } + } + } + } + } + } Declares.insert("memset"); return CH___default__(CI, "_memset", 3) + "|0"; }) @@ -577,10 +639,45 @@ DEF_CALL_HANDLER(llvm_ctlz_i32, { }) DEF_CALL_HANDLER(llvm_cttz_i32, { + if (OnlyWebAssembly) { + return CH___default__(CI, "i32_cttz", 1); + } Declares.insert("llvm_cttz_i32"); return CH___default__(CI, "_llvm_cttz_i32", 1); }) +DEF_CALL_HANDLER(llvm_ctlz_i64, { + if (OnlyWebAssembly) { + return CH___default__(CI, "i64_ctlz", 1); + } + Declares.insert("llvm_ctlz_i64"); + return CH___default__(CI, "_llvm_ctlz_i64"); +}) + +DEF_CALL_HANDLER(llvm_cttz_i64, { + if (OnlyWebAssembly) { + return CH___default__(CI, "i64_cttz", 1); + } + Declares.insert("llvm_cttz_i64"); + return CH___default__(CI, "_llvm_cttz_i64"); +}) + +DEF_CALL_HANDLER(llvm_ctpop_i32, { + if (OnlyWebAssembly) { + return CH___default__(CI, "i32_ctpop", 1); + } + Declares.insert("llvm_ctpop_i32"); + return CH___default__(CI, "_llvm_ctpop_i32"); +}) + +DEF_CALL_HANDLER(llvm_ctpop_i64, { + if (OnlyWebAssembly) { + return CH___default__(CI, "i64_ctpop", 1); + } + Declares.insert("llvm_ctpop_i64"); + return CH___default__(CI, "_llvm_ctpop_i64"); +}) + DEF_CALL_HANDLER(llvm_maxnum_f32, { return CH___default__(CI, "Math_max", 2); }) @@ -590,11 +687,17 @@ DEF_CALL_HANDLER(llvm_maxnum_f64, { }) DEF_CALL_HANDLER(llvm_copysign_f32, { + if (OnlyWebAssembly) { + return CH___default__(CI, "f32_copysign", 2); + } Declares.insert("llvm_copysign_f32"); return CH___default__(CI, "_llvm_copysign_f32", 2); }) DEF_CALL_HANDLER(llvm_copysign_f64, { + if (OnlyWebAssembly) { + return CH___default__(CI, "(f64_copysign)", 2); // XXX add parens as this will be +f64_copysign(...), which triggers +f64 => f64.0. TODO fix regex in emscripten.py + } Declares.insert("llvm_copysign_f64"); return CH___default__(CI, "_llvm_copysign_f64", 2); }) @@ -1526,6 +1629,10 @@ void setupCallHandlers() { SETUP_CALL_HANDLER(bitshift64Shl); SETUP_CALL_HANDLER(llvm_ctlz_i32); SETUP_CALL_HANDLER(llvm_cttz_i32); + SETUP_CALL_HANDLER(llvm_ctlz_i64); + SETUP_CALL_HANDLER(llvm_cttz_i64); + SETUP_CALL_HANDLER(llvm_ctpop_i32); + SETUP_CALL_HANDLER(llvm_ctpop_i64); SETUP_CALL_HANDLER(llvm_maxnum_f32); SETUP_CALL_HANDLER(llvm_maxnum_f64); SETUP_CALL_HANDLER(llvm_copysign_f32); diff --git a/lib/Target/JSBackend/ExpandBigSwitches.cpp b/lib/Target/JSBackend/ExpandBigSwitches.cpp index 59301b72cde..02be915d1d0 100644 --- a/lib/Target/JSBackend/ExpandBigSwitches.cpp +++ b/lib/Target/JSBackend/ExpandBigSwitches.cpp @@ -24,12 +24,6 @@ namespace llvm { -/* - * Find cases where an alloca is used only to load and store a single value, - * even though it is bitcast. Then replace it with a direct alloca of that - * simple type, and avoid the bitcasts. - */ - struct ExpandBigSwitches : public FunctionPass { static char ID; // Pass identification, replacement for typeid ExpandBigSwitches() : FunctionPass(ID) {} diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp index 50214c8edb4..d959102bb61 100644 --- a/lib/Target/JSBackend/JSBackend.cpp +++ b/lib/Target/JSBackend/JSBackend.cpp @@ -148,6 +148,11 @@ WebAssembly("emscripten-wasm", cl::desc("Generate asm.js which will later be compiled to WebAssembly (see emscripten BINARYEN setting)"), cl::init(false)); +static cl::opt +OnlyWebAssembly("emscripten-only-wasm", + cl::desc("Generate code that will only ever be used as WebAssembly, and is not valid JS or asm.js"), + cl::init(false)); + extern "C" void LLVMInitializeJSBackendTarget() { // Register the target. @@ -250,6 +255,7 @@ namespace { int MaxGlobalAlign; int StaticBump; const Instruction* CurrInstruction; + Type* i32; // the type of i32 #include "CallHandlers.h" @@ -363,7 +369,11 @@ namespace { return 'F'; } } else { - return 'i'; + if (OnlyWebAssembly && T->isIntegerTy() && T->getIntegerBitWidth() == 64) { + return 'j'; + } else { + return 'i'; + } } } std::string getFunctionSignature(const FunctionType *F) { @@ -604,6 +614,14 @@ namespace { } } + std::string emitI64Const(uint64_t value) { + return "i64_const(" + itostr(value & uint32_t(-1)) + "," + itostr((value >> 32) & uint32_t(-1)) + ")"; + } + + std::string emitI64Const(APInt i) { + return emitI64Const(i.getZExtValue()); + } + std::string ftostr(const ConstantFP *CFP, AsmCast sign) { const APFloat &flt = CFP->getValueAPF(); @@ -995,6 +1013,7 @@ std::string JSWriter::getCast(const StringRef &s, Type *t, AsmCast sign) { 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(); + case 64: return ("i64(" + s + ")").str(); default: llvm_unreachable("Unsupported integer cast bitwidth"); } } @@ -1114,8 +1133,31 @@ static const char *heapNameToAtomicTypeName(const char *HeapName) std::string JSWriter::getLoad(const Instruction *I, const Value *P, Type *T, unsigned Alignment, char sep) { std::string Assign = getAssign(I); unsigned Bytes = DL->getTypeAllocSize(T); + bool Aligned = Bytes <= Alignment || Alignment == 0; + if (OnlyWebAssembly) { + if (isAbsolute(P)) { + // loads from an absolute constants are either intentional segfaults (int x = *((int*)0)), or code problems + JSWriter::getAssign(I); // ensure the variable is defined, even if it isn't used + return "abort() /* segfault, load from absolute addr */"; + } + if (T->isIntegerTy() || T->isPointerTy()) { + switch (Bytes) { + case 1: return Assign + "load1(" + getValueAsStr(P) + ")"; + case 2: return Assign + "load2(" + getValueAsStr(P) + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 4: return Assign + "load4(" + getValueAsStr(P) + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 8: return Assign + "load8(" + getValueAsStr(P) + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + default: llvm_unreachable("invalid wasm-only int load size"); + } + } else { + switch (Bytes) { + case 4: return Assign + "loadf(" + getValueAsStr(P) + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 8: return Assign + "loadd(" + getValueAsStr(P) + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + default: llvm_unreachable("invalid wasm-only float load size"); + } + } + } std::string text; - if (Bytes <= Alignment || Alignment == 0) { + if (Aligned) { if (EnablePthreads && cast(I)->isVolatile()) { const char *HeapName; std::string Index = getHeapNameAndIndex(P, &HeapName); @@ -1232,8 +1274,29 @@ std::string JSWriter::getLoad(const Instruction *I, const Value *P, Type *T, uns std::string JSWriter::getStore(const Instruction *I, const Value *P, Type *T, const std::string& VS, unsigned Alignment, char sep) { assert(sep == ';'); // FIXME when we need that unsigned Bytes = DL->getTypeAllocSize(T); + bool Aligned = Bytes <= Alignment || Alignment == 0; + if (OnlyWebAssembly) { + if (Alignment == 536870912) { + return "abort() /* segfault */"; + } + if (T->isIntegerTy() || T->isPointerTy()) { + switch (Bytes) { + case 1: return "store1(" + getValueAsStr(P) + "," + VS + ")"; + case 2: return "store2(" + getValueAsStr(P) + "," + VS + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 4: return "store4(" + getValueAsStr(P) + "," + VS + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 8: return "store8(" + getValueAsStr(P) + "," + VS + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + default: llvm_unreachable("invalid wasm-only int load size"); + } + } else { + switch (Bytes) { + case 4: return "storef(" + getValueAsStr(P) + "," + VS + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + case 8: return "stored(" + getValueAsStr(P) + "," + VS + (Aligned ? "" : "," + itostr(Alignment)) + ")"; + default: llvm_unreachable("invalid wasm-only float load size"); + } + } + } std::string text; - if (Bytes <= Alignment || Alignment == 0) { + if (Aligned) { if (EnablePthreads && cast(I)->isVolatile()) { const char *HeapName; std::string Index = getHeapNameAndIndex(P, &HeapName); @@ -1400,7 +1463,7 @@ std::string JSWriter::getConstant(const Constant* CV, AsmCast sign) { // we access linked externs through calls, which we load at the beginning of basic blocks FuncRelocatableExterns.insert(Name); Name = "t$" + Name; - UsedVars[Name] = Type::getInt32Ty(CV->getContext()); + UsedVars[Name] = i32; } return Name; } @@ -1430,13 +1493,21 @@ std::string JSWriter::getConstant(const Constant* CV, AsmCast sign) { 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); + if (!OnlyWebAssembly || CI->getValue().getBitWidth() != 64) { + return CI->getValue().toString(10, sign != ASM_UNSIGNED); + } else { + // i64 constant. emit as 32 bits, 32 bits, for ease of parsing by a JS-style parser + return emitI64Const(CI->getValue()); + } } else if (isa(CV)) { std::string S; if (VectorType *VT = dyn_cast(CV->getType())) { checkVectorType(VT); S = std::string("SIMD_") + SIMDType(VT) + "_splat(" + ensureFloat("0", !VT->getElementType()->isIntegerTy()) + ')'; } else { + if (OnlyWebAssembly && CV->getType()->isIntegerTy() && CV->getType()->getIntegerBitWidth() == 64) { + return "i64(0)"; + } S = CV->getType()->isFloatingPointTy() ? "+0" : "0"; // XXX refactor this if (PreciseF32 && CV->getType()->isFloatTy() && !(sign & ASM_FFI_OUT)) { S = "Math_fround(" + S + ")"; @@ -2258,7 +2329,8 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { assert(I == I->stripPointerCasts()); Type *T = I->getType(); - if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { + if (T->isIntegerTy() && ((!OnlyWebAssembly && T->getIntegerBitWidth() > 32) || + ( OnlyWebAssembly && T->getIntegerBitWidth() > 64))) { errs() << *I << "\n"; report_fatal_error("legalization problem"); } @@ -2309,6 +2381,25 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { case Instruction::AShr:{ Code << getAssignIfNeeded(I); unsigned opcode = Operator::getOpcode(I); + if (OnlyWebAssembly && I->getType()->isIntegerTy() && I->getType()->getIntegerBitWidth() == 64) { + switch (opcode) { + case Instruction::Add: Code << "i64_add(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Sub: Code << "i64_sub(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Mul: Code << "i64_mul(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::UDiv: Code << "i64_udiv(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::SDiv: Code << "i64_sdiv(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::URem: Code << "i64_urem(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::SRem: Code << "i64_srem(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::And: Code << "i64_and(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Or: Code << "i64_or(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Xor: Code << "i64_xor(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::Shl: Code << "i64_shl(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::AShr: Code << "i64_ashr(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case Instruction::LShr: Code << "i64_lshr(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + default: error("bad wasm-i64 binary opcode"); break; + } + break; + } switch (opcode) { case Instruction::Add: Code << getParenCast( getValueAsParenStr(I->getOperand(0)) + @@ -2426,6 +2517,23 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { auto predicate = isa(I) ? (CmpInst::Predicate)cast(I)->getPredicate() : cast(I)->getPredicate(); + if (OnlyWebAssembly && I->getOperand(0)->getType()->isIntegerTy() && I->getOperand(0)->getType()->getIntegerBitWidth() == 64) { + Code << getAssignIfNeeded(I); + switch (predicate) { + case ICmpInst::ICMP_EQ: Code << "i64_eq(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_NE: Code << "i64_ne(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_ULE: Code << "i64_ule(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_SLE: Code << "i64_sle(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_UGE: Code << "i64_uge(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_SGE: Code << "i64_sge(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_ULT: Code << "i64_ult(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_SLT: Code << "i64_slt(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_UGT: Code << "i64_ugt(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + case ICmpInst::ICMP_SGT: Code << "i64_sgt(" << getValueAsStr(I->getOperand(0)) << "," << getValueAsStr(I->getOperand(1)) << ")"; break; + default: llvm_unreachable("Invalid ICmp-64 predicate"); + } + break; + } AsmCast sign = CmpInst::isUnsigned(predicate) ? ASM_UNSIGNED : ASM_SIGNED; Code << getAssignIfNeeded(I) << "(" << getValueAsCastStr(I->getOperand(0), sign) << @@ -2523,7 +2631,7 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { } Type *T = V->getType(); - if (T->isIntegerTy() && T->getIntegerBitWidth() > 32) { + if (T->isIntegerTy() && T->getIntegerBitWidth() > 32 && !OnlyWebAssembly) { errs() << *I << "\n"; report_fatal_error("legalization problem"); } @@ -2568,7 +2676,7 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { ConstantOffset = 0; // Now add the scaled dynamic index. - std::string Mul = getIMul(Index, ConstantInt::get(Type::getInt32Ty(GEP->getContext()), ElementSize)); + std::string Mul = getIMul(Index, ConstantInt::get(i32, ElementSize)); text = text.empty() ? Mul : ("(" + text + " + (" + Mul + ")|0)"); } } @@ -2581,10 +2689,24 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { // handled separately - we push them back into the relooper branchings return; } - case Instruction::PtrToInt: - case Instruction::IntToPtr: + case Instruction::PtrToInt: { + if (OnlyWebAssembly && I->getType()->getIntegerBitWidth() == 64) { + // it is valid in LLVM IR to convert a pointer into an i64, it zexts + Code << getAssignIfNeeded(I) << "i64_zext(" << getValueAsStr(I->getOperand(0)) << ')'; + break; + } Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)); break; + } + case Instruction::IntToPtr: { + if (OnlyWebAssembly && I->getOperand(0)->getType()->getIntegerBitWidth() == 64) { + // it is valid in LLVM IR to convert an i64 into a 32-bit pointer, it truncates + Code << getAssignIfNeeded(I) << "i64_trunc(" << getValueAsStr(I->getOperand(0)) << ')'; + break; + } + Code << getAssignIfNeeded(I) << getValueAsStr(I->getOperand(0)); + break; + } case Instruction::Trunc: case Instruction::ZExt: case Instruction::SExt: @@ -2595,6 +2717,40 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { case Instruction::UIToFP: case Instruction::SIToFP: { Code << getAssignIfNeeded(I); + if (OnlyWebAssembly && + ((I->getType()->isIntegerTy() && I->getType()->getIntegerBitWidth() == 64) || + (I->getOperand(0)->getType()->isIntegerTy() && I->getOperand(0)->getType()->getIntegerBitWidth() == 64))) { + switch (Operator::getOpcode(I)) { + case Instruction::Trunc: { + unsigned outBits = I->getType()->getIntegerBitWidth(); + Code << "i64_trunc(" << getValueAsStr(I->getOperand(0)) << ')'; + if (outBits < 32) { + Code << "&" << utostr(LSBMask(outBits)); + } + break; + } + case Instruction::SExt: { + unsigned inBits = I->getOperand(0)->getType()->getIntegerBitWidth(); + std::string bits = utostr(32 - inBits); + Code << "i64_sext(" << getValueAsStr(I->getOperand(0)); + if (inBits < 32) { + Code << " << " << bits << " >> " << bits; + } + Code << ')'; + break; + } + case Instruction::ZExt: { + Code << "i64_zext(" << getValueAsCastStr(I->getOperand(0), ASM_UNSIGNED) << ')'; + break; + } + case Instruction::SIToFP: Code << (I->getType()->isFloatTy() ? "i64_s2f(" : "i64_s2d(") << getValueAsStr(I->getOperand(0)) << ')'; break; + case Instruction::UIToFP: Code << (I->getType()->isFloatTy() ? "i64_u2f(" : "i64_u2d(") << getValueAsStr(I->getOperand(0)) << ')'; break; + case Instruction::FPToSI: Code << (I->getOperand(0)->getType()->isFloatTy() ? "i64_f2s(" : "i64_d2s(") << getValueAsStr(I->getOperand(0)) << ')'; break; + case Instruction::FPToUI: Code << (I->getOperand(0)->getType()->isFloatTy() ? "i64_f2u(" : "i64_d2u(") << getValueAsStr(I->getOperand(0)) << ')'; break; + default: llvm_unreachable("Unreachable-i64"); + } + break; + } switch (Operator::getOpcode(I)) { case Instruction::Trunc: { //unsigned inBits = V->getType()->getIntegerBitWidth(); @@ -2640,9 +2796,25 @@ void JSWriter::generateExpression(const User *I, raw_string_ostream& Code) { Type *OutType = I->getType(); std::string V = getValueAsStr(I->getOperand(0)); if (InType->isIntegerTy() && OutType->isFloatingPointTy()) { + if (OnlyWebAssembly) { + if (InType->getIntegerBitWidth() == 64) { + Code << "i64_bc2d(" << V << ')'; + } else { + Code << "i32_bc2f(" << V << ')'; + } + break; + } assert(InType->getIntegerBitWidth() == 32); Code << "(HEAP32[tempDoublePtr>>2]=" << V << "," << getCast("HEAPF32[tempDoublePtr>>2]", Type::getFloatTy(TheModule->getContext())) << ")"; } else if (OutType->isIntegerTy() && InType->isFloatingPointTy()) { + if (OnlyWebAssembly) { + if (OutType->getIntegerBitWidth() == 64) { + Code << "i64_bc2i(" << V << ')'; + } else { + Code << "i32_bc2i(" << V << ')'; + } + break; + } assert(OutType->getIntegerBitWidth() == 32); Code << "(HEAPF32[tempDoublePtr>>2]=" << V << "," "HEAP32[tempDoublePtr>>2]|0)"; } else { @@ -2851,7 +3023,13 @@ void JSWriter::printFunctionBody(const Function *F) { BlockCondMap BlocksToConditions; for (SwitchInst::ConstCaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { const BasicBlock *BB = i.getCaseSuccessor(); - std::string Curr = i.getCaseValue()->getValue().toString(10, true); + APInt CaseValue = i.getCaseValue()->getValue(); + std::string Curr; + if (CaseValue.getBitWidth() == 64) { + Curr = emitI64Const(CaseValue); + } else { + Curr = CaseValue.toString(10, true); + } std::string Condition; if (UseSwitch) { Condition = "case " + Curr + ": "; @@ -2880,12 +3058,12 @@ void JSWriter::printFunctionBody(const Function *F) { R.Render(); // Emit local variables - UsedVars["sp"] = Type::getInt32Ty(F->getContext()); + UsedVars["sp"] = i32; unsigned MaxAlignment = Allocas.getMaxAlignment(); if (MaxAlignment > STACK_ALIGN) { - UsedVars["sp_a"] = Type::getInt32Ty(F->getContext()); + UsedVars["sp_a"] = i32; } - UsedVars["label"] = Type::getInt32Ty(F->getContext()); + UsedVars["label"] = i32; if (!UsedVars.empty()) { unsigned Count = 0; for (VarMap::const_iterator VI = UsedVars.begin(); VI != UsedVars.end(); ++VI) { @@ -2903,9 +3081,16 @@ void JSWriter::printFunctionBody(const Function *F) { default: llvm_unreachable("unsupported variable initializer type"); case Type::PointerTyID: - case Type::IntegerTyID: Out << "0"; break; + case Type::IntegerTyID: + if (VI->second->getIntegerBitWidth() == 64) { + assert(OnlyWebAssembly); + Out << "i64()"; + } else { + Out << "0"; + } + break; case Type::FloatTyID: if (PreciseF32) { Out << "Math_fround(0)"; @@ -2948,7 +3133,7 @@ void JSWriter::printFunctionBody(const Function *F) { } // Emit stack entry - Out << " " << getAdHocAssign("sp", Type::getInt32Ty(F->getContext())) << "STACKTOP;"; + Out << " " << getAdHocAssign("sp", i32) << "STACKTOP;"; if (uint64_t FrameSize = Allocas.getFrameSize()) { if (MaxAlignment > STACK_ALIGN) { // We must align this entire stack frame to something higher than the default @@ -3913,6 +4098,7 @@ void JSWriter::printModule(const std::string& fname, bool JSWriter::runOnModule(Module &M) { TheModule = &M; DL = &M.getDataLayout(); + i32 = Type::getInt32Ty(M.getContext()); // sanity checks on options assert(Relocatable ? GlobalBase == 0 : true); @@ -4072,7 +4258,14 @@ bool JSTargetMachine::addPassesToEmitFile( // end PNaCl legalization PM.add(createExpandInsertExtractElementPass()); - PM.add(createExpandI64Pass()); + + if (!OnlyWebAssembly) { + // if only wasm, then we can emit i64s, otherwise they must be lowered + PM.add(createExpandI64Pass()); + } else { + // only wasm, and for now no atomics there, so just lower them out + PM.add(createLowerAtomicPass()); + } CodeGenOpt::Level OptLevel = getOptLevel();