This is an archive of the discontinued LLVM Phabricator instance.

[SystemZ] Allow fp/int casting into inline assembly operands
ClosedPublic

Authored by jonpa on Mar 14 2023, 8:55 AM.

Details

Summary

Allow bitcasting of fp/vec values into inline assembly 'r' operands, int/vec operands into 'f' operands, and int/fp operands into 'v' operands. This now matches GCCs behavior.

For the 'f' constraint, I changed the register class assigned for 128-bit to depend on the vector facility (using getRegClassFor()). This was needed to match GCCs behavior, but I am now a little surprised that this was always FP128BitRegClass..?

The casting between i128 and f128 required some extra work as i128 is untyped.

Diff Detail

Event Timeline

jonpa created this revision.Mar 14 2023, 8:55 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 14 2023, 8:55 AM
jonpa requested review of this revision.Mar 14 2023, 8:55 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 14 2023, 8:55 AM
iii added inline comments.Mar 15 2023, 6:34 PM
llvm/test/CodeGen/SystemZ/inline-asm-fp-int-casting.ll
44

Did this work for you in GCC? I tried:

__int128 foo(__int128 bar) {
    asm("" : "+f" (bar));
    return bar;
}

and got:

error: inconsistent operand constraints in an ‘asm’

I wanted to give it a try, because I would expect ld/std here, like for long double.

iii added a comment.EditedMar 16 2023, 1:30 AM

I can't really comment on the implementation, but the examples in the testcase look good to me.

llvm/test/CodeGen/SystemZ/inline-asm-fp-int-casting.ll
44

My bad, I should have specified -march=z13.

jonpa marked 2 inline comments as done.Mar 16 2023, 3:32 AM

The 128-bit logic doesn't look quite right to me. I think what should be used there is:

  • "r" constraint - GPR register pair
  • "f" constraint - FPR register pair if hard-float, error if soft-float
  • "v" constraint - single VR if vector support enabled, error otherwise

In particular, for "f" I believe this should *not* depend on the -march level, that would be very confusing. (I'm actually not sure GCC is getting this right in all cases either.) In any case, we definitely need tests verifying this for pre-z13, z13, and z14/later.

llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1110

All this CW_Default stuff is confusing - isn't this, well, the default?

jonpa updated this revision to Diff 507047.Mar 21 2023, 10:46 AM

Updated patch per review.

Added more tests, but left this one out for zEC12 as it failed (not sure exactly why):

define <4 x i32> @vec128_and_r(<4 x i32> %cc_dep1) {
entry:

%0 = tail call <4 x i32> asm sideeffect "", "=r,0"(<4 x i32> %cc_dep1)
ret <4 x i32> %0

}

llc: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp:733: void getCopyToPartsVector(llvm::SelectionDAG&, const llvm::SDLoc&, llvm::SDValue, llvm::SDValue*, unsigned int, llvm::MVT, const llvm::Value*, std:\
:optional<unsigned int>): Assertion `RegisterVT == PartVT && "Part type doesn't match vector breakdown!"' failed.

Is this expected to fail?

jonpa marked an inline comment as done.Mar 21 2023, 10:47 AM
jonpa added inline comments.
llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1110

Yeah, I was just following the pattern in use, but I guess it's more readable this way.

uweigand added inline comments.Mar 21 2023, 11:24 AM
llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1110

Ah, sorry - I overlooked the default was CW_Invalid, not CW_Default. And in fact the default has to be CW_Invalid, since this is critical for the various constant constraints like 'I'. If the argument is not an in-range constant, this has to be refused.

1114

Also, this makes me wonder now if we should return CW_Invalid here if soft-float (and similarly for 'v' and no-vector).

llvm/test/CodeGen/SystemZ/inline-asm-fp-int-casting.ll
39

This difference between z15 and z13 is a bit weird. Both instruction sequences would be correct and available on both architectures, so I'm not sure why it chooses a different one. (Also, I'm not actually sure which one performs better ...) - That may be something to look at as a follow-on.

llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll
10 ↗(On Diff #507047)

Hmm, I think the previous error (reported via emitInlineAsmError) is better - this is not an LLVM bug (which is what report_fatal_error is primarily for), but rather a user error.

jonpa updated this revision to Diff 507447.Mar 22 2023, 11:37 AM
jonpa marked an inline comment as done.

Patch updated per review - see inline comments.

llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1110

ouch - yeah, I guess it needs to handle those cases as well.

1114

IIUC, the multiple-alternative decision is the same for all operands, and if one of the operands is invalid in that alternative, the search continues still in other alternatives. So I guess it would make sense to return CW_Invalid and then perhaps end up with a legal alternative, rather than giving the error message in case it would end up with the illegal alternative.

llvm/test/CodeGen/SystemZ/inline-asm-fp-int-casting.ll
39

The i128 load is bitcasted into f128, and becomes a f128 load. So I guess this then relates to the fact that for z13 we use FP128BitRegClass for f128, and with z14 and later instead the VR128BitRegClass. I guess VectorEnhancements1 is the difference and that makes it worthwhile to use VR128. Yeah, it seems actually better to have two independent instructions and maybe that could be worth trying on z15 as well.

llvm/test/CodeGen/SystemZ/soft-float-inline-asm-01.ll
10 ↗(On Diff #507047)

It seems the easy thing to do is to return nullptr for the register class. This diagnostic is not quite as informative, though... (not sure we could do like emitInlineAsmError() and call LLVMContext::emitError() with a better message as the DAG pointer/LLVMContext is not available here).

uweigand added inline comments.Mar 23 2023, 8:48 AM
llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1199

Isn't this the same the original code (base class) already does? I don't think we need to change this here at all.

1216

However, I'm wondering if we shouldn't also make the same set of changes in this block, so enable the same logic for explicitly named register constraints like {f0}.

1463

Maybe we should just merge the two if blocks into a something like:

if (ValueVT.getSizeInBits() == 128 && NumParts == 1 && PartVT == MVT::Untyped) {
      if (ValueVT != MVT::i128)
         Val = DAG.getNode(ISD::BITCAST, SDLoc(Val), MVT::i128, Val);
      Parts[0] = lowerI128ToGR128(DAG, Val);
}

and similarly below.

1485

It's a bit confusing that this accepts any type with getSizeInBits 128, but then hardcodes MVT::f128 as result type. Shouldn't this simply bitcast to ValueVT then?

jonpa updated this revision to Diff 508113.Mar 24 2023, 8:53 AM
jonpa marked 4 inline comments as done.

Patch updated per review.

llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
1199

ok

1463

Yes - and the getBitcast() has the type check so even better.

I removed the assertions because they didn't seem to be of much use anymore now that we are bitcasting ValueVT and also checking carefully in the if statement.

1485

Makes sense.

uweigand accepted this revision.Mar 24 2023, 10:27 AM

OK; this version LGTM now. Thanks!

This revision is now accepted and ready to land.Mar 24 2023, 10:27 AM
This revision was landed with ongoing or failed builds.Mar 24 2023, 11:57 AM
This revision was automatically updated to reflect the committed changes.