|
12 | 12 |
|
13 | 13 | #include "swift/SILOptimizer/Utils/ConstantFolding.h"
|
14 | 14 |
|
15 |
| -#include "swift/AST/Expr.h" |
16 | 15 | #include "swift/AST/DiagnosticsSIL.h"
|
| 16 | +#include "swift/AST/Expr.h" |
17 | 17 | #include "swift/SIL/PatternMatch.h"
|
18 | 18 | #include "swift/SIL/SILBuilder.h"
|
19 | 19 | #include "swift/SILOptimizer/Utils/CastOptimizer.h"
|
20 | 20 | #include "swift/SILOptimizer/Utils/Local.h"
|
| 21 | +#include "llvm/ADT/APFloat.h" |
| 22 | +#include "llvm/ADT/APSInt.h" |
21 | 23 | #include "llvm/ADT/Statistic.h"
|
22 | 24 | #include "llvm/Support/Debug.h"
|
23 |
| -#include "llvm/ADT/APSInt.h" |
24 | 25 |
|
25 | 26 | #define DEBUG_TYPE "constant-folding"
|
26 | 27 |
|
@@ -970,6 +971,205 @@ static SILValue foldFPToIntConversion(BuiltinInst *BI,
|
970 | 971 | return B.createIntegerLiteral(BI->getLoc(), BI->getType(), resInt);
|
971 | 972 | }
|
972 | 973 |
|
| 974 | +/// Captures the layout of IEEE754 floating point values. |
| 975 | +struct IEEESemantics { |
| 976 | + uint8_t bitWidth; |
| 977 | + uint8_t exponentBitWidth; |
| 978 | + uint8_t significandBitWidth; // Ignores the integer part. |
| 979 | + bool explicitIntegerPart; |
| 980 | + int minExponent; |
| 981 | + |
| 982 | +public: |
| 983 | + IEEESemantics(uint8_t bits, uint8_t expBits, uint8_t sigBits, |
| 984 | + bool explicitIntPart) { |
| 985 | + bitWidth = bits; |
| 986 | + exponentBitWidth = expBits; |
| 987 | + significandBitWidth = sigBits; |
| 988 | + explicitIntegerPart = explicitIntPart; |
| 989 | + minExponent = -(1 << (exponentBitWidth - 1)) + 2; |
| 990 | + } |
| 991 | +}; |
| 992 | + |
| 993 | +IEEESemantics getFPSemantics(BuiltinFloatType *fpType) { |
| 994 | + switch (fpType->getFPKind()) { |
| 995 | + case BuiltinFloatType::IEEE32: |
| 996 | + return IEEESemantics(32, 8, 23, false); |
| 997 | + case BuiltinFloatType::IEEE64: |
| 998 | + return IEEESemantics(64, 11, 52, false); |
| 999 | + case BuiltinFloatType::IEEE80: |
| 1000 | + return IEEESemantics(80, 15, 63, true); |
| 1001 | + default: |
| 1002 | + llvm_unreachable("Unexpected semantics"); |
| 1003 | + } |
| 1004 | +} |
| 1005 | + |
| 1006 | +/// This function, given the exponent and significand of a binary fraction |
| 1007 | +/// equalling 1.srcSignificand x 2^srcExponent, |
| 1008 | +/// determines whether converting the value to a given destination semantics |
| 1009 | +/// results in an underflow and whether the significand precision is reduced |
| 1010 | +/// because of the underflow. |
| 1011 | +bool isLossyUnderflow(int srcExponent, uint64_t srcSignificand, |
| 1012 | + IEEESemantics srcSem, IEEESemantics destSem) { |
| 1013 | + if (srcExponent >= destSem.minExponent) |
| 1014 | + return false; |
| 1015 | + |
| 1016 | + // Is the value smaller than the smallest non-zero value of destSem? |
| 1017 | + if (srcExponent < destSem.minExponent - destSem.significandBitWidth) |
| 1018 | + return true; |
| 1019 | + |
| 1020 | + // Truncate the significand to the significand width of destSem. |
| 1021 | + uint8_t bitWidthDecrease = |
| 1022 | + srcSem.significandBitWidth - destSem.significandBitWidth; |
| 1023 | + uint64_t truncSignificand = bitWidthDecrease > 0 |
| 1024 | + ? (srcSignificand >> bitWidthDecrease) |
| 1025 | + : srcSignificand; |
| 1026 | + |
| 1027 | + // Compute the significand bits lost due to subnormal form. Note that the |
| 1028 | + // integer part: 1 will use up a significand bit in denormal form. |
| 1029 | + unsigned additionalLoss = destSem.minExponent - srcExponent + 1; |
| 1030 | + |
| 1031 | + // Check whether a set LSB is lost due to subnormal representation. |
| 1032 | + unsigned lostLSBBitMask = (1 << additionalLoss) - 1; |
| 1033 | + return (truncSignificand & lostLSBBitMask); |
| 1034 | +} |
| 1035 | + |
| 1036 | +/// This function, given an IEEE floating-point value (srcVal), determines |
| 1037 | +/// whether the conversion to a given destination semantics results |
| 1038 | +/// in an underflow and whether the significand precision is reduced |
| 1039 | +/// because of the underflow. |
| 1040 | +bool isLossyUnderflow(APFloat srcVal, BuiltinFloatType *srcType, |
| 1041 | + BuiltinFloatType *destType) { |
| 1042 | + if (srcVal.isNaN() || srcVal.isZero() || srcVal.isInfinity()) |
| 1043 | + return false; |
| 1044 | + |
| 1045 | + IEEESemantics srcSem = getFPSemantics(srcType); |
| 1046 | + IEEESemantics destSem = getFPSemantics(destType); |
| 1047 | + |
| 1048 | + if (srcSem.bitWidth <= destSem.bitWidth) |
| 1049 | + return false; |
| 1050 | + |
| 1051 | + if (srcVal.isDenormal()) { |
| 1052 | + // A denormal value of a larger IEEE FP type will definitely |
| 1053 | + // reduce to zero when truncated to smaller IEEE FP type. |
| 1054 | + return true; |
| 1055 | + } |
| 1056 | + |
| 1057 | + APInt bitPattern = srcVal.bitcastToAPInt(); |
| 1058 | + uint64_t significand = |
| 1059 | + bitPattern.getLoBits(srcSem.significandBitWidth).getZExtValue(); |
| 1060 | + return isLossyUnderflow(ilogb(srcVal), significand, srcSem, destSem); |
| 1061 | +} |
| 1062 | + |
| 1063 | +/// This function determines whether the float literal in the given |
| 1064 | +/// SIL instruction is specified using hex-float notation in the Swift source. |
| 1065 | +bool isHexLiteralInSource(FloatLiteralInst *flitInst) { |
| 1066 | + Expr *expr = flitInst->getLoc().getAsASTNode<Expr>(); |
| 1067 | + if (!expr) |
| 1068 | + return false; |
| 1069 | + |
| 1070 | + // Iterate through a sequence of folded implicit constructors if any, and |
| 1071 | + // try to extract the FloatLiteralExpr. |
| 1072 | + while (auto *callExpr = dyn_cast<CallExpr>(expr)) { |
| 1073 | + if (!callExpr->isImplicit() || callExpr->getNumArguments() != 1 || |
| 1074 | + !dyn_cast<ConstructorRefCallExpr>(callExpr->getFn())) |
| 1075 | + break; |
| 1076 | + |
| 1077 | + auto *tupleExpr = dyn_cast<TupleExpr>(callExpr->getArg()); |
| 1078 | + if (!tupleExpr) |
| 1079 | + break; |
| 1080 | + |
| 1081 | + expr = tupleExpr->getElement(0); |
| 1082 | + } |
| 1083 | + auto *flitExpr = dyn_cast<FloatLiteralExpr>(expr); |
| 1084 | + if (!flitExpr) |
| 1085 | + return false; |
| 1086 | + return flitExpr->getDigitsText().startswith("0x"); |
| 1087 | +} |
| 1088 | + |
| 1089 | +bool maybeExplicitFPCons(BuiltinInst *BI, const BuiltinInfo &Builtin) { |
| 1090 | + assert(Builtin.ID == BuiltinValueKind::FPTrunc); |
| 1091 | + |
| 1092 | + auto *callExpr = BI->getLoc().getAsASTNode<CallExpr>(); |
| 1093 | + if (!callExpr || !dyn_cast<ConstructorRefCallExpr>(callExpr->getFn())) |
| 1094 | + return true; // not enough information here, so err on the safer side. |
| 1095 | + |
| 1096 | + if (!callExpr->isImplicit()) |
| 1097 | + return true; |
| 1098 | + |
| 1099 | + // Here, the 'callExpr' is an implicit FP construction. However, if it is |
| 1100 | + // constructing a Double it could be a part of an explicit construction of |
| 1101 | + // another FP type, which uses an implicit conversion to Double as an |
| 1102 | + // intermediate step. So we conservatively assume that an implicit |
| 1103 | + // construction of Double could be a part of an explicit conversion |
| 1104 | + // and suppress the warning. |
| 1105 | + auto &astCtx = BI->getModule().getASTContext(); |
| 1106 | + auto *typeDecl = callExpr->getType()->getCanonicalType().getAnyNominal(); |
| 1107 | + return (typeDecl && typeDecl == astCtx.getDoubleDecl()); |
| 1108 | +} |
| 1109 | + |
| 1110 | +static SILValue foldFPTrunc(BuiltinInst *BI, const BuiltinInfo &Builtin, |
| 1111 | + Optional<bool> &ResultsInError) { |
| 1112 | + |
| 1113 | + assert(Builtin.ID == BuiltinValueKind::FPTrunc); |
| 1114 | + |
| 1115 | + auto *flitInst = dyn_cast<FloatLiteralInst>(BI->getArguments()[0]); |
| 1116 | + if (!flitInst) |
| 1117 | + return nullptr; // We can fold only compile-time constant arguments. |
| 1118 | + |
| 1119 | + SILLocation Loc = BI->getLoc(); |
| 1120 | + auto *srcType = Builtin.Types[0]->castTo<BuiltinFloatType>(); |
| 1121 | + auto *destType = Builtin.Types[1]->castTo<BuiltinFloatType>(); |
| 1122 | + bool losesInfo; |
| 1123 | + APFloat truncVal = flitInst->getValue(); |
| 1124 | + APFloat::opStatus opStatus = |
| 1125 | + truncVal.convert(destType->getAPFloatSemantics(), |
| 1126 | + APFloat::rmNearestTiesToEven, &losesInfo); |
| 1127 | + |
| 1128 | + // Emit a warning if one of the following conditions hold: (a) the source |
| 1129 | + // value overflows the destination type, or (b) the source value is tiny and |
| 1130 | + // the tininess results in additional loss of precision when converted to the |
| 1131 | + // destination type beyond what would result in the normal scenario, or |
| 1132 | + // (c) the source value is a hex-float literal that cannot be precisely |
| 1133 | + // represented in the destination type. |
| 1134 | + // Suppress all warnings if the conversion is made through an explicit |
| 1135 | + // constructor invocation. |
| 1136 | + if (ResultsInError.hasValue() && !maybeExplicitFPCons(BI, Builtin)) { |
| 1137 | + bool overflow = opStatus & APFloat::opStatus::opOverflow; |
| 1138 | + bool tinynInexact = |
| 1139 | + isLossyUnderflow(flitInst->getValue(), srcType, destType); |
| 1140 | + bool hexnInexact = |
| 1141 | + (opStatus != APFloat::opStatus::opOK) && isHexLiteralInSource(flitInst); |
| 1142 | + |
| 1143 | + if (overflow || tinynInexact || hexnInexact) { |
| 1144 | + SILModule &M = BI->getModule(); |
| 1145 | + const ApplyExpr *CE = Loc.getAsASTNode<ApplyExpr>(); |
| 1146 | + |
| 1147 | + SmallString<10> fplitStr; |
| 1148 | + tryExtractLiteralText(flitInst, fplitStr); |
| 1149 | + |
| 1150 | + auto userType = CE ? CE->getType() : destType; |
| 1151 | + auto diagId = overflow |
| 1152 | + ? diag::warning_float_trunc_overflow |
| 1153 | + : (hexnInexact ? diag::warning_float_trunc_hex_inexact |
| 1154 | + : diag::warning_float_trunc_underflow); |
| 1155 | + diagnose(M.getASTContext(), Loc.getSourceLoc(), diagId, fplitStr, |
| 1156 | + userType, truncVal.isNegative()); |
| 1157 | + |
| 1158 | + ResultsInError = Optional<bool>(true); |
| 1159 | + } |
| 1160 | + } |
| 1161 | + // Abort folding if we have subnormality, NaN or opInvalid status. |
| 1162 | + if ((opStatus & APFloat::opStatus::opInvalidOp) || |
| 1163 | + (opStatus & APFloat::opStatus::opDivByZero) || |
| 1164 | + (opStatus & APFloat::opStatus::opUnderflow) || truncVal.isDenormal()) { |
| 1165 | + return nullptr; |
| 1166 | + } |
| 1167 | + // Allow folding if there is no loss, overflow or normal imprecision |
| 1168 | + // (i.e., opOverflow, opOk, or opInexact). |
| 1169 | + SILBuilderWithScope B(BI); |
| 1170 | + return B.createFloatLiteral(Loc, BI->getType(), truncVal); |
| 1171 | +} |
| 1172 | + |
973 | 1173 | static SILValue constantFoldBuiltin(BuiltinInst *BI,
|
974 | 1174 | Optional<bool> &ResultsInError) {
|
975 | 1175 | const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo();
|
@@ -1044,10 +1244,9 @@ case BuiltinValueKind::id:
|
1044 | 1244 | if (!V)
|
1045 | 1245 | return nullptr;
|
1046 | 1246 | APInt SrcVal = V->getValue();
|
1047 |
| - Type DestTy = Builtin.Types[1]; |
| 1247 | + auto *DestTy = Builtin.Types[1]->castTo<BuiltinFloatType>(); |
1048 | 1248 |
|
1049 |
| - APFloat TruncVal( |
1050 |
| - DestTy->castTo<BuiltinFloatType>()->getAPFloatSemantics()); |
| 1249 | + APFloat TruncVal(DestTy->getAPFloatSemantics()); |
1051 | 1250 | APFloat::opStatus ConversionStatus = TruncVal.convertFromAPInt(
|
1052 | 1251 | SrcVal, /*IsSigned=*/true, APFloat::rmNearestTiesToEven);
|
1053 | 1252 |
|
@@ -1077,27 +1276,7 @@ case BuiltinValueKind::id:
|
1077 | 1276 | }
|
1078 | 1277 |
|
1079 | 1278 | case BuiltinValueKind::FPTrunc: {
|
1080 |
| - // Get the value. It should be a constant in most cases. |
1081 |
| - auto *V = dyn_cast<FloatLiteralInst>(Args[0]); |
1082 |
| - if (!V) |
1083 |
| - return nullptr; |
1084 |
| - APFloat TruncVal = V->getValue(); |
1085 |
| - Type DestTy = Builtin.Types[1]; |
1086 |
| - bool losesInfo; |
1087 |
| - APFloat::opStatus ConversionStatus = TruncVal.convert( |
1088 |
| - DestTy->castTo<BuiltinFloatType>()->getAPFloatSemantics(), |
1089 |
| - APFloat::rmNearestTiesToEven, &losesInfo); |
1090 |
| - SILLocation Loc = BI->getLoc(); |
1091 |
| - |
1092 |
| - // Check if conversion was successful. |
1093 |
| - if (ConversionStatus != APFloat::opStatus::opOK && |
1094 |
| - ConversionStatus != APFloat::opStatus::opInexact) { |
1095 |
| - return nullptr; |
1096 |
| - } |
1097 |
| - |
1098 |
| - // The call to the builtin should be replaced with the constant value. |
1099 |
| - SILBuilderWithScope B(BI); |
1100 |
| - return B.createFloatLiteral(Loc, BI->getType(), TruncVal); |
| 1279 | + return foldFPTrunc(BI, Builtin, ResultsInError); |
1101 | 1280 | }
|
1102 | 1281 |
|
1103 | 1282 | // Conversions from floating point to integer,
|
@@ -1216,6 +1395,21 @@ bool ConstantFolder::constantFoldStringConcatenation(ApplyInst *AI) {
|
1216 | 1395 | void ConstantFolder::initializeWorklist(SILFunction &F) {
|
1217 | 1396 | for (auto &BB : F) {
|
1218 | 1397 | for (auto &I : BB) {
|
| 1398 | + // If `I` is a floating-point literal instruction where the literal is |
| 1399 | + // inf, it means the input has a literal that overflows even |
| 1400 | + // MaxBuiltinFloatType. Diagnose this error, but allow this instruction |
| 1401 | + // to be folded, if needed. |
| 1402 | + if (auto floatLit = dyn_cast<FloatLiteralInst>(&I)) { |
| 1403 | + APFloat fpVal = floatLit->getValue(); |
| 1404 | + if (EnableDiagnostics && fpVal.isInfinity()) { |
| 1405 | + SmallString<10> litStr; |
| 1406 | + tryExtractLiteralText(floatLit, litStr); |
| 1407 | + diagnose(I.getModule().getASTContext(), I.getLoc().getSourceLoc(), |
| 1408 | + diag::warning_float_overflows_maxbuiltin, litStr, |
| 1409 | + fpVal.isNegative()); |
| 1410 | + } |
| 1411 | + } |
| 1412 | + |
1219 | 1413 | if (isFoldable(&I) && I.hasUsesOfAnyResult()) {
|
1220 | 1414 | WorkList.insert(&I);
|
1221 | 1415 | continue;
|
|
0 commit comments