if (symtab->state == PARSING)
     /* Maybe.  */
     return true;
+  if (cfun && cfun->after_inlining)
+    return false;
   if (current_function_decl
       && lookup_attribute ("omp declare target",
                           DECL_ATTRIBUTES (current_function_decl)))
             (so in most of the cases), and we'd need to maintain set of
             surrounding OpenMP constructs, which is better handled during
             gimplification.  */
-         if (symtab->state == PARSING
-             || (cfun->curr_properties & PROP_gimple_any) != 0)
+         if (symtab->state == PARSING)
            {
              ret = -1;
              continue;
          enum tree_code constructs[5];
          int nconstructs
            = omp_constructor_traits_to_codes (TREE_VALUE (t1), constructs);
+
+         if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+           {
+             if (!cfun->after_inlining)
+               {
+                 ret = -1;
+                 continue;
+               }
+             int i;
+             for (i = 0; i < nconstructs; ++i)
+               if (constructs[i] == OMP_SIMD)
+                 break;
+             if (i < nconstructs)
+               {
+                 ret = -1;
+                 continue;
+               }
+             /* If there is no simd, assume it is ok after IPA,
+                constructs should have been checked before.  */
+             continue;
+           }
+
          int r = omp_construct_selector_matches (constructs, nconstructs,
                                                  NULL);
          if (r == 0)
            case 'a':
              if (set == 'i' && !strcmp (sel, "atomic_default_mem_order"))
                {
+                 if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+                   break;
+
                  enum omp_memory_order omo
                    = ((enum omp_memory_order)
                       (omp_requires_mask
            case 'u':
              if (set == 'i' && !strcmp (sel, "unified_address"))
                {
+                 if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+                   break;
+
                  if ((omp_requires_mask & OMP_REQUIRES_UNIFIED_ADDRESS) == 0)
                    {
                      if (symtab->state == PARSING)
                }
              if (set == 'i' && !strcmp (sel, "unified_shared_memory"))
                {
+                 if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+                   break;
+
                  if ((omp_requires_mask
                       & OMP_REQUIRES_UNIFIED_SHARED_MEMORY) == 0)
                    {
            case 'd':
              if (set == 'i' && !strcmp (sel, "dynamic_allocators"))
                {
+                 if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+                   break;
+
                  if ((omp_requires_mask
                       & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0)
                    {
            case 'r':
              if (set == 'i' && !strcmp (sel, "reverse_offload"))
                {
+                 if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+                   break;
+
                  if ((omp_requires_mask & OMP_REQUIRES_REVERSE_OFFLOAD) == 0)
                    {
                      if (symtab->state == PARSING)
                           #pragma omp declare simd on it, some simd clones
                           might have the isa added later on.  */
                        if (r == -1
-                           && targetm.simd_clone.compute_vecsize_and_simdlen)
+                           && targetm.simd_clone.compute_vecsize_and_simdlen
+                           && (cfun == NULL || !cfun->after_inlining))
                          {
                            tree attrs
                              = DECL_ATTRIBUTES (current_function_decl);
   return ret;
 }
 
+/* Class describing a single variant.  */
+struct GTY(()) omp_declare_variant_entry {
+  /* NODE of the variant.  */
+  cgraph_node *variant;
+  /* Score if not in declare simd clone.  */
+  widest_int score;
+  /* Score if in declare simd clone.  */
+  widest_int score_in_declare_simd_clone;
+  /* Context selector for the variant.  */
+  tree ctx;
+  /* True if the context selector is known to match already.  */
+  bool matches;
+};
+
+/* Class describing a function with variants.  */
+struct GTY((for_user)) omp_declare_variant_base_entry {
+  /* NODE of the base function.  */
+  cgraph_node *base;
+  /* NODE of the artificial function created for the deferred variant
+     resolution.  */
+  cgraph_node *node;
+  /* Vector of the variants.  */
+  vec<omp_declare_variant_entry, va_gc> *variants;
+};
+
+struct omp_declare_variant_hasher
+  : ggc_ptr_hash<omp_declare_variant_base_entry> {
+  static hashval_t hash (omp_declare_variant_base_entry *);
+  static bool equal (omp_declare_variant_base_entry *,
+                    omp_declare_variant_base_entry *);
+};
+
+hashval_t
+omp_declare_variant_hasher::hash (omp_declare_variant_base_entry *x)
+{
+  inchash::hash hstate;
+  hstate.add_int (DECL_UID (x->base->decl));
+  hstate.add_int (x->variants->length ());
+  omp_declare_variant_entry *variant;
+  unsigned int i;
+  FOR_EACH_VEC_SAFE_ELT (x->variants, i, variant)
+    {
+      hstate.add_int (DECL_UID (variant->variant->decl));
+      hstate.add_wide_int (variant->score);
+      hstate.add_wide_int (variant->score_in_declare_simd_clone);
+      hstate.add_ptr (variant->ctx);
+      hstate.add_int (variant->matches);
+    }
+  return hstate.end ();
+}
+
+bool
+omp_declare_variant_hasher::equal (omp_declare_variant_base_entry *x,
+                                  omp_declare_variant_base_entry *y)
+{
+  if (x->base != y->base
+      || x->variants->length () != y->variants->length ())
+    return false;
+  omp_declare_variant_entry *variant;
+  unsigned int i;
+  FOR_EACH_VEC_SAFE_ELT (x->variants, i, variant)
+    if (variant->variant != (*y->variants)[i].variant
+       || variant->score != (*y->variants)[i].score
+       || (variant->score_in_declare_simd_clone
+           != (*y->variants)[i].score_in_declare_simd_clone)
+       || variant->ctx != (*y->variants)[i].ctx
+       || variant->matches != (*y->variants)[i].matches)
+      return false;
+  return true;
+}
+
+static GTY(()) hash_table<omp_declare_variant_hasher> *omp_declare_variants;
+
+struct omp_declare_variant_alt_hasher
+  : ggc_ptr_hash<omp_declare_variant_base_entry> {
+  static hashval_t hash (omp_declare_variant_base_entry *);
+  static bool equal (omp_declare_variant_base_entry *,
+                    omp_declare_variant_base_entry *);
+};
+
+hashval_t
+omp_declare_variant_alt_hasher::hash (omp_declare_variant_base_entry *x)
+{
+  return DECL_UID (x->node->decl);
+}
+
+bool
+omp_declare_variant_alt_hasher::equal (omp_declare_variant_base_entry *x,
+                                      omp_declare_variant_base_entry *y)
+{
+  return x->node == y->node;
+}
+
+static GTY(()) hash_table<omp_declare_variant_alt_hasher>
+  *omp_declare_variant_alt;
+
+/* Try to resolve declare variant after gimplification.  */
+
+static tree
+omp_resolve_late_declare_variant (tree alt)
+{
+  cgraph_node *node = cgraph_node::get (alt);
+  cgraph_node *cur_node = cgraph_node::get (cfun->decl);
+  if (node == NULL
+      || !node->declare_variant_alt
+      || !cfun->after_inlining)
+    return alt;
+
+  omp_declare_variant_base_entry entry;
+  entry.base = NULL;
+  entry.node = node;
+  entry.variants = NULL;
+  omp_declare_variant_base_entry *entryp
+    = omp_declare_variant_alt->find_with_hash (&entry, DECL_UID (alt));
+
+  unsigned int i, j;
+  omp_declare_variant_entry *varentry1, *varentry2;
+  auto_vec <bool, 16> matches;
+  unsigned int nmatches = 0;
+  FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1)
+    {
+      if (varentry1->matches)
+       {
+         /* This has been checked to be ok already.  */
+         matches.safe_push (true);
+         nmatches++;
+         continue;
+       }
+      switch (omp_context_selector_matches (varentry1->ctx))
+       {
+       case 0:
+          matches.safe_push (false);
+         break;
+       case -1:
+         return alt;
+       default:
+         matches.safe_push (true);
+         nmatches++;
+         break;
+       }
+    }
+
+  if (nmatches == 0)
+    return entryp->base->decl;
+
+  /* A context selector that is a strict subset of another context selector
+     has a score of zero.  */
+  FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1)
+    if (matches[i])
+      {
+        for (j = i + 1;
+            vec_safe_iterate (entryp->variants, j, &varentry2); ++j)
+         if (matches[j])
+           {
+             int r = omp_context_selector_compare (varentry1->ctx,
+                                                   varentry2->ctx);
+             if (r == -1)
+               {
+                 /* ctx1 is a strict subset of ctx2, ignore ctx1.  */
+                 matches[i] = false;
+                 break;
+               }
+             else if (r == 1)
+               /* ctx2 is a strict subset of ctx1, remove ctx2.  */
+               matches[j] = false;
+           }
+      }
+
+  widest_int max_score = -1;
+  varentry2 = NULL;
+  FOR_EACH_VEC_SAFE_ELT (entryp->variants, i, varentry1)
+    if (matches[i])
+      {
+       widest_int score
+         = (cur_node->simdclone ? varentry1->score_in_declare_simd_clone
+            : varentry1->score);
+       if (score > max_score)
+         {
+           max_score = score;
+           varentry2 = varentry1;
+         }
+      }
+  return varentry2->variant->decl;
+}
+
 /* Try to resolve declare variant, return the variant decl if it should
    be used instead of base, or base otherwise.  */
 
 omp_resolve_declare_variant (tree base)
 {
   tree variant1 = NULL_TREE, variant2 = NULL_TREE;
+  if (cfun && (cfun->curr_properties & PROP_gimple_any) != 0)
+    return omp_resolve_late_declare_variant (base);
+
   auto_vec <tree, 16> variants;
   auto_vec <bool, 16> defer;
   bool any_deferred = false;
       bool first = true;
       unsigned int i;
       tree attr1, attr2;
+      omp_declare_variant_base_entry entry;
+      entry.base = cgraph_node::get_create (base);
+      entry.node = NULL;
+      vec_alloc (entry.variants, variants.length ());
       FOR_EACH_VEC_ELT (variants, i, attr1)
        {
          widest_int score1;
                  variant2 = defer[i] ? NULL_TREE : attr1;
                }
            }
+         omp_declare_variant_entry varentry;
+         varentry.variant
+           = cgraph_node::get_create (TREE_PURPOSE (TREE_VALUE (attr1)));
+         varentry.score = score1;
+         varentry.score_in_declare_simd_clone = score2;
+         varentry.ctx = ctx;
+         varentry.matches = !defer[i];
+         entry.variants->quick_push (varentry);
        }
 
       /* If there is a clear winner variant with the score which is not
                }
            }
          if (variant1)
-           return TREE_PURPOSE (TREE_VALUE (variant1));
+           {
+             vec_free (entry.variants);
+             return TREE_PURPOSE (TREE_VALUE (variant1));
+           }
+       }
+
+      if (omp_declare_variants == NULL)
+       omp_declare_variants
+         = hash_table<omp_declare_variant_hasher>::create_ggc (64);
+      omp_declare_variant_base_entry **slot
+       = omp_declare_variants->find_slot (&entry, INSERT);
+      if (*slot != NULL)
+       {
+         vec_free (entry.variants);
+         return (*slot)->node->decl;
        }
 
-      return base;
+      *slot = ggc_cleared_alloc<omp_declare_variant_base_entry> ();
+      (*slot)->base = entry.base;
+      (*slot)->node = entry.base;
+      (*slot)->variants = entry.variants;
+      tree alt = build_decl (DECL_SOURCE_LOCATION (base), FUNCTION_DECL,
+                            DECL_NAME (base), TREE_TYPE (base));
+      DECL_ARTIFICIAL (alt) = 1;
+      DECL_IGNORED_P (alt) = 1;
+      TREE_STATIC (alt) = 1;
+      tree attributes = DECL_ATTRIBUTES (base);
+      if (lookup_attribute ("noipa", attributes) == NULL)
+       {
+         attributes = tree_cons (get_identifier ("noipa"), NULL, attributes);
+         if (lookup_attribute ("noinline", attributes) == NULL)
+           attributes = tree_cons (get_identifier ("noinline"), NULL,
+                                   attributes);
+         if (lookup_attribute ("noclone", attributes) == NULL)
+           attributes = tree_cons (get_identifier ("noclone"), NULL,
+                                   attributes);
+         if (lookup_attribute ("no_icf", attributes) == NULL)
+           attributes = tree_cons (get_identifier ("no_icf"), NULL,
+                                   attributes);
+       }
+      DECL_ATTRIBUTES (alt) = attributes;
+      DECL_INITIAL (alt) = error_mark_node;
+      (*slot)->node = cgraph_node::create (alt);
+      (*slot)->node->declare_variant_alt = 1;
+      (*slot)->node->create_reference (entry.base, IPA_REF_ADDR);
+      omp_declare_variant_entry *varentry;
+      FOR_EACH_VEC_SAFE_ELT (entry.variants, i, varentry)
+       (*slot)->node->create_reference (varentry->variant, IPA_REF_ADDR);
+      if (omp_declare_variant_alt == NULL)
+       omp_declare_variant_alt
+         = hash_table<omp_declare_variant_alt_hasher>::create_ggc (64);
+      *omp_declare_variant_alt->find_slot_with_hash (*slot, DECL_UID (alt),
+                                                    INSERT) = *slot;
+      return alt;
     }
 
   if (variants.length () == 1)
     return TREE_PURPOSE (TREE_VALUE (variants[0]));
 
-  /* A context selector that is a strict subset of another context selector has a score
-     of zero.  */
+  /* A context selector that is a strict subset of another context selector
+     has a score of zero.  */
   tree attr1, attr2;
   unsigned int i, j;
   FOR_EACH_VEC_ELT (variants, i, attr1)
   gcc_checking_assert (axis >= 0 && axis < GOMP_DIM_MAX);
   return (int) axis;
 }
+
+#include "gt-omp-general.h"