diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc
index 192029669e..088fdb34cc 100644
--- a/src/compiler/effect-control-linearizer.cc
+++ b/src/compiler/effect-control-linearizer.cc
@@ -891,6 +891,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
     case IrOpcode::kStoreTypedElement:
       LowerStoreTypedElement(node);
       break;
+    case IrOpcode::kStoreSignedSmallElement:
+      LowerStoreSignedSmallElement(node);
+      break;
     case IrOpcode::kLookupHashStorageIndex:
       result = LowerLookupHashStorageIndex(node);
       break;
@@ -3300,6 +3303,63 @@ void EffectControlLinearizer::LowerTransitionAndStoreElement(Node* node) {
   __ Bind(&done);
 }
 
+void EffectControlLinearizer::LowerStoreSignedSmallElement(Node* node) {
+  Node* array = node->InputAt(0);
+  Node* index = node->InputAt(1);
+  Node* value = node->InputAt(2);
+
+  // Store a signed small in an output array.
+  //
+  //   kind = ElementsKind(array)
+  //
+  //   -- STORE PHASE ----------------------
+  //   if kind == HOLEY_DOUBLE_ELEMENTS {
+  //     float_value = convert smi to float
+  //     Store array[index] = float_value
+  //   } else {
+  //     // kind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS
+  //     Store array[index] = value
+  //   }
+  //
+  Node* map = __ LoadField(AccessBuilder::ForMap(), array);
+  Node* kind;
+  {
+    Node* bit_field2 = __ LoadField(AccessBuilder::ForMapBitField2(), map);
+    Node* mask = __ Int32Constant(Map::ElementsKindBits::kMask);
+    Node* andit = __ Word32And(bit_field2, mask);
+    Node* shift = __ Int32Constant(Map::ElementsKindBits::kShift);
+    kind = __ Word32Shr(andit, shift);
+  }
+
+  Node* elements = __ LoadField(AccessBuilder::ForJSObjectElements(), array);
+  auto if_kind_is_double = __ MakeLabel();
+  auto done = __ MakeLabel();
+  __ GotoIf(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS),
+            &if_kind_is_double);
+  {
+    // Our ElementsKind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS.
+    // In this case, we know our value is a signed small, and we can optimize
+    // the ElementAccess information.
+    ElementAccess access = AccessBuilder::ForFixedArrayElement();
+    access.type = Type::SignedSmall();
+    access.machine_type = MachineType::TaggedSigned();
+    access.write_barrier_kind = kNoWriteBarrier;
+    __ StoreElement(access, elements, index, value);
+    __ Goto(&done);
+  }
+  __ Bind(&if_kind_is_double);
+  {
+    // Our ElementsKind is HOLEY_DOUBLE_ELEMENTS.
+    Node* int_value = ChangeSmiToInt32(value);
+    Node* float_value = __ ChangeInt32ToFloat64(int_value);
+    __ StoreElement(AccessBuilder::ForFixedDoubleArrayElement(), elements,
+                    index, float_value);
+    __ Goto(&done);
+  }
+
+  __ Bind(&done);
+}
+
 void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
   BailoutReason reason = BailoutReasonOf(node->op());
   Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow;
diff --git a/src/compiler/effect-control-linearizer.h b/src/compiler/effect-control-linearizer.h
index bae85e8e6f..19439c61d4 100644
--- a/src/compiler/effect-control-linearizer.h
+++ b/src/compiler/effect-control-linearizer.h
@@ -125,6 +125,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
   Node* LowerLoadFieldByIndex(Node* node);
   Node* LowerLoadTypedElement(Node* node);
   void LowerStoreTypedElement(Node* node);
+  void LowerStoreSignedSmallElement(Node* node);
   Node* LowerLookupHashStorageIndex(Node* node);
   Node* LowerLookupSigned32HashStorageIndex(Node* node);
   void LowerTransitionAndStoreElement(Node* node);
diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h
index 69fc01808d..efc5fffd57 100644
--- a/src/compiler/opcodes.h
+++ b/src/compiler/opcodes.h
@@ -353,6 +353,7 @@
   V(StoreField)                     \
   V(StoreElement)                   \
   V(StoreTypedElement)              \
+  V(StoreSignedSmallElement)        \
   V(TransitionAndStoreElement)      \
   V(ObjectIsArrayBufferView)        \
   V(ObjectIsCallable)               \
diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc
index 20f9e0677f..98f69d5027 100644
--- a/src/compiler/simplified-lowering.cc
+++ b/src/compiler/simplified-lowering.cc
@@ -2577,9 +2577,19 @@ class RepresentationSelector {
         return;
       }
       case IrOpcode::kTransitionAndStoreElement: {
+        Type* value_type = TypeOf(node->InputAt(2));
+
         ProcessInput(node, 0, UseInfo::AnyTagged());         // array
         ProcessInput(node, 1, UseInfo::TruncatingWord32());  // index
         ProcessInput(node, 2, UseInfo::AnyTagged());         // value
+
+        if (value_type->Is(Type::SignedSmall())) {
+          if (lower()) {
+            NodeProperties::ChangeOp(node,
+                                     simplified()->StoreSignedSmallElement());
+          }
+        }
+
         ProcessRemainingInputs(node, 3);
         SetOutput(node, MachineRepresentation::kNone);
         return;
diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc
index 02357a02e6..8aff74c564 100644
--- a/src/compiler/simplified-operator.cc
+++ b/src/compiler/simplified-operator.cc
@@ -1076,6 +1076,12 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreElement(
       1, 1, 0, 1, 0, parameters);
 }
 
+const Operator* SimplifiedOperatorBuilder::StoreSignedSmallElement() {
+  return new (zone()) Operator(IrOpcode::kStoreSignedSmallElement,
+                               Operator::kNoDeopt | Operator::kNoThrow,
+                               "StoreSignedSmallElement", 3, 1, 1, 0, 1, 0);
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h
index 3d46bb52c6..d527739b39 100644
--- a/src/compiler/simplified-operator.h
+++ b/src/compiler/simplified-operator.h
@@ -485,6 +485,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
   // store-element [base + index], value, only with fast arrays.
   const Operator* TransitionAndStoreElement(Handle<Map> double_map,
                                             Handle<Map> fast_map);
+  // store-element [base + index], smi value, only with fast arrays.
+  const Operator* StoreSignedSmallElement();
 
   // load-typed-element buffer, [base + external + index]
   const Operator* LoadTypedElement(ExternalArrayType const&);
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index 2b401f7993..81eadd80dd 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1976,6 +1976,8 @@ Type* Typer::Visitor::TypeTransitionAndStoreElement(Node* node) {
   UNREACHABLE();
 }
 
+Type* Typer::Visitor::TypeStoreSignedSmallElement(Node* node) { UNREACHABLE(); }
+
 Type* Typer::Visitor::TypeStoreTypedElement(Node* node) {
   UNREACHABLE();
 }
diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc
index 05c803fd42..9dd2a2facb 100644
--- a/src/compiler/verifier.cc
+++ b/src/compiler/verifier.cc
@@ -1306,6 +1306,10 @@ void Verifier::Visitor::Check(Node* node) {
     case IrOpcode::kTransitionAndStoreElement:
       CheckNotTyped(node);
       break;
+    case IrOpcode::kStoreSignedSmallElement:
+      CheckValueInputIs(node, 1, Type::SignedSmall());
+      CheckNotTyped(node);
+      break;
     case IrOpcode::kStoreTypedElement:
       CheckNotTyped(node);
       break;