mirror of
				https://github.com/c64scene-ar/llvm-6502.git
				synced 2025-11-04 05:17:07 +00:00 
			
		
		
		
	Revert "AArch64: Use CMP;CCMP sequences for and/or/setcc trees."
The patch triggers a miscompile on SPEC 2006 403.gcc with the (ref) 200.i and scilab.i inputs. I opened PR23866 to track analysis of this. This reverts commit r238793. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239880 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
		@@ -76,9 +76,6 @@ cl::opt<bool> EnableAArch64ELFLocalDynamicTLSGeneration(
 | 
				
			|||||||
    cl::desc("Allow AArch64 Local Dynamic TLS code generation"),
 | 
					    cl::desc("Allow AArch64 Local Dynamic TLS code generation"),
 | 
				
			||||||
    cl::init(false));
 | 
					    cl::init(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Value type used for condition codes.
 | 
					 | 
				
			||||||
static const MVT MVT_CC = MVT::i32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
 | 
					AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
 | 
				
			||||||
                                             const AArch64Subtarget &STI)
 | 
					                                             const AArch64Subtarget &STI)
 | 
				
			||||||
    : TargetLowering(TM), Subtarget(&STI) {
 | 
					    : TargetLowering(TM), Subtarget(&STI) {
 | 
				
			||||||
@@ -810,9 +807,6 @@ const char *AArch64TargetLowering::getTargetNodeName(unsigned Opcode) const {
 | 
				
			|||||||
  case AArch64ISD::ADCS:              return "AArch64ISD::ADCS";
 | 
					  case AArch64ISD::ADCS:              return "AArch64ISD::ADCS";
 | 
				
			||||||
  case AArch64ISD::SBCS:              return "AArch64ISD::SBCS";
 | 
					  case AArch64ISD::SBCS:              return "AArch64ISD::SBCS";
 | 
				
			||||||
  case AArch64ISD::ANDS:              return "AArch64ISD::ANDS";
 | 
					  case AArch64ISD::ANDS:              return "AArch64ISD::ANDS";
 | 
				
			||||||
  case AArch64ISD::CCMP:              return "AArch64ISD::CCMP";
 | 
					 | 
				
			||||||
  case AArch64ISD::CCMN:              return "AArch64ISD::CCMN";
 | 
					 | 
				
			||||||
  case AArch64ISD::FCCMP:             return "AArch64ISD::FCCMP";
 | 
					 | 
				
			||||||
  case AArch64ISD::FCMP:              return "AArch64ISD::FCMP";
 | 
					  case AArch64ISD::FCMP:              return "AArch64ISD::FCMP";
 | 
				
			||||||
  case AArch64ISD::FMIN:              return "AArch64ISD::FMIN";
 | 
					  case AArch64ISD::FMIN:              return "AArch64ISD::FMIN";
 | 
				
			||||||
  case AArch64ISD::FMAX:              return "AArch64ISD::FMAX";
 | 
					  case AArch64ISD::FMAX:              return "AArch64ISD::FMAX";
 | 
				
			||||||
@@ -1171,133 +1165,10 @@ static SDValue emitComparison(SDValue LHS, SDValue RHS, ISD::CondCode CC,
 | 
				
			|||||||
    LHS = LHS.getOperand(0);
 | 
					    LHS = LHS.getOperand(0);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return DAG.getNode(Opcode, dl, DAG.getVTList(VT, MVT_CC), LHS, RHS)
 | 
					  return DAG.getNode(Opcode, dl, DAG.getVTList(VT, MVT::i32), LHS, RHS)
 | 
				
			||||||
      .getValue(1);
 | 
					      .getValue(1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SDValue emitConditionalComparison(SDValue LHS, SDValue RHS,
 | 
					 | 
				
			||||||
                                         ISD::CondCode CC, SDValue CCOp,
 | 
					 | 
				
			||||||
                                         SDValue Condition, unsigned NZCV,
 | 
					 | 
				
			||||||
                                         SDLoc DL, SelectionDAG &DAG) {
 | 
					 | 
				
			||||||
  unsigned Opcode = 0;
 | 
					 | 
				
			||||||
  if (LHS.getValueType().isFloatingPoint())
 | 
					 | 
				
			||||||
    Opcode = AArch64ISD::FCCMP;
 | 
					 | 
				
			||||||
  else if (RHS.getOpcode() == ISD::SUB) {
 | 
					 | 
				
			||||||
    SDValue SubOp0 = RHS.getOperand(0);
 | 
					 | 
				
			||||||
    if (const ConstantSDNode *SubOp0C = dyn_cast<ConstantSDNode>(SubOp0))
 | 
					 | 
				
			||||||
      if (SubOp0C->isNullValue() && (CC == ISD::SETEQ || CC == ISD::SETNE)) {
 | 
					 | 
				
			||||||
        // See emitComparison() on why we can only do this for SETEQ and SETNE.
 | 
					 | 
				
			||||||
        Opcode = AArch64ISD::CCMN;
 | 
					 | 
				
			||||||
        RHS = RHS.getOperand(1);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (Opcode == 0)
 | 
					 | 
				
			||||||
    Opcode = AArch64ISD::CCMP;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  SDValue NZCVOp = DAG.getConstant(NZCV, DL, MVT::i32);
 | 
					 | 
				
			||||||
  return DAG.getNode(Opcode, DL, MVT_CC, LHS, RHS, NZCVOp, Condition, CCOp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns true if @p Val is a tree of AND/OR/SETCC operations.
 | 
					 | 
				
			||||||
static bool isConjunctionDisjunctionTree(const SDValue Val, unsigned Depth) {
 | 
					 | 
				
			||||||
  if (!Val.hasOneUse())
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  if (Val->getOpcode() == ISD::SETCC)
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  // Protect against stack overflow.
 | 
					 | 
				
			||||||
  if (Depth > 1000)
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  if (Val->getOpcode() == ISD::AND || Val->getOpcode() == ISD::OR) {
 | 
					 | 
				
			||||||
    SDValue O0 = Val->getOperand(0);
 | 
					 | 
				
			||||||
    SDValue O1 = Val->getOperand(1);
 | 
					 | 
				
			||||||
    return isConjunctionDisjunctionTree(O0, Depth+1) &&
 | 
					 | 
				
			||||||
           isConjunctionDisjunctionTree(O1, Depth+1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Emit conjunction or disjunction tree with the CMP/FCMP followed by a chain
 | 
					 | 
				
			||||||
/// of CCMP/CFCMP ops. For example (SETCC_0 & SETCC_1) with condition cond0 and
 | 
					 | 
				
			||||||
/// cond1 can be transformed into "CMP; CCMP" with CCMP executing on cond_0
 | 
					 | 
				
			||||||
/// and setting flags to inversed(cond_1) otherwise.
 | 
					 | 
				
			||||||
/// This recursive function produces DAG nodes that produce condition flags
 | 
					 | 
				
			||||||
/// suitable to determine the truth value of @p Val (which is AND/OR/SETCC)
 | 
					 | 
				
			||||||
/// by testing the result for the condition set to @p OutCC. If @p Negate is
 | 
					 | 
				
			||||||
/// set the opposite truth value is produced. If @p CCOp and @p Condition are
 | 
					 | 
				
			||||||
/// given then conditional comparison are created so that false is reported
 | 
					 | 
				
			||||||
/// when they are false.
 | 
					 | 
				
			||||||
static SDValue emitConjunctionDisjunctionTree(
 | 
					 | 
				
			||||||
    SelectionDAG &DAG, SDValue Val, AArch64CC::CondCode &OutCC, bool Negate,
 | 
					 | 
				
			||||||
    SDValue CCOp = SDValue(), AArch64CC::CondCode Condition = AArch64CC::AL) {
 | 
					 | 
				
			||||||
  assert(isConjunctionDisjunctionTree(Val, 0));
 | 
					 | 
				
			||||||
  // We're at a tree leaf, produce a c?f?cmp.
 | 
					 | 
				
			||||||
  unsigned Opcode = Val->getOpcode();
 | 
					 | 
				
			||||||
  if (Opcode == ISD::SETCC) {
 | 
					 | 
				
			||||||
    SDValue LHS = Val->getOperand(0);
 | 
					 | 
				
			||||||
    SDValue RHS = Val->getOperand(1);
 | 
					 | 
				
			||||||
    ISD::CondCode CC = cast<CondCodeSDNode>(Val->getOperand(2))->get();
 | 
					 | 
				
			||||||
    bool isInteger = LHS.getValueType().isInteger();
 | 
					 | 
				
			||||||
    if (Negate)
 | 
					 | 
				
			||||||
      CC = getSetCCInverse(CC, isInteger);
 | 
					 | 
				
			||||||
    SDLoc DL(Val);
 | 
					 | 
				
			||||||
    // Determine OutCC and handle FP special case.
 | 
					 | 
				
			||||||
    if (isInteger) {
 | 
					 | 
				
			||||||
      OutCC = changeIntCCToAArch64CC(CC);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      assert(LHS.getValueType().isFloatingPoint());
 | 
					 | 
				
			||||||
      AArch64CC::CondCode ExtraCC;
 | 
					 | 
				
			||||||
      changeFPCCToAArch64CC(CC, OutCC, ExtraCC);
 | 
					 | 
				
			||||||
      // Surpisingly some floating point conditions can't be tested with a
 | 
					 | 
				
			||||||
      // single condition code. Construct an additional comparison in this case.
 | 
					 | 
				
			||||||
      // See comment below on how we deal with OR conditions.
 | 
					 | 
				
			||||||
      if (ExtraCC != AArch64CC::AL) {
 | 
					 | 
				
			||||||
        SDValue ExtraCmp;
 | 
					 | 
				
			||||||
        if (!CCOp.getNode())
 | 
					 | 
				
			||||||
          ExtraCmp = emitComparison(LHS, RHS, CC, DL, DAG);
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
          SDValue ConditionOp = DAG.getConstant(Condition, DL, MVT_CC);
 | 
					 | 
				
			||||||
          // Note that we want the inverse of ExtraCC, so NZCV is not inversed.
 | 
					 | 
				
			||||||
          unsigned NZCV = AArch64CC::getNZCVToSatisfyCondCode(ExtraCC);
 | 
					 | 
				
			||||||
          ExtraCmp = emitConditionalComparison(LHS, RHS, CC, CCOp, ConditionOp,
 | 
					 | 
				
			||||||
                                               NZCV, DL, DAG);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        CCOp = ExtraCmp;
 | 
					 | 
				
			||||||
        Condition = AArch64CC::getInvertedCondCode(ExtraCC);
 | 
					 | 
				
			||||||
        OutCC = AArch64CC::getInvertedCondCode(OutCC);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Produce a normal comparison if we are first in the chain
 | 
					 | 
				
			||||||
    if (!CCOp.getNode())
 | 
					 | 
				
			||||||
      return emitComparison(LHS, RHS, CC, DL, DAG);
 | 
					 | 
				
			||||||
    // Otherwise produce a ccmp.
 | 
					 | 
				
			||||||
    SDValue ConditionOp = DAG.getConstant(Condition, DL, MVT_CC);
 | 
					 | 
				
			||||||
    AArch64CC::CondCode InvOutCC = AArch64CC::getInvertedCondCode(OutCC);
 | 
					 | 
				
			||||||
    unsigned NZCV = AArch64CC::getNZCVToSatisfyCondCode(InvOutCC);
 | 
					 | 
				
			||||||
    return emitConditionalComparison(LHS, RHS, CC, CCOp, ConditionOp, NZCV, DL,
 | 
					 | 
				
			||||||
                                     DAG);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Construct comparison sequence for the left hand side.
 | 
					 | 
				
			||||||
  SDValue LHS = Val->getOperand(0);
 | 
					 | 
				
			||||||
  SDValue RHS = Val->getOperand(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // We can only implement AND-like behaviour here, but negation is free. So we
 | 
					 | 
				
			||||||
  // use (not (and (not x) (not y))) to implement (or x y).
 | 
					 | 
				
			||||||
  bool isOr = Val->getOpcode() == ISD::OR;
 | 
					 | 
				
			||||||
  assert((isOr || Val->getOpcode() == ISD::AND) && "Should have AND or OR.");
 | 
					 | 
				
			||||||
  Negate ^= isOr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  AArch64CC::CondCode RHSCC;
 | 
					 | 
				
			||||||
  SDValue CmpR =
 | 
					 | 
				
			||||||
      emitConjunctionDisjunctionTree(DAG, RHS, RHSCC, isOr, CCOp, Condition);
 | 
					 | 
				
			||||||
  SDValue CmpL =
 | 
					 | 
				
			||||||
      emitConjunctionDisjunctionTree(DAG, LHS, OutCC, isOr, CmpR, RHSCC);
 | 
					 | 
				
			||||||
  if (Negate)
 | 
					 | 
				
			||||||
    OutCC = AArch64CC::getInvertedCondCode(OutCC);
 | 
					 | 
				
			||||||
  return CmpL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
 | 
					static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
 | 
				
			||||||
                             SDValue &AArch64cc, SelectionDAG &DAG, SDLoc dl) {
 | 
					                             SDValue &AArch64cc, SelectionDAG &DAG, SDLoc dl) {
 | 
				
			||||||
  SDValue Cmp;
 | 
					  SDValue Cmp;
 | 
				
			||||||
@@ -1356,55 +1227,47 @@ static SDValue getAArch64Cmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  // The imm operand of ADDS is an unsigned immediate, in the range 0 to 4095.
 | 
				
			||||||
 | 
					  // For the i8 operand, the largest immediate is 255, so this can be easily
 | 
				
			||||||
 | 
					  // encoded in the compare instruction. For the i16 operand, however, the
 | 
				
			||||||
 | 
					  // largest immediate cannot be encoded in the compare.
 | 
				
			||||||
 | 
					  // Therefore, use a sign extending load and cmn to avoid materializing the -1
 | 
				
			||||||
 | 
					  // constant. For example,
 | 
				
			||||||
 | 
					  // movz w1, #65535
 | 
				
			||||||
 | 
					  // ldrh w0, [x0, #0]
 | 
				
			||||||
 | 
					  // cmp w0, w1
 | 
				
			||||||
 | 
					  // >
 | 
				
			||||||
 | 
					  // ldrsh w0, [x0, #0]
 | 
				
			||||||
 | 
					  // cmn w0, #1
 | 
				
			||||||
 | 
					  // Fundamental, we're relying on the property that (zext LHS) == (zext RHS)
 | 
				
			||||||
 | 
					  // if and only if (sext LHS) == (sext RHS). The checks are in place to ensure
 | 
				
			||||||
 | 
					  // both the LHS and RHS are truely zero extended and to make sure the
 | 
				
			||||||
 | 
					  // transformation is profitable.
 | 
				
			||||||
  if ((CC == ISD::SETEQ || CC == ISD::SETNE) && isa<ConstantSDNode>(RHS)) {
 | 
					  if ((CC == ISD::SETEQ || CC == ISD::SETNE) && isa<ConstantSDNode>(RHS)) {
 | 
				
			||||||
    const ConstantSDNode *RHSC = cast<ConstantSDNode>(RHS);
 | 
					    if ((cast<ConstantSDNode>(RHS)->getZExtValue() >> 16 == 0) &&
 | 
				
			||||||
 | 
					        isa<LoadSDNode>(LHS)) {
 | 
				
			||||||
    // The imm operand of ADDS is an unsigned immediate, in the range 0 to 4095.
 | 
					      if (cast<LoadSDNode>(LHS)->getExtensionType() == ISD::ZEXTLOAD &&
 | 
				
			||||||
    // For the i8 operand, the largest immediate is 255, so this can be easily
 | 
					          cast<LoadSDNode>(LHS)->getMemoryVT() == MVT::i16 &&
 | 
				
			||||||
    // encoded in the compare instruction. For the i16 operand, however, the
 | 
					          LHS.getNode()->hasNUsesOfValue(1, 0)) {
 | 
				
			||||||
    // largest immediate cannot be encoded in the compare.
 | 
					        int16_t ValueofRHS = cast<ConstantSDNode>(RHS)->getZExtValue();
 | 
				
			||||||
    // Therefore, use a sign extending load and cmn to avoid materializing the
 | 
					        if (ValueofRHS < 0 && isLegalArithImmed(-ValueofRHS)) {
 | 
				
			||||||
    // -1 constant. For example,
 | 
					          SDValue SExt =
 | 
				
			||||||
    // movz w1, #65535
 | 
					              DAG.getNode(ISD::SIGN_EXTEND_INREG, dl, LHS.getValueType(), LHS,
 | 
				
			||||||
    // ldrh w0, [x0, #0]
 | 
					                          DAG.getValueType(MVT::i16));
 | 
				
			||||||
    // cmp w0, w1
 | 
					          Cmp = emitComparison(SExt,
 | 
				
			||||||
    // >
 | 
					                               DAG.getConstant(ValueofRHS, dl,
 | 
				
			||||||
    // ldrsh w0, [x0, #0]
 | 
					                                               RHS.getValueType()),
 | 
				
			||||||
    // cmn w0, #1
 | 
					                               CC, dl, DAG);
 | 
				
			||||||
    // Fundamental, we're relying on the property that (zext LHS) == (zext RHS)
 | 
					          AArch64CC = changeIntCCToAArch64CC(CC);
 | 
				
			||||||
    // if and only if (sext LHS) == (sext RHS). The checks are in place to
 | 
					          AArch64cc = DAG.getConstant(AArch64CC, dl, MVT::i32);
 | 
				
			||||||
    // ensure both the LHS and RHS are truely zero extended and to make sure the
 | 
					          return Cmp;
 | 
				
			||||||
    // transformation is profitable.
 | 
					        }
 | 
				
			||||||
    if ((RHSC->getZExtValue() >> 16 == 0) && isa<LoadSDNode>(LHS) &&
 | 
					 | 
				
			||||||
        cast<LoadSDNode>(LHS)->getExtensionType() == ISD::ZEXTLOAD &&
 | 
					 | 
				
			||||||
        cast<LoadSDNode>(LHS)->getMemoryVT() == MVT::i16 &&
 | 
					 | 
				
			||||||
        LHS.getNode()->hasNUsesOfValue(1, 0)) {
 | 
					 | 
				
			||||||
      int16_t ValueofRHS = cast<ConstantSDNode>(RHS)->getZExtValue();
 | 
					 | 
				
			||||||
      if (ValueofRHS < 0 && isLegalArithImmed(-ValueofRHS)) {
 | 
					 | 
				
			||||||
        SDValue SExt =
 | 
					 | 
				
			||||||
            DAG.getNode(ISD::SIGN_EXTEND_INREG, dl, LHS.getValueType(), LHS,
 | 
					 | 
				
			||||||
                        DAG.getValueType(MVT::i16));
 | 
					 | 
				
			||||||
        Cmp = emitComparison(SExt, DAG.getConstant(ValueofRHS, dl,
 | 
					 | 
				
			||||||
                                                   RHS.getValueType()),
 | 
					 | 
				
			||||||
                             CC, dl, DAG);
 | 
					 | 
				
			||||||
        AArch64CC = changeIntCCToAArch64CC(CC);
 | 
					 | 
				
			||||||
        goto CreateCCNode;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ((RHSC->isNullValue() || RHSC->isOne()) &&
 | 
					 | 
				
			||||||
        isConjunctionDisjunctionTree(LHS, 0)) {
 | 
					 | 
				
			||||||
      bool Negate = (CC == ISD::SETNE) ^ RHSC->isNullValue();
 | 
					 | 
				
			||||||
      Cmp = emitConjunctionDisjunctionTree(DAG, LHS, AArch64CC, Negate);
 | 
					 | 
				
			||||||
      goto CreateCCNode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  Cmp = emitComparison(LHS, RHS, CC, dl, DAG);
 | 
					  Cmp = emitComparison(LHS, RHS, CC, dl, DAG);
 | 
				
			||||||
  AArch64CC = changeIntCCToAArch64CC(CC);
 | 
					  AArch64CC = changeIntCCToAArch64CC(CC);
 | 
				
			||||||
 | 
					  AArch64cc = DAG.getConstant(AArch64CC, dl, MVT::i32);
 | 
				
			||||||
CreateCCNode:
 | 
					 | 
				
			||||||
  AArch64cc = DAG.getConstant(AArch64CC, dl, MVT_CC);
 | 
					 | 
				
			||||||
  return Cmp;
 | 
					  return Cmp;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9269,8 +9132,3 @@ bool AArch64TargetLowering::functionArgumentNeedsConsecutiveRegisters(
 | 
				
			|||||||
    Type *Ty, CallingConv::ID CallConv, bool isVarArg) const {
 | 
					    Type *Ty, CallingConv::ID CallConv, bool isVarArg) const {
 | 
				
			||||||
  return Ty->isArrayTy();
 | 
					  return Ty->isArrayTy();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
bool AArch64TargetLowering::shouldNormalizeToSelectSequence(LLVMContext &,
 | 
					 | 
				
			||||||
                                                            EVT) const {
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,11 +58,6 @@ enum NodeType : unsigned {
 | 
				
			|||||||
  SBCS,
 | 
					  SBCS,
 | 
				
			||||||
  ANDS,
 | 
					  ANDS,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Conditional compares. Operands: left,right,falsecc,cc,flags
 | 
					 | 
				
			||||||
  CCMP,
 | 
					 | 
				
			||||||
  CCMN,
 | 
					 | 
				
			||||||
  FCCMP,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Floating point comparison
 | 
					  // Floating point comparison
 | 
				
			||||||
  FCMP,
 | 
					  FCMP,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -513,8 +508,6 @@ private:
 | 
				
			|||||||
  bool functionArgumentNeedsConsecutiveRegisters(Type *Ty,
 | 
					  bool functionArgumentNeedsConsecutiveRegisters(Type *Ty,
 | 
				
			||||||
                                                 CallingConv::ID CallConv,
 | 
					                                                 CallingConv::ID CallConv,
 | 
				
			||||||
                                                 bool isVarArg) const override;
 | 
					                                                 bool isVarArg) const override;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool shouldNormalizeToSelectSequence(LLVMContext &, EVT) const override;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AArch64 {
 | 
					namespace AArch64 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -525,13 +525,6 @@ def imm0_31 : Operand<i64>, ImmLeaf<i64, [{
 | 
				
			|||||||
  let ParserMatchClass = Imm0_31Operand;
 | 
					  let ParserMatchClass = Imm0_31Operand;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// True if the 32-bit immediate is in the range [0,31]
 | 
					 | 
				
			||||||
def imm32_0_31 : Operand<i32>, ImmLeaf<i32, [{
 | 
					 | 
				
			||||||
  return ((uint64_t)Imm) < 32;
 | 
					 | 
				
			||||||
}]> {
 | 
					 | 
				
			||||||
  let ParserMatchClass = Imm0_31Operand;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// imm0_15 predicate - True if the immediate is in the range [0,15]
 | 
					// imm0_15 predicate - True if the immediate is in the range [0,15]
 | 
				
			||||||
def imm0_15 : Operand<i64>, ImmLeaf<i64, [{
 | 
					def imm0_15 : Operand<i64>, ImmLeaf<i64, [{
 | 
				
			||||||
  return ((uint64_t)Imm) < 16;
 | 
					  return ((uint64_t)Imm) < 16;
 | 
				
			||||||
@@ -549,9 +542,7 @@ def imm0_7 : Operand<i64>, ImmLeaf<i64, [{
 | 
				
			|||||||
// imm32_0_15 predicate - True if the 32-bit immediate is in the range [0,15]
 | 
					// imm32_0_15 predicate - True if the 32-bit immediate is in the range [0,15]
 | 
				
			||||||
def imm32_0_15 : Operand<i32>, ImmLeaf<i32, [{
 | 
					def imm32_0_15 : Operand<i32>, ImmLeaf<i32, [{
 | 
				
			||||||
  return ((uint32_t)Imm) < 16;
 | 
					  return ((uint32_t)Imm) < 16;
 | 
				
			||||||
}]> {
 | 
					}]>;
 | 
				
			||||||
  let ParserMatchClass = Imm0_15Operand;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// An arithmetic shifter operand:
 | 
					// An arithmetic shifter operand:
 | 
				
			||||||
//  {7-6} - shift type: 00 = lsl, 01 = lsr, 10 = asr
 | 
					//  {7-6} - shift type: 00 = lsl, 01 = lsr, 10 = asr
 | 
				
			||||||
@@ -2077,12 +2068,9 @@ multiclass LogicalRegS<bits<2> opc, bit N, string mnemonic,
 | 
				
			|||||||
//---
 | 
					//---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
					let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
				
			||||||
class BaseCondComparisonImm<bit op, RegisterClass regtype, ImmLeaf immtype,
 | 
					class BaseCondSetFlagsImm<bit op, RegisterClass regtype, string asm>
 | 
				
			||||||
                            string mnemonic, SDNode OpNode>
 | 
					    : I<(outs), (ins regtype:$Rn, imm0_31:$imm, imm0_15:$nzcv, ccode:$cond),
 | 
				
			||||||
    : I<(outs), (ins regtype:$Rn, immtype:$imm, imm32_0_15:$nzcv, ccode:$cond),
 | 
					         asm, "\t$Rn, $imm, $nzcv, $cond", "", []>,
 | 
				
			||||||
         mnemonic, "\t$Rn, $imm, $nzcv, $cond", "",
 | 
					 | 
				
			||||||
         [(set NZCV, (OpNode regtype:$Rn, immtype:$imm, (i32 imm:$nzcv),
 | 
					 | 
				
			||||||
                             (i32 imm:$cond), NZCV))]>,
 | 
					 | 
				
			||||||
      Sched<[WriteI, ReadI]> {
 | 
					      Sched<[WriteI, ReadI]> {
 | 
				
			||||||
  let Uses = [NZCV];
 | 
					  let Uses = [NZCV];
 | 
				
			||||||
  let Defs = [NZCV];
 | 
					  let Defs = [NZCV];
 | 
				
			||||||
@@ -2102,13 +2090,19 @@ class BaseCondComparisonImm<bit op, RegisterClass regtype, ImmLeaf immtype,
 | 
				
			|||||||
  let Inst{3-0}   = nzcv;
 | 
					  let Inst{3-0}   = nzcv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					multiclass CondSetFlagsImm<bit op, string asm> {
 | 
				
			||||||
 | 
					  def Wi : BaseCondSetFlagsImm<op, GPR32, asm> {
 | 
				
			||||||
 | 
					    let Inst{31} = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  def Xi : BaseCondSetFlagsImm<op, GPR64, asm> {
 | 
				
			||||||
 | 
					    let Inst{31} = 1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
					let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
				
			||||||
class BaseCondComparisonReg<bit op, RegisterClass regtype, string mnemonic,
 | 
					class BaseCondSetFlagsReg<bit op, RegisterClass regtype, string asm>
 | 
				
			||||||
                            SDNode OpNode>
 | 
					    : I<(outs), (ins regtype:$Rn, regtype:$Rm, imm0_15:$nzcv, ccode:$cond),
 | 
				
			||||||
    : I<(outs), (ins regtype:$Rn, regtype:$Rm, imm32_0_15:$nzcv, ccode:$cond),
 | 
					         asm, "\t$Rn, $Rm, $nzcv, $cond", "", []>,
 | 
				
			||||||
         mnemonic, "\t$Rn, $Rm, $nzcv, $cond", "",
 | 
					 | 
				
			||||||
         [(set NZCV, (OpNode regtype:$Rn, regtype:$Rm, (i32 imm:$nzcv),
 | 
					 | 
				
			||||||
                             (i32 imm:$cond), NZCV))]>,
 | 
					 | 
				
			||||||
      Sched<[WriteI, ReadI, ReadI]> {
 | 
					      Sched<[WriteI, ReadI, ReadI]> {
 | 
				
			||||||
  let Uses = [NZCV];
 | 
					  let Uses = [NZCV];
 | 
				
			||||||
  let Defs = [NZCV];
 | 
					  let Defs = [NZCV];
 | 
				
			||||||
@@ -2128,19 +2122,11 @@ class BaseCondComparisonReg<bit op, RegisterClass regtype, string mnemonic,
 | 
				
			|||||||
  let Inst{3-0}   = nzcv;
 | 
					  let Inst{3-0}   = nzcv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
multiclass CondComparison<bit op, string mnemonic, SDNode OpNode> {
 | 
					multiclass CondSetFlagsReg<bit op, string asm> {
 | 
				
			||||||
  // immediate operand variants
 | 
					  def Wr : BaseCondSetFlagsReg<op, GPR32, asm> {
 | 
				
			||||||
  def Wi : BaseCondComparisonImm<op, GPR32, imm32_0_31, mnemonic, OpNode> {
 | 
					 | 
				
			||||||
    let Inst{31} = 0;
 | 
					    let Inst{31} = 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  def Xi : BaseCondComparisonImm<op, GPR64, imm0_31, mnemonic, OpNode> {
 | 
					  def Xr : BaseCondSetFlagsReg<op, GPR64, asm> {
 | 
				
			||||||
    let Inst{31} = 1;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  // register operand variants
 | 
					 | 
				
			||||||
  def Wr : BaseCondComparisonReg<op, GPR32, mnemonic, OpNode> {
 | 
					 | 
				
			||||||
    let Inst{31} = 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  def Xr : BaseCondComparisonReg<op, GPR64, mnemonic, OpNode> {
 | 
					 | 
				
			||||||
    let Inst{31} = 1;
 | 
					    let Inst{31} = 1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -3948,14 +3934,11 @@ multiclass FPComparison<bit signalAllNans, string asm,
 | 
				
			|||||||
//---
 | 
					//---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
					let mayLoad = 0, mayStore = 0, hasSideEffects = 0 in
 | 
				
			||||||
class BaseFPCondComparison<bit signalAllNans, RegisterClass regtype,
 | 
					class BaseFPCondComparison<bit signalAllNans,
 | 
				
			||||||
                           string mnemonic, list<dag> pat>
 | 
					                              RegisterClass regtype, string asm>
 | 
				
			||||||
    : I<(outs), (ins regtype:$Rn, regtype:$Rm, imm32_0_15:$nzcv, ccode:$cond),
 | 
					    : I<(outs), (ins regtype:$Rn, regtype:$Rm, imm0_15:$nzcv, ccode:$cond),
 | 
				
			||||||
         mnemonic, "\t$Rn, $Rm, $nzcv, $cond", "", pat>,
 | 
					         asm, "\t$Rn, $Rm, $nzcv, $cond", "", []>,
 | 
				
			||||||
      Sched<[WriteFCmp]> {
 | 
					      Sched<[WriteFCmp]> {
 | 
				
			||||||
  let Uses = [NZCV];
 | 
					 | 
				
			||||||
  let Defs = [NZCV];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bits<5> Rn;
 | 
					  bits<5> Rn;
 | 
				
			||||||
  bits<5> Rm;
 | 
					  bits<5> Rm;
 | 
				
			||||||
  bits<4> nzcv;
 | 
					  bits<4> nzcv;
 | 
				
			||||||
@@ -3971,18 +3954,16 @@ class BaseFPCondComparison<bit signalAllNans, RegisterClass regtype,
 | 
				
			|||||||
  let Inst{3-0}   = nzcv;
 | 
					  let Inst{3-0}   = nzcv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
multiclass FPCondComparison<bit signalAllNans, string mnemonic,
 | 
					multiclass FPCondComparison<bit signalAllNans, string asm> {
 | 
				
			||||||
                            SDPatternOperator OpNode = null_frag> {
 | 
					  let Defs = [NZCV], Uses = [NZCV] in {
 | 
				
			||||||
  def Srr : BaseFPCondComparison<signalAllNans, FPR32, mnemonic,
 | 
					  def Srr : BaseFPCondComparison<signalAllNans, FPR32, asm> {
 | 
				
			||||||
      [(set NZCV, (OpNode (f32 FPR32:$Rn), (f32 FPR32:$Rm), (i32 imm:$nzcv),
 | 
					 | 
				
			||||||
                          (i32 imm:$cond), NZCV))]> {
 | 
					 | 
				
			||||||
    let Inst{22} = 0;
 | 
					    let Inst{22} = 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  def Drr : BaseFPCondComparison<signalAllNans, FPR64, mnemonic,
 | 
					
 | 
				
			||||||
      [(set NZCV, (OpNode (f64 FPR64:$Rn), (f64 FPR64:$Rm), (i32 imm:$nzcv),
 | 
					  def Drr : BaseFPCondComparison<signalAllNans, FPR64, asm> {
 | 
				
			||||||
                          (i32 imm:$cond), NZCV))]> {
 | 
					 | 
				
			||||||
    let Inst{22} = 1;
 | 
					    let Inst{22} = 1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  } // Defs = [NZCV], Uses = [NZCV]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//---
 | 
					//---
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,20 +66,6 @@ def SDT_AArch64CSel  : SDTypeProfile<1, 4,
 | 
				
			|||||||
                                    SDTCisSameAs<0, 2>,
 | 
					                                    SDTCisSameAs<0, 2>,
 | 
				
			||||||
                                    SDTCisInt<3>,
 | 
					                                    SDTCisInt<3>,
 | 
				
			||||||
                                    SDTCisVT<4, i32>]>;
 | 
					                                    SDTCisVT<4, i32>]>;
 | 
				
			||||||
def SDT_AArch64CCMP : SDTypeProfile<1, 5,
 | 
					 | 
				
			||||||
                                    [SDTCisVT<0, i32>,
 | 
					 | 
				
			||||||
                                     SDTCisInt<1>,
 | 
					 | 
				
			||||||
                                     SDTCisSameAs<1, 2>,
 | 
					 | 
				
			||||||
                                     SDTCisInt<3>,
 | 
					 | 
				
			||||||
                                     SDTCisInt<4>,
 | 
					 | 
				
			||||||
                                     SDTCisVT<5, i32>]>;
 | 
					 | 
				
			||||||
def SDT_AArch64FCCMP : SDTypeProfile<1, 5,
 | 
					 | 
				
			||||||
                                     [SDTCisVT<0, i32>,
 | 
					 | 
				
			||||||
                                      SDTCisFP<1>,
 | 
					 | 
				
			||||||
                                      SDTCisSameAs<1, 2>,
 | 
					 | 
				
			||||||
                                      SDTCisInt<3>,
 | 
					 | 
				
			||||||
                                      SDTCisInt<4>,
 | 
					 | 
				
			||||||
                                      SDTCisVT<5, i32>]>;
 | 
					 | 
				
			||||||
def SDT_AArch64FCmp   : SDTypeProfile<0, 2,
 | 
					def SDT_AArch64FCmp   : SDTypeProfile<0, 2,
 | 
				
			||||||
                                   [SDTCisFP<0>,
 | 
					                                   [SDTCisFP<0>,
 | 
				
			||||||
                                    SDTCisSameAs<0, 1>]>;
 | 
					                                    SDTCisSameAs<0, 1>]>;
 | 
				
			||||||
@@ -174,10 +160,6 @@ def AArch64and_flag  : SDNode<"AArch64ISD::ANDS",  SDTBinaryArithWithFlagsOut,
 | 
				
			|||||||
def AArch64adc_flag  : SDNode<"AArch64ISD::ADCS",  SDTBinaryArithWithFlagsInOut>;
 | 
					def AArch64adc_flag  : SDNode<"AArch64ISD::ADCS",  SDTBinaryArithWithFlagsInOut>;
 | 
				
			||||||
def AArch64sbc_flag  : SDNode<"AArch64ISD::SBCS",  SDTBinaryArithWithFlagsInOut>;
 | 
					def AArch64sbc_flag  : SDNode<"AArch64ISD::SBCS",  SDTBinaryArithWithFlagsInOut>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def AArch64ccmp      : SDNode<"AArch64ISD::CCMP",  SDT_AArch64CCMP>;
 | 
					 | 
				
			||||||
def AArch64ccmn      : SDNode<"AArch64ISD::CCMN",  SDT_AArch64CCMP>;
 | 
					 | 
				
			||||||
def AArch64fccmp     : SDNode<"AArch64ISD::FCCMP", SDT_AArch64FCCMP>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def AArch64threadpointer : SDNode<"AArch64ISD::THREAD_POINTER", SDTPtrLeaf>;
 | 
					def AArch64threadpointer : SDNode<"AArch64ISD::THREAD_POINTER", SDTPtrLeaf>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def AArch64fcmp      : SDNode<"AArch64ISD::FCMP", SDT_AArch64FCmp>;
 | 
					def AArch64fcmp      : SDNode<"AArch64ISD::FCMP", SDT_AArch64FCmp>;
 | 
				
			||||||
@@ -1036,10 +1018,13 @@ def : InstAlias<"uxth $dst, $src", (UBFMXri GPR64:$dst, GPR64:$src, 0, 15)>;
 | 
				
			|||||||
def : InstAlias<"uxtw $dst, $src", (UBFMXri GPR64:$dst, GPR64:$src, 0, 31)>;
 | 
					def : InstAlias<"uxtw $dst, $src", (UBFMXri GPR64:$dst, GPR64:$src, 0, 31)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
// Conditional comparison instructions.
 | 
					// Conditionally set flags instructions.
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
defm CCMN : CondComparison<0, "ccmn", AArch64ccmn>;
 | 
					defm CCMN : CondSetFlagsImm<0, "ccmn">;
 | 
				
			||||||
defm CCMP : CondComparison<1, "ccmp", AArch64ccmp>;
 | 
					defm CCMP : CondSetFlagsImm<1, "ccmp">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defm CCMN : CondSetFlagsReg<0, "ccmn">;
 | 
				
			||||||
 | 
					defm CCMP : CondSetFlagsReg<1, "ccmp">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
// Conditional select instructions.
 | 
					// Conditional select instructions.
 | 
				
			||||||
@@ -2569,7 +2554,7 @@ defm FCMP  : FPComparison<0, "fcmp", AArch64fcmp>;
 | 
				
			|||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defm FCCMPE : FPCondComparison<1, "fccmpe">;
 | 
					defm FCCMPE : FPCondComparison<1, "fccmpe">;
 | 
				
			||||||
defm FCCMP  : FPCondComparison<0, "fccmp", AArch64fccmp>;
 | 
					defm FCCMP  : FPCondComparison<0, "fccmp">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
// Floating point conditional select instruction.
 | 
					// Floating point conditional select instruction.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -287,43 +287,3 @@ sw.bb.i.i:
 | 
				
			|||||||
  %code1.i.i.phi.trans.insert = getelementptr inbounds %str1, %str1* %0, i64 0, i32 0, i32 0, i64 16
 | 
					  %code1.i.i.phi.trans.insert = getelementptr inbounds %str1, %str1* %0, i64 0, i32 0, i32 0, i64 16
 | 
				
			||||||
  br label %sw.bb.i.i
 | 
					  br label %sw.bb.i.i
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
; CHECK-LABEL: select_and
 | 
					 | 
				
			||||||
define i64 @select_and(i32 %v1, i32 %v2, i64 %a, i64 %b) {
 | 
					 | 
				
			||||||
; CHECK: cmp
 | 
					 | 
				
			||||||
; CHECK: ccmp{{.*}}, #0, ne
 | 
					 | 
				
			||||||
; CHECK: csel{{.*}}, lt
 | 
					 | 
				
			||||||
  %1 = icmp slt i32 %v1, %v2
 | 
					 | 
				
			||||||
  %2 = icmp ne i32 5, %v2
 | 
					 | 
				
			||||||
  %3 = and i1 %1, %2
 | 
					 | 
				
			||||||
  %sel = select i1 %3, i64 %a, i64 %b
 | 
					 | 
				
			||||||
  ret i64 %sel
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; CHECK-LABEL: select_or
 | 
					 | 
				
			||||||
define i64 @select_or(i32 %v1, i32 %v2, i64 %a, i64 %b) {
 | 
					 | 
				
			||||||
; CHECK: cmp
 | 
					 | 
				
			||||||
; CHECK: ccmp{{.*}}, #8, eq
 | 
					 | 
				
			||||||
; CHECK: csel{{.*}}, lt
 | 
					 | 
				
			||||||
  %1 = icmp slt i32 %v1, %v2
 | 
					 | 
				
			||||||
  %2 = icmp ne i32 5, %v2
 | 
					 | 
				
			||||||
  %3 = or i1 %1, %2
 | 
					 | 
				
			||||||
  %sel = select i1 %3, i64 %a, i64 %b
 | 
					 | 
				
			||||||
  ret i64 %sel
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; CHECK-LABEL: select_complicated
 | 
					 | 
				
			||||||
define i16 @select_complicated(double %v1, double %v2, i16 %a, i16 %b) {
 | 
					 | 
				
			||||||
; CHECK: fcmp
 | 
					 | 
				
			||||||
; CHECK: fccmp{{.*}}, #4, ne
 | 
					 | 
				
			||||||
; CHECK: fccmp{{.*}}, #1, ne
 | 
					 | 
				
			||||||
; CHECK: fccmp{{.*}}, #4, vc
 | 
					 | 
				
			||||||
; CEHCK: csel{{.*}}, eq
 | 
					 | 
				
			||||||
  %1 = fcmp one double %v1, %v2
 | 
					 | 
				
			||||||
  %2 = fcmp oeq double %v2, 13.0
 | 
					 | 
				
			||||||
  %3 = fcmp oeq double %v1, 42.0
 | 
					 | 
				
			||||||
  %or0 = or i1 %2, %3
 | 
					 | 
				
			||||||
  %or1 = or i1 %1, %or0
 | 
					 | 
				
			||||||
  %sel = select i1 %or1, i16 %a, i16 %b
 | 
					 | 
				
			||||||
  ret i16 %sel
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user