summaryrefslogtreecommitdiffstats
path: root/js/src/jit/WarpBuilderShared.cpp
blob: e04984690c37bfe341734be5e9fa67e9335adca9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "jit/WarpBuilderShared.h"

#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"

using namespace js;
using namespace js::jit;

WarpBuilderShared::WarpBuilderShared(WarpSnapshot& snapshot,
                                     MIRGenerator& mirGen,
                                     MBasicBlock* current_)
    : snapshot_(snapshot),
      mirGen_(mirGen),
      alloc_(mirGen.alloc()),
      current(current_) {}

bool WarpBuilderShared::resumeAfter(MInstruction* ins, BytecodeLocation loc) {
  // resumeAfter should only be used with effectful instructions. The only
  // exceptions are:
  // 1. MInt64ToBigInt, which is used to convert the result of a call into Wasm
  //    code so we attach the resume point to that instead of to the call.
  // 2. MPostIntPtrConversion which is used after conversion from IntPtr.
  MOZ_ASSERT(ins->isEffectful() || ins->isInt64ToBigInt() ||
             ins->isPostIntPtrConversion());
  MOZ_ASSERT(!ins->isMovable());

  MResumePoint* resumePoint = MResumePoint::New(
      alloc(), ins->block(), loc.toRawBytecode(), ResumeMode::ResumeAfter);
  if (!resumePoint) {
    return false;
  }

  ins->setResumePoint(resumePoint);
  return true;
}

MConstant* WarpBuilderShared::constant(const Value& v) {
  MOZ_ASSERT_IF(v.isString(), v.toString()->isLinear());
  MOZ_ASSERT_IF(v.isGCThing(), !IsInsideNursery(v.toGCThing()));

  MConstant* cst = MConstant::New(alloc(), v);
  current->add(cst);
  return cst;
}

void WarpBuilderShared::pushConstant(const Value& v) {
  MConstant* cst = constant(v);
  current->push(cst);
}

MDefinition* WarpBuilderShared::unboxObjectInfallible(MDefinition* def,
                                                      IsMovable movable) {
  if (def->type() == MIRType::Object) {
    return def;
  }

  if (def->type() != MIRType::Value) {
    // Corner case: if the MIR node has a type other than Object or Value, this
    // code isn't actually reachable and we expect an earlier guard to fail.
    // Just insert a Box to satisfy MIR invariants.
    MOZ_ASSERT(movable == IsMovable::No);
    auto* box = MBox::New(alloc(), def);
    current->add(box);
    def = box;
  }

  auto* unbox = MUnbox::New(alloc(), def, MIRType::Object, MUnbox::Infallible);
  if (movable == IsMovable::No) {
    unbox->setNotMovable();
  }
  current->add(unbox);
  return unbox;
}

MCall* WarpBuilderShared::makeCall(CallInfo& callInfo, bool needsThisCheck,
                                   WrappedFunction* target, bool isDOMCall) {
  auto addUndefined = [this]() -> MConstant* {
    return constant(UndefinedValue());
  };

  return MakeCall(alloc(), addUndefined, callInfo, needsThisCheck, target,
                  isDOMCall);
}

MInstruction* WarpBuilderShared::makeSpreadCall(CallInfo& callInfo,
                                                bool needsThisCheck,
                                                bool isSameRealm,
                                                WrappedFunction* target) {
  MOZ_ASSERT(callInfo.argFormat() == CallInfo::ArgFormat::Array);
  MOZ_ASSERT_IF(needsThisCheck, !target);

  // Load dense elements of the argument array.
  MElements* elements = MElements::New(alloc(), callInfo.arrayArg());
  current->add(elements);

  if (callInfo.constructing()) {
    auto* newTarget = unboxObjectInfallible(callInfo.getNewTarget());
    auto* construct =
        MConstructArray::New(alloc(), target, callInfo.callee(), elements,
                             callInfo.thisArg(), newTarget);
    if (isSameRealm) {
      construct->setNotCrossRealm();
    }
    if (needsThisCheck) {
      construct->setNeedsThisCheck();
    }
    return construct;
  }

  auto* apply = MApplyArray::New(alloc(), target, callInfo.callee(), elements,
                                 callInfo.thisArg());

  if (callInfo.ignoresReturnValue()) {
    apply->setIgnoresReturnValue();
  }
  if (isSameRealm) {
    apply->setNotCrossRealm();
  }
  MOZ_ASSERT(!needsThisCheck);
  return apply;
}