[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