On Thu, Mar 28, 2013 at 12:40:19AM +0100, Vincent Lejeune wrote: > --- Thanks for working on this, it is a very nice improvement. See my comments inline.
> lib/Target/R600/AMDGPU.h | 1 + > lib/Target/R600/AMDGPUTargetMachine.cpp | 1 + > lib/Target/R600/MCTargetDesc/R600MCCodeEmitter.cpp | 10 +- > lib/Target/R600/R600EmitClauseMarkers.cpp | 243 > +++++++++++++++++++++ > lib/Target/R600/R600Instructions.td | 83 ++++++- > lib/Target/R600/R600RegisterInfo.td | 63 ++++++ > 6 files changed, 389 insertions(+), 12 deletions(-) > create mode 100644 lib/Target/R600/R600EmitClauseMarkers.cpp > > diff --git a/lib/Target/R600/AMDGPU.h b/lib/Target/R600/AMDGPU.h > index e099a9f..3cd792a 100644 > --- a/lib/Target/R600/AMDGPU.h > +++ b/lib/Target/R600/AMDGPU.h > @@ -23,6 +23,7 @@ class AMDGPUTargetMachine; > // R600 Passes > FunctionPass* createR600KernelParametersPass(const DataLayout *TD); > FunctionPass *createR600ExpandSpecialInstrsPass(TargetMachine &tm); > +FunctionPass *createR600EmitClauseMarkers(TargetMachine &tm); > > // SI Passes > FunctionPass *createSIAnnotateControlFlowPass(); > diff --git a/lib/Target/R600/AMDGPUTargetMachine.cpp > b/lib/Target/R600/AMDGPUTargetMachine.cpp > index 0185747..45b1be0 100644 > --- a/lib/Target/R600/AMDGPUTargetMachine.cpp > +++ b/lib/Target/R600/AMDGPUTargetMachine.cpp > @@ -151,6 +151,7 @@ bool AMDGPUPassConfig::addPreEmitPass() { > if (ST.device()->getGeneration() <= AMDGPUDeviceInfo::HD6XXX) { > addPass(createAMDGPUCFGPreparationPass(*TM)); > addPass(createAMDGPUCFGStructurizerPass(*TM)); > + addPass(createR600EmitClauseMarkers(*TM)); > addPass(createR600ExpandSpecialInstrsPass(*TM)); > addPass(&FinalizeMachineBundlesID); > } else { > diff --git a/lib/Target/R600/MCTargetDesc/R600MCCodeEmitter.cpp > b/lib/Target/R600/MCTargetDesc/R600MCCodeEmitter.cpp > index 00ebb44..cf43f3f 100644 > --- a/lib/Target/R600/MCTargetDesc/R600MCCodeEmitter.cpp > +++ b/lib/Target/R600/MCTargetDesc/R600MCCodeEmitter.cpp > @@ -101,7 +101,8 @@ enum InstrTypes { > INSTR_FC, > INSTR_NATIVE, > INSTR_VTX, > - INSTR_EXPORT > + INSTR_EXPORT, > + INSTR_CFALU > }; > > enum FCInstr { > @@ -250,6 +251,13 @@ void R600MCCodeEmitter::EncodeInstruction(const MCInst > &MI, raw_ostream &OS, > Emit(Inst, OS); > break; > } > + case AMDGPU::CF_ALU: > + case AMDGPU::CF_ALU_PUSH_BEFORE: { > + uint64_t Inst = getBinaryCodeForInstr(MI, Fixups); > + EmitByte(INSTR_CFALU, OS); > + Emit(Inst, OS); > + break; > + } > > default: > EmitALUInstr(MI, Fixups, OS); > diff --git a/lib/Target/R600/R600EmitClauseMarkers.cpp > b/lib/Target/R600/R600EmitClauseMarkers.cpp > new file mode 100644 > index 0000000..b869c88 > --- /dev/null > +++ b/lib/Target/R600/R600EmitClauseMarkers.cpp > @@ -0,0 +1,243 @@ > +//===-- R600EmitClauseMarkers.cpp - Emit CF_ALU > ---------------------------===// > +// > +// The LLVM Compiler Infrastructure > +// > +// This file is distributed under the University of Illinois Open Source > +// License. See LICENSE.TXT for details. > +// > +//===----------------------------------------------------------------------===// > +// > +/// \file > +/// Add CF_ALU. R600 Alu instructions are grouped in clause which can hold > +/// 128 Alu instructions ; these instructions can access up to 4 prefetched > +/// 4 lines of 16 registers from constant buffers. Such ALU clauses are > +/// initiated by CF_ALU instructions. > +//===----------------------------------------------------------------------===// > + > +#include "AMDGPU.h" > +#include "R600Defines.h" > +#include "R600InstrInfo.h" > +#include "R600MachineFunctionInfo.h" > +#include "R600RegisterInfo.h" > +#include "llvm/CodeGen/MachineFunctionPass.h" > +#include "llvm/CodeGen/MachineInstrBuilder.h" > +#include "llvm/CodeGen/MachineRegisterInfo.h" > + > +namespace llvm { > + > +class R600EmitClauseMarkersPass : public MachineFunctionPass { > + > +private: > + static char ID; > + const R600InstrInfo *TII; > + > + unsigned OccupiedDwords(MachineInstr *MI) const { > + switch (MI->getOpcode()) { > + case AMDGPU::INTERP_PAIR_XY: > + case AMDGPU::INTERP_PAIR_ZW: > + case AMDGPU::INTERP_VEC_LOAD: > + case AMDGPU::DOT4_eg_pseudo: > + case AMDGPU::DOT4_r600_pseudo: > + return 4; > + case AMDGPU::KILL: > + return 0; > + default: > + break; > + } > + > + if(TII->isVector(*MI) || > + TII->isCubeOp(MI->getOpcode()) || > + TII->isReductionOp(MI->getOpcode())) > + return 4; > + > + unsigned NumLiteral = 0; > + for (MachineInstr::mop_iterator It = MI->operands_begin(), > + E = MI->operands_end(); It != E; ++It) { > + MachineOperand &MO = *It; > + if (MO.isReg() && MO.getReg() == AMDGPU::ALU_LITERAL_X) > + ++NumLiteral; > + } > + return 1 + NumLiteral; > + } > + > + bool isALU(const MachineInstr *MI) const { > + if (TII->isALUInstr(MI->getOpcode())) > + return true; > + if (TII->isVector(*MI) || TII->isCubeOp(MI->getOpcode())) > + return true; > + switch (MI->getOpcode()) { > + case AMDGPU::INTERP_PAIR_XY: > + case AMDGPU::INTERP_PAIR_ZW: > + case AMDGPU::INTERP_VEC_LOAD: > + case AMDGPU::COPY: > + case AMDGPU::DOT4_eg_pseudo: > + case AMDGPU::DOT4_r600_pseudo: > + return true; > + default: > + return false; > + } > + } > + > + bool IsTrivialInst(MachineInstr *MI) const { > + switch (MI->getOpcode()) { > + case AMDGPU::KILL: > + case AMDGPU::RETURN: > + return true; > + default: > + return false; > + } > + } > + > + // Register Idx, then Const value > + std::vector<std::pair<unsigned, unsigned> > ExtractConstRead(MachineInstr > *MI) > + const { > + const R600Operands::Ops OpTable[3][2] = { > + {R600Operands::SRC0, R600Operands::SRC0_SEL}, > + {R600Operands::SRC1, R600Operands::SRC1_SEL}, > + {R600Operands::SRC2, R600Operands::SRC2_SEL}, > + }; > + std::vector<std::pair<unsigned, unsigned> > Result; > + > + if (!TII->isALUInstr(MI->getOpcode())) > + return Result; > + for (unsigned j = 0; j < 3; j++) { > + int SrcIdx = TII->getOperandIdx(MI->getOpcode(), OpTable[j][0]); > + if (SrcIdx < 0) > + break; > + if (MI->getOperand(SrcIdx).getReg() == AMDGPU::ALU_CONST) { > + unsigned Const = MI->getOperand( > + TII->getOperandIdx(MI->getOpcode(), OpTable[j][1])).getImm(); > + Result.push_back(std::pair<unsigned, unsigned>(SrcIdx, Const)); > + } > + } > + return Result; > + } > + > + std::pair<unsigned, unsigned> getAccessedBankLine(unsigned Sel) const { > + return std::pair<unsigned, unsigned>( > + ((Sel >> 2) - 512) >> 12, // Bank Number > + ((((Sel >> 2) - 512) & 4095) >> 5) << 1); // Line number Could you comment this code and / or replace these magic numbers with named constants. It's not clear what is going on here. > + } > + > + bool SubstituteKCacheBank(MachineInstr *MI, > + std::vector<std::pair<unsigned, unsigned> > &CachedConsts) const { > + std::vector<std::pair<unsigned, unsigned> > UsedKCache; > + std::vector<std::pair<unsigned, unsigned> > Consts = > ExtractConstRead(MI); > + assert(TII->isALUInstr(MI->getOpcode()) && "Can't assign Const"); > + for (unsigned i = 0, n = Consts.size(); i < n; ++i) { > + unsigned Sel = Consts[i].second; > + unsigned Chan = Sel & 3, Index = ((Sel >> 2) - 512) & 31; > + unsigned KCacheIndex = Index * 4 + Chan; > + const std::pair<unsigned, unsigned> &BankLine = > getAccessedBankLine(Sel); > + if (CachedConsts.empty()) { > + CachedConsts.push_back(BankLine); > + UsedKCache.push_back(std::pair<unsigned, unsigned>(0, KCacheIndex)); > + continue; > + } > + if (CachedConsts[0] == BankLine) { > + UsedKCache.push_back(std::pair<unsigned, unsigned>(0, KCacheIndex)); > + continue; > + } > + if (CachedConsts.size() == 1) { > + CachedConsts.push_back(BankLine); > + UsedKCache.push_back(std::pair<unsigned, unsigned>(1, KCacheIndex)); > + continue; > + } > + if (CachedConsts[1] == BankLine) { > + UsedKCache.push_back(std::pair<unsigned, unsigned>(1, KCacheIndex)); > + continue; > + } > + return false; > + } > + > + for (unsigned i = 0, n = Consts.size(); i < n; ++i) { > + switch(UsedKCache[i].first) { > + case 0: > + MI->getOperand(Consts[i].first).setReg( > + AMDGPU::R600_KC0RegClass.getRegister(UsedKCache[i].second)); > + break; > + case 1: > + MI->getOperand(Consts[i].first).setReg( > + AMDGPU::R600_KC1RegClass.getRegister(UsedKCache[i].second)); > + break; > + default: > + llvm_unreachable("Wrong Cache Line"); > + } > + } > + return true; > + } > + > + MachineBasicBlock::iterator > + MakeALUClause(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const > { > + MachineBasicBlock::iterator ClauseHead = I; > + std::vector<std::pair<unsigned, unsigned> > KCacheBanks; > + bool PushBeforeModifier = false; > + unsigned AluInstCount = 0; > + for (MachineBasicBlock::iterator E = MBB.end(); I != E; ++I) { > + if (IsTrivialInst(I)) > + continue; > + if (I->getOpcode() == AMDGPU::PRED_X) { > + uint64_t Flags = I->getOperand(3).getImm(); You should use TII->getFlagOp(I) to retrieve the flag operand. > + if (Flags & MO_FLAG_MASK) > + PushBeforeModifier = true; > + AluInstCount ++; > + continue; > + } > + if (!isALU(I)) > + break; > + if (TII->isALUInstr(I->getOpcode()) && > + !SubstituteKCacheBank(I, KCacheBanks)) > + break; > + AluInstCount += OccupiedDwords(I); > + if (AluInstCount > 124) > + break; > + } > + unsigned Opcode = PushBeforeModifier ? > + AMDGPU::CF_ALU_PUSH_BEFORE : AMDGPU::CF_ALU; > + BuildMI(MBB, ClauseHead, MBB.findDebugLoc(ClauseHead), TII->get(Opcode)) > + .addImm(0) // ADDR > + .addImm(KCacheBanks.empty()?0:KCacheBanks[0].first) // KB0 > + .addImm((KCacheBanks.size() < 2)?0:KCacheBanks[1].first) // KB1 > + .addImm(2) // KM0 > + .addImm(2) // KM1 > + .addImm(KCacheBanks.empty()?0:KCacheBanks[0].second) // KLINE0 > + .addImm((KCacheBanks.size() < 2)?0:KCacheBanks[1].second) // KLINE1 > + .addImm(AluInstCount); // COUNT > + return I; > + } > + > +public: > + R600EmitClauseMarkersPass(TargetMachine &tm) : MachineFunctionPass(ID), > + TII (static_cast<const R600InstrInfo *>(tm.getInstrInfo())) { } > + > + virtual bool runOnMachineFunction(MachineFunction &MF) { > + for (MachineFunction::iterator BB = MF.begin(), BB_E = MF.end(); > + BB != BB_E; ++BB) { > + MachineBasicBlock &MBB = *BB; > + MachineBasicBlock::iterator I = MBB.begin(); > + if (I->getOpcode() == AMDGPU::CF_ALU) > + continue; // BB was already parsed > + for (MachineBasicBlock::iterator E = MBB.end(); I != E;) { > + if (isALU(I)) > + I = MakeALUClause(MBB, I); > + else > + ++I; > + } > + } > + return false; > + } > + > + const char *getPassName() const { > + return "R600 Emit Clause Markers Pass"; > + } > +}; > + > +char R600EmitClauseMarkersPass::ID = 0; > + > +} > + > + > +llvm::FunctionPass *llvm::createR600EmitClauseMarkers(TargetMachine &TM) { > + return new R600EmitClauseMarkersPass(TM); > +} > + > diff --git a/lib/Target/R600/R600Instructions.td > b/lib/Target/R600/R600Instructions.td > index 69e3d7e..6d98efa 100644 > --- a/lib/Target/R600/R600Instructions.td > +++ b/lib/Target/R600/R600Instructions.td > @@ -351,9 +351,9 @@ class R600_1OP <bits<11> inst, string opName, list<dag> > pattern, > (ins WRITE:$write, OMOD:$omod, REL:$dst_rel, CLAMP:$clamp, > R600_Reg32:$src0, NEG:$src0_neg, REL:$src0_rel, > ABS:$src0_abs, SEL:$src0_sel, > LAST:$last, R600_Pred:$pred_sel, LITERAL:$literal), > - !strconcat(opName, > + !strconcat(" ", opName, I'm guessing this additional whitespace is meant to indent instructions that are inside a clause. Is there any way this indentation can be done in the assembly printer? > "$clamp $dst$write$dst_rel$omod, " > - "$src0_neg$src0_abs$src0$src0_sel$src0_abs$src0_rel, " > + "$src0_neg$src0_abs$src0$src0_abs$src0_rel, " Does this change break any tests? > "$literal $pred_sel$last"), > pattern, > itin>, > @@ -392,10 +392,10 @@ class R600_2OP <bits<11> inst, string opName, list<dag> > pattern, > R600_Reg32:$src0, NEG:$src0_neg, REL:$src0_rel, > ABS:$src0_abs, SEL:$src0_sel, > R600_Reg32:$src1, NEG:$src1_neg, REL:$src1_rel, > ABS:$src1_abs, SEL:$src1_sel, > LAST:$last, R600_Pred:$pred_sel, LITERAL:$literal), > - !strconcat(opName, > + !strconcat(" ", opName, > "$clamp > $update_exec_mask$update_pred$dst$write$dst_rel$omod, " > - "$src0_neg$src0_abs$src0$src0_sel$src0_abs$src0_rel, " > - "$src1_neg$src1_abs$src1$src1_sel$src1_abs$src1_rel, " > + "$src0_neg$src0_abs$src0$src0_abs$src0_rel, " > + "$src1_neg$src1_abs$src1$src1_abs$src1_rel, " > "$literal $pred_sel$last"), > pattern, > itin>, > @@ -430,10 +430,10 @@ class R600_3OP <bits<5> inst, string opName, list<dag> > pattern, > R600_Reg32:$src1, NEG:$src1_neg, REL:$src1_rel, SEL:$src1_sel, > R600_Reg32:$src2, NEG:$src2_neg, REL:$src2_rel, SEL:$src2_sel, > LAST:$last, R600_Pred:$pred_sel, LITERAL:$literal), > - !strconcat(opName, "$clamp $dst$dst_rel, " > - "$src0_neg$src0$src0_sel$src0_rel, " > - "$src1_neg$src1$src1_sel$src1_rel, " > - "$src2_neg$src2$src2_sel$src2_rel, " > + !strconcat(" ", opName, "$clamp $dst$dst_rel, " > + "$src0_neg$src0$src0_rel, " > + "$src1_neg$src1$src1_rel, " > + "$src2_neg$src2$src2_rel, " > "$literal $pred_sel$last"), > pattern, > itin>, > @@ -765,6 +765,67 @@ class ExportBufInst : InstR600ISA<( > let Inst{63-32} = Word1; > } > > +//===----------------------------------------------------------------------===// > +// Control Flow Instructions > +//===----------------------------------------------------------------------===// > + > +class CF_ALU_WORD0 { > + field bits<32> Word0; > + > + bits<22> ADDR; > + bits<4> KCACHE_BANK0; > + bits<4> KCACHE_BANK1; > + bits<2> KCACHE_MODE0; > + > + let Word0{21-0} = ADDR; > + let Word0{25-22} = KCACHE_BANK0; > + let Word0{29-26} = KCACHE_BANK1; > + let Word0{31-30} = KCACHE_MODE0; > +} > + > +class CF_ALU_WORD1 { > + field bits<32> Word1; > + > + bits<2> KCACHE_MODE1; > + bits<8> KCACHE_ADDR0; > + bits<8> KCACHE_ADDR1; > + bits<7> COUNT; > + bits<1> ALT_CONST; > + bits<4> CF_INST; > + bits<1> WHOLE_QUAD_MODE; > + bits<1> BARRIER; > + > + let Word1{1-0} = KCACHE_MODE1; > + let Word1{9-2} = KCACHE_ADDR0; > + let Word1{17-10} = KCACHE_ADDR1; > + let Word1{24-18} = COUNT; > + let Word1{25} = ALT_CONST; > + let Word1{29-26} = CF_INST; > + let Word1{30} = WHOLE_QUAD_MODE; > + let Word1{31} = BARRIER; > +} > + > +class ALU_CLAUSE<bits<4> inst, string OpName> : AMDGPUInst <(outs), > +(ins i32imm:$ADDR, i32imm:$KCACHE_BANK0, i32imm:$KCACHE_BANK1, > i32imm:$KCACHE_MODE0, i32imm:$KCACHE_MODE1, > +i32imm:$KCACHE_ADDR0, i32imm:$KCACHE_ADDR1, i32imm:$COUNT), > +"CF_ALU $COUNT, @$ADDR, " > +"KC0[CB$KCACHE_BANK0:$KCACHE_ADDR0-$KCACHE_ADDR0+32]" > +", KC1[CB$KCACHE_BANK1:$KCACHE_ADDR1-$KCACHE_ADDR1+32]", > +[] >, CF_ALU_WORD0, CF_ALU_WORD1 { > + field bits<64> Inst; > + > + let CF_INST = inst; > + let ALT_CONST = 0; > + let WHOLE_QUAD_MODE = 0; > + let BARRIER = 1; > + > + let Inst{31-0} = Word0; > + let Inst{63-32} = Word1; > +} > + > +def CF_ALU : ALU_CLAUSE<8, "ALU">; > +def CF_ALU_PUSH_BEFORE : ALU_CLAUSE<9, "ALU_PUSH_BEFORE">; > + > let Predicates = [isR600toCayman] in { > > > //===----------------------------------------------------------------------===// > @@ -1407,7 +1468,7 @@ let hasSideEffects = 1 in { > def EG_ExportBuf : ExportBufInst { > let Word1{19-16} = 1; // BURST_COUNT > let Word1{20} = 1; // VALID_PIXEL_MODE > - let Word1{21} = eop; > + let Word1{21} = 0; I'm not sure why this change is here, but it will break some compute shaders. > let Word1{29-22} = inst; > let Word1{30} = 0; // MARK > let Word1{31} = 1; // BARRIER > @@ -1421,7 +1482,7 @@ let usesCustomInserter = 1 in { > > class RAT_WRITE_CACHELESS_eg <dag ins, bits<4> comp_mask, string name, > list<dag> pattern> > - : EG_CF_RAT <0x57, 0x2, 0, (outs), ins, > + : EG_CF_RAT <0x57, 0x0, 0, (outs), ins, > !strconcat(name, " $rw_gpr, $index_gpr, $eop"), pattern> { This change is probably by accident too. > let RIM = 0; > // XXX: Have a separate instruction for non-indexed writes. > diff --git a/lib/Target/R600/R600RegisterInfo.td > b/lib/Target/R600/R600RegisterInfo.td > index ce5994c..3ee6623 100644 > --- a/lib/Target/R600/R600RegisterInfo.td > +++ b/lib/Target/R600/R600RegisterInfo.td > @@ -43,6 +43,37 @@ foreach Index = 0-127 in { > Index>; > } > > +// KCACHE_BANK0 > +foreach Index = 159-128 in { > + foreach Chan = [ "X", "Y", "Z", "W" ] in { > + // 32-bit Temporary Registers > + def KC0_#Index#_#Chan : R600RegWithChan <"KC0["#Index#"-128]."#Chan, > Index, Chan>; > + } > + // 128-bit Temporary Registers > + def KC0_#Index#_XYZW : R600Reg_128 <"KC0["#Index#"-128].XYZW", > + [!cast<Register>("KC0_"#Index#"_X"), > + !cast<Register>("KC0_"#Index#"_Y"), > + !cast<Register>("KC0_"#Index#"_Z"), > + !cast<Register>("KC0_"#Index#"_W")], > + Index>; > +} > + > +// KCACHE_BANK1 > +foreach Index = 191-159 in { > + foreach Chan = [ "X", "Y", "Z", "W" ] in { > + // 32-bit Temporary Registers > + def KC1_#Index#_#Chan : R600RegWithChan <"KC1["#Index#"-159]."#Chan, > Index, Chan>; > + } > + // 128-bit Temporary Registers > + def KC1_#Index#_XYZW : R600Reg_128 <"KC1["#Index#"-159].XYZW", > + [!cast<Register>("KC1_"#Index#"_X"), > + !cast<Register>("KC1_"#Index#"_Y"), > + !cast<Register>("KC1_"#Index#"_Z"), > + !cast<Register>("KC1_"#Index#"_W")], > + Index>; > +} > + > + > // Array Base Register holding input in FS > foreach Index = 448-480 in { > def ArrayBase#Index : R600Reg<"ARRAY_BASE", Index>; > @@ -80,6 +111,38 @@ def R600_Addr : RegisterClass <"AMDGPU", [i32], 127, (add > (sequence "Addr%u_X", > > } // End isAllocatable = 0 > > +def R600_KC0_X : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC0_%u_X", 128, 159))>; > + > +def R600_KC0_Y : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC0_%u_Y", 128, 159))>; > + > +def R600_KC0_Z : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC0_%u_Z", 128, 159))>; > + > +def R600_KC0_W : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC0_%u_W", 128, 159))>; > + > +def R600_KC0 : RegisterClass <"AMDGPU", [f32, i32], 32, > + (interleave R600_KC0_X, R600_KC0_Y, > + R600_KC0_Z, R600_KC0_W)>; > + > +def R600_KC1_X : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC1_%u_X", 160, 191))>; > + > +def R600_KC1_Y : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC1_%u_Y", 160, 191))>; > + > +def R600_KC1_Z : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC1_%u_Z", 160, 191))>; > + > +def R600_KC1_W : RegisterClass <"AMDGPU", [f32, i32], 32, > + (add (sequence "KC1_%u_W", 160, 191))>; > + > +def R600_KC1 : RegisterClass <"AMDGPU", [f32, i32], 32, > + (interleave R600_KC1_X, R600_KC1_Y, > + R600_KC1_Z, R600_KC1_W)>; > + > def R600_TReg32_X : RegisterClass <"AMDGPU", [f32, i32], 32, > (add (sequence "T%u_X", 0, 127), AR_X)>; > > -- > 1.8.1.4 > > _______________________________________________ > mesa-dev mailing list > mesa-dev@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/mesa-dev _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev