Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/jit/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 214 kB image not shown  

Quelle  WarpCacheIRTranspiler.cpp   Sprache: C

 
/* -*- 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/WarpCacheIRTranspiler.h"

#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"

#include "jsmath.h"

#include "jit/AtomicOp.h"
#include "jit/CacheIR.h"
#include "jit/CacheIRCompiler.h"
#include "jit/CacheIROpsGenerated.h"
#include "jit/CacheIRReader.h"
#include "jit/LIR.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/WarpBuilder.h"
#include "jit/WarpBuilderShared.h"
#include "jit/WarpSnapshot.h"
#include "js/ScalarType.h"  // js::Scalar::Type
#include "vm/BytecodeLocation.h"
#include "vm/TypeofEqOperand.h"  // TypeofEqOperand
#include "wasm/WasmCode.h"

#include "gc/ObjectKind-inl.h"
#include "vm/NativeObject-inl.h"
#include "wasm/WasmInstance-inl.h"

using namespace js;
using namespace js::jit;

// The CacheIR transpiler generates MIR from Baseline CacheIR.
class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
  WarpBuilder* builder_;
  BytecodeLocation loc_;
  const CacheIRStubInfo* stubInfo_;
  const uint8_t* stubData_;

  // Vector mapping OperandId to corresponding MDefinition.
  using MDefinitionStackVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
  MDefinitionStackVector operands_;

  CallInfo* callInfo_;

  // Array mapping call arguments to OperandId.
  using ArgumentKindArray =
      mozilla::EnumeratedArray<ArgumentKind, OperandId,
                               size_t(ArgumentKind::NumKinds)>;
  ArgumentKindArray argumentOperandIds_;

  void setArgumentId(ArgumentKind kind, OperandId id) {
    MOZ_ASSERT(kind != ArgumentKind::Callee);
    MOZ_ASSERT(!argumentOperandIds_[kind].valid());
    argumentOperandIds_[kind] = id;
  }

  void updateArgumentsFromOperands();

#ifdef DEBUG
  // Used to assert that there is only one effectful instruction
  // per stub. And that this instruction has a resume point.
  MInstruction* effectful_ = nullptr;
  bool pushedResult_ = false;
#endif

  inline void addUnchecked(MInstruction* ins) {
    current->add(ins);

    // If we have not set a more specific bailout kind, mark this instruction
    // as transpiled CacheIR. If one of these instructions bails out, we
    // expect to hit the baseline fallback stub and invalidate the Warp script
    // in tryAttach.
    if (ins->bailoutKind() == BailoutKind::Unknown) {
      ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
    }
  }

  inline void add(MInstruction* ins) {
    MOZ_ASSERT(!ins->isEffectful());
    addUnchecked(ins);
  }

  inline void addEffectful(MInstruction* ins) {
    MOZ_ASSERT(ins->isEffectful());
    MOZ_ASSERT(!effectful_, "Can only have one effectful instruction");
    addUnchecked(ins);
#ifdef DEBUG
    effectful_ = ins;
#endif
  }

  // Bypasses all checks in addEffectful. Use with caution!
  inline void addEffectfulUnsafe(MInstruction* ins) {
    MOZ_ASSERT(ins->isEffectful());
    addUnchecked(ins);
  }

  [[nodiscard]] bool resumeAfterUnchecked(MInstruction* ins) {
    return WarpBuilderShared::resumeAfter(ins, loc_);
  }
  [[nodiscard]] bool resumeAfter(MInstruction* ins) {
    MOZ_ASSERT(effectful_ == ins);
    return resumeAfterUnchecked(ins);
  }

  // CacheIR instructions writing to the IC's result register (the *Result
  // instructions) must call this to push the result onto the virtual stack.
  void pushResult(MDefinition* result) {
    MOZ_ASSERT(!pushedResult_, "Can't have more than one result");
    current->push(result);
#ifdef DEBUG
    pushedResult_ = true;
#endif
  }

  MDefinition* getOperand(OperandId id) const { return operands_[id.id()]; }

  void setOperand(OperandId id, MDefinition* def) { operands_[id.id()] = def; }

  [[nodiscard]] bool defineOperand(OperandId id, MDefinition* def) {
    MOZ_ASSERT(id.id() == operands_.length());
    return operands_.append(def);
  }

  uintptr_t readStubWord(uint32_t offset) {
    return stubInfo_->getStubRawWord(stubData_, offset);
  }

  Shape* shapeStubField(uint32_t offset) {
    return reinterpret_cast<Shape*>(readStubWord(offset));
  }
  GetterSetter* getterSetterStubField(uint32_t offset) {
    return reinterpret_cast<GetterSetter*>(readStubWord(offset));
  }
  const JSClass* classStubField(uint32_t offset) {
    return reinterpret_cast<const JSClass*>(readStubWord(offset));
  }
  JSString* stringStubField(uint32_t offset) {
    return reinterpret_cast<JSString*>(readStubWord(offset));
  }
  JS::Symbol* symbolStubField(uint32_t offset) {
    return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
  }
  BaseScript* baseScriptStubField(uint32_t offset) {
    return reinterpret_cast<BaseScript*>(readStubWord(offset));
  }
  const JSJitInfo* jitInfoStubField(uint32_t offset) {
    return reinterpret_cast<const JSJitInfo*>(readStubWord(offset));
  }
  JSNative jsnativeStubField(uint32_t offset) {
    return reinterpret_cast<JSNative>(readStubWord(offset));
  }
  JS::ExpandoAndGeneration* expandoAndGenerationField(uint32_t offset) {
    return reinterpret_cast<JS::ExpandoAndGeneration*>(readStubWord(offset));
  }
  const wasm::FuncExport* wasmFuncExportField(uint32_t offset) {
    return reinterpret_cast<const wasm::FuncExport*>(readStubWord(offset));
  }
  NativeIteratorListHead* nativeIteratorListHeadStubField(uint32_t offset) {
    return reinterpret_cast<NativeIteratorListHead*>(readStubWord(offset));
  }
  size_t* fuseStubField(uint32_t offset) {
    return reinterpret_cast<size_t*>(readStubWord(offset));
  }
  gc::Heap allocSiteInitialHeapField(uint32_t offset) {
    uintptr_t word = readStubWord(offset);
    MOZ_ASSERT(word == uintptr_t(gc::Heap::Default) ||
               word == uintptr_t(gc::Heap::Tenured));
    return gc::Heap(word);
  }
  const void* rawPointerField(uint32_t offset) {
    return reinterpret_cast<const void*>(readStubWord(offset));
  }
  jsid idStubField(uint32_t offset) {
    return jsid::fromRawBits(readStubWord(offset));
  }
  int32_t int32StubField(uint32_t offset) {
    return static_cast<int32_t>(readStubWord(offset));
  }
  uint32_t uint32StubField(uint32_t offset) {
    return static_cast<uint32_t>(readStubWord(offset));
  }
  uint64_t uint64StubField(uint32_t offset) {
    return static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
  }
  Value valueStubField(uint32_t offset) {
    uint64_t raw =
        static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
    Value val = Value::fromRawBits(raw);
    MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
    return val;
  }
  double doubleStubField(uint32_t offset) {
    uint64_t raw =
        static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
    return mozilla::BitwiseCast<double>(raw);
  }

  // This must only be called when the caller knows the object is tenured and
  // not a nursery index.
  JSObject* tenuredObjectStubField(uint32_t offset) {
    WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
    return field.toObject();
  }

  // Returns either MConstant or MNurseryIndex. See WarpObjectField.
  MInstruction* objectStubField(uint32_t offset);

  const JSClass* classForGuardClassKind(GuardClassKind kind);

  [[nodiscard]] bool emitGuardTo(ValOperandId inputId, MIRType type);

  [[nodiscard]] bool emitToString(OperandId inputId, StringOperandId resultId);

  template <typename T>
  [[nodiscard]] bool emitDoubleBinaryArithResult(NumberOperandId lhsId,
                                                 NumberOperandId rhsId);

  template <typename T>
  [[nodiscard]] bool emitInt32BinaryArithResult(Int32OperandId lhsId,
                                                Int32OperandId rhsId);

  template <typename T>
  [[nodiscard]] bool emitBigIntBinaryArithResult(BigIntOperandId lhsId,
                                                 BigIntOperandId rhsId);

  template <typename T>
  [[nodiscard]] bool emitBigIntBinaryArithEffectfulResult(
      BigIntOperandId lhsId, BigIntOperandId rhsId);

  template <typename T>
  [[nodiscard]] bool emitBigIntUnaryArithResult(BigIntOperandId inputId);

  template <typename T>
  [[nodiscard]] bool emitBigIntPtrBinaryArith(IntPtrOperandId lhsId,
                                              IntPtrOperandId rhsId,
                                              IntPtrOperandId resultId);

  [[nodiscard]] bool emitCompareResult(JSOp op, OperandId lhsId,
                                       OperandId rhsId,
                                       MCompare::CompareType compareType);

  [[nodiscard]] bool emitTruthyResult(OperandId inputId);

  [[nodiscard]] bool emitNewIteratorResult(MNewIterator::Type type,
                                           uint32_t templateObjectOffset);

  MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);

  [[nodiscard]] MInstruction* convertToBoolean(MDefinition* input);

  bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind,
                                 ObjOperandId objId, uint32_t offsetOffset,
                                 ValOperandId rhsId, uint32_t newShapeOffset);

  MInstruction* emitTypedArrayLength(ArrayBufferViewKind viewKind,
                                     MDefinition* obj);

  MInstruction* emitDataViewLength(ArrayBufferViewKind viewKind,
                                   MDefinition* obj);

  void addDataViewData(ArrayBufferViewKind viewKind, MDefinition* obj,
                       Scalar::Type type, MDefinition** offset,
                       MInstruction** elements);

  [[nodiscard]] bool emitAtomicsBinaryOp(
      ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
      Scalar::Type elementType, bool forEffect, ArrayBufferViewKind viewKind,
      AtomicOp op);

  [[nodiscard]] bool emitLoadArgumentSlot(ValOperandId resultId,
                                          uint32_t slotIndex);

  // Calls are either Native (native function without a JitEntry),
  // a DOM Native (native function with a JitInfo OpType::Method),
  // or Scripted (scripted function or native function with a JitEntry).
  enum class CallKind { Native, DOM, Scripted };

  [[nodiscard]] bool updateCallInfo(MDefinition* callee, CallFlags flags);

  [[nodiscard]] bool emitCallFunction(
      ObjOperandId calleeId, Int32OperandId argcId,
      mozilla::Maybe<ObjOperandId> thisObjId, CallFlags flags, CallKind kind,
      mozilla::Maybe<uint32_t> siteOffset = mozilla::Nothing());
  [[nodiscard]] bool emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
                                         CallFlags flags);

  MDefinition* convertWasmArg(MDefinition* arg, wasm::ValType::Kind kind);

  WrappedFunction* maybeWrappedFunction(MDefinition* callee, CallKind kind,
                                        uint16_t nargs, FunctionFlags flags);
  WrappedFunction* maybeCallTarget(MDefinition* callee, CallKind kind);

  bool maybeCreateThis(MDefinition* callee, CallFlags flags, CallKind kind);

  [[nodiscard]] bool emitCallGetterResult(CallKind kind,
                                          ValOperandId receiverId,
                                          uint32_t getterOffset, bool sameRealm,
                                          uint32_t nargsAndFlagsOffset);
  [[nodiscard]] bool emitCallSetter(CallKind kind, ObjOperandId receiverId,
                                    uint32_t setterOffset, ValOperandId rhsId,
                                    bool sameRealm,
                                    uint32_t nargsAndFlagsOffset);

#ifndef JS_CODEGEN_X86
  [[nodiscard]] bool emitCallScriptedProxyGetShared(
      MDefinition* target, MDefinition* receiver, MDefinition* handler,
      MDefinition* id, MDefinition* trapDef, WrappedFunction* trap);
#endif

  CACHE_IR_TRANSPILER_GENERATED

 public:
  WarpCacheIRTranspiler(WarpBuilder* builder, BytecodeLocation loc,
                        CallInfo* callInfo, const WarpCacheIR* cacheIRSnapshot)
      : WarpBuilderShared(builder->snapshot(), builder->mirGen(),
                          builder->currentBlock()),
        builder_(builder),
        loc_(loc),
        stubInfo_(cacheIRSnapshot->stubInfo()),
        stubData_(cacheIRSnapshot->stubData()),
        callInfo_(callInfo) {}

  [[nodiscard]] bool transpile(std::initializer_list<MDefinition*> inputs);
};

bool WarpCacheIRTranspiler::transpile(
    std::initializer_list<MDefinition*> inputs) {
  if (!operands_.append(inputs.begin(), inputs.end())) {
    return false;
  }

  CacheIRReader reader(stubInfo_);
  do {
    CacheOp op = reader.readOp();
    switch (op) {
#define DEFINE_OP(op, ...)   \
  case CacheOp::op:          \
    if (!emit##op(reader)) { \
      return false;          \
    }                        \
    break;
      CACHE_IR_TRANSPILER_OPS(DEFINE_OP)
#undef DEFINE_OP

      default:
        fprintf(stderr, "Unsupported op: %s\n", CacheIROpNames[size_t(op)]);
        MOZ_CRASH("Unsupported op");
    }
  } while (reader.more());

  // Effectful instructions should have a resume point. We allow a limited
  // number of exceptions:
  // - MIonToWasmCall: Resumes after MInt64ToBigInt
  // - MLoadUnboxedScalar: Resumes after MInt64ToBigInt
  // - MAtomicTypedArrayElementBinop: Resumes after MInt64ToBigInt
  // - MAtomicExchangeTypedArrayElement: Resumes after MInt64ToBigInt
  // - MCompareExchangeTypedArrayElement: Resumes after MInt64ToBigInt
  // - MResizableTypedArrayLength: Resumes after MPostIntPtrConversion
  // - MResizableDataViewByteLength: Resumes after MPostIntPtrConversion
  // - MGrowableSharedArrayBufferByteLength: Resumes after MPostIntPtrConversion
  MOZ_ASSERT_IF(effectful_,
                effectful_->resumePoint() || effectful_->isIonToWasmCall() ||
                    effectful_->isLoadUnboxedScalar() ||
                    effectful_->isAtomicTypedArrayElementBinop() ||
                    effectful_->isAtomicExchangeTypedArrayElement() ||
                    effectful_->isCompareExchangeTypedArrayElement() ||
                    effectful_->isResizableTypedArrayLength() ||
                    effectful_->isResizableDataViewByteLength() ||
                    effectful_->isGrowableSharedArrayBufferByteLength());
  return true;
}

MInstruction* WarpCacheIRTranspiler::objectStubField(uint32_t offset) {
  WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));

  if (field.isNurseryIndex()) {
    auto* ins = MNurseryObject::New(alloc(), field.toNurseryIndex());
    add(ins);
    return ins;
  }

  auto* ins = MConstant::NewObject(alloc(), field.toObject());
  add(ins);
  return ins;
}

bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
                                           GuardClassKind kind) {
  MDefinition* def = getOperand(objId);

  MInstruction* ins;
  if (kind == GuardClassKind::JSFunction) {
    ins = MGuardToFunction::New(alloc(), def);
  } else {
    const JSClass* classp = classForGuardClassKind(kind);
    ins = MGuardToClass::New(alloc(), def, classp);
  }

  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardEitherClass(ObjOperandId objId,
                                                 GuardClassKind kind1,
                                                 GuardClassKind kind2) {
  MDefinition* def = getOperand(objId);

  // We don't yet need this case, so it's unsupported for now.
  MOZ_ASSERT(kind1 != GuardClassKind::JSFunction &&
             kind2 != GuardClassKind::JSFunction);

  const JSClass* classp1 = classForGuardClassKind(kind1);
  const JSClass* classp2 = classForGuardClassKind(kind2);
  auto* ins = MGuardToEitherClass::New(alloc(), def, classp1, classp2);

  add(ins);

  setOperand(objId, ins);
  return true;
}

const JSClass* WarpCacheIRTranspiler::classForGuardClassKind(
    GuardClassKind kind) {
  switch (kind) {
    case GuardClassKind::Array:
    case GuardClassKind::PlainObject:
    case GuardClassKind::FixedLengthArrayBuffer:
    case GuardClassKind::ResizableArrayBuffer:
    case GuardClassKind::FixedLengthSharedArrayBuffer:
    case GuardClassKind::GrowableSharedArrayBuffer:
    case GuardClassKind::FixedLengthDataView:
    case GuardClassKind::ResizableDataView:
    case GuardClassKind::MappedArguments:
    case GuardClassKind::UnmappedArguments:
    case GuardClassKind::Set:
    case GuardClassKind::Map:
    case GuardClassKind::BoundFunction:
    case GuardClassKind::Date:
      return ClassFor(kind);
    case GuardClassKind::WindowProxy:
      return mirGen().runtime->maybeWindowProxyClass();
    case GuardClassKind::JSFunction:
      break;
  }
  MOZ_CRASH("unexpected kind");
}

bool WarpCacheIRTranspiler::emitGuardAnyClass(ObjOperandId objId,
                                              uint32_t claspOffset) {
  MDefinition* def = getOperand(objId);
  const JSClass* classp = classStubField(claspOffset);

  auto* ins = MGuardToClass::New(alloc(), def, classp);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardShape(ObjOperandId objId,
                                           uint32_t shapeOffset) {
  MDefinition* def = getOperand(objId);

  // No read barrier is required because snapshot data is not weak and is traced
  // as part of IonCompileTask.
  Shape* shape = shapeStubField(shapeOffset);

  auto* ins = MGuardShape::New(alloc(), def, shape);
  add(ins);

  setOperand(objId, ins);
  return true;
}

template <auto FuseMember>
struct RealmFuseDependency final : public CompilationDependency {
  RealmFuseDependency();

  virtual bool registerDependency(JSContext* cx, HandleScript script) override {
    MOZ_ASSERT(checkDependency(cx));

    return (cx->realm()->realmFuses.*FuseMember).addFuseDependency(cx, script);
  }

  virtual UniquePtr<CompilationDependency> clone() override {
    return MakeUnique<RealmFuseDependency<FuseMember>>();
  }

  virtual bool checkDependency(JSContext* cx) override {
    return (cx->realm()->realmFuses.*FuseMember).intact();
  }

  virtual bool operator==(CompilationDependency& dep) override {
    return dep.type == type;
  }
};

using GetIteratorDependency =
    RealmFuseDependency<&RealmFuses::optimizeGetIteratorFuse>;
template <>
GetIteratorDependency::RealmFuseDependency()
    : CompilationDependency(CompilationDependency::Type::GetIterator) {}

bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
  if (fuseIndex != RealmFuses::FuseIndex::OptimizeGetIteratorFuse) {
    auto* ins = MGuardFuse::New(alloc(), fuseIndex);
    add(ins);
    return true;
  }

  // Register the compilation dependency.
  GetIteratorDependency dep;
  return mirGen().tracker.addDependency(dep);
}

bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId,
                                                    uint32_t shapesOffset) {
  MDefinition* def = getOperand(objId);
  MInstruction* shapeList = objectStubField(shapesOffset);

  auto* ins = MGuardMultipleShapes::New(alloc(), def, shapeList);
  if (builder_->info().inlineScriptTree()->hasSharedICScript()) {
    ins->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding);
  }
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
  MDefinition* def = getOperand(objId);

  auto* ins = MGuardNullProto::New(alloc(), def);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsNativeObject::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsProxy::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsNotProxy::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsNotDOMProxy::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
    ObjOperandId objId, uint32_t idOffset, uint32_t getterSetterOffset) {
  MDefinition* obj = getOperand(objId);
  jsid id = idStubField(idOffset);
  GetterSetter* gs = getterSetterStubField(getterSetterOffset);

  auto* ins = MGuardHasGetterSetter::New(alloc(), obj, id, gs);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId,
                                               uint32_t idOffset) {
  MDefinition* obj = getOperand(objId);
  jsid id = idStubField(idOffset);

  auto* ins = MProxyGet::New(alloc(), obj, id);
  addEffectful(ins);

  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId,
                                                      ValOperandId idId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);

  auto* ins = MProxyGetByValue::New(alloc(), obj, id);
  addEffectful(ins);

  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId,
                                                   ValOperandId idId,
                                                   bool hasOwn) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);

  auto* ins = MProxyHasProp::New(alloc(), obj, id, hasOwn);
  addEffectful(ins);

  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitProxySet(ObjOperandId objId, uint32_t idOffset,
                                         ValOperandId rhsId, bool strict) {
  MDefinition* obj = getOperand(objId);
  jsid id = idStubField(idOffset);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MProxySet::New(alloc(), obj, rhs, id, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId,
                                                ValOperandId idId,
                                                ValOperandId rhsId,
                                                bool strict) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MProxySetByValue::New(alloc(), obj, id, rhs, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId,
                                                   bool strict,
                                                   ValOperandId rhsId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MCallSetArrayLength::New(alloc(), obj, rhs, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitCallDOMGetterResult(ObjOperandId objId,
                                                    uint32_t jitInfoOffset) {
  MDefinition* obj = getOperand(objId);
  const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);

  MInstruction* ins;
  if (jitInfo->isAlwaysInSlot) {
    ins = MGetDOMMember::New(alloc(), jitInfo, obj, nullptr, nullptr);
  } else {
    // TODO(post-Warp): realms, guard operands (movable?).
    ins = MGetDOMProperty::New(alloc(), jitInfo, DOMObjectKind::Native,
                               (JS::Realm*)mirGen().realm->realmPtr(), obj,
                               nullptr, nullptr);
  }

  if (!ins) {
    return false;
  }

  if (ins->isEffectful()) {
    addEffectful(ins);
    pushResult(ins);
    return resumeAfter(ins);
  }

  add(ins);
  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitCallDOMSetter(ObjOperandId objId,
                                              uint32_t jitInfoOffset,
                                              ValOperandId rhsId) {
  MDefinition* obj = getOperand(objId);
  const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
  MDefinition* value = getOperand(rhsId);

  MOZ_ASSERT(jitInfo->type() == JSJitInfo::Setter);
  auto* set =
      MSetDOMProperty::New(alloc(), jitInfo->setter, DOMObjectKind::Native,
                           (JS::Realm*)mirGen().realm->realmPtr(), obj, value);
  addEffectful(set);
  return resumeAfter(set);
}

bool WarpCacheIRTranspiler::emitLoadDOMExpandoValue(ObjOperandId objId,
                                                    ValOperandId resultId) {
  MDefinition* proxy = getOperand(objId);

  auto* ins = MLoadDOMExpandoValue::New(alloc(), proxy);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueGuardGeneration(
    ObjOperandId objId, uint32_t expandoAndGenerationOffset,
    uint32_t generationOffset, ValOperandId resultId) {
  MDefinition* proxy = getOperand(objId);
  JS::ExpandoAndGeneration* expandoAndGeneration =
      expandoAndGenerationField(expandoAndGenerationOffset);
  uint64_t generation = uint64StubField(generationOffset);

  auto* ins = MLoadDOMExpandoValueGuardGeneration::New(
      alloc(), proxy, expandoAndGeneration, generation);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueIgnoreGeneration(
    ObjOperandId objId, ValOperandId resultId) {
  MDefinition* proxy = getOperand(objId);

  auto* ins = MLoadDOMExpandoValueIgnoreGeneration::New(alloc(), proxy);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardDOMExpandoMissingOrGuardShape(
    ValOperandId expandoId, uint32_t shapeOffset) {
  MDefinition* expando = getOperand(expandoId);
  Shape* shape = shapeStubField(shapeOffset);

  auto* ins = MGuardDOMExpandoMissingOrGuardShape::New(alloc(), expando, shape);
  add(ins);

  setOperand(expandoId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotResult(ObjOperandId objId,
                                                          uint32_t nameOffset) {
  MDefinition* obj = getOperand(objId);
  PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();

  auto* ins = MMegamorphicLoadSlot::New(alloc(), obj, NameToId(name));

  add(ins);
  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotPermissiveResult(
    ObjOperandId objId, uint32_t nameOffset) {
  MDefinition* obj = getOperand(objId);
  PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();

  auto* ins = MMegamorphicLoadSlotPermissive::New(alloc(), obj, NameToId(name));

  addEffectful(ins);
  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValueResult(
    ObjOperandId objId, ValOperandId idId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);

  auto* ins = MMegamorphicLoadSlotByValue::New(alloc(), obj, id);

  add(ins);
  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValuePermissiveResult(
    ObjOperandId objId, ValOperandId idId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);

  auto* ins = MMegamorphicLoadSlotByValuePermissive::New(alloc(), obj, id);

  addEffectful(ins);
  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId,
                                                     uint32_t idOffset,
                                                     ValOperandId rhsId,
                                                     bool strict) {
  MDefinition* obj = getOperand(objId);
  jsid id = idStubField(idOffset);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MMegamorphicStoreSlot::New(alloc(), obj, rhs, id, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId,
                                                         ValOperandId idId,
                                                         bool hasOwn) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);

  auto* ins = MMegamorphicHasProp::New(alloc(), obj, id, hasOwn);
  add(ins);

  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitSmallObjectVariableKeyHasOwnResult(
    StringOperandId idId, uint32_t propNamesOffset, uint32_t shapeOffset) {
  MDefinition* id = getOperand(idId);
  SharedShape* shape = &shapeStubField(shapeOffset)->asShared();

  auto* ins = MSmallObjectVariableKeyHasProp::New(alloc(), id, shape);
  add(ins);

  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId,
                                                      ValOperandId idId,
                                                      ValOperandId rhsId,
                                                      bool strict) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MMegamorphicSetElement::New(alloc(), obj, id, rhs, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitObjectToIteratorResult(
    ObjOperandId objId, uint32_t enumeratorsAddrOffset) {
  MDefinition* obj = getOperand(objId);
  NativeIteratorListHead* enumeratorsAddr =
      nativeIteratorListHeadStubField(enumeratorsAddrOffset);

  auto* ins = MObjectToIterator::New(alloc(), obj, enumeratorsAddr);
  addEffectful(ins);
  pushResult(ins);
  if (!resumeAfter(ins)) {
    return false;
  }

  return true;
}

bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId) {
  MDefinition* val = getOperand(valId);

  auto* ins = MValueToIterator::New(alloc(), val);
  addEffectful(ins);

  pushResult(ins);
  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsNotArrayBufferMaybeShared::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsTypedArray::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsFixedLengthTypedArray(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsFixedLengthTypedArray::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsResizableTypedArray(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsResizableTypedArray::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId,
                                                     uint32_t handlerOffset) {
  MDefinition* obj = getOperand(objId);
  const void* handler = rawPointerField(handlerOffset);

  auto* ins = MGuardHasProxyHandler::New(alloc(), obj, handler);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
                                           uint32_t protoOffset) {
  MDefinition* def = getOperand(objId);
  MDefinition* proto = objectStubField(protoOffset);

  auto* ins = MGuardProto::New(alloc(), def, proto);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsSpecificObject(
    ObjOperandId objId, ObjOperandId expectedId, uint32_t slotOffset) {
  size_t slotIndex = int32StubField(slotOffset);
  MDefinition* obj = getOperand(objId);
  MDefinition* expected = getOperand(expectedId);

  auto* slots = MSlots::New(alloc(), obj);
  add(slots);

  auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
  add(load);

  auto* unbox = MUnbox::New(alloc(), load, MIRType::Object, MUnbox::Fallible);
  add(unbox);

  auto* guard = MGuardObjectIdentity::New(alloc(), unbox, expected,
                                          /* bailOnEquality = */ false);
  add(guard);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId,
                                                ObjOperandId objId,
                                                uint32_t slotOffset) {
  size_t slotIndex = int32StubField(slotOffset);
  MDefinition* obj = getOperand(objId);

  auto* slots = MSlots::New(alloc(), obj);
  add(slots);

  auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
  add(load);

  return defineOperand(resultId, load);
}

bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject(
    ObjOperandId objId, uint32_t slotOffset) {
  size_t slotIndex = int32StubField(slotOffset);
  MDefinition* obj = getOperand(objId);

  auto* slots = MSlots::New(alloc(), obj);
  add(slots);

  auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
  add(load);

  auto* guard = MGuardIsNotObject::New(alloc(), load);
  add(guard);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFixedSlotValue(ObjOperandId objId,
                                                    uint32_t offsetOffset,
                                                    uint32_t valOffset) {
  MDefinition* obj = getOperand(objId);

  size_t offset = int32StubField(offsetOffset);
  Value val = valueStubField(valOffset);

  uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);

  auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
  add(load);

  auto* guard = MGuardValue::New(alloc(), load, val);
  add(guard);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId,
                                                      uint32_t offsetOffset,
                                                      uint32_t valOffset) {
  MDefinition* obj = getOperand(objId);

  size_t offset = int32StubField(offsetOffset);
  Value val = valueStubField(valOffset);

  size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);

  auto* slots = MSlots::New(alloc(), obj);
  add(slots);

  auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
  add(load);

  auto* guard = MGuardValue::New(alloc(), load, val);
  add(guard);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ObjOperandId resultId,
                                                         ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* load = MLoadScriptedProxyHandler::New(alloc(), obj);
  add(load);

  return defineOperand(resultId, load);
}

bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId,
                                                   ValOperandId idId) {
  MDefinition* id = getOperand(idId);

  auto* ins = MIdToStringOrSymbol::New(alloc(), id);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId,
                                                  uint32_t expectedOffset) {
  MDefinition* str = getOperand(strId);
  JSString* expected = stringStubField(expectedOffset);

  auto* ins = MGuardSpecificAtom::New(alloc(), str, &expected->asAtom());
  add(ins);

  setOperand(strId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId,
                                                    uint32_t expectedOffset) {
  MDefinition* symbol = getOperand(symId);
  JS::Symbol* expected = symbolStubField(expectedOffset);

  auto* ins = MGuardSpecificSymbol::New(alloc(), symbol, expected);
  add(ins);

  setOperand(symId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId,
                                                   int32_t expected) {
  MDefinition* num = getOperand(numId);

  auto* ins = MGuardSpecificInt32::New(alloc(), num, expected);
  add(ins);

  setOperand(numId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardSpecificValue(ValOperandId valId,
                                                   uint32_t expectedOffset) {
  MDefinition* val = getOperand(valId);
  Value expected = valueStubField(expectedOffset);

  auto* ins = MGuardValue::New(alloc(), val, expected);
  add(ins);

  setOperand(valId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
                                                    uint32_t expectedOffset) {
  MDefinition* obj = getOperand(objId);
  MDefinition* expected = objectStubField(expectedOffset);

  auto* ins = MGuardObjectIdentity::New(alloc(), obj, expected,
                                        /* bailOnEquality = */ false);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
    ObjOperandId objId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
  MDefinition* obj = getOperand(objId);
  MDefinition* expected = objectStubField(expectedOffset);
  uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);

  uint16_t nargs = nargsAndFlags >> 16;
  FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));

  auto* ins = MGuardSpecificFunction::New(alloc(), obj, expected, nargs, flags);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFunctionScript(
    ObjOperandId funId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
  MDefinition* fun = getOperand(funId);
  BaseScript* expected = baseScriptStubField(expectedOffset);
  uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);

  uint16_t nargs = nargsAndFlags >> 16;
  FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));

  auto* ins = MGuardFunctionScript::New(alloc(), fun, expected, nargs, flags);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId,
                                                   Int32OperandId resultId) {
  MDefinition* str = getOperand(strId);

  auto* ins = MGuardStringToIndex::New(alloc(), str);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardStringToInt32(StringOperandId strId,
                                                   Int32OperandId resultId) {
  MDefinition* str = getOperand(strId);

  auto* ins = MGuardStringToInt32::New(alloc(), str);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardStringToNumber(StringOperandId strId,
                                                    NumberOperandId resultId) {
  MDefinition* str = getOperand(strId);

  auto* ins = MGuardStringToDouble::New(alloc(), str);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardNoDenseElements::New(alloc(), obj);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFunctionHasJitEntry(ObjOperandId funId) {
  MDefinition* fun = getOperand(funId);
  uint16_t expectedFlags = FunctionFlags::HasJitEntryFlags();
  uint16_t unexpectedFlags = 0;

  auto* ins =
      MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFunctionHasNoJitEntry(ObjOperandId funId) {
  MDefinition* fun = getOperand(funId);
  uint16_t expectedFlags = 0;
  uint16_t unexpectedFlags = FunctionFlags::HasJitEntryFlags();

  auto* ins =
      MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
    ObjOperandId funId) {
  MDefinition* fun = getOperand(funId);

  auto* ins = MGuardFunctionIsNonBuiltinCtor::New(alloc(), fun);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId) {
  MDefinition* fun = getOperand(funId);
  uint16_t expectedFlags = FunctionFlags::CONSTRUCTOR;
  uint16_t unexpectedFlags = 0;

  auto* ins =
      MGuardFunctionFlags::New(alloc(), fun, expectedFlags, unexpectedFlags);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId) {
  MDefinition* fun = getOperand(funId);

  auto* ins =
      MGuardFunctionKind::New(alloc(), fun, FunctionFlags::ClassConstructor,
                              /*bailOnEquality=*/true);
  add(ins);

  setOperand(funId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId) {
  MDefinition* array = getOperand(arrayId);

  auto* ins = MGuardArrayIsPacked::New(alloc(), array);
  add(ins);

  setOperand(arrayId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId,
                                                          uint8_t flags) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardArgumentsObjectFlags::New(alloc(), obj, flags);
  add(ins);

  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId,
                                                   ValueType type) {
  switch (type) {
    case ValueType::String:
    case ValueType::Symbol:
    case ValueType::BigInt:
    case ValueType::Int32:
    case ValueType::Boolean:
      return emitGuardTo(inputId, MIRTypeFromValueType(JSValueType(type)));
    case ValueType::Undefined:
      return emitGuardIsUndefined(inputId);
    case ValueType::Null:
      return emitGuardIsNull(inputId);
    case ValueType::Double:
    case ValueType::Magic:
    case ValueType::PrivateGCThing:
    case ValueType::Object:
#ifdef ENABLE_RECORD_TUPLE
    case ValueType::ExtendedPrimitive:
#endif
      break;
  }

  MOZ_CRASH("unexpected type");
}

bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId, MIRType type) {
  MDefinition* def = getOperand(inputId);
  if (def->type() == type) {
    return true;
  }

  auto* ins = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
  add(ins);

  setOperand(inputId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardToObject(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::Object);
}

bool WarpCacheIRTranspiler::emitGuardToString(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::String);
}

bool WarpCacheIRTranspiler::emitGuardToSymbol(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::Symbol);
}

bool WarpCacheIRTranspiler::emitGuardToBigInt(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::BigInt);
}

bool WarpCacheIRTranspiler::emitGuardToBoolean(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::Boolean);
}

bool WarpCacheIRTranspiler::emitGuardToInt32(ValOperandId inputId) {
  return emitGuardTo(inputId, MIRType::Int32);
}

bool WarpCacheIRTranspiler::emitGuardBooleanToInt32(ValOperandId inputId,
                                                    Int32OperandId resultId) {
  if (!emitGuardTo(inputId, MIRType::Boolean)) {
    return false;
  }

  MDefinition* input = getOperand(inputId);
  MOZ_ASSERT(input->type() == MIRType::Boolean);

  auto* ins = MBooleanToInt32::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardIsNumber(ValOperandId inputId) {
  MDefinition* def = getOperand(inputId);

  // No guard needed when the input is already a number type.
  if (IsNumberType(def->type())) {
    return true;
  }

  // MIRType::Double also implies int32 in Ion.
  return emitGuardTo(inputId, MIRType::Double);
}

bool WarpCacheIRTranspiler::emitGuardIsNullOrUndefined(ValOperandId inputId) {
  MDefinition* input = getOperand(inputId);
  if (input->type() == MIRType::Null || input->type() == MIRType::Undefined) {
    return true;
  }

  auto* ins = MGuardNullOrUndefined::New(alloc(), input);
  add(ins);

  setOperand(inputId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId) {
  MDefinition* input = getOperand(inputId);
  if (input->type() == MIRType::Null) {
    return true;
  }

  auto* ins = MGuardValue::New(alloc(), input, NullValue());
  add(ins);
  setOperand(inputId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId) {
  MDefinition* input = getOperand(inputId);
  if (input->type() == MIRType::Undefined) {
    return true;
  }

  auto* ins = MGuardValue::New(alloc(), input, UndefinedValue());
  add(ins);
  setOperand(inputId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MGuardIsExtensible::New(alloc(), obj);
  add(ins);
  setOperand(objId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
    Int32OperandId indexId) {
  MDefinition* index = getOperand(indexId);

  auto* ins = MGuardInt32IsNonNegative::New(alloc(), index);
  add(ins);
  setOperand(indexId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIndexIsNotDenseElement(
    ObjOperandId objId, Int32OperandId indexId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* index = getOperand(indexId);

  auto* ins = MGuardIndexIsNotDenseElement::New(alloc(), obj, index);
  add(ins);
  setOperand(indexId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIndexIsValidUpdateOrAdd(
    ObjOperandId objId, Int32OperandId indexId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* index = getOperand(indexId);

  auto* ins = MGuardIndexIsValidUpdateOrAdd::New(alloc(), obj, index);
  add(ins);
  setOperand(indexId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitCallAddOrUpdateSparseElementHelper(
    ObjOperandId objId, Int32OperandId idId, ValOperandId rhsId, bool strict) {
  MDefinition* obj = getOperand(objId);
  MDefinition* id = getOperand(idId);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MCallAddOrUpdateSparseElement::New(alloc(), obj, id, rhs, strict);
  addEffectful(ins);

  return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitGuardTagNotEqual(ValueTagOperandId lhsId,
                                                 ValueTagOperandId rhsId) {
  MDefinition* lhs = getOperand(lhsId);
  MDefinition* rhs = getOperand(rhsId);

  auto* ins = MGuardTagNotEqual::New(alloc(), lhs, rhs);
  add(ins);

  return true;
}

bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId,
                                                  Int32OperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins =
      MToNumberInt32::New(alloc(), input, IntConversionInputKind::NumbersOnly);

  // ToPropertyKey(-0) is "0", so we can silently convert -0 to 0 here.
  ins->setNeedsNegativeZeroCheck(false);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitTruncateDoubleToUInt32(
    NumberOperandId inputId, Int32OperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins = MTruncateToInt32::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitDoubleToUint8Clamped(NumberOperandId inputId,
                                                     Int32OperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins = MClampToUint8::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardToInt32ModUint32(ValOperandId valId,
                                                      Int32OperandId resultId) {
  MDefinition* input = getOperand(valId);
  auto* ins = MTruncateToInt32::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardToUint8Clamped(ValOperandId valId,
                                                    Int32OperandId resultId) {
  MDefinition* input = getOperand(valId);
  auto* ins = MClampToUint8::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitToString(OperandId inputId,
                                         StringOperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins =
      MToString::New(alloc(), input, MToString::SideEffectHandling::Bailout);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitInt32ToIntPtr(Int32OperandId inputId,
                                              IntPtrOperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins = MInt32ToIntPtr::New(alloc(), input);
  add(ins);
  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitGuardNumberToIntPtrIndex(
    NumberOperandId inputId, bool supportOOB, IntPtrOperandId resultId) {
  MDefinition* input = getOperand(inputId);
  auto* ins = MGuardNumberToIntPtrIndex::New(alloc(), input, supportOOB);
  add(ins);
  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitCallInt32ToString(Int32OperandId inputId,
                                                  StringOperandId resultId) {
  return emitToString(inputId, resultId);
}

bool WarpCacheIRTranspiler::emitCallNumberToString(NumberOperandId inputId,
                                                   StringOperandId resultId) {
  return emitToString(inputId, resultId);
}

bool WarpCacheIRTranspiler::emitInt32ToStringWithBaseResult(
    Int32OperandId inputId, Int32OperandId baseId) {
  MDefinition* input = getOperand(inputId);
  MDefinition* base = getOperand(baseId);

  auto* guardedBase = MGuardInt32Range::New(alloc(), base, 2, 36);
  add(guardedBase);

  // Use lower-case characters by default.
  constexpr bool lower = true;

  auto* ins = MInt32ToStringWithBase::New(alloc(), input, guardedBase, lower);
  add(ins);

  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitBooleanToString(BooleanOperandId inputId,
                                                StringOperandId resultId) {
  return emitToString(inputId, resultId);
}

bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId,
                                                NumberOperandId resultId) {
  MDefinition* input = getOperand(inputId);

  auto* ins = MToDouble::New(alloc(), input);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitStringToAtom(StringOperandId strId) {
  MDefinition* str = getOperand(strId);

  auto* ins = MToHashableString::New(alloc(), str);
  add(ins);

  setOperand(strId, ins);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
  MDefinition* val = getOperand(valId);
  MOZ_ASSERT(val->type() == MIRType::Int32);
  pushResult(val);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId) {
  MDefinition* val = getOperand(valId);
  MOZ_ASSERT(IsNumberType(val->type()));

  if (val->type() != MIRType::Double) {
    auto* ins = MToDouble::New(alloc(), val);
    add(ins);

    val = ins;
  }

  pushResult(val);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId) {
  MDefinition* val = getOperand(valId);
  MOZ_ASSERT(val->type() == MIRType::BigInt);
  pushResult(val);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);
  MOZ_ASSERT(obj->type() == MIRType::Object);
  pushResult(obj);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId) {
  MDefinition* str = getOperand(strId);
  MOZ_ASSERT(str->type() == MIRType::String);
  pushResult(str);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId) {
  MDefinition* sym = getOperand(symId);
  MOZ_ASSERT(sym->type() == MIRType::Symbol);
  pushResult(sym);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
  pushResult(constant(UndefinedValue()));
  return true;
}

bool WarpCacheIRTranspiler::emitLoadBooleanResult(bool val) {
  pushResult(constant(BooleanValue(val)));
  return true;
}

bool WarpCacheIRTranspiler::emitLoadInt32Constant(uint32_t valOffset,
                                                  Int32OperandId resultId) {
  int32_t val = int32StubField(valOffset);
  auto* valConst = constant(Int32Value(val));
  return defineOperand(resultId, valConst);
}

bool WarpCacheIRTranspiler::emitLoadDoubleConstant(uint32_t valOffset,
                                                   NumberOperandId resultId) {
  double val = doubleStubField(valOffset);
  auto* valConst = constant(DoubleValue(val));
  return defineOperand(resultId, valConst);
}

bool WarpCacheIRTranspiler::emitLoadBooleanConstant(bool val,
                                                    BooleanOperandId resultId) {
  auto* valConst = constant(BooleanValue(val));
  return defineOperand(resultId, valConst);
}

bool WarpCacheIRTranspiler::emitLoadUndefined(ValOperandId resultId) {
  auto* valConst = constant(UndefinedValue());
  return defineOperand(resultId, valConst);
}

bool WarpCacheIRTranspiler::emitLoadConstantString(uint32_t strOffset,
                                                   StringOperandId resultId) {
  JSString* val = stringStubField(strOffset);
  auto* valConst = constant(StringValue(val));
  return defineOperand(resultId, valConst);
}

bool WarpCacheIRTranspiler::emitLoadConstantStringResult(uint32_t strOffset) {
  JSString* val = stringStubField(strOffset);
  auto* valConst = constant(StringValue(val));
  pushResult(valConst);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);
  auto* typeOf = MTypeOf::New(alloc(), obj);
  add(typeOf);

  auto* ins = MTypeOfName::New(alloc(), typeOf);
  add(ins);
  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadTypeOfEqObjectResult(
    ObjOperandId objId, TypeofEqOperand operand) {
  MDefinition* obj = getOperand(objId);
  auto* typeOf = MTypeOf::New(alloc(), obj);
  add(typeOf);

  auto* typeInt = MConstant::New(alloc(), Int32Value(operand.type()));
  add(typeInt);

  auto* ins = MCompare::New(alloc(), typeOf, typeInt, operand.compareOp(),
                            MCompare::Compare_Int32);
  add(ins);
  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
    ObjOperandId objId, ObjOperandId resultId) {
  MDefinition* env = getOperand(objId);
  auto* ins = MEnclosingEnvironment::New(alloc(), env);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId,
                                           uint32_t objOffset) {
  MInstruction* ins = objectStubField(objOffset);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadProtoObject(ObjOperandId resultId,
                                                uint32_t objOffset,
                                                ObjOperandId receiverObjId) {
  MInstruction* ins = objectStubField(objOffset);
  if (ins->isConstant()) {
    MDefinition* receiverObj = getOperand(receiverObjId);

    ins = MConstantProto::New(alloc(), ins, receiverObj->skipObjectGuards());
    add(ins);
  }
  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadProto(ObjOperandId objId,
                                          ObjOperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* ins = MObjectStaticProto::New(alloc(), obj);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadInstanceOfObjectResult(
    ValOperandId lhsId, ObjOperandId protoId) {
  MDefinition* lhs = getOperand(lhsId);
  MDefinition* proto = getOperand(protoId);

  auto* instanceOf = MInstanceOf::New(alloc(), lhs, proto);
  addEffectful(instanceOf);

  pushResult(instanceOf);
  return resumeAfter(instanceOf);
}

bool WarpCacheIRTranspiler::emitLoadValueTag(ValOperandId valId,
                                             ValueTagOperandId resultId) {
  MDefinition* val = getOperand(valId);

  auto* ins = MLoadValueTag::New(alloc(), val);
  add(ins);

  return defineOperand(resultId, ins);
}

bool WarpCacheIRTranspiler::emitLoadDynamicSlotResult(ObjOperandId objId,
                                                      uint32_t offsetOffset) {
  int32_t offset = int32StubField(offsetOffset);

  MDefinition* obj = getOperand(objId);
  size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);

  auto* slots = MSlots::New(alloc(), obj);
  add(slots);

  auto* load = MLoadDynamicSlot::New(alloc(), slots, slotIndex);
  add(load);

  pushResult(load);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId,
                                              ObjOperandId objId,
                                              uint32_t offsetOffset) {
  MDefinition* obj = getOperand(objId);

  size_t offset = int32StubField(offsetOffset);
  uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);

  auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
  add(load);

  return defineOperand(resultId, load);
}

bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId,
                                                    uint32_t offsetOffset) {
  int32_t offset = int32StubField(offsetOffset);

  MDefinition* obj = getOperand(objId);
  uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);

  auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
  add(load);

  pushResult(load);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId,
                                                         uint32_t offsetOffset,
                                                         ValueType type) {
  int32_t offset = int32StubField(offsetOffset);

  MDefinition* obj = getOperand(objId);
  uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);

  auto* load = MLoadFixedSlot::New(alloc(), obj, slotIndex);
  load->setResultType(MIRTypeFromValueType(JSValueType(type)));
  add(load);

  pushResult(load);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
    ValOperandId valId) {
  MDefinition* val = getOperand(valId);

  auto* lexicalCheck = MLexicalCheck::New(alloc(), val);
  add(lexicalCheck);

  if (snapshot().bailoutInfo().failedLexicalCheck()) {
    lexicalCheck->setNotMovable();
  }

  setOperand(valId, lexicalCheck);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* elements = MElements::New(alloc(), obj);
  add(elements);

  auto* length = MArrayLength::New(alloc(), elements);
  add(length);

  pushResult(length);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId,
                                                     Int32OperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* elements = MElements::New(alloc(), obj);
  add(elements);

  auto* length = MArrayLength::New(alloc(), elements);
  add(length);

  return defineOperand(resultId, length);
}

bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgResult(
    ObjOperandId objId, Int32OperandId indexId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* index = getOperand(indexId);

  auto* load = MLoadArgumentsObjectArg::New(alloc(), obj, index);
  add(load);

  pushResult(load);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgHoleResult(
    ObjOperandId objId, Int32OperandId indexId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* index = getOperand(indexId);

  auto* load = MLoadArgumentsObjectArgHole::New(alloc(), obj, index);
  add(load);

  pushResult(load);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgExistsResult(
    ObjOperandId objId, Int32OperandId indexId) {
  MDefinition* obj = getOperand(objId);
  MDefinition* index = getOperand(indexId);

  auto* ins = MInArgumentsObjectArg::New(alloc(), obj, index);
  add(ins);

  pushResult(ins);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MArgumentsObjectLength::New(alloc(), obj);
  add(length);

  pushResult(length);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
    ObjOperandId objId, Int32OperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MArgumentsObjectLength::New(alloc(), obj);
  add(length);

  return defineOperand(resultId, length);
}

bool WarpCacheIRTranspiler::emitLoadBoundFunctionNumArgs(
    ObjOperandId objId, Int32OperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* numArgs = MBoundFunctionNumArgs::New(alloc(), obj);
  add(numArgs);

  return defineOperand(resultId, numArgs);
}

bool WarpCacheIRTranspiler::emitLoadBoundFunctionTarget(ObjOperandId objId,
                                                        ObjOperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* target = MLoadFixedSlotAndUnbox::New(
      alloc(), obj, BoundFunctionObject::targetSlot(), MUnbox::Mode::Infallible,
      MIRType::Object);
  add(target);

  return defineOperand(resultId, target);
}

bool WarpCacheIRTranspiler::emitLoadBoundFunctionArgument(
    ObjOperandId objId, uint32_t index, ValOperandId resultId) {
  MDefinition* obj = getOperand(objId);

  auto* boundArgs = MLoadFixedSlotAndUnbox::New(
      alloc(), obj, BoundFunctionObject::firstInlineBoundArgSlot(),
      MUnbox::Mode::Infallible, MIRType::Object);
  add(boundArgs);

  auto* elements = MElements::New(alloc(), boundArgs);
  add(elements);

  auto argIndex = constant(Int32Value(index));
  auto* load = MLoadElement::New(alloc(), elements, argIndex);
  add(load);

  return defineOperand(resultId, load);
}

bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* guard = MGuardBoundFunctionIsConstructor::New(alloc(), obj);
  add(guard);

  setOperand(objId, guard);
  return true;
}

bool WarpCacheIRTranspiler::emitGuardObjectIdentity(ObjOperandId obj1Id,
                                                    ObjOperandId obj2Id) {
  MDefinition* obj1 = getOperand(obj1Id);
  MDefinition* obj2 = getOperand(obj2Id);

  auto* guard = MGuardObjectIdentity::New(alloc(), obj1, obj2,
                                          /* bailOnEquality = */ false);
  add(guard);
  return true;
}

bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
    ObjOperandId objId, uint32_t shapeOffset) {
  MDefinition* obj = getOperand(objId);
  Shape* shape = shapeStubField(shapeOffset);
  MOZ_ASSERT(shape);

  auto* array = MArrayFromArgumentsObject::New(alloc(), obj, shape);
  addEffectful(array);

  pushResult(array);
  return resumeAfter(array);
}

bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MFunctionLength::New(alloc(), obj);
  add(length);

  pushResult(length);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* name = MFunctionName::New(alloc(), obj);
  add(name);

  pushResult(name);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MArrayBufferByteLength::New(alloc(), obj);
  add(length);

  auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
  add(lengthInt32);

  pushResult(lengthInt32);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MArrayBufferByteLength::New(alloc(), obj);
  add(length);

  auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
  add(lengthDouble);

  pushResult(lengthDouble);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthInt32Result(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  // Use a separate instruction for converting the length to Int32, so that we
  // can fold the MArrayBufferViewLength instruction with length instructions
  // added for bounds checks.

  auto* length = MArrayBufferViewLength::New(alloc(), obj);
  add(length);

  auto* lengthInt32 = MNonNegativeIntPtrToInt32::New(alloc(), length);
  add(lengthInt32);

  pushResult(lengthInt32);
  return true;
}

bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
    ObjOperandId objId) {
  MDefinition* obj = getOperand(objId);

  auto* length = MArrayBufferViewLength::New(alloc(), obj);
  add(length);

  auto* lengthDouble = MIntPtrToDouble::New(alloc(), length);
  add(lengthDouble);

  pushResult(lengthDouble);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=98 G=95

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.