Skip to content

Commit 566d574

Browse files
Merge pull request swiftlang#29501 from ravikandhadai/constexpr-binary-integer-description
[Constant Evaluator] Add support for BinaryInteger.description which converts an integer to a string
2 parents f83fead + c198c1a commit 566d574

File tree

5 files changed

+107
-0
lines changed

5 files changed

+107
-0
lines changed

include/swift/AST/SemanticAttrs.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ SEMANTICS_ATTR(STRING_PLUS_EQUALS, "string.plusequals")
3434
SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE, "findStringSwitchCase")
3535
SEMANTICS_ATTR(FIND_STRING_SWITCH_CASE_WITH_CACHE, "findStringSwitchCaseWithCache")
3636

37+
SEMANTICS_ATTR(BINARY_INTEGER_DESCRIPTION, "binaryInteger.description")
38+
3739
SEMANTICS_ATTR(SWIFT_CONCURRENT_ASYNC, "swift.concurrent.async")
3840
SEMANTICS_ATTR(SWIFT_CONCURRENT_SAFE, "swift.concurrent.safe")
3941
SEMANTICS_ATTR(SWIFT_CONCURRENT, "swift.concurrent")

lib/SILOptimizer/Utils/ConstExpr.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ enum class WellKnownFunction {
5656
StringEquals,
5757
// String.percentEscapedString.getter
5858
StringEscapePercent,
59+
// BinaryInteger.description.getter
60+
BinaryIntegerDescription,
5961
// _assertionFailure(_: StaticString, _: StaticString, file: StaticString,...)
6062
AssertionFailure,
6163
// A function taking one argument that prints the symbolic value of the
@@ -83,6 +85,8 @@ static llvm::Optional<WellKnownFunction> classifyFunction(SILFunction *fn) {
8385
return WellKnownFunction::StringEquals;
8486
if (fn->hasSemanticsAttr(semantics::STRING_ESCAPE_PERCENT_GET))
8587
return WellKnownFunction::StringEscapePercent;
88+
if (fn->hasSemanticsAttr(semantics::BINARY_INTEGER_DESCRIPTION))
89+
return WellKnownFunction::BinaryIntegerDescription;
8690
if (fn->hasSemanticsAttrThatStartsWith("programtermination_point"))
8791
return WellKnownFunction::AssertionFailure;
8892
// A call to a function with the following semantics annotation will be
@@ -789,6 +793,28 @@ static Type getArrayElementType(Type ty) {
789793
return Type();
790794
}
791795

796+
/// Check if the given type \p ty is a stdlib integer type and if so return
797+
/// whether the type is signed. Returns \c None if \p ty is not a stdlib integer
798+
/// type, \c true if it is a signed integer type and \c false if it is an
799+
/// unsigned integer type.
800+
static Optional<bool> getSignIfStdlibIntegerType(Type ty) {
801+
StructDecl *decl = ty->getStructOrBoundGenericStruct();
802+
if (!decl)
803+
return None;
804+
ASTContext &astCtx = ty->getASTContext();
805+
if (decl == astCtx.getIntDecl() || decl == astCtx.getInt8Decl() ||
806+
decl == astCtx.getInt16Decl() || decl == astCtx.getInt32Decl() ||
807+
decl == astCtx.getInt64Decl()) {
808+
return true;
809+
}
810+
if (decl == astCtx.getUIntDecl() || decl == astCtx.getUInt8Decl() ||
811+
decl == astCtx.getUInt16Decl() || decl == astCtx.getUInt32Decl() ||
812+
decl == astCtx.getUInt64Decl()) {
813+
return false;
814+
}
815+
return None;
816+
}
817+
792818
/// Given a call to a well known function, collect its arguments as constants,
793819
/// fold it, and return None. If any of the arguments are not constants, marks
794820
/// the call's results as Unknown, and return an Unknown with information about
@@ -1064,6 +1090,42 @@ ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply,
10641090
setValue(apply, resultVal);
10651091
return None;
10661092
}
1093+
case WellKnownFunction::BinaryIntegerDescription: {
1094+
// BinaryInteger.description.getter
1095+
assert(conventions.getNumDirectSILResults() == 1 &&
1096+
conventions.getNumIndirectSILResults() == 0 &&
1097+
conventions.getNumParameters() == 1 && apply->hasSubstitutions() &&
1098+
"unexpected BinaryInteger.description.getter signature");
1099+
// Get the type of the argument and check if it is a signed or
1100+
// unsigned integer.
1101+
SILValue integerArgument = apply->getOperand(1);
1102+
CanType argumentType = substituteGenericParamsAndSimpify(
1103+
integerArgument->getType().getASTType());
1104+
Optional<bool> isSignedIntegerType =
1105+
getSignIfStdlibIntegerType(argumentType);
1106+
if (!isSignedIntegerType.hasValue()) {
1107+
return getUnknown(evaluator, (SILInstruction *)apply,
1108+
UnknownReason::InvalidOperandValue);
1109+
}
1110+
// Load the stdlib integer's value and convert it to a string.
1111+
SymbolicValue stdlibIntegerValue =
1112+
getConstAddrAndLoadResult(integerArgument);
1113+
if (!stdlibIntegerValue.isConstant()) {
1114+
return stdlibIntegerValue;
1115+
}
1116+
SymbolicValue builtinIntegerValue =
1117+
stdlibIntegerValue.lookThroughSingleElementAggregates();
1118+
assert(builtinIntegerValue.getKind() == SymbolicValue::Integer &&
1119+
"stdlib integer type must store only a builtin integer");
1120+
APInt integer = builtinIntegerValue.getIntegerValue();
1121+
SmallString<8> integerString;
1122+
isSignedIntegerType.getValue() ? integer.toStringSigned(integerString)
1123+
: integer.toStringUnsigned(integerString);
1124+
SymbolicValue resultVal =
1125+
SymbolicValue::getString(integerString.str(), evaluator.getAllocator());
1126+
setValue(apply, resultVal);
1127+
return None;
1128+
}
10671129
case WellKnownFunction::DebugPrint: {
10681130
assert(apply->getNumArguments() == 1 &&
10691131
"debug_print function must take exactly one argument");

stdlib/public/core/Integers.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ extension BinaryInteger {
15571557
}
15581558

15591559
/// A textual representation of this value.
1560+
@_semantics("binaryInteger.description")
15601561
public var description: String {
15611562
return _description(radix: 10, uppercase: false)
15621563
}

test/SILOptimizer/constant_evaluable_subset_test.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,3 +911,17 @@ func interpretMetaTypeCast() -> Bool {
911911
func interpretMetaTypeCast2() -> Bool {
912912
return testMetaTypeCast(((Int) -> Int).self)
913913
}
914+
915+
// CHECK-LABEL: @testBinaryIntegerDescription
916+
// CHECK-NOT: error:
917+
@_semantics("constant_evaluable")
918+
func testBinaryIntegerDescription<T: BinaryInteger>(_ x: T) -> String {
919+
return x.description
920+
}
921+
922+
@_semantics("test_driver")
923+
func interpretBinaryIntegerDescription() -> String {
924+
var str = testBinaryIntegerDescription(-10)
925+
str += testBinaryIntegerDescription(UInt(20))
926+
return str
927+
}

test/SILOptimizer/constant_evaluator_test.sil

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,3 +1503,31 @@ bb0:
15031503
%3 = apply %2<Int64>(%1) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Builtin.Int1
15041504
return %3 : $Builtin.Int1
15051505
} // CHECK: Returns int: -1
1506+
1507+
// CHECK-LABEL: @interpretBinaryIntegerDescription
1508+
sil [ossa] @interpretBinaryIntegerDescription : $@convention(thin) () -> @owned String {
1509+
bb0:
1510+
%0 = integer_literal $Builtin.Int64, -10
1511+
%1 = struct $Int64 (%0 : $Builtin.Int64)
1512+
%2 = alloc_stack $Int64
1513+
store %1 to [trivial] %2 : $*Int64
1514+
%4 = function_ref @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String
1515+
%5 = apply %4<Int64>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String
1516+
dealloc_stack %2 : $*Int64
1517+
return %5 : $String
1518+
} // CHECK: Returns string: "-10"
1519+
1520+
sil [_semantics "binaryInteger.description"] @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String
1521+
1522+
// CHECK-LABEL: @interpretUnsignedBinaryIntegerDescription
1523+
sil [ossa] @interpretUnsignedBinaryIntegerDescription : $@convention(thin) () -> @owned String {
1524+
bb0:
1525+
%0 = integer_literal $Builtin.Int64, 0xffffffffffffffff
1526+
%1 = struct $UInt64 (%0 : $Builtin.Int64)
1527+
%2 = alloc_stack $UInt64
1528+
store %1 to [trivial] %2 : $*UInt64
1529+
%4 = function_ref @binaryIntegerDescription : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String
1530+
%5 = apply %4<UInt64>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : BinaryInteger> (@in_guaranteed τ_0_0) -> @owned String
1531+
dealloc_stack %2 : $*UInt64
1532+
return %5 : $String
1533+
} // CHECK: Returns string: "18446744073709551615"

0 commit comments

Comments
 (0)