summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/OperationKinds.def4
-rw-r--r--clang/include/clang/AST/Type.h3
-rw-r--r--clang/include/clang/Basic/DiagnosticCommonKinds.td2
-rw-r--r--clang/lib/AST/Expr.cpp1
-rw-r--r--clang/lib/AST/ExprConstant.cpp2
-rw-r--r--clang/lib/AST/Type.cpp1
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp1
-rw-r--r--clang/lib/CodeGen/CGExprAgg.cpp1
-rw-r--r--clang/lib/CodeGen/CGExprComplex.cpp1
-rw-r--r--clang/lib/CodeGen/CGExprConstant.cpp1
-rw-r--r--clang/lib/CodeGen/CGExprScalar.cpp111
-rw-r--r--clang/lib/Edit/RewriteObjCFoundationAPI.cpp3
-rw-r--r--clang/lib/Sema/Sema.cpp2
-rw-r--r--clang/lib/Sema/SemaExpr.cpp52
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp3
-rw-r--r--clang/test/Frontend/fixed_point_conversions.c283
-rw-r--r--clang/test/Frontend/fixed_point_unknown_conversions.c50
17 files changed, 517 insertions, 4 deletions
diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def
index e2d65d84880..47b8d67af2d 100644
--- a/clang/include/clang/AST/OperationKinds.def
+++ b/clang/include/clang/AST/OperationKinds.def
@@ -197,6 +197,10 @@ CAST_OPERATION(IntegralToBoolean)
/// float f = i;
CAST_OPERATION(IntegralToFloating)
+/// CK_FixedPointCast - Fixed point to fixed point.
+/// (_Accum) 0.5r
+CAST_OPERATION(FixedPointCast)
+
/// CK_FloatingToIntegral - Floating point to integral. Rounds
/// towards zero, discarding any fractional component.
/// (int) f
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 369ce6dfa1c..cbcce8832e7 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2066,7 +2066,8 @@ public:
STK_Integral,
STK_Floating,
STK_IntegralComplex,
- STK_FloatingComplex
+ STK_FloatingComplex,
+ STK_FixedPoint
};
/// Given that this is a scalar type, classify it.
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index c3e2aa8b5c3..f0aff9a188c 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -172,6 +172,8 @@ def err_too_large_for_fixed_point : Error<
"this value is too large for this fixed point type">;
def err_fixed_point_not_enabled : Error<"compile with "
"'-ffixed-point' to enable fixed point types">;
+def err_unimplemented_conversion_with_fixed_point_type : Error<
+ "conversion between fixed point and %0 is not yet supported">;
// SEH
def err_seh_expected_handler : Error<
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 651981374d4..b9606876e43 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1644,6 +1644,7 @@ bool CastExpr::CastConsistency() const {
case CK_ZeroToOCLEvent:
case CK_ZeroToOCLQueue:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
assert(!getType()->isBooleanType() && "unheralded conversion to bool");
goto CheckNoBasePath;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d2258cc2124..de0de5d13cd 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9556,6 +9556,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_NonAtomicToAtomic:
case CK_AddressSpaceConversion:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
llvm_unreachable("invalid cast kind for integral value");
case CK_BitCast:
@@ -10090,6 +10091,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_NonAtomicToAtomic:
case CK_AddressSpaceConversion:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
llvm_unreachable("invalid cast kind for complex value");
case CK_LValueToRValue:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 1b4b84d030f..08f91c6ca99 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1965,6 +1965,7 @@ Type::ScalarTypeKind Type::getScalarTypeKind() const {
if (BT->getKind() == BuiltinType::NullPtr) return STK_CPointer;
if (BT->isInteger()) return STK_Integral;
if (BT->isFloatingPoint()) return STK_Floating;
+ if (BT->isFixedPointType()) return STK_FixedPoint;
llvm_unreachable("unknown scalar builtin type");
} else if (isa<PointerType>(T)) {
return STK_CPointer;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 028aa963cda..55fa4551167 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4153,6 +4153,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
case CK_CopyAndAutoreleaseBlockObject:
case CK_AddressSpaceConversion:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
return EmitUnsupportedLValue(E, "unexpected cast lvalue");
case CK_Dependent:
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 62641102861..9e809872ce9 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -851,6 +851,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
case CK_ZeroToOCLQueue:
case CK_AddressSpaceConversion:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
llvm_unreachable("cast kind invalid for aggregate types");
}
}
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index fb176093a74..1d5438668bb 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -509,6 +509,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
case CK_ZeroToOCLQueue:
case CK_AddressSpaceConversion:
case CK_IntToOCLSampler:
+ case CK_FixedPointCast:
llvm_unreachable("invalid cast kind for complex value");
case CK_FloatingRealToComplex:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 651b05a26f7..3dbb20dab2a 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -871,6 +871,7 @@ public:
case CK_FloatingCast:
case CK_ZeroToOCLEvent:
case CK_ZeroToOCLQueue:
+ case CK_FixedPointCast:
return nullptr;
}
llvm_unreachable("Invalid CastKind");
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 0bbd788f028..3abb26991a4 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -11,11 +11,11 @@
//
//===----------------------------------------------------------------------===//
-#include "CodeGenFunction.h"
-#include "CGCleanup.h"
#include "CGCXXABI.h"
+#include "CGCleanup.h"
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
+#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
@@ -23,6 +23,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/FixedPoint.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/ADT/Optional.h"
@@ -327,6 +328,9 @@ public:
SourceLocation Loc,
ScalarConversionOpts Opts = ScalarConversionOpts());
+ Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
+ SourceLocation Loc);
+
/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src,
@@ -1011,6 +1015,10 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
QualType DstType,
SourceLocation Loc,
ScalarConversionOpts Opts) {
+ assert(!SrcType->isFixedPointType() && !DstType->isFixedPointType() &&
+ "Use the ScalarExprEmitter::EmitFixedPoint family functions for "
+ "handling conversions involving fixed point types.");
+
QualType NoncanonicalSrcType = SrcType;
QualType NoncanonicalDstType = DstType;
@@ -1204,6 +1212,101 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
return Res;
}
+Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
+ QualType DstTy,
+ SourceLocation Loc) {
+ using llvm::APInt;
+ using llvm::ConstantInt;
+ using llvm::Value;
+
+ assert(SrcTy->isFixedPointType());
+ assert(DstTy->isFixedPointType());
+
+ FixedPointSemantics SrcFPSema =
+ CGF.getContext().getFixedPointSemantics(SrcTy);
+ FixedPointSemantics DstFPSema =
+ CGF.getContext().getFixedPointSemantics(DstTy);
+ unsigned SrcWidth = SrcFPSema.getWidth();
+ unsigned DstWidth = DstFPSema.getWidth();
+ unsigned SrcScale = SrcFPSema.getScale();
+ unsigned DstScale = DstFPSema.getScale();
+ bool IsSigned = SrcFPSema.isSigned();
+
+ Value *Result = Src;
+ unsigned ResultWidth = SrcWidth;
+
+ if (!DstFPSema.isSaturated()) {
+ // Downscale
+ if (DstScale < SrcScale) {
+ if (IsSigned)
+ Result = Builder.CreateAShr(Result, SrcScale - DstScale);
+ else
+ Result = Builder.CreateLShr(Result, SrcScale - DstScale);
+ }
+
+ // Resize
+ llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
+ if (IsSigned)
+ Result = Builder.CreateSExtOrTrunc(Result, DstIntTy);
+ else
+ Result = Builder.CreateZExtOrTrunc(Result, DstIntTy);
+
+ // Upscale
+ if (DstScale > SrcScale)
+ Result = Builder.CreateShl(Result, DstScale - SrcScale);
+ } else {
+ if (DstScale > SrcScale) {
+ // Need to extend first before scaling up
+ ResultWidth = SrcWidth + DstScale - SrcScale;
+ llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
+
+ if (IsSigned)
+ Result = Builder.CreateSExt(Result, UpscaledTy);
+ else
+ Result = Builder.CreateZExt(Result, UpscaledTy);
+
+ Result = Builder.CreateShl(Result, DstScale - SrcScale);
+ } else if (DstScale < SrcScale) {
+ if (IsSigned)
+ Result = Builder.CreateAShr(Result, SrcScale - DstScale);
+ else
+ Result = Builder.CreateLShr(Result, SrcScale - DstScale);
+ }
+
+ if (DstFPSema.getIntegralBits() < SrcFPSema.getIntegralBits()) {
+ auto Max = ConstantInt::get(
+ CGF.getLLVMContext(),
+ APFixedPoint::getMax(DstFPSema).getValue().extOrTrunc(ResultWidth));
+ Value *TooHigh = IsSigned ? Builder.CreateICmpSGT(Result, Max)
+ : Builder.CreateICmpUGT(Result, Max);
+ Result = Builder.CreateSelect(TooHigh, Max, Result);
+
+ if (IsSigned) {
+ // Cannot overflow min to dest type is src is unsigned since all fixed
+ // point types can cover the unsigned min of 0.
+ auto Min = ConstantInt::get(
+ CGF.getLLVMContext(),
+ APFixedPoint::getMin(DstFPSema).getValue().extOrTrunc(ResultWidth));
+ Value *TooLow = Builder.CreateICmpSLT(Result, Min);
+ Result = Builder.CreateSelect(TooLow, Min, Result);
+ }
+ } else if (IsSigned && !DstFPSema.isSigned()) {
+ llvm::Type *ResultTy = Builder.getIntNTy(ResultWidth);
+ Value *Zero = ConstantInt::getNullValue(ResultTy);
+ Value *LTZero = Builder.CreateICmpSLT(Result, Zero);
+ Result = Builder.CreateSelect(LTZero, Zero, Result);
+ }
+
+ // Final resizing to dst width
+ llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
+ if (IsSigned)
+ Result = Builder.CreateSExtOrTrunc(Result, DstIntTy);
+ else
+ Result = Builder.CreateZExtOrTrunc(Result, DstIntTy);
+ }
+ return Result;
+}
+
/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
Value *ScalarExprEmitter::EmitComplexToScalarConversion(
@@ -1894,6 +1997,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return Builder.CreateVectorSplat(NumElements, Elt, "splat");
}
+ case CK_FixedPointCast:
+ return EmitFixedPointConversion(Visit(E), E->getType(), DestTy,
+ CE->getExprLoc());
+
case CK_IntegralCast: {
ScalarConversionOpts Opts;
if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) {
diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
index 3a305ecd94e..e18c488d877 100644
--- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -1085,6 +1085,9 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
case CK_BooleanToSignedIntegral:
llvm_unreachable("OpenCL-specific cast in Objective-C?");
+
+ case CK_FixedPointCast:
+ llvm_unreachable("Fixed point types are disabled for Objective-C");
}
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 916f23c1caa..7e162029888 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -533,6 +533,8 @@ CastKind Sema::ScalarTypeToBooleanCastKind(QualType ScalarTy) {
case Type::STK_Floating: return CK_FloatingToBoolean;
case Type::STK_IntegralComplex: return CK_IntegralComplexToBoolean;
case Type::STK_FloatingComplex: return CK_FloatingComplexToBoolean;
+ case Type::STK_FixedPoint:
+ llvm_unreachable("Unknown cast from FixedPoint to boolean");
}
llvm_unreachable("unknown scalar type kind");
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8144b3a7796..72499b0e905 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5884,10 +5884,36 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
case Type::STK_FloatingComplex:
case Type::STK_IntegralComplex:
case Type::STK_MemberPointer:
+ case Type::STK_FixedPoint:
llvm_unreachable("illegal cast from pointer");
}
llvm_unreachable("Should have returned before this");
+ case Type::STK_FixedPoint:
+ switch (DestTy->getScalarTypeKind()) {
+ case Type::STK_FixedPoint:
+ return CK_FixedPointCast;
+ case Type::STK_Bool:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << DestTy;
+ return CK_IntegralToBoolean;
+ case Type::STK_Integral:
+ case Type::STK_Floating:
+ case Type::STK_IntegralComplex:
+ case Type::STK_FloatingComplex:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << DestTy;
+ return CK_IntegralCast;
+ case Type::STK_CPointer:
+ case Type::STK_ObjCObjectPointer:
+ case Type::STK_BlockPointer:
+ case Type::STK_MemberPointer:
+ llvm_unreachable("illegal cast to pointer type");
+ }
+ llvm_unreachable("Should have returned before this");
+
case Type::STK_Bool: // casting from bool is like casting from an integer
case Type::STK_Integral:
switch (DestTy->getScalarTypeKind()) {
@@ -5916,6 +5942,11 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
return CK_FloatingRealToComplex;
case Type::STK_MemberPointer:
llvm_unreachable("member pointer type in C");
+ case Type::STK_FixedPoint:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << SrcTy;
+ return CK_IntegralCast;
}
llvm_unreachable("Should have returned before this");
@@ -5943,6 +5974,11 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
llvm_unreachable("valid float->pointer cast?");
case Type::STK_MemberPointer:
llvm_unreachable("member pointer type in C");
+ case Type::STK_FixedPoint:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << SrcTy;
+ return CK_IntegralCast;
}
llvm_unreachable("Should have returned before this");
@@ -5972,6 +6008,11 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
llvm_unreachable("valid complex float->pointer cast?");
case Type::STK_MemberPointer:
llvm_unreachable("member pointer type in C");
+ case Type::STK_FixedPoint:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << SrcTy;
+ return CK_IntegralCast;
}
llvm_unreachable("Should have returned before this");
@@ -6001,6 +6042,11 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
llvm_unreachable("valid complex int->pointer cast?");
case Type::STK_MemberPointer:
llvm_unreachable("member pointer type in C");
+ case Type::STK_FixedPoint:
+ Diag(Src.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << SrcTy;
+ return CK_IntegralCast;
}
llvm_unreachable("Should have returned before this");
}
@@ -12747,6 +12793,12 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
if (Context.getLangOpts().CPlusPlus) {
// C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
// operand contextually converted to bool.
+ if (resultType->getScalarTypeKind() == Type::STK_FixedPoint) {
+ return ExprError(
+ Diag(Input.get()->getExprLoc(),
+ diag::err_unimplemented_conversion_with_fixed_point_type)
+ << resultType);
+ }
Input = ImpCastExprToType(Input.get(), Context.BoolTy,
ScalarTypeToBooleanCastKind(resultType));
} else if (Context.getLangOpts().OpenCL &&
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 61b7a290e42..f40a8d9a546 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -415,7 +415,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_ZeroToOCLEvent:
case CK_ZeroToOCLQueue:
case CK_IntToOCLSampler:
- case CK_LValueBitCast: {
+ case CK_LValueBitCast:
+ case CK_FixedPointCast: {
state =
handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
continue;
diff --git a/clang/test/Frontend/fixed_point_conversions.c b/clang/test/Frontend/fixed_point_conversions.c
new file mode 100644
index 00000000000..4064ac5d2cb
--- /dev/null
+++ b/clang/test/Frontend/fixed_point_conversions.c
@@ -0,0 +1,283 @@
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s -check-prefix=SAME
+
+void TestFixedPointCastSameType() {
+ _Accum a = 2.5k;
+ _Accum a2 = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4
+
+ a2 = (_Accum)a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4
+}
+
+void TestFixedPointCastDown() {
+ long _Accum la = 2.5lk;
+ _Accum a = la;
+ // DEFAULT: [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8
+ // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ a = (_Accum)la;
+ // DEFAULT: [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8
+ // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ short _Accum sa = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+ // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16
+ // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2
+
+ sa = (short _Accum)a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+ // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16
+ // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2
+}
+
+void TestFixedPointCastUp() {
+ short _Accum sa = 2.5hk;
+ _Accum a = sa;
+ // DEFAULT: [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2
+ // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i32
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i32 [[SACCUM_BUFF]], 8
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ long _Accum la = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i64
+ // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i64 [[ACCUM_BUFF]], 16
+ // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %la, align 8
+
+ a = (_Accum)sa;
+ // DEFAULT: [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2
+ // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i32
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i32 [[SACCUM_BUFF]], 8
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ la = (long _Accum)a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i64
+ // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i64 [[ACCUM_BUFF]], 16
+ // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %la, align 8
+}
+
+void TestFixedPointCastSignedness() {
+ _Accum a = 2.5k;
+ unsigned _Accum ua = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i32 [[ACCUM]], 1
+ // DEFAULT-NEXT: store i32 [[UACCUM]], i32* %ua, align 4
+ // SAME: TestFixedPointCastSignedness
+ // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+
+ a = ua;
+ // DEFAULT: [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+ // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+ // SAME-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ ua = (unsigned _Accum)a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i32 [[ACCUM]], 1
+ // DEFAULT-NEXT: store i32 [[UACCUM]], i32* %ua, align 4
+
+ a = (_Accum)ua;
+ // DEFAULT: [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ _Accum a2;
+ unsigned long _Accum ula = a2;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4
+ // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i64
+ // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i64 [[ACCUM_EXT]], 17
+ // DEFAULT-NEXT: store i64 [[LACCUM]], i64* %ula, align 8
+ // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4
+ // SAME-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i64
+ // SAME-NEXT: [[LACCUM:%[0-9]+]] = shl i64 [[ACCUM_EXT]], 16
+ // SAME-NEXT: store i64 [[LACCUM]], i64* %ula, align 8
+}
+
+void TestFixedPointCastSaturation() {
+ _Accum a;
+ _Sat short _Accum sat_sa;
+ _Sat _Accum sat_a;
+ _Sat long _Accum sat_la;
+ _Sat unsigned short _Accum sat_usa;
+ _Sat unsigned _Accum sat_ua;
+ _Sat unsigned long _Accum sat_ula;
+ _Sat short _Fract sat_sf;
+ _Sat _Fract sat_f;
+ _Sat long _Fract sat_lf;
+
+ // Casting down between types
+ sat_sa = sat_a;
+ // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+ // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+ // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_sa, align 2
+
+ // Accum to Fract, decreasing scale
+ sat_sf = sat_a;
+ // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+ // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[FRACT]], 127
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 127, i32 [[FRACT]]
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -128
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -128, i32 [[RESULT]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i8
+ // DEFAULT-NEXT: store i8 [[RESULT_TRUNC]], i8* %sat_sf, align 1
+
+ // Accum to Fract, same scale
+ sat_f = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+ // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_f, align 2
+
+ // Accum to Fract, increasing scale
+ sat_lf = sat_a;
+ // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i48
+ // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = shl i48 [[ACCUM]], 16
+ // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i48 [[FRACT]], 2147483647
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i48 2147483647, i48 [[FRACT]]
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i48 [[RESULT]], -2147483648
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i48 -2147483648, i48 [[RESULT]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i48 [[RESULT2]] to i32
+ // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_lf, align 4
+
+ // Signed to unsigned, decreasing scale
+ _Sat _Accum sat_a2;
+ sat_usa = sat_a2;
+ // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 7
+ // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 65535
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 65535, i32 [[ACCUM]]
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+ // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2
+ // SAME: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4
+ // SAME-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+ // SAME-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+ // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+ // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0
+ // SAME-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]]
+ // SAME-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+ // SAME-NEXT: store i16 [[RESULT_TRUNC]], i16* %sat_usa, align 2
+
+ // Signed to unsigned, increasing scale
+ sat_ua = sat_a;
+ // DEFAULT: [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i33
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1
+ // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[ACCUM]], 0
+ // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i33 0, i33 [[ACCUM]]
+ // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i33 [[RESULT2]] to i32
+ // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_ua, align 4
+ // SAME: [[ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+ // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACCUM]]
+ // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4
+
+ // Nothing when saturating to the same type and size
+ sat_a = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %sat_a, align 4
+
+ // Nothing when assigning back
+ a = sat_a;
+ // DEFAULT: [[SAT_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+ // DEFAULT-NEXT: store i32 [[SAT_ACCUM]], i32* %a, align 4
+
+ // No overflow when casting from fract to signed accum
+ sat_a = sat_f;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i16, i16* %sat_f, align 2
+ // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i16 [[FRACT]] to i32
+ // DEFAULT-NEXT: store i32 [[FRACT_EXT]], i32* %sat_a, align 4
+
+ // Only get overflow checking if signed fract to unsigned accum
+ sat_ua = sat_sf;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i8, i8* %sat_sf, align 1
+ // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i17
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i17 [[FRACT_EXT]], 9
+ // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i17 [[ACCUM]], 0
+ // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]]
+ // DEFAULT-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i17 [[RESULT]] to i32
+ // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+ // SAME: [[FRACT:%[0-9]+]] = load i8, i8* %sat_sf, align 1
+ // SAME-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i16
+ // SAME-NEXT: [[ACCUM:%[0-9]+]] = shl i16 [[FRACT_EXT]], 8
+ // SAME-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i16 [[ACCUM]], 0
+ // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]]
+ // SAME-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i16 [[RESULT]] to i32
+ // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+}
+
+void TestFixedPointCastBetFractAccum() {
+ short _Accum sa;
+ _Accum a;
+ long _Accum la;
+ short _Fract sf;
+ _Fract f;
+ long _Fract lf;
+ unsigned _Accum ua;
+ unsigned _Fract uf;
+
+ // To lower scale
+ sf = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+ // DEFAULT-NEXT: [[FRACT_TRUNC:%[0-9]+]] = trunc i32 [[FRACT]] to i8
+ // DEFAULT-NEXT: store i8 [[FRACT_TRUNC]], i8* %sf, align 1
+
+ // To higher scale
+ a = sf;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i8, i8* %sf, align 1
+ // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i32
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i32 [[FRACT_EXT]], 8
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ // To same scale
+ f = a;
+ // DEFAULT: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = trunc i32 [[ACCUM]] to i16
+ // DEFAULT-NEXT: store i16 [[FRACT]], i16* %f, align 2
+
+ a = f;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i16, i16* %f, align 2
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = sext i16 [[FRACT]] to i32
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+ // To unsigned
+ ua = uf;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32
+ // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+ // SAME: [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2
+ // SAME-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32
+ // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+
+ uf = ua;
+ // DEFAULT: [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4
+ // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16
+ // DEFAULT-NEXT: store i16 [[ACCUM]], i16* %uf, align 2
+ // SAME: [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4
+ // SAME-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16
+ // SAME-NEXT: store i16 [[ACCUM]], i16* %uf, align 2
+}
diff --git a/clang/test/Frontend/fixed_point_unknown_conversions.c b/clang/test/Frontend/fixed_point_unknown_conversions.c
new file mode 100644
index 00000000000..94b1fc32e9b
--- /dev/null
+++ b/clang/test/Frontend/fixed_point_unknown_conversions.c
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -verify -ffixed-point %s
+
+void func() {
+ _Bool b;
+ char c;
+ int i;
+ float f;
+ double d;
+ double _Complex dc;
+ int _Complex ic;
+ struct S {
+ int i;
+ } s;
+ enum E {
+ A
+ } e;
+ int *ptr;
+ typedef int int_t;
+ int_t i2;
+
+ _Accum accum;
+ _Fract fract = accum; // ok
+ _Accum *accum_ptr;
+
+ accum = b; // expected-error{{conversion between fixed point and '_Bool' is not yet supported}}
+ accum = i; // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+ accum = i; // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+ accum = f; // expected-error{{conversion between fixed point and 'float' is not yet supported}}
+ accum = d; // expected-error{{conversion between fixed point and 'double' is not yet supported}}
+ accum = dc; // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}}
+ accum = ic; // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}}
+ accum = s; // expected-error{{assigning to '_Accum' from incompatible type 'struct S'}}
+ accum = e; // expected-error{{conversion between fixed point and 'enum E' is not yet supported}}
+ accum = ptr; // expected-error{{assigning to '_Accum' from incompatible type 'int *'}}
+ accum_ptr = ptr; // expected-warning{{incompatible pointer types assigning to '_Accum *' from 'int *'}}
+ accum = i2; // expected-error{{conversion between fixed point and 'int_t' (aka 'int') is not yet supported}}
+
+ b = accum; // expected-error{{conversion between fixed point and '_Bool' is not yet supported}}
+ c = accum; // expected-error{{conversion between fixed point and 'char' is not yet supported}}
+ i = accum; // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+ f = accum; // expected-error{{conversion between fixed point and 'float' is not yet supported}}
+ d = accum; // expected-error{{conversion between fixed point and 'double' is not yet supported}}
+ dc = accum; // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}}
+ ic = accum; // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}}
+ s = accum; // expected-error{{assigning to 'struct S' from incompatible type '_Accum'}}
+ e = accum; // expected-error{{conversion between fixed point and 'enum E' is not yet supported}}
+ ptr = accum; // expected-error{{assigning to 'int *' from incompatible type '_Accum'}}
+ ptr = accum_ptr; // expected-warning{{incompatible pointer types assigning to 'int *' from '_Accum *'}}
+ i2 = accum; // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+}
OpenPOWER on IntegriCloud