Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 116 additions & 57 deletions lib/Target/JSBackend/CallHandlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConstantInt>(CI->getOperand(3));
if (AlignInt) {
ConstantInt *LenInt = dyn_cast<ConstantInt>(CI->getOperand(2));
if (LenInt) {
ConstantInt *ValInt = dyn_cast<ConstantInt>(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) {
Expand All @@ -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;
Expand All @@ -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<ConstantInt>(CI->getOperand(3));
if (AlignInt) {
ConstantInt *LenInt = dyn_cast<ConstantInt>(CI->getOperand(2));
if (LenInt) {
ConstantInt *ValInt = dyn_cast<ConstantInt>(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";
})
Expand Down
79 changes: 64 additions & 15 deletions lib/Target/JSBackend/JSBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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<LoadInst>(I)->isVolatile()) {
const char *HeapName;
std::string Index = getHeapNameAndIndex(P, &HeapName);
Expand Down Expand Up @@ -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<StoreInst>(I)->isVolatile()) {
const char *HeapName;
std::string Index = getHeapNameAndIndex(P, &HeapName);
Expand Down Expand Up @@ -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<ConstantPointerNull>(CV)) return "0";

Expand Down Expand Up @@ -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);
Expand Down