Here's the big RFC. A I've gone through and designed patterns for AVX, I quickly realized that the existing SSE pattern specification, while functional, is less than ideal in terms of maintenance. In particular, a number of nearly-identical patterns are specified all over for nearly-identical instructions. For example: let Constraints = "$src1 = $dst" in { multiclass basic_sse1_fp_binop_rm<bits<8> opc, string OpcodeStr, SDNode OpNode, Intrinsic F32Int, bit Commutable = 0> { // Scalar operation, reg+reg. def SSrr : SSI<opc, MRMSrcReg, (outs FR32:$dst), (ins FR32:$src1, FR32:$src2), !strconcat(OpcodeStr, "ss\t{$src2, $dst|$dst, $src2}"), [(set FR32:$dst, (OpNode FR32:$src1, FR32:$src2))]> { let isCommutable = Commutable; } // Scalar operation, reg+mem. def SSrm : SSI<opc, MRMSrcMem, (outs FR32:$dst), (ins FR32:$src1, f32mem:$src2), !strconcat(OpcodeStr, "ss\t{$src2, $dst|$dst, $src2}"), [(set FR32:$dst, (OpNode FR32:$src1, (load addr:$src2)))]>; // Vector operation, reg+reg. def PSrr : PSI<opc, MRMSrcReg, (outs VR128:$dst), (ins VR128:$src1, VR128:$src2), !strconcat(OpcodeStr, "ps\t{$src2, $dst|$dst, $src2}"), [(set VR128:$dst, (v4f32 (OpNode VR128:$src1, VR128:$src2)))]> { let isCommutable = Commutable; } // Vector operation, reg+mem. def PSrm : PSI<opc, MRMSrcMem, (outs VR128:$dst), (ins VR128:$src1, f128mem:$src2), !strconcat(OpcodeStr, "ps\t{$src2, $dst|$dst, $src2}"), [(set VR128:$dst, (OpNode VR128:$src1, (memopv4f32 addr:$src2)))]>; } These are all essentially the same except that ModRM formats, types and register classes change. For patterns that access memory there are special "memory access operators" like memopv4f32. But the base pattern of dest = src1 op src2 is the same. Worse yet: let Constraints = "$src1 = $dst" in { multiclass basic_sse2_fp_binop_rm<bits<8> opc, string OpcodeStr, SDNode OpNode, Intrinsic F64Int, bit Commutable = 0> { // Scalar operation, reg+reg. def SDrr : SDI<opc, MRMSrcReg, (outs FR64:$dst), (ins FR64:$src1, FR64:$src2), !strconcat(OpcodeStr, "sd\t{$src2, $dst|$dst, $src2}"), [(set FR64:$dst, (OpNode FR64:$src1, FR64:$src2))]> { let isCommutable = Commutable; } // Scalar operation, reg+mem. def SDrm : SDI<opc, MRMSrcMem, (outs FR64:$dst), (ins FR64:$src1, f64mem:$src2), !strconcat(OpcodeStr, "sd\t{$src2, $dst|$dst, $src2}"), [(set FR64:$dst, (OpNode FR64:$src1, (load addr:$src2)))]>; // Vector operation, reg+reg. def PDrr : PDI<opc, MRMSrcReg, (outs VR128:$dst), (ins VR128:$src1, VR128:$src2), !strconcat(OpcodeStr, "pd\t{$src2, $dst|$dst, $src2}"), [(set VR128:$dst, (v2f64 (OpNode VR128:$src1, VR128:$src2)))]> { let isCommutable = Commutable; } // Vector operation, reg+mem. def PDrm : PDI<opc, MRMSrcMem, (outs VR128:$dst), (ins VR128:$src1, f128mem:$src2), !strconcat(OpcodeStr, "pd\t{$src2, $dst|$dst, $src2}"), [(set VR128:$dst, (OpNode VR128:$src1, (memopv2f64 addr:$src2)))]>; } This looks identical to basic_sse1_fp_binop_rm except it's SD/PD instead of SS/PS and types and register classes differ in a predictable way. So we already have two "levels" or redundancy: one within a single multiclass and then redudancy across multiclasses. This gets even worse with more complicated patterns like converts. Essentially the same complex pattern gets duplicated for the variously-sized converts. Bug fixes in once place need to be replicated everywhere and it's easy to miss one or two. This is the very definition of "maintenance problem." Moreover, the various SSE levels were implemented at different times and do things subtly differently. For example: SSE1 : def ANDNPSrr : PSI<0x55, MRMSrcReg, (outs VR128:$dst), (ins VR128:$src1, VR128:$src2), "andnps\t{$src2, $dst|$dst, $src2}", [(set VR128:$dst, (v2i64 (and (xor VR128:$src1, (bc_v2i64 (v4i32 immAllOnesV))), VR128:$src2)))]>; SSE2 : def ANDNPDrr : PDI<0x55, MRMSrcReg, (outs VR128:$dst), (ins VR128:$src1, VR128:$src2), "andnpd\t{$src2, $dst|$dst, $src2}", [(set VR128:$dst, (and (vnot (bc_v2i64 (v2f64 VR128:$src1))), (bc_v2i64 (v2f64 VR128:$src2))))]>; Note the use of xor vs. vnot and the different placement of the bc* fragments and use of type specifiers. I wonder if we even match both of these. And naming is not consistent: def Int_CVTSS2SIrr : SSI<0x2D, MRMSrcReg, (outs GR32:$dst), (ins VR128:$src), def MOVUPSrm_Int : PSI<0x10, MRMSrcMem, (outs VR128:$dst), (ins f128mem:$src), Furthermore, the current scheme ties patterns to prefix encodings and Requires predicates: // Scalar operation, reg+reg. def SSrr : SSI<opc, MRMSrcReg, (outs FR32:$dst), (ins FR32:$src1, FR32:$src2), !strconcat(OpcodeStr, "ss\t{$src2, $dst|$dst, $src2}"), [(set FR32:$dst, (OpNode FR32:$src1, FR32:$src2))]> { let isCommutable = Commutable; }>From X86InstrFormats.td:class SSI<bits<8> o, Format F, dag outs, dag ins, string asm, list<dag> pattern> : I<o, F, outs, ins, asm, pattern>, XS, Requires<[HasSSE1]>; For AVX we would need a different set of format classes because while AVX could reuse the existing XS class (it's recoded as part of the VEX prefix so we still need the information XS provides), "Requires<[HasSSE1]>" is certainly inappropriate. Initially I started factoring things out to separate XS and other prefix classes from Requires<> but that didn't solve the pattern problems I mentioned above. All of this complication gets multipled with AVX because AVX recodes all of the legacy SSE instructions using VEX to provide three-address forms. So if we were to follow the existing sceheme, we would duplicate *all* of X86InstrSSE.td and edit patterns to match three-address modes and then add the 256-bit patterns on top of that, effectively duplicating X86InstrSSE.td a second time. This is not scalable. So what I've done is a little experiment to see if I can unify all SSE and AVX SIMD instructions under one framework. I'll leave MMX and 3dNow alone since they're oddballs and hardly anyone uses them. Essentially I've created a set of base pattern classes that are very generic. These contain the basic asm string templates and dag patterns we want to match. These classes are parameterized by things like register class, operand type, ModRM format and "memory access operation." I've also created patterns that take a fully specified asm string and/or dag pattern to provide flexibility for "oddball" instructions. Multiclasses sit on top of the patterns and aggregate various legal combinations (e.g. SS, SD, PS, PD for basic arithmetic). There's a set of base multiclasses and a set of derived multiclasses that aggregate things into legal sets. For example, some SSE instructions are vector-only while others have scalar and vector versions. Some instructions use the XS, XD, TB and OpSize/TB prefixes while others use the TA, T8 and OpSize prefixes. The point of all of this is to write patterns and asm strings *once* for each kind of instruction (binary arithmetic, convert, shuffle, etc.) and then use multiclasses to generate all of the concrete patterns for SSE and AVX. So for example, an ADD would be specified like this: // Arithmetic ops with intrinsics and scalar equivalents defm ADD : sse1_sse2_avx_binary_scalar_xs_xd_vector_tb_ostb_node_intrinsic_rm_rrm< 0x58, // Opcode "add", // asm base opcode name fadd, // SDNode name "add", // Intrinsic base name (we pre-concat int_x86_sse*/avx and // post-contact ps/pd, etc.) 1 // Commutative>;Now the multiclass name is rather unwieldy, I know. That can be changed so don't worry too much about it. I'm more concerned about the overall scheme and that it make sense to you all. I have a Perl script that auto-generates the necessary mutliclass combinations as well as the needed base classes depending on what's in the top-level .td file. For now, I've named that top-level file X86InstrSIMD.td. The Perl script would only be need to run as X86InstrSIMD.td changes. Thus its use would be similar to how we use autoconf today. We only run autoconf / automake when we update the .ac files, not as part of the build process. Initially, X86InstrSIMD.td would define only AVX instructions so it would not impact existing SSE clients. My intent is that X86InstrSIMD.td essentially become the canonical description of all SSE and AVX instructions and X86InstrSSE.td would go away completely. Of course we would not transition away from X86InstrSSE.td until X86InstrSIMD.td is proven to cover all current uses of SSE correctly. The pros of the scheme: * Unify all "important" x86 SIMD instructions into one framework and provide consistency * Specify patterns and asm strings *once* per instruction type / family rather than the current scheme of multiple patterns for essentially the same instruction * Bugfixes / optimizations / new patterns instantly apply to all SSE levels and AVX The cons: * Transition from X86InstrSSE.td * A more complex class hierarchy * A class-generating tool / indirection Personally, I think the pros far outweigh the cons but I realize that this proposes a major change and there are probably cons I haven't considered (and pros as well!). So right now I'm looking for comments. This is the way I intend to go because it's far easier in the long run considering maintenance and future extension. I'll post an example as soon as I have time to package it up and get approval on this end to release it. As of now I have simple arithmetic operations implemented and the proposed scheme seems to work. Right now we're generating simple arithmetic and having it correctly assembled by gas. Thanks for your ideas and input. -Dave
On Apr 30, 2009, at 3:59 PM, David Greene wrote:> Here's the big RFC. > > > Of course we would not transition away from X86InstrSSE.td until > X86InstrSIMD.td is proven to cover all current uses of SSE correctly. > > The pros of the scheme: > > * Unify all "important" x86 SIMD instructions into one framework and > provide > consistencyWhile almost all of this sounds pretty great to me, since I'm also in the process of cleaning up how x86 vector shuffles and shifts are implemented, I'm a little worried by what "important" means. Aren't *all* the instructions important? Was the goal to convert 50% of the instructions over to the new class hierarchy and leave 50% as they currently are?> * Specify patterns and asm strings *once* per instruction type / > family > rather than the current scheme of multiple patterns for essentially > the > same instructionSounds great; you'll probably need some kind of custom lowering to go with this to make sure instructions end up in whatever canonicalized form you've chosen.> * Bugfixes / optimizations / new patterns instantly apply to all SSE > levels > and AVX > > The cons: > > * Transition from X86InstrSSE.tdWhat's the timeline look like for this? Nate
On Friday 01 May 2009 12:25, mon p wang wrote:> > * Unify all "important" x86 SIMD instructions into one framework and > > provide > > consistency > > While almost all of this sounds pretty great to me, since I'm also in > the process of cleaning up how x86 vector shuffles and shifts are > implemented, I'm a little worried by what "important" means. Aren't > *all* the instructions important? Was the goal to convert 50% of the > instructions over to the new class hierarchy and leave 50% as they > currently are?"Important" in this context means all SSE instructions. I'm excluding MMX and 3dNow. I suppose we could add them but I just don't see much point. One could also imagine moving the scalar patterns into this but frankly, the scalar instruction set isn't changing much. The vector instruction set is more likely to be extended which is why I put the focus there. I'm trying to set us up better for the future.> > * Specify patterns and asm strings *once* per instruction type / > > family > > rather than the current scheme of multiple patterns for essentially > > the > > same instruction > > Sounds great; you'll probably need some kind of custom lowering to go > with this to make sure instructions end up in whatever canonicalized > form you've chosen.Maybe. More likely we will need some more pattern fragments. At least that's what I've run into so far.> > * Bugfixes / optimizations / new patterns instantly apply to all SSE > > levels > > and AVX > > > > The cons: > > > > * Transition from X86InstrSSE.td > > What's the timeline look like for this?Good question. What I hope to do is add AVX stuff incrementally. Basic arithmetic works now. But I want to vet this proposal fully before I start adding things to the repository. So the timeline really depends on that process. Once people are comfortable with moving forward, I'll start checking things in as they become available. Moving SSE over is a longer timeline. I would expect that wouldn't be done until 2.7 at the very earliest and probably more like 2.8. -Dave
On Apr 30, 2009, at 3:59 PM, David Greene wrote:> Here's the big RFC. > > A I've gone through and designed patterns for AVX, I quickly > realized that the > existing SSE pattern specification, while functional, is less than > ideal in > terms of maintenance. In particular, a number of nearly-identical > patterns > are specified all over for nearly-identical instructions. For > example:Right. A lot of the X86 backend was written before the current set of tblgen features was available. In particular, multiclasses only got retrofitted in later to avoid some of the duplicated code. Where they are used, they aren't used as well as they could be.> Moreover, the various SSE levels were implemented at different times > and do > things subtly differently. For example: > > Note the use of xor vs. vnot and the different placement of the bc* > fragments > and use of type specifiers. I wonder if we even match both of these.Right, we have the same problem within the GPR operations. Ideally we'd have a multiclass for most arithmetic operations that expands out into 8/16/32/64-bit operations, perhaps even handling reg/reg, reg/ imm, and reg/mem versions all at the same time. Similar things should probably be done for SSE1/2 since it adds double versions of all the float operations that SSE1 has.> For AVX we would need a different set of format classes because > while AVX > could reuse the existing XS class (it's recoded as part of the VEX > prefix so > we still need the information XS provides), "Requires<[HasSSE1]>" is > certainly inappropriate. Initially I started factoring things out > to separate > XS and other prefix classes from Requires<> but that didn't solve > the pattern > problems I mentioned above.Right, a lot of these problems can be solved by some nice refactoring stuff. I'm also hoping that some of the complexity in defining shuffle matching code can be helped by making the definition of the shuffle patterns more declarative within the td file. It would be really nice to say that "this shuffle does a "1,0,3,2 shuffle and has cost 42" and have tblgen generate all the required matching code.> All of this complication gets multipled with AVX because AVX recodes > all of > the legacy SSE instructions using VEX to provide three-address > forms. So if > we were to follow the existing sceheme, we would duplicate *all* of > X86InstrSSE.td and edit patterns to match three-address modes and > then add the > 256-bit patterns on top of that, effectively duplicating > X86InstrSSE.td a > second time. > > This is not scalable.I agree, I think it is unfortunate that AVX decided to do this at an architectural level :).> So what I've done is a little experiment to see if I can unify all > SSE and AVX > SIMD instructions under one framework. I'll leave MMX and 3dNow > alone since > they're oddballs and hardly anyone uses them.Ok. I agree that the similarity being factored here is across SSE1/2/ AVX.> Essentially I've created a set of base pattern classes that are very > generic. > These contain the basic asm string templates and dag patterns we > want to > match. These classes are parameterized by things like register class, > operand type, ModRM format and "memory access operation." I've also > created > patterns that take a fully specified asm string and/or dag pattern > to provide > flexibility for "oddball" instructions.Ok.> The point of all of this is to write patterns and asm strings *once* > for each > kind of instruction (binary arithmetic, convert, shuffle, etc.) and > then use > multiclasses to generate all of the concrete patterns for SSE and AVX.Very nice.> So for example, an ADD would be specified like this: > > // Arithmetic ops with intrinsics and scalar equivalents > defm ADD : > sse1_sse2_avx_binary_scalar_xs_xd_vector_tb_ostb_node_intrinsic_rm_rrm > < > 0x58, // Opcode > "add", // asm base opcode name > fadd, // SDNode name > "add", // Intrinsic base name (we pre-concat int_x86_sse*/avx and > // post-contact ps/pd, etc.) > 1 // Commutative >> ; > > Now the multiclass name is rather unwieldy, I know. That can be > changed so > don't worry too much about it. I'm more concerned about the overall > scheme > and that it make sense to you all.This does look very nice.> I have a Perl script that auto-generates the necessary mutliclass > combinations > as well as the needed base classes depending on what's in the top- > level .td > file. For now, I've named that top-level file X86InstrSIMD.td. > > The Perl script would only be need to run as X86InstrSIMD.td > changes. Thus > its use would be similar to how we use autoconf today. We only run > autoconf / > automake when we update the .ac files, not as part of the build > process.While I agree that we want to refactor this, I really don't think that we should autogenerate .td files from perl. This has a number of significant logistical problems. What is it that perl gives you that we can't enhance tblgen to do directly?> Initially, X86InstrSIMD.td would define only AVX instructions so it > would not > impact existing SSE clients. My intent is that X86InstrSIMD.td > essentially > become the canonical description of all SSE and AVX instructions and > X86InstrSSE.td would go away completely.Instead of slowly building it up and then cutting over, I'd prefer to incrementally move patterns into it, removing them from the other .td files at the same time. This should be a nice clean and continuous refactoring that makes the code monotonically better (smaller).> The pros of the scheme: > > * Unify all "important" x86 SIMD instructions into one framework and > provide > consistencyYay!> * Specify patterns and asm strings *once* per instruction type / > family > rather than the current scheme of multiple patterns for essentially > the > same instructionYay!> * Bugfixes / optimizations / new patterns instantly apply to all SSE > levels > and AVXYay!> The cons: > * Transition from X86InstrSSE.td > * A more complex class hierarchyI'm not worried about these.> * A class-generating tool / indirectionI really don't like this :). If there is something higher level that you need, I think it would be very interesting to carefully consider what the root problem is and whether there is a good solution that we can directly implement in tblgen. It is pretty clear that we can *improve* the current situation with no tblgen enhancements, but I agree that AVX is a nice forcing function that will greatly benefit from a *much improved* target description. -Chris
Hi David, On 30-Apr-09, at 6:59 PM, David Greene wrote:> This is not scalable. > > So what I've done is a little experiment to see if I can unify all > SSE and AVX > SIMD instructions under one framework. I'll leave MMX and 3dNow > alone since > they're oddballs and hardly anyone uses them.I don't want to unnecessarily expand your scope, but while you're doing this, it might make sense to keep in mind the new Larrabee instructions as well. They operate on 512-bit registers, and there's a (slightly indirect) reference available here: http://software.intel.com/en-us/articles/prototype-primitives-guide/ I'm not suggesting adding these now, just that they might be interesting to keep in mind while you're doing this work. Stefanus -- Stefanus Du Toit <stefanus.dutoit at rapidmind.com> RapidMind Inc. phone: +1 519 885 5455 x116 -- fax: +1 519 885 1463
On Friday 01 May 2009 13:46, Chris Lattner wrote:> Right, a lot of these problems can be solved by some nice refactoring > stuff. I'm also hoping that some of the complexity in defining > shuffle matching code can be helped by making the definition of the > shuffle patterns more declarative within the td file. It would be > really nice to say that "this shuffle does a "1,0,3,2 shuffle and has > cost 42" and have tblgen generate all the required matching code.That would be nice. Any ideas how this would work?> I agree, I think it is unfortunate that AVX decided to do this at an > architectural level :).I'm not sure what else would be done.> > So what I've done is a little experiment to see if I can unify all > > SSE and AVX > > SIMD instructions under one framework. I'll leave MMX and 3dNow > > alone since > > they're oddballs and hardly anyone uses them. > > Ok. I agree that the similarity being factored here is across SSE1/2/ > AVX.And SSE3/4.> > I have a Perl script that auto-generates the necessary mutliclass > > combinations > > as well as the needed base classes depending on what's in the top- > > level .td > > file. For now, I've named that top-level file X86InstrSIMD.td. > > > > The Perl script would only be need to run as X86InstrSIMD.td > > changes. Thus > > its use would be similar to how we use autoconf today. We only run > > autoconf / > > automake when we update the .ac files, not as part of the build > > process. > > While I agree that we want to refactor this, I really don't think that > we should autogenerate .td files from perl. This has a number of > significant logistical problems. What is it that perl gives you that > we can't enhance tblgen to do directly?Well, mainly it's because we don't have whatever tblgen enhancements we need. I'll have to think on this some and see if I can come up with some tblgen features that could help. I was writing a lot of these base classes by hand at first, but there are a lot of them (they tend to be very small) and writing them is very mechanical. So we probably can enhance tblgen somehow. I'm just not sure what that looks like right now.> > Initially, X86InstrSIMD.td would define only AVX instructions so it > > would not > > impact existing SSE clients. My intent is that X86InstrSIMD.td > > essentially > > become the canonical description of all SSE and AVX instructions and > > X86InstrSSE.td would go away completely. > > Instead of slowly building it up and then cutting over, I'd prefer to > incrementally move patterns into it, removing them from the other .td > files at the same time. This should be a nice clean and continuous > refactoring that makes the code monotonically better (smaller).Ok, that sounds like a pretty good idea.> > The cons: > > * Transition from X86InstrSSE.td > > * A more complex class hierarchy > > I'm not worried about these.Ok.> > * A class-generating tool / indirection > > I really don't like this :). If there is something higher level that > you need, I think it would be very interesting to carefully consider > what the root problem is and whether there is a good solution that we > can directly implement in tblgen. It is pretty clear that we can > *improve* the current situation with no tblgen enhancements, but I > agree that AVX is a nice forcing function that will greatly benefit > from a *much improved* target description.Your point is well taken. Let me think on this a bit. -Dave
On Friday 01 May 2009 15:05, Stefanus Du Toit wrote:> Hi David, > > On 30-Apr-09, at 6:59 PM, David Greene wrote: > > This is not scalable. > > > > So what I've done is a little experiment to see if I can unify all > > SSE and AVX > > SIMD instructions under one framework. I'll leave MMX and 3dNow > > alone since > > they're oddballs and hardly anyone uses them. > > I don't want to unnecessarily expand your scope, but while you're > doing this, it might make sense to keep in mind the new Larrabee > instructions as well. They operate on 512-bit registers, and there's a > (slightly indirect) reference available here: > > http://software.intel.com/en-us/articles/prototype-primitives-guide/ > > I'm not suggesting adding these now, just that they might be > interesting to keep in mind while you're doing this work.Oh, I'm definitely keeping Larrabee in mind. I've looked at the primitives library and also the Dr. Dobb's article on LRBni: http://www.ddj.com/hpc-high-performance-computing/216402188 I'm designing all of this so it should be relatively easy to plug-in the non-mask/swizzle variants of the Larrabee instructions. Masks and swizzles will require new patterns. I haven't seen a Larrabee ISA manual yet. My hope is that the opcodes and formats for SSE-like instructions will be the same (e.g. a 512-bit ADD will still be 0x58). I think that's a safe assumption. But who knows? Maybe their graphics guys don't talk to their CPU guys. If things are radically different, we won't be able to re-use as much. But it should still be better than what we have now. Also, Larrabee is a great motivator for direct mask/predicate support in LLVM. Anyone want to re-engage in that discussion? -Dave