Merge pull request #316 from erozenfeld/StoreElem1

Implement storing of array elements.
This commit is contained in:
Eugene Rozenfeld 2015-03-15 21:50:37 -07:00
Родитель 9fb611f51b 2e1f163f8c
Коммит f6100e53ba
132 изменённых файлов: 558760 добавлений и 23649 удалений

Просмотреть файл

@ -2220,9 +2220,11 @@ public:
virtual void storeArg(uint32_t LocOrdinal, IRNode *Arg1,
ReaderAlignType Alignment, bool IsVolatile) = 0;
virtual void storeElem(ReaderBaseNS::StElemOpcode Opcode,
CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *Arg1,
IRNode *Arg2, IRNode *Arg3) = 0;
virtual void storeElemRefAny(IRNode *Value, IRNode *Index, IRNode *Obj);
CORINFO_RESOLVED_TOKEN *ResolvedToken,
IRNode *ValueToStore, IRNode *Index,
IRNode *Array) = 0;
virtual void storeElemRefAny(IRNode *ValueToStore, IRNode *Index,
IRNode *Array);
virtual void storeField(CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *Arg1,
IRNode *Arg2, ReaderAlignType Alignment,
bool IsVolatile) = 0;

Просмотреть файл

@ -418,10 +418,8 @@ public:
void storeArg(uint32_t ArgOrdinal, IRNode *Arg1, ReaderAlignType Alignment,
bool IsVolatile) override;
void storeElem(ReaderBaseNS::StElemOpcode Opcode,
CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *Arg1,
IRNode *Arg2, IRNode *Arg3) override {
throw NotYetImplementedException("storeElem");
};
CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *ValueToStore,
IRNode *Index, IRNode *Array) override;
void storeField(CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *Arg1,
IRNode *Arg2, ReaderAlignType Alignment,
@ -826,10 +824,10 @@ private:
/// Convert node to the desired type.
/// May reinterpret, truncate, or extend as needed.
/// \p Type - Desired type
/// \p Node - Value to be converted
/// \p IsSigned - Perform sign extension if necessary, otherwise
/// integral values are zero extended
/// \param Type Desired type
/// \param Node Value to be converted
/// \param IsSigned Perform sign extension if necessary, otherwise
/// integral values are zero extended
IRNode *convert(llvm::Type *Type, llvm::Value *Node, bool IsSigned);
llvm::Type *binaryOpType(llvm::Type *Type1, llvm::Type *Type2);
@ -849,14 +847,14 @@ private:
/// Generate instructions for loading value of the specified type at the
/// specified address.
///
/// \p Address - Address to load from.
/// \p Ty - llvm type of the value to load.
/// \p CorType - CorInfoType of the value to load.
/// \p ResolvedToken - Resolved token corresponding to the type of the value
/// to load.
/// \p AlignmentPrefix - Alignment of the value.
/// \p IsVolatile - true if the load is volatile.
/// \p AddressMayBeNull - true if the address may be null.
/// \param Address Address to load from.
/// \param Ty llvm type of the value to load.
/// \param CorType CorInfoType of the value to load.
/// \param ResolvedToken Resolved token corresponding to the type of the value
/// to load.
/// \param AlignmentPrefix Alignment of the value.
/// \param IsVolatile true iff the load is volatile.
/// \param AddressMayBeNull true iff the address may be null.
/// \returns Value at the specified address.
IRNode *loadAtAddress(IRNode *Address, llvm::Type *Ty, CorInfoType CorType,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
@ -872,22 +870,49 @@ private:
IsVolatile, false);
}
/// Generate instructions for storing value of the specified type at the
/// specified address.
///
/// \param Address Address to store to.
/// \param ValueToStore Value to store.
/// \param Ty llvm type of the value to store.
/// \param ResolvedToken Resolved token corresponding to the type of the value
/// to store.
/// \param AlignmentPrefix Alignment of the value.
/// \param IsVolatile true iff the store is volatile.
/// \param IsField true iff this is a field address.
/// \param AddressMayBeNull true iff the address may be null.
void storeAtAddress(IRNode *Address, IRNode *ValueToStore, llvm::Type *Ty,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
ReaderAlignType AlignmentPrefix, bool IsVolatile,
bool IsField, bool AddressMayBeNull);
void storeAtAddressNonNull(IRNode *Address, IRNode *ValueToStore,
llvm::Type *Ty,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
ReaderAlignType AlignmentPrefix, bool IsVolatile,
bool IsField) {
return storeAtAddress(Address, ValueToStore, Ty, ResolvedToken,
AlignmentPrefix, IsVolatile, IsField, false);
}
void classifyCmpType(llvm::Type *Ty, uint32_t &Size, bool &IsPointer,
bool &IsFloat);
/// Generate a call to the throw helper if the condition is met.
///
/// \p Condition - Condition that will trigger the throw.
/// \p HelperId - Id of the throw-helper.
/// \p ThrowBlockName - Name of the basic block that will contain the throw.
/// \param Condition Condition that will trigger the throw.
/// \param HelperId Id of the throw-helper.
/// \param ThrowBlockName Name of the basic block that will contain the throw.
void genConditionalThrow(llvm::Value *Condition, CorInfoHelpFunc HelperId,
const llvm::Twine &ThrowBlockName);
/// Generate array bounds check.
///
/// \p Array - Array to be accessed.
/// \p Index - Index to be accessed.
/// \returns - The input array.
/// \param Array Array to be accessed.
/// \param Index Index to be accessed.
/// \returns The input array.
IRNode *genBoundsCheck(IRNode *Array, IRNode *Index);
uint32_t size(CorInfoType CorType);
@ -897,17 +922,17 @@ private:
/// Convert a result to a valid stack type,
/// generally by either reinterpretation or extension.
/// \p Node - Value to be converted
/// \p CorType - additional information needed to determine if
/// Node's type is signed or unsigned
/// \param Node Value to be converted
/// \param CorType additional information needed to determine if
/// Node's type is signed or unsigned
IRNode *convertToStackType(IRNode *Node, CorInfoType CorType);
/// Convert a result from a stack type to the desired type,
/// generally by either reinterpretation or truncation.
/// \p Node - Value to be converted
/// \p CorType - additional information needed to determine if
/// ResultTy is signed or unsigned
/// \p ResultTy - Desired type
/// \param Node Value to be converted
/// \param CorType additional information needed to determine if
/// ResultTy is signed or unsigned
/// \param ResultTy - Desired type
IRNode *convertFromStackType(IRNode *Node, CorInfoType CorType,
llvm::Type *ResultTy);
@ -916,7 +941,7 @@ private:
/// Create a new temporary variable that can be
/// used anywhere within the method.
///
/// \p Ty - Type for the new variable.
/// \param Ty Type for the new variable.
/// \returns Instruction establishing the variable's location.
llvm::Instruction *createTemporary(llvm::Type *Ty);
@ -948,13 +973,33 @@ private:
/// Get address of the array element.
///
/// \p Array - Array that the element belongs to.
/// \p Index - Index of the element.
/// \p ElementTy - Type of the element.
/// \param Array Array that the element belongs to.
/// \param Index Index of the element.
/// \param ElementTy Type of the element.
/// \returns Value representing the address of the element.
llvm::Value *genArrayElemAddress(IRNode *Array, IRNode *Index,
llvm::Type *ElementTy);
/// Convert ReaderAlignType to byte alighnment to byte alignment.
///
/// \param ReaderAlignment Reader alignment.
/// \returns Alignment in bytes.
uint32_t convertReaderAlignment(ReaderAlignType ReaderAlignment);
/// Get array element type.
///
/// \param Array Array node.
/// \param ResolvedToken Resolved token from ldelem or stelem instruction.
/// \param CorInfoType [IN/OUT] Type of the element (will be resolved for
/// CORINFO_TYPE_UNDEF).
/// \param Alignment - [IN/OUT] Alignment that will be updated for value
/// classes.
/// \returns Array element type.
llvm::Type *getArrayElementType(IRNode *Array,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
CorInfoType *CorType,
ReaderAlignType *Alignment);
private:
LLILCJitContext *JitContext;
llvm::Function *Function;

Просмотреть файл

@ -553,6 +553,14 @@ IRNode *GenIR::instParam() {
return (IRNode *)TypeParameter;
}
// Convert ReaderAlignType to byte alighnment
uint32_t GenIR::convertReaderAlignment(ReaderAlignType ReaderAlignment) {
uint32_t Result = (ReaderAlignment == Reader_AlignNatural)
? TargetPointerSizeInBits / 8
: ReaderAlignment;
return Result;
}
#pragma endregion
#pragma region DIAGNOSTICS
@ -2547,9 +2555,7 @@ IRNode *GenIR::loadAtAddress(IRNode *Address, Type *Ty, CorInfoType CorType,
AddressMayBeNull);
} else {
LoadInst *LoadInst = makeLoad(Address, IsVolatile, AddressMayBeNull);
uint32_t Align = (AlignmentPrefix == Reader_AlignNatural)
? TargetPointerSizeInBits / 8
: AlignmentPrefix;
uint32_t Align = convertReaderAlignment(AlignmentPrefix);
LoadInst->setAlignment(Align);
IRNode *Result = convertToStackType((IRNode *)LoadInst, CorType);
@ -2558,6 +2564,25 @@ IRNode *GenIR::loadAtAddress(IRNode *Address, Type *Ty, CorInfoType CorType,
}
}
// Generate instructions for storing value of the specified type at the
// specified address.
void GenIR::storeAtAddress(IRNode *Address, IRNode *ValueToStore, Type *Ty,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
ReaderAlignType Alignment, bool IsVolatile,
bool IsField, bool AddressMayBeNull) {
// We do things differently based on whether the field is a value class.
if (Ty->isStructTy()) {
storeObj(ResolvedToken, ValueToStore, Address, Alignment, IsVolatile,
IsField, AddressMayBeNull);
}
else {
StoreInst *StoreInst = makeStore(ValueToStore, Address, IsVolatile,
AddressMayBeNull);
uint32_t Align = convertReaderAlignment(Alignment);
StoreInst->setAlignment(Align);
}
}
void GenIR::storeField(CORINFO_RESOLVED_TOKEN *FieldToken, IRNode *ValueToStore,
IRNode *Object, ReaderAlignType Alignment,
bool IsVolatile) {
@ -2620,21 +2645,9 @@ void GenIR::storeField(CORINFO_RESOLVED_TOKEN *FieldToken, IRNode *ValueToStore,
return;
}
// We do things differently based on whether the field is a value class.
if (!IsStructTy) {
makeStore(ValueToStore, Address, IsVolatile, !NullCheckBeforeStore);
return;
} else {
// The WVM lowerer cannot handle multi-byte indirs whose base pointer
// is the address of a field.
if (FieldCorType == CORINFO_TYPE_VALUECLASS ||
FieldCorType == CORINFO_TYPE_REFANY) {
Alignment = getMinimumClassAlignment(FieldClassHandle, Alignment);
}
storeObj(FieldToken, ValueToStore, Address, Alignment, IsVolatile, true,
!NullCheckBeforeStore);
return;
}
bool IsField = true;
return storeAtAddress(Address, ValueToStore, FieldTy, FieldToken, Alignment,
IsVolatile, IsField, !NullCheckBeforeStore);
}
void GenIR::storePrimitiveType(IRNode *Value, IRNode *Addr,
@ -2807,32 +2820,15 @@ IRNode *GenIR::loadElem(ReaderBaseNS::LdElemOpcode Opcode,
ASSERTNR(Opcode >= ReaderBaseNS::LdelemI1 &&
Opcode < ReaderBaseNS::LastLdelemOpcode);
CORINFO_CLASS_HANDLE ClassHandle = nullptr;
Type *ElementTy = nullptr;
CorInfoType CorType = Map[Opcode];
ReaderAlignType Alignment = Reader_AlignNatural;
if (CorType == CorInfoType::CORINFO_TYPE_CLASS) {
PointerType *Ty = cast<PointerType>(Array->getType());
StructType *ReferentTy = cast<StructType>(Ty->getPointerElementType());
unsigned int NumElements = ReferentTy->getNumElements();
ArrayType *ArrayTy =
cast<ArrayType>(ReferentTy->getElementType(NumElements - 1));
ElementTy = ArrayTy->getElementType();
} else {
// The type is either specified via token (for ldelem) or is primitive.
if (Opcode == ReaderBaseNS::Ldelem) {
ASSERTNR(ResolvedToken != nullptr);
ClassHandle = ResolvedToken->hClass;
CorType = JitContext->JitInfo->asCorInfoType(ClassHandle);
if (CorType == CorInfoType::CORINFO_TYPE_VALUECLASS) {
Alignment = getMinimumClassAlignment(ClassHandle, Reader_AlignNatural);
}
}
ASSERTNR(CorType != CorInfoType::CORINFO_TYPE_UNDEF);
ElementTy = getType(CorType, ClassHandle);
// ResolvedToken is only valid for ldelem.
if (Opcode != ReaderBaseNS::Ldelem) {
ResolvedToken = nullptr;
}
Type *ElementTy = getArrayElementType(Array, ResolvedToken, &CorType,
&Alignment);
Value *ElementAddress = genArrayElemAddress(Array, Index, ElementTy);
bool IsVolatile = false;
@ -2861,6 +2857,95 @@ IRNode *GenIR::loadElemA(CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *Index,
return (IRNode *)genArrayElemAddress(Array, Index, ElementTy);
}
void GenIR::storeElem(ReaderBaseNS::StElemOpcode Opcode,
CORINFO_RESOLVED_TOKEN *ResolvedToken, IRNode *ValueToStore, IRNode *Index,
IRNode *Array) {
static const CorInfoType Map[ReaderBaseNS::LastStelemOpcode] = {
CorInfoType::CORINFO_TYPE_NATIVEINT, // STELEM_I
CorInfoType::CORINFO_TYPE_BYTE, // STELEM_I1
CorInfoType::CORINFO_TYPE_SHORT, // STELEM_I2
CorInfoType::CORINFO_TYPE_INT, // STELEM_I4
CorInfoType::CORINFO_TYPE_LONG, // STELEM_I8
CorInfoType::CORINFO_TYPE_FLOAT, // STELEM_R4
CorInfoType::CORINFO_TYPE_DOUBLE, // STELEM_R8
CorInfoType::CORINFO_TYPE_CLASS, // STELEM_REF
CorInfoType::CORINFO_TYPE_UNDEF // STELEM
};
ASSERTNR(Opcode >= ReaderBaseNS::StelemI &&
Opcode < ReaderBaseNS::LastStelemOpcode);
CorInfoType CorType = Map[Opcode];
ReaderAlignType Alignment = Reader_AlignNatural;
// ResolvedToken is only valid for stelem.
if (Opcode != ReaderBaseNS::Stelem) {
ResolvedToken = nullptr;
}
Type *ElementTy = getArrayElementType(Array, ResolvedToken, &CorType,
&Alignment);
if (CorType == CorInfoType::CORINFO_TYPE_CLASS) {
Constant *ConstantValue = dyn_cast<Constant>(ValueToStore);
if (ConstantValue == nullptr || !ConstantValue->isNullValue()) {
// This will call a helper that stores an element of object array with
// type checking. It will also call a write barrier if necessary. Storing
// null is always legal, doesn't need a write barrier, and thus does not
// need a helper call.
return storeElemRefAny(ValueToStore, Index, Array);
}
}
Value *ElementAddress = genArrayElemAddress(Array, Index, ElementTy);
bool IsVolatile = false;
bool IsField = false;
ValueToStore = convertFromStackType(ValueToStore, CorType, ElementTy);
if (ElementTy->isStructTy()) {
bool IsNonValueClass = false;
bool IsValueIsPointer = false;
bool IsUnchecked = false;
// Store with a write barrier if the struct has gc pointers.
rdrCallWriteBarrierHelper(Array, ValueToStore, Alignment, IsVolatile,
ResolvedToken, IsNonValueClass, IsValueIsPointer,
IsField, IsUnchecked);
}
else {
storeAtAddressNonNull((IRNode *)ElementAddress, ValueToStore,
ElementTy, ResolvedToken, Alignment, IsVolatile,
IsField);
}
}
// Get array element type.
Type *GenIR::getArrayElementType(IRNode *Array,
CORINFO_RESOLVED_TOKEN *ResolvedToken,
CorInfoType *CorType,
ReaderAlignType *Alignment) {
ASSERTNR(Alignment != nullptr);
ASSERTNR(CorType != nullptr);
CORINFO_CLASS_HANDLE ClassHandle = nullptr;
if (*CorType == CorInfoType::CORINFO_TYPE_CLASS) {
PointerType *Ty = cast<PointerType>(Array->getType());
StructType *ReferentTy = cast<StructType>(Ty->getPointerElementType());
unsigned int NumElements = ReferentTy->getNumElements();
ArrayType *ArrayTy =
cast<ArrayType>(ReferentTy->getElementType(NumElements - 1));
return ArrayTy->getElementType();
}
if (ResolvedToken != nullptr) {
ClassHandle = ResolvedToken->hClass;
*CorType = JitContext->JitInfo->asCorInfoType(ClassHandle);
if ((*CorType == CorInfoType::CORINFO_TYPE_VALUECLASS) ||
(*CorType == CORINFO_TYPE_REFANY)) {
*Alignment = getMinimumClassAlignment(ClassHandle, Reader_AlignNatural);
}
}
ASSERTNR(*CorType != CorInfoType::CORINFO_TYPE_UNDEF);
return getType(*CorType, ClassHandle);
}
// Get address of the array element.
Value *GenIR::genArrayElemAddress(IRNode *Array, IRNode *Index,
Type *ElementTy) {
@ -3963,8 +4048,7 @@ IRNode *GenIR::getPrimitiveAddress(IRNode *Addr, CorInfoType CorInfoType,
}
}
*Align = (Alignment == Reader_AlignNatural) ? TargetPointerSizeInBits / 8
: Alignment;
*Align = convertReaderAlignment(Alignment);
return TypedAddr;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше