Skip to content

Commit eb0924b

Browse files
committed
Add getEnumTag and injectEnumTag builtins
1 parent 956acc1 commit eb0924b

File tree

12 files changed

+167
-0
lines changed

12 files changed

+167
-0
lines changed

include/swift/AST/Builtins.def

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,21 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
10171017
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildMainActorExecutorRef,
10181018
"buildMainActorExecutorRef", "n", Special)
10191019

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

10221037
#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
@@ -1968,6 +1968,26 @@ static ValueDecl *getPackLength(ASTContext &ctx, Identifier id) {
19681968
return builder.build(id);
19691969
}
19701970

1971+
static ValueDecl *getGetEnumTag(ASTContext &ctx, Identifier id) {
1972+
BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);
1973+
1974+
auto paramTy = makeGenericParam();
1975+
builder.addParameter(paramTy);
1976+
builder.setResult(makeConcrete(BuiltinIntegerType::get(32, ctx)));
1977+
1978+
return builder.build(id);
1979+
}
1980+
1981+
static ValueDecl *getInjectEnumTag(ASTContext &ctx, Identifier id) {
1982+
BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);
1983+
1984+
builder.addParameter(makeGenericParam(), ParamSpecifier::InOut);
1985+
builder.addParameter(makeConcrete(BuiltinIntegerType::get(32, ctx)));
1986+
builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));
1987+
1988+
return builder.build(id);
1989+
}
1990+
19711991
/// An array of the overloaded builtin kinds.
19721992
static const OverloadedBuiltinKind OverloadedBuiltinKinds[] = {
19731993
OverloadedBuiltinKind::None,
@@ -2996,6 +3016,12 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
29963016

29973017
case BuiltinValueKind::PackLength:
29983018
return getPackLength(Context, Id);
3019+
3020+
case BuiltinValueKind::GetEnumTag:
3021+
return getGetEnumTag(Context, Id);
3022+
3023+
case BuiltinValueKind::InjectEnumTag:
3024+
return getInjectEnumTag(Context, Id);
29993025
}
30003026

30013027
llvm_unreachable("bad builtin value!");

lib/IRGen/GenBuiltin.cpp

Lines changed: 43 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"
@@ -1491,5 +1493,46 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
14911493
return;
14921494
}
14931495

1496+
if (Builtin.ID == BuiltinValueKind::GetEnumTag) {
1497+
auto arg = args.claimNext();
1498+
auto ty = argTypes[0];
1499+
auto &ti = IGF.getTypeInfo(ty);
1500+
1501+
// If the type is just an archetype, then we know nothing about the enum
1502+
// strategy for it. Just call the vwt function. Otherwise, we know that this
1503+
// is at least an enum and can optimize away some of the cost of getEnumTag.
1504+
if (!ty.is<ArchetypeType>()) {
1505+
assert(ty.getEnumOrBoundGenericEnum() && "expected enum type in "
1506+
"getEnumTag builtin!");
1507+
1508+
auto &strategy = getEnumImplStrategy(IGF.IGM, ty);
1509+
1510+
out.add(strategy.emitGetEnumTag(IGF, ty, ti.getAddressForPointer(arg)));
1511+
return;
1512+
}
1513+
1514+
out.add(emitGetEnumTagCall(IGF, ty, ti.getAddressForPointer(arg)));
1515+
return;
1516+
}
1517+
1518+
if (Builtin.ID == BuiltinValueKind::InjectEnumTag) {
1519+
auto input = args.claimNext();
1520+
auto tag = args.claimNext();
1521+
auto inputTy = argTypes[0];
1522+
auto &inputTi = IGF.getTypeInfo(inputTy);
1523+
1524+
// In order for us to call 'storeTag' on an enum strategy (when type is not
1525+
// an archetype), we'd need to be able to map the tag back into an
1526+
// EnumElementDecl which might be fragile. We don't really care about being
1527+
// able to optimize this vwt function call anyway because we expect most
1528+
// use cases to be the truly dynamic case where the compiler has no static
1529+
// information about the type to be able to optimize it away. Just call the
1530+
// vwt function.
1531+
1532+
emitDestructiveInjectEnumTagCall(IGF, inputTy, tag,
1533+
inputTi.getAddressForPointer(input));
1534+
return;
1535+
}
1536+
14941537
llvm_unreachable("IRGen unimplemented for this builtin!");
14951538
}

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,8 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
873873
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
874874
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
875875
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TargetOSVersionAtLeast)
876+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetEnumTag)
877+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InjectEnumTag)
876878
OperandOwnership OperandOwnershipBuiltinClassifier::visitCopy(BuiltinInst *bi,
877879
StringRef) {
878880
if (bi->getFunction()->getConventions().useLoweredAddresses()) {

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroupWithFlags)
612612
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
613613
CONSTANT_OWNERSHIP_BUILTIN(None, TaskRunInline)
614614
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)
615+
CONSTANT_OWNERSHIP_BUILTIN(None, GetEnumTag)
616+
CONSTANT_OWNERSHIP_BUILTIN(None, InjectEnumTag)
615617

616618
#undef CONSTANT_OWNERSHIP_BUILTIN
617619

lib/SIL/Utils/MemAccessUtils.cpp

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

2647+
case BuiltinValueKind::GetEnumTag:
2648+
visitor(&builtin->getAllOperands()[0]);
2649+
return;
2650+
2651+
case BuiltinValueKind::InjectEnumTag:
2652+
visitor(&builtin->getAllOperands()[0]);
2653+
visitor(&builtin->getAllOperands()[1]);
2654+
return;
2655+
26472656
// Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer,
26482657
// Builtin.Word)
26492658
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
@@ -156,6 +156,8 @@ static bool isBarrier(SILInstruction *inst) {
156156
case BuiltinValueKind::UnprotectedStackAlloc:
157157
case BuiltinValueKind::StackDealloc:
158158
case BuiltinValueKind::AssumeAlignment:
159+
case BuiltinValueKind::GetEnumTag:
160+
case BuiltinValueKind::InjectEnumTag:
159161
return false;
160162

161163
// 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)