Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -9373,7 +9373,8 @@ /// C99 6.5.16. AssignConvertType CheckAssignmentConstraints(SourceLocation Loc, QualType LHSType, - QualType RHSType); + QualType RHSType, + bool IsCompAssign = false); /// Check assignment constraints and optionally prepare for a conversion of /// the RHS to the LHS type. The conversion is prepared for if ConvertRHS @@ -9381,7 +9382,8 @@ AssignConvertType CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, CastKind &Kind, - bool ConvertRHS = true); + bool ConvertRHS = true, + bool IsCompAssign = false); /// Check assignment constraints for an assignment of RHS to LHSType. /// Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -1014,10 +1014,48 @@ return Builder.CreateVectorSplat(NumElements, Src, "splat"); } - // Allow bitcast from vector to integer/fp of the same size. - if (isa(SrcTy) || - isa(DstTy)) - return Builder.CreateBitCast(Src, DstTy, "conv"); + if (isa(SrcTy) || isa(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(); + + // Truncate an i32 vector to an i16 vector. + if (SrcElementTy->isIntegerTy()) { + assert(SrcElementTy == CGF.Int32Ty && + "Src is expected to be an i32 vector"); + assert(DstElementTy == CGF.Int16Ty && + "Dst is expected to be an i16 vector"); + return Builder.CreateIntCast(Src, DstTy, false, "conv"); + } + + assert(SrcElementTy->isFloatingPointTy() && + DstElementTy->isFloatingPointTy() && + "Source and destination must be floating point vectors"); + + // Truncate a float vector to a half vector. + if (SrcSize > DstSize) { + assert(SrcElementTy->isFloatTy() && DstElementTy->isHalfTy() && + "float vector to half vector conversion expected"); + return Builder.CreateFPTrunc(Src, DstTy, "conv"); + } + + // Promote a half vector to a float vector. + assert(SrcElementTy->isHalfTy() && DstElementTy->isFloatTy() && + "half vector to float vector conversion expected"); + return Builder.CreateFPExt(Src, DstTy, "conv"); + } // Finally, we have the arithmetic types: real int/float. Value *Res = nullptr; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7405,7 +7405,8 @@ Sema::AssignConvertType Sema::CheckAssignmentConstraints(SourceLocation Loc, - QualType LHSType, QualType RHSType) { + QualType LHSType, QualType RHSType, + bool IsCompAssign) { // Fake up an opaque expression. We don't actually care about what // cast operations are required, so if CheckAssignmentConstraints // adds casts to this they'll be wasted, but fortunately that doesn't @@ -7414,7 +7415,16 @@ ExprResult RHSPtr = &RHSExpr; CastKind K = CK_Invalid; - return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false); + return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false, + IsCompAssign); +} + +/// 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()) + return VT->getElementType() == ElementType; + return false; } /// CheckAssignmentConstraints (C99 6.5.16) - This routine currently @@ -7436,7 +7446,8 @@ /// Sets 'Kind' for any result kind except Incompatible. Sema::AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, - CastKind &Kind, bool ConvertRHS) { + CastKind &Kind, bool ConvertRHS, + bool IsCompAssign) { QualType RHSType = RHS.get()->getType(); QualType OrigLHSType = LHSType; @@ -7496,6 +7507,18 @@ // Conversions to or from vector type. if (LHSType->isVectorType() || RHSType->isVectorType()) { if (LHSType->isVectorType() && RHSType->isVectorType()) { + // If this is a compound assignment, allow converting the RHS to the type + // of the LHS. + if (IsCompAssign && isVector(LHSType, Context.HalfTy)) { + assert(isVector(RHSType, Context.FloatTy) && + "RHS is not a vector of floats"); + assert(LHSType->getAs()->getNumElements() == + RHSType->getAs()->getNumElements() && + "LHS and RHS have different number of elements"); + Kind = CK_FloatingCast; + return Compatible; + } + // Allow assignments of an AltiVec vector type to an equivalent GCC // vector type and vice versa if (Context.areCompatibleVectorTypes(LHSType, RHSType)) { @@ -8045,6 +8068,25 @@ return false; } +/// Convert E, which is a vector, to a vector that has a different element +/// type. +static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) { + const auto *VecTy = E->getType()->getAs(); + 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(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, @@ -10541,7 +10583,8 @@ } } else { // Compound assignment "x += y" - ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); + ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType, + /*CompAssign*/true); } if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, @@ -11269,6 +11312,34 @@ 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"); + RHS = convertVector(RHS.get(), Context.FloatTy, S); + QualType BinOpResTy = RHS.get()->getType(); + 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()->getElementType(), S); +} + /// 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. @@ -11300,6 +11371,7 @@ QualType CompResultTy; // Type of computation result ExprValueKind VK = VK_RValue; ExprObjectKind OK = OK_Ordinary; + bool ConvertHalfVec = false; if (!getLangOpts().CPlusPlus) { // C cannot handle TypoExpr nodes on either side of a binop because it @@ -11363,6 +11435,7 @@ break; case BO_Mul: case BO_Div: + ConvertHalfVec = true; ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, Opc == BO_Div); break; @@ -11370,9 +11443,11 @@ 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: @@ -11383,10 +11458,12 @@ 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: @@ -11398,10 +11475,12 @@ 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; @@ -11415,11 +11494,13 @@ 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); @@ -11452,6 +11533,16 @@ if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); + // Some of the binary operations require promoting operands of half vector + // and truncating the result. For now, we do this only when HalfArgsAndReturn + // is set (that is, when the target is arm or arm64). + bool LHSHalfVec = isVector(LHS.get()->getType(), Context.HalfTy); + assert(isVector(RHS.get()->getType(), Context.HalfTy) == LHSHalfVec && + "both sides are half vectors or neither sides are"); + ConvertHalfVec = ConvertHalfVec && LHSHalfVec && + !Context.getLangOpts().NativeHalfType && + Context.getLangOpts().HalfArgsAndReturns; + // Check for array bounds violations for both sides of the BinaryOperator CheckArrayAccess(LHS.get()); CheckArrayAccess(RHS.get()); @@ -11474,14 +11565,24 @@ dyn_cast(LHS.get()->IgnoreParenCasts())) DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get()); - if (CompResultTy.isNull()) + 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); + } + 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); @@ -11832,6 +11933,7 @@ 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 '&'. @@ -11871,6 +11973,17 @@ case UO_Minus: Input = UsualUnaryConversions(Input.get()); if (Input.isInvalid()) return ExprError(); + // Unary plus and minus require promoting an operand of half vector and + // truncating the result. For now, we do this only when HalfArgsAndReturns + // is set (that is, when the target is arm or arm64). + ConvertHalfVec = + !Context.getLangOpts().NativeHalfType && + Context.getLangOpts().HalfArgsAndReturns && + isVector(Input.get()->getType(), Context.HalfTy); + + // 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; @@ -12008,8 +12121,12 @@ 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 Index: test/CodeGen/fp16vec-ops.c =================================================================== --- /dev/null +++ test/CodeGen/fp16vec-ops.c @@ -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; +} Index: test/Sema/fp16vec-sema.c =================================================================== --- /dev/null +++ test/Sema/fp16vec-sema.c @@ -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}} +}