[llvm] r298262 - Templatize parts of VNCoercion, and add constant-only versions of the functions to be used in NewGVN.
Daniel Berlin via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 20 11:08:40 PDT 2017
I missed a test update in the commit.
The test changes the names of some of the instructions (no code is affected)
On Mon, Mar 20, 2017 at 10:28 AM, Juergen Ributzka <juergen at ributzka.de>
wrote:
> Hi Daniel,
>
> this broke a test on green dragon:
> http://green.lab.llvm.org/green/job/clang-stage1-cmake-
> RA-incremental_check/34915/
>
> Could you please take a look?
>
> Thanks
>
> Cheers,
> Juergen
>
> On Mon, Mar 20, 2017 at 9:08 AM, Daniel Berlin via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Author: dannyb
>> Date: Mon Mar 20 11:08:29 2017
>> New Revision: 298262
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=298262&view=rev
>> Log:
>> Templatize parts of VNCoercion, and add constant-only versions of the
>> functions to be used in NewGVN.
>> NFCI.
>>
>> Summary:
>> This is ground work for the changes to enable coercion in NewGVN.
>> GVN doesn't care if they end up constant because it eliminates as it goes.
>> NewGVN cares.
>>
>> IRBuilder and ConstantFolder deliberately present the same interface,
>> so we use this to our advantage to templatize our functions to make
>> them either constant only or not.
>>
>> Reviewers: davide
>>
>> Subscribers: llvm-commits, Prazek
>>
>> Differential Revision: https://reviews.llvm.org/D30928
>>
>> Modified:
>> llvm/trunk/include/llvm/Transforms/Utils/VNCoercion.h
>> llvm/trunk/lib/Transforms/Scalar/GVN.cpp
>> llvm/trunk/lib/Transforms/Utils/VNCoercion.cpp
>>
>> Modified: llvm/trunk/include/llvm/Transforms/Utils/VNCoercion.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/
>> Transforms/Utils/VNCoercion.h?rev=298262&r1=298261&r2=298262&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/Transforms/Utils/VNCoercion.h (original)
>> +++ llvm/trunk/include/llvm/Transforms/Utils/VNCoercion.h Mon Mar 20
>> 11:08:29 2017
>> @@ -76,13 +76,21 @@ int analyzeLoadFromClobberingMemInst(Typ
>> /// inserts instructions to do so at InsertPt, and returns the extracted
>> value.
>> Value *getStoreValueForLoad(Value *SrcVal, unsigned Offset, Type *LoadTy,
>> Instruction *InsertPt, const DataLayout &DL);
>> +// This is the same as getStoreValueForLoad, except it performs no
>> insertion
>> +// It only allows constant inputs.
>> +Constant *getConstantStoreValueForLoad(Constant *SrcVal, unsigned
>> Offset,
>> + Type *LoadTy, const DataLayout
>> &DL);
>>
>> /// If analyzeLoadFromClobberingLoad returned an offset, this function
>> can be
>> /// used to actually perform the extraction of the bits from the load,
>> including
>> /// any necessary load widening. It inserts instructions to do so at
>> InsertPt,
>> /// and returns the extracted value.
>> Value *getLoadValueForLoad(LoadInst *SrcVal, unsigned Offset, Type
>> *LoadTy,
>> - Instruction *InsertPt);
>> + Instruction *InsertPt, const DataLayout &DL);
>> +// This is the same as getLoadValueForLoad, except it is given the load
>> value as
>> +// a constant. It returns nullptr if it would require widening the load.
>> +Constant *getConstantLoadValueForLoad(Constant *SrcVal, unsigned Offset,
>> + Type *LoadTy, const DataLayout
>> &DL);
>>
>> /// If analyzeLoadFromClobberingMemInst returned an offset, this
>> function can be
>> /// used to actually perform the extraction of the bits from the memory
>> @@ -91,6 +99,10 @@ Value *getLoadValueForLoad(LoadInst *Src
>> Value *getMemInstValueForLoad(MemIntrinsic *SrcInst, unsigned Offset,
>> Type *LoadTy, Instruction *InsertPt,
>> const DataLayout &DL);
>> +// This is the same as getStoreValueForLoad, except it performs no
>> insertion.
>> +// It returns nullptr if it cannot produce a constant.
>> +Constant *getConstantMemInstValueForLoad(MemIntrinsic *SrcInst,
>> unsigned Offset,
>> + Type *LoadTy, const DataLayout
>> &DL);
>> }
>> }
>> #endif
>>
>> Modified: llvm/trunk/lib/Transforms/Scalar/GVN.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transform
>> s/Scalar/GVN.cpp?rev=298262&r1=298261&r2=298262&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Transforms/Scalar/GVN.cpp (original)
>> +++ llvm/trunk/lib/Transforms/Scalar/GVN.cpp Mon Mar 20 11:08:29 2017
>> @@ -750,7 +750,7 @@ Value *AvailableValue::MaterializeAdjust
>> if (Load->getType() == LoadTy && Offset == 0) {
>> Res = Load;
>> } else {
>> - Res = getLoadValueForLoad(Load, Offset, LoadTy, InsertPt);
>> + Res = getLoadValueForLoad(Load, Offset, LoadTy, InsertPt, DL);
>> // We would like to use gvn.markInstructionForDeletion here, but
>> we can't
>> // because the load is already memoized into the leader map table
>> that GVN
>> // tracks. It is potentially possible to remove the load from the
>> table,
>>
>> Modified: llvm/trunk/lib/Transforms/Utils/VNCoercion.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transform
>> s/Utils/VNCoercion.cpp?rev=298262&r1=298261&r2=298262&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Transforms/Utils/VNCoercion.cpp (original)
>> +++ llvm/trunk/lib/Transforms/Utils/VNCoercion.cpp Mon Mar 20 11:08:29
>> 2017
>> @@ -27,17 +27,12 @@ bool canCoerceMustAliasedValueToLoad(Val
>> return true;
>> }
>>
>> -/// If we saw a store of a value to memory, and
>> -/// then a load from a must-aliased pointer of a different type, try to
>> coerce
>> -/// the stored value. LoadedTy is the type of the load we want to
>> replace.
>> -/// IRB is IRBuilder used to insert new instructions.
>> -///
>> -/// If we can't do it, return null.
>> -Value *coerceAvailableValueToLoadType(Value *StoredVal, Type *LoadedTy,
>> - IRBuilder<> &IRB, const DataLayout
>> &DL) {
>> +template <class T, class HelperClass>
>> +static T *coerceAvailableValueToLoadTypeHelper(T *StoredVal, Type
>> *LoadedTy,
>> + HelperClass &Helper,
>> + const DataLayout &DL) {
>> assert(canCoerceMustAliasedValueToLoad(StoredVal, LoadedTy, DL) &&
>> "precondition violation - materialization can't fail");
>> -
>> if (auto *C = dyn_cast<Constant>(StoredVal))
>> if (auto *FoldedStoredVal = ConstantFoldConstant(C, DL))
>> StoredVal = FoldedStoredVal;
>> @@ -53,12 +48,12 @@ Value *coerceAvailableValueToLoadType(Va
>> // Pointer to Pointer -> use bitcast.
>> if (StoredValTy->getScalarType()->isPointerTy() &&
>> LoadedTy->getScalarType()->isPointerTy()) {
>> - StoredVal = IRB.CreateBitCast(StoredVal, LoadedTy);
>> + StoredVal = Helper.CreateBitCast(StoredVal, LoadedTy);
>> } else {
>> // Convert source pointers to integers, which can be bitcast.
>> if (StoredValTy->getScalarType()->isPointerTy()) {
>> StoredValTy = DL.getIntPtrType(StoredValTy);
>> - StoredVal = IRB.CreatePtrToInt(StoredVal, StoredValTy);
>> + StoredVal = Helper.CreatePtrToInt(StoredVal, StoredValTy);
>> }
>>
>> Type *TypeToCastTo = LoadedTy;
>> @@ -66,11 +61,11 @@ Value *coerceAvailableValueToLoadType(Va
>> TypeToCastTo = DL.getIntPtrType(TypeToCastTo);
>>
>> if (StoredValTy != TypeToCastTo)
>> - StoredVal = IRB.CreateBitCast(StoredVal, TypeToCastTo);
>> + StoredVal = Helper.CreateBitCast(StoredVal, TypeToCastTo);
>>
>> // Cast to pointer if the load needs a pointer type.
>> if (LoadedTy->getScalarType()->isPointerTy())
>> - StoredVal = IRB.CreateIntToPtr(StoredVal, LoadedTy);
>> + StoredVal = Helper.CreateIntToPtr(StoredVal, LoadedTy);
>> }
>>
>> if (auto *C = dyn_cast<ConstantExpr>(StoredVal))
>> @@ -79,7 +74,6 @@ Value *coerceAvailableValueToLoadType(Va
>>
>> return StoredVal;
>> }
>> -
>> // If the loaded value is smaller than the available value, then we can
>> // extract out a piece from it. If the available value is too small,
>> then we
>> // can't do anything.
>> @@ -89,13 +83,13 @@ Value *coerceAvailableValueToLoadType(Va
>> // Convert source pointers to integers, which can be manipulated.
>> if (StoredValTy->getScalarType()->isPointerTy()) {
>> StoredValTy = DL.getIntPtrType(StoredValTy);
>> - StoredVal = IRB.CreatePtrToInt(StoredVal, StoredValTy);
>> + StoredVal = Helper.CreatePtrToInt(StoredVal, StoredValTy);
>> }
>>
>> // Convert vectors and fp to integer, which can be manipulated.
>> if (!StoredValTy->isIntegerTy()) {
>> StoredValTy = IntegerType::get(StoredValTy->getContext(),
>> StoredValSize);
>> - StoredVal = IRB.CreateBitCast(StoredVal, StoredValTy);
>> + StoredVal = Helper.CreateBitCast(StoredVal, StoredValTy);
>> }
>>
>> // If this is a big-endian system, we need to shift the value down to
>> the low
>> @@ -103,20 +97,21 @@ Value *coerceAvailableValueToLoadType(Va
>> if (DL.isBigEndian()) {
>> uint64_t ShiftAmt = DL.getTypeStoreSizeInBits(StoredValTy) -
>> DL.getTypeStoreSizeInBits(LoadedTy);
>> - StoredVal = IRB.CreateLShr(StoredVal, ShiftAmt, "tmp");
>> + StoredVal = Helper.CreateLShr(
>> + StoredVal, ConstantInt::get(StoredVal->getType(), ShiftAmt));
>> }
>>
>> // Truncate the integer to the right size now.
>> Type *NewIntTy = IntegerType::get(StoredValTy->getContext(),
>> LoadedValSize);
>> - StoredVal = IRB.CreateTrunc(StoredVal, NewIntTy, "trunc");
>> + StoredVal = Helper.CreateTruncOrBitCast(StoredVal, NewIntTy);
>>
>> if (LoadedTy != NewIntTy) {
>> // If the result is a pointer, inttoptr.
>> if (LoadedTy->getScalarType()->isPointerTy())
>> - StoredVal = IRB.CreateIntToPtr(StoredVal, LoadedTy, "inttoptr");
>> + StoredVal = Helper.CreateIntToPtr(StoredVal, LoadedTy);
>> else
>> // Otherwise, bitcast.
>> - StoredVal = IRB.CreateBitCast(StoredVal, LoadedTy, "bitcast");
>> + StoredVal = Helper.CreateBitCast(StoredVal, LoadedTy);
>> }
>>
>> if (auto *C = dyn_cast<Constant>(StoredVal))
>> @@ -126,10 +121,21 @@ Value *coerceAvailableValueToLoadType(Va
>> return StoredVal;
>> }
>>
>> -/// This function is called when we have a
>> -/// memdep query of a load that ends up being a clobbering memory write
>> (store,
>> -/// memset, memcpy, memmove). This means that the write *may* provide
>> bits used
>> -/// by the load but we can't be sure because the pointers don't
>> mustalias.
>> +/// If we saw a store of a value to memory, and
>> +/// then a load from a must-aliased pointer of a different type, try to
>> coerce
>> +/// the stored value. LoadedTy is the type of the load we want to
>> replace.
>> +/// IRB is IRBuilder used to insert new instructions.
>> +///
>> +/// If we can't do it, return null.
>> +Value *coerceAvailableValueToLoadType(Value *StoredVal, Type *LoadedTy,
>> + IRBuilder<> &IRB, const DataLayout
>> &DL) {
>> + return coerceAvailableValueToLoadTypeHelper(StoredVal, LoadedTy, IRB,
>> DL);
>> +}
>> +
>> +/// This function is called when we have a memdep query of a load that
>> ends up
>> +/// being a clobbering memory write (store, memset, memcpy, memmove).
>> This
>> +/// means that the write *may* provide bits used by the load but we
>> can't be
>> +/// sure because the pointers don't must-alias.
>> ///
>> /// Check this case to see if there is anything more we can do before we
>> give
>> /// up. This returns -1 if we have to give up, or a byte number in the
>> stored
>> @@ -286,28 +292,20 @@ int analyzeLoadFromClobberingMemInst(Typ
>> return -1;
>> }
>>
>> -/// This function is called when we have a
>> -/// memdep query of a load that ends up being a clobbering store. This
>> means
>> -/// that the store provides bits used by the load but we the pointers
>> don't
>> -/// mustalias. Check this case to see if there is anything more we can
>> do
>> -/// before we give up.
>> -Value *getStoreValueForLoad(Value *SrcVal, unsigned Offset, Type *LoadTy,
>> - Instruction *InsertPt, const DataLayout &DL)
>> {
>> +template <class T, class HelperClass>
>> +static T *getStoreValueForLoadHelper(T *SrcVal, unsigned Offset, Type
>> *LoadTy,
>> + HelperClass &Helper,
>> + const DataLayout &DL) {
>> LLVMContext &Ctx = SrcVal->getType()->getContext();
>>
>> uint64_t StoreSize = (DL.getTypeSizeInBits(SrcVal->getType()) + 7) /
>> 8;
>> uint64_t LoadSize = (DL.getTypeSizeInBits(LoadTy) + 7) / 8;
>> -
>> - IRBuilder<> Builder(InsertPt);
>> -
>> // Compute which bits of the stored value are being used by the load.
>> Convert
>> // to an integer type to start with.
>> if (SrcVal->getType()->getScalarType()->isPointerTy())
>> - SrcVal =
>> - Builder.CreatePtrToInt(SrcVal, DL.getIntPtrType(SrcVal->getTy
>> pe()));
>> + SrcVal = Helper.CreatePtrToInt(SrcVal, DL.getIntPtrType(SrcVal->getTy
>> pe()));
>> if (!SrcVal->getType()->isIntegerTy())
>> - SrcVal =
>> - Builder.CreateBitCast(SrcVal, IntegerType::get(Ctx, StoreSize *
>> 8));
>> + SrcVal = Helper.CreateBitCast(SrcVal, IntegerType::get(Ctx,
>> StoreSize * 8));
>>
>> // Shift the bits to the least significant depending on endianness.
>> unsigned ShiftAmt;
>> @@ -315,25 +313,42 @@ Value *getStoreValueForLoad(Value *SrcVa
>> ShiftAmt = Offset * 8;
>> else
>> ShiftAmt = (StoreSize - LoadSize - Offset) * 8;
>> -
>> if (ShiftAmt)
>> - SrcVal = Builder.CreateLShr(SrcVal, ShiftAmt);
>> + SrcVal = Helper.CreateLShr(SrcVal,
>> + ConstantInt::get(SrcVal->getType(),
>> ShiftAmt));
>>
>> if (LoadSize != StoreSize)
>> - SrcVal = Builder.CreateTrunc(SrcVal, IntegerType::get(Ctx, LoadSize
>> * 8));
>> + SrcVal = Helper.CreateTruncOrBitCast(SrcVal,
>> + IntegerType::get(Ctx, LoadSize
>> * 8));
>> + return SrcVal;
>> +}
>>
>> - return coerceAvailableValueToLoadType(SrcVal, LoadTy, Builder, DL);
>> +/// This function is called when we have a memdep query of a load that
>> ends up
>> +/// being a clobbering store. This means that the store provides bits
>> used by
>> +/// the load but the pointers don't must-alias. Check this case to see
>> if
>> +/// there is anything more we can do before we give up.
>> +Value *getStoreValueForLoad(Value *SrcVal, unsigned Offset, Type *LoadTy,
>> + Instruction *InsertPt, const DataLayout &DL)
>> {
>> +
>> + IRBuilder<> Builder(InsertPt);
>> + SrcVal = getStoreValueForLoadHelper(SrcVal, Offset, LoadTy, Builder,
>> DL);
>> + return coerceAvailableValueToLoadTypeHelper(SrcVal, LoadTy, Builder,
>> DL);
>> }
>>
>> -/// This function is called when we have a
>> -/// memdep query of a load that ends up being a clobbering load. This
>> means
>> -/// that the load *may* provide bits used by the load but we can't be
>> sure
>> -/// because the pointers don't mustalias. Check this case to see if
>> there is
>> -/// anything more we can do before we give up.
>> -Value *getLoadValueForLoad(LoadInst *SrcVal, unsigned Offset, Type
>> *LoadTy,
>> - Instruction *InsertPt) {
>> +Constant *getConstantStoreValueForLoad(Constant *SrcVal, unsigned
>> Offset,
>> + Type *LoadTy, const DataLayout
>> &DL) {
>> + ConstantFolder F;
>> + SrcVal = getStoreValueForLoadHelper(SrcVal, Offset, LoadTy, F, DL);
>> + return coerceAvailableValueToLoadTypeHelper(SrcVal, LoadTy, F, DL);
>> +}
>>
>> - const DataLayout &DL = SrcVal->getModule()->getDataLayout();
>> +/// This function is called when we have a memdep query of a load that
>> ends up
>> +/// being a clobbering load. This means that the load *may* provide
>> bits used
>> +/// by the load but we can't be sure because the pointers don't
>> must-alias.
>> +/// Check this case to see if there is anything more we can do before we
>> give
>> +/// up.
>> +Value *getLoadValueForLoad(LoadInst *SrcVal, unsigned Offset, Type
>> *LoadTy,
>> + Instruction *InsertPt, const DataLayout &DL) {
>> // If Offset+LoadTy exceeds the size of SrcVal, then we must be
>> wanting to
>> // widen SrcVal out to a larger load.
>> unsigned SrcValStoreSize = DL.getTypeStoreSize(SrcVal->getType());
>> @@ -348,7 +363,6 @@ Value *getLoadValueForLoad(LoadInst *Src
>> NewLoadSize = NextPowerOf2(NewLoadSize);
>>
>> Value *PtrVal = SrcVal->getPointerOperand();
>> -
>> // Insert the new load after the old load. This ensures that
>> subsequent
>> // memdep queries will find the new load. We can't easily remove
>> the old
>> // load completely because it is already in the value numbering
>> table.
>> @@ -379,44 +393,51 @@ Value *getLoadValueForLoad(LoadInst *Src
>> return getStoreValueForLoad(SrcVal, Offset, LoadTy, InsertPt, DL);
>> }
>>
>> -/// This function is called when we have a
>> -/// memdep query of a load that ends up being a clobbering mem intrinsic.
>> -Value *getMemInstValueForLoad(MemIntrinsic *SrcInst, unsigned Offset,
>> - Type *LoadTy, Instruction *InsertPt,
>> - const DataLayout &DL) {
>> +Constant *getConstantLoadValueForLoad(Constant *SrcVal, unsigned Offset,
>> + Type *LoadTy, const DataLayout
>> &DL) {
>> + unsigned SrcValStoreSize = DL.getTypeStoreSize(SrcVal->getType());
>> + unsigned LoadSize = DL.getTypeStoreSize(LoadTy);
>> + if (Offset + LoadSize > SrcValStoreSize)
>> + return nullptr;
>> + return getConstantStoreValueForLoad(SrcVal, Offset, LoadTy, DL);
>> +}
>> +
>> +template <class T, class HelperClass>
>> +T *getMemInstValueForLoadHelper(MemIntrinsic *SrcInst, unsigned Offset,
>> + Type *LoadTy, HelperClass &Helper,
>> + const DataLayout &DL) {
>> LLVMContext &Ctx = LoadTy->getContext();
>> uint64_t LoadSize = DL.getTypeSizeInBits(LoadTy) / 8;
>>
>> - IRBuilder<> Builder(InsertPt);
>> -
>> // We know that this method is only called when the mem transfer fully
>> // provides the bits for the load.
>> if (MemSetInst *MSI = dyn_cast<MemSetInst>(SrcInst)) {
>> // memset(P, 'x', 1234) -> splat('x'), even if x is a variable, and
>> // independently of what the offset is.
>> - Value *Val = MSI->getValue();
>> + T *Val = cast<T>(MSI->getValue());
>> if (LoadSize != 1)
>> - Val = Builder.CreateZExt(Val, IntegerType::get(Ctx, LoadSize * 8));
>> -
>> - Value *OneElt = Val;
>> + Val =
>> + Helper.CreateZExtOrBitCast(Val, IntegerType::get(Ctx,
>> LoadSize * 8));
>> + T *OneElt = Val;
>>
>> // Splat the value out to the right number of bits.
>> for (unsigned NumBytesSet = 1; NumBytesSet != LoadSize;) {
>> // If we can double the number of bytes set, do it.
>> if (NumBytesSet * 2 <= LoadSize) {
>> - Value *ShVal = Builder.CreateShl(Val, NumBytesSet * 8);
>> - Val = Builder.CreateOr(Val, ShVal);
>> + T *ShVal = Helper.CreateShl(
>> + Val, ConstantInt::get(Val->getType(), NumBytesSet * 8));
>> + Val = Helper.CreateOr(Val, ShVal);
>> NumBytesSet <<= 1;
>> continue;
>> }
>>
>> // Otherwise insert one byte at a time.
>> - Value *ShVal = Builder.CreateShl(Val, 1 * 8);
>> - Val = Builder.CreateOr(OneElt, ShVal);
>> + T *ShVal = Helper.CreateShl(Val, ConstantInt::get(Val->getType(),
>> 1 * 8));
>> + Val = Helper.CreateOr(OneElt, ShVal);
>> ++NumBytesSet;
>> }
>>
>> - return coerceAvailableValueToLoadType(Val, LoadTy, Builder, DL);
>> + return coerceAvailableValueToLoadTypeHelper(Val, LoadTy, Helper,
>> DL);
>> }
>>
>> // Otherwise, this is a memcpy/memmove from a constant global.
>> @@ -435,5 +456,27 @@ Value *getMemInstValueForLoad(MemIntrins
>> Src = ConstantExpr::getBitCast(Src, PointerType::get(LoadTy, AS));
>> return ConstantFoldLoadFromConstPtr(Src, LoadTy, DL);
>> }
>> +
>> +/// This function is called when we have a
>> +/// memdep query of a load that ends up being a clobbering mem intrinsic.
>> +Value *getMemInstValueForLoad(MemIntrinsic *SrcInst, unsigned Offset,
>> + Type *LoadTy, Instruction *InsertPt,
>> + const DataLayout &DL) {
>> + IRBuilder<> Builder(InsertPt);
>> + return getMemInstValueForLoadHelper<Value, IRBuilder<>>(SrcInst,
>> Offset,
>> + LoadTy,
>> Builder, DL);
>> +}
>> +
>> +Constant *getConstantMemInstValueForLoad(MemIntrinsic *SrcInst,
>> unsigned Offset,
>> + Type *LoadTy, const DataLayout
>> &DL) {
>> + // The only case analyzeLoadFromClobberingMemInst cannot be converted
>> to a
>> + // constant is when it's a memset of a non-constant.
>> + if (auto *MSI = dyn_cast<MemSetInst>(SrcInst))
>> + if (!isa<Constant>(MSI->getValue()))
>> + return nullptr;
>> + ConstantFolder F;
>> + return getMemInstValueForLoadHelper<Constant,
>> ConstantFolder>(SrcInst, Offset,
>> + LoadTy,
>> F, DL);
>> +}
>> } // namespace VNCoercion
>> } // namespace llvm
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20170320/90a7cc0a/attachment.html>
More information about the llvm-commits
mailing list