diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index 5578cd81b0..8691f8811b 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -1255,39 +1255,52 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
   Node* effect = NodeProperties::GetEffectInput(node);
   Node* control = NodeProperties::GetControlInput(node);
 
-  // Optimize access for constant {receiver}.
-  HeapObjectMatcher mreceiver(receiver);
-  if (mreceiver.HasValue() && mreceiver.Value()->IsString()) {
-    Handle<String> string = Handle<String>::cast(mreceiver.Value());
+  // Optimize the case where we load from a constant {receiver}.
+  if (access_mode == AccessMode::kLoad) {
+    HeapObjectMatcher mreceiver(receiver);
+    if (mreceiver.HasValue() &&
+        !mreceiver.Value()->IsNullOrUndefined(isolate())) {
+      // Check whether we're accessing a known element on the {receiver}
+      // that is non-configurable, non-writable (i.e. the {receiver} was
+      // frozen using Object.freeze).
+      NumberMatcher mindex(index);
+      if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32)) {
+        LookupIterator it(isolate(), mreceiver.Value(),
+                          static_cast<uint32_t>(mindex.Value()),
+                          LookupIterator::OWN);
+        if (it.state() == LookupIterator::DATA && it.IsReadOnly() &&
+            !it.IsConfigurable()) {
+          // We can safely constant-fold the {index} access to {receiver},
+          // since the element is non-configurable, non-writable and thus
+          // cannot change anymore.
+          value = jsgraph()->Constant(it.GetDataValue());
+          ReplaceWithValue(node, value, effect, control);
+          return Replace(value);
+        }
+      }
 
-    // Strings are immutable in JavaScript.
-    if (access_mode == AccessMode::kStore) return NoChange();
+      // For constant Strings we can eagerly strength-reduce the keyed
+      // accesses using the known length, which doesn't change.
+      if (mreceiver.Value()->IsString()) {
+        Handle<String> string = Handle<String>::cast(mreceiver.Value());
 
-    // Properly deal with constant {index}.
-    NumberMatcher mindex(index);
-    if (mindex.IsInteger() && mindex.IsInRange(0.0, string->length() - 1)) {
-      // Constant-fold the {index} access to {string}.
-      Node* value = jsgraph()->HeapConstant(
-          factory()->LookupSingleCharacterStringFromCode(
-              string->Get(static_cast<int>(mindex.Value()))));
-      ReplaceWithValue(node, value, effect, control);
-      return Replace(value);
-    }
+        // We can only assume that the {index} is a valid array index if the IC
+        // is in element access mode and not MEGAMORPHIC, otherwise there's no
+        // guard for the bounds check below.
+        if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) {
+          // Ensure that {index} is less than {receiver} length.
+          Node* length = jsgraph()->Constant(string->length());
+          index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
+                                            length, effect, control);
 
-    // We can only assume that the {index} is a valid array index if the IC
-    // is in element access mode and not MEGAMORPHIC, otherwise there's no
-    // guard for the bounds check below.
-    if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) {
-      // Ensure that {index} is less than {receiver} length.
-      Node* length = jsgraph()->Constant(string->length());
-      index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
-                                        length, effect, control);
-
-      // Return the character from the {receiver} as single character string.
-      value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
-                               control);
-      ReplaceWithValue(node, value, effect, control);
-      return Replace(value);
+          // Return the character from the {receiver} as single character
+          // string.
+          value = graph()->NewNode(simplified()->StringCharAt(), receiver,
+                                   index, control);
+          ReplaceWithValue(node, value, effect, control);
+          return Replace(value);
+        }
+      }
     }
   }