#ifdef DEBUG
size_t BaseCompiler::countMemRefsOnStk() {
size_t nRefs = 0; for (Stk& v : stk_) { if (v.kind() == Stk::MemRef) {
nRefs++;
}
} return nRefs;
}
bool BaseCompiler::hasLiveRegsOnStk() { for (Stk& v : stk_) { if (v.isReg()) { returntrue;
}
} returnfalse;
} #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));
}
// 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().
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;
}
}
// 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) { returnfalse;
}
// Local opcodes follow memory opcodes in the enum, single check against // LocalLast is sufficient. if (kind <= Stk::LocalLast && stk_[i - 1].slot() == slot) { returntrue;
}
} returnfalse;
}
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;
}
}
}
// 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.
// 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.
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); #ifdefined(DEBUG) && defined(JS_64BIT)
masm.debugAssertCanonicalInt32(dest); #endif break; case Stk::MemI32:
fr.popGPR(dest); #ifdefined(DEBUG) && defined(JS_64BIT)
masm.debugAssertCanonicalInt32(dest); #endif 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());
}
}
// 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: returntrue; default: returnfalse;
}
}
bool BaseCompiler::popConst(int32_t* c) {
Stk& v = stk_.back(); if (v.kind() != Stk::ConstI32) { returnfalse;
}
*c = v.i32val();
stk_.popBack(); returntrue;
}
bool BaseCompiler::popConst(int64_t* c) {
Stk& v = stk_.back(); if (v.kind() != Stk::ConstI64) { returnfalse;
}
*c = v.i64val();
stk_.popBack(); returntrue;
}
bool BaseCompiler::peekConst(int32_t* c) {
Stk& v = stk_.back(); if (v.kind() != Stk::ConstI32) { returnfalse;
}
*c = v.i32val(); returntrue;
}
bool BaseCompiler::peekConst(int64_t* c) {
Stk& v = stk_.back(); if (v.kind() != Stk::ConstI64) { returnfalse;
}
*c = v.i64val(); returntrue;
}
#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);
}
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.