LegalizerInfo assumes all G_MERGE_VALUES and G_UNMERGE_VALUES instructions are legal, so it is not possible to legalize vector operations on illegal vector types. This patch fixes the problem by removing the related check and adding default actions for G_MERGE_VALUES and G_UNMERGE_VALUES.
Details
Diff Detail
Event Timeline
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
---|---|---|
67–88 | This is in the class that contains target-independent info on how to legalize operations and types. | |
207–208 | This is very generic code, and having explicit checks for specific Gopcodes feels wrong. if ((MI.getOpcode() == TargetOpcode::G_MERGE_VALUES || MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) && TypeIdx == 1) be more something like the following? if (hasVariableNumberOfOperands(MI.getOpcode() && TypeIdx == numberOfTypeIndices(MI.getOpcode()) Also, I fail to understand the need for this logic here with the little documentation that exists about G_MERGE_VALUES and G_UNMERGE_VALUES in include/llvm/Target/GenericOpcodes.td. |
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
---|---|---|
67–88 | I thought about this, but then I decided to handle the common types here. My concern was targets use these operations in order to legalize some other operations. For example, in test/CodeGen/X86/GlobalISel/legalize-add.mir, there is a function called test_add_i64 as below. bb.1 (%ir-block.0): %0(s64) = IMPLICIT_DEF %1(s64) = IMPLICIT_DEF %2(s64) = G_ADD %0, %1 RET 0 Here, Legalizer builds G_MERGE_VALUES and G_UNMERGE_VALUES to extract 32-bit values, but they are not actually legal. The patch Aditya proposed (https://reviews.llvm.org/D39267) should fix this problem by processing the legalizer artifacts after legalizing all other instructions. I'll move this to target-specific code and create another diff. | |
207–208 |
Yes, G_MERGE_VALUES concatenates multiple registers of the same size into a wider register and G_UNMERGE_VALUES extracts multiple registers with the specified size. These instructions has variable number of operands, but there is only one source type and one destination type as all sources for G_MERGE_VALUES and all destination for G_UNMERGE_VALUES must be the same type.
That would be incorrect because other instruction with variable number of operands may have other operands with different types. For example: def G_OPCODE : Instruction { let OutOperandList = (outs type0:$dst); let InOperandList = (ins type1:$src0, type2:src1, ..., variable_ops); } So, this check is okay only for these instructions because they have size constraints.
Simply, I'm trying to set legalizer actions for these instruction, but not for every operand. The existing definition for G_MERGE_VALUES is: def G_MERGE_VALUES : Instruction { let OutOperandList = (outs type0:$dst); let InOperandList = (ins variable_ops); let hasSideEffects = 0; } In this case, there is no way to legalize source operands because MI.getDesc().getNumOperands() returns 1. We can fix this by replacing MI.getDesc().getNumOperands() with MI.getNumOperands(), but this means we need to set actions for every possible case. I'll add a comment to explain why we are doing this. |
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
---|---|---|
77 | Isn't this one redundant? We've already said 2<=Size<8 are unsupported | |
207–208 | Do we really need to treat G_MERGE_VALUE's specially here? G_UNMERGE_VALUES needs it because type1 is only on the last operand, but for G_MERGE_VALUES, type1 is on operands 1 and higher.
So long as variable_ops is equivalent to specifying typeN (where N is 1 higher than the highest number used elsewhere in the definition) a variable number of times, I think Kristof's if-statement would work for your example. Either way, I agree with the main point that we should be driving this with a generic mechanism rather than a special case. Maybe, we should have a variable_ops_typeN that indicates the type constraint and leaves some information in the MCInstrDesc we can inspect during this code. |
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
---|---|---|
207–208 |
Right, it seems like I was trying to get the last operand for G_MERGE_VALUES for no reason. I'll remove the special case for G_MERGE_VALUES.
I think it would not work because $src1 doesn't have to be the same type with variable_ops. MRI.getType(MI.getOperand(LastTypeIdx).getReg()) must return type2. |
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
---|---|---|
207–208 | To make this easier to read, I think it may make sense to factor this out into a separate function, e.g. with a name like "getTyFromTypeIdx(MI, TypeIdx)". I'm not sure on the name, but doing it this way would make this code a lot easier to understand, I think. | |
lib/Target/AArch64/AArch64LegalizerInfo.cpp | ||
355 | I haven't thought this through at all, but I'm a bit surprised v8s64 also isn't legal if v6s64 is? |
lib/Target/AArch64/AArch64LegalizerInfo.cpp | ||
---|---|---|
355 | Added missing cases. |
include/llvm/Target/GenericOpcodes.td | ||
---|---|---|
612 | I know this isn't from your patch but Concatenante -> Concatenate | |
lib/CodeGen/GlobalISel/LegalizerInfo.cpp | ||
185–189 | This is a nitpick but we should early-return here | |
test/CodeGen/AArch64/GlobalISel/legalize-merge-values.mir | ||
2 | Could you add a comment explaining the intent of this test? | |
test/CodeGen/AArch64/GlobalISel/legalize-nonpowerof2eltsvec.mir | ||
21–34 | I'm not sure why this test has changed from s32's to s64's. Could you explain? Also, the name is inconsistent with the types used |
test/CodeGen/AArch64/GlobalISel/legalize-nonpowerof2eltsvec.mir | ||
---|---|---|
21–34 |
It's because <3 x s32> is not legal. |
LGTM
test/CodeGen/AArch64/GlobalISel/legalize-nonpowerof2eltsvec.mir | ||
---|---|---|
21–34 | That makes sense. In that case, changing the name is enough |
I know this isn't from your patch but Concatenante -> Concatenate