Skip to content

Commit 49b0a23

Browse files
committed
Add getEnumTag and injectEnumTag builtins
1 parent d270dad commit 49b0a23

File tree

12 files changed

+169
-0
lines changed

12 files changed

+169
-0
lines changed

include/swift/AST/Builtins.def

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,21 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
10201020
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildMainActorExecutorRef,
10211021
"buildMainActorExecutorRef", "n", Special)
10221022

1023+
/// getEnumTag: <T>(_: T) -> Builtin.Int32
1024+
///
1025+
/// Given a dynamic generic value, unsafely assume it is an enum type and call
1026+
/// the getEnumTag vwt function.
1027+
BUILTIN_MISC_OPERATION_WITH_SILGEN(GetEnumTag, "getEnumTag", "", Special)
1028+
1029+
/// injectEnumTag: <T>(_: inout T, _: Builtin.Int32) -> ()
1030+
///
1031+
/// Given a dynamic inout generic value, unsafely assume it is an enum type and
1032+
/// inject the given tag into it.
1033+
///
1034+
/// Note: This assume that either 1. the given tag has no payload or 2. the
1035+
/// tag's payload is already initialized with the given source.
1036+
BUILTIN_MISC_OPERATION_WITH_SILGEN(InjectEnumTag, "injectEnumTag", "", Special)
1037+
10231038
#undef BUILTIN_MISC_OPERATION_WITH_SILGEN
10241039

10251040
#undef BUILTIN_MISC_OPERATION

include/swift/SIL/AddressWalker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
259259
case BuiltinValueKind::GenericXor:
260260
case BuiltinValueKind::TaskRunInline:
261261
case BuiltinValueKind::ZeroInitializer:
262+
case BuiltinValueKind::GetEnumTag:
263+
case BuiltinValueKind::InjectEnumTag:
262264
callVisitUse(op);
263265
continue;
264266
default:

lib/AST/Builtins.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,26 @@ static ValueDecl *getPackLength(ASTContext &ctx, Identifier id) {
19751975
return builder.build(id);
19761976
}
19771977

1978+
static ValueDecl *getGetEnumTag(ASTContext &ctx, Identifier id) {
1979+
BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);
1980+
1981+
auto paramTy = makeGenericParam();
1982+
builder.addParameter(paramTy);
1983+
builder.setResult(makeConcrete(BuiltinIntegerType::get(32, ctx)));
1984+
1985+
return builder.build(id);
1986+
}
1987+
1988+
static ValueDecl *getInjectEnumTag(ASTContext &ctx, Identifier id) {
1989+
BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);
1990+
1991+
builder.addParameter(makeGenericParam(), ParamSpecifier::InOut);
1992+
builder.addParameter(makeConcrete(BuiltinIntegerType::get(32, ctx)));
1993+
builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));
1994+
1995+
return builder.build(id);
1996+
}
1997+
19781998
/// An array of the overloaded builtin kinds.
19791999
static const OverloadedBuiltinKind OverloadedBuiltinKinds[] = {
19802000
OverloadedBuiltinKind::None,
@@ -3006,6 +3026,12 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
30063026

30073027
case BuiltinValueKind::PackLength:
30083028
return getPackLength(Context, Id);
3029+
3030+
case BuiltinValueKind::GetEnumTag:
3031+
return getGetEnumTag(Context, Id);
3032+
3033+
case BuiltinValueKind::InjectEnumTag:
3034+
return getInjectEnumTag(Context, Id);
30093035
}
30103036

30113037
llvm_unreachable("bad builtin value!");

lib/IRGen/GenBuiltin.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
#include "GenCast.h"
3232
#include "GenConcurrency.h"
3333
#include "GenDistributed.h"
34+
#include "GenEnum.h"
3435
#include "GenPointerAuth.h"
3536
#include "GenIntegerLiteral.h"
37+
#include "GenOpaque.h"
3638
#include "IRGenFunction.h"
3739
#include "IRGenModule.h"
3840
#include "LoadableTypeInfo.h"
@@ -1490,6 +1492,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
14901492
out.add(pointerSrc);
14911493
return;
14921494
}
1495+
14931496
if (Builtin.ID == BuiltinValueKind::AllocVector) {
14941497
(void)args.claimAll();
14951498
IGF.emitTrap("escaped vector allocation", /*EmitUnreachable=*/true);
@@ -1498,5 +1501,47 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
14981501
IGF.Builder.emitBlock(contBB);
14991502
return;
15001503
}
1504+
1505+
if (Builtin.ID == BuiltinValueKind::GetEnumTag) {
1506+
auto arg = args.claimNext();
1507+
auto ty = argTypes[0];
1508+
auto &ti = IGF.getTypeInfo(ty);
1509+
1510+
// If the type is just an archetype, then we know nothing about the enum
1511+
// strategy for it. Just call the vwt function. Otherwise, we know that this
1512+
// is at least an enum and can optimize away some of the cost of getEnumTag.
1513+
if (!ty.is<ArchetypeType>()) {
1514+
assert(ty.getEnumOrBoundGenericEnum() && "expected enum type in "
1515+
"getEnumTag builtin!");
1516+
1517+
auto &strategy = getEnumImplStrategy(IGF.IGM, ty);
1518+
1519+
out.add(strategy.emitGetEnumTag(IGF, ty, ti.getAddressForPointer(arg)));
1520+
return;
1521+
}
1522+
1523+
out.add(emitGetEnumTagCall(IGF, ty, ti.getAddressForPointer(arg)));
1524+
return;
1525+
}
1526+
1527+
if (Builtin.ID == BuiltinValueKind::InjectEnumTag) {
1528+
auto input = args.claimNext();
1529+
auto tag = args.claimNext();
1530+
auto inputTy = argTypes[0];
1531+
auto &inputTi = IGF.getTypeInfo(inputTy);
1532+
1533+
// In order for us to call 'storeTag' on an enum strategy (when type is not
1534+
// an archetype), we'd need to be able to map the tag back into an
1535+
// EnumElementDecl which might be fragile. We don't really care about being
1536+
// able to optimize this vwt function call anyway because we expect most
1537+
// use cases to be the truly dynamic case where the compiler has no static
1538+
// information about the type to be able to optimize it away. Just call the
1539+
// vwt function.
1540+
1541+
emitDestructiveInjectEnumTagCall(IGF, inputTy, tag,
1542+
inputTi.getAddressForPointer(input));
1543+
return;
1544+
}
1545+
15011546
llvm_unreachable("IRGen unimplemented for this builtin!");
15021547
}

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,8 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
876876
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
877877
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
878878
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TargetOSVersionAtLeast)
879+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetEnumTag)
880+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InjectEnumTag)
879881
OperandOwnership OperandOwnershipBuiltinClassifier::visitCopy(BuiltinInst *bi,
880882
StringRef) {
881883
if (bi->getFunction()->getConventions().useLoweredAddresses()) {

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,8 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
615615
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
616616
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
617617
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)
618+
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
619+
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
618620

619621
#undef CONSTANT_OWNERSHIP_BUILTIN
620622

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,6 +2645,15 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
26452645
}
26462646
return;
26472647

2648+
case BuiltinValueKind::GetEnumTag:
2649+
visitor(&builtin->getAllOperands()[0]);
2650+
return;
2651+
2652+
case BuiltinValueKind::InjectEnumTag:
2653+
visitor(&builtin->getAllOperands()[0]);
2654+
visitor(&builtin->getAllOperands()[1]);
2655+
return;
2656+
26482657
// Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer,
26492658
// Builtin.Word)
26502659
case BuiltinValueKind::CopyArray:

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,11 @@ struct ImmutableAddressUseVerifier {
559559
if (isPolymorphicBuiltin(*builtinKind)) {
560560
break;
561561
}
562+
563+
// Get enum tag borrows its operand address value.
564+
if (builtinKind == BuiltinValueKind::GetEnumTag) {
565+
return false;
566+
}
562567
}
563568

564569
// Otherwise this is a builtin that we are not expecting to see, so bail

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,34 @@ static ManagedValue emitBuiltinBuildMainActorExecutorRef(
18051805
BuiltinValueKind::BuildMainActorExecutorRef);
18061806
}
18071807

1808+
static ManagedValue emitBuiltinGetEnumTag(SILGenFunction &SGF, SILLocation loc,
1809+
SubstitutionMap subs,
1810+
ArrayRef<ManagedValue> args,
1811+
SGFContext C) {
1812+
auto &ctx = SGF.getASTContext();
1813+
1814+
auto bi = SGF.B.createBuiltin(
1815+
loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetEnumTag)),
1816+
SILType::getBuiltinIntegerType(32, ctx), subs,
1817+
{ args[0].getValue() });
1818+
1819+
return ManagedValue::forObjectRValueWithoutOwnership(bi);
1820+
}
1821+
1822+
static ManagedValue emitBuiltinInjectEnumTag(SILGenFunction &SGF, SILLocation loc,
1823+
SubstitutionMap subs,
1824+
ArrayRef<ManagedValue> args,
1825+
SGFContext C) {
1826+
auto &ctx = SGF.getASTContext();
1827+
1828+
auto bi = SGF.B.createBuiltin(
1829+
loc, ctx.getIdentifier(getBuiltinName(BuiltinValueKind::InjectEnumTag)),
1830+
SILType::getEmptyTupleType(ctx), subs,
1831+
{ args[0].getValue(), args[1].getValue() });
1832+
1833+
return ManagedValue::forObjectRValueWithoutOwnership(bi);
1834+
}
1835+
18081836
llvm::Optional<SpecializedEmitter>
18091837
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
18101838
// Only consider standalone declarations in the Builtin module.

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ static bool isBarrier(SILInstruction *inst) {
157157
case BuiltinValueKind::StackDealloc:
158158
case BuiltinValueKind::AllocVector:
159159
case BuiltinValueKind::AssumeAlignment:
160+
case BuiltinValueKind::GetEnumTag:
161+
case BuiltinValueKind::InjectEnumTag:
160162
return false;
161163

162164
// Handle some rare builtins that may be sensitive to object lifetime

test/IRGen/builtins.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,5 +827,19 @@ func convertTaskToJob(_ task: Builtin.NativeObject) -> Builtin.Job {
827827
return Builtin.convertTaskToJob(task)
828828
}
829829

830+
// CHECK-LABEL: define {{.*}} swiftcc i32 @"$s8builtins10getEnumTagys6UInt32VxlF"(ptr {{.*}} %0, ptr %T)
831+
// CHECK: %GetEnumTag = load ptr, ptr {{%.*}}
832+
// CHECK: [[TAG:%.*]] = call i32 %GetEnumTag(ptr {{.*}} %0, ptr %T)
833+
// CHECK: ret i32 [[TAG]]
834+
func getEnumTag<T>(_ x: T) -> UInt32 {
835+
UInt32(Builtin.getEnumTag(x))
836+
}
837+
838+
// CHECK-LABEL: define {{.*}} swiftcc void @"$s8builtins13injectEnumTag_3tagyxz_s6UInt32VtlF"(ptr %0, i32 %1, ptr %T)
839+
// CHECK: %DestructiveInjectEnumTag = load ptr, ptr {{%.*}}
840+
// CHECK: call void %DestructiveInjectEnumTag(ptr {{.*}} %0, i32 %1, ptr %T)
841+
func injectEnumTag<T>(_ x: inout T, tag: UInt32) {
842+
Builtin.injectEnumTag(&x, tag._value)
843+
}
830844

831845
// CHECK: ![[R]] = !{i64 0, i64 9223372036854775807}

test/SILGen/builtins.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,3 +882,22 @@ func assumeAlignment(_ p: Builtin.RawPointer, _ x: Builtin.Word) {
882882
func packCount<each T>(_ x: repeat each T) -> Builtin.Word {
883883
Builtin.packLength((repeat each T).self)
884884
}
885+
886+
// CHECK-LABEL: sil hidden [ossa] @$s8builtins10getEnumTagyBi32_xlF : $@convention(thin) <T> (@in_guaranteed T) -> Builtin.Int32 {
887+
// CHECK: bb0([[INPUT:%.*]] : $*T):
888+
// CHECK-NOT: copy_addr
889+
// CHECK: [[TAG:%.*]] = builtin "getEnumTag"<T>([[INPUT]] : $*T)
890+
// CHECK: return [[TAG]] : $Builtin.Int32
891+
func getEnumTag<T>(_ x: T) -> Builtin.Int32 {
892+
Builtin.getEnumTag(x)
893+
}
894+
895+
// CHECK-LABEL: sil hidden [ossa] @$s8builtins13injectEnumTag_3tagyxz_Bi32_tlF : $@convention(thin) <T> (@inout T, Builtin.Int32) -> () {
896+
// CHECK: bb0([[INPUT:%.*]] : $*T, [[TAG:%.*]] : $Builtin.Int32):
897+
// CHECK-NOT: copy_addr
898+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] [[INPUT]] : $*T
899+
// CHECK: builtin "injectEnumTag"<T>([[ACCESS]] : $*T, [[TAG]] : $Builtin.Int32)
900+
// CHECK: end_access [[ACCESS]]
901+
func injectEnumTag<T>(_ x: inout T, tag: Builtin.Int32) {
902+
Builtin.injectEnumTag(&x, tag)
903+
}

0 commit comments

Comments
 (0)