diff --git a/llvm/include/llvm/Cheerp/PointerAnalyzer.h b/llvm/include/llvm/Cheerp/PointerAnalyzer.h index 9d624abae731..274f44ebcc15 100644 --- a/llvm/include/llvm/Cheerp/PointerAnalyzer.h +++ b/llvm/include/llvm/Cheerp/PointerAnalyzer.h @@ -14,6 +14,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/Value.h" #include "llvm/IR/Function.h" #include "llvm/IR/Constants.h" @@ -281,6 +282,8 @@ class PointerAnalyzer POINTER_KIND getPointerKindForStoredType( llvm::Type * pointerType ) const; POINTER_KIND getPointerKindForMemberPointer( const TypeAndIndex& baseAndIndex ) const; POINTER_KIND getPointerKindForMember( const TypeAndIndex& baseAndIndex ) const; + POINTER_KIND getPointerKindForLoad(const llvm::LoadInst* v) const; + POINTER_KIND getPointerKindForStore(const llvm::StoreInst* v) const; POINTER_KIND getPointerKindForArgumentTypeAndIndex( const TypeAndIndex& argTypeAndIndex ) const; POINTER_KIND getPointerKindForArgument( const llvm::Argument* A ) const; POINTER_KIND getPointerKindForJSExportedType (llvm::Type* pointerType) const; @@ -288,6 +291,8 @@ class PointerAnalyzer static TypeAndIndex getBaseStructAndIndexFromGEP( const llvm::Value* v ); const llvm::ConstantInt* getConstantOffsetForPointer( const llvm::Value* ) const; const llvm::ConstantInt* getConstantOffsetForMember( const TypeAndIndex& baseAndIndex ) const; + const llvm::ConstantInt* getConstantOffsetForLoad(const llvm::LoadInst* v) const; + const llvm::ConstantInt* getConstantOffsetForStore(const llvm::StoreInst* v) const; /** * Functions to manually invalidate the cache diff --git a/llvm/include/llvm/Cheerp/Writer.h b/llvm/include/llvm/Cheerp/Writer.h index 02172f2f67bd..92d17767caca 100644 --- a/llvm/include/llvm/Cheerp/Writer.h +++ b/llvm/include/llvm/Cheerp/Writer.h @@ -493,6 +493,7 @@ class CheerpWriter final : public CheerpBaseWriter COMPILE_INSTRUCTION_FEEDBACK compileNotInlineableInstruction(const llvm::Instruction& I, PARENT_PRIORITY parentPrio); COMPILE_INSTRUCTION_FEEDBACK compileInlineableInstruction(const llvm::Instruction& I, PARENT_PRIORITY parentPrio); COMPILE_INSTRUCTION_FEEDBACK compileCallInstruction(const llvm::CallBase& I, PARENT_PRIORITY parentPrio); + void compileElem(const llvm::Value* ptrOp, llvm::Type* Ty, llvm::StructType* STy, POINTER_KIND valKind, POINTER_KIND ptrKind, POINTER_KIND memKind, bool isOffset, uint32_t structElemIdx); void compileLoadElem(const llvm::LoadInst& li, llvm::Type* Ty, llvm::StructType* STy, POINTER_KIND ptrKind, POINTER_KIND loadKind, bool isOffset, Registerize::REGISTER_KIND regKind, uint32_t structElemIdx, bool asmjs, PARENT_PRIORITY parentPrio); void compileLoad(const llvm::LoadInst& li, PARENT_PRIORITY parentPrio); void compileStoreElem(const llvm::StoreInst& si, llvm::Type* Ty, llvm::StructType* STy, POINTER_KIND ptrKind, POINTER_KIND storedKind, bool isOffset, Registerize::REGISTER_KIND regKind, uint32_t structElemIdx, uint32_t elemIdx, bool asmjs); diff --git a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp index 723c066ea340..79914e1f20e1 100644 --- a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp @@ -1723,6 +1723,16 @@ POINTER_KIND PointerAnalyzer::getPointerKindForMember(const TypeAndIndex& baseAn return getPointerKindForMemberImpl(baseAndIndex, PACache); } +POINTER_KIND PointerAnalyzer::getPointerKindForLoad(const llvm::LoadInst* v) const +{ + return getPointerKind(v); +} + +POINTER_KIND PointerAnalyzer::getPointerKindForStore(const llvm::StoreInst* v) const +{ + return getPointerKind(v); +} + TypeAndIndex PointerAnalyzer::getBaseStructAndIndexFromGEP(const Value* p) { if(!isGEP(p)) @@ -1826,6 +1836,16 @@ const llvm::ConstantInt* PointerAnalyzer::getConstantOffsetForMember( const Type return ret.getPointerOffset(); } +const llvm::ConstantInt* PointerAnalyzer::getConstantOffsetForLoad(const llvm::LoadInst* v) const +{ + return getConstantOffsetForPointer(v); +} + +const llvm::ConstantInt* PointerAnalyzer::getConstantOffsetForStore(const llvm::StoreInst* v) const +{ + return getConstantOffsetForPointer(v); +} + void PointerAnalyzer::invalidate(const Value * v) { assert(status == MODIFIABLE); diff --git a/llvm/lib/CheerpUtils/Utility.cpp b/llvm/lib/CheerpUtils/Utility.cpp index 1a28cd5c90f2..59d309b9220e 100644 --- a/llvm/lib/CheerpUtils/Utility.cpp +++ b/llvm/lib/CheerpUtils/Utility.cpp @@ -1478,6 +1478,10 @@ bool InstElemIterator::isTwoElems(const llvm::Instruction* I, llvm::Type* Ty, in { if(!Ty->isStructTy()) { + // avoid calling getPointerKind for store instruction + if (const StoreInst* SI = dyn_cast(I)) + return Ty->isPointerTy() && PA.getPointerKindForStore(SI) == SPLIT_REGULAR && !PA.getConstantOffsetForStore(SI); + return Ty->isPointerTy() && PA.getPointerKind(I) == SPLIT_REGULAR && !PA.getConstantOffsetForPointer(I); } auto* STy = llvm::cast(Ty); diff --git a/llvm/lib/CheerpWriter/CheerpWriter.cpp b/llvm/lib/CheerpWriter/CheerpWriter.cpp index c3f2d9c61bf7..2ab0b8ef31d5 100644 --- a/llvm/lib/CheerpWriter/CheerpWriter.cpp +++ b/llvm/lib/CheerpWriter/CheerpWriter.cpp @@ -4531,29 +4531,9 @@ CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::compileInlineableInstru void CheerpWriter::compileLoad(const LoadInst& li, PARENT_PRIORITY parentPrio) { - auto* Ty = li.getType(); const Value* ptrOp = li.getPointerOperand(); bool asmjs = currentFun->getSection()==StringRef("asmjs"); POINTER_KIND ptrKind = PA.getPointerKind(ptrOp); - bool needsCheckBounds = false; - if (checkBounds) - { - if(ptrKind == REGULAR || ptrKind == SPLIT_REGULAR) - { - needsCheckBounds = true; - stream<<"("; - compileCheckBounds(ptrOp); - stream<<","; - } - else if(ptrKind == COMPLETE_OBJECT && isGEP(ptrOp)) - { - needsCheckBounds = true; - bool needsOffset = !li.use_empty() && Ty->isPointerTy() && PA.getPointerKindAssert(&li) == SPLIT_REGULAR && !PA.getConstantOffsetForPointer(&li); - stream<<"("; - compileCheckMemberExists(ptrOp, needsOffset); - stream<<","; - } - } for(const auto& ie: getInstElems(&li, PA)) { if(ie.totalIdx != 0) @@ -4577,15 +4557,10 @@ void CheerpWriter::compileLoad(const LoadInst& li, PARENT_PRIORITY parentPrio) } else if(Ty->isPointerTy()) { - elemPtrKind = PA.getPointerKind(&li); + elemPtrKind = PA.getPointerKindForLoad(&li); } bool isOffset = ie.ptrIdx == 1; compileLoadElem(li, Ty, STy, ptrKind, elemPtrKind, isOffset, elemRegKind, ie.structIdx, asmjs, parentPrio); - if(needsCheckBounds) - { - needsCheckBounds = false; - stream << ')'; - } } } @@ -4726,13 +4701,18 @@ void CheerpWriter::compileLoadElem(const LoadInst& li, Type* Ty, StructType* STy } else { - compileCompleteObject(ptrOp); - if(STy) + POINTER_KIND valKind = li.getType()->isPointerTy() ? PA.getPointerKind(&li) : COMPLETE_OBJECT; + + if (valKind == COMPLETE_OBJECT && (loadKind == REGULAR || loadKind == SPLIT_REGULAR)) { - compileAccessToElement(STy, {ConstantInt::get(IntegerType::get(Ty->getContext(), 32), structElemIdx)}, true); + assert(!isOffset); + compileElem(ptrOp, Ty, STy, COMPLETE_OBJECT, ptrKind, loadKind, false, structElemIdx); + stream << "["; + compileElem(ptrOp, Ty, STy, COMPLETE_OBJECT, ptrKind, loadKind, true, structElemIdx); + stream << "]"; } - if(isOffset) - stream << 'o'; + else + compileElem(ptrOp, Ty, STy, valKind, ptrKind, loadKind, isOffset, structElemIdx); } if(regKind==Registerize::INTEGER && needsIntCoercion(parentPrio)) { @@ -4755,20 +4735,6 @@ void CheerpWriter::compileStore(const StoreInst& si) assert(ptrKind != CONSTANT); auto* Ty = valOp->getType(); - if (checkBounds) - { - if(ptrKind == REGULAR || ptrKind == SPLIT_REGULAR) - { - compileCheckBounds(ptrOp); - stream<<","; - } - else if(ptrKind == COMPLETE_OBJECT && isGEP(ptrOp)) - { - bool needsOffset = Ty->isPointerTy() && PA.getPointerKindAssert(&si) == SPLIT_REGULAR && !PA.getConstantOffsetForPointer(&si); - compileCheckMemberExists(ptrOp, needsOffset); - stream<<","; - } - } StructType* STy = dyn_cast(Ty); for(const auto& ie: getInstElems(&si, PA)) { @@ -4789,7 +4755,7 @@ void CheerpWriter::compileStore(const StoreInst& si) } else if(Ty->isPointerTy()) { - elemPtrKind = PA.getPointerKind(&si); + elemPtrKind = PA.getPointerKindForStore(&si); } bool isOffset = ie.ptrIdx == 1; compileStoreElem(si, Ty, STy, ptrKind, elemPtrKind, isOffset, elemRegKind, ie.totalIdx, ie.structIdx, asmjs); @@ -4876,13 +4842,7 @@ void CheerpWriter::compileStoreElem(const StoreInst& si, Type* Ty, StructType* S } else { - compileCompleteObject(ptrOp); - if(STy) - { - compileAccessToElement(STy, {ConstantInt::get(IntegerType::get(Ty->getContext(), 32), structElemIdx)}, true); - } - if(isOffset) - stream << 'o'; + compileElem(ptrOp, Ty, STy, storedKind, ptrKind, storedKind, isOffset, structElemIdx); } stream << '='; @@ -4895,7 +4855,7 @@ void CheerpWriter::compileStoreElem(const StoreInst& si, Type* Ty, StructType* S if(Ty->isPointerTy()) { assert(storedKind != CONSTANT); - bool hasConstantOffset = PA.getConstantOffsetForPointer(&si); + bool hasConstantOffset = PA.getConstantOffsetForStore(&si); if(storedKind==SPLIT_REGULAR || ((storedKind == REGULAR || storedKind == BYTE_LAYOUT) && hasConstantOffset)) { if(isOffset) @@ -4932,6 +4892,27 @@ void CheerpWriter::compileStoreElem(const StoreInst& si, Type* Ty, StructType* S } } +void CheerpWriter::compileElem(const Value* ptrOp, llvm::Type* Ty, llvm::StructType* STy, POINTER_KIND valKind, POINTER_KIND ptrKind, POINTER_KIND memKind, bool isOffset, uint32_t structElemIdx) +{ + if(checkBounds && (ptrKind == REGULAR || ptrKind == SPLIT_REGULAR)) + compileCheckBounds(ptrOp); + else if(checkBounds && ptrKind == COMPLETE_OBJECT && isGEP(ptrOp)) + compileCheckMemberExists(ptrOp, isOffset && memKind != REGULAR); + else + compileCompleteObject(ptrOp); + + if(STy) + compileAccessToElement(STy, {ConstantInt::get(IntegerType::get(Ty->getContext(), 32), structElemIdx)}, true); + + if(valKind != REGULAR && memKind != COMPLETE_OBJECT) + { + if(isOffset) + stream << (memKind == REGULAR ? ".o" : "o"); + else + stream << (memKind == REGULAR ? ".d" : ""); + } +} + void CheerpWriter::compileAtomicRMW(const AtomicRMWInst& ai, PARENT_PRIORITY parentPrio) { if (parentPrio > BIT_OR) @@ -6252,7 +6233,7 @@ void CheerpWriter::compileBuiltins(bool asmjs) void CheerpWriter::compileCheckBoundsHelper() { - stream << "function checkBounds(arr,offs){if(offs>=arr.length || offs<0) throw new Error('OutOfBounds');}" << NewLine; + stream << "function checkBounds(arr,offs){if(offs>=arr.length || offs<0) throw new Error('OutOfBounds');return arr;}" << NewLine; } void CheerpWriter::compileCheckBounds(const Value* p) @@ -6261,12 +6242,14 @@ void CheerpWriter::compileCheckBounds(const Value* p) compilePointerBase(p); stream<<","; compilePointerOffset(p,LOWEST); - stream<<")"; + stream<<")["; + compilePointerOffset(p,LOWEST); + stream<<"]"; } void CheerpWriter::compileCheckMemberExistsHelper() { - stream << "function checkMemberExists(obj, member){if(!(member in obj)) throw new Error('MemberDoesNotExist');}" << NewLine; + stream << "function checkMemberExists(obj, member){if(!(member in obj)) throw new Error('MemberDoesNotExist');return obj;}" << NewLine; } void CheerpWriter::compileCheckMemberExists(const Value* p, bool needsOffset) @@ -6293,17 +6276,24 @@ void CheerpWriter::compileCheckMemberExists(const Value* p, bool needsOffset) { const APInt& index = cast(lastOperand)->getUniqueInteger(); uint64_t idxVal = index.getLimitedValue(); + char prefixChar = types.getPrefixCharForMember(PA, containerStructType, idxVal); stream << "'"; - stream << types.getPrefixCharForMember(PA, containerStructType, idxVal) << idxVal; + stream << prefixChar << idxVal; if (needsOffset) stream << "o"; - stream << "'"; + stream << "')."; + stream << prefixChar << idxVal; + if(types.useWrapperArrayForMember(PA, containerStructType, idxVal)) + stream << "[0]"; } else if (dyn_cast(basePointedType)) + { compileOperand(lastOperand, LOWEST); - - stream << ")"; + stream << ")["; + compileOperand(lastOperand, LOWEST); + stream << "]"; + } } void CheerpWriter::compileCheckBoundsAsmJSHelper()