Skip to content

Commit 2426172

Browse files
author
Jessica Paquette
committed
[GlobalISel] Add G_ASSERT_ZEXT
This adds a generic opcode which communicates that a type has already been zero-extended from a narrower type. This is intended to be similar to AssertZext in SelectionDAG. For example, ``` %x_was_extended:_(s64) = G_ASSERT_ZEXT %x, 16 ``` Signifies that the top 48 bits of %x are known to be 0. This is useful in cases like this: ``` define i1 @zeroext_param(i8 zeroext %x) { %cmp = icmp ult i8 %x, -20 ret i1 %cmp } ``` In AArch64, `%x` must use a 32-bit register, which is then truncated to a 8-bit value. If we know that `%x` is already zero-ed out in the relevant high bits, we can avoid the truncate. Currently, in GISel, this looks like this: ``` _zeroext_param: and w8, w0, #0xff ; We don't actually need this! cmp w8, #236 cset w0, lo ret ``` While SDAG does not produce the truncation, since it knows that it's unnecessary: ``` _zeroext_param: cmp w0, #236 cset w0, lo ret ``` This patch - Adds G_ASSERT_ZEXT - Adds MIRBuilder support for it - Adds MachineVerifier support for it - Documents it It also puts G_ASSERT_ZEXT into its own class of "hint instruction." (There should be a G_ASSERT_SEXT in the future, maybe a G_ASSERT_ALIGN as well.) This allows us to skip over hints in the legalizer etc. These can then later be selected like COPY instructions or removed. Differential Revision: https://reviews.llvm.org/D95564
1 parent ac70a53 commit 2426172

File tree

10 files changed

+211
-1
lines changed

10 files changed

+211
-1
lines changed

llvm/docs/GlobalISel/GenericOpcode.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,3 +735,37 @@ An alignment value of `0` or `1` mean no specific alignment.
735735
.. code-block:: none
736736
737737
%8:_(p0) = G_DYN_STACKALLOC %7(s64), 32
738+
739+
Optimization Hints
740+
------------------
741+
742+
These instructions do not correspond to any target instructions. They act as
743+
hints for various combines.
744+
745+
G_ASSERT_ZEXT
746+
^^^^^^^^^^^^^
747+
748+
Signifies that the contents of a register were previously zero-extended from a
749+
smaller type.
750+
751+
The smaller type is denoted using an immediate operand. For scalars, this is the
752+
width of the entire smaller type. For vectors, this is the width of the smaller
753+
element type.
754+
755+
.. code-block:: none
756+
757+
%x_assert:_(s32) = G_ASSERT_ZEXT %x(s32), 16
758+
%y_assert:_(<2 x s32>) = G_ASSERT_ZEXT %y(<2 x s32>), 16
759+
760+
G_ASSERT_ZEXT acts like a restricted form of copy.
761+
762+
The source and destination registers must
763+
764+
- Be virtual
765+
- Belong to the same register class
766+
- Belong to the same register bank
767+
768+
It should always be safe to
769+
770+
- Look through the source register
771+
- Replace the destination register with the source register

llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ class MachineIRBuilder {
810810
/// \return a MachineInstrBuilder for the newly created instruction.
811811
MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op);
812812

813+
/// Build and insert \p Res = G_ASSERT_ZEXT Op, Size
814+
///
815+
/// \return a MachineInstrBuilder for the newly created instruction.
816+
MachineInstrBuilder buildAssertZExt(const DstOp &Res, const SrcOp &Op,
817+
unsigned Size);
818+
813819
/// Build and insert `Res = G_LOAD Addr, MMO`.
814820
///
815821
/// Loads the value stored at \p Addr. Puts the result in \p Res.

llvm/include/llvm/CodeGen/TargetOpcodes.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ inline bool isPreISelGenericOpcode(unsigned Opcode) {
3636
inline bool isTargetSpecificOpcode(unsigned Opcode) {
3737
return Opcode > TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
3838
}
39+
40+
/// \returns true if \p Opcode is an optimization hint opcode which is not
41+
/// supposed to appear after ISel.
42+
inline bool isPreISelGenericOptimizationHint(unsigned Opcode) {
43+
return Opcode >= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START &&
44+
Opcode <= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END;
45+
}
46+
3947
} // end namespace llvm
4048

4149
#endif

llvm/include/llvm/Support/TargetOpcodes.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL)
213213
/// This is something we might want to relax, but for now, this is convenient
214214
/// to produce diagnostics.
215215

216+
/// Instructions which should not exist past instruction selection, but do not
217+
/// generate code. These instructions only act as optimization hints.
218+
HANDLE_TARGET_OPCODE(G_ASSERT_ZEXT)
219+
HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START,
220+
G_ASSERT_ZEXT)
221+
HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END,
222+
G_ASSERT_ZEXT)
223+
216224
/// Generic ADD instruction. This is an integer add.
217225
HANDLE_TARGET_OPCODE(G_ADD)
218226
HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD)

llvm/include/llvm/Target/GenericOpcodes.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,3 +1337,15 @@ def G_MEMSET : GenericInstruction {
13371337
let hasSideEffects = false;
13381338
let mayStore = true;
13391339
}
1340+
1341+
//------------------------------------------------------------------------------
1342+
// Optimization hints
1343+
//------------------------------------------------------------------------------
1344+
1345+
// Asserts that an operation has already been zero-extended from a specific
1346+
// type.
1347+
def G_ASSERT_ZEXT : GenericInstruction {
1348+
let OutOperandList = (outs type0:$dst);
1349+
let InOperandList = (ins type0:$src, untyped_imm_0:$sz);
1350+
let hasSideEffects = false;
1351+
}

llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ MachineInstrBuilder MachineIRBuilder::buildCopy(const DstOp &Res,
240240
return buildInstr(TargetOpcode::COPY, Res, Op);
241241
}
242242

243+
MachineInstrBuilder MachineIRBuilder::buildAssertZExt(const DstOp &Res,
244+
const SrcOp &Op,
245+
unsigned Size) {
246+
return buildInstr(TargetOpcode::G_ASSERT_ZEXT, Res, Op).addImm(Size);
247+
}
248+
243249
MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res,
244250
const ConstantInt &Val) {
245251
LLT Ty = Res.getLLTTy(*getMRI());

llvm/lib/CodeGen/MachineVerifier.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,41 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) {
941941

942942
// Verify properties of various specific instruction types
943943
switch (MI->getOpcode()) {
944+
case TargetOpcode::G_ASSERT_ZEXT: {
945+
if (!MI->getOperand(2).isImm()) {
946+
report("G_ASSERT_ZEXT expects an immediate operand #2", MI);
947+
break;
948+
}
949+
950+
Register Dst = MI->getOperand(0).getReg();
951+
Register Src = MI->getOperand(1).getReg();
952+
LLT DstTy = MRI->getType(Dst);
953+
LLT SrcTy = MRI->getType(Src);
954+
verifyVectorElementMatch(DstTy, SrcTy, MI);
955+
int64_t Imm = MI->getOperand(2).getImm();
956+
if (Imm <= 0) {
957+
report("G_ASSERT_ZEXT size must be >= 1", MI);
958+
break;
959+
}
960+
961+
if (Imm >= SrcTy.getScalarSizeInBits()) {
962+
report("G_ASSERT_ZEXT size must be less than source bit width", MI);
963+
break;
964+
}
965+
966+
if (MRI->getRegBankOrNull(Src) != MRI->getRegBankOrNull(Dst)) {
967+
report("G_ASSERT_ZEXT source and destination register banks must match",
968+
MI);
969+
break;
970+
}
971+
972+
if (MRI->getRegClassOrNull(Src) != MRI->getRegClassOrNull(Dst))
973+
report("G_ASSERT_ZEXT source and destination register classes must match",
974+
MI);
975+
976+
break;
977+
}
978+
944979
case TargetOpcode::G_CONSTANT:
945980
case TargetOpcode::G_FCONSTANT: {
946981
LLT DstTy = MRI->getType(MI->getOperand(0).getReg());
@@ -1594,7 +1629,8 @@ void MachineVerifier::visitMachineInstrBefore(const MachineInstr *MI) {
15941629
}
15951630
}
15961631

1597-
if (isPreISelGenericOpcode(MCID.getOpcode())) {
1632+
unsigned Opc = MCID.getOpcode();
1633+
if (isPreISelGenericOpcode(Opc) || isPreISelGenericOptimizationHint(Opc)) {
15981634
verifyPreISelGenericInstruction(MI);
15991635
return;
16001636
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc -mtriple=aarch64 -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
3+
#
4+
# Verify that hint instructions are ignored by the legalizer.
5+
6+
---
7+
name: assert_zext
8+
tracksRegLiveness: true
9+
body: |
10+
bb.0:
11+
liveins: $w0, $w1
12+
; CHECK-LABEL: name: assert_zext
13+
; CHECK: %copy:_(s32) = COPY $w1
14+
; CHECK: %hint:_(s32) = G_ASSERT_ZEXT %copy, 16
15+
; CHECK: $w0 = COPY %hint(s32)
16+
; CHECK: RET_ReallyLR implicit $w0
17+
%copy:_(s32) = COPY $w1
18+
%hint:_(s32) = G_ASSERT_ZEXT %copy, 16
19+
$w0 = COPY %hint
20+
RET_ReallyLR implicit $w0
21+
...
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# REQUIRES: aarch64-registered-target
2+
# RUN: not --crash llc -verify-machineinstrs -mtriple aarch64 -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
3+
4+
name: test
5+
body: |
6+
bb.0:
7+
liveins: $x0, $w0
8+
%0:_(s64) = COPY $x0
9+
%1:_(<4 x s16>) = COPY $x0
10+
%2:_(s32) = COPY $w0
11+
12+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 ***
13+
; CHECK: instruction: %assert_zext_1:_(s64) = G_ASSERT_ZEXT
14+
%assert_zext_1:_(s64) = G_ASSERT_ZEXT %0, %0
15+
16+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 ***
17+
; CHECK: instruction: %assert_zext_2:_(s64) = G_ASSERT_ZEXT
18+
%assert_zext_2:_(s64) = G_ASSERT_ZEXT %0, i8 8
19+
20+
; CHECK: *** Bad machine code: Type mismatch in generic instruction ***
21+
; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT
22+
; CHECK: *** Bad machine code: operand types must be all-vector or all-scalar ***
23+
; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT
24+
%assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT %0, 8
25+
26+
; CHECK: *** Bad machine code: operand types must preserve number of vector elements ***
27+
; CHECK: instruction: %assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT
28+
%assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT %1, 8
29+
30+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be >= 1 ***
31+
; CHECK: instruction: %assert_zext_5:_(s64) = G_ASSERT_ZEXT
32+
%assert_zext_5:_(s64) = G_ASSERT_ZEXT %0, 0
33+
34+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be less than source bit width ***
35+
; CHECK: instruction: %assert_zext_6:_(s64) = G_ASSERT_ZEXT
36+
%assert_zext_6:_(s64) = G_ASSERT_ZEXT %0, 128
37+
38+
; CHECK: *** Bad machine code: Type mismatch in generic instruction ***
39+
; CHECK: instruction: %assert_zext_7:_(s64) = G_ASSERT_ZEXT %2:_, 8
40+
%assert_zext_7:_(s64) = G_ASSERT_ZEXT %2, 8
41+
42+
; CHECK: *** Bad machine code: Generic instruction cannot have physical register ***
43+
; CHECK: instruction: %assert_zext_8:_(s64) = G_ASSERT_ZEXT $x0, 8
44+
%assert_zext_8:_(s64) = G_ASSERT_ZEXT $x0, 8
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# REQUIRES: aarch64-registered-target
2+
# RUN: not --crash llc -verify-machineinstrs -mtriple aarch64 -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
3+
4+
name: test
5+
legalized: true
6+
regBankSelected: true
7+
body: |
8+
bb.0:
9+
liveins: $w0, $w1
10+
%bank:gpr(s32) = COPY $w0
11+
%class:gpr32(s32) = COPY $w1
12+
13+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
14+
; CHECK: instruction: %bank_mismatch:fpr(s32) = G_ASSERT_ZEXT %bank:gpr, 16
15+
%bank_mismatch:fpr(s32) = G_ASSERT_ZEXT %bank, 16
16+
17+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register classes must match ***
18+
; CHECK: instruction: %class_mismatch_gpr:gpr32all(s32) = G_ASSERT_ZEXT %class:gpr32, 16
19+
%class_mismatch_gpr:gpr32all(s32) = G_ASSERT_ZEXT %class, 16
20+
21+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register classes must match ***
22+
; CHECK: instruction: %class_mismatch_fpr:fpr32(s32) = G_ASSERT_ZEXT %class:gpr32, 16
23+
%class_mismatch_fpr:fpr32(s32) = G_ASSERT_ZEXT %class, 16
24+
25+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
26+
; CHECK: instruction: %dst_has_class_src_has_bank:gpr32all(s32) = G_ASSERT_ZEXT %bank:gpr, 16
27+
%dst_has_class_src_has_bank:gpr32all(s32) = G_ASSERT_ZEXT %bank, 16
28+
29+
; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
30+
; CHECK: instruction: %dst_has_bank_src_has_class:gpr(s32) = G_ASSERT_ZEXT %class:gpr32, 16
31+
%dst_has_bank_src_has_class:gpr(s32) = G_ASSERT_ZEXT %class, 16
32+
33+
; CHECK: *** Bad machine code: Generic instruction cannot have physical register ***
34+
; CHECK: instruction: %implicit_physreg:gpr(s32) = G_ASSERT_ZEXT %class:gpr32, 16, implicit-def $w0
35+
%implicit_physreg:gpr(s32) = G_ASSERT_ZEXT %class, 16, implicit-def $w0

0 commit comments

Comments
 (0)