Index: llvm/docs/GlobalISel/GenericOpcode.rst =================================================================== --- llvm/docs/GlobalISel/GenericOpcode.rst +++ llvm/docs/GlobalISel/GenericOpcode.rst @@ -735,3 +735,26 @@ .. code-block:: none %8:_(p0) = G_DYN_STACKALLOC %7(s64), 32 + +Optimization Hints +------------------ + +These instructions do not correspond to any target instructions. They act as +hints for various combines. + +These must be removed prior to register bank selection. + +G_ASSERT_ZEXT +^^^^^^^^^^^^^ + +Signifies that the contents of a register were previously zero-extended from a +smaller type. + +The smaller type is denoted using an immediate operand. For scalars, this is the +width of the entire smaller type. For vectors, this is the width of the smaller +element type. + +.. code-block:: none + + %x_assert:_(s32) = G_ASSERT_ZEXT %x(s32), 16 + %y_assert:_(<2 x s32>) = G_ASSERT_ZEXT %y(<2 x s32>), 16 Index: llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -810,6 +810,12 @@ /// \return a MachineInstrBuilder for the newly created instruction. MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op); + /// Build and insert \p Res = G_ASSERT_ZEXT Op, Size + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildAssertZExt(const DstOp &Res, const SrcOp &Op, + unsigned Size); + /// Build and insert `Res = G_LOAD Addr, MMO`. /// /// Loads the value stored at \p Addr. Puts the result in \p Res. Index: llvm/include/llvm/CodeGen/TargetOpcodes.h =================================================================== --- llvm/include/llvm/CodeGen/TargetOpcodes.h +++ llvm/include/llvm/CodeGen/TargetOpcodes.h @@ -36,6 +36,22 @@ inline bool isTargetSpecificOpcode(unsigned Opcode) { return Opcode > TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END; } + +/// \returns true if \p Opcode is an optimization hint opcode which is not +/// supposed to appear after ISel. +inline bool isPreISelGenericOptimizationHint(unsigned Opcode) { + return Opcode >= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START && + Opcode <= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END; +} + +/// \returns true if \p Opcode is a generic opcode which is not supposed to +/// appear after ISel, and \p Opcode corresponds to something which should be +/// selected. (For example, not a hint.) +inline bool isSelectablePreISelGenericOpcode(unsigned Opcode) { + return isPreISelGenericOpcode(Opcode) && + !isPreISelGenericOptimizationHint(Opcode); +} + } // end namespace llvm #endif Index: llvm/include/llvm/Support/TargetOpcodes.def =================================================================== --- llvm/include/llvm/Support/TargetOpcodes.def +++ llvm/include/llvm/Support/TargetOpcodes.def @@ -730,10 +730,19 @@ HANDLE_TARGET_OPCODE(G_VECREDUCE_UMAX) HANDLE_TARGET_OPCODE(G_VECREDUCE_UMIN) +/// Instructions which should not exist past instruction selection, but do not +/// generate code. These instructions only act as optimization hints. +HANDLE_TARGET_OPCODE(G_ASSERT_ZEXT) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START, + G_ASSERT_ZEXT) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END, + G_ASSERT_ZEXT) + /// Marker for the end of the generic opcode. /// This is used to check if an opcode is in the range of the /// generic opcodes. -HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, G_VECREDUCE_UMIN) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, + PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END) /// BUILTIN_OP_END - This must be the last enum value in this list. /// The target-specific post-isel opcode values start here. Index: llvm/include/llvm/Target/GenericOpcodes.td =================================================================== --- llvm/include/llvm/Target/GenericOpcodes.td +++ llvm/include/llvm/Target/GenericOpcodes.td @@ -1337,3 +1337,15 @@ let hasSideEffects = false; let mayStore = true; } + +//------------------------------------------------------------------------------ +// Optimization hints +//------------------------------------------------------------------------------ + +// Asserts that an operation has already been zero-extended from a specific +// type. +def G_ASSERT_ZEXT : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src, untyped_imm_0:$sz); + let hasSideEffects = false; +} Index: llvm/lib/CodeGen/GlobalISel/Legalizer.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/Legalizer.cpp +++ llvm/lib/CodeGen/GlobalISel/Legalizer.cpp @@ -124,7 +124,7 @@ // Only legalize pre-isel generic instructions. // Legalization process could generate Target specific pseudo // instructions with generic types. Don't record them - if (isPreISelGenericOpcode(MI.getOpcode())) { + if (isSelectablePreISelGenericOpcode(MI.getOpcode())) { if (isArtifact(MI)) ArtifactList.insert(&MI); else @@ -185,7 +185,7 @@ for (MachineInstr &MI : *MBB) { // Only legalize pre-isel generic instructions: others don't have types // and are assumed to be legal. - if (!isPreISelGenericOpcode(MI.getOpcode())) + if (!isSelectablePreISelGenericOpcode(MI.getOpcode())) continue; if (isArtifact(MI)) ArtifactList.deferred_insert(&MI); @@ -220,7 +220,7 @@ unsigned NumArtifacts = ArtifactList.size(); while (!InstList.empty()) { MachineInstr &MI = *InstList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && + assert(isSelectablePreISelGenericOpcode(MI.getOpcode()) && "Expecting generic opcode"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n"); @@ -269,7 +269,7 @@ LocObserver.checkpoint(); while (!ArtifactList.empty()) { MachineInstr &MI = *ArtifactList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && + assert(isSelectablePreISelGenericOpcode(MI.getOpcode()) && "Expecting generic opcode"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead\n"); Index: llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp +++ llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp @@ -741,7 +741,7 @@ const MachineRegisterInfo &MRI = MF.getRegInfo(); for (const MachineBasicBlock &MBB : MF) for (const MachineInstr &MI : MBB) - if (isPreISelGenericOpcode(MI.getOpcode()) && + if (isSelectablePreISelGenericOpcode(MI.getOpcode()) && !MLI->isLegalOrCustom(MI, MRI)) return &MI; } Index: llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -240,6 +240,12 @@ return buildInstr(TargetOpcode::COPY, Res, Op); } +MachineInstrBuilder MachineIRBuilder::buildAssertZExt(const DstOp &Res, + const SrcOp &Op, + unsigned Size) { + return buildInstr(TargetOpcode::G_ASSERT_ZEXT, Res, Op).addImm(Size); +} + MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res, const ConstantInt &Val) { LLT Ty = Res.getLLTTy(*getMRI()); Index: llvm/lib/CodeGen/MachineVerifier.cpp =================================================================== --- llvm/lib/CodeGen/MachineVerifier.cpp +++ llvm/lib/CodeGen/MachineVerifier.cpp @@ -939,8 +939,30 @@ if (!TII->verifyInstruction(*MI, ErrorInfo)) report(ErrorInfo.data(), MI); + if (isPreISelGenericOptimizationHint(MI->getOpcode())) { + if (MF->getProperties().hasProperty( + MachineFunctionProperties::Property::RegBankSelected)) + report("Hint instructions should be removed before regbankselect", MI); + } + // Verify properties of various specific instruction types switch (MI->getOpcode()) { + case TargetOpcode::G_ASSERT_ZEXT: { + if (!MI->getOperand(2).isImm()) { + report("G_ASSERT_ZEXT expects an immediate operand #2", MI); + break; + } + LLT DstTy = MRI->getType(MI->getOperand(0).getReg()); + LLT SrcTy = MRI->getType(MI->getOperand(1).getReg()); + verifyVectorElementMatch(DstTy, SrcTy, MI); + int64_t Imm = MI->getOperand(2).getImm(); + if (Imm <= 0) + report("G_ASSERT_ZEXT size must be >= 1", MI); + else if (Imm >= SrcTy.getScalarSizeInBits()) + report("G_ASSERT_ZEXT size must be less than source bit width", MI); + break; + } + case TargetOpcode::G_CONSTANT: case TargetOpcode::G_FCONSTANT: { LLT DstTy = MRI->getType(MI->getOperand(0).getReg()); Index: llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir @@ -0,0 +1,21 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=aarch64 -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s +# +# Verify that hint instructions are ignored by the legalizer. + +--- +name: assert_zext +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; CHECK-LABEL: name: assert_zext + ; CHECK: %copy:_(s32) = COPY $w1 + ; CHECK: %hint:_(s32) = G_ASSERT_ZEXT %copy, 16 + ; CHECK: $w0 = COPY %hint(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %copy:_(s32) = COPY $w1 + %hint:_(s32) = G_ASSERT_ZEXT %copy, 16 + $w0 = COPY %hint + RET_ReallyLR implicit $w0 +... Index: llvm/test/MachineVerifier/test_g_assert_ext.mir =================================================================== --- /dev/null +++ llvm/test/MachineVerifier/test_g_assert_ext.mir @@ -0,0 +1,41 @@ +# RUN: not --crash llc -verify-machineinstrs -run-pass none -o /dev/null %s 2>&1 | FileCheck %s +# REQUIRES: aarch64-registered-target + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-unknown" + define void @test() { ret void } +... +--- +name: test +body: | + bb.0: + liveins: $x0 + %0:_(s64) = COPY $x0 + %1:_(<4 x s16>) = COPY $x0 + + ; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 *** + ; CHECK: instruction: %assert_zext_1:_(s64) = G_ASSERT_ZEXT + %assert_zext_1:_(s64) = G_ASSERT_ZEXT %0, %0 + + ; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 *** + ; CHECK: instruction: %assert_zext_2:_(s64) = G_ASSERT_ZEXT + %assert_zext_2:_(s64) = G_ASSERT_ZEXT %0, i8 8 + + ; CHECK: *** Bad machine code: Type mismatch in generic instruction *** + ; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT + ; CHECK: *** Bad machine code: operand types must be all-vector or all-scalar *** + ; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT + %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT %0, 8 + + ; CHECK: *** Bad machine code: operand types must preserve number of vector elements *** + ; CHECK: instruction: %assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT + %assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT %1, 8 + + ; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be >= 1 *** + ; CHECK: instruction: %assert_zext_5:_(s64) = G_ASSERT_ZEXT + %assert_zext_5:_(s64) = G_ASSERT_ZEXT %0, 0 + + ; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be less than source bit width *** + ; CHECK: instruction: %assert_zext_6:_(s64) = G_ASSERT_ZEXT + %assert_zext_6:_(s64) = G_ASSERT_ZEXT %0, 128 Index: llvm/test/MachineVerifier/test_hint_properties.mir =================================================================== --- /dev/null +++ llvm/test/MachineVerifier/test_hint_properties.mir @@ -0,0 +1,20 @@ +# RUN: not --crash llc -verify-machineinstrs -run-pass none -o /dev/null %s 2>&1 | FileCheck %s +# REQUIRES: aarch64-registered-target + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-unknown" + define void @test() { ret void } +... +--- +name: test +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0 + %0:gpr64(s64) = COPY $x0 + + ; CHECK: *** Bad machine code: Hint instructions should be removed before regbankselect *** + ; CHECK: instruction: %assert_zext:gpr64(s64) = G_ASSERT_ZEXT %0:gpr64, 16 + %assert_zext:gpr64(s64) = G_ASSERT_ZEXT %0, 16