/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(PC pc) { static const int32_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define MMANY -1 #define C_MMANY -2 #define V_MMANY -2 #define R_MMANY -2 #define MFINAL -3 #define FMANY -3 #define CVMANY -3 #define CVUMANY -3 #define CMANY -3 #define SMANY -1 #define IDX_A -4 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef MMANY #undef C_MMANY #undef V_MMANY #undef R_MMANY #undef MFINAL #undef FMANY #undef CVMANY #undef CVUMANY #undef CMANY #undef SMANY #undef IDX_A #undef O }; int n = numberOfPops[size_t(peek_op(pc))]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // BaseSC and BaseSL remove an A that may be on the top of the stack or one // element below the top, depending on the second immediate. if (n == -4) return getImm(pc, 1).u_IVA + 1; // FCall, NewPackedArray, and final member operations specify how many values // are popped in their first immediate if (n == -3) return getImm(pc, 0).u_IVA; // For instructions with vector immediates, we have to scan the // contents of the vector immediate to determine how many values // are popped assert(n == -1 || n == -2); ImmVector iv = getImmVector(pc); // Count the number of values on the stack accounted for by the // ImmVector's location and members int k = iv.numStackValues(); // If this instruction also takes a RHS, count that too if (n == -2) ++k; return k; }
/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(PC pc) { static const int32_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define MFINAL -3 #define F_MFINAL -6 #define C_MFINAL -5 #define V_MFINAL C_MFINAL #define FMANY -3 #define CVUMANY -3 #define CMANY -3 #define SMANY -1 #define IDX_A -4 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef MFINAL #undef F_MFINAL #undef C_MFINAL #undef V_MFINAL #undef FMANY #undef CVUMANY #undef CMANY #undef SMANY #undef IDX_A #undef O }; auto const op = peek_op(pc); int n = numberOfPops[size_t(op)]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // BaseSC and BaseSL remove an A that may be on the top of the stack or one // element below the top, depending on the second immediate. if (n == -4) return getImm(pc, 1).u_IVA + 1; // FCall, NewPackedArray, and some final member operations specify how many // values are popped in their first immediate if (n == -3) return getImm(pc, 0).u_IVA; // FPassM final operations have paramId as imm 0 and stackCount as imm1 if (n == -6) return getImm(pc, 1).u_IVA; // Other final member operations pop their first immediate + 1 if (n == -5) return getImm(pc, 0).u_IVA + 1; // For instructions with vector immediates, we have to scan the contents of // the vector immediate to determine how many values are popped assert(n == -1); ImmVector iv = getImmVector(pc); int k = iv.numStackValues(); return k; }
/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(PC pc) { static const int32_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define MFINAL -3 #define F_MFINAL -6 #define C_MFINAL -5 #define V_MFINAL C_MFINAL #define FMANY -3 #define UFMANY -4 #define CVUMANY -3 #define CMANY -3 #define SMANY -1 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef MFINAL #undef F_MFINAL #undef C_MFINAL #undef V_MFINAL #undef FMANY #undef UFMANY #undef CVUMANY #undef CMANY #undef SMANY #undef O }; auto const op = peek_op(pc); int n = numberOfPops[size_t(op)]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // FCall, NewPackedArray, and some final member operations specify how many // values are popped in their first immediate if (n == -3) return getImm(pc, 0).u_IVA; // FCallM, FCallDM, and FCallUnpackM pop uninit values from the stack and // push multiple returned values. if (n == -4) return getImm(pc, 0).u_IVA + getImm(pc, 1).u_IVA - 1; // FPassM final operations have paramId as imm 0 and stackCount as imm1 if (n == -6) return getImm(pc, 1).u_IVA; // Other final member operations pop their first immediate + 1 if (n == -5) return getImm(pc, 0).u_IVA + 1; // For instructions with vector immediates, we have to scan the contents of // the vector immediate to determine how many values are popped assertx(n == -1); ImmVector iv = getImmVector(pc); int k = iv.numStackValues(); return k; }
/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(PC pc) { static const int32_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define FIVE(...) 5 #define MFINAL -3 #define C_MFINAL -5 #define V_MFINAL C_MFINAL #define CVMANY -3 #define CVUMANY -3 #define FCALL -4 #define CMANY -3 #define SMANY -1 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef FIVE #undef MFINAL #undef C_MFINAL #undef V_MFINAL #undef CVMANY #undef CVUMANY #undef FCALL #undef CMANY #undef SMANY #undef O }; auto const op = peek_op(pc); int n = numberOfPops[size_t(op)]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // FCallAwait, NewPackedArray, and some final member operations specify how // many values are popped in their first immediate if (n == -3) return getImm(pc, 0).u_IVA; // FCall pops numArgs, unpack and (numRets - 1) uninit values if (n == -4) { auto const fca = getImm(pc, 0).u_FCA; return fca.numArgs + (fca.hasUnpack ? 1 : 0) + fca.numRets - 1; } // Other final member operations pop their first immediate + 1 if (n == -5) return getImm(pc, 0).u_IVA + 1; // For instructions with vector immediates, we have to scan the contents of // the vector immediate to determine how many values are popped assertx(n == -1); ImmVector iv = getImmVector(pc); int k = iv.numStackValues(); return k; }
/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(const Op* opcode) { static const int8_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define MMANY -1 #define C_MMANY -2 #define V_MMANY -2 #define R_MMANY -2 #define FMANY -3 #define CVMANY -3 #define CVUMANY -3 #define CMANY -3 #define SMANY -1 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef MMANY #undef C_MMANY #undef V_MMANY #undef R_MMANY #undef FMANY #undef CVMANY #undef CVUMANY #undef CMANY #undef SMANY #undef O }; int n = numberOfPops[uint8_t(*opcode)]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // FCall and NewPackedArray specify how many values are popped in their // first immediate if (n == -3) return getImm(opcode, 0).u_IVA; // For instructions with vector immediates, we have to scan the // contents of the vector immediate to determine how many values // are popped assert(n == -1 || n == -2); ImmVector iv = getImmVector(opcode); // Count the number of values on the stack accounted for by the // ImmVector's location and members int k = iv.numStackValues(); // If this instruction also takes a RHS, count that too if (n == -2) ++k; return k; }
void emitIterBreak(IRGS& env, Offset relOffset, const ImmVector& iv) { for (int iterIndex = 0; iterIndex < iv.size(); iterIndex += 2) { IterKind iterKind = (IterKind)iv.vec32()[iterIndex]; Id iterId = iv.vec32()[iterIndex + 1]; switch (iterKind) { case KindOfIter: gen(env, IterFree, IterId(iterId), fp(env)); break; case KindOfMIter: gen(env, MIterFree, IterId(iterId), fp(env)); break; case KindOfCIter: gen(env, CIterFree, IterId(iterId), fp(env)); break; } } jmpImpl(env, bcOff(env) + relOffset); }
void emitIterBreak(IRGS& env, const ImmVector& iv, Offset relOffset) { always_assert(env.currentNormalizedInstruction->endsRegion); for (int iterIndex = 0; iterIndex < iv.size(); iterIndex += 2) { IterKind iterKind = (IterKind)iv.vec32()[iterIndex]; Id iterId = iv.vec32()[iterIndex + 1]; switch (iterKind) { case KindOfIter: gen(env, IterFree, IterId(iterId), fp(env)); break; case KindOfMIter: gen(env, MIterFree, IterId(iterId), fp(env)); break; case KindOfCIter: gen(env, CIterFree, IterId(iterId), fp(env)); break; } } // Would need to change this if we support not ending regions on this: gen(env, Jmp, makeExit(env, bcOff(env) + relOffset)); }
/** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(const Opcode* opcode) { static const int8_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define LMANY(...) -1 #define C_LMANY(...) -2 #define V_LMANY(...) -2 #define FMANY -3 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef LMANY #undef C_LMANY #undef V_LMANY #undef FMANY #undef O }; int n = numberOfPops[*opcode]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // FCall specifies how many values are popped in its first immediate if (n == -3) return getImm(opcode, 0).u_IVA; // For instructions with vector immediates, we have to scan the // contents of the vector immediate to determine how many values // are popped ASSERT(n == -1 || n == -2); ImmVector iv = getImmVector(opcode); // Count the number of values on the stack accounted for by the // ImmVector's location and members int k = iv.numStackValues(); // If this instruction also takes a RHS, count that too if (n == -2) ++k; return k; }
void emitNewStructArray(HTS& env, const ImmVector& immVec) { auto const numArgs = immVec.size(); auto const ids = immVec.vec32(); // The NewPackedArray opcode's helper needs array values passed to it // via the stack. We use spillStack() to flush the eval stack and // obtain a pointer to the topmost item; if over-flushing becomes // a problem then we should refactor the NewPackedArray opcode to // take its values directly as SSA operands. spillStack(env); NewStructData extra; extra.offset = offsetFromSP(env, 0); extra.numKeys = numArgs; extra.keys = new (env.unit.arena()) StringData*[numArgs]; for (auto i = size_t{0}; i < numArgs; ++i) { extra.keys[i] = curUnit(env)->lookupLitstrId(ids[i]); } discard(env, numArgs); push(env, gen(env, NewStructArray, extra, sp(env))); }
void emitSSwitch(HTS& env, const ImmVector& iv) { const int numCases = iv.size() - 1; /* * We use a fast path translation with a hashtable if none of the * cases are numeric strings and if the input is actually a string. * * Otherwise we do a linear search through the cases calling string * conversion routines. */ const bool fastPath = topC(env)->isA(Type::Str) && std::none_of(iv.strvec(), iv.strvec() + numCases, [&](const StrVecItem& item) { return curUnit(env)->lookupLitstrId(item.str)->isNumeric(); } ); auto const testVal = popC(env); std::vector<LdSSwitchData::Elm> cases(numCases); for (int i = 0; i < numCases; ++i) { auto const& kv = iv.strvec()[i]; cases[i].str = curUnit(env)->lookupLitstrId(kv.str); cases[i].dest = SrcKey{curSrcKey(env), bcOff(env) + kv.dest}; } LdSSwitchData data; data.numCases = numCases; data.cases = &cases[0]; data.defaultSk = SrcKey{curSrcKey(env), bcOff(env) + iv.strvec()[iv.size() - 1].dest}; auto const dest = gen(env, fastPath ? LdSSwitchDestFast : LdSSwitchDestSlow, data, testVal); gen(env, DecRef, testVal); gen(env, AdjustSP, IRSPOffsetData { offsetFromIRSP(env, BCSPOffset{0}) }, sp(env)); gen(env, JmpSSwitchDest, dest, sp(env)); }
void emitSwitch(HTS& env, const ImmVector& iv, int64_t base, int32_t bounded) { int nTargets = bounded ? iv.size() - 2 : iv.size(); SSATmp* const switchVal = popC(env); Type type = switchVal->type(); assert(IMPLIES(!(type <= Type::Int), bounded)); assert(IMPLIES(bounded, iv.size() > 2)); SSATmp* index; SSATmp* ssabase = cns(env, base); SSATmp* ssatargets = cns(env, nTargets); Offset defaultOff = bcOff(env) + iv.vec32()[iv.size() - 1]; Offset zeroOff = 0; if (base <= 0 && (base + nTargets) > 0) { zeroOff = bcOff(env) + iv.vec32()[0 - base]; } else { zeroOff = defaultOff; } if (type <= Type::Null) { gen(env, Jmp, makeExit(env, zeroOff)); return; } if (type <= Type::Bool) { Offset nonZeroOff = bcOff(env) + iv.vec32()[iv.size() - 2]; gen(env, JmpNZero, makeExit(env, nonZeroOff), switchVal); gen(env, Jmp, makeExit(env, zeroOff)); return; } if (type <= Type::Int) { // No special treatment needed index = switchVal; } else if (type <= Type::Dbl) { // switch(Double|String|Obj)Helper do bounds-checking for us, so // we need to make sure the default case is in the jump table, // and don't emit our own bounds-checking code bounded = false; index = gen(env, LdSwitchDblIndex, switchVal, ssabase, ssatargets); } else if (type <= Type::Str) { bounded = false; index = gen(env, LdSwitchStrIndex, switchVal, ssabase, ssatargets); } else if (type <= Type::Obj) { // switchObjHelper can throw exceptions and reenter the VM so we use the // catch block here. bounded = false; index = gen(env, LdSwitchObjIndex, switchVal, ssabase, ssatargets); } else if (type <= Type::Arr) { gen(env, DecRef, switchVal); gen(env, Jmp, makeExit(env, defaultOff)); return; } else { PUNT(Switch-UnknownType); } std::vector<Offset> targets(iv.size()); for (int i = 0; i < iv.size(); i++) { targets[i] = bcOff(env) + iv.vec32()[i]; } JmpSwitchData data; data.base = base; data.bounded = bounded; data.cases = iv.size(); data.defaultOff = defaultOff; data.targets = &targets[0]; spillStack(env); gen(env, AdjustSP, StackOffset { offsetFromSP(env, 0) }, sp(env)); gen(env, JmpSwitchDest, data, index, sp(env)); }