[llvm-dev] [GlobalISel] Legalize generic instructions that also depend on type of scalar, not only scalar size

Petar Avramovic via llvm-dev llvm-dev at lists.llvm.org
Mon Sep 24 08:05:20 PDT 2018


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
>>
>>



More information about the llvm-dev mailing list