Petar Avramovic via llvm-dev
2018-Sep-21 09:28 UTC
[llvm-dev] [GlobalISel] Legalize generic instructions that also depend on type of scalar, not only scalar size
Hi, Mips32 has 64 bit floating point instructions, while i64 instructions have to be emulated with i32 instructions. This means that G_LOAD should be custom legalized for s64 integer value, and be legal for s64 floating point value. There are also other generic instructions with the same problem: G_STORE, G_SELECT, G_EXTRACT, and G_INSERT. There are also other configurations where integer and floating point instructions of the same size are not simultaneously available. This problem was already addressed here http://lists.llvm.org/pipermail/llvm-dev/2017-July/114978.html for G_LOAD/G_STORE. Legality of an instruction should not depend on surrounding instructions. Because of that, approach from regbankselect that iterates through uses of G_LOAD def register and checks if some of the uses was an generic floating point instruction should not be an option for legalizer. Since GlobalISel Legalizer cannot distinguish between them using only LLT, only other option that I can see at this moment is having "F" variant of the generic instruction (like this is the case with G_ADD and G_FADD). Is this change possible? Or are there some other approaches for dealing with this problem? Petar
Daniel Sanders via llvm-dev
2018-Sep-21 15:50 UTC
[llvm-dev] [GlobalISel] Legalize generic instructions that also depend on type of scalar, not only scalar size
For G_LOAD/G_STORE it should be ok to use double load/store instructions to load/store i64 values. You just end up with a copy between fprs and gprs if you need to perform an operation that the fpu can't do. Could you elaborate on the issue with G_SELECT, G_EXTRACT, and G_INSERT?> On 21 Sep 2018, at 02:28, Petar Avramovic <Petar.Avramovic at rt-rk.com> wrote: > > Hi, > > Mips32 has 64 bit floating point instructions, while i64 instructions have to be emulated with i32 instructions. This means that G_LOAD should be custom legalized for s64 integer value, and be legal for s64 floating point value. There are also other generic instructions with the same problem: G_STORE, G_SELECT, G_EXTRACT, and G_INSERT. > > There are also other configurations where integer and floating point instructions of the same size are not simultaneously available. This problem was already addressed here http://lists.llvm.org/pipermail/llvm-dev/2017-July/114978.html for G_LOAD/G_STORE. > > Legality of an instruction should not depend on surrounding instructions. Because of that, approach from regbankselect that iterates through uses of G_LOAD def register and checks if some of the uses was an generic floating point instruction should not be an option for legalizer. > > Since GlobalISel Legalizer cannot distinguish between them using only LLT, only other option that I can see at this moment is having "F" variant of the generic instruction (like this is the case with G_ADD and G_FADD). > > Is this change possible? Or are there some other approaches for dealing with this problem? > > Petar > >
Petar Avramovic via llvm-dev
2018-Sep-24 15:05 UTC
[llvm-dev] [GlobalISel] Legalize generic instructions that also depend on type of scalar, not only scalar size
Hi, On 21.09.2018. 17:50, Daniel Sanders wrote:> For G_LOAD/G_STORE it should be ok to use double load/store instructions to load/store i64 values. You just end up with a copy between fprs and gprs if you need to perform an operation that the fpu can't do.Since all mentioned instructions for i64 just move bits around, available 64bit fpr instructions could be used. I thought that this approach would result in a few extra move instructions but in order to make it work there seem to be some additional checks and post legalizer legalization. This don't go along with my understanding of GlobalISel pipeline, particularly emulating i64 for mips32. I have encountered following questions in my attempt to legalize i64 instructions. How do we know if s64 was i64, not double, and we need to copy to gprs? How will this copy fold into i32 instructions that emulate i64 instruction? Doubles have 64 bit register class (afgr64 or fgr64) while i64 need to be emulated with two gpr32 registers. This copy would require some additional checks in order to be selected into two move instructions, I am not entirely sure where this checks should happen. Also what register classes would be used for operands of such copy?> Could you elaborate on the issue with G_SELECT, G_EXTRACT, and G_INSERT?Issues with G_LOAD, G_STORE, G_SELECT, G_EXTRACT, and G_INSERT are similar and the problem is my approach to legalization of i64 instructions. In mips32 td files there are no register classes that can hold i64 nor pseudoinstructions that work with i64. For that reason I consider i64 instructions "not legal". Because of that my approach was to handle i64 instructions in legalizer, where they are replaced with a sequence of i32 instructions that work with high and low bits (held in s32 virtual registers) or replaced with a libcall. Also s64 vregs that are "def" merge two s32 vregs (high and low bits) with G_MERGE_VALUES artifact. All "uses" of this s64 register will have to unmerge it and define two s32 vregs with G_UNMERGE_VALUES artifact. After all instructions have been legalized LegalizationArtifactCombiner will combine G_MERGE_VALUES with G_UNMERGE_VALUES and there will be no more s64 vregs that used to hold i64. Here is the llvm-ir input: define i64 @f(i64 %a, i64* %b){ entry: %0 = load i64, i64* %b, align 8 %add = add nsw i64 %0, %a ret i64 %add } After IRTranslator we get this mir as input for legalizer pass liveins: $a0, $a1, $a2 %2:_(s32) = COPY $a0 %3:_(s32) = COPY $a1 %0:_(s64) = G_MERGE_VALUES %3(s32), %2(s32) %1:_(p0) = COPY $a2 %4:_(s64) = G_LOAD %1(p0) :: (load 8 from %ir.b) %5:_(s64) = G_ADD %4, %0 %6:_(s32), %7:_(s32) = G_UNMERGE_VALUES %5(s64) $v0 = COPY %7(s32) $v1 = COPY %6(s32) RetRA implicit $v0, implicit $v1 This is the state of our function after legalization of s64 G_LOAD and G_ADD, we are now going to combine G_MERGE_VALUES with G_UNMERGE_VALUES liveins: $a0, $a1, $a2 %2:_(s32) = COPY $a0 %3:_(s32) = COPY $a1 %0:_(s64) = G_MERGE_VALUES %3:_(s32), %2:_(s32) %1:_(p0) = COPY $a2 %16:_(s32) = G_LOAD %1:_(p0) :: (load 8 from %ir.b) %19:_(s32) = G_CONSTANT i32 4 %18:_(p0) = G_GEP %1:_, %19:_(s32) %17:_(s32) = G_LOAD %18:_(p0) :: (load 8 from %ir.b) %4:_(s64) = G_MERGE_VALUES %17:_(s32), %16:_(s32) %9:_(s32), %8:_(s32) = G_UNMERGE_VALUES %0:_(s64) %11:_(s32), %10:_(s32) = G_UNMERGE_VALUES %4:_(s64) %15:_(s32) = G_ADD %11:_, %9:_ %12:_(s32) = G_ADD %10:_, %8:_ %14:_(s32) = G_ICMP intpred(ult), %12:_(s32), %10:_ %13:_(s32) = G_ADD %15:_, %14:_ %5:_(s64) = G_MERGE_VALUES %13:_(s32), %12:_(s32) %6:_(s32), %7:_(s32) = G_UNMERGE_VALUES %5:_(s64) $v0 = COPY %7:_(s32) $v1 = COPY %6:_(s32) RetRA implicit $v0, implicit $v1 and this is the legalizer output: liveins: $a0, $a1, $a2 %2:_(s32) = COPY $a0 %3:_(s32) = COPY $a1 %1:_(p0) = COPY $a2 %16:_(s32) = G_LOAD %1(p0) :: (load 8 from %ir.b) %19:_(s32) = G_CONSTANT i32 4 %18:_(p0) = G_GEP %1, %19(s32) %17:_(s32) = G_LOAD %18(p0) :: (load 8 from %ir.b) %15:_(s32) = G_ADD %17, %3 %12:_(s32) = G_ADD %16, %2 %14:_(s32) = G_ICMP intpred(ult), %12(s32), %16 %13:_(s32) = G_ADD %15, %14 $v0 = COPY %12(s32) $v1 = COPY %13(s32) RetRA implicit $v0, implicit $v1 As we can see there are no s64 vregs that hold integers after legalizer. This approach works as long as we know types of operands (s64 in G_ADD is i64, s64 in G_FADD is double). With described approach there are issues if we don't know the type of operands and declare generic instruction legal (it should not be legal if we knew the type). Then, we will be missing a G_MERGE_VALUES (G_LOAD and G_EXTRACT) or G_UNMERGE_VALUES (G_STORE and G_INSERT) or both (G_SELECT). After legalizer there will be a few s64 vregs that hold i64 and few G_MERGE_VALUES or G_UNMERGE_VALUES that were not combined. I would consider this to be legalization error. Perhaps i misunderstood the purpose of legalizer, but why it is possible to determine type of operands for some generic instructions and not possible for the others? How could such typeless instruction(that is not legal for all types) fold (in Legalizer) into other instructions that use legalization artifacts to become legal? Is there some approach in which we can avoid use of artifacts for i64?>> On 21 Sep 2018, at 02:28, Petar Avramovic <Petar.Avramovic at rt-rk.com> wrote: >> >> Hi, >> >> Mips32 has 64 bit floating point instructions, while i64 instructions have to be emulated with i32 instructions. This means that G_LOAD should be custom legalized for s64 integer value, and be legal for s64 floating point value. There are also other generic instructions with the same problem: G_STORE, G_SELECT, G_EXTRACT, and G_INSERT. >> >> There are also other configurations where integer and floating point instructions of the same size are not simultaneously available. This problem was already addressed here http://lists.llvm.org/pipermail/llvm-dev/2017-July/114978.html for G_LOAD/G_STORE. >> >> Legality of an instruction should not depend on surrounding instructions. Because of that, approach from regbankselect that iterates through uses of G_LOAD def register and checks if some of the uses was an generic floating point instruction should not be an option for legalizer. >> >> Since GlobalISel Legalizer cannot distinguish between them using only LLT, only other option that I can see at this moment is having "F" variant of the generic instruction (like this is the case with G_ADD and G_FADD). >> >> Is this change possible? Or are there some other approaches for dealing with this problem? >> >> Petar >> >>