mirror of https://github.com/microsoft/clang.git
Promote storage-only __fp16 vector operands to float vectors.
This commit fixes a bug in the handling of storage-only __fp16 vectors where clang didn't promote __fp16 vector operands to float vectors. Conceptually, it performs the following transformation on the AST in CreateBuiltinBinOp and CreateBuiltinUnaryOp: (Before) typedef __fp16 half4 __attribute__ ((vector_size (8))); typedef float float4 __attribute__ ((vector_size (16))); half4 hv0, hv1, hv2, hv3; hv0 = hv1 + hv2 + hv3; (After) float4 t0 = (float4)hv1 + (float4)hv2; float4 t1 = t0 + (float4)hv3; hv0 = (half4)t1; Note that this commit fixes the bug for targets that set HalfArgsAndReturns to true (ARM and ARM64). Targets using intrinsics such as llvm.convert.to.fp16 to handle __fp16 are still broken. rdar://problem/20625184 Differential Revision: https://reviews.llvm.org/D32520 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@314056 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
2e4086bb2d
commit
d0d150d713
|
@ -1014,10 +1014,41 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
|
|||
return Builder.CreateVectorSplat(NumElements, Src, "splat");
|
||||
}
|
||||
|
||||
// Allow bitcast from vector to integer/fp of the same size.
|
||||
if (isa<llvm::VectorType>(SrcTy) ||
|
||||
isa<llvm::VectorType>(DstTy))
|
||||
return Builder.CreateBitCast(Src, DstTy, "conv");
|
||||
if (isa<llvm::VectorType>(SrcTy) || isa<llvm::VectorType>(DstTy)) {
|
||||
// Allow bitcast from vector to integer/fp of the same size.
|
||||
unsigned SrcSize = SrcTy->getPrimitiveSizeInBits();
|
||||
unsigned DstSize = DstTy->getPrimitiveSizeInBits();
|
||||
if (SrcSize == DstSize)
|
||||
return Builder.CreateBitCast(Src, DstTy, "conv");
|
||||
|
||||
// Conversions between vectors of different sizes are not allowed except
|
||||
// when vectors of half are involved. Operations on storage-only half
|
||||
// vectors require promoting half vector operands to float vectors and
|
||||
// truncating the result, which is either an int or float vector, to a
|
||||
// short or half vector.
|
||||
|
||||
// Source and destination are both expected to be vectors.
|
||||
llvm::Type *SrcElementTy = SrcTy->getVectorElementType();
|
||||
llvm::Type *DstElementTy = DstTy->getVectorElementType();
|
||||
|
||||
assert(((SrcElementTy->isIntegerTy() &&
|
||||
DstElementTy->isIntegerTy()) ||
|
||||
(SrcElementTy->isFloatingPointTy() &&
|
||||
DstElementTy->isFloatingPointTy())) &&
|
||||
"unexpected conversion between a floating-point vector and an "
|
||||
"integer vector");
|
||||
|
||||
// Truncate an i32 vector to an i16 vector.
|
||||
if (SrcElementTy->isIntegerTy())
|
||||
return Builder.CreateIntCast(Src, DstTy, false, "conv");
|
||||
|
||||
// Truncate a float vector to a half vector.
|
||||
if (SrcSize > DstSize)
|
||||
return Builder.CreateFPTrunc(Src, DstTy, "conv");
|
||||
|
||||
// Promote a half vector to a float vector.
|
||||
return Builder.CreateFPExt(Src, DstTy, "conv");
|
||||
}
|
||||
|
||||
// Finally, we have the arithmetic types: real int/float.
|
||||
Value *Res = nullptr;
|
||||
|
|
|
@ -7503,6 +7503,14 @@ Sema::CheckAssignmentConstraints(SourceLocation Loc,
|
|||
return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false);
|
||||
}
|
||||
|
||||
/// This helper function returns true if QT is a vector type that has element
|
||||
/// type ElementType.
|
||||
static bool isVector(QualType QT, QualType ElementType) {
|
||||
if (const VectorType *VT = QT->getAs<VectorType>())
|
||||
return VT->getElementType() == ElementType;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently
|
||||
/// has code to accommodate several GCC extensions when type checking
|
||||
/// pointers. Here are some objectionable examples that GCC considers warnings:
|
||||
|
@ -8131,6 +8139,25 @@ static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar,
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Convert vector E to a vector with the same number of elements but different
|
||||
/// element type.
|
||||
static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) {
|
||||
const auto *VecTy = E->getType()->getAs<VectorType>();
|
||||
assert(VecTy && "Expression E must be a vector");
|
||||
QualType NewVecTy = S.Context.getVectorType(ElementType,
|
||||
VecTy->getNumElements(),
|
||||
VecTy->getVectorKind());
|
||||
|
||||
// Look through the implicit cast. Return the subexpression if its type is
|
||||
// NewVecTy.
|
||||
if (auto *ICE = dyn_cast<ImplicitCastExpr>(E))
|
||||
if (ICE->getSubExpr()->getType() == NewVecTy)
|
||||
return ICE->getSubExpr();
|
||||
|
||||
auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast;
|
||||
return S.ImpCastExprToType(E, NewVecTy, Cast);
|
||||
}
|
||||
|
||||
/// Test if a (constant) integer Int can be casted to another integer type
|
||||
/// IntTy without losing precision.
|
||||
static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int,
|
||||
|
@ -11465,6 +11492,41 @@ static NamedDecl *getDeclFromExpr(Expr *E) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// This helper function promotes a binary operator's operands (which are of a
|
||||
// half vector type) to a vector of floats and then truncates the result to
|
||||
// a vector of either half or short.
|
||||
static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS,
|
||||
BinaryOperatorKind Opc, QualType ResultTy,
|
||||
ExprValueKind VK, ExprObjectKind OK,
|
||||
bool IsCompAssign, SourceLocation OpLoc,
|
||||
FPOptions FPFeatures) {
|
||||
auto &Context = S.getASTContext();
|
||||
assert((isVector(ResultTy, Context.HalfTy) ||
|
||||
isVector(ResultTy, Context.ShortTy)) &&
|
||||
"Result must be a vector of half or short");
|
||||
assert(isVector(LHS.get()->getType(), Context.HalfTy) &&
|
||||
isVector(RHS.get()->getType(), Context.HalfTy) &&
|
||||
"both operands expected to be a half vector");
|
||||
|
||||
RHS = convertVector(RHS.get(), Context.FloatTy, S);
|
||||
QualType BinOpResTy = RHS.get()->getType();
|
||||
|
||||
// If Opc is a comparison, ResultType is a vector of shorts. In that case,
|
||||
// change BinOpResTy to a vector of ints.
|
||||
if (isVector(ResultTy, Context.ShortTy))
|
||||
BinOpResTy = S.GetSignedVectorType(BinOpResTy);
|
||||
|
||||
if (IsCompAssign)
|
||||
return new (Context) CompoundAssignOperator(
|
||||
LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, BinOpResTy, BinOpResTy,
|
||||
OpLoc, FPFeatures);
|
||||
|
||||
LHS = convertVector(LHS.get(), Context.FloatTy, S);
|
||||
auto *BO = new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, BinOpResTy,
|
||||
VK, OK, OpLoc, FPFeatures);
|
||||
return convertVector(BO, ResultTy->getAs<VectorType>()->getElementType(), S);
|
||||
}
|
||||
|
||||
static std::pair<ExprResult, ExprResult>
|
||||
CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr,
|
||||
Expr *RHSExpr) {
|
||||
|
@ -11485,6 +11547,14 @@ CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr,
|
|||
return std::make_pair(LHS, RHS);
|
||||
}
|
||||
|
||||
/// Returns true if conversion between vectors of halfs and vectors of floats
|
||||
/// is needed.
|
||||
static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx,
|
||||
QualType SrcType) {
|
||||
return OpRequiresConversion && !Ctx.getLangOpts().NativeHalfType &&
|
||||
Ctx.getLangOpts().HalfArgsAndReturns && isVector(SrcType, Ctx.HalfTy);
|
||||
}
|
||||
|
||||
/// CreateBuiltinBinOp - Creates a new built-in binary operation with
|
||||
/// operator @p Opc at location @c TokLoc. This routine only supports
|
||||
/// built-in operations; ActOnBinOp handles overloaded operators.
|
||||
|
@ -11516,6 +11586,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
QualType CompResultTy; // Type of computation result
|
||||
ExprValueKind VK = VK_RValue;
|
||||
ExprObjectKind OK = OK_Ordinary;
|
||||
bool ConvertHalfVec = false;
|
||||
|
||||
std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr);
|
||||
if (!LHS.isUsable() || !RHS.isUsable())
|
||||
|
@ -11567,6 +11638,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
break;
|
||||
case BO_Mul:
|
||||
case BO_Div:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false,
|
||||
Opc == BO_Div);
|
||||
break;
|
||||
|
@ -11574,9 +11646,11 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc);
|
||||
break;
|
||||
case BO_Add:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc);
|
||||
break;
|
||||
case BO_Sub:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc);
|
||||
break;
|
||||
case BO_Shl:
|
||||
|
@ -11587,10 +11661,12 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
case BO_LT:
|
||||
case BO_GE:
|
||||
case BO_GT:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
|
||||
break;
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
|
||||
break;
|
||||
case BO_And:
|
||||
|
@ -11602,10 +11678,12 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
break;
|
||||
case BO_LAnd:
|
||||
case BO_LOr:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc);
|
||||
break;
|
||||
case BO_MulAssign:
|
||||
case BO_DivAssign:
|
||||
ConvertHalfVec = true;
|
||||
CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true,
|
||||
Opc == BO_DivAssign);
|
||||
CompLHSTy = CompResultTy;
|
||||
|
@ -11619,11 +11697,13 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
|
||||
break;
|
||||
case BO_AddAssign:
|
||||
ConvertHalfVec = true;
|
||||
CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy);
|
||||
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
|
||||
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
|
||||
break;
|
||||
case BO_SubAssign:
|
||||
ConvertHalfVec = true;
|
||||
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy);
|
||||
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
|
||||
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
|
||||
|
@ -11656,6 +11736,16 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
// Some of the binary operations require promoting operands of half vector to
|
||||
// float vectors and truncating the result back to half vector. For now, we do
|
||||
// this only when HalfArgsAndReturn is set (that is, when the target is arm or
|
||||
// arm64).
|
||||
assert(isVector(RHS.get()->getType(), Context.HalfTy) ==
|
||||
isVector(LHS.get()->getType(), Context.HalfTy) &&
|
||||
"both sides are half vectors or neither sides are");
|
||||
ConvertHalfVec = needsConversionOfHalfVec(ConvertHalfVec, Context,
|
||||
LHS.get()->getType());
|
||||
|
||||
// Check for array bounds violations for both sides of the BinaryOperator
|
||||
CheckArrayAccess(LHS.get());
|
||||
CheckArrayAccess(RHS.get());
|
||||
|
@ -11678,14 +11768,26 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
dyn_cast<ObjCIvarRefExpr>(LHS.get()->IgnoreParenCasts()))
|
||||
DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get());
|
||||
|
||||
if (CompResultTy.isNull())
|
||||
// Opc is not a compound assignment if CompResultTy is null.
|
||||
if (CompResultTy.isNull()) {
|
||||
if (ConvertHalfVec)
|
||||
return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false,
|
||||
OpLoc, FPFeatures);
|
||||
return new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, ResultTy, VK,
|
||||
OK, OpLoc, FPFeatures);
|
||||
}
|
||||
|
||||
// Handle compound assignments.
|
||||
if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() !=
|
||||
OK_ObjCProperty) {
|
||||
VK = VK_LValue;
|
||||
OK = LHS.get()->getObjectKind();
|
||||
}
|
||||
|
||||
if (ConvertHalfVec)
|
||||
return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true,
|
||||
OpLoc, FPFeatures);
|
||||
|
||||
return new (Context) CompoundAssignOperator(
|
||||
LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, CompLHSTy, CompResultTy,
|
||||
OpLoc, FPFeatures);
|
||||
|
@ -12043,6 +12145,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
|
|||
ExprValueKind VK = VK_RValue;
|
||||
ExprObjectKind OK = OK_Ordinary;
|
||||
QualType resultType;
|
||||
bool ConvertHalfVec = false;
|
||||
if (getLangOpts().OpenCL) {
|
||||
QualType Ty = InputExpr->getType();
|
||||
// The only legal unary operation for atomics is '&'.
|
||||
|
@ -12082,6 +12185,16 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
|
|||
case UO_Minus:
|
||||
Input = UsualUnaryConversions(Input.get());
|
||||
if (Input.isInvalid()) return ExprError();
|
||||
// Unary plus and minus require promoting an operand of half vector to a
|
||||
// float vector and truncating the result back to a half vector. For now, we
|
||||
// do this only when HalfArgsAndReturns is set (that is, when the target is
|
||||
// arm or arm64).
|
||||
ConvertHalfVec =
|
||||
needsConversionOfHalfVec(true, Context, Input.get()->getType());
|
||||
|
||||
// If the operand is a half vector, promote it to a float vector.
|
||||
if (ConvertHalfVec)
|
||||
Input = convertVector(Input.get(), Context.FloatTy, *this);
|
||||
resultType = Input.get()->getType();
|
||||
if (resultType->isDependentType())
|
||||
break;
|
||||
|
@ -12219,8 +12332,12 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
|
|||
if (Opc != UO_AddrOf && Opc != UO_Deref)
|
||||
CheckArrayAccess(Input.get());
|
||||
|
||||
return new (Context)
|
||||
auto *UO = new (Context)
|
||||
UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc);
|
||||
// Convert the result back to a half vector.
|
||||
if (ConvertHalfVec)
|
||||
return convertVector(UO, Context.HalfTy, *this);
|
||||
return UO;
|
||||
}
|
||||
|
||||
/// \brief Determine whether the given expression is a qualified member
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
// REQUIRES: arm-registered-target
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios9 -emit-llvm -o - -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=CHECK
|
||||
// RUN: %clang_cc1 -triple armv7-apple-ios9 -emit-llvm -o - -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=CHECK
|
||||
|
||||
typedef __fp16 half4 __attribute__ ((vector_size (8)));
|
||||
typedef short short4 __attribute__ ((vector_size (8)));
|
||||
|
||||
half4 hv0, hv1;
|
||||
short4 sv0;
|
||||
|
||||
// CHECK-LABEL: testFP16Vec0
|
||||
// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
|
||||
// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
|
||||
// CHECK: %[[ADD:.*]] = fadd <4 x float> %[[CONV]], %[[CONV1]]
|
||||
// CHECK: %[[CONV2:.*]] = fptrunc <4 x float> %[[ADD]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV2]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
|
||||
// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
|
||||
// CHECK: %[[SUB:.*]] = fsub <4 x float> %[[CONV3]], %[[CONV4]]
|
||||
// CHECK: %[[CONV5:.*]] = fptrunc <4 x float> %[[SUB]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV5]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV6:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
|
||||
// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV7:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
|
||||
// CHECK: %[[MUL:.*]] = fmul <4 x float> %[[CONV6]], %[[CONV7]]
|
||||
// CHECK: %[[CONV8:.*]] = fptrunc <4 x float> %[[MUL]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV8]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
|
||||
// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV10:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
|
||||
// CHECK: %[[DIV:.*]] = fdiv <4 x float> %[[CONV9]], %[[CONV10]]
|
||||
// CHECK: %[[CONV11:.*]] = fptrunc <4 x float> %[[DIV]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV11]], <4 x half>* @hv0, align 8
|
||||
|
||||
void testFP16Vec0() {
|
||||
hv0 = hv0 + hv1;
|
||||
hv0 = hv0 - hv1;
|
||||
hv0 = hv0 * hv1;
|
||||
hv0 = hv0 / hv1;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testFP16Vec1
|
||||
// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
|
||||
// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
|
||||
// CHECK: %[[ADD:.*]] = fadd <4 x float> %[[CONV1]], %[[CONV]]
|
||||
// CHECK: %[[CONV2:.*]] = fptrunc <4 x float> %[[ADD]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV2]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
|
||||
// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
|
||||
// CHECK: %[[SUB:.*]] = fsub <4 x float> %[[CONV4]], %[[CONV3]]
|
||||
// CHECK: %[[CONV5:.*]] = fptrunc <4 x float> %[[SUB]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV5]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV6:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
|
||||
// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV7:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
|
||||
// CHECK: %[[MUL:.*]] = fmul <4 x float> %[[CONV7]], %[[CONV6]]
|
||||
// CHECK: %[[CONV8:.*]] = fptrunc <4 x float> %[[MUL]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV8]], <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
|
||||
// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV10:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
|
||||
// CHECK: %[[DIV:.*]] = fdiv <4 x float> %[[CONV10]], %[[CONV9]]
|
||||
// CHECK: %[[CONV11:.*]] = fptrunc <4 x float> %[[DIV]] to <4 x half>
|
||||
// CHECK: store <4 x half> %[[CONV11]], <4 x half>* @hv0, align 8
|
||||
|
||||
void testFP16Vec1() {
|
||||
hv0 += hv1;
|
||||
hv0 -= hv1;
|
||||
hv0 *= hv1;
|
||||
hv0 /= hv1;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testFP16Vec2
|
||||
// CHECK: %[[CADDR:.*]] = alloca i32, align 4
|
||||
// CHECK: store i32 %[[C:.*]], i32* %[[CADDR]], align 4
|
||||
// CHECK: %[[V0:.*]] = load i32, i32* %[[CADDR]], align 4
|
||||
// CHECK: %[[TOBOOL:.*]] = icmp ne i32 %[[V0]], 0
|
||||
// CHECK: br i1 %[[TOBOOL]], label %{{.*}}, label %{{.*}}
|
||||
//
|
||||
// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: br label %{{.*}}
|
||||
//
|
||||
// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: br label %{{.*}}
|
||||
//
|
||||
// CHECK: %[[COND:.*]] = phi <4 x half> [ %[[V1]], %{{.*}} ], [ %[[V2]], %{{.*}} ]
|
||||
// CHECK: store <4 x half> %[[COND]], <4 x half>* @hv0, align 8
|
||||
|
||||
void testFP16Vec2(int c) {
|
||||
hv0 = c ? hv0 : hv1;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: testFP16Vec3
|
||||
// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
|
||||
// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
|
||||
// CHECK: %[[CMP:.*]] = fcmp oeq <4 x float> %[[CONV]], %[[CONV1]]
|
||||
// CHECK: %[[SEXT:.*]] = sext <4 x i1> %[[CMP]] to <4 x i32>
|
||||
// CHECK: %[[CONV2:.*]] = trunc <4 x i32> %[[SEXT]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV2]], <4 x i16>* @sv0, align 8
|
||||
// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
|
||||
// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
|
||||
// CHECK: %[[CMP5:.*]] = fcmp une <4 x float> %[[CONV3]], %[[CONV4]]
|
||||
// CHECK: %[[SEXT6:.*]] = sext <4 x i1> %[[CMP5]] to <4 x i32>
|
||||
// CHECK: %[[CONV7:.*]] = trunc <4 x i32> %[[SEXT6]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV7]], <4 x i16>* @sv0, align 8
|
||||
// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV8:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
|
||||
// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
|
||||
// CHECK: %[[CMP10:.*]] = fcmp olt <4 x float> %[[CONV8]], %[[CONV9]]
|
||||
// CHECK: %[[SEXT11:.*]] = sext <4 x i1> %[[CMP10]] to <4 x i32>
|
||||
// CHECK: %[[CONV12:.*]] = trunc <4 x i32> %[[SEXT11]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV12]], <4 x i16>* @sv0, align 8
|
||||
// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV13:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
|
||||
// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV14:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
|
||||
// CHECK: %[[CMP15:.*]] = fcmp ogt <4 x float> %[[CONV13]], %[[CONV14]]
|
||||
// CHECK: %[[SEXT16:.*]] = sext <4 x i1> %[[CMP15]] to <4 x i32>
|
||||
// CHECK: %[[CONV17:.*]] = trunc <4 x i32> %[[SEXT16]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV17]], <4 x i16>* @sv0, align 8
|
||||
// CHECK: %[[V8:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV18:.*]] = fpext <4 x half> %[[V8]] to <4 x float>
|
||||
// CHECK: %[[V9:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV19:.*]] = fpext <4 x half> %[[V9]] to <4 x float>
|
||||
// CHECK: %[[CMP20:.*]] = fcmp ole <4 x float> %[[CONV18]], %[[CONV19]]
|
||||
// CHECK: %[[SEXT21:.*]] = sext <4 x i1> %[[CMP20]] to <4 x i32>
|
||||
// CHECK: %[[CONV22:.*]] = trunc <4 x i32> %[[SEXT21]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV22]], <4 x i16>* @sv0, align 8
|
||||
// CHECK: %[[V10:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
|
||||
// CHECK: %[[CONV23:.*]] = fpext <4 x half> %[[V10]] to <4 x float>
|
||||
// CHECK: %[[V11:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
|
||||
// CHECK: %[[CONV24:.*]] = fpext <4 x half> %[[V11]] to <4 x float>
|
||||
// CHECK: %[[CMP25:.*]] = fcmp oge <4 x float> %[[CONV23]], %[[CONV24]]
|
||||
// CHECK: %[[SEXT26:.*]] = sext <4 x i1> %[[CMP25]] to <4 x i32>
|
||||
// CHECK: %[[CONV27:.*]] = trunc <4 x i32> %[[SEXT26]] to <4 x i16>
|
||||
// CHECK: store <4 x i16> %[[CONV27]], <4 x i16>* @sv0, align 8
|
||||
|
||||
void testFP16Vec3() {
|
||||
sv0 = hv0 == hv1;
|
||||
sv0 = hv0 != hv1;
|
||||
sv0 = hv0 < hv1;
|
||||
sv0 = hv0 > hv1;
|
||||
sv0 = hv0 <= hv1;
|
||||
sv0 = hv0 >= hv1;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
typedef __fp16 half4 __attribute__ ((vector_size (8)));
|
||||
typedef float float4 __attribute__ ((vector_size (16)));
|
||||
typedef short short4 __attribute__ ((vector_size (8)));
|
||||
typedef int int4 __attribute__ ((vector_size (16)));
|
||||
|
||||
half4 hv0, hv1;
|
||||
float4 fv0, fv1;
|
||||
short4 sv0;
|
||||
int4 iv0;
|
||||
|
||||
void testFP16Vec(int c) {
|
||||
hv0 = hv0 + hv1;
|
||||
hv0 = hv0 - hv1;
|
||||
hv0 = hv0 * hv1;
|
||||
hv0 = hv0 / hv1;
|
||||
hv0 = c ? hv0 : hv1;
|
||||
hv0 += hv1;
|
||||
hv0 -= hv1;
|
||||
hv0 *= hv1;
|
||||
hv0 /= hv1;
|
||||
sv0 = hv0 == hv1;
|
||||
sv0 = hv0 != hv1;
|
||||
sv0 = hv0 < hv1;
|
||||
sv0 = hv0 > hv1;
|
||||
sv0 = hv0 <= hv1;
|
||||
sv0 = hv0 >= hv1;
|
||||
sv0 = hv0 || hv1; // expected-error{{logical expression with vector types 'half4' (vector of 4 '__fp16' values) and 'half4' is only supported in C++}}
|
||||
sv0 = hv0 && hv1; // expected-error{{logical expression with vector types 'half4' (vector of 4 '__fp16' values) and 'half4' is only supported in C++}}
|
||||
|
||||
// Implicit conversion between half vectors and float vectors are not allowed.
|
||||
hv0 = fv0; // expected-error{{assigning to}}
|
||||
fv0 = hv0; // expected-error{{assigning to}}
|
||||
hv0 = (half4)fv0; // expected-error{{invalid conversion between}}
|
||||
fv0 = (float4)hv0; // expected-error{{invalid conversion between}}
|
||||
hv0 = fv0 + fv1; // expected-error{{assigning to}}
|
||||
fv0 = hv0 + hv1; // expected-error{{assigning to}}
|
||||
hv0 = hv0 + fv1; // expected-error{{cannot convert between vector}}
|
||||
hv0 = c ? hv0 : fv1; // expected-error{{cannot convert between vector}}
|
||||
sv0 = hv0 == fv1; // expected-error{{cannot convert between vector}}
|
||||
sv0 = hv0 < fv1; // expected-error{{cannot convert between vector}}
|
||||
sv0 = hv0 || fv1; // expected-error{{cannot convert between vector}} expected-error{{invalid operands to binary expression}}
|
||||
iv0 = hv0 == hv1; // expected-error{{assigning to}}
|
||||
|
||||
// FIXME: clang currently disallows using these operators on vectors, which is
|
||||
// allowed by gcc.
|
||||
sv0 = !hv0; // expected-error{{invalid argument type}}
|
||||
hv0++; // expected-error{{cannot increment value of type}}
|
||||
++hv0; // expected-error{{cannot increment value of type}}
|
||||
}
|
Loading…
Reference in New Issue