diff --git a/lib/Target/JSBackend/CallHandlers.h b/lib/Target/JSBackend/CallHandlers.h index 10ca64a9e82..4ffb43366fd 100644 --- a/lib/Target/JSBackend/CallHandlers.h +++ b/lib/Target/JSBackend/CallHandlers.h @@ -360,58 +360,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 +394,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 +422,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"; }) diff --git a/lib/Target/JSBackend/JSBackend.cpp b/lib/Target/JSBackend/JSBackend.cpp index 28b3c46e843..d959102bb61 100644 --- a/lib/Target/JSBackend/JSBackend.cpp +++ b/lib/Target/JSBackend/JSBackend.cpp @@ -614,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(); @@ -1125,11 +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); - if (OnlyWebAssembly && Bytes == 8 && T->isIntegerTy()) { - return Assign + "i64_load(" + getValueAsStr(P) + "," + itostr(Alignment) + ")"; + 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); @@ -1246,11 +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); - if (OnlyWebAssembly && Bytes == 8 && T->isIntegerTy()) { - return "i64_store(" + getValueAsStr(P) + "," + VS + "," + itostr(Alignment) + ")"; + 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); @@ -1402,11 +1448,6 @@ std::string JSWriter::getPtrUse(const Value* Ptr) { return std::string(HeapName) + '[' + Index + ']'; } -static std::string emitI64Const(APInt i) { - auto value = i.getZExtValue(); - return "i64_const(" + itostr(value & uint32_t(-1)) + "," + itostr((value >> 32) & uint32_t(-1)) + ")"; -} - std::string JSWriter::getConstant(const Constant* CV, AsmCast sign) { if (isa(CV)) return "0"; @@ -2755,15 +2796,23 @@ 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 && InType->getIntegerBitWidth() == 64) { - Code << "i64_bc2d(" << V << ')'; + 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 && OutType->getIntegerBitWidth() == 64) { - Code << "i64_bc2i(" << V << ')'; + if (OnlyWebAssembly) { + if (OutType->getIntegerBitWidth() == 64) { + Code << "i64_bc2i(" << V << ')'; + } else { + Code << "i32_bc2i(" << V << ')'; + } break; } assert(OutType->getIntegerBitWidth() == 32);