Merge pull request #9931 from CodaFi/what-does-brian-eno

diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp
index 626cb05..2bf4152 100644
--- a/lib/SIL/DynamicCasts.cpp
+++ b/lib/SIL/DynamicCasts.cpp
@@ -89,15 +89,24 @@
   if (source == target)
     return DynamicCastFeasibility::WillSucceed;
 
-  auto *SourceNominalTy = source.getAnyNominal();
-
-  if (!SourceNominalTy)
-    return DynamicCastFeasibility::MaySucceed;
-
   auto *TargetProtocol = target.getAnyNominal();
   if (!TargetProtocol)
     return DynamicCastFeasibility::MaySucceed;
 
+  auto *SourceNominalTy = source.getAnyNominal();
+
+  if (!SourceNominalTy) {
+    if (auto Archetype = dyn_cast<ArchetypeType>(source)) {
+      auto SourceProtocols = Archetype->getConformsTo();
+      // Check all protocols implemented by the archetype.
+      for (auto *Protocol : SourceProtocols) {
+        if (Protocol == TargetProtocol)
+          return DynamicCastFeasibility::WillSucceed;
+      }
+    }
+    return DynamicCastFeasibility::MaySucceed;
+  }
+
   auto SourceProtocols = SourceNominalTy->getAllProtocols();
 
   // Check all protocols implemented by the type.
@@ -330,11 +339,8 @@
   if (source->hasArchetype() || source.isExistentialType() ||
       target->hasArchetype() || target.isExistentialType()) {
 
-    auto *SourceNominalTy = source.getAnyNominal();
-
     // Check conversions from non-protocol types into protocol types.
     if (!source.isExistentialType() &&
-        SourceNominalTy &&
         target.isExistentialType())
       return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);
 
diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp
index e4c9d7d..638a3e5 100644
--- a/lib/SILOptimizer/Utils/Local.cpp
+++ b/lib/SILOptimizer/Utils/Local.cpp
@@ -2458,6 +2458,104 @@
   }
 }
 
+/// TODO: Move to emitSuccessfulIndirectUnconditionalCast?
+///
+/// Peephole to avoid runtime calls:
+/// unconditional_checked_cast_addr T in %0 : $*T to P in %1 : $*P
+/// ->
+/// %addr = init_existential_addr %1 : $*P, T
+/// copy_addr %0 to %addr
+///
+/// where T is a type statically known to conform to P.
+///
+/// In caase P is a class existential type, it generates:
+/// %val = load %0 : $*T
+/// %existential = init_existential_ref %val : $T, $T, P
+/// store %existential to %1 : $*P
+///
+/// Returns true if the optimization was possible and false otherwise.
+static bool optimizeStaticallyKnownProtocolConformance(
+    UnconditionalCheckedCastAddrInst *Inst) {
+  auto Loc = Inst->getLoc();
+  auto Src = Inst->getSrc();
+  auto Dest = Inst->getDest();
+  auto SourceType = Inst->getSourceType();
+  auto TargetType = Inst->getTargetType();
+  auto &Mod = Inst->getModule();
+
+  if (TargetType->isAnyExistentialType() &&
+      !SourceType->isAnyExistentialType()) {
+    auto &Ctx = Mod.getASTContext();
+    auto *SM = Mod.getSwiftModule();
+
+    auto Proto = dyn_cast<ProtocolDecl>(TargetType->getAnyNominal());
+    if (Proto) {
+      auto Conformance = SM->lookupConformance(SourceType, Proto, nullptr);
+      if (Conformance.hasValue()) {
+        // SourceType is a non-existential type conforming to a
+        // protocol represented by the TargetType.
+        SILBuilder B(Inst);
+        SmallVector<ProtocolConformanceRef, 1> NewConformances;
+        NewConformances.push_back(Conformance.getValue());
+        ArrayRef<ProtocolConformanceRef> Conformances =
+            Ctx.AllocateCopy(NewConformances);
+
+        auto ExistentialRepr =
+            Dest->getType().getPreferredExistentialRepresentation(Mod,
+                                                                  SourceType);
+
+        switch (ExistentialRepr) {
+        default:
+          return false;
+        case ExistentialRepresentation::Opaque: {
+          auto ExistentialAddr = B.createInitExistentialAddr(
+              Loc, Dest, SourceType, Src->getType().getObjectType(),
+              Conformances);
+          B.createCopyAddr(
+              Loc, Src, ExistentialAddr,
+              (Inst->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess)
+                  ? IsTake_t::IsTake
+                  : IsTake_t::IsNotTake,
+              IsInitialization_t::IsInitialization);
+          break;
+        }
+        case ExistentialRepresentation::Class: {
+          auto Value = B.createLoad(Loc, Src,
+                                    swift::LoadOwnershipQualifier::Unqualified);
+          if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
+            B.createRetainValue(Loc, Value, B.getDefaultAtomicity());
+          auto Existential =
+              B.createInitExistentialRef(Loc, Dest->getType().getObjectType(),
+                                         SourceType, Value, Conformances);
+          B.createStore(Loc, Existential, Dest,
+                        swift::StoreOwnershipQualifier::Unqualified);
+          break;
+        }
+        case ExistentialRepresentation::Boxed: {
+          auto AllocBox = B.createAllocExistentialBox(Loc, Dest->getType(),
+                                                      SourceType, Conformances);
+          auto Projection =
+              B.createProjectExistentialBox(Loc, Src->getType(), AllocBox);
+          auto Value = B.createLoad(Loc, Src,
+                                    swift::LoadOwnershipQualifier::Unqualified);
+          if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
+            B.createRetainValue(Loc, Value, B.getDefaultAtomicity());
+          B.createStore(Loc, Value, Projection,
+                        swift::StoreOwnershipQualifier::Unqualified);
+          B.createStore(Loc, AllocBox, Dest,
+                        swift::StoreOwnershipQualifier::Unqualified);
+          break;
+        }
+        };
+
+        Inst->replaceAllUsesWithUndef();
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 SILInstruction *
 CastOptimizer::
 optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst) {
@@ -2512,18 +2610,24 @@
   if (Feasibility == DynamicCastFeasibility::WillSucceed ||
       Feasibility == DynamicCastFeasibility::MaySucceed) {
 
+    // Check if a result of a cast is unused. If this is the case, the cast can
+    // be removed even if the cast may fail at runtime.
+    // Swift optimizer does not claim to be crash-preserving.
     bool ResultNotUsed = isa<AllocStackInst>(Dest);
     DestroyAddrInst *DestroyDestInst = nullptr;
-    for (auto Use : Dest->getUses()) {
-      auto *User = Use->getUser();
-      if (isa<DeallocStackInst>(User) || User == Inst)
-        continue;
-      if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
-        DestroyDestInst = cast<DestroyAddrInst>(User);
-        continue;
+    if (ResultNotUsed) {
+      for (auto Use : Dest->getUses()) {
+        auto *User = Use->getUser();
+        if (isa<DeallocStackInst>(User) || User == Inst)
+          continue;
+        if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
+          DestroyDestInst = cast<DestroyAddrInst>(User);
+          continue;
+        }
+        ResultNotUsed = false;
+        DestroyDestInst = nullptr;
+        break;
       }
-      ResultNotUsed = false;
-      break;
     }
 
     if (ResultNotUsed) {
@@ -2544,23 +2648,33 @@
       return nullptr;
     }
 
-    // Try to apply the bridged casts optimizations
+    // Try to apply the bridged casts optimizations.
     auto NewI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
                                      false, Src, Dest, SourceType,
                                      TargetType, nullptr, nullptr);
     if (NewI) {
-        WillSucceedAction();
-        return nullptr;
+      WillSucceedAction();
+      return nullptr;
+    }
+
+    if (Feasibility == DynamicCastFeasibility::MaySucceed)
+      return nullptr;
+
+    assert(Feasibility == DynamicCastFeasibility::WillSucceed);
+
+    if (optimizeStaticallyKnownProtocolConformance(Inst)) {
+      EraseInstAction(Inst);
+      WillSucceedAction();
+      return nullptr;
     }
 
     if (isBridgingCast(SourceType, TargetType))
       return nullptr;
 
     SILBuilderWithScope Builder(Inst);
-    if (!emitSuccessfulIndirectUnconditionalCast(Builder, Mod.getSwiftModule(),
-                                            Loc, Inst->getConsumptionKind(),
-                                            Src, SourceType,
-                                            Dest, TargetType, Inst)) {
+    if (!emitSuccessfulIndirectUnconditionalCast(
+            Builder, Mod.getSwiftModule(), Loc, Inst->getConsumptionKind(), Src,
+            SourceType, Dest, TargetType, Inst)) {
       // No optimization was possible.
       return nullptr;
     }
diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h
index 636c587..e631905 100644
--- a/stdlib/public/SwiftShims/LibcShims.h
+++ b/stdlib/public/SwiftShims/LibcShims.h
@@ -33,7 +33,7 @@
 
 // This declaration is not universally correct.  We verify its correctness for
 // the current platform in the runtime code.
-#if defined(__linux__) && defined (__arm__) && !defined(__android__)
+#if defined(__linux__) && defined (__arm__) && !defined(__ANDROID__)
 typedef           int __swift_ssize_t;
 #elif defined(_WIN32)
 #if defined(_M_ARM) || defined(_M_IX86)
@@ -113,7 +113,9 @@
 
 // TLS - thread local storage
 
-#if defined(__linux__)
+#if defined(__ANDROID__)
+typedef int __swift_pthread_key_t;
+#elif defined(__linux__)
 typedef unsigned int __swift_pthread_key_t;
 #else
 typedef unsigned long __swift_pthread_key_t;
diff --git a/test/SILOptimizer/constant_propagation.sil b/test/SILOptimizer/constant_propagation.sil
index 90d483a..48e9e17 100644
--- a/test/SILOptimizer/constant_propagation.sil
+++ b/test/SILOptimizer/constant_propagation.sil
@@ -1,6 +1,7 @@
 // RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s
 // RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -performance-constant-propagation | %FileCheck %s
 
+import Swift
 import Builtin
 
 struct UInt {
@@ -692,3 +693,254 @@
   dealloc_stack %31 : $*AnObject
   br bb1
 }
+
+public protocol P {
+}
+
+public protocol PP {
+}
+
+struct X : P {
+}
+
+struct Y<T>: P {
+  var x : T
+}
+
+class Z: P {
+  init()
+}
+
+// Do not optimize casts to unrelated protocols.
+// CHECK-LABEL: sil @dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential
+// CHECK: unconditional_checked_cast_addr
+// CHECK: end sil function 'dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential'
+sil @dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential : $@convention(thin) (@in X) -> (@out PP) {
+bb0(%0 : $*PP, %1 : $*X):
+  unconditional_checked_cast_addr take_always X in %1 : $*X to PP in %0 : $*PP
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// Do not optimize casts between existentials.
+// CHECK-LABEL: sil @dont_replace_unconditional_check_cast_addr_for_existential_to_existential
+// CHECK: unconditional_checked_cast_addr
+// CHECK: end sil function 'dont_replace_unconditional_check_cast_addr_for_existential_to_existential'
+sil @dont_replace_unconditional_check_cast_addr_for_existential_to_existential : $@convention(thin) (@in PP) -> (@out P) {
+bb0(%0 : $*P, %1 : $*PP):
+  unconditional_checked_cast_addr take_always PP in %1 : $*PP to P in %0 : $*P
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// Check that an unconditional_checked_cast_addr from a non-existential loadable type to a protocol
+// can be replaced by a more efficient code sequence if it is statitcally known that this
+// type conforms to this protocol.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential
+// CHECK-NOT: store
+// CHECK: copy_addr
+// CHECK-NOT: destroy_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_type_to_existential : $@convention(thin) (@in X) -> (@out P) {
+bb0(%0 : $*P, %1 : $*X):
+  unconditional_checked_cast_addr take_always X in %1 : $*X to P in %0 : $*P
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_class_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_class_to_existential : $@convention(thin) (@in Z) -> (@out P) {
+bb0(%0 : $*P, %1 : $*Z):
+  unconditional_checked_cast_addr take_always Z in %1 : $*Z to P in %0 : $*P
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_archetype_to_existential : $@convention(thin) <X:P> (@in X) -> (@out P) {
+bb0(%0 : $*P, %1 : $*X):
+  unconditional_checked_cast_addr take_always X in %1 : $*X to P in %0 : $*P
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_generic_type_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_generic_type_to_existential : $@convention(thin) <X:P> (@in Y<X>) -> (@out P) {
+bb0(%0 : $*P, %1 : $*Y<X>):
+  unconditional_checked_cast_addr take_always Y<X> in %1 : $*Y<X> to P in %0 : $*P
+  %2 = tuple ()
+  return %2 : $()
+}
+
+protocol Q : class {
+}
+
+class V : Q {
+  init()
+}
+
+class W<T>: Q {
+  var x : T
+  init()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_class_existential : $@convention(thin) (@in V) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*V):
+  unconditional_checked_cast_addr take_always V in %1 : $*V to Q in %0 : $*Q
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_class_existential_copy_on_success
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_class_existential_copy_on_success : $@convention(thin) (@in V) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*V):
+  unconditional_checked_cast_addr copy_on_success V in %1 : $*V to Q in %0 : $*Q
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_archetype_to_class_existential : $@convention(thin) <X:Q> (@in X) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*X):
+  unconditional_checked_cast_addr take_always X in %1 : $*X to Q in %0 : $*Q
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_to_class_existential : $@convention(thin) <X:Q> (@in W<X>) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*W<X>):
+  unconditional_checked_cast_addr take_always W<X> in %1 : $*W<X> to Q in %0 : $*Q
+  %2 = tuple ()
+  return %2 : $()
+}
+
+public protocol MyError : Error {
+}
+
+public class E1 : MyError {
+  init()
+}
+
+public class E2<T> : MyError {
+  init()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential 
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential : $@convention(thin) (@in E1) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E1):
+  unconditional_checked_cast_addr take_always E1 in %1 : $*E1 to MyError in %0 : $*MyError
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential_copy_on_success
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// There should be no [take] in copy_addr! 
+// CHECK: copy_addr %{{.}} to [initialization]
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential_copy_on_success : $@convention(thin) (@in E1) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E1):
+  unconditional_checked_cast_addr copy_on_success E1 in %1 : $*E1 to MyError in %0 : $*MyError
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_myerror_existentia
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_archetype_to_myerror_existential : $@convention(thin) <X:MyError> (@in X) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*X):
+  unconditional_checked_cast_addr take_always X in %1 : $*X to MyError in %0 : $*MyError
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_to_myerror_existential 
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_to_myerror_existential : $@convention(thin) <X:MyError> (@in E2<X>) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E2<X>):
+  unconditional_checked_cast_addr take_always E2<X> in %1 : $*E2<X> to MyError in %0 : $*MyError
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// Check casts to Error.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_error_existential 
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: [[ALLOC_BOX:%.*]] = alloc_existential_box $Error, $E1
+// CHECK: [[PROJ:%.*]] = project_existential_box $E1 in [[ALLOC_BOX]] : $Error
+// CHECK: [[VAL:%.*]] = load %1 : $*E1
+// CHECK: store [[VAL]] to [[PROJ]]
+// CHECK: store [[ALLOC_BOX]] to %0 : $*Error
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_error_existential : $@convention(thin) (@in E1) -> (@out Error) {
+bb0(%0 : $*Error, %1 : $*E1):
+  unconditional_checked_cast_addr take_always E1 in %1 : $*E1 to Error in %0 : $*Error
+  %2 = tuple ()
+  return %2 : $()
+}
+
+// Check casts to Error.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_error_existential_copy_on_success 
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: [[ALLOC_BOX:%.*]] = alloc_existential_box $Error, $E1
+// CHECK: [[PROJ:%.*]] = project_existential_box $E1 in [[ALLOC_BOX]] : $Error
+// CHECK: [[VAL:%.*]] = load %1 : $*E1
+// CHECK: retain_value [[VAL]]
+// CHECK: store [[VAL]] to [[PROJ]]
+// CHECK: store [[ALLOC_BOX]] to %0 : $*Error
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_error_existential_copy_on_success : $@convention(thin) (@in E1) -> (@out Error) {
+bb0(%0 : $*Error, %1 : $*E1):
+  unconditional_checked_cast_addr copy_on_success E1 in %1 : $*E1 to Error in %0 : $*Error
+  %2 = tuple ()
+  return %2 : $()
+}
diff --git a/test/SILOptimizer/sil_combine_enum_addr.sil b/test/SILOptimizer/sil_combine_enum_addr.sil
index 8493ee9..a525fbf 100644
--- a/test/SILOptimizer/sil_combine_enum_addr.sil
+++ b/test/SILOptimizer/sil_combine_enum_addr.sil
@@ -6,7 +6,7 @@
 import Swift
 
 // CHECK-LABEL: sil  @convert_inject_enum_addr_select_enum_addr_into_cond_br : $@convention(thin) (@in Int, @inout _Stdout) -> ()
-// CHECK: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
 // CHECK: inject_enum_addr
 // CHECK-NOT: select_enum_addr
 // CHECK-NOT: bb1
@@ -42,7 +42,7 @@
 
 
 // CHECK-LABEL: sil  @convert_inject_enum_addr_switch_enum_addr_into_cond_br : $@convention(thin) (@in Int, @inout _Stdout) -> ()
-// CHECK: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
 // CHECK: inject_enum_addr
 // CHECK-NOT: switch_enum_addr
 // CHECK-NOT: bb1