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:
Akira Hatanaka 2017-09-23 05:02:02 +00:00
parent 2e4086bb2d
commit d0d150d713
4 changed files with 367 additions and 6 deletions

View File

@ -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;

View File

@ -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

162
test/CodeGen/fp16vec-ops.c Normal file
View File

@ -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;
}

51
test/Sema/fp16vec-sema.c Normal file
View File

@ -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}}
}