summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmBCStkMgmt-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm/WasmBCStkMgmt-inl.h')
-rw-r--r--js/src/wasm/WasmBCStkMgmt-inl.h1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/js/src/wasm/WasmBCStkMgmt-inl.h b/js/src/wasm/WasmBCStkMgmt-inl.h
new file mode 100644
index 0000000000..6ce55cab3a
--- /dev/null
+++ b/js/src/wasm/WasmBCStkMgmt-inl.h
@@ -0,0 +1,1320 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is an INTERNAL header for Wasm baseline compiler: inline methods in the
+// compiler for Stk values and value stack management.
+
+#ifndef wasm_wasm_baseline_stk_mgmt_inl_h
+#define wasm_wasm_baseline_stk_mgmt_inl_h
+
+namespace js {
+namespace wasm {
+
+#ifdef DEBUG
+size_t BaseCompiler::countMemRefsOnStk() {
+ size_t nRefs = 0;
+ for (Stk& v : stk_) {
+ if (v.kind() == Stk::MemRef) {
+ nRefs++;
+ }
+ }
+ return nRefs;
+}
+#endif
+
+template <typename T>
+void BaseCompiler::push(T item) {
+ // None of the single-arg Stk constructors create a Stk::MemRef, so
+ // there's no need to increment stackMapGenerator_.memRefsOnStk here.
+ stk_.infallibleEmplaceBack(Stk(item));
+}
+
+void BaseCompiler::pushConstRef(intptr_t v) {
+ stk_.infallibleEmplaceBack(Stk::StkRef(v));
+}
+
+void BaseCompiler::loadConstI32(const Stk& src, RegI32 dest) {
+ moveImm32(src.i32val(), dest);
+}
+
+void BaseCompiler::loadMemI32(const Stk& src, RegI32 dest) {
+ fr.loadStackI32(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalI32(const Stk& src, RegI32 dest) {
+ fr.loadLocalI32(localFromSlot(src.slot(), MIRType::Int32), dest);
+}
+
+void BaseCompiler::loadRegisterI32(const Stk& src, RegI32 dest) {
+ moveI32(src.i32reg(), dest);
+}
+
+void BaseCompiler::loadConstI64(const Stk& src, RegI64 dest) {
+ moveImm64(src.i64val(), dest);
+}
+
+void BaseCompiler::loadMemI64(const Stk& src, RegI64 dest) {
+ fr.loadStackI64(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalI64(const Stk& src, RegI64 dest) {
+ fr.loadLocalI64(localFromSlot(src.slot(), MIRType::Int64), dest);
+}
+
+void BaseCompiler::loadRegisterI64(const Stk& src, RegI64 dest) {
+ moveI64(src.i64reg(), dest);
+}
+
+void BaseCompiler::loadConstRef(const Stk& src, RegRef dest) {
+ moveImmRef(src.refval(), dest);
+}
+
+void BaseCompiler::loadMemRef(const Stk& src, RegRef dest) {
+ fr.loadStackRef(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalRef(const Stk& src, RegRef dest) {
+ fr.loadLocalRef(localFromSlot(src.slot(), MIRType::RefOrNull), dest);
+}
+
+void BaseCompiler::loadRegisterRef(const Stk& src, RegRef dest) {
+ moveRef(src.refReg(), dest);
+}
+
+void BaseCompiler::loadConstF64(const Stk& src, RegF64 dest) {
+ double d;
+ src.f64val(&d);
+ masm.loadConstantDouble(d, dest);
+}
+
+void BaseCompiler::loadMemF64(const Stk& src, RegF64 dest) {
+ fr.loadStackF64(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalF64(const Stk& src, RegF64 dest) {
+ fr.loadLocalF64(localFromSlot(src.slot(), MIRType::Double), dest);
+}
+
+void BaseCompiler::loadRegisterF64(const Stk& src, RegF64 dest) {
+ moveF64(src.f64reg(), dest);
+}
+
+void BaseCompiler::loadConstF32(const Stk& src, RegF32 dest) {
+ float f;
+ src.f32val(&f);
+ masm.loadConstantFloat32(f, dest);
+}
+
+void BaseCompiler::loadMemF32(const Stk& src, RegF32 dest) {
+ fr.loadStackF32(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalF32(const Stk& src, RegF32 dest) {
+ fr.loadLocalF32(localFromSlot(src.slot(), MIRType::Float32), dest);
+}
+
+void BaseCompiler::loadRegisterF32(const Stk& src, RegF32 dest) {
+ moveF32(src.f32reg(), dest);
+}
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::loadConstV128(const Stk& src, RegV128 dest) {
+ V128 f;
+ src.v128val(&f);
+ masm.loadConstantSimd128(SimdConstant::CreateX16((int8_t*)f.bytes), dest);
+}
+
+void BaseCompiler::loadMemV128(const Stk& src, RegV128 dest) {
+ fr.loadStackV128(src.offs(), dest);
+}
+
+void BaseCompiler::loadLocalV128(const Stk& src, RegV128 dest) {
+ fr.loadLocalV128(localFromSlot(src.slot(), MIRType::Simd128), dest);
+}
+
+void BaseCompiler::loadRegisterV128(const Stk& src, RegV128 dest) {
+ moveV128(src.v128reg(), dest);
+}
+#endif
+
+void BaseCompiler::loadI32(const Stk& src, RegI32 dest) {
+ switch (src.kind()) {
+ case Stk::ConstI32:
+ loadConstI32(src, dest);
+ break;
+ case Stk::MemI32:
+ loadMemI32(src, dest);
+ break;
+ case Stk::LocalI32:
+ loadLocalI32(src, dest);
+ break;
+ case Stk::RegisterI32:
+ loadRegisterI32(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: Expected I32 on stack");
+ }
+}
+
+void BaseCompiler::loadI64(const Stk& src, RegI64 dest) {
+ switch (src.kind()) {
+ case Stk::ConstI64:
+ loadConstI64(src, dest);
+ break;
+ case Stk::MemI64:
+ loadMemI64(src, dest);
+ break;
+ case Stk::LocalI64:
+ loadLocalI64(src, dest);
+ break;
+ case Stk::RegisterI64:
+ loadRegisterI64(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: Expected I64 on stack");
+ }
+}
+
+#if !defined(JS_PUNBOX64)
+void BaseCompiler::loadI64Low(const Stk& src, RegI32 dest) {
+ switch (src.kind()) {
+ case Stk::ConstI64:
+ moveImm32(int32_t(src.i64val()), dest);
+ break;
+ case Stk::MemI64:
+ fr.loadStackI64Low(src.offs(), dest);
+ break;
+ case Stk::LocalI64:
+ fr.loadLocalI64Low(localFromSlot(src.slot(), MIRType::Int64), dest);
+ break;
+ case Stk::RegisterI64:
+ moveI32(RegI32(src.i64reg().low), dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: Expected I64 on stack");
+ }
+}
+
+void BaseCompiler::loadI64High(const Stk& src, RegI32 dest) {
+ switch (src.kind()) {
+ case Stk::ConstI64:
+ moveImm32(int32_t(src.i64val() >> 32), dest);
+ break;
+ case Stk::MemI64:
+ fr.loadStackI64High(src.offs(), dest);
+ break;
+ case Stk::LocalI64:
+ fr.loadLocalI64High(localFromSlot(src.slot(), MIRType::Int64), dest);
+ break;
+ case Stk::RegisterI64:
+ moveI32(RegI32(src.i64reg().high), dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: Expected I64 on stack");
+ }
+}
+#endif
+
+void BaseCompiler::loadF64(const Stk& src, RegF64 dest) {
+ switch (src.kind()) {
+ case Stk::ConstF64:
+ loadConstF64(src, dest);
+ break;
+ case Stk::MemF64:
+ loadMemF64(src, dest);
+ break;
+ case Stk::LocalF64:
+ loadLocalF64(src, dest);
+ break;
+ case Stk::RegisterF64:
+ loadRegisterF64(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected F64 on stack");
+ }
+}
+
+void BaseCompiler::loadF32(const Stk& src, RegF32 dest) {
+ switch (src.kind()) {
+ case Stk::ConstF32:
+ loadConstF32(src, dest);
+ break;
+ case Stk::MemF32:
+ loadMemF32(src, dest);
+ break;
+ case Stk::LocalF32:
+ loadLocalF32(src, dest);
+ break;
+ case Stk::RegisterF32:
+ loadRegisterF32(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected F32 on stack");
+ }
+}
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::loadV128(const Stk& src, RegV128 dest) {
+ switch (src.kind()) {
+ case Stk::ConstV128:
+ loadConstV128(src, dest);
+ break;
+ case Stk::MemV128:
+ loadMemV128(src, dest);
+ break;
+ case Stk::LocalV128:
+ loadLocalV128(src, dest);
+ break;
+ case Stk::RegisterV128:
+ loadRegisterV128(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected V128 on stack");
+ }
+}
+#endif
+
+void BaseCompiler::loadRef(const Stk& src, RegRef dest) {
+ switch (src.kind()) {
+ case Stk::ConstRef:
+ loadConstRef(src, dest);
+ break;
+ case Stk::MemRef:
+ loadMemRef(src, dest);
+ break;
+ case Stk::LocalRef:
+ loadLocalRef(src, dest);
+ break;
+ case Stk::RegisterRef:
+ loadRegisterRef(src, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected ref on stack");
+ }
+}
+
+void BaseCompiler::peekRefAt(uint32_t depth, RegRef dest) {
+ MOZ_ASSERT(depth < stk_.length());
+ Stk& src = peek(stk_.length() - depth - 1);
+ loadRef(src, dest);
+}
+
+// Flush all local and register value stack elements to memory.
+//
+// TODO / OPTIMIZE: As this is fairly expensive and causes worse
+// code to be emitted subsequently, it is useful to avoid calling
+// it. (Bug 1316802)
+//
+// Some optimization has been done already. Remaining
+// opportunities:
+//
+// - It would be interesting to see if we can specialize it
+// before calls with particularly simple signatures, or where
+// we can do parallel assignment of register arguments, or
+// similar. See notes in emitCall().
+//
+// - Operations that need specific registers: multiply, quotient,
+// remainder, will tend to sync because the registers we need
+// will tend to be allocated. We may be able to avoid that by
+// prioritizing registers differently (takeLast instead of
+// takeFirst) but we may also be able to allocate an unused
+// register on demand to free up one we need, thus avoiding the
+// sync. That type of fix would go into needI32().
+
+void BaseCompiler::sync() {
+ size_t start = 0;
+ size_t lim = stk_.length();
+
+ for (size_t i = lim; i > 0; i--) {
+ // Memory opcodes are first in the enum, single check against MemLast is
+ // fine.
+ if (stk_[i - 1].kind() <= Stk::MemLast) {
+ start = i;
+ break;
+ }
+ }
+
+ for (size_t i = start; i < lim; i++) {
+ Stk& v = stk_[i];
+ switch (v.kind()) {
+ case Stk::LocalI32: {
+ ScratchI32 scratch(*this);
+ loadLocalI32(v, scratch);
+ uint32_t offs = fr.pushGPR(scratch);
+ v.setOffs(Stk::MemI32, offs);
+ break;
+ }
+ case Stk::RegisterI32: {
+ uint32_t offs = fr.pushGPR(v.i32reg());
+ freeI32(v.i32reg());
+ v.setOffs(Stk::MemI32, offs);
+ break;
+ }
+ case Stk::LocalI64: {
+ ScratchI32 scratch(*this);
+#ifdef JS_PUNBOX64
+ loadI64(v, fromI32(scratch));
+ uint32_t offs = fr.pushGPR(scratch);
+#else
+ fr.loadLocalI64High(localFromSlot(v.slot(), MIRType::Int64), scratch);
+ fr.pushGPR(scratch);
+ fr.loadLocalI64Low(localFromSlot(v.slot(), MIRType::Int64), scratch);
+ uint32_t offs = fr.pushGPR(scratch);
+#endif
+ v.setOffs(Stk::MemI64, offs);
+ break;
+ }
+ case Stk::RegisterI64: {
+#ifdef JS_PUNBOX64
+ uint32_t offs = fr.pushGPR(v.i64reg().reg);
+ freeI64(v.i64reg());
+#else
+ fr.pushGPR(v.i64reg().high);
+ uint32_t offs = fr.pushGPR(v.i64reg().low);
+ freeI64(v.i64reg());
+#endif
+ v.setOffs(Stk::MemI64, offs);
+ break;
+ }
+ case Stk::LocalF64: {
+ ScratchF64 scratch(*this);
+ loadF64(v, scratch);
+ uint32_t offs = fr.pushDouble(scratch);
+ v.setOffs(Stk::MemF64, offs);
+ break;
+ }
+ case Stk::RegisterF64: {
+ uint32_t offs = fr.pushDouble(v.f64reg());
+ freeF64(v.f64reg());
+ v.setOffs(Stk::MemF64, offs);
+ break;
+ }
+ case Stk::LocalF32: {
+ ScratchF32 scratch(*this);
+ loadF32(v, scratch);
+ uint32_t offs = fr.pushFloat32(scratch);
+ v.setOffs(Stk::MemF32, offs);
+ break;
+ }
+ case Stk::RegisterF32: {
+ uint32_t offs = fr.pushFloat32(v.f32reg());
+ freeF32(v.f32reg());
+ v.setOffs(Stk::MemF32, offs);
+ break;
+ }
+#ifdef ENABLE_WASM_SIMD
+ case Stk::LocalV128: {
+ ScratchV128 scratch(*this);
+ loadV128(v, scratch);
+ uint32_t offs = fr.pushV128(scratch);
+ v.setOffs(Stk::MemV128, offs);
+ break;
+ }
+ case Stk::RegisterV128: {
+ uint32_t offs = fr.pushV128(v.v128reg());
+ freeV128(v.v128reg());
+ v.setOffs(Stk::MemV128, offs);
+ break;
+ }
+#endif
+ case Stk::LocalRef: {
+ ScratchRef scratch(*this);
+ loadLocalRef(v, scratch);
+ uint32_t offs = fr.pushGPR(scratch);
+ v.setOffs(Stk::MemRef, offs);
+ stackMapGenerator_.memRefsOnStk++;
+ break;
+ }
+ case Stk::RegisterRef: {
+ uint32_t offs = fr.pushGPR(v.refReg());
+ freeRef(v.refReg());
+ v.setOffs(Stk::MemRef, offs);
+ stackMapGenerator_.memRefsOnStk++;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+}
+
+// This is an optimization used to avoid calling sync() for
+// setLocal(): if the local does not exist unresolved on the stack
+// then we can skip the sync.
+
+bool BaseCompiler::hasLocal(uint32_t slot) {
+ for (size_t i = stk_.length(); i > 0; i--) {
+ // Memory opcodes are first in the enum, single check against MemLast is
+ // fine.
+ Stk::Kind kind = stk_[i - 1].kind();
+ if (kind <= Stk::MemLast) {
+ return false;
+ }
+
+ // Local opcodes follow memory opcodes in the enum, single check against
+ // LocalLast is sufficient.
+ if (kind <= Stk::LocalLast && stk_[i - 1].slot() == slot) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void BaseCompiler::syncLocal(uint32_t slot) {
+ if (hasLocal(slot)) {
+ sync(); // TODO / OPTIMIZE: Improve this? (Bug 1316817)
+ }
+}
+
+// Push the register r onto the stack.
+
+void BaseCompiler::pushAny(AnyReg r) {
+ switch (r.tag) {
+ case AnyReg::I32: {
+ pushI32(r.i32());
+ break;
+ }
+ case AnyReg::I64: {
+ pushI64(r.i64());
+ break;
+ }
+ case AnyReg::F32: {
+ pushF32(r.f32());
+ break;
+ }
+ case AnyReg::F64: {
+ pushF64(r.f64());
+ break;
+ }
+#ifdef ENABLE_WASM_SIMD
+ case AnyReg::V128: {
+ pushV128(r.v128());
+ break;
+ }
+#endif
+ case AnyReg::REF: {
+ pushRef(r.ref());
+ break;
+ }
+ }
+}
+
+void BaseCompiler::pushI32(RegI32 r) {
+ MOZ_ASSERT(!isAvailableI32(r));
+ push(Stk(r));
+}
+
+void BaseCompiler::pushI64(RegI64 r) {
+ MOZ_ASSERT(!isAvailableI64(r));
+ push(Stk(r));
+}
+
+void BaseCompiler::pushRef(RegRef r) {
+ MOZ_ASSERT(!isAvailableRef(r));
+ push(Stk(r));
+}
+
+void BaseCompiler::pushPtr(RegPtr r) {
+ MOZ_ASSERT(!isAvailablePtr(r));
+#ifdef JS_64BIT
+ pushI64(RegI64(Register64(r)));
+#else
+ pushI32(RegI32(r));
+#endif
+}
+
+void BaseCompiler::pushF64(RegF64 r) {
+ MOZ_ASSERT(!isAvailableF64(r));
+ push(Stk(r));
+}
+
+void BaseCompiler::pushF32(RegF32 r) {
+ MOZ_ASSERT(!isAvailableF32(r));
+ push(Stk(r));
+}
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::pushV128(RegV128 r) {
+ MOZ_ASSERT(!isAvailableV128(r));
+ push(Stk(r));
+}
+#endif
+
+// Push the value onto the stack. PushI32 can also take uint32_t, and PushI64
+// can take uint64_t; the semantics are the same. Appropriate sign extension
+// for a 32-bit value on a 64-bit architecture happens when the value is
+// popped, see the definition of moveImm32 below.
+
+void BaseCompiler::pushI32(int32_t v) { push(Stk(v)); }
+
+void BaseCompiler::pushI64(int64_t v) { push(Stk(v)); }
+
+void BaseCompiler::pushRef(intptr_t v) { pushConstRef(v); }
+
+void BaseCompiler::pushPtr(intptr_t v) {
+#ifdef JS_64BIT
+ pushI64(v);
+#else
+ pushI32(v);
+#endif
+}
+
+void BaseCompiler::pushF64(double v) { push(Stk(v)); }
+
+void BaseCompiler::pushF32(float v) { push(Stk(v)); }
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::pushV128(V128 v) { push(Stk(v)); }
+#endif
+
+// Push the local slot onto the stack. The slot will not be read
+// here; it will be read when it is consumed, or when a side
+// effect to the slot forces its value to be saved.
+
+void BaseCompiler::pushLocalI32(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalI32, slot));
+}
+
+void BaseCompiler::pushLocalI64(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalI64, slot));
+}
+
+void BaseCompiler::pushLocalRef(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalRef, slot));
+}
+
+void BaseCompiler::pushLocalF64(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalF64, slot));
+}
+
+void BaseCompiler::pushLocalF32(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalF32, slot));
+}
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::pushLocalV128(uint32_t slot) {
+ stk_.infallibleEmplaceBack(Stk(Stk::LocalV128, slot));
+}
+#endif
+
+void BaseCompiler::pushU32AsI64(RegI32 rs) {
+ RegI64 rd = widenI32(rs);
+ masm.move32To64ZeroExtend(rs, rd);
+ pushI64(rd);
+}
+
+AnyReg BaseCompiler::popAny(AnyReg specific) {
+ switch (stk_.back().kind()) {
+ case Stk::MemI32:
+ case Stk::LocalI32:
+ case Stk::RegisterI32:
+ case Stk::ConstI32:
+ return AnyReg(popI32(specific.i32()));
+
+ case Stk::MemI64:
+ case Stk::LocalI64:
+ case Stk::RegisterI64:
+ case Stk::ConstI64:
+ return AnyReg(popI64(specific.i64()));
+
+ case Stk::MemF32:
+ case Stk::LocalF32:
+ case Stk::RegisterF32:
+ case Stk::ConstF32:
+ return AnyReg(popF32(specific.f32()));
+
+ case Stk::MemF64:
+ case Stk::LocalF64:
+ case Stk::RegisterF64:
+ case Stk::ConstF64:
+ return AnyReg(popF64(specific.f64()));
+
+#ifdef ENABLE_WASM_SIMD
+ case Stk::MemV128:
+ case Stk::LocalV128:
+ case Stk::RegisterV128:
+ case Stk::ConstV128:
+ return AnyReg(popV128(specific.v128()));
+#endif
+
+ case Stk::MemRef:
+ case Stk::LocalRef:
+ case Stk::RegisterRef:
+ case Stk::ConstRef:
+ return AnyReg(popRef(specific.ref()));
+
+ case Stk::Unknown:
+ MOZ_CRASH();
+
+ default:
+ MOZ_CRASH();
+ }
+}
+
+AnyReg BaseCompiler::popAny() {
+ switch (stk_.back().kind()) {
+ case Stk::MemI32:
+ case Stk::LocalI32:
+ case Stk::RegisterI32:
+ case Stk::ConstI32:
+ return AnyReg(popI32());
+
+ case Stk::MemI64:
+ case Stk::LocalI64:
+ case Stk::RegisterI64:
+ case Stk::ConstI64:
+ return AnyReg(popI64());
+
+ case Stk::MemF32:
+ case Stk::LocalF32:
+ case Stk::RegisterF32:
+ case Stk::ConstF32:
+ return AnyReg(popF32());
+
+ case Stk::MemF64:
+ case Stk::LocalF64:
+ case Stk::RegisterF64:
+ case Stk::ConstF64:
+ return AnyReg(popF64());
+
+#ifdef ENABLE_WASM_SIMD
+ case Stk::MemV128:
+ case Stk::LocalV128:
+ case Stk::RegisterV128:
+ case Stk::ConstV128:
+ return AnyReg(popV128());
+#endif
+
+ case Stk::MemRef:
+ case Stk::LocalRef:
+ case Stk::RegisterRef:
+ case Stk::ConstRef:
+ return AnyReg(popRef());
+
+ case Stk::Unknown:
+ MOZ_CRASH();
+
+ default:
+ MOZ_CRASH();
+ }
+}
+
+// Call only from other popI32() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popI32(const Stk& v, RegI32 dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstI32:
+ loadConstI32(v, dest);
+ break;
+ case Stk::LocalI32:
+ loadLocalI32(v, dest);
+ break;
+ case Stk::MemI32:
+ fr.popGPR(dest);
+ break;
+ case Stk::RegisterI32:
+ loadRegisterI32(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected int on stack");
+ }
+}
+
+RegI32 BaseCompiler::popI32() {
+ Stk& v = stk_.back();
+ RegI32 r;
+ if (v.kind() == Stk::RegisterI32) {
+ r = v.i32reg();
+ } else {
+ popI32(v, (r = needI32()));
+ }
+ stk_.popBack();
+ return r;
+}
+
+RegI32 BaseCompiler::popI32(RegI32 specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterI32 && v.i32reg() == specific)) {
+ needI32(specific);
+ popI32(v, specific);
+ if (v.kind() == Stk::RegisterI32) {
+ freeI32(v.i32reg());
+ }
+ }
+
+ stk_.popBack();
+ return specific;
+}
+
+#ifdef ENABLE_WASM_SIMD
+// Call only from other popV128() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popV128(const Stk& v, RegV128 dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstV128:
+ loadConstV128(v, dest);
+ break;
+ case Stk::LocalV128:
+ loadLocalV128(v, dest);
+ break;
+ case Stk::MemV128:
+ fr.popV128(dest);
+ break;
+ case Stk::RegisterV128:
+ loadRegisterV128(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected int on stack");
+ }
+}
+
+RegV128 BaseCompiler::popV128() {
+ Stk& v = stk_.back();
+ RegV128 r;
+ if (v.kind() == Stk::RegisterV128) {
+ r = v.v128reg();
+ } else {
+ popV128(v, (r = needV128()));
+ }
+ stk_.popBack();
+ return r;
+}
+
+RegV128 BaseCompiler::popV128(RegV128 specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterV128 && v.v128reg() == specific)) {
+ needV128(specific);
+ popV128(v, specific);
+ if (v.kind() == Stk::RegisterV128) {
+ freeV128(v.v128reg());
+ }
+ }
+
+ stk_.popBack();
+ return specific;
+}
+#endif
+
+// Call only from other popI64() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popI64(const Stk& v, RegI64 dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstI64:
+ loadConstI64(v, dest);
+ break;
+ case Stk::LocalI64:
+ loadLocalI64(v, dest);
+ break;
+ case Stk::MemI64:
+#ifdef JS_PUNBOX64
+ fr.popGPR(dest.reg);
+#else
+ fr.popGPR(dest.low);
+ fr.popGPR(dest.high);
+#endif
+ break;
+ case Stk::RegisterI64:
+ loadRegisterI64(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected long on stack");
+ }
+}
+
+RegI64 BaseCompiler::popI64() {
+ Stk& v = stk_.back();
+ RegI64 r;
+ if (v.kind() == Stk::RegisterI64) {
+ r = v.i64reg();
+ } else {
+ popI64(v, (r = needI64()));
+ }
+ stk_.popBack();
+ return r;
+}
+
+// Note, the stack top can be in one half of "specific" on 32-bit
+// systems. We can optimize, but for simplicity, if the register
+// does not match exactly, then just force the stack top to memory
+// and then read it back in.
+
+RegI64 BaseCompiler::popI64(RegI64 specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterI64 && v.i64reg() == specific)) {
+ needI64(specific);
+ popI64(v, specific);
+ if (v.kind() == Stk::RegisterI64) {
+ freeI64(v.i64reg());
+ }
+ }
+
+ stk_.popBack();
+ return specific;
+}
+
+// Call only from other popRef() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popRef(const Stk& v, RegRef dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstRef:
+ loadConstRef(v, dest);
+ break;
+ case Stk::LocalRef:
+ loadLocalRef(v, dest);
+ break;
+ case Stk::MemRef:
+ fr.popGPR(dest);
+ break;
+ case Stk::RegisterRef:
+ loadRegisterRef(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected ref on stack");
+ }
+}
+
+RegRef BaseCompiler::popRef(RegRef specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterRef && v.refReg() == specific)) {
+ needRef(specific);
+ popRef(v, specific);
+ if (v.kind() == Stk::RegisterRef) {
+ freeRef(v.refReg());
+ }
+ }
+
+ stk_.popBack();
+ if (v.kind() == Stk::MemRef) {
+ stackMapGenerator_.memRefsOnStk--;
+ }
+ return specific;
+}
+
+RegRef BaseCompiler::popRef() {
+ Stk& v = stk_.back();
+ RegRef r;
+ if (v.kind() == Stk::RegisterRef) {
+ r = v.refReg();
+ } else {
+ popRef(v, (r = needRef()));
+ }
+ stk_.popBack();
+ if (v.kind() == Stk::MemRef) {
+ stackMapGenerator_.memRefsOnStk--;
+ }
+ return r;
+}
+
+// Call only from other popPtr() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popPtr(const Stk& v, RegPtr dest) {
+#ifdef JS_64BIT
+ popI64(v, RegI64(Register64(dest)));
+#else
+ popI32(v, RegI32(dest));
+#endif
+}
+
+RegPtr BaseCompiler::popPtr(RegPtr specific) {
+#ifdef JS_64BIT
+ return RegPtr(popI64(RegI64(Register64(specific))).reg);
+#else
+ return RegPtr(popI32(RegI32(specific)));
+#endif
+}
+
+RegPtr BaseCompiler::popPtr() {
+#ifdef JS_64BIT
+ return RegPtr(popI64().reg);
+#else
+ return RegPtr(popI32());
+#endif
+}
+
+// Call only from other popF64() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popF64(const Stk& v, RegF64 dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstF64:
+ loadConstF64(v, dest);
+ break;
+ case Stk::LocalF64:
+ loadLocalF64(v, dest);
+ break;
+ case Stk::MemF64:
+ fr.popDouble(dest);
+ break;
+ case Stk::RegisterF64:
+ loadRegisterF64(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected double on stack");
+ }
+}
+
+RegF64 BaseCompiler::popF64() {
+ Stk& v = stk_.back();
+ RegF64 r;
+ if (v.kind() == Stk::RegisterF64) {
+ r = v.f64reg();
+ } else {
+ popF64(v, (r = needF64()));
+ }
+ stk_.popBack();
+ return r;
+}
+
+RegF64 BaseCompiler::popF64(RegF64 specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterF64 && v.f64reg() == specific)) {
+ needF64(specific);
+ popF64(v, specific);
+ if (v.kind() == Stk::RegisterF64) {
+ freeF64(v.f64reg());
+ }
+ }
+
+ stk_.popBack();
+ return specific;
+}
+
+// Call only from other popF32() variants.
+// v must be the stack top. May pop the CPU stack.
+
+void BaseCompiler::popF32(const Stk& v, RegF32 dest) {
+ MOZ_ASSERT(&v == &stk_.back());
+ switch (v.kind()) {
+ case Stk::ConstF32:
+ loadConstF32(v, dest);
+ break;
+ case Stk::LocalF32:
+ loadLocalF32(v, dest);
+ break;
+ case Stk::MemF32:
+ fr.popFloat32(dest);
+ break;
+ case Stk::RegisterF32:
+ loadRegisterF32(v, dest);
+ break;
+ default:
+ MOZ_CRASH("Compiler bug: expected float on stack");
+ }
+}
+
+RegF32 BaseCompiler::popF32() {
+ Stk& v = stk_.back();
+ RegF32 r;
+ if (v.kind() == Stk::RegisterF32) {
+ r = v.f32reg();
+ } else {
+ popF32(v, (r = needF32()));
+ }
+ stk_.popBack();
+ return r;
+}
+
+RegF32 BaseCompiler::popF32(RegF32 specific) {
+ Stk& v = stk_.back();
+
+ if (!(v.kind() == Stk::RegisterF32 && v.f32reg() == specific)) {
+ needF32(specific);
+ popF32(v, specific);
+ if (v.kind() == Stk::RegisterF32) {
+ freeF32(v.f32reg());
+ }
+ }
+
+ stk_.popBack();
+ return specific;
+}
+
+bool BaseCompiler::hasConst() const {
+ const Stk& v = stk_.back();
+ switch (v.kind()) {
+ case Stk::ConstI32:
+ case Stk::ConstI64:
+ case Stk::ConstF32:
+ case Stk::ConstF64:
+#ifdef ENABLE_WASM_SIMD
+ case Stk::ConstV128:
+#endif
+ case Stk::ConstRef:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool BaseCompiler::popConst(int32_t* c) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI32) {
+ return false;
+ }
+ *c = v.i32val();
+ stk_.popBack();
+ return true;
+}
+
+bool BaseCompiler::popConst(int64_t* c) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI64) {
+ return false;
+ }
+ *c = v.i64val();
+ stk_.popBack();
+ return true;
+}
+
+bool BaseCompiler::peekConst(int32_t* c) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI32) {
+ return false;
+ }
+ *c = v.i32val();
+ return true;
+}
+
+bool BaseCompiler::peekConst(int64_t* c) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI64) {
+ return false;
+ }
+ *c = v.i64val();
+ return true;
+}
+
+bool BaseCompiler::peek2xConst(int32_t* c0, int32_t* c1) {
+ MOZ_ASSERT(stk_.length() >= 2);
+ const Stk& v0 = *(stk_.end() - 1);
+ const Stk& v1 = *(stk_.end() - 2);
+ if (v0.kind() != Stk::ConstI32 || v1.kind() != Stk::ConstI32) {
+ return false;
+ }
+ *c0 = v0.i32val();
+ *c1 = v1.i32val();
+ return true;
+}
+
+bool BaseCompiler::popConstPositivePowerOfTwo(int32_t* c, uint_fast8_t* power,
+ int32_t cutoff) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI32) {
+ return false;
+ }
+ *c = v.i32val();
+ if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint32_t>(*c))) {
+ return false;
+ }
+ *power = FloorLog2(*c);
+ stk_.popBack();
+ return true;
+}
+
+bool BaseCompiler::popConstPositivePowerOfTwo(int64_t* c, uint_fast8_t* power,
+ int64_t cutoff) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::ConstI64) {
+ return false;
+ }
+ *c = v.i64val();
+ if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(*c))) {
+ return false;
+ }
+ *power = FloorLog2(*c);
+ stk_.popBack();
+ return true;
+}
+
+void BaseCompiler::pop2xI32(RegI32* r0, RegI32* r1) {
+ *r1 = popI32();
+ *r0 = popI32();
+}
+
+void BaseCompiler::pop2xI64(RegI64* r0, RegI64* r1) {
+ *r1 = popI64();
+ *r0 = popI64();
+}
+
+void BaseCompiler::pop2xF32(RegF32* r0, RegF32* r1) {
+ *r1 = popF32();
+ *r0 = popF32();
+}
+
+void BaseCompiler::pop2xF64(RegF64* r0, RegF64* r1) {
+ *r1 = popF64();
+ *r0 = popF64();
+}
+
+#ifdef ENABLE_WASM_SIMD
+void BaseCompiler::pop2xV128(RegV128* r0, RegV128* r1) {
+ *r1 = popV128();
+ *r0 = popV128();
+}
+#endif
+
+void BaseCompiler::pop2xRef(RegRef* r0, RegRef* r1) {
+ *r1 = popRef();
+ *r0 = popRef();
+}
+
+// Pop to a specific register
+RegI32 BaseCompiler::popI32ToSpecific(RegI32 specific) {
+ freeI32(specific);
+ return popI32(specific);
+}
+
+RegI64 BaseCompiler::popI64ToSpecific(RegI64 specific) {
+ freeI64(specific);
+ return popI64(specific);
+}
+
+#ifdef JS_CODEGEN_ARM
+// Pop an I64 as a valid register pair.
+RegI64 BaseCompiler::popI64Pair() {
+ RegI64 r = needI64Pair();
+ popI64ToSpecific(r);
+ return r;
+}
+#endif
+
+// Pop an I64 but narrow it and return the narrowed part.
+RegI32 BaseCompiler::popI64ToI32() {
+ RegI64 r = popI64();
+ return narrowI64(r);
+}
+
+RegI32 BaseCompiler::popI64ToSpecificI32(RegI32 specific) {
+ RegI64 rd = widenI32(specific);
+ popI64ToSpecific(rd);
+ return narrowI64(rd);
+}
+
+bool BaseCompiler::peekLocal(uint32_t* local) {
+ Stk& v = stk_.back();
+ // See hasLocal() for documentation of this logic.
+ if (v.kind() <= Stk::MemLast || v.kind() > Stk::LocalLast) {
+ return false;
+ }
+ *local = v.slot();
+ return true;
+}
+
+size_t BaseCompiler::stackConsumed(size_t numval) {
+ size_t size = 0;
+ MOZ_ASSERT(numval <= stk_.length());
+ for (uint32_t i = stk_.length() - 1; numval > 0; numval--, i--) {
+ Stk& v = stk_[i];
+ switch (v.kind()) {
+ case Stk::MemRef:
+ size += BaseStackFrame::StackSizeOfPtr;
+ break;
+ case Stk::MemI32:
+ size += BaseStackFrame::StackSizeOfPtr;
+ break;
+ case Stk::MemI64:
+ size += BaseStackFrame::StackSizeOfInt64;
+ break;
+ case Stk::MemF64:
+ size += BaseStackFrame::StackSizeOfDouble;
+ break;
+ case Stk::MemF32:
+ size += BaseStackFrame::StackSizeOfFloat;
+ break;
+#ifdef ENABLE_WASM_SIMD
+ case Stk::MemV128:
+ size += BaseStackFrame::StackSizeOfV128;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ return size;
+}
+
+void BaseCompiler::popValueStackTo(uint32_t stackSize) {
+ for (uint32_t i = stk_.length(); i > stackSize; i--) {
+ Stk& v = stk_[i - 1];
+ switch (v.kind()) {
+ case Stk::RegisterI32:
+ freeI32(v.i32reg());
+ break;
+ case Stk::RegisterI64:
+ freeI64(v.i64reg());
+ break;
+ case Stk::RegisterF64:
+ freeF64(v.f64reg());
+ break;
+ case Stk::RegisterF32:
+ freeF32(v.f32reg());
+ break;
+#ifdef ENABLE_WASM_SIMD
+ case Stk::RegisterV128:
+ freeV128(v.v128reg());
+ break;
+#endif
+ case Stk::RegisterRef:
+ freeRef(v.refReg());
+ break;
+ case Stk::MemRef:
+ stackMapGenerator_.memRefsOnStk--;
+ break;
+ default:
+ break;
+ }
+ }
+ stk_.shrinkTo(stackSize);
+}
+
+void BaseCompiler::popValueStackBy(uint32_t items) {
+ popValueStackTo(stk_.length() - items);
+}
+
+void BaseCompiler::dropValue() {
+ if (peek(0).isMem()) {
+ fr.popBytes(stackConsumed(1));
+ }
+ popValueStackBy(1);
+}
+
+// Peek at the stack, for calls.
+
+Stk& BaseCompiler::peek(uint32_t relativeDepth) {
+ return stk_[stk_.length() - 1 - relativeDepth];
+}
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_wasm_baseline_stk_mgmt_inl_h