Commit c210bf30 authored by Jan de Mooij's avatar Jan de Mooij
Browse files

Bug 1825014 part 2 - Implement ArgumentsLength() and GetArgument(i) intrinsics. r=iain

These can be used to replace uses of `arguments.length` and `arguments[i]` in
self-hosted code.

The frontend emits specialized bytecode ops for those instructions that access
the frame directly. This means we no longer have to allocate an arguments object in
the interpreter and Baseline for self-hosted functions. This speeds up many
perf-sensitive builtins such as `ArrayMap`.

Later patches convert all `arguments` uses in self-hosted code and add assertions to
ensure we never create an arguments object for any self-hosted function.

Differential Revision: https://phabricator.services.mozilla.com/D173952
parent 1e10b13e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1482,6 +1482,8 @@ static bool BytecodeIsEffectful(JSScript* script, size_t offset) {
    case JSOp::SetArg:
    case JSOp::GetLocal:
    case JSOp::SetLocal:
    case JSOp::GetActualArg:
    case JSOp::ArgumentsLength:
    case JSOp::ThrowSetConst:
    case JSOp::CheckLexical:
    case JSOp::CheckAliasedLexical:
+30 −0
Original line number Diff line number Diff line
@@ -7483,6 +7483,30 @@ bool BytecodeEmitter::emitSelfHostedGetBuiltinSymbol(CallNode* callNode) {
  return emit2(JSOp::Symbol, uint8_t(code));
}

bool BytecodeEmitter::emitSelfHostedArgumentsLength(CallNode* callNode) {
  MOZ_ASSERT(!sc->asFunctionBox()->needsArgsObj());
  sc->asFunctionBox()->setUsesArgumentsIntrinsics();

  MOZ_ASSERT(callNode->right()->as<ListNode>().count() == 0);

  return emit1(JSOp::ArgumentsLength);
}

bool BytecodeEmitter::emitSelfHostedGetArgument(CallNode* callNode) {
  MOZ_ASSERT(!sc->asFunctionBox()->needsArgsObj());
  sc->asFunctionBox()->setUsesArgumentsIntrinsics();

  ListNode* argsList = &callNode->right()->as<ListNode>();
  MOZ_ASSERT(argsList->count() == 1);

  ParseNode* argNode = argsList->head();
  if (!emitTree(argNode)) {
    return false;
  }

  return emit1(JSOp::GetActualArg);
}

#ifdef DEBUG
void BytecodeEmitter::assertSelfHostedExpectedTopLevel(ParseNode* node) {
  // The function argument is expected to be a simple binding/function name.
@@ -8044,6 +8068,12 @@ bool BytecodeEmitter::emitCallOrNew(CallNode* callNode, ValueUsage valueUsage) {
    if (calleeName == TaggedParserAtomIndex::WellKnown::GetBuiltinSymbol()) {
      return emitSelfHostedGetBuiltinSymbol(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::ArgumentsLength()) {
      return emitSelfHostedArgumentsLength(callNode);
    }
    if (calleeName == TaggedParserAtomIndex::WellKnown::GetArgument()) {
      return emitSelfHostedGetArgument(callNode);
    }
    if (calleeName ==
        TaggedParserAtomIndex::WellKnown::SetIsInlinableLargeFunction()) {
      return emitSelfHostedSetIsInlinableLargeFunction(callNode);
+2 −0
Original line number Diff line number Diff line
@@ -952,6 +952,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
  [[nodiscard]] bool emitSelfHostedSetIsInlinableLargeFunction(
      CallNode* callNode);
  [[nodiscard]] bool emitSelfHostedSetCanonicalName(CallNode* callNode);
  [[nodiscard]] bool emitSelfHostedArgumentsLength(CallNode* callNode);
  [[nodiscard]] bool emitSelfHostedGetArgument(CallNode* callNode);
#ifdef DEBUG
  void assertSelfHostedExpectedTopLevel(ParseNode* node);
  void assertSelfHostedUnsafeGetReservedSlot(ListNode* argsList);
+7 −0
Original line number Diff line number Diff line
@@ -663,6 +663,13 @@ class FunctionBox : public SuspendableContext {
    }
  }

  void setUsesArgumentsIntrinsics() {
    immutableFlags_.setFlag(ImmutableFlags::UsesArgumentsIntrinsics, true);
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedImmutableFlags();
    }
  }

  uint16_t length() { return length_; }
  void setLength(uint16_t length) { length_ = length; }

+3 −0
Original line number Diff line number Diff line
@@ -4003,6 +4003,9 @@ void js::DumpImmutableScriptFlags(js::JSONPrinter& json,
        case ImmutableScriptFlagsEnum::FunctionHasNewTargetBinding:
          json.value("FunctionHasNewTargetBinding");
          break;
        case ImmutableScriptFlagsEnum::UsesArgumentsIntrinsics:
          json.value("UsesArgumentsIntrinsics");
          break;
        default:
          json.value("Unknown(%x)", i);
          break;
Loading