Skip to content

Add Builtin.isConcrete<T>(T.Type) -> Int1 #26466

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
merged 12 commits into from
Aug 29, 2019
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 @@ -418,6 +418,14 @@ BUILTIN_MISC_OPERATION(Strideof, "strideof", "n", Special)
/// IsPOD has type T.Type -> Bool
BUILTIN_MISC_OPERATION(IsPOD, "ispod", "n", Special)

/// IsConcrete has type (T.Type) -> Bool
///
/// If the meta type T is concrete, we can always transform this to `true` at
/// any time in SIL. If it's generic, then we lower it to `false` right before
/// IRGen in IRGenPrepare. This allows for the optimizer to specialize this at
/// -O and eliminate conditional code.
BUILTIN_MISC_OPERATION(IsConcrete, "isConcrete", "n", Special)

/// IsBitwiseTakable has type T.Type -> Bool
BUILTIN_MISC_OPERATION(IsBitwiseTakable, "isbitwisetakable", "n", Special)

Expand Down
10 changes: 10 additions & 0 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,13 @@ static ValueDecl *getIsPODOperation(ASTContext &Context, Identifier Id) {
return builder.build(Id);
}

static ValueDecl *getIsConcrete(ASTContext &Context, Identifier Id) {
BuiltinGenericSignatureBuilder builder(Context);
builder.addParameter(makeMetatype(makeGenericParam()));
builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context)));
return builder.build(Id);
}

static ValueDecl *getIsBitwiseTakable(ASTContext &Context, Identifier Id) {
BuiltinGenericSignatureBuilder builder(Context);
builder.addParameter(makeMetatype(makeGenericParam()));
Expand Down Expand Up @@ -1857,6 +1864,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
case BuiltinValueKind::IsPOD:
return getIsPODOperation(Context, Id);

case BuiltinValueKind::IsConcrete:
return getIsConcrete(Context, Id);

case BuiltinValueKind::IsBitwiseTakable:
return getIsBitwiseTakable(Context, Id);

Expand Down
7 changes: 7 additions & 0 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
return;
}

if (Builtin.ID == BuiltinValueKind::IsConcrete) {
(void)args.claimAll();
auto isConcrete = !substitutions.getReplacementTypes()[0]->hasArchetype();
out.add(llvm::ConstantInt::get(IGF.IGM.Int1Ty, isConcrete));
return;
}

if (Builtin.ID == BuiltinValueKind::IsBitwiseTakable) {
(void)args.claimAll();
auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM,
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,7 @@ ANY_OWNERSHIP_BUILTIN(IntToFPWithOverflow)
ANY_OWNERSHIP_BUILTIN(IntToPtr)
ANY_OWNERSHIP_BUILTIN(IsOptionalType)
ANY_OWNERSHIP_BUILTIN(IsPOD)
ANY_OWNERSHIP_BUILTIN(IsConcrete)
ANY_OWNERSHIP_BUILTIN(IsBitwiseTakable)
ANY_OWNERSHIP_BUILTIN(IsSameMetatype)
ANY_OWNERSHIP_BUILTIN(LShr)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Any, Sizeof)
CONSTANT_OWNERSHIP_BUILTIN(Any, Strideof)
CONSTANT_OWNERSHIP_BUILTIN(Any, StringObjectOr)
CONSTANT_OWNERSHIP_BUILTIN(Any, IsPOD)
CONSTANT_OWNERSHIP_BUILTIN(Any, IsConcrete)
CONSTANT_OWNERSHIP_BUILTIN(Any, IsBitwiseTakable)
CONSTANT_OWNERSHIP_BUILTIN(Any, IsSameMetatype)
CONSTANT_OWNERSHIP_BUILTIN(Any, Alignof)
Expand Down
3 changes: 3 additions & 0 deletions lib/SILOptimizer/SILCombiner/SILCombiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ class SILCombiner :
/// Instruction visitor helpers.
SILInstruction *optimizeBuiltinCanBeObjCClass(BuiltinInst *AI);

// Optimize the "isConcrete" builtin.
SILInstruction *optimizeBuiltinIsConcrete(BuiltinInst *I);

// Optimize the "trunc_N1_M2" builtin. if N1 is a result of "zext_M1_*" and
// the following holds true: N1 > M1 and M2>= M1
SILInstruction *optimizeBuiltinTruncOrBitCast(BuiltinInst *I);
Expand Down
9 changes: 9 additions & 0 deletions lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ SILInstruction *SILCombiner::optimizeBuiltinCanBeObjCClass(BuiltinInst *BI) {
llvm_unreachable("Unhandled TypeTraitResult in switch.");
}

SILInstruction *SILCombiner::optimizeBuiltinIsConcrete(BuiltinInst *BI) {
if (BI->getOperand(0)->getType().hasArchetype())
return nullptr;

return Builder.createIntegerLiteral(BI->getLoc(), BI->getType(), 1);
}

static unsigned getTypeWidth(SILType Ty) {
if (auto BuiltinIntTy = Ty.getAs<BuiltinIntegerType>()) {
if (BuiltinIntTy->isFixedWidth()) {
Expand Down Expand Up @@ -525,6 +532,8 @@ SILInstruction *SILCombiner::optimizeStringObject(BuiltinInst *BI) {
SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {
if (I->getBuiltinInfo().ID == BuiltinValueKind::CanBeObjCClass)
return optimizeBuiltinCanBeObjCClass(I);
if (I->getBuiltinInfo().ID == BuiltinValueKind::IsConcrete)
return optimizeBuiltinIsConcrete(I);
if (I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayFrontToBack ||
I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayBackToFront ||
I->getBuiltinInfo().ID == BuiltinValueKind::TakeArrayNoAlias ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static bool isBarrier(SILInstruction *inst) {
case BuiltinValueKind::Sizeof:
case BuiltinValueKind::Strideof:
case BuiltinValueKind::IsPOD:
case BuiltinValueKind::IsConcrete:
case BuiltinValueKind::IsBitwiseTakable:
case BuiltinValueKind::IsSameMetatype:
case BuiltinValueKind::Alignof:
Expand Down
26 changes: 25 additions & 1 deletion lib/SILOptimizer/Utils/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,18 @@ static SILValue foldFPTrunc(BuiltinInst *BI, const BuiltinInfo &Builtin,
return B.createFloatLiteral(Loc, BI->getType(), truncVal);
}

static SILValue constantFoldIsConcrete(BuiltinInst *BI) {
if (BI->getOperand(0)->getType().hasArchetype()) {
return SILValue();
}
SILBuilderWithScope builder(BI);
auto *inst = builder.createIntegerLiteral(
BI->getLoc(), SILType::getBuiltinIntegerType(1, builder.getASTContext()),
true);
BI->replaceAllUsesWith(inst);
return inst;
}

static SILValue constantFoldBuiltin(BuiltinInst *BI,
Optional<bool> &ResultsInError) {
const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo();
Expand Down Expand Up @@ -1563,7 +1575,8 @@ void ConstantFolder::initializeWorklist(SILFunction &f) {
continue;
}

if (isApplyOfBuiltin(*inst, BuiltinValueKind::GlobalStringTablePointer)) {
if (isApplyOfBuiltin(*inst, BuiltinValueKind::GlobalStringTablePointer) ||
isApplyOfBuiltin(*inst, BuiltinValueKind::IsConcrete)) {
WorkList.insert(inst);
continue;
}
Expand Down Expand Up @@ -1781,6 +1794,17 @@ ConstantFolder::processWorkList() {
continue;
}

if (isApplyOfBuiltin(*I, BuiltinValueKind::IsConcrete)) {
if (constantFoldIsConcrete(cast<BuiltinInst>(I))) {
// Here, the bulitin instruction got folded, so clean it up.
recursivelyDeleteTriviallyDeadInstructions(
I, /*force*/ true,
[&](SILInstruction *DeadI) { WorkList.remove(DeadI); });
InvalidateInstructions = true;
}
continue;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimizing this builtin works by:

  1. Inserting it in the worklist during initializeWorklist, which you've done
  2. Adding a call to constantFoldIsConcrete to contentFoldBuiltin, which you've done-- that will be called for everything in the worklist.

So, this extra optimization can just be removed. If I'm right, your tests should still pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I ran my tests against this bit being removed and it starts to fail. So apparently this is a necessary component. 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atrick gentle ping

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno, but it definitely doesn't make sense to do the same optimization in two places. To debug it take a minimal .sil test case and run sil-opt -diagnostic-constant-propagation t.sil. My reading of the code is that your change to initalizeWorklist would push the builtin apply instruction on the worklist, and constantFoldBuiltin should get a chance to run on each worklist item.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other call to constantFoldIsConcrete has no effect, so I removed it in 931ab46. I found out that this call happens before constantFoldInstruction gets called.

// Go through all users of the constant and try to fold them.
FoldedUsers.clear();
for (auto Result : I->getResults()) {
Expand Down
13 changes: 13 additions & 0 deletions stdlib/public/core/Builtin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,19 @@ func _isPOD<T>(_ type: T.Type) -> Bool {
return Bool(Builtin.ispod(type))
}

/// Returns `true` if `type` is known to refer to a concrete type once all
/// optimizations and constant folding has occurred at the call site. Otherwise,
/// this returns `false` if the check has failed.
///
/// Note that there may be cases in which, despite `T` being concrete at some
/// point in the caller chain, this function will return `false`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add that it will never return a false positive.

@_alwaysEmitIntoClient
@_transparent
public // @testable
func _isConcrete<T>(_ type: T.Type) -> Bool {
return Bool(Builtin.isConcrete(type))
}

/// Returns `true` if type is a bitwise takable. A bitwise takable type can
/// just be moved to a different address in memory.
@_transparent
Expand Down
28 changes: 28 additions & 0 deletions test/IRGen/builtin_isConcrete.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %target-swift-frontend -emit-ir -parse-sil %s -module-name Swift -parse-stdlib | %FileCheck %s

import Builtin

struct MyInt {
var value: Builtin.Int32
}

// CHECK-LABEL: define{{( protected| dllexport)?}} swiftcc i1 @isConcrete_true() {{.*}} {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i1 true
// CHECK-NEXT: }
sil @isConcrete_true : $@convention(thin) (@thin MyInt.Type) -> Builtin.Int1 {
bb0(%0 : $@thin MyInt.Type):
%1 = builtin "isConcrete"(%0 : $@thin MyInt.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}

// CHECK-LABEL: define{{( protected| dllexport)?}} swiftcc i1 @isConcrete_false(%swift.type* %T) {{.*}} {
// CHECK-NEXT: entry:
// CHECK: ret i1 false
// CHECK-NEXT: }
sil @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
bb0(%0 : $@thin T.Type):
// FIXME: Explicit specialization is required here when it shouldn't be
%1 = builtin "isConcrete"<T>(%0 : $@thin T.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}
22 changes: 22 additions & 0 deletions test/SILOptimizer/constant_propagation.sil
Original file line number Diff line number Diff line change
Expand Up @@ -1024,3 +1024,25 @@ bb0(%0 : $Builtin.Int64):
%2 = tuple_extract %1 : $(Builtin.Int64, Builtin.Int64), 0
return %2 : $Builtin.Int64
}

// CHECK-LABEL: sil [canonical] @isConcrete_true : $@convention(thin) (@thin UInt.Type) -> Builtin.Int1 {
// CHECK: bb0(
// CHECK-NEXT: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: return [[RESULT]]
// CHECK-NEXT: } // end sil function 'isConcrete_true'
sil [canonical] @isConcrete_true : $@convention(thin) (@thin UInt.Type) -> Builtin.Int1 {
bb0(%0 : $@thin UInt.Type):
%1 = builtin "isConcrete"(%0 : $@thin UInt.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}

// CHECK-LABEL: sil [canonical] @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
// CHECK: bb0(
// CHECK-NEXT: [[RESULT:%.*]] = builtin "isConcrete"(
// CHECK-NEXT: return [[RESULT]]
// CHECK-NEXT: } // end sil function 'isConcrete_false'
sil [canonical] @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
bb0(%0 : $@thin T.Type):
%1 = builtin "isConcrete"(%0 : $@thin T.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}
94 changes: 94 additions & 0 deletions test/SILOptimizer/constant_propagation_stdlib.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// RUN: %target-swift-frontend -module-name constant_propagation_stdlib %s -parse-stdlib -emit-sil -o - | %FileCheck --check-prefix=CHECK-ONONE %s
// RUN: %target-swift-frontend -module-name constant_propagation_stdlib %s -parse-stdlib -emit-sil -o - -O | %FileCheck --check-prefix=CHECK-O %s

public struct MyInt {
var v: Builtin.Int32
}

// CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 {
// CHECK-ONONE: bb0(
// CHECK-ONONE: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-ONONE: return [[RESULT]]
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 {
// CHECK-O-NEXT: bb0(
// CHECK-O-NEXT: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O-NEXT: return [[RESULT]]
// CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF'
public func isConcrete_true(_ x: MyInt) -> Builtin.Int1 {
return Builtin.isConcrete(MyInt.self)
}

// CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) <T> (@in_guaranteed T) -> Builtin.Int1 {
// CHECK-ONONE: bb0(
// CHECK-ONONE: [[METATYPE:%.*]] = metatype $@thick T.Type
// CHECK-ONONE: [[RESULT:%.*]] = builtin "isConcrete"<T>([[METATYPE]] : $@thick T.Type) : $Builtin.Int1
// CHECK-ONONE: return [[RESULT]]
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF'
// CHECK-O-LABEL: sil [signature_optimized_thunk] [always_inline] @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) <T> (@in_guaranteed T) -> Builtin.Int1 {
// CHECK-O: bb0(
// CHECK-O: [[GEN_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlFTf4d_n : $@convention(thin) <τ_0_0> () -> Builtin.Int1
// CHECK-O: [[RESULT:%.*]] = apply [[GEN_FUNC]]<T>() : $@convention(thin) <τ_0_0> () -> Builtin.Int1
// CHECK-O: return [[RESULT]]
// CHECK-O: } // end sil function '$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF'
public func isConcrete_false<T>(_ x: T) -> Builtin.Int1 {
return Builtin.isConcrete(T.self)
}

// CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib25isConcrete_generic_calleryBi1_xlF : $@convention(thin) <T> (@in_guaranteed T) -> Builtin.Int1 {
// CHECK-ONONE: bb0(
// CHECK-ONONE: [[GEN_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: [[RESULT:%.*]] = apply [[GEN_FUNC]]<T>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: return [[RESULT]]
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib25isConcrete_generic_calleryBi1_xlF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib25isConcrete_generic_calleryBi1_xlF : $@convention(thin) <T> (@in_guaranteed T) -> Builtin.Int1 {
// CHECK-O: bb0(
// CHECK-O: [[GEN_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlFTf4d_n : $@convention(thin) <τ_0_0> () -> Builtin.Int1
// CHECK-O: [[RESULT:%.*]] = apply [[GEN_FUNC]]<T>() : $@convention(thin) <τ_0_0> () -> Builtin.Int1
// CHECK-O: return [[RESULT]]
// CHECK-O: } // end sil function '$s27constant_propagation_stdlib25isConcrete_generic_calleryBi1_xlF'
public func isConcrete_generic_caller<T>(_ x: T) -> Builtin.Int1 {
return isConcrete_false(x)
}

// CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib26isConcrete_concrete_calleryBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 {
// CHECK-ONONE: bb0(
// CHECK-ONONE: [[STACK_ARG:%.*]] = alloc_stack $MyInt
// CHECK-ONONE: store %0 to [[STACK_ARG]] : $*MyInt
// CHECK-ONONE: [[GEN_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib25isConcrete_generic_calleryBi1_xlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: [[RESULT:%.*]] = apply [[GEN_FUNC]]<MyInt>([[STACK_ARG]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: dealloc_stack [[STACK_ARG]] : $*MyInt
// CHECK-ONONE: return [[RESULT]]
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib26isConcrete_concrete_calleryBi1_AA5MyIntVF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib26isConcrete_concrete_calleryBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 {
// CHECK-O: bb0(
// CHECK-O: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O: return [[RESULT]]
// CHECK-O: } // end sil function '$s27constant_propagation_stdlib26isConcrete_concrete_calleryBi1_AA5MyIntVF'
public func isConcrete_concrete_caller(_ x: MyInt) -> Builtin.Int1 {
return isConcrete_generic_caller(x)
}

// CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF : $@convention(thin) (MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) {
// CHECK-ONONE: bb0(
// CHECK-ONONE: [[IS_CONCRETE_TRUE_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1
// CHECK-ONONE: [[IS_CONCRETE_TRUE:%.*]] = apply [[IS_CONCRETE_TRUE_FUNC]](%0) : $@convention(thin) (MyInt) -> Builtin.Int1
// CHECK-ONONE: [[STACK_ARG:%.*]] = alloc_stack $MyInt
// CHECK-ONONE: store %0 to [[STACK_ARG]] : $*MyInt
// CHECK-ONONE: [[IS_CONCRETE_FALSE_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: [[IS_CONCRETE_FALSE:%.*]] = apply [[IS_CONCRETE_FALSE_FUNC]]<MyInt>([[STACK_ARG]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> Builtin.Int1
// CHECK-ONONE: dealloc_stack [[STACK_ARG]] : $*MyInt
// CHECK-ONONE: [[IS_CONCRETE_CONCRETE_CALLER_FUNC:%.*]] = function_ref @$s27constant_propagation_stdlib26isConcrete_concrete_calleryBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1
// CHECK-ONONE: [[IS_CONCRETE_CONCRETE_CALLER:%.*]] = apply [[IS_CONCRETE_CONCRETE_CALLER_FUNC]](%0) : $@convention(thin) (MyInt) -> Builtin.Int1
// CHECK-ONONE: [[RESULT:%.*]] = tuple ([[IS_CONCRETE_TRUE]] : $Builtin.Int1, [[IS_CONCRETE_FALSE]] : $Builtin.Int1, [[IS_CONCRETE_CONCRETE_CALLER]] : $Builtin.Int1)
// CHECK-ONONE: return [[RESULT]]
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF : $@convention(thin) (MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) {
// CHECK-O-NEXT: bb0(
// CHECK-O-NEXT: [[VALUE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O-NEXT: [[RESULT:%.*]] = tuple ([[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1)
// CHECK-O-NEXT: return [[RESULT]]
// CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF'
public func main(x: MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) {
return (isConcrete_true(x), isConcrete_false(x), isConcrete_concrete_caller(x))
}
27 changes: 27 additions & 0 deletions test/SILOptimizer/sil_combine.sil
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ protocol FakeProtocol {
func requirement()
}

struct MyInt {
var value: Builtin.Int32
}

sil [global_init] @global_init_fun : $@convention(thin) () -> Builtin.RawPointer

sil @user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
Expand Down Expand Up @@ -3723,3 +3727,26 @@ bb0(%2 : $TestObjCInit):
%19 = enum $Optional<TestObjCInit>, #Optional.some!enumelt.1, %14 : $TestObjCInit
return %19 : $Optional<TestObjCInit>
}

// CHECK-LABEL: sil @isConcrete_true : $@convention(thin) (@thin MyInt.Type) -> Builtin.Int1 {
// CHECK: bb0(%0 : $@thin MyInt.Type):
// CHECK: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK: return [[RESULT]]
// CHECK: } // end sil function 'isConcrete_true'
sil @isConcrete_true : $@convention(thin) (@thin MyInt.Type) -> Builtin.Int1 {
bb0(%0 : $@thin MyInt.Type):
%1 = builtin "isConcrete"(%0 : $@thin MyInt.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}

// CHECK-LABEL: sil @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
// CHECK: bb0(%0 : $@thin T.Type):
// CHECK: [[RESULT:%.*]] = builtin "isConcrete"<T>(%0 : $@thin T.Type) : $Builtin.Int1
// CHECK: return [[RESULT]]
// CHECK: } // end sil function 'isConcrete_false'
sil @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
bb0(%0 : $@thin T.Type):
// FIXME: Explicit specialization is required here when it shouldn't be
%1 = builtin "isConcrete"<T>(%0 : $@thin T.Type) : $Builtin.Int1
return %1 : $Builtin.Int1
}
14 changes: 14 additions & 0 deletions test/stdlib/Builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,18 @@ tests.test("_isOptional") {
expectFalse(_isOptional(P.self))
}

tests.test("_isConcrete") {
@_transparent
func isConcrete_true<T>(_ type: T.Type) -> Bool {
return _isConcrete(type)
}
@inline(never)
func isConcrete_false<T>(_ type: T.Type) -> Bool {
return _isConcrete(type)
}
expectTrue(_isConcrete(Int.self))
expectTrue(isConcrete_true(Int.self))
expectFalse(isConcrete_false(Int.self))
}

runAllTests()