Bug 1629867 - Add support for LoadStringChar. r=jandem
authorTom Schuster <evilpies@gmail.com>
Wed, 15 Apr 2020 15:36:22 +0000
changeset 524192 706e0a08e6fadb0076f8ac965a7c0a5b851b192c
parent 524191 02970449a7bee151d7a2f4939369cb9bc562e4c3
child 524193 0c092e04a5a907dc7d6fce1d415215bbd5f33fe7
push id113041
push userevilpies@gmail.com
push dateWed, 15 Apr 2020 15:59:25 +0000
treeherderautoland@706e0a08e6fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1629867
milestone77.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1629867 - Add support for LoadStringChar. r=jandem Differential Revision: https://phabricator.services.mozilla.com/D70872
js/src/jit/WarpCacheIRTranspiler.cpp
js/src/jit/WarpCacheIRTranspiler.h
--- a/js/src/jit/WarpCacheIRTranspiler.cpp
+++ b/js/src/jit/WarpCacheIRTranspiler.cpp
@@ -56,16 +56,18 @@ class MOZ_RAII WarpCacheIRTranspiler {
     return reinterpret_cast<Shape*>(readStubWord(offset));
   }
   int32_t int32StubField(uint32_t offset) {
     return static_cast<int32_t>(readStubWord(offset));
   }
 
   bool transpileGuardTo(MIRType type);
 
+  MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
+
 #define DEFINE_OP(op, ...) MOZ_MUST_USE bool transpile_##op();
   WARP_CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
 
  public:
   WarpCacheIRTranspiler(MIRGenerator& mirGen, MBasicBlock* current,
                         const WarpCacheIR* snapshot, TranspilerOutput& output)
       : alloc_(mirGen.alloc()),
@@ -270,46 +272,87 @@ bool WarpCacheIRTranspiler::transpile_Lo
 
   auto* length = MStringLength::New(alloc(), str);
   current->add(length);
 
   setResult(length);
   return true;
 }
 
+MInstruction* WarpCacheIRTranspiler::addBoundsCheck(MDefinition* index,
+                                                    MDefinition* length) {
+  MInstruction* check = MBoundsCheck::New(alloc(), index, length);
+  current->add(check);
+
+  if (JitOptions.spectreIndexMasking) {
+    // Use a separate MIR instruction for the index masking. Doing this as
+    // part of MBoundsCheck would be unsound because bounds checks can be
+    // optimized or eliminated completely. Consider this:
+    //
+    //   for (var i = 0; i < x; i++)
+    //        res = arr[i];
+    //
+    // If we can prove |x < arr.length|, we are able to eliminate the bounds
+    // check, but we should not get rid of the index masking because the
+    // |i < x| branch could still be mispredicted.
+    //
+    // Using a separate instruction lets us eliminate the bounds check
+    // without affecting the index masking.
+    check = MSpectreMaskIndex::New(alloc(), check, length);
+    current->add(check);
+  }
+
+  return check;
+}
+
 bool WarpCacheIRTranspiler::transpile_LoadDenseElementResult() {
   ObjOperandId objId = reader.objOperandId();
   Int32OperandId indexId = reader.int32OperandId();
   MDefinition* obj = getOperand(objId);
   MDefinition* index = getOperand(indexId);
 
   auto* elements = MElements::New(alloc(), obj);
   current->add(elements);
 
   auto* length = MInitializedLength::New(alloc(), elements);
   current->add(length);
 
-  MInstruction* check = MBoundsCheck::New(alloc(), index, length);
-  current->add(check);
-
-  if (JitOptions.spectreIndexMasking) {
-    check = MSpectreMaskIndex::New(alloc(), check, length);
-    current->add(check);
-  }
+  index = addBoundsCheck(index, length);
 
   bool needsHoleCheck = true;
   bool loadDouble = false;  // TODO: Ion-only optimization.
   auto* load =
-      MLoadElement::New(alloc(), elements, check, needsHoleCheck, loadDouble);
+      MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
   current->add(load);
 
   setResult(load);
   return true;
 }
 
+bool WarpCacheIRTranspiler::transpile_LoadStringCharResult() {
+  StringOperandId strId = reader.stringOperandId();
+  Int32OperandId indexId = reader.int32OperandId();
+  MDefinition* str = getOperand(strId);
+  MDefinition* index = getOperand(indexId);
+
+  auto* length = MStringLength::New(alloc(), str);
+  current->add(length);
+
+  index = addBoundsCheck(index, length);
+
+  auto* charCode = MCharCodeAt::New(alloc(), str, index);
+  current->add(charCode);
+
+  auto* fromCharCode = MFromCharCode::New(alloc(), charCode);
+  current->add(fromCharCode);
+
+  setResult(fromCharCode);
+  return true;
+}
+
 bool WarpCacheIRTranspiler::transpile_TypeMonitorResult() {
   MOZ_ASSERT(output_.result, "Didn't set result MDefinition");
   return true;
 }
 
 bool WarpCacheIRTranspiler::transpile_ReturnFromIC() { return true; }
 
 bool jit::TranspileCacheIRToMIR(MIRGenerator& mirGen, MBasicBlock* current,
--- a/js/src/jit/WarpCacheIRTranspiler.h
+++ b/js/src/jit/WarpCacheIRTranspiler.h
@@ -32,16 +32,17 @@ using MDefinitionStackVector = Vector<MD
   _(LoadEnclosingEnvironment)         \
   _(LoadDynamicSlotResult)            \
   _(LoadFixedSlotResult)              \
   _(LoadEnvironmentFixedSlotResult)   \
   _(LoadEnvironmentDynamicSlotResult) \
   _(LoadInt32ArrayLengthResult)       \
   _(LoadStringLengthResult)           \
   _(LoadDenseElementResult)           \
+  _(LoadStringCharResult)             \
   _(TypeMonitorResult)                \
   _(ReturnFromIC)
 
 // TranspilerOutput contains information from the transpiler that needs to be
 // passed back to WarpBuilder.
 struct MOZ_STACK_CLASS TranspilerOutput {
   // For ICs that return a result, this is the corresponding MIR instruction.
   MDefinition* result = nullptr;