<div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Thu, Jan 3, 2019 at 1:37 PM Roland Scheidegger <<a href="mailto:sroland@vmware.com">sroland@vmware.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Am 03.01.19 um 18:58 schrieb Jason Ekstrand:<br>
> On Thu, Jan 3, 2019 at 3:39 AM Erik Faye-Lund<br>
> <<a href="mailto:erik.faye-lund@collabora.com" target="_blank">erik.faye-lund@collabora.com</a> <mailto:<a href="mailto:erik.faye-lund@collabora.com" target="_blank">erik.faye-lund@collabora.com</a>>> wrote:<br>
> <br>
>     On Wed, 2019-01-02 at 10:16 -0600, Jason Ekstrand wrote:<br>
>     > On Wed, Jan 2, 2019 at 9:43 AM Ilia Mirkin <<a href="mailto:imirkin@alum.mit.edu" target="_blank">imirkin@alum.mit.edu</a><br>
>     <mailto:<a href="mailto:imirkin@alum.mit.edu" target="_blank">imirkin@alum.mit.edu</a>>><br>
>     > wrote:<br>
>     > > Have a look at the first 4 patches in the series from Jonathan<br>
>     > > Marek<br>
>     > > to address some of these issues:<br>
>     > ><br>
>     > > <a href="https://patchwork.freedesktop.org/series/54295/" rel="noreferrer" target="_blank">https://patchwork.freedesktop.org/series/54295/</a><br>
>     <<a href="https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatchwork.freedesktop.org%2Fseries%2F54295%2F&data=02%7C01%7Csroland%40vmware.com%7Cb5a1b18f854a4533274508d671a52647%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C636821351481601457&sdata=RPh1ZhwqvjBP8eSmE9D%2BErOVgCcmb4kobJVy%2BpJeyIc%3D&reserved=0" rel="noreferrer" target="_blank">https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatchwork.freedesktop.org%2Fseries%2F54295%2F&data=02%7C01%7Csroland%40vmware.com%7Cb5a1b18f854a4533274508d671a52647%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C636821351481601457&sdata=RPh1ZhwqvjBP8eSmE9D%2BErOVgCcmb4kobJVy%2BpJeyIc%3D&reserved=0</a>><br>
>     > ><br>
>     > > Not sure exactly what state that work is in, but I've added<br>
>     > > Jonathan<br>
>     > > to CC, perhaps he can provide an update.<br>
>     > ><br>
>     > > Cheers,<br>
>     > ><br>
>     > >   -ilia<br>
>     > ><br>
>     > > On Wed, Jan 2, 2019 at 6:28 AM Qiang Yu <<a href="mailto:yuq825@gmail.com" target="_blank">yuq825@gmail.com</a><br>
>     <mailto:<a href="mailto:yuq825@gmail.com" target="_blank">yuq825@gmail.com</a>>> wrote:<br>
>     > > ><br>
>     > > > Hi guys,<br>
>     > > ><br>
>     > > > I found the problem with this test fragment shader when lima<br>
>     > > development:<br>
>     > > > uniform int color;<br>
>     > > > void main() {<br>
>     > > >     if (color > 1)<br>
>     > > >         gl_FragColor = vec4(1.0, 0.0, 0.0, 1);<br>
>     > > >     else<br>
>     > > >         gl_FragColor = vec4(0.0, 1.0, 0.0, 1);<br>
>     > > > }<br>
>     > > ><br>
>     > > > nir_print_shader output:<br>
>     > > > impl main {<br>
>     > > >         block block_0:<br>
>     > > >         /* preds: */<br>
>     > > >         vec1 32 ssa_0 = load_const (0x00000001 /* 0.000000 */)<br>
>     > > >         vec4 32 ssa_1 = load_const (0x3f800000 /* 1.000000 */,<br>
>     > > > 0x00000000 /* 0.000000 */, 0x00000000 /* 0.000000 */, 0x3f800000<br>
>     > > /*<br>
>     > > > 1.000000 */)<br>
>     > > >         vec4 32 ssa_2 = load_const (0x00000000 /* 0.000000 */,<br>
>     > > > 0x3f800000 /* 1.000000 */, 0x00000000 /* 0.000000 */, 0x3f800000<br>
>     > > /*<br>
>     > > > 1.000000 */)<br>
>     > > >         vec1 32 ssa_3 = load_const (0x00000000 /* 0.000000 */)<br>
>     > > >         vec1 32 ssa_4 = intrinsic load_uniform (ssa_3) (0, 1, 0)<br>
>     > > /*<br>
>     > > > base=0 */ /* range=1 */ /* component=0 */   /* color */<br>
>     > > >         vec1 32 ssa_5 = slt ssa_0, ssa_4<br>
>     > > >         vec1 32 ssa_6 = fnot ssa_5<br>
>     > > >         vec4 32 ssa_7 = bcsel ssa_6.xxxx, ssa_2, ssa_1<br>
>     > > >         intrinsic store_output (ssa_7, ssa_3) (0, 15, 0) /*<br>
>     > > base=0 */<br>
>     > > > /* wrmask=xyzw */ /* component=0 */       /* gl_FragColor */<br>
>     > > >         /* succs: block_1 */<br>
>     > > >         block block_1:<br>
>     > > > }<br>
>     > > ><br>
>     > > > ssa0 is not converted to float when glsl to nir. I see<br>
>     > > glsl_to_nir.cpp<br>
>     > > > will create flt/ilt/ult<br>
>     > > > based on source type for gpu support native integer, but for gpu<br>
>     > > not<br>
>     > > > support native<br>
>     > > > integer, just create slt for all source type. And in<br>
>     > > > nir_lower_constant_initializers,<br>
>     > > > there's also no type conversion for integer constant.<br>
>     ><br>
>     > This is a generally sticky issue.  In NIR, we have no concept of<br>
>     > types on SSA values which has proven perfectly reasonable and<br>
>     > actually very powerful in a world where integers are supported<br>
>     > natively.  Unfortunately, it causes significant problems for float-<br>
>     > only architectures.<br>
> <br>
>     I would like to take this chance to say that this untyped SSA-value<br>
>     choice has lead to issues in both radeon_si (because LLVM values are<br>
>     typed) and zink (similarly, because SPIR-V values are typed), where we<br>
>     need to to bitcasts on every access because there's just not enough<br>
>     information available to emit variables with the right type.<br>
> <br>
> <br>
> I'm not sure if I agree that the two problems are the same or not... <br>
> More on that in a bit.<br>
>  <br>
> <br>
>     It took us a lot of time to realize that the meta-data from the opcodes<br>
>     doesn't *really* provide this, because the rest of nir doesn't treat<br>
>     values consistently. In fact, this feels arguably more like buggy<br>
>     behavior; why do we even have fmov when all of the time the compiler<br>
>     will emit imovs for floating-point values...? Or why do we have bitcast<br>
> <br>
> <br>
> Why do we have different mov opcodes?  Because they have different<br>
> behavior in the presence of source/destination modifiers.  You likely<br>
> don't use those in radeon or Zink but we do use them on Intel.  That<br>
> being said, I've very seriously considered adding a generic nir_op_mov<br>
> which is entirely typeless and doesn't support modifiers at all and make<br>
> most of core NIR use that.  For that matter, there's no real reason why<br>
> we need fmov with modifiers at all when we could we could easily replace<br>
> "ssa1 = fmov.sat |x|" with "ssa1 = fsat |x|" or "ssa1 = fabs.sat x".  If<br>
> we had a generic nir_op_mov to deal with swizzling etc., the only thing<br>
> having i/fmov would really accomplish is lowering the number of<br>
> different ways you can write "fmov.sat |x|".  The only reason I haven't<br>
> added this nir_op_mov opcode is because it's a pile of work and anyone<br>
> who can't do integers can just implement imov as fmov and it's not a big<br>
> deal.<br>
>  <br>
> <br>
>     I would really love it if we could at least consider making this "we<br>
>     can just treat floats as ints without bitcasts if we feel like it"-<br>
>     design optional for the backend somehow.<br>
> <br>
>     I guess the assumption is that bitcasts are for free? They aren't once<br>
>     you have to emit them and have a back-end remove a bunch of redundant<br>
>     ones... We should already have all the information to trivially place<br>
>     casts for backends that care, yet we currently make it hard (unless<br>
>     your HW/backend happens to be untyped)...<br>
> <br>
>     Is there some way we can perhaps improve this for backends that care?<br>
> <br>
> <br>
> That's an interesting question...<br>
> <br>
> Before answering that question, let's back up and ask why.  From a<br>
> hardware perspective, I know of no graphics hardware that puts types on<br>
> registers.  On Intel, we assign types to sources and destinations of<br>
> instructions but outside a specific instruction, a register is just a<br>
> bag of bits.  I suspect most other hardware is the same.  In the<br>
> hardware that his particular thread was originally about, you could<br>
> argue that they have a type and it's always float.  The real issue that<br>
> the GLES2 people are having is that constants have a bag of bits value<br>
> which may need to be interpreted as integer or float depending on<br>
> context which they do not have when lowering.  They don't care, for<br>
> instance, about the fact that imov or bcsel take integers because they<br>
> know that the sources will either be floats or integers that they've<br>
> lowered to floats and so they know that the result will be a float.<br>
> <br>
> The problem you're describing is in converting from NIR to another IR,<br>
> not to hardware.  In LLVM they made a choice to put types on SSA values<br>
> but then to have the actual semantics be based on the instruction<br>
> itself.  This leads to lots of redundancy in the IR but also lots of<br>
> things you can validate which is kind-of nice.  Is that redundancy<br>
> really useful?  In our experience with NIR, we haven't found it to be<br>
> other than booleans (now sorted), this one constant issue, and<br>
> translating to IRs that have that redundancy.  Then why did they add<br>
> it?  I'm really not sure but it may, at least somewhat, be related to<br>
> the fact that they allow arrays and structs in their SSA values and so<br>
> need full types.  This is another design decision in LLVM which I find<br>
> highly questionable.  What you're is more-or-less that NIR should carry,<br>
> maintain, and validate extra useless information just so we can pass it<br>
> on to LLVM which is going to use it for what, exactly?  Sorry if I'm<br>
> extremely reluctant to make fairly fundamental changes to NIR with no<br>
> better reason than "LLVM did it that way".<br>
> <br>
> There's a second reason why I don't like the idea of putting types on<br>
> SSA values: It's extremely deceptive.  In SPIR-V you're allowed to do an<br>
> OpSConvert with a source that is an unsigned 16-bit integer and a<br>
> destination that is an unsigned 32-bit integer.  What happens?  Your<br>
> uint -> uint cast sign extends!  Yup.... That's what happens.  No joke. <br>
> The same is true of signed vs. unsigned division or modulus.  The end<br>
> result is that the types in SPIR-V are useless and you can't actually<br>
> trust them for anything except bit-size and sometimes labelling<br>
> something as a float vs. int.<br>
This is really a decision of spir-v only though, llvm doesn't have that<br>
nonsense (maybe for making it easier to translate to other languages?) -<br>
there's just int and float types there, with no distinction between<br>
signed and unsigned.<br></blockquote><div><br></div><div>I think with SPIR-V you could probably just pick one and make everything either signed or unsigned.  I'm not immediately aware of any opcodes that require one signedness or the other; most just require an integer or require a float.  That said, this is SPIR-V so I'm not going to bet money on that...<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
You are quite right though that float vs. int is somewhat redundant too<br>
due to the instructions indicating the type. I suppose there might be<br>
reasons why there's different types - hw may use different registers for<br>
instance (whereas of course noone in their right mind would use<br>
different registers for signed vs. unsigned ints), or there might be<br>
some real cost of bitcasts (at least bypass delays are common for cpus<br>
when transitioning values between int and float execution units). For<br>
instance, it makes a difference with x86 sse if you use float or int<br>
loads, which otherwise you couldn't indicate directly (llvm can optimize<br>
this into the right kind of load nowadays even if you use the wrong kind<br>
of variable for the load, e.g. int load then bitcast to float and do<br>
some float op will change it into a float load, but this is IIRC<br>
actually a pretty new ability, and possibly doesn't happen if you<br>
disable enough optimization passes).<br></blockquote><div><br></div><div>Having it for the purpose of register allocation makes sense in the CPU world.  In the GPU world, everything is typically designed float-first and I'm not aware of any hardware has separate int and float registers like x86 does.  That said, hardware changes over time and it's entirely possible that someone will decide that a heterogeneous register file is a good idea on a GPU.  (Technically, most GPUs do have flag regs or accumulators or something but it's not as bad as x86.)  Our of curiosity, do you (or anyone else) know if LLVM actually uses them for that purpose?  I could see that information getting lost in the back-end and them using something else to choose registers.</div><div><br></div><div>--Jason<br></div></div></div>