Skip to content

Revert "Remove apparently obsolete builtin functions (#20947)" #20975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,14 @@ BUILTIN_MISC_OPERATION(SToSCheckedTrunc, "s_to_s_checked_trunc", "n", Special)
BUILTIN_MISC_OPERATION(SToUCheckedTrunc, "s_to_u_checked_trunc", "n", Special)
BUILTIN_MISC_OPERATION(UToUCheckedTrunc, "u_to_u_checked_trunc", "n", Special)

/// Checked conversions for signed <-> unsigned integers of the same size.
/// Returns a tuple containing the conversion result as well as
/// the sign error / overflow bit.
BUILTIN_MISC_OPERATION(SUCheckedConversion,
"s_to_u_checked_conversion", "n", Special)
BUILTIN_MISC_OPERATION(USCheckedConversion,
"u_to_s_checked_conversion", "n", Special)

/// IntToFPWithOverflow has type (Integer) -> Float
BUILTIN_MISC_OPERATION(IntToFPWithOverflow, "itofp_with_overflow", "n", Special)

Expand Down
7 changes: 7 additions & 0 deletions include/swift/SIL/PatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,13 @@ using BuiltinApplyTy = typename Apply_match<BuiltinValueKind, Tys...>::Ty;
// if any of the sub-matchers succeed.
//

/// Matcher for any of the builtin checked conversions.
template <typename T0>
inline typename OneOf_match<BuiltinApplyTy<T0>, BuiltinApplyTy<T0>>::Ty
m_CheckedConversion(const T0 &Op0) {
return m_USCheckedConversion(Op0) || m_SUCheckedConversion(Op0);
}

/// Matcher for any of the builtin ExtOrBitCast instructions.
template <typename T0>
inline typename OneOf_match<BuiltinApplyTy<T0>, BuiltinApplyTy<T0>>::Ty
Expand Down
18 changes: 18 additions & 0 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,19 @@ static ValueDecl *getCheckedTruncOperation(ASTContext &Context, Identifier Id,
return getBuiltinFunction(Id, { InTy }, ResultTy);
}

static ValueDecl *getCheckedConversionOperation(ASTContext &Context,
Identifier Id,
Type Ty) {
Type BuiltinTy = Ty->getAs<BuiltinIntegerType>();
if (!BuiltinTy)
return nullptr;

Type SignErrorBitTy = BuiltinIntegerType::get(1, Context);
TupleTypeElt ResultElts[] = { BuiltinTy, SignErrorBitTy };
Type ResultTy = TupleType::get(ResultElts, Context);
return getBuiltinFunction(Id, { BuiltinTy }, ResultTy);
}

static ValueDecl *getIntToFPWithOverflowOperation(ASTContext &Context,
Identifier Id, Type InputTy,
Type OutputTy) {
Expand Down Expand Up @@ -1840,6 +1853,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
if (Types.size() != 2) return nullptr;
return getCheckedTruncOperation(Context, Id, Types[0], Types[1], false);

case BuiltinValueKind::SUCheckedConversion:
case BuiltinValueKind::USCheckedConversion:
if (Types.size() != 1) return nullptr;
return getCheckedConversionOperation(Context, Id, Types[0]);

case BuiltinValueKind::ClassifyBridgeObject:
if (!Types.empty()) return nullptr;
return getClassifyBridgeObject(Context, Id);
Expand Down
16 changes: 16 additions & 0 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,22 @@ if (Builtin.ID == BuiltinValueKind::id) { \
return out.add(OverflowFlag);
}

if (Builtin.ID == BuiltinValueKind::SUCheckedConversion ||
Builtin.ID == BuiltinValueKind::USCheckedConversion) {
auto Ty =
IGF.IGM.getStorageTypeForLowered(Builtin.Types[0]->getCanonicalType());

// Report a sign error if the input parameter is a negative number, when
// interpreted as signed.
llvm::Value *Arg = args.claimNext();
llvm::Value *Zero = llvm::ConstantInt::get(Ty, 0);
llvm::Value *OverflowFlag = IGF.Builder.CreateICmpSLT(Arg, Zero);

// Return the tuple: (the result (same as input), the overflow flag).
out.add(Arg);
return out.add(OverflowFlag);
}

// We are currently emitting code for '_convertFromBuiltinIntegerLiteral',
// which will call the builtin and pass it a non-compile-time-const parameter.
if (Builtin.ID == BuiltinValueKind::IntToFPWithOverflow) {
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SRem)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SSubOver)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SToSCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SToUCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SUCheckedConversion)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Shl)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Sizeof)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, StaticReport)
Expand All @@ -1139,6 +1140,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UDiv)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UIToFP)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UMulOver)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, URem)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, USCheckedConversion)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, USubOver)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UToSCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UToUCheckedTrunc)
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,8 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, UToSCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, SToSCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, SToUCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, UToUCheckedTrunc)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, SUCheckedConversion)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, USCheckedConversion)
CONSTANT_OWNERSHIP_BUILTIN(Trivial, IntToFPWithOverflow)

// This is surprising, Builtin.unreachable returns a "Never" value which is
Expand Down
29 changes: 29 additions & 0 deletions lib/SILOptimizer/Analysis/SimplifyInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,18 @@ static SILValue simplifyBuiltin(BuiltinInst *BI) {
return Result;
}

// trunc(tuple_extract(conversion(extOrBitCast(x))))) -> x
if (match(Op, m_TupleExtractInst(
m_CheckedConversion(
m_ExtOrBitCast(m_SILValue(Result))), 0))) {
// If the top bit of Result is known to be 0, then
// it is safe to replace the whole pattern by original bits of x
if (Result->getType() == BI->getType()) {
if (auto signBit = computeSignBit(Result))
if (!signBit.getValue())
return Result;
}
}
return SILValue();
}

Expand Down Expand Up @@ -627,6 +639,23 @@ SILValue InstSimplifier::simplifyOverflowBuiltin(BuiltinInst *BI) {
switch (Builtin.ID) {
default: break;

case BuiltinValueKind::SUCheckedConversion:
case BuiltinValueKind::USCheckedConversion: {
OperandValueArrayRef Args = BI->getArguments();
const SILValue &Op = Args[0];
if (auto signBit = computeSignBit(Op))
if (!signBit.getValue())
return Op;
SILValue Result;
// CheckedConversion(ExtOrBitCast(x)) -> x
if (match(BI, m_CheckedConversion(m_ExtOrBitCast(m_SILValue(Result)))))
if (Result->getType() == BI->getType().getTupleElementType(0)) {
assert (!computeSignBit(Result).getValue() && "Sign bit should be 0");
return Result;
}
}
break;

case BuiltinValueKind::UToSCheckedTrunc:
case BuiltinValueKind::UToUCheckedTrunc:
case BuiltinValueKind::SToUCheckedTrunc:
Expand Down
19 changes: 19 additions & 0 deletions lib/SILOptimizer/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,25 @@ Optional<bool> swift::computeSignBit(SILValue V) {
continue;
}

// Source and target type sizes are the same.
// S->U conversion can only succeed if
// the sign bit of its operand is 0, i.e. it is >= 0.
// The sign bit of a result is 0 only if the sign
// bit of a source operand is 0.
case BuiltinValueKind::SUCheckedConversion:
Value = BI->getArguments()[0];
continue;

// Source and target type sizes are the same.
// U->S conversion can only succeed if
// the top bit of its operand is 0, i.e.
// it is representable as a signed integer >=0.
// The sign bit of a result is 0 only if the sign
// bit of a source operand is 0.
case BuiltinValueKind::USCheckedConversion:
Value = BI->getArguments()[0];
continue;

// Sign bit of the operand is promoted.
case BuiltinValueKind::SExt:
Value = BI->getArguments()[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ static bool isBarrier(SILInstruction *inst) {
case BuiltinValueKind::SToUCheckedTrunc:
case BuiltinValueKind::SToSCheckedTrunc:
case BuiltinValueKind::UToUCheckedTrunc:
case BuiltinValueKind::SUCheckedConversion:
case BuiltinValueKind::USCheckedConversion:
case BuiltinValueKind::IntToFPWithOverflow:
case BuiltinValueKind::ZeroInitializer:
case BuiltinValueKind::Once:
Expand Down
2 changes: 2 additions & 0 deletions lib/SILOptimizer/Utils/ConstExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ ConstExprFunctionState::computeConstantValueBuiltin(BuiltinInst *inst) {
if (!operand.isConstant())
return operand;

// TODO: SUCheckedConversion/USCheckedConversion

// Implement support for s_to_s_checked_trunc_Int2048_Int64 and other
// checking integer truncates. These produce a tuple of the result value
// and an overflow bit.
Expand Down
101 changes: 62 additions & 39 deletions lib/SILOptimizer/Utils/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,9 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI,
assert(Builtin.ID == BuiltinValueKind::SToSCheckedTrunc ||
Builtin.ID == BuiltinValueKind::UToUCheckedTrunc ||
Builtin.ID == BuiltinValueKind::SToUCheckedTrunc ||
Builtin.ID == BuiltinValueKind::UToSCheckedTrunc);
Builtin.ID == BuiltinValueKind::UToSCheckedTrunc ||
Builtin.ID == BuiltinValueKind::SUCheckedConversion ||
Builtin.ID == BuiltinValueKind::USCheckedConversion);

// Check if we are converting a constant integer.
OperandValueArrayRef Args = BI->getArguments();
Expand All @@ -686,9 +688,11 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI,
auto SrcBitWidth = SrcVal.getBitWidth();

bool DstTySigned = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc ||
Builtin.ID == BuiltinValueKind::UToSCheckedTrunc);
Builtin.ID == BuiltinValueKind::UToSCheckedTrunc ||
Builtin.ID == BuiltinValueKind::USCheckedConversion);
bool SrcTySigned = (Builtin.ID == BuiltinValueKind::SToSCheckedTrunc ||
Builtin.ID == BuiltinValueKind::SToUCheckedTrunc);
Builtin.ID == BuiltinValueKind::SToUCheckedTrunc ||
Builtin.ID == BuiltinValueKind::SUCheckedConversion);

// Get source type and bit width.
auto SrcTy = Builtin.Types[0]->castTo<AnyBuiltinIntegerType>();
Expand All @@ -701,33 +705,44 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI,
bool OverflowError;
Type DstTy;

assert(Builtin.Types.size() == 2);
DstTy = Builtin.Types[1];
uint32_t DstBitWidth =
DstTy->castTo<BuiltinIntegerType>()->getGreatestWidth();

assert((DstBitWidth < SrcBitWidth || !SrcTy->getWidth().isFixedWidth()) &&
"preconditions on builtin trunc operations should prevent"
"fixed-width truncations that actually extend");

// The only way a true extension can overflow is if the value is
// negative and the result is unsigned.
if (DstBitWidth > SrcBitWidth) {
OverflowError = (SrcTySigned && !DstTySigned && SrcVal.isNegative());
Result = (SrcTySigned ? SrcVal.sext(DstBitWidth)
: SrcVal.zext(DstBitWidth));

// A same-width change can overflow if the top bit disagrees.
} else if (DstBitWidth == SrcBitWidth) {
OverflowError = (SrcTySigned != DstTySigned && SrcVal.isNegative());
// Process conversions signed <-> unsigned for same size integers.
if (Builtin.ID == BuiltinValueKind::SUCheckedConversion ||
Builtin.ID == BuiltinValueKind::USCheckedConversion) {
DstTy = SrcTy;
Result = SrcVal;
// Report an error if the sign bit is set.
OverflowError = SrcVal.isNegative();

// A truncation can overflow if the value changes.
// Process the checked truncations.
} else {
Result = SrcVal.trunc(DstBitWidth);
APInt Ext = (DstTySigned ? Result.sext(SrcBitWidth)
: Result.zext(SrcBitWidth));
OverflowError = (SrcVal != Ext);
assert(Builtin.Types.size() == 2);
DstTy = Builtin.Types[1];
uint32_t DstBitWidth =
DstTy->castTo<BuiltinIntegerType>()->getGreatestWidth();

assert((DstBitWidth < SrcBitWidth || !SrcTy->getWidth().isFixedWidth()) &&
"preconditions on builtin trunc operations should prevent"
"fixed-width truncations that actually extend");

// The only way a true extension can overflow is if the value is
// negative and the result is unsigned.
if (DstBitWidth > SrcBitWidth) {
OverflowError = (SrcTySigned && !DstTySigned && SrcVal.isNegative());
Result = (SrcTySigned ? SrcVal.sext(DstBitWidth)
: SrcVal.zext(DstBitWidth));

// A same-width change can overflow if the top bit disagrees.
} else if (DstBitWidth == SrcBitWidth) {
OverflowError = (SrcTySigned != DstTySigned && SrcVal.isNegative());
Result = SrcVal;

// A truncation can overflow if the value changes.
} else {
Result = SrcVal.trunc(DstBitWidth);
APInt Ext = (DstTySigned ? Result.sext(SrcBitWidth)
: Result.zext(SrcBitWidth));
OverflowError = (SrcVal != Ext);
}
}

// Check for overflow.
Expand Down Expand Up @@ -804,19 +819,25 @@ constantFoldAndCheckIntegerConversions(BuiltinInst *BI,
DstTySigned, DstTy, SrcAsString);
}
} else {
// Try to print user-visible types if they are available.
if (!UserSrcTy.isNull()) {
if (Builtin.ID == BuiltinValueKind::SUCheckedConversion) {
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_conversion_overflow,
UserSrcTy, UserDstTy);

// Otherwise, print the Builtin Types.
diag::integer_conversion_sign_error,
UserDstTy.isNull() ? DstTy : UserDstTy);
} else {
// Since builtin types are sign-agnostic, print the signedness
// separately.
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_conversion_overflow_builtin_types,
SrcTySigned, SrcTy, DstTySigned, DstTy);
// Try to print user-visible types if they are available.
if (!UserSrcTy.isNull()) {
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_conversion_overflow,
UserSrcTy, UserDstTy);

// Otherwise, print the Builtin Types.
} else {
// Since builtin types are sign-agnostic, print the signedness
// separately.
diagnose(M.getASTContext(), Loc.getSourceLoc(),
diag::integer_conversion_overflow_builtin_types,
SrcTySigned, SrcTy, DstTySigned, DstTy);
}
}
}

Expand Down Expand Up @@ -1199,7 +1220,9 @@ case BuiltinValueKind::id:
case BuiltinValueKind::SToSCheckedTrunc:
case BuiltinValueKind::UToUCheckedTrunc:
case BuiltinValueKind::SToUCheckedTrunc:
case BuiltinValueKind::UToSCheckedTrunc: {
case BuiltinValueKind::UToSCheckedTrunc:
case BuiltinValueKind::SUCheckedConversion:
case BuiltinValueKind::USCheckedConversion: {
return constantFoldAndCheckIntegerConversions(BI, Builtin, ResultsInError);
}

Expand Down
Loading