[llvm] r244601 - [X86] Allow merging of immediates within a basic block for code size savings
Sanjay Patel via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 10:41:01 PDT 2015
Hi Sean -
I think your example shows 3 possible improvements. Let me know if this
looks right, and I'll file some bugs to track them:
1. When addressing multiple places beyond an imm8, load a register with a
base and use SIB addressing to bring the offsets within 8-bits. So the
example would end up looking something like this:
movl $376, %ebx
movq $0, (%rax, %rbx)
movq $0, 8(%rax, %rbx)
movq $0, 16(%rax, %rbx)
...
Not sure we can do this unless optimizing for size. See below.
2. When storing the same immediate multiple times, use a reg to hold the
immediate even when not optimizing specifically for size (this was
mentioned as a follow-on in D11363):
xorl %ebx, %ebx <--- xor is only for zero; in the general
case, this would be a mov (load immediate)
movq %rbx, 376(%rax)
movq %rbx, 384(%rax)
movq %rbx, 392(%rax)
...
I don't think this qualifies as a no-brainer when not optimizing for size.
If it's an xor to make a zero, then it's *almost* free (handled by the
renamer) on any recent OOO x86, but that xor still requires some decode/uop
resources. In the general case where we use a mov to load the reg with the
immediate, this argument gets tougher. We need a perf heuristic that says
'saving X bytes of instructions is worth adding Y extra instructions of
type Z'?
3. If we really want to make this example smaller and faster, we should
merge stores like we do in DAGCombiner's MergeConsecutiveStores:
vxorps %ymm0, %ymm0 <--- assumes we have AVX for 32-byte ops; if
not, SSE for 16-byte
vmovups %ymm0, 376(%rax)
...
Are the stores in the example created too late for the DAGCombiner? Do we
need to repeat some subset of that merge functionality in a machine pass?
On Wed, Aug 12, 2015 at 2:00 AM, Sean Silva <chisophugis at gmail.com> wrote:
> This may be interesting even outside of optimizing for size. For example,
> I see wonderful fragments like the following all over my binaries:
>
> 20b7f:` 48 c7 80 78 01 00 00 00 00 00 00 ` movq` $0,
> 376(%rax)
>
> 20b8a:` 48 c7 80 80 01 00 00 00 00 00 00 ` movq` $0,
> 384(%rax)
>
> 20b95:` 48 c7 80 88 01 00 00 00 00 00 00 ` movq` $0,
> 392(%rax)
>
> 20ba0:` 48 c7 80 90 01 00 00 00 00 00 00 ` movq` $0,
> 400(%rax)
>
> 20bab:` 48 c7 80 a0 01 00 00 00 00 00 00 ` movq` $0,
> 416(%rax)
>
> 20bb6:` 48 c7 80 a8 09 00 00 00 00 00 00 ` movq` $0,
> 2472(%rax)
>
> Yes, 11 byte instructions just to zero out 8 bytes of memory :)
> If you are omitting frame pointer and are using stack slots beyond an
> imm8, pay an extra byte for the SIB.
>
> As observed in this patch, this pattern often occurs in close proximity to
> calls (the register is usually rax for a return value or rbp/rsp when
> preparing stack-passed arguments for a call), so there is often a
> no-brainer choice of register to xor and use r,m forms.
>
> I have seen functions for which more than half of the text size is due to
> these sorts of instructions, getting into pretty serious icache threat
> territory and worth looking at even outside optsize.
>
> -- Sean Silva
>
>
>
>
> On Tue, Aug 11, 2015 at 7:10 AM, Michael Kuperstein via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Author: mkuper
>> Date: Tue Aug 11 09:10:58 2015
>> New Revision: 244601
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=244601&view=rev
>> Log:
>> [X86] Allow merging of immediates within a basic block for code size
>> savings
>>
>> First step in preventing immediates that occur more than once within a
>> single
>> basic block from being pulled into their users, in order to prevent
>> unnecessary
>> large instruction encoding .Currently enabled only when optimizing for
>> size.
>>
>> Patch by: zia.ansari at intel.com
>> Differential Revision: http://reviews.llvm.org/D11363
>>
>> Added:
>> llvm/trunk/test/CodeGen/X86/immediate_merging.ll
>> Removed:
>> llvm/trunk/test/CodeGen/X86/remat-invalid-liveness.ll
>> Modified:
>> llvm/trunk/lib/Target/X86/X86ISelDAGToDAG.cpp
>> llvm/trunk/lib/Target/X86/X86InstrArithmetic.td
>> llvm/trunk/lib/Target/X86/X86InstrInfo.td
>>
>> Modified: llvm/trunk/lib/Target/X86/X86ISelDAGToDAG.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86ISelDAGToDAG.cpp?rev=244601&r1=244600&r2=244601&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Target/X86/X86ISelDAGToDAG.cpp (original)
>> +++ llvm/trunk/lib/Target/X86/X86ISelDAGToDAG.cpp Tue Aug 11 09:10:58 2015
>> @@ -283,6 +283,82 @@ namespace {
>> Segment = CurDAG->getRegister(0, MVT::i32);
>> }
>>
>> + // Utility function to determine whether we should avoid selecting
>> + // immediate forms of instructions for better code size or not.
>> + // At a high level, we'd like to avoid such instructions when
>> + // we have similar constants used within the same basic block
>> + // that can be kept in a register.
>> + //
>> + bool shouldAvoidImmediateInstFormsForSize(SDNode *N) const {
>> + uint32_t UseCount = 0;
>> +
>> + // Do not want to hoist if we're not optimizing for size.
>> + // TODO: We'd like to remove this restriction.
>> + // See the comment in X86InstrInfo.td for more info.
>> + if (!OptForSize)
>> + return false;
>> +
>> + // Walk all the users of the immediate.
>> + for (SDNode::use_iterator UI = N->use_begin(),
>> + UE = N->use_end(); (UI != UE) && (UseCount < 2); ++UI) {
>> +
>> + SDNode *User = *UI;
>> +
>> + // This user is already selected. Count it as a legitimate use
>> and
>> + // move on.
>> + if (User->isMachineOpcode()) {
>> + UseCount++;
>> + continue;
>> + }
>> +
>> + // We want to count stores of immediates as real uses.
>> + if (User->getOpcode() == ISD::STORE &&
>> + User->getOperand(1).getNode() == N) {
>> + UseCount++;
>> + continue;
>> + }
>> +
>> + // We don't currently match users that have > 2 operands (except
>> + // for stores, which are handled above)
>> + // Those instruction won't match in ISEL, for now, and would
>> + // be counted incorrectly.
>> + // This may change in the future as we add additional instruction
>> + // types.
>> + if (User->getNumOperands() != 2)
>> + continue;
>> +
>> + // Immediates that are used for offsets as part of stack
>> + // manipulation should be left alone. These are typically
>> + // used to indicate SP offsets for argument passing and
>> + // will get pulled into stores/pushes (implicitly).
>> + if (User->getOpcode() == X86ISD::ADD ||
>> + User->getOpcode() == ISD::ADD ||
>> + User->getOpcode() == X86ISD::SUB ||
>> + User->getOpcode() == ISD::SUB) {
>> +
>> + // Find the other operand of the add/sub.
>> + SDValue OtherOp = User->getOperand(0);
>> + if (OtherOp.getNode() == N)
>> + OtherOp = User->getOperand(1);
>> +
>> + // Don't count if the other operand is SP.
>> + RegisterSDNode *RegNode;
>> + if (OtherOp->getOpcode() == ISD::CopyFromReg &&
>> + (RegNode = dyn_cast_or_null<RegisterSDNode>(
>> + OtherOp->getOperand(1).getNode())))
>> + if ((RegNode->getReg() == X86::ESP) ||
>> + (RegNode->getReg() == X86::RSP))
>> + continue;
>> + }
>> +
>> + // ... otherwise, count this and move on.
>> + UseCount++;
>> + }
>> +
>> + // If we have more than 1 use, then recommend for hoisting.
>> + return (UseCount > 1);
>> + }
>> +
>> /// getI8Imm - Return a target constant with the specified value, of
>> type
>> /// i8.
>> inline SDValue getI8Imm(unsigned Imm, SDLoc DL) {
>>
>> Modified: llvm/trunk/lib/Target/X86/X86InstrArithmetic.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86InstrArithmetic.td?rev=244601&r1=244600&r2=244601&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Target/X86/X86InstrArithmetic.td (original)
>> +++ llvm/trunk/lib/Target/X86/X86InstrArithmetic.td Tue Aug 11 09:10:58
>> 2015
>> @@ -615,14 +615,14 @@ class X86TypeInfo<ValueType vt, string i
>> def invalid_node : SDNode<"<<invalid_node>>",
>> SDTIntLeaf,[],"<<invalid_node>>">;
>>
>>
>> -def Xi8 : X86TypeInfo<i8 , "b", GR8 , loadi8 , i8mem ,
>> - Imm8 , i8imm , imm, i8imm ,
>> invalid_node,
>> +def Xi8 : X86TypeInfo<i8, "b", GR8, loadi8, i8mem,
>> + Imm8, i8imm, imm8_su, i8imm, invalid_node,
>> 0, OpSizeFixed, 0>;
>> def Xi16 : X86TypeInfo<i16, "w", GR16, loadi16, i16mem,
>> - Imm16, i16imm, imm, i16i8imm,
>> i16immSExt8,
>> + Imm16, i16imm, imm16_su, i16i8imm, i16immSExt8_su,
>> 1, OpSize16, 0>;
>> def Xi32 : X86TypeInfo<i32, "l", GR32, loadi32, i32mem,
>> - Imm32, i32imm, imm, i32i8imm,
>> i32immSExt8,
>> + Imm32, i32imm, imm32_su, i32i8imm, i32immSExt8_su,
>> 1, OpSize32, 0>;
>> def Xi64 : X86TypeInfo<i64, "q", GR64, loadi64, i64mem,
>> Imm32S, i64i32imm, i64immSExt32, i64i8imm,
>> i64immSExt8,
>>
>> Modified: llvm/trunk/lib/Target/X86/X86InstrInfo.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86InstrInfo.td?rev=244601&r1=244600&r2=244601&view=diff
>>
>> ==============================================================================
>> --- llvm/trunk/lib/Target/X86/X86InstrInfo.td (original)
>> +++ llvm/trunk/lib/Target/X86/X86InstrInfo.td Tue Aug 11 09:10:58 2015
>> @@ -873,6 +873,40 @@ def i16immSExt8 : ImmLeaf<i16, [{ retur
>> def i32immSExt8 : ImmLeaf<i32, [{ return Imm == (int8_t)Imm; }]>;
>> def i64immSExt8 : ImmLeaf<i64, [{ return Imm == (int8_t)Imm; }]>;
>>
>> +// If we have multiple users of an immediate, it's much smaller to reuse
>> +// the register, rather than encode the immediate in every instruction.
>> +// This has the risk of increasing register pressure from stretched live
>> +// ranges, however, the immediates should be trivial to rematerialize by
>> +// the RA in the event of high register pressure.
>> +// TODO : This is currently enabled for stores and binary ops. There are
>> more
>> +// cases for which this can be enabled, though this catches the bulk of
>> the
>> +// issues.
>> +// TODO2 : This should really also be enabled under O2, but there's
>> currently
>> +// an issue with RA where we don't pull the constants into their users
>> +// when we rematerialize them. I'll follow-up on enabling O2 after we
>> fix that
>> +// issue.
>> +// TODO3 : This is currently limited to single basic blocks (DAG creation
>> +// pulls block immediates to the top and merges them if necessary).
>> +// Eventually, it would be nice to allow ConstantHoisting to merge
>> constants
>> +// globally for potentially added savings.
>> +//
>> +def imm8_su : PatLeaf<(i8 imm), [{
>> + return !shouldAvoidImmediateInstFormsForSize(N);
>> +}]>;
>> +def imm16_su : PatLeaf<(i16 imm), [{
>> + return !shouldAvoidImmediateInstFormsForSize(N);
>> +}]>;
>> +def imm32_su : PatLeaf<(i32 imm), [{
>> + return !shouldAvoidImmediateInstFormsForSize(N);
>> +}]>;
>> +
>> +def i16immSExt8_su : PatLeaf<(i16immSExt8), [{
>> + return !shouldAvoidImmediateInstFormsForSize(N);
>> +}]>;
>> +def i32immSExt8_su : PatLeaf<(i32immSExt8), [{
>> + return !shouldAvoidImmediateInstFormsForSize(N);
>> +}]>;
>> +
>>
>> def i64immSExt32 : ImmLeaf<i64, [{ return Imm == (int32_t)Imm; }]>;
>>
>> @@ -1283,13 +1317,13 @@ def MOV32ri_alt : Ii32<0xC7, MRM0r, (out
>> let SchedRW = [WriteStore] in {
>> def MOV8mi : Ii8 <0xC6, MRM0m, (outs), (ins i8mem :$dst, i8imm :$src),
>> "mov{b}\t{$src, $dst|$dst, $src}",
>> - [(store (i8 imm:$src), addr:$dst)], IIC_MOV_MEM>;
>> + [(store (i8 imm8_su:$src), addr:$dst)], IIC_MOV_MEM>;
>> def MOV16mi : Ii16<0xC7, MRM0m, (outs), (ins i16mem:$dst, i16imm:$src),
>> "mov{w}\t{$src, $dst|$dst, $src}",
>> - [(store (i16 imm:$src), addr:$dst)], IIC_MOV_MEM>,
>> OpSize16;
>> + [(store (i16 imm16_su:$src), addr:$dst)],
>> IIC_MOV_MEM>, OpSize16;
>> def MOV32mi : Ii32<0xC7, MRM0m, (outs), (ins i32mem:$dst, i32imm:$src),
>> "mov{l}\t{$src, $dst|$dst, $src}",
>> - [(store (i32 imm:$src), addr:$dst)], IIC_MOV_MEM>,
>> OpSize32;
>> + [(store (i32 imm32_su:$src), addr:$dst)],
>> IIC_MOV_MEM>, OpSize32;
>> def MOV64mi32 : RIi32S<0xC7, MRM0m, (outs), (ins i64mem:$dst,
>> i64i32imm:$src),
>> "mov{q}\t{$src, $dst|$dst, $src}",
>> [(store i64immSExt32:$src, addr:$dst)],
>> IIC_MOV_MEM>;
>>
>> Added: llvm/trunk/test/CodeGen/X86/immediate_merging.ll
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/immediate_merging.ll?rev=244601&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/CodeGen/X86/immediate_merging.ll (added)
>> +++ llvm/trunk/test/CodeGen/X86/immediate_merging.ll Tue Aug 11 09:10:58
>> 2015
>> @@ -0,0 +1,82 @@
>> +; RUN: llc -o - -mtriple=i386-unknown-linux-gnu < %s | FileCheck %s
>> +; RUN: llc -o - -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
>> +
>> + at a = common global i32 0, align 4
>> + at b = common global i32 0, align 4
>> + at c = common global i32 0, align 4
>> + at e = common global i32 0, align 4
>> + at x = common global i32 0, align 4
>> + at f = common global i32 0, align 4
>> + at h = common global i32 0, align 4
>> + at i = common global i32 0, align 4
>> +
>> +; Test -Os to make sure immediates with multiple users don't get pulled
>> in to
>> +; instructions.
>> +define i32 @foo() optsize {
>> +; CHECK-LABEL: foo:
>> +; CHECK: movl $1234, [[R1:%[a-z]+]]
>> +; CHECK-NOT: movl $1234, a
>> +; CHECK-NOT: movl $1234, b
>> +; CHECK-NOT: movl $12, c
>> +; CHECK-NOT: cmpl $12, e
>> +; CHECK: movl [[R1]], a
>> +; CHECK: movl [[R1]], b
>> +
>> +entry:
>> + store i32 1234, i32* @a
>> + store i32 1234, i32* @b
>> + store i32 12, i32* @c
>> + %0 = load i32, i32* @e
>> + %cmp = icmp eq i32 %0, 12
>> + br i1 %cmp, label %if.then, label %if.end
>> +
>> +if.then: ; preds = %entry
>> + store i32 1, i32* @x
>> + br label %if.end
>> +
>> +; New block.. Make sure 1234 isn't live across basic blocks from before.
>> +; CHECK: movl $1234, f
>> +; CHECK: movl $555, [[R3:%[a-z]+]]
>> +; CHECK-NOT: movl $555, h
>> +; CHECK-NOT: addl $555, i
>> +; CHECK: movl [[R3]], h
>> +; CHECK: addl [[R3]], i
>> +
>> +if.end: ; preds = %if.then,
>> %entry
>> + store i32 1234, i32* @f
>> + store i32 555, i32* @h
>> + %1 = load i32, i32* @i
>> + %add1 = add nsw i32 %1, 555
>> + store i32 %add1, i32* @i
>> + ret i32 0
>> +}
>> +
>> +; Test -O2 to make sure that all immediates get pulled in to their users.
>> +define i32 @foo2() {
>> +; CHECK-LABEL: foo2:
>> +; CHECK: movl $1234, a
>> +; CHECK: movl $1234, b
>> +
>> +entry:
>> + store i32 1234, i32* @a
>> + store i32 1234, i32* @b
>> +
>> + ret i32 0
>> +}
>> +
>> +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #1
>> +
>> + at AA = common global [100 x i8] zeroinitializer, align 1
>> +
>> +; memset gets lowered in DAG. Constant merging should hoist all the
>> +; immediates used to store to the individual memory locations. Make
>> +; sure we don't directly store the immediates.
>> +define void @foomemset() optsize {
>> +; CHECK-LABEL: foomemset:
>> +; CHECK-NOT: movl ${{.*}}, AA
>> +; CHECK: mov{{l|q}} %{{e|r}}ax, AA
>> +
>> +entry:
>> + call void @llvm.memset.p0i8.i32(i8* getelementptr inbounds ([100 x
>> i8], [100 x i8]* @AA, i32 0, i32 0), i8 33, i32 24, i32 1, i1 false)
>> + ret void
>> +}
>>
>> Removed: llvm/trunk/test/CodeGen/X86/remat-invalid-liveness.ll
>> URL:
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/remat-invalid-liveness.ll?rev=244600&view=auto
>>
>> ==============================================================================
>> --- llvm/trunk/test/CodeGen/X86/remat-invalid-liveness.ll (original)
>> +++ llvm/trunk/test/CodeGen/X86/remat-invalid-liveness.ll (removed)
>> @@ -1,85 +0,0 @@
>> -; RUN: llc %s -mcpu=core2 -o - | FileCheck %s
>> -; This test was failing while tracking the liveness in the register
>> scavenger
>> -; during the branching folding pass. The allocation of the subregisters
>> was
>> -; incorrect.
>> -; I.e., the faulty pattern looked like:
>> -; CH = movb 64
>> -; ECX = movl 3 <- CH was killed here.
>> -; CH = subb CH, ...
>> -;
>> -; This reduced test case triggers the crash before the fix, but does not
>> -; strictly speaking check that the resulting code is correct.
>> -; To check that the code is actually correct we would need to check the
>> -; liveness of the produced code.
>> -;
>> -; Currently, we check that after ECX = movl 3, we do not have subb CH,
>> -; whereas CH could have been redefine in between and that would have been
>> -; totally fine.
>> -; <rdar://problem/16582185>
>> -target datalayout = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128"
>> -target triple = "i386-apple-macosx10.9"
>> -
>> -%struct.A = type { %struct.B, %struct.C, %struct.D*, [1 x i8*] }
>> -%struct.B = type { i32, [4 x i8] }
>> -%struct.C = type { i128 }
>> -%struct.D = type { {}*, [0 x i32] }
>> -%union.E = type { i32 }
>> -
>> -; CHECK-LABEL: __XXX1:
>> -; CHECK: movl $3, %ecx
>> -; CHECK-NOT: subb %{{[a-z]+}}, %ch
>> -; Function Attrs: nounwind optsize ssp
>> -define fastcc void @__XXX1(%struct.A* %ht) #0 {
>> -entry:
>> - %const72 = bitcast i128 72 to i128
>> - %const3 = bitcast i128 3 to i128
>> - switch i32 undef, label %if.end196 [
>> - i32 1, label %sw.bb.i
>> - i32 3, label %sw.bb2.i
>> - ]
>> -
>> -sw.bb.i: ; preds = %entry
>> - %call.i.i.i = tail call i32 undef(%struct.A* %ht, i8 zeroext 22, i32
>> undef, i32 0, %struct.D* undef)
>> - %bf.load.i.i = load i128, i128* undef, align 4
>> - %bf.lshr.i.i = lshr i128 %bf.load.i.i, %const72
>> - %shl1.i.i = shl nuw nsw i128 %bf.lshr.i.i, 8
>> - %shl.i.i = trunc i128 %shl1.i.i to i32
>> - br i1 undef, label %cond.false10.i.i, label %__XXX2.exit.i.i
>> -
>> -__XXX2.exit.i.i: ; preds = %sw.bb.i
>> - %extract11.i.i.i = lshr i128 %bf.load.i.i, %const3
>> - %extract.t12.i.i.i = trunc i128 %extract11.i.i.i to i32
>> - %bf.cast7.i.i.i = and i32 %extract.t12.i.i.i, 3
>> - %arrayidx.i.i.i = getelementptr inbounds %struct.A, %struct.A* %ht,
>> i32 0, i32 3, i32 %bf.cast7.i.i.i
>> - br label %cond.end12.i.i
>> -
>> -cond.false10.i.i: ; preds = %sw.bb.i
>> - %arrayidx.i6.i.i = getelementptr inbounds %struct.A, %struct.A* %ht,
>> i32 0, i32 3, i32 0
>> - br label %cond.end12.i.i
>> -
>> -cond.end12.i.i: ; preds =
>> %cond.false10.i.i, %__XXX2.exit.i.i
>> - %.sink.in.i.i = phi i8** [ %arrayidx.i.i.i, %__XXX2.exit.i.i ], [
>> %arrayidx.i6.i.i, %cond.false10.i.i ]
>> - %.sink.i.i = load i8*, i8** %.sink.in.i.i, align 4
>> - %tmp = bitcast i8* %.sink.i.i to %union.E*
>> - br i1 undef, label %for.body.i.i, label %if.end196
>> -
>> -for.body.i.i: ; preds =
>> %for.body.i.i, %cond.end12.i.i
>> - %weak.i.i = getelementptr inbounds %union.E, %union.E* %tmp, i32
>> undef, i32 0
>> - %tmp1 = load i32, i32* %weak.i.i, align 4
>> - %cmp36.i.i = icmp ne i32 %tmp1, %shl.i.i
>> - %or.cond = and i1 %cmp36.i.i, false
>> - br i1 %or.cond, label %for.body.i.i, label %if.end196
>> -
>> -sw.bb2.i: ; preds = %entry
>> - %bf.lshr.i85.i = lshr i128 undef, %const72
>> - br i1 undef, label %if.end196, label %__XXX2.exit.i95.i
>> -
>> -__XXX2.exit.i95.i: ; preds = %sw.bb2.i
>> - %extract11.i.i91.i = lshr i128 undef, %const3
>> - br label %if.end196
>> -
>> -if.end196: ; preds =
>> %__XXX2.exit.i95.i, %sw.bb2.i, %for.body.i.i, %cond.end12.i.i, %entry
>> - ret void
>> -}
>> -
>> -attributes #0 = { nounwind optsize ssp "no-frame-pointer-elim"="true"
>> "no-frame-pointer-elim-non-leaf" }
>>
>>
>> _______________________________________________
>> 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/20150812/355f1e44/attachment.html>
More information about the llvm-commits
mailing list