Skip to content

Commit

Permalink
Remove FPass[CV]Nop opcodes
Browse files Browse the repository at this point in the history
Summary:
Remove FPassCNop and FPassVNop opcodes. These opcodes were not doing anything
except of converting flavor, so let's make verifier able to unify C and V into
a CV type instead and make a workaround for hphpc to pretend it is emitting Vs
in both branches of FIsParamByRef.

Reviewed By: alexeyt

Differential Revision: D8503574

fbshipit-source-id: a0fd406d1fbcc63714fc7a713514111b61838e82
  • Loading branch information
jano authored and hhvm-bot committed Jun 26, 2018
1 parent b8db924 commit dc7a6c9
Show file tree
Hide file tree
Showing 130 changed files with 201 additions and 888 deletions.
74 changes: 29 additions & 45 deletions hphp/compiler/analysis/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,10 @@ namespace StackSym {
static const char V = 0x02; // Ref symbolic flavor
static const char A = 0x03; // Classref symbolic flavor
static const char R = 0x04; // Return value symbolic flavor
static const char F = 0x05; // Function argument symbolic flavor
static const char L = 0x06; // Local symbolic flavor
static const char T = 0x07; // String literal symbolic flavor
static const char I = 0x08; // int literal symbolic flavor
static const char H = 0x09; // $this symbolic flavor
static const char L = 0x05; // Local symbolic flavor
static const char T = 0x06; // String literal symbolic flavor
static const char I = 0x07; // int literal symbolic flavor
static const char H = 0x08; // $this symbolic flavor

static const char N = 0x10; // Name marker
static const char G = 0x20; // Global name marker
Expand Down Expand Up @@ -242,7 +241,6 @@ namespace StackSym {
case StackSym::V: res = "V"; break;
case StackSym::A: res = "A"; break;
case StackSym::R: res = "R"; break;
case StackSym::F: res = "F"; break;
case StackSym::L: res = "L"; break;
case StackSym::T: res = "T"; break;
case StackSym::I: res = "I"; break;
Expand Down Expand Up @@ -1502,11 +1500,11 @@ struct OpEmitContext {
#define COUNT_MFINAL 0
#define COUNT_C_MFINAL 0
#define COUNT_V_MFINAL 0
#define COUNT_FMANY 0
#define COUNT_C_FMANY 0
#define COUNT_UFMANY 0
#define COUNT_C_UFMANY 0
#define COUNT_CVMANY 0
#define COUNT_CVUMANY 0
#define COUNT_C_CVMANY 0
#define COUNT_CVMANY_UMANY 0
#define COUNT_C_CVMANY_UMANY 0
#define COUNT_CMANY 0
#define COUNT_SMANY 0

Expand Down Expand Up @@ -1571,20 +1569,20 @@ struct OpEmitContext {
#define POP_V_MFINAL \
getEmitterVisitor().popEvalStack(StackSym::V); \
getEmitterVisitor().popEvalStackMMany()
#define POP_FMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
#define POP_C_FMANY \
#define POP_CVMANY \
getEmitterVisitor().popEvalStackCVMany(a1)
#define POP_CVUMANY \
getEmitterVisitor().popEvalStackCVMany(a1)
#define POP_C_CVMANY \
getEmitterVisitor().popEvalStack(StackSym::C); \
getEmitterVisitor().popEvalStackMany(a1 - 1, StackSym::F)
#define POP_UFMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::F); \
getEmitterVisitor().popEvalStackCVMany(a1 - 1)
#define POP_CVMANY_UMANY \
getEmitterVisitor().popEvalStackCVMany(a1); \
getEmitterVisitor().popEvalStackMany(a2 - 1, StackSym::C)
#define POP_C_UFMANY \
#define POP_C_CVMANY_UMANY \
getEmitterVisitor().popEvalStack(StackSym::C); \
getEmitterVisitor().popEvalStackMany(a1 - 1, StackSym::F); \
getEmitterVisitor().popEvalStackCVMany(a1 - 1); \
getEmitterVisitor().popEvalStackMany(a2 - 1, StackSym::C)
#define POP_CVUMANY \
getEmitterVisitor().popEvalStackCVMany(a1)
#define POP_CMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
#define POP_SMANY \
Expand All @@ -1593,7 +1591,6 @@ struct OpEmitContext {
#define POP_CV(i) getEmitterVisitor().popEvalStack(StackSym::C)
#define POP_VV(i) getEmitterVisitor().popEvalStack(StackSym::V)
#define POP_RV(i) getEmitterVisitor().popEvalStack(StackSym::R)
#define POP_FV(i) getEmitterVisitor().popEvalStack(StackSym::F)
#define POP_UV(i) POP_CV(i)
#define POP_CUV(i) POP_CV(i)

Expand Down Expand Up @@ -1763,7 +1760,6 @@ struct OpEmitContext {
#define PUSH_CUV PUSH_CV
#define PUSH_VV getEmitterVisitor().pushEvalStackFromOp(StackSym::V, ctx)
#define PUSH_RV getEmitterVisitor().pushEvalStackFromOp(StackSym::R, ctx)
#define PUSH_FV getEmitterVisitor().pushEvalStackFromOp(StackSym::F, ctx)

#define PUSH_INS_1_CV \
getEmitterVisitor().insertEvalStackFromOp(StackSym::C, 1, ctx);
Expand Down Expand Up @@ -2034,13 +2030,12 @@ struct OpEmitContext {
#undef POP_VV
#undef POP_HV
#undef POP_RV
#undef POP_FV
#undef POP_LREST
#undef POP_FMANY
#undef POP_C_FMANY
#undef POP_UFMANY
#undef POP_C_UFMANY
#undef POP_CVMANY
#undef POP_CVUMANY
#undef POP_C_CVMANY
#undef POP_CVMANY_UMANY
#undef POP_C_CVMANY_UMANY
#undef POP_CMANY
#undef POP_SMANY
#undef POP_LA_ONE
Expand Down Expand Up @@ -2120,7 +2115,6 @@ struct OpEmitContext {
#undef PUSH_VV
#undef PUSH_HV
#undef PUSH_RV
#undef PUSH_FV
#undef IMPL_ONE
#undef IMPL_TWO
#undef IMPL_THREE
Expand Down Expand Up @@ -6032,7 +6026,6 @@ bool EmitterVisitor::visit(ConstructPtr node) {

if (el->getType() == '`') {
emitConvertToCell(e);
e.FPassCNop();
delete fpi;
e.FCall(1);
}
Expand Down Expand Up @@ -7301,7 +7294,6 @@ bool EmitterVisitor::emitInlineGena(
FPIRegionRecorder fpi(this, m_ue, m_evalStack, fromDArrayStart);
emitVirtualLocal(array);
emitCGet(e);
e.FPassCNop();
}
e.FCall(1);
e.UnboxR();
Expand Down Expand Up @@ -8074,13 +8066,11 @@ EmitterVisitor::MInstrChain EmitterVisitor::emitInOutArg(
e.Null();
e.PopL(loc);
}
e.FPassCNop();
} else {
auto const stackCount = emitMOp(i, iLast, e, MInstrOpts{MOpMode::InOut});
e.QueryM(
stackCount, QueryMOp::InOut, symToMemberKey(e, iLast, false /* allowW */)
);
e.FPassCNop();
}
return chain;
}
Expand All @@ -8094,11 +8084,8 @@ void EmitterVisitor::emitFPassStrict(Emitter& e, ExpressionPtr exp) {

if (!exp->hasContext(Expression::RefParameter)) {
emitCGet(e);
e.FPassCNop();
} else if (emitVGet(e, true)) {
e.FPassCNop();
} else {
e.FPassVNop();
emitVGet(e, true);
}
}

Expand All @@ -8113,12 +8100,16 @@ void EmitterVisitor::emitFPass(Emitter& e, ExpressionPtr exp, int paramId) {
e.JmpNZ(byRef);
if (shouldVisit) visit(exp);
emitCGet(e, true);
e.FPassCNop();
// hphpc doesn't support the CV unified type so let's pretend this is a V
// on a symbolic stack as it's going to be consumed only by FCall anyway
assertx(m_evalStack.top() == StackSym::C);
m_evalStack.pop();
m_evalStack.push(StackSym::V);
e.Jmp(done);
byRef.set(e);
if (shouldVisit) visit(exp);
emitVGet(e);
e.FPassVNop();
assertx(m_evalStack.top() == StackSym::V);
done.set(e);
};

Expand Down Expand Up @@ -8161,7 +8152,6 @@ void EmitterVisitor::emitFPass(Emitter& e, ExpressionPtr exp, int paramId) {
case StackSym::C: {
e.FIsParamByRef(paramId, hint);
e.PopC();
e.FPassCNop();
return;
}
case StackSym::LN:
Expand Down Expand Up @@ -9319,11 +9309,9 @@ void EmitterVisitor::emitInOutToRefWrapper(PostponedMeth& p,
inoutParams.push_back(i);
emitVirtualLocal(i);
emitVGet(e);
e.FPassVNop();
} else {
emitVirtualLocal(i);
e.PushL(i);
e.FPassCNop();
}
},
[&] (Emitter& e) {
Expand Down Expand Up @@ -9362,9 +9350,6 @@ void EmitterVisitor::emitRefToInOutWrapper(PostponedMeth& p,
emitVirtualLocal(i);
e.PushL(i);
}
if (!fe->params[i].variadic) {
e.FPassCNop();
}
},
[&] (Emitter& e) {
emitUnpackInOutCall(
Expand Down Expand Up @@ -10122,7 +10107,6 @@ void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth,
for (uint32_t i = 0; i < numParams; i++) {
emitVirtualLocal(i);
emitCGet(e);
e.FPassCNop();
}
}
e.FCall(numParams);
Expand Down
75 changes: 30 additions & 45 deletions hphp/doc/bytecode.specification
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,13 @@ function. The entry on the top of the FPI stack is called the "current FPI".

The FPush* instructions push a new entry onto the FPI stack, initializing the
entry with a reference to the FPI structure for a given function and the
bytecode address of the appropriate entry point. The FPass* instructions
prepare the parameters that will be passed into the callee. The FCall*
instructions look at the current FPI to get the bytecode address of the
function (the callee), transfers the parameters from the evaluation stack to
the callee, pops the current FPI off of the FPI stack, and then invokes the
dispatcher to call the function.
bytecode address of the appropriate entry point. Arguments are pushed on the
stack either as cells or as refs, but they may be refs only if the parameter
is passed by ref. The FIsParamByRef* and FThrowOnRefMismatch instructions are
used to enforce proper flavor. The FCall* instructions look at the current
FPI to get the bytecode address of the function (the callee), transfers the
parameters from the evaluation stack to the callee, pops the current FPI off
of the FPI stack, and then invokes the dispatcher to call the function.

Calls to builtin functions may be optimized to avoid pushing an entry on the
FPI stack if it is known that the builtin function does not need access to the
Expand All @@ -371,14 +372,13 @@ all FCall instructions other than FCallBuiltin.
Calling convention
------------------

The caller may pass any number of parameters to the callee by executing FPass*
instructions zero or more times prior to executing an FCall* instruction. The
caller must pass the parameters in forward order, i.e. the first use of FPass*
passes the first parameter, the second use of FPass* passes the second
parameter, and so forth.
The caller may pass any number of parameters to the callee by pushing zero or
more cells or refs on the stack prior to executing an FCall* instruction. The
caller must pass the parameters in forward order, i.e. the first pushed value
corresponds to the first parameter, and so forth.

The FPush*/FPass*/FCall* instructions can be used to call a global function, a
method on an object, or a method from a class. The caller is responsible for
The FPush*/FCall* instructions can be used to call a global function, a method
on an object, or a method from a class. The caller is responsible for
evaluating all of the parameters in forward order. When the caller executes an
FCall* instruction, the dispatcher creates a new frame and moves the parameters
prepared by the caller into the callee's variable environment. The dispatcher
Expand Down Expand Up @@ -681,9 +681,9 @@ other FPI region. An FPI region may not contain backward jumps, nor may it
contain forward jumps that jump past the end of the FPI region.

In the case of an exception, an FPI region can terminate without an FCall*, and
without including an FPass for each argument if they would be unreachable
(e.g. calling a function that never returns). In this case it is possible for
multiple fpi regions to end at the same bytecode.
without pushing a value on the stack for each argument if these instructions
would be unreachable (e.g. calling a function that never returns). In this case
it is possible for multiple fpi regions to end at the same bytecode.

Each function has an "FPI region table". Each row in the FPI region table
consists of offset of the FPush* instruction that begins the region, the FCall
Expand All @@ -710,9 +710,6 @@ Here is a description of each flavor descriptor:
R - return value; specifies that the value may be a cell or a ref at run
time; this flavor descriptor is used for return values from function
calls
F - function argument; specifies that the value may be a cell or a ref at run
time; this flavor descriptor is used for parameter values that are about
to be passed into a function
U - uninit; specifies that the value must be an uninitialized null at run
time; this is only used for FCallBuiltin, CreateCl, and CUGetL.

Expand Down Expand Up @@ -790,8 +787,8 @@ must point to a Catch instruction.
after an FPush* instruction and it must end with an FCall* instruction. Each
use of the FPush* instruction must be the instruction immediately before
exactly one FPI region. Likewise, each use of the FCall* instruction must be
the last instruction in exactly one FPI region. Finally, FPass* instructions
may not be used outside an FPI region.
the last instruction in exactly one FPI region. Finally, FIsParamByRef and
FThrowOnRefMismatch instructions may not be used outside an FPI region.

10) Each FPI region may not contain backward jumps, nor may it contain forward
jumps that jump outside the end of the FPI region. Also, there may not be jumps
Expand All @@ -803,15 +800,13 @@ an instruction inside an FPI region.
11) The depth of the FPI stack at any given point in the bytecode must be the
same for all possible control flow paths. Also, for any given FPI region that
passes n parameters, all possible control flow paths from the beginning of the
region to the end must pass through exactly n FPass* instructions associated
with the region which pass the parameters in forward order.
region to the end must push exactly n cells or values on the stack.

12) Given an evaluation stack of depth n after an FPush* instruction, the
evaluation stack before the corresponding FCall* instruction must also have a
depth of n. Likewise, the evaluation stack after corresponding FPass*
instructions must have a depth of n as well. Finally, no instruction between an
FPush* and its corresponding FCall* may consume any of the values from the
evaluation stack that were pushed onto the stack before the FPush* instruction.
depth of n. Finally, no instruction between an FPush* and its corresponding
FCall* may consume any of the values from the evaluation stack that were
pushed onto the stack before the FPush* instruction.

13) The initialization state of each iterator variable must be known at every
point in the code and must be the same for all control paths. There are four
Expand Down Expand Up @@ -2257,17 +2252,7 @@ FHandleRefMismatch <param id> <reffiness hint> <func name> [] -> []
named %3 and the reffiness hint %2, which may be 'Cell', or 'Ref'. Depending
on the runtime configuration, may raise a warning or throw an exception.

FPassCNop [C] -> [F]

FPI pass parameter, no op. This instruction pushes $1 onto the stack as
a cell.

FPassVNop [V] -> [F]

FPI pass parameter, no op. This instruction pushes $1 onto the stack as
a ref.

FCall <num params> [F..F] -> [R]
FCall <num params> [C|V..C|V] -> [R]

FPI call. This instruction gets the bytecode address of the function
associated with the current FPI (the callee), transfers the top %1 values
Expand All @@ -2276,15 +2261,15 @@ FCall <num params> [F..F] -> [R]
callee returns, it will transfer the return value onto the caller's
evaluation stack using the R flavor.

FCallD <num params> <class name> <func name> [F..F] -> [R]
FCallD <num params> <class name> <func name> [C|V..C|V] -> [R]

FPI call direct. This instruction has exactly the effects of FCall %1, but
provides hints from static analysis to assist the region selector in
determining the callee. The strings in %2 and %3 are statically known names
of the class (if any) and method being called. If the call is targeting a
non-method, %2 must be the empty string.

FCallAwait <num params> <class name> <func name> [F..F] -> [R]
FCallAwait <num params> <class name> <func name> [C|V..C|V] -> [R]

FPI call direct to a known async function. Equivalent to the sequence

Expand All @@ -2295,7 +2280,7 @@ FCallAwait <num params> <class name> <func name> [F..F] -> [R]
but may enable the runtime to avoid creating a StaticWaitHandle in the event
that the called function returns eagerly.

FCallUnpack <num params> [F..F] [C:Arr] -> [R]
FCallUnpack <num params> [C|V..C|V] [C:Arr] -> [R]

FPI call with params and array. This instruction gets the bytecode address of
the function associated with the current FPI (the callee), transfers the top
Expand All @@ -2307,9 +2292,9 @@ FCallUnpack <num params> [F..F] [C:Arr] -> [R]
When the callee returns, it will transfer the return value onto the caller's
evaluation stack using the R flavor.

FCallM <num params> <num returns> [F..F] -> [C..C]
FCallDM <num params> <num returns> <class name> <func name> [F..F] -> [C..C]
FCallUnpackM <num params> <num returns> [F..F] [C:Arr] -> [C..C]
FCallM <num params> <num returns> [C..C] -> [C..C]
FCallDM <num params> <num returns> <class name> <func name> [C..C] -> [C..C]
FCallUnpackM <num params> <num returns> [C..C] [C:Arr] -> [C..C]

These "M" variants of FCall behave similarly to FCall, FCallD, and FCallUnpack
respectively. These instructions must be used iff the callee returns via the
Expand Down Expand Up @@ -4360,7 +4345,7 @@ short-circuit semantics correctly. All Jmp* instructions used to implement
"and" and "or" operators will be forward jumps.

5) The new expression
The new expression is implemented by using the FPushCtor*, FPass*, and FCall
The new expression is implemented by using the FPushCtor* and FCall
instructions.

6) The ternary operator (?:)
Expand Down
4 changes: 0 additions & 4 deletions hphp/hack/src/hhbc/Hhas_parser_actions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,6 @@ let make_nullary_inst s =
| "Clone" -> IOp (Clone)
| "Exit" -> IOp (Hhbc_ast.Exit) (* Need to qualify because of shadowing *)

(* instruct_call *)
| "FPassCNop" -> ICall (FPassCNop)
| "FPassVNop" -> ICall (FPassVNop)

(* instruct_control_flow *)
| "RetC" -> IContFlow (RetC)
| "RetV" -> IContFlow (RetV)
Expand Down
Loading

0 comments on commit dc7a6c9

Please sign in to comment.