37 Commits

Autor SHA1 Mensagem Data
Daniel Sloof 30b0fc0026 headers_list should return array() with no headers
headers_list should return an empty array when there are no
 headers. There were already tests involving this in ext_network.php, but it's
 being skipped (at least in the github repository).

Closes #1218

Reviewed By: @alexmalyshev

Differential Revision: D1030259

Pulled By: @scannell
2013-10-30 11:06:15 -07:00
Brett Simmers 4f78c7de03 Clear out request-local globals at the beginning of each request
The rpc server keeps the ExecutionContext alive across requests to
reduce startup costs. Unfortunately, we weren't clearing $_SESSION, $_GET,
etc... at the beginning of the request. This was making us think that most of
the http headers in most requests were duplicates, and emit a warning where the
option is enabled. It's also generally bad practice to leak this stuff between
different requests.

Reviewed By: @markw65

Differential Revision: D1010456
2013-10-30 11:06:04 -07:00
Paul Tarjan d6a8578e34 try to fix yii again
It turns out the path is unique to my machine. Try glob.

Reviewed By: @JoelMarcey

Differential Revision: D1031707
2013-10-30 11:05:53 -07:00
Paul Tarjan 085f510c23 don't call autoload handler with an empty class
Zend doesn't do it and we shouldn't either. It was causing an assert in a debug build.

Reviewed By: @markw65

Differential Revision: D1030881
2013-10-30 11:05:37 -07:00
mwilliams 1e1eac2a0e Fix fallback when optimized translation fails
analyze needs to read live state, which is only
valid if this is the first tracelet in the region. For
subsequent tracelets, fall back to the interpreter.

Reviewed By: @ottoni

Differential Revision: D1028657
2013-10-30 11:04:53 -07:00
Jordan DeLong 565465d580 Fix slow/intercept tests to pass DynamicInvokeFunctions to compiler
Modify one of the tests so it fails with the wrong options
and pass the right ones, add a similar test for static member
functions.  It appears DynamicInvokeFunction is ignored for member
functions (but member functions are inspected for a "dyn_" prefix??),
so I added norepo for the ones that do intercept on member functions.

Reviewed By: @swtaarrs

Differential Revision: D1029398
2013-10-30 11:03:05 -07:00
Sean Cannella 38be2694ae Fix linking issue on OS X 10.9 with libc++
ld on OS X 10.9 doesn't seem to be able to locate the ctype.h
functions (ex. isdigit) when they are used as function pointers, so wrap
them in lambdas.

Reviewed By: @markw65

Differential Revision: D1030137
2013-10-30 11:02:53 -07:00
Paul Tarjan d87b7c84fb stop yii from see-sawing
yii leaves this file around which is a duplicate of its test. So the autoloader loads it twice. Kill it between runs.

Reviewed By: @JoelMarcey

Differential Revision: D1030682
2013-10-30 11:02:30 -07:00
Herman Venter a8b31881f6 Fix the variable command so that it does not fail totally when one variable is too large.
The variable command, along with its clients the global command and the = command, obtained variable names and values from the server by asking for an map of variable name to variable values in a single request. If one or more of these variables have really large values, the serialization of the map exceeds the serialization limit and the entire command fails. This makes it difficult pin-point which variable causes the trouble and breaks the client commands in unexpected ways.

This diff changes the protocol of the variable command so that it first gets an array of variable names only and then separately gets the value of each variable. If such a separate get fails because of a serialization limit, the variable's value is printed as "...omitted".

Reviewed By: @mikemag

Differential Revision: D1021035
2013-10-30 11:00:11 -07:00
mwilliams bd49a4ac26 Fix retranslateOpt bug
If we've already optimized, we want to do a regular
translation, not an optimized one

Reviewed By: @ottoni

Differential Revision: D1026773
2013-10-30 10:59:14 -07:00
Joel Marcey c33c344f99 fix stream_select error
I am a clown.

Reviewed By: @ptarjan

Differential Revision: D1027352
2013-10-30 10:56:27 -07:00
aravind ec3d41f15f IncRef Sinking fix
An IncRef that is marked as a candidate for sinking should
be removed from the sinking list only if the corresponding DecRefNZ
is live.

Reviewed By: @ottoni

Differential Revision: D1026155
2013-10-30 10:54:54 -07:00
Alex Malyshev afc63c1ab6 Remove incorrect assert
We assert in DOMNode::{appendChild,insertBefore} that the node that
has been passed in is an orphaned node, however it's only orphaned if
it has no parent or its parent is also orphaned.

Fixes the last fatal in yii

Reviewed By: @ptarjan

Differential Revision: D1026211
2013-10-30 10:54:42 -07:00
Paul Tarjan b10c2021e2 set m_documentRoot even if there is no hdf
This variable is entirely independent of the hdf, so it should be set even it there isn't one.

This came up when someone (me) was running `hhvm -m server` from the symphony directory trying to show it off, but PATH_INFO doesn't work unless the m_documentRoot is set. It just assumes that all files exist if there is no document root.

This shouldn't affect anything in FB since there is always a `.hdf`.

Reviewed By: @markw65

Differential Revision: D1025781
2013-10-30 10:54:25 -07:00
mwilliams d04d9ec022 Fix jitted symbols in gdb 7.6
gdb traps calls to a function named __jit_debug_register_code,
and updates its internal symbol tables.

Apparently gdb-7.2 was prepared to demangle the name,
while gdb-7.6 is not.

Reviewed By: aravind

Differential Revision: D1026295
2013-10-30 10:54:10 -07:00
Sean Cannella 5a4db6875a StoreImmPatcher incorrectly handles 64-bit imms
StoreImmPatcher is currently subtracting the wrong offset (not
taking into account the extra instruction emitted) when setting m_addr.

Closes #1210

Reviewed By: @markw65

Differential Revision: D1025861
2013-10-30 10:53:44 -07:00
Bert Maher 251b5d78b4 Fix /stop command issued when hhvm can't bind port
When HHVM can't bind to a port it tries to shut down the
currently running instance using /stop on the admin port, but
RuntimeOption::ServerIP was blank (do we actually set this anywhere?)
and we weren't passing the admin password.

Reviewed By: @markw65

Differential Revision: D944849
2013-10-24 11:53:28 -07:00
Kristaps Kaupe d2ca7292fb Native.h macro typo
Fix macro typo

Reviewed By: @JoelMarcey

Differential Revision: D1023850

Pulled By: @scannell
2013-10-24 11:53:12 -07:00
Franck STAUFFER ebb9f3cadd row_count properly set on INSERT/UPDATES
Fixes bug due to variable shadowing

Closes #364

Reviewed By: @JoelMarcey

Differential Revision: D1021780

Pulled By: @scannell
2013-10-23 13:24:37 -07:00
Paul Tarjan b9d8d0ff47 fix ASAN bug in JSON parser
Wow, our json parser has diverged so much from zend. This is basicaly what theirs does. More importantly, this is correct.

Sadly I don't think is the memory corruption bug.

Reviewed By: @scannell

Differential Revision: D1021678
2013-10-23 13:24:08 -07:00
Herman Venter 73f16bc03b Fix list command so that it finds the right start line and it can find systemlib.php
The list command was using the the string "line2", cunningly disguised as the static string variable 'line1' to look for the line where a func/class/method starts. Also, the file name it got for systemlib.php was expected to always be exactly "systemlib.php" but it turns out this is no longer the case, or at least not always.

Reviewed By: @mikemag

Differential Revision: D1018535
2013-10-22 12:17:20 -07:00
Matthias Eck 540cac6ec2 Fixed step to skip generated functions
Changed cmd_step to check at onBeginInterrupt if the file line is empty i.e. returns as ":0"
In this case continue stepping

Reviewed By: @mikemag

Differential Revision: D1017420
2013-10-22 12:16:59 -07:00
Sara Golemon c20fcce91b Add missing define
Branch has some commits piled onto a non-fix diff on master.
They only really need this one line to work.
2013-10-21 21:37:38 -07:00
Yuval Hager 922a61dad5 Fix Buffer Overrun messages for tiff files
While processing tiff tags, the end pointer was not updated
after a realloc. Also, while processing IFD tag, end and begin pointers
were switched.

Closes #1154
Closes #1193

Reviewed By: @ptarjan

Differential Revision: D1019738

Pulled By: @scannell
2013-10-21 15:38:06 -07:00
Sean Cannella a568ad1a07 Import php-src pull #479
Fix clowny error in zend-strtod.cpp on ARM platforms that passed through the PHP internals mailing list

Reviewed By: @JoelMarcey

Differential Revision: D1019758
2013-10-21 15:37:29 -07:00
Sean Cannella 5815d21518 Fix ICU compliation when not using 51.x
'brew update' to a newer icu4c on OS X exposed that we aren't
being consistent in how we include it resulting in linker errors due to
confusion regarding what the icu namespace is.

Reviewed By: @sgolemon

Differential Revision: D1019529
2013-10-21 15:36:00 -07:00
Anton Grbin 757c1414f5 ArrayInit constructor ssize_t -> size_t
In ArrayInit constructor, n is defined as ssize_t, but negative values
are not handled in any way. We should point that out to callers by
making n of type size_t.

Reviewed By: @edwinsmith

Differential Revision: D1017631
2013-10-21 15:35:42 -07:00
Jordan DeLong a1745fb58b Fix a bug in AGet{L,C} and simplifyLdCls
If AGetL throws due to autoload failure (e.g. parse time
fatal), we crash in the unwinder.

Reviewed By: @bertmaher

Differential Revision: D1018048
2013-10-20 23:22:15 -07:00
aravind 20f0306dd3 Fixes for PGO mode
This fixes a couple of asserts I hit running www in DEBUG mode with
-vEval.JitPGO=1 -vEval.JitRegionSelector=hottrace
-vEval.JitPGOHotOnly=0.

Reviewed By: @ottoni

Differential Revision: D1016284
2013-10-19 00:37:51 -07:00
Alex Malyshev 8121dbb0d3 Fix three eval() bugs
* @ eval() was not being silenced correctly
* If the string passed to eval() hits a parse error, then the runtime
  is not supposed to fatal. We currently do.
* Errors in eval() code did not print a valid file name, they now
  print it as "$FILENAME($LINE1) : eval()'d code on line $LINE2"

Closes #945

Reviewed By: @jdelong

Differential Revision: D1006603
2013-10-19 00:37:41 -07:00
seanc 1177492d76 Fix CMake warning
Summary: Latest CMake on OS X with brew surfaced this warning and that
this check was not doing what it was originally intended to do.
2013-10-19 00:37:21 -07:00
Jordan DeLong 23cd8e033e Fix a bug in TypeConstraint::isSoft
This function returned true if the type had isHHType(), which
it will always if EnableHipHopSyntax=1.  The flag was 0x16 instead of
0x10, and isSoft() just does return m_flags & Soft.

Reviewed By: @dariorussi

Differential Revision: D1014812
2013-10-17 23:44:08 -07:00
Alex Malyshev 6050afa844 Emit an error message when our PHP systemlib doesn't compile
Fixes the terrible "Undefined interface: arrayaccess" error message

Reviewed By: @jdelong

Differential Revision: D1014257
2013-10-17 23:43:56 -07:00
mwilliams 98a2c6ede2 Fix TranslatorX64::m_mode
We need to ensure that m_mode is reset after each
translation attempt.

There was already code in TranslatorX64::translate to reset
it, but after setting it in TranslatorX64::retranslateOpt,
its not certain that we'll get there.

Reviewed By: @swtaarrs

Differential Revision: D1012330
2013-10-17 23:43:43 -07:00
James Miller e766a8a4ac Make the embedded systemlib a dependency of the binary 2013-10-17 23:43:23 -07:00
James Miller 5b7366968a Fix broken make install behaviour
CMake uses rpath to make it easier to run built executables from the build
tree without installing prerequisite libraries first. This means that when
installing, CMake will re-link the binary in order to change the rpath so
it works properly in an install. Unfortunately, it's not smart enough to know
to re-embed the systemlib section, so the installed `hhvm` binary does not
have a systemlib section and doesn't work.

This commit adds a cmake function `HHVM_INSTALL` that effectively bypasses the
standard install command to ensure that the binary is not relinked. To help
with rpaths, this also checks for the existance of the `chrpath` tool. If it
exists, it uses it to either remove or replace the rpath entry in the file,
otherwise it just leaves the rpath entry alone.

This allows `make install` to work again.
2013-10-17 23:42:56 -07:00
Sara Golemon 2d963c4ffb 2.2.0 release 2013-10-16 11:17:06 -07:00
189 arquivos alterados com 2504 adições e 19523 exclusões
+1 -1
Ver Arquivo
@@ -1,4 +1,4 @@
# HipHop VM for PHP [![Build Status](https://travis-ci.org/facebook/hhvm.png?branch=master)](https://travis-ci.org/facebook/hhvm)
# HipHop VM for PHP [![Build Status](https://travis-ci.org/facebook/hiphop-php.png?branch=master)](https://travis-ci.org/facebook/hiphop-php)
HipHop VM (HHVM) is a new open-source virtual machine designed for executing programs written in PHP. HHVM uses a just-in-time compilation approach to achieve superior performance while maintaining the flexibility that PHP developers are accustomed to. HipHop VM (and before it HPHPc) has realized > 5x increase in throughput for Facebook compared with Zend PHP 5.2.
-26
Ver Arquivo
@@ -1,26 +0,0 @@
# HHVM build results
*.hhbc
*.[oa]
/hhvm/hhvm
/hhvm/hphp
# vim swapfiles
.*.swp
.*.swo
# tags files
*/tags
*/TAGS
# logs
*.log
# git patch files
*.diff
# OS X
.DS_Store
._.DS_Store
# gdb
.gdb_history
+17 -28
Ver Arquivo
@@ -118,12 +118,6 @@ namespace {
namespace StackSym {
static const char None = 0x00;
/*
* We don't actually track the U flavor (we treat it as a C),
* because there's nothing important to do with it for emission.
* The verifier will check they are only created at the appropriate
* times.
*/
static const char C = 0x01; // Cell symbolic flavor
static const char V = 0x02; // Var symbolic flavor
static const char A = 0x03; // Classref symbolic flavor
@@ -293,13 +287,12 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define COUNT_TWO(t1,t2) 2
#define COUNT_THREE(t1,t2,t3) 3
#define COUNT_FOUR(t1,t2,t3,t4) 4
#define COUNT_MMANY 0
#define COUNT_C_MMANY 0
#define COUNT_R_MMANY 0
#define COUNT_V_MMANY 0
#define COUNT_LMANY 0
#define COUNT_C_LMANY 0
#define COUNT_R_LMANY 0
#define COUNT_V_LMANY 0
#define COUNT_FMANY 0
#define COUNT_CVMANY 0
#define COUNT_CVUMANY 0
#define COUNT_CMANY 0
#define ONE(t) \
@@ -340,22 +333,21 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
POP_##t2(1); \
POP_##t3(2); \
POP_##t4(3)
#define POP_MMANY \
getEmitterVisitor().popEvalStackMMany()
#define POP_C_MMANY \
#define POP_LMANY \
getEmitterVisitor().popEvalStackLMany()
#define POP_C_LMANY \
getEmitterVisitor().popEvalStack(StackSym::C); \
getEmitterVisitor().popEvalStackMMany()
#define POP_V_MMANY \
getEmitterVisitor().popEvalStackLMany()
#define POP_V_LMANY \
getEmitterVisitor().popEvalStack(StackSym::V); \
getEmitterVisitor().popEvalStackMMany()
#define POP_R_MMANY \
getEmitterVisitor().popEvalStackLMany()
#define POP_R_LMANY \
getEmitterVisitor().popEvalStack(StackSym::R); \
getEmitterVisitor().popEvalStackMMany()
getEmitterVisitor().popEvalStackLMany()
#define POP_FMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::F)
#define POP_CVMANY \
getEmitterVisitor().popEvalStackCVMany(a1)
#define POP_CVUMANY POP_CVMANY
#define POP_CMANY \
getEmitterVisitor().popEvalStackMany(a1, StackSym::C)
@@ -417,7 +409,6 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define PUSH_INS_2(t) PUSH_INS_2_##t
#define PUSH_CV getEmitterVisitor().pushEvalStack(StackSym::C)
#define PUSH_UV PUSH_CV
#define PUSH_VV getEmitterVisitor().pushEvalStack(StackSym::V)
#define PUSH_AV getEmitterVisitor().pushEvalStack(StackSym::A)
#define PUSH_RV getEmitterVisitor().pushEvalStack(StackSym::R)
@@ -583,10 +574,10 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef POP_TWO
#undef POP_THREE
#undef POP_FOUR
#undef POP_MMANY
#undef POP_C_MMANY
#undef POP_V_MMANY
#undef POP_R_MMANY
#undef POP_LMANY
#undef POP_C_LMANY
#undef POP_V_LMANY
#undef POP_R_LMANY
#undef POP_CV
#undef POP_VV
#undef POP_HV
@@ -596,7 +587,6 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef POP_LREST
#undef POP_FMANY
#undef POP_CVMANY
#undef POP_CVUMANY
#undef POP_CMANY
#undef POP_LA_ONE
#undef POP_LA_TWO
@@ -619,7 +609,6 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef PUSH_THREE
#undef PUSH_FOUR
#undef PUSH_CV
#undef PUSH_UV
#undef PUSH_VV
#undef PUSH_HV
#undef PUSH_AV
@@ -1288,7 +1277,7 @@ void EmitterVisitor::popSymbolicLocal(Op op, int arg, int pos) {
}
}
void EmitterVisitor::popEvalStackMMany() {
void EmitterVisitor::popEvalStackLMany() {
while (!m_evalStack.empty()) {
char sym = m_evalStack.top();
char symFlavor = StackSym::GetSymFlavor(sym);
+1 -1
Ver Arquivo
@@ -349,7 +349,7 @@ public:
bool evalStackIsUnknown() { return m_evalStackIsUnknown; }
void popEvalStack(char symFlavor, int arg = -1, int pos = -1);
void popSymbolicLocal(Op opcode, int arg = -1, int pos = -1);
void popEvalStackMMany();
void popEvalStackLMany();
void popEvalStackMany(int len, char symFlavor);
void popEvalStackCVMany(int len);
void pushEvalStack(char symFlavor);
+2 -4
Ver Arquivo
@@ -220,8 +220,7 @@ void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
"Access type for interface method %s::%s() must be omitted",
classScope->getOriginalName().c_str(), getOriginalName().c_str());
}
// FIXME: WholeProgram check is temporary (t3044335)
if (!Option::WholeProgram && m_modifiers->isAsync()) {
if (m_modifiers->isAsync()) {
m_modifiers->parseTimeFatal(
Compiler::InvalidAttribute,
Strings::ASYNC_WITHOUT_BODY,
@@ -254,8 +253,7 @@ void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
classScope->getOriginalName().c_str(),
getOriginalName().c_str());
}
// FIXME: WholeProgram check is temporary (t3044335)
if (!Option::WholeProgram && m_modifiers->isAsync()) {
if (m_modifiers->isAsync()) {
m_modifiers->parseTimeFatal(
Compiler::InvalidAttribute,
Strings::ASYNC_WITHOUT_BODY,
+27 -42
Ver Arquivo
@@ -9,7 +9,7 @@ Introduction
HipHop bytecode (HHBC) v1 is intended to serve as the conceptual basis for
encoding the semantic meaning of HipHop source code into a format that is
appropriate for consumption by interpreters and just-in-time compilers. By
using simpler constructs to encode more complex expressions and statements,
using simpler constructs to encode more complex expressesion and statements,
HHBC makes it straightforward for an interpreter or a compiler to determine
the order of execution for a program.
@@ -24,7 +24,7 @@ code into HipHop bytecode in a way that preserves the semantic meaning of the
source.
3) Simplicity. The design of HHBC should avoid features that could be removed
or simplified without compromising PHP 5.4 compatibility, run-time efficiency,
or simplified without comprimising PHP 5.4 compatibility, run-time efficiency,
or design cleanliness.
@@ -111,7 +111,7 @@ logic.
A "ref" is a structure that contains a pointer to a cell container. When a
ref is duplicated, the new ref will point to the same container as the
original ref. When a ref is duplicated or destroyed, the execution engine is
responsible for honoring the container's refcount logic. When the container
responsible for honoring the containers's refcount logic. When the container
is destroyed, the cell it contains is also destroyed.
A "classref" is a structure that contains a reference to a class. When a
@@ -126,7 +126,7 @@ Functions
---------
A unit's bytecode is organized into functions. Each function has its own
metadata that provides essential information about the function, such as the
metadata that provdes essential information about the function, such as the
name of the function, how many local variables it has, how many iterator
variables it has, how many formal parameters it has, the names of the local
variables, the names of the formal parameters, how each parameter should be
@@ -153,7 +153,7 @@ in the current variable environment.
Formally declared parameters are considered to be local variables. Given a
function with n formally declared parameters, local ids 0 through n-1 will be
used to reference the formally declared parameters. Formal parameters without
default values are called "required parameters", while formal parameters with
default values are called "required parameters", while formal parmeters with
default values are called "optional parameters".
The bytecode for each function consists of the instructions of the primary
@@ -163,7 +163,7 @@ the primary function body, along with information about each fault funclet.
Entry points and fault funclets are discussed in more detail in the next
section. The total size of the bytecode for the primary function body and all
the fault funclets must not exceed 2^31 - 1 bytes. The primary function body
and each fault funclet must be a contiguous range of bytecode.
and each fault funclet must be a continguous range of bytecode.
Each function's metadata provides a "line number table" to allow mapping
bytecode offsets back to source line numbers. Each row in the line number table
@@ -221,7 +221,7 @@ Exceptions
The metadata for each function provides an "exception handler (EH) table".
Each row in the EH table consists of a kind ("fault" or "catch"), a range
of bytecode that constitutes the protected region, and an offset of a fault
of bytecode that constitues the protected region, and an offset of a fault
funclet or list of (classname, offset) pairs describing catch entry points.
Each range of bytecode is given by a starting offset and an ending
@@ -270,13 +270,8 @@ Unit metadata
Every compilation unit has a litstr table, a scalar array table, a function
table, and a class table.
The litstr table maps litstr ids to literal strings. Bytecodes that refer to
literal strings do so by litstr id. Litstr ids are signed 32-bit integer
values, which must be between 0 and 2^31 - 2 inclusive. In addition to the
per-unit litstr tables, a global table is built when generating an
"authoritative" repo (one in which all the PHP code is known at bytecode
generation time, and is guaranteed not to change). Global litstr ids can be
used in any unit, and are encoded in the range [2^30..2^31-2].
The litstr table maps litstr ids to literal strings. Litstr ids are signed
32-bit integer values. Each litstr id must be between 0 and 2^31 - 2 inclusive.
The scalar array table maps scalar array ids to a description of the contents
of a scalar array. An array is a scalar array if and only if each element of
@@ -325,7 +320,7 @@ 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
Calls to builtin functions may be optimized to avoid pusing an entry on the FPI
stack if it is known that the builtin function does not need access to the call
stack. In this case, the arguments to the builtin are pushed on stack as Cells
and Vars, and the builtin can be invoked with the FCallBuiltin instruction.
@@ -364,7 +359,7 @@ responsible for following certain rules to honor each property's accessibility
and visibility.
The accessibility and visibility of a property in a given class is determined
by that class's definition and the definitions of all of that class's ancestors.
by that class's definition and the definitons of all of that class's ancestors.
When a property is declared in a class definition (a "declared property") it
may be specified as being "public", "protected", or "private". Accessibility
and visibility are two related but distinct concepts. Depending on the current
@@ -392,13 +387,13 @@ the ancestor class. Note that there may exist a class D that is a descendent of
C and declares a property as "public" with the same name as P. In such cases
the new "public" declaration in D is considered to refer to the same property
as the original "protected" declaration in C, and the "protected" qualifier
from the original declaration is effectively overridden by the "public"
from the original declaration is effectively overriden by the "public"
qualifier from the new declaration. Class D is said to "redeclare" property P
with the "public" qualifier. Thus, for instances of class D and descendent
classes of D, property P will be visible and accessible in all contexts.
Finally, if a class E that is descendent of C does not redeclare P as public
and does not have an ancestor class that redeclares P as public, for instances
of class E the property P will be visible in all contexts, but only accessible
of class E the property P will be visibile in all contexts, but only accessible
in the context of class E, an ancestor class of E, or a descendent class of E.
If a property P is declared with the "private" qualifier in the definition of
@@ -459,7 +454,7 @@ engine is responsible for following certain rules to honor each static
property's accessibility and visibility.
The accessibility and visibility of a static property in a given class is
determined by that class's definition and the definitions of all of that class's
determined by that class's definition and the definitons of all of that class's
ancestors. When a static property is declared in a class definition it may be
specified as being "public", "protected", or "private". Depending on the
current context, a static property may be visible and accessible, visible but
@@ -518,7 +513,7 @@ not declared in the class definition.
FPI regions
-----------
An FPI region is a contiguous range of bytecode that constitutes a call site.
An FPI region is a continguous range of bytecode that constitutes a call site.
Each FPI region begins immediately after an FPush* instruction that pushes an
FPI structure onto the FPI stack and must end with the corresponding FCall*
instruction that pops that FPI structure off of the FPI stack. If two FPI
@@ -557,8 +552,6 @@ Here is a description of each flavor descriptor:
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
Verifiability
@@ -571,7 +564,7 @@ inputs to each instruction is said to be "flavor-safe".
HHBC provides a set of verification rules that can be mechanically applied to
verify that an HHBC program is flavor-safe. All valid HHBC programs must be
verifiably flavor-safe, and the execution engine may refuse to execute HHBC
verifiably flavor-safe, and the execution engine may refuse to execute HHBC
programs that cannot be verified.
At bytecode generation time, what is known about the state of the evaluation
@@ -767,7 +760,7 @@ number of cells from the stack. In most cases the immediate vector arguments
are ordered such that the loc-desc comes first (deepest in the stack), with the
last M-vector element last (shallowest in the stack). However, classrefs that
are part of the BaseSC and BaseSL loc-desc inputs always come last (the cell
input to BaseSC comes first though). Instructions accepting an immediate vector
input to BaseSC comes first though). Instuctions accepting an immediate vector
containing a list of iterators and iterator types use the notation "<iter-vec>".
In addition to describing each instruction, this instruction set documentation
@@ -843,9 +836,9 @@ False [] -> [C:Bool]
Push constant. Null pushes null onto the stack, True pushes true onto
the stack, and False pushes false onto the stack.
NullUninit [] -> [U]
NullUninit [] -> [C:Uninit]
Push an uninitialized null on the stack.
Push Uninit Null Cell onto stack.
Int <signed 64-bit integer value> [] -> [C:Int]
Double <double value> [] -> [C:Dbl]
@@ -1094,7 +1087,7 @@ Shr [C C] -> [C:Int]
Floor [C] -> [C:Dbl]
Round $1 to nearest integer value not greater than $1. Converts $1 to
Round $1 to nearest integer value not greater than $1. Converts $1 to
numeric as appropriate and then takes floor of resulting numeric value.
Ceil [C] -> [C:Dbl]
@@ -1854,7 +1847,7 @@ FCallArray [F] -> [R]
call the callee. When the callee returns, it will transfer the return value
onto the caller's evaluation stack using the R flavor.
FCallBuiltin <total params> <passed params> <litstr id> [C|V|U..C|V|U] -> [R]
FCallBuiltin <total params> <passed params> <litstr id> [C|V..C|V] -> [R]
Optimized builtin call without an ActRec. This instruction attempts to
lookup a builtin function named %3. If no function named %3 is defined,
@@ -3532,8 +3525,8 @@ CIterFree <iterator id> [] -> []
IterBreak <iter-vec> <rel offset> [] -> []
Iterator break. Frees vectors in %1 in left to right order then transfers
control to the locaton specified by %2. Surprise checks are performed
Iterator break. Frees vectors in %1 in left to right order then transfers
control to the locaton specified by %2. Surprise checks are performed
before iterators are freed so that in the event of an exception iterators
are not double freed. Note that as with normal jumps surprise checks will
only be performed if %2 is negative.
@@ -3635,7 +3628,7 @@ This [] -> [C:Obj]
instruction throws a fatal error. Next, this instruction pushes the current
instance onto the stack.
BareThis <notice> [] -> [C:Obj|Null]
BareThis <notice> [] -> [C:Obj]
This. This instruction pushes the current instance onto the stack. If %1 is
not zero, and the current instance is null, emits a notice.
@@ -3750,14 +3743,6 @@ ArrayIdx [C C C] -> [C]
if found. Otherwise, $1 is pushed onto the stack. A fatal error will be
thrown if $3 is not an array.
AssertTL <local id> <op>
AssertTStk <stack offset> <op>
These opcodes are currently only supplied for debugging purposes. They
instruct the runtime that the value in the specified local or stack offset
must have a particular type. The type sub-opcodes are not currently
specified, as they are particular to the implementation.
14. Continuation creation and execution
---------------------------------------
@@ -3770,9 +3755,9 @@ CreateCont <function name> [] -> [C]
CreateAsync <function name> <label id> <number of iterators> [C] -> [C]
Analogy of CreateCont for async functions. Creates a Continuation object,
updates its label with <label id> immediate and stores a value from the stack
to its m_value field. Unlike CreateCont, it also copies the first
Analogy of CreateCont for async functions. Creates a Continuation object,
updates its label with <label id> immediate and stores a value from the stack
to its m_value field. Unlike CreateCont, it also copies the first
<number of iterators> iterators into the continuation frame.
ContEnter [C] -> []
+90 -81
Ver Arquivo
@@ -65,14 +65,14 @@ but may not form loops.
No Variables are defined on entry to the main Trace.
Blocks which are join points may start with a DefLabel with destination
Variables. In that case, each predecessor must be a Jmp passing a matching
number of source Variables. In this case the Jmp acts as a tail-call, passing
Variables. In that case, each predecessor must be a Jmp_ passing a matching
number of source Variables. In this case the Jmp_ acts as a tail-call, passing
arguments the same way a plain call would.
Together, the sources of the Jmp instructions and the destinations of the
Together, the sources of the Jmp_ instructions and the destinations of the
DefLabel instructions act as traditional SSA Phi pseudo-functions; The type
of the label's destination is the type-union of the corresponding sources.
Because the Jmp sources are at the ends of blocks, they do not violate the
Because the Jmp_ sources are at the ends of blocks, they do not violate the
SSA dominator rule (rule 2, above).
Implementation note: The JIT's compilation unit ("Trace" in this spec) actually
@@ -337,23 +337,23 @@ approach is that boxed values may alias. Therefore, in lack of memory
alias analysis, the inner types generally need to be rechecked before
each use.
D:T = CheckType<T> S0:Gen -> B
D:T = CheckType<T> S0:Gen -> L
Check that the type of the src S0 is T, and if so copy it to D. If
S0 is not type T, branch to block B.
S0 is not type T, branch to the label L.
CheckNullptr S0:{CountedStr|NullPtr} -> B
CheckNullptr S0:{CountedStr|NullPtr} -> L
If S0 is a null pointer, branch to block B. This is used to check the
return value of a native helper that returns a potentially null StringData*.
If S0 is a null pointer, branch to L. This is used to check the return value
of a native helper that returns a potentially null StringData*.
D:T = AssertType<T> S0:{Gen|Nullptr}
Assert that the type of S0 is T, copying it to D.
CheckTypeMem<T> S0:PtrToGen -> B
CheckTypeMem<T> S0:PtrToGen -> L
If the value pointed to by S0 is not type T, branch to the block B.
If the value pointed to by S0 is not type T, branch to the label L.
D:FramePtr = GuardLoc<T,localId> S0:FramePtr
@@ -364,10 +364,10 @@ D:FramePtr = GuardLoc<T,localId> S0:FramePtr
Returns a new frame pointer representing the same frame as S0 but with the
knowledge that the guarded local has type T.
D:FramePtr = CheckLoc<T,localId> S0:FramePtr -> B
D:FramePtr = CheckLoc<T,localId> S0:FramePtr -> L
Check that type of the given localId on the frame S0 is T; if not,
branch to block B.
branch to the label L.
Returns a new frame pointer representing the same frame as S0 but with the
knowledge that the checked local has type T.
@@ -407,10 +407,10 @@ D:StkPtr = GuardStk<T,offset> S0:StkPtr
Returns a new StkPtr that represents the same stack but with the
knowledge that the slot at the index S1 has type T.
D:StkPtr = CheckStk<T,offset> S0:StkPtr -> B
D:StkPtr = CheckStk<T,offset> S0:StkPtr -> L
Check that the type of the cell on the stack pointed to by S0 at
offset (in cells) is T; if not, branch to block B.
offset (in cells) is T; if not, branch to the label L.
Returns a new StkPtr that represents the same stack but with the
knowledge that the slot at the index S1 has type T.
@@ -432,31 +432,31 @@ D:StkPtr = CastStk<T,offset> S0:StkPtr
Returns a new StkPtr that represents the same stack as S0, but with
the slot at offset (in cells) converted to type T.
D:StkPtr = CoerceStk<T,offset> S0:StkPtr -> B
D:StkPtr = CoerceStk<T,offset> S0:StkPtr -> L
Returns a new StkPtr that represents the same stack as S0, but with
the slot at offset (in cells) converted to type T. If the type conversion
can't be done then branches to block B.
can't be done then branches to label L.
CheckInit S0:Gen -> B
CheckInit S0:Gen -> L
If S0's type is Uninit, branch to block B.
If S0's type is Uninit, branch to label L.
CheckInitMem S0:PtrToGen S1:ConstInt -> B
CheckInitMem S0:PtrToGen S1:ConstInt -> L
If the value at S0 + S1 (in bytes) has type Uninit, branch to block B.
If the value at S0 + S1 (in bytes) has type Uninit, branch to L.
CheckDefinedClsEq<className,classPtr> -> B
CheckDefinedClsEq<className,classPtr> -> L
Compare the currently defined class of name `className' with
`classPtr'; if they aren't equal or if `className' is not defined,
branch to block B.
branch to L.
CheckCold<TransID> -> B
CheckCold<TransID> -> L
Check if the counter associated with translation TransID is cold
(i.e. within a fixed threshold). If it's not (i.e. such translation
has reached the "hotness threshold"), then branch to block B.
has reached the "hotness threshold"), then branch to label L.
GuardRefs S0:FuncPtr S1:Int S2:Int S3:Int S4:Int S5:Int
@@ -476,10 +476,10 @@ D:CountedStr = AssertNonNull S0:{Nullptr|CountedStr}
Returns S0, with Nullptr removed from its type. This instruction currently
supports a very limited range of types but can be expanded if needed.
CheckStaticLocInit S0:BoxedCell -> B
CheckStaticLocInit S0:BoxedCell -> L
Check if the static local (RDS) RefData represented by S0 is
initialized, and if not branch to block B.
initialized, and if not branch to L.
2. Arithmetic
@@ -492,7 +492,7 @@ D:{Int|Dbl} = OpSub S0:{Int|Dbl} S1:{Int|Dbl}
D:{Int|Dbl} = OpMul S0:{Int|Dbl} S1:{Int|Dbl}
D:Int = OpMod S0:Int S1:Int
D:Dbl = OpSqrt S0:Dbl
D:Dbl = OpDivDbl S0:Dbl S1:Dbl -> B
D:Dbl = OpDivDbl S0:Dbl S1:Dbl -> L
D:Int = OpBitAnd S0:Int S1:Int
D:Int = OpBitOr S0:Int S1:Int
D:Int = OpBitXor S0:Int S1:Int
@@ -510,10 +510,10 @@ D:Dbl = OpCeil S0:Dbl
AbsInt and AbsDbl compute the absolute value of an integer or double
respectively.
OpDivDbl Will branch to block B when S1 is zero (signed or unsigned). When
the result of the division is a real valued number OpDivDbl conforms to IEEE
754. In particular should the result of a division be zero the sign will
follow normal sign rules for division.
OpDivDbl Will branch to L when S1 is zero (signed or unsigned). When the
result of the division is a real valued number OpDivDbl conforms to IEEE 754.
In particular should the result of a division be zero the sign will follow
normal sign rules for division.
Note that OpShr is an arithmetic right shift.
@@ -665,10 +665,10 @@ D:None JmpSwitchDest<JmpSwitchData> S0:Int
Jump to the target of a switch stamement using table metadata
<JmpSwitchData> and index S0.
CheckSurpriseFlags -> B
CheckSurpriseFlags -> L
Tests the implementation-specific surprise flags. If they're true, branches
to block B.
to L.
SurpriseHook
@@ -680,26 +680,33 @@ FunctionExitSurpriseHook S0:FramePtr S1:StkPtr S2:Gen
should be the current frame pointer, S1 should be the current stack pointer,
and S2 should be the return value of the function.
ExitOnVarEnv S0:FramePtr -> B
ExitOnVarEnv S0:FramePtr -> L
Loads the VarEnv slot off the ActRec pointed to by S0. If it is
non-zero, jumps to the exit-trace block B.
non-zero, jumps to the exit-trace label L.
Jmp -> B
Jmp [S:T ...] -> B: DefLabel
Jmp_ [S:T ...] -> L
Unconditional jump to block B. In the second form, the target block must
start with a DefLabel with the same number of destinations as Jmp's number
of sources. Jmp parallel-copies its sources to the DefLabel destinations.
Unconditional jump to L. L is the name of a DefLabel instruction.
Each the sources are copied to the corresponding destinations of the
target L. The number of destinations on L must match the number of
sources of this Jmp_.
DefLabel
D:T ... = DefLabel
DefLabel defines variables received from a previous Jmp. A DefLabel with
zero destinations is a no-op, and the predecessor blocks may not necessarily
end in Jmp. A DefLabel with one or more destinations may only be reached
by a Jmp instruction with the same number of sources. Ordinary branch
instructions may not pass values to a DefLabel.
Denote the position of a jump target. Branch instructions, and
instructions which implicitly guard, take a Label argument which refers
to a specific DefLabel instruction. Additionally, every Trace must begin
with a DefLabel instruction.
DefLabel can optionally define one or more destination variables. In that
case, the values are passed to it by Jmp_ instruction(s) with the same
number of sources. The type of each destination variable is the least common
supertype of each of the corresponding Jmp_ sources.
Note that when a label defines destination variables, it is only reachable
from the Jmp_ instruction. Other branch instructions cannot pass arguments.
6. Reference manipulation
@@ -774,37 +781,38 @@ D:PtrToCell = LdPairBase S0:Obj<Pair>
Loads the base pointer to an array of Cells from the given collection
instance in S0.
D:T = LdMem<T> S0:PtrToGen S1:ConstInt [ -> B ]
D:T = LdMem<T> S0:PtrToGen S1:ConstInt [ -> L ]
Loads from S0 + S1 (in bytes) and puts the value in D. If the
optional target block B is specified and the loaded value's type does not
match T, this instruction does not load into D and branches to block B.
optional label L is specified and the loaded value's type does not
match T, this instruction does not load into D and transfers control
to L.
D:T = LdProp<T> S0:Obj S1:ConstInt [ -> B ]
D:T = LdProp<T> S0:Obj S1:ConstInt [ -> L ]
Loads a property from the object referenced by S0 at the offset
given by S1 and puts the value in D. If the optional target B is
given by S1 and puts the value in D. If the optional label L is
specified and the loaded value's type does not match T, this
instruction does not load into D and branches to block B.
instruction does not load into D and transfers control to L.
D:Cell = LdElem S0:PtrToCell S1:Int
Loads the element at index S1 from the base pointer in S0.
The index in S1 is the number of bytes from the base in S0.
D:T = LdRef<T> S0:Cell& [ -> B ]
D:T = LdRef<T> S0:Cell& [ -> L ]
Loads the value held in the box referenced by S0 and puts the value
in D. If the optional target B is specified and the loaded value's
in D. If the optional label L is specified and the loaded value's
type does not match T, this instruction does not load into D and
branches to block B.
transfers control to L.
D:Obj = LdThis S0:FramePtr [ -> B ]
D:Obj = LdThis S0:FramePtr [ -> L ]
Loads the this pointer out of the ActRec pointed to by S0, and puts
it in D. If the optional block B is supplied, if the this pointer
in S0 is null, this instruction does not load it into D and branches
to block B.
it in D. If the optional label L is supplied, if the this pointer
in S0 is not, this instruction does not load it into D and transfers
control to the exit label L.
D:Ctx = LdCtx<func> S0:FramePtr
@@ -865,20 +873,20 @@ D:Cls = LdClsCached S0:ConstStr
Loads the class named S0 via the RDS. Invokes autoload and may raise
an error if the class is not defined.
D:Cls = LdClsCachedSafe S0:ConstStr [ -> B ]
D:Cls = LdClsCachedSafe S0:ConstStr [ -> L ]
Loads the class whose name is S0 out of the RDS. If the class is not
defined, returns null and optionally branches to block B.
defined, returns null and optionally branches to L.
D:T = LdClsCns<T,className,constName> [ -> B ]
D:T = LdClsCns<T,className,constName> [ -> L ]
Loads the named class constant for a class via the RDS. This
instruction should generally be followed by CheckInit, unless we
know the class is already loaded.
If the optional block B is specified and the loaded value's type
If the optional label L is specified and the loaded value's type
does not match T, this instruction does not load into D and
branches to block B.
transfers control to L.
The result may be uninitialized if the class is not defined. Note
that no decref of the result is necessary because class constants
@@ -942,9 +950,9 @@ D:PtrToGen = LdPropAddr S0:Obj S1:ConstInt
Load the address of the object property for S0 at offset S1 (in
bytes) into D.
D:PtrToGen = LdGblAddr S0:Str -> B
D:PtrToGen = LdGblAddr S0:Str -> L
Loads a pointer to a global. S0 is the global's name. Branches to B
Loads a pointer to a global. S0 is the global's name. Branches to L
if the global is not defined.
D:PtrToGen = LdGblAddrDef S0:Str
@@ -953,24 +961,24 @@ D:PtrToGen = LdGblAddrDef S0:Str
global if it is not already defined. Decrements the reference count
of S0.
D:PtrToGen = LdClsPropAddr S0:Cls S1:Str S2:ConstCls [ -> B ]
D:PtrToGen = LdClsPropAddr S0:Cls S1:Str S2:ConstCls [ -> L ]
Loads a pointer to a static class property. S0 points to the class, S1 is the
property name, and S2 is the class representing the context of the code
accessing the property. If class S0 does not have a visible and accessible
static property named S1, then this instruction will either (1) jump to B if
static property named S1, then this instruction will either (1) jump to L if
it is present and not a catch trace, or (2) throw a fatal error.
D:PtrToGen = LdClsPropAddrCached S0:Cls S1:ConstStr S2:ConstStr
S3:ConstCls [ -> B ]
S3:ConstCls [ -> L ]
Loads a pointer to a static class property via the RDS. S0
points to the class, S1 is the property name, S2 is the class name,
and S3 is the class representing the context of the code accessing
the property. If class S0 does not have a visible and accessible
static property named S1, then this instruction will either (1)
throw a fatal error if the optional block B is not given, or (2)
jump to B if it is present.
throw a fatal error if the optional label L is not present, or (2)
jump to L if it is present.
LdObjMethod S0:Cls S1:ConstStr S2:StkPtr
@@ -980,10 +988,10 @@ LdObjMethod S0:Cls S1:ConstStr S2:StkPtr
cache. Fatals if the class does not have an accessible method with
the given name and does not have a __call method.
D:Func = LdObjInvoke S0:Cls -> B
D:Func = LdObjInvoke S0:Cls -> L
Try to load a cached non-static __invoke Func from the Class in S0,
or branch to block B if it is not present.
or branch to L if it is not present.
D:Cls = LdObjClass S0:Obj
@@ -1000,10 +1008,10 @@ D:Func = LdFuncCached<funcName>
autoload if it not defined yet. Fatal if function autoloader fails
to define it.
D:Func = LdFuncCachedSafe<funcName> [ -> B ]
D:Func = LdFuncCachedSafe<funcName> [ -> L ]
Try to load the Func named funcName from the RDS. If the function
is not defined, returns null and optionally branches to B.
is not defined, returns null and optionally branches to L.
D:Func = LdFuncCachedU<funcName,fallbackName>
@@ -1191,11 +1199,11 @@ D:StkPtr = RetAdjustStack S0:FramePtr
Loads the new VM stack pointer into the destination. S0 is a
pointer to the current activation record.
ReleaseVVOrExit S0:FramePtr -> B
ReleaseVVOrExit S0:FramePtr -> L
Loads the VarEnv slot off the ActRec pointed to by S0. If it is
null, does nothing. If it is an ExtraArgs, deallocates the
ExtraArgs structure. Otherwise jumps to block B.
ExtraArgs structure. Otherwise jumps to the exit-trace label L.
D:StkPtr = GenericRetDecRefs S0:FramePtr S1:ConstInt
@@ -1644,6 +1652,7 @@ D:T = Reload S0:T
Loads from a spilled temporary S0, and stores the result in D.
16. Continuations & Closures
@@ -1685,16 +1694,16 @@ ContEnter S0:FramePtr S1:TCA S2:ConstInt S3:FramePtr
object. S1 is the address to jump to. S2 is the bytecode offset in the
caller to return to when the generator body yields. S3 is the current frame.
ContPreNext S0:Obj -> B
ContPreNext S0:Obj -> L
Performs operations needed for the Continuation::next() method. This
includes checking m_done and m_running---if either is not the case
branches to block B.
branches to the label L.
ContStartedCheck S0:Obj -> B
ContStartedCheck S0:Obj -> L
Checks if the continuation object S0 has started, and if not
branches to block B.
branches to the label L.
ContSetRunning S0:Obj S1:ConstBool
+2 -2
Ver Arquivo
@@ -22,7 +22,7 @@ namespace HPHP {
// ArrayInit
HOT_FUNC
ArrayInit::ArrayInit(ssize_t n)
ArrayInit::ArrayInit(size_t n)
#ifdef DEBUG
: m_addCount(0)
, m_expectedCount(n)
@@ -37,7 +37,7 @@ ArrayInit::ArrayInit(ssize_t n)
}
HOT_FUNC
ArrayInit::ArrayInit(ssize_t n, MapInit)
ArrayInit::ArrayInit(size_t n, MapInit)
: m_data(HphpArray::MakeReserve(n))
#ifdef DEBUG
, m_addCount(0)
+2 -2
Ver Arquivo
@@ -27,9 +27,9 @@ namespace HPHP {
struct ArrayInit {
enum MapInit { mapInit };
explicit ArrayInit(ssize_t n);
explicit ArrayInit(size_t n);
ArrayInit(ssize_t n, MapInit);
ArrayInit(size_t n, MapInit);
ArrayInit(ArrayInit&& other)
: m_data(other.m_data)
+5
Ver Arquivo
@@ -1198,6 +1198,11 @@ bool AutoloadHandler::autoloadType(const String& name) {
*/
bool AutoloadHandler::invokeHandler(const String& className,
bool forceSplStack /* = false */) {
if (className.empty()) {
return false;
}
if (!m_map.isNull()) {
ClassExistsChecker ce;
Result res = loadFromMap(className, s_class, true, ce);
+2 -5
Ver Arquivo
@@ -176,11 +176,8 @@ inline Variant throw_missing_class(const char *cls) {
throw ClassNotFoundException((std::string("unknown class ") + cls).c_str());
}
inline Variant throw_missing_file(const char *file) {
if (file[0] == '\0') {
throw NoFileSpecifiedException();
}
throw PhpFileDoesNotExistException(file);
inline Variant throw_missing_file(const char *cls) {
throw PhpFileDoesNotExistException(cls);
}
void throw_instance_method_fatal(const char *name);
+1 -16
Ver Arquivo
@@ -25,7 +25,7 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// definitions
enum DataType : int8_t {
enum DataType: int8_t {
KindOfClass = -13,
MinDataType = -13,
@@ -277,21 +277,6 @@ inline DataType typeInitNull(DataType t) {
return t == KindOfUninit ? KindOfNull : t;
}
/*
* Returns whether two DataTypes for primitive types are "equivalent"
* as far as user-visible php types are concerned. (I.e. ignoring
* different types of strings or different types of nulls.)
*
* Pre: t1 and t2 must both be DataTypes that represent php-types.
* (Non-internal KindOfs.)
*/
inline bool equivDataTypes(DataType t1, DataType t2) {
return
(t1 == t2) ||
(IS_STRING_TYPE(t1) && IS_STRING_TYPE(t2)) ||
(IS_NULL_TYPE(t1) && IS_NULL_TYPE(t2));
}
///////////////////////////////////////////////////////////////////////////////
} // namespace HPHP
-4
Ver Arquivo
@@ -45,10 +45,6 @@ int emulate_zend(int argc, char** argv){
vector<string> newargv;
newargv.push_back(argv[0]);
#ifdef PHP_DEFAULT_HDF
newargv.push_back("-c");
newargv.push_back(PHP_DEFAULT_HDF);
#endif
bool lint = false;
bool show = false;
-15
Ver Arquivo
@@ -300,25 +300,10 @@ class PhpFileDoesNotExistException : public ExtendedException {
public:
explicit PhpFileDoesNotExistException(const char *file)
: ExtendedException("File could not be loaded: %s", file) {}
explicit PhpFileDoesNotExistException(const char *msg, bool empty_file)
: ExtendedException("%s", msg) {
assert(empty_file);
}
virtual ~PhpFileDoesNotExistException() throw() {}
EXCEPTION_COMMON_IMPL(PhpFileDoesNotExistException);
};
class NoFileSpecifiedException : public PhpFileDoesNotExistException {
public:
explicit NoFileSpecifiedException()
: PhpFileDoesNotExistException(
"Nothing to do. Either pass a .php file to run, or use -m server",
true
) {}
virtual ~NoFileSpecifiedException() throw() {}
EXCEPTION_COMMON_IMPL(NoFileSpecifiedException);
};
class RequestTimeoutException : public FatalErrorException {
public:
RequestTimeoutException(const std::string &msg, const Array& backtrace)
+1 -1
Ver Arquivo
@@ -635,7 +635,7 @@ public:
VMParserFrame* parserFrame = nullptr,
bool ignoreArgs = false,
int limit = 0);
VarEnv* getVarEnv();
VarEnv* getVarEnv(int frame = 0);
void setVar(StringData* name, TypedValue* v, bool ref);
Array getLocalDefinedVariables(int frame);
HPHP::PCFilter* m_breakPointFilter;
+1 -1
Ver Arquivo
@@ -140,7 +140,7 @@ std::pair<uint32_t,uint32_t> computeCapAndMask(uint32_t minimumMaxElms) {
}
ALWAYS_INLINE
size_t computeAllocBytes(uint32_t cap, uint32_t mask) {
uint32_t computeAllocBytes(uint32_t cap, uint32_t mask) {
auto const tabSize = mask + 1;
auto const tabBytes = tabSize * sizeof(int32_t);
auto const dataBytes = cap * sizeof(HphpArray::Elm);
+2 -5
Ver Arquivo
@@ -253,7 +253,7 @@ int RuntimeOption::ProxyPercentage = 0;
std::set<std::string> RuntimeOption::ProxyURLs;
std::vector<std::string> RuntimeOption::ProxyPatterns;
bool RuntimeOption::AlwaysUseRelativePath = false;
std::string RuntimeOption::IniFile = "/etc/hhvm/php.ini";
std::string RuntimeOption::IniFile;
int RuntimeOption::HttpDefaultTimeout = 30;
int RuntimeOption::HttpSlowQueryThreshold = 5000; // ms
@@ -849,11 +849,8 @@ void RuntimeOption::Load(Hdf &config, StringVec *overwrites /* = NULL */,
UseDirectCopy = server["UseDirectCopy"].getBool(false);
AlwaysUseRelativePath = server["AlwaysUseRelativePath"].getBool(false);
IniFile = server["IniFile"].getString(IniFile);
IniFile = server["IniFile"].getString("/etc/hhvm/php.ini");
if (access(IniFile.c_str(), R_OK) == -1) {
if (IniFile != "/etc/hhvm/php.ini") {
Logger::Error("INI file doens't exist: %s", IniFile.c_str());
}
IniFile.clear();
}
+15 -6
Ver Arquivo
@@ -118,13 +118,22 @@ char *string_to_case(const char *s, int len, int (*tocase)(int));
char *string_to_case_first(const char *s, int len, int (*tocase)(int));
char *string_to_case_words(const char *s, int len, int (*tocase)(int));
#define string_to_upper(s,len) string_to_case((s), (len), toupper)
#define string_to_upper_first(s, len) string_to_case_first((s), (len), toupper)
#define string_to_upper_words(s, len) string_to_case_words((s), (len), toupper)
// Use lambdas wrapping the ctype.h functions because of linker weirdness on
// OS X Mavericks.
#define string_to_lower(s,len) string_to_case((s), (len), tolower)
#define string_to_lower_first(s, len) string_to_case_first((s), (len), tolower)
#define string_to_lower_words(s, len) string_to_case_words((s), (len), tolower)
#define string_to_upper(s,len) \
string_to_case((s), (len), [] (int i) -> int { return toupper(i); })
#define string_to_upper_first(s, len) \
string_to_case_first((s), (len), [] (int i) -> int { return toupper(i); })
#define string_to_upper_words(s, len) \
string_to_case_words((s), (len), [] (int i) -> int { return toupper(i); })
#define string_to_lower(s,len) \
string_to_case((s), (len), [] (int i) -> int { return tolower(i); })
#define string_to_lower_first(s, len) \
string_to_case_first((s), (len), [] (int i) -> int { return tolower(i); })
#define string_to_lower_words(s, len) \
string_to_case_words((s), (len), [] (int i) -> int { return tolower(i); })
/**
+2 -2
Ver Arquivo
@@ -210,8 +210,8 @@ extern void *MALLOC(size_t);
#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + defined(VAX) + \
defined(IBM) != 1
Exactly one of IEEE_LITTLE_ENDIAN IEEE_BIG_ENDIAN, VAX, or
IBM should be defined.
#error Exactly one of IEEE_LITTLE_ENDIAN, IEEE_BIG_ENDIAN, VAX, or IBM \
should be defined.
#endif
typedef union {
+13 -2
Ver Arquivo
@@ -16,6 +16,7 @@
#include "hphp/runtime/debugger/cmd/cmd_global.h"
#include "hphp/runtime/debugger/cmd/cmd_variable.h"
#include "hphp/runtime/base/hphp-system.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
@@ -30,6 +31,7 @@ void CmdGlobal::sendImpl(DebuggerThriftBuffer &thrift) {
void CmdGlobal::recvImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::recvImpl(thrift);
thrift.read(m_globals);
if (m_version == 1) m_version = 2;
}
void CmdGlobal::help(DebuggerClient &client) {
@@ -62,13 +64,22 @@ void CmdGlobal::onClient(DebuggerClient &client) {
if (cmd->m_globals.empty()) {
client.info("(no global variable was found)");
} else {
m_globals = cmd->m_globals;
CmdVariable::PrintVariables(client, cmd->m_globals, true, text);
CmdVariable::PrintVariables(client, cmd->m_globals, -1,
text, cmd->m_version);
}
}
bool CmdGlobal::onServer(DebuggerProxy &proxy) {
m_globals = CmdVariable::GetGlobalVariables();
if (m_version == 2) {
// Remove the values before sending to client.
ArrayInit ret(m_globals->size());
Variant v;
for (ArrayIter iter(m_globals); iter; ++iter) {
ret.add(iter.first().toString(), v);
}
m_globals = ret.toArray();
}
return proxy.sendToClient(this);
}
+3 -1
Ver Arquivo
@@ -25,7 +25,9 @@ namespace HPHP { namespace Eval {
DECLARE_BOOST_TYPES(CmdGlobal);
class CmdGlobal : public DebuggerCommand {
public:
CmdGlobal() : DebuggerCommand(KindOfGlobal) {}
CmdGlobal() : DebuggerCommand(KindOfGlobal) {
m_version = 1;
}
virtual void help(DebuggerClient &client);
+9 -8
Ver Arquivo
@@ -158,7 +158,7 @@ bool CmdList::listFileRange(DebuggerClient &client,
const StaticString
s_methods("methods"),
s_file("file"),
s_line1("line2"),
s_line1("line1"),
s_line2("line2");
// Sends an Info command to the server to retrieve source location
@@ -278,6 +278,9 @@ void CmdList::onClient(DebuggerClient &client) {
} else {
if (!DebuggerClient::IsValidNumber(arg)) {
if (m_file.empty()) {
if (client.argCount() == 1 && listFunctionOrClass(client)) {
return;
}
m_file = arg;
m_line1 = 1;
m_line2 = DebuggerClient::CodeBlockSize;
@@ -320,12 +323,9 @@ void CmdList::onClient(DebuggerClient &client) {
}
}
if (listFileRange(client, line, charFocus0, lineFocus1, charFocus1)) {
return;
} else if (client.argCount() != 1 || !listFunctionOrClass(client)) {
client.error(
"Unable to read specified function, class or source file location.");
return;
if (!listFileRange(client, line, charFocus0, lineFocus1, charFocus1)) {
client.error(
"Unable to read specified function, class or source file location.");
}
}
@@ -350,7 +350,8 @@ bool CmdList::onServer(DebuggerProxy &proxy) {
}
}
RuntimeOption::WarningFrequency = savedWarningFrequency;
if (!m_code.toBoolean() && m_file == "systemlib.php") {
if (!m_code.toBoolean() &&
m_file.find("systemlib.php") == m_file.length() - 13) {
m_code = SystemLib::s_source;
}
return proxy.sendToClient((DebuggerCommand*)this);
+2 -19
Ver Arquivo
@@ -15,7 +15,6 @@
*/
#include "hphp/runtime/debugger/cmd/cmd_out.h"
#include "hphp/runtime/vm/hhbc.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
@@ -49,12 +48,6 @@ void CmdOut::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) {
assert(!m_complete); // Complete cmds should not be asked to do work.
m_needsVMInterrupt = false;
if (m_skippingOverPopR) {
m_complete = true;
return;
}
int currentVMDepth = g_vmContext->m_nesting;
int currentStackDepth = proxy.getStackDepth();
@@ -77,18 +70,8 @@ void CmdOut::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) {
TRACE(2, "CmdOut: shallower stack depth, done.\n");
cleanupStepOuts();
int depth = decCount();
if (depth == 0) {
PC pc = g_vmContext->getPC();
// Step over PopR following a call
if (toOp(*pc) == OpPopR) {
m_skippingOverPopR = true;
m_needsVMInterrupt = true;
} else {
m_complete = true;
}
return;
} else {
m_complete = (decCount() == 0);
if (!m_complete) {
TRACE(2, "CmdOut: not complete, step out again.\n");
onSetup(proxy, interrupt);
}
+1 -3
Ver Arquivo
@@ -25,13 +25,11 @@ namespace HPHP { namespace Eval {
DECLARE_BOOST_TYPES(CmdOut);
class CmdOut : public CmdFlowControl {
public:
CmdOut() : CmdFlowControl(KindOfOut), m_skippingOverPopR(false) {}
CmdOut() : CmdFlowControl(KindOfOut) {}
virtual void help(DebuggerClient &client);
virtual void onSetup(DebuggerProxy &proxy, CmdInterrupt &interrupt);
virtual void onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt);
private:
bool m_skippingOverPopR;
};
///////////////////////////////////////////////////////////////////////////////
+2
Ver Arquivo
@@ -43,6 +43,8 @@ void CmdStep::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) {
// Step doesn't care about this interrupt... we just stay the course and
// keep stepping.
if (interrupt.getInterruptType() == ExceptionHandler) return;
// Don't step into generated functions, keep looking.
if (interrupt.getSite()->getLine0() == 0) return;
m_complete = (decCount() == 0);
if (!m_complete) {
installLocationFilterForLine(interrupt.getSite());
+125 -13
Ver Arquivo
@@ -31,6 +31,10 @@ void CmdVariable::sendImpl(DebuggerThriftBuffer &thrift) {
thrift.write(sdata);
}
thrift.write(m_global);
if (m_version == 2) {
thrift.write(m_varName);
thrift.write(m_filter);
}
}
void CmdVariable::recvImpl(DebuggerThriftBuffer &thrift) {
@@ -39,13 +43,20 @@ void CmdVariable::recvImpl(DebuggerThriftBuffer &thrift) {
{
String sdata;
thrift.read(sdata);
if (DebuggerWireHelpers::WireUnserialize(sdata, m_variables) !=
DebuggerWireHelpers::NoError) {
auto error = DebuggerWireHelpers::WireUnserialize(sdata, m_variables);
if (error != DebuggerWireHelpers::NoError) {
m_variables = null_array;
m_wireError = sdata;
if (error != DebuggerWireHelpers::HitLimit || m_version == 0) {
// Unexpected error. Log it.
m_wireError = sdata;
}
}
}
thrift.read(m_global);
if (m_version == 2) {
thrift.read(m_varName);
thrift.read(m_filter);
}
}
void CmdVariable::help(DebuggerClient &client) {
@@ -72,6 +83,26 @@ void CmdVariable::PrintVariable(DebuggerClient &client, const String& varName) {
auto charCount = client.getDebuggerClientShortPrintCharCount();
cmd.m_frame = client.getFrame();
CmdVariablePtr rcmd = client.xend<CmdVariable>(&cmd);
if (rcmd->m_version == 2) {
// Using the new protocol. rcmd contains a list of variables only.
// Fetch value of varName only, so that we can recover nicely when its
// value is too large to serialize.
cmd.m_varName = varName;
cmd.m_variables.reset();
cmd.m_version = 2;
rcmd = client.xend<CmdVariable>(&cmd);
if (rcmd->m_variables.empty()) {
// Perhaps the value is too large? See recvImpl.
// Retry the command with version 1, in which case values are omitted.
cmd.m_version = 1;
rcmd = client.xend<CmdVariable>(&cmd);
if (!rcmd->m_variables.empty()) {
// It's there without values, and gone with values, so it is too large.
client.output("...(omitted)");
return;
}
}
}
if (!rcmd->m_variables.empty()) {
for (ArrayIter iter(rcmd->m_variables); iter; ++iter) {
String name = iter.first().toString();
@@ -94,21 +125,61 @@ void CmdVariable::PrintVariable(DebuggerClient &client, const String& varName) {
}
const StaticString s_http_response_header("http_response_header");
const StaticString s_omitted("...(omitted)");
void CmdVariable::PrintVariables(DebuggerClient &client, CArrRef variables,
bool global, const String& text) {
int frame, const String& text, int version) {
bool global = frame == -1; // I.e. we were called from CmdGlobal, or the
//client's current frame is the global frame, according to OnServer
bool system = true;
int i = 0;
bool found = false;
for (ArrayIter iter(variables); iter; ++iter) {
String name = iter.first().toString();
String value = DebuggerClient::FormatVariable(iter.second(), 200);
if (!text.empty()) {
String fullvalue = DebuggerClient::FormatVariable(iter.second(), -1);
if (name.find(text, 0, false) >= 0 ||
fullvalue.find(text, 0, false) >= 0) {
String value;
if (version == 2) {
// Using the new protocol, so variables contain only names.
// Fetch the value separately.
CmdVariable cmd;
cmd.m_frame = frame;
cmd.m_variables = null_array;
cmd.m_varName = name;
cmd.m_filter = text;
cmd.m_version = 2;
auto rcmd = client.xend<CmdVariable>(&cmd);
if (!rcmd->m_variables.empty()) {
value = DebuggerClient::FormatVariable(rcmd->m_variables[name], 200);
found = true;
} else if (text.empty()) {
// Not missing because filtered out, assume the value is too large.
value = s_omitted;
found = true;
} else {
if (name.find(text, 0, false) >= 0) {
// Server should have matched it.
// Assume missing because value is too large.
value = s_omitted;
found = true;
} else {
// The variable was filtered out on the server, using text.
// Or it was just too large. Either way we let skip over it.
continue;
}
}
} else {
value = DebuggerClient::FormatVariable(iter.second(), 200);
}
if (version == 0 && !text.empty()) {
if (name.find(text, 0, false) >= 0) {
client.print("%s = %s", name.data(), value.data());
found = true;
} else {
String fullvalue = DebuggerClient::FormatVariable(value, -1);
if (fullvalue.find(text, 0, false) >= 0) {
client.print("%s = %s", name.data(), value.data());
found = true;
}
}
} else {
if (global && system) {
@@ -117,7 +188,7 @@ void CmdVariable::PrintVariables(DebuggerClient &client, CArrRef variables,
client.output("$%s = %s", name.data(), value.data());
}
// we knew this is the last system global
// we know s_http_response_header is the last system global
if (global && name == s_http_response_header) {
client.output("%s", "");
system = false;
@@ -153,8 +224,8 @@ void CmdVariable::onClient(DebuggerClient &client) {
if (cmd->m_variables.empty()) {
client.info("(no variable was defined)");
} else {
m_variables = cmd->m_variables;
PrintVariables(client, cmd->m_variables, cmd->m_global, text);
PrintVariables(client, cmd->m_variables, cmd->m_global ? -1 : m_frame,
text, cmd->m_version);
}
}
@@ -167,7 +238,48 @@ Array CmdVariable::GetGlobalVariables() {
}
bool CmdVariable::onServer(DebuggerProxy &proxy) {
m_variables = g_vmContext->getLocalDefinedVariables(m_frame);
if (m_frame < 0) {
m_variables = g_vmContext->m_globalVarEnv->getDefinedVariables();
m_global = true;
} else {
m_variables = g_vmContext->getLocalDefinedVariables(m_frame);
m_global = g_vmContext->getVarEnv(m_frame) == g_vmContext->m_globalVarEnv;
}
if (m_global) {
m_variables.remove(s_GLOBALS);
}
if (m_version == 1) {
// Remove the values before sending to client.
ArrayInit ret(m_variables->size());
Variant v;
for (ArrayIter iter(m_variables); iter; ++iter) {
ret.add(iter.first().toString(), v);
}
m_variables = ret.toArray();
m_version = 2;
} else if (m_version == 2) {
// Remove entries that do not match a non empty m_varName.
if (!m_varName.empty()) {
ArrayInit ret(1);
ret.add(m_varName, m_variables[m_varName]);
m_variables = ret.toArray();
}
// Remove entries whose name or contents do not match a non empty m_filter
if (!m_filter.empty()) {
ArrayInit ret(1);
for (ArrayIter iter(m_variables); iter; ++iter) {
String name = iter.first().toString();
if (name.find(m_filter, 0, false) < 0) {
String fullvalue = DebuggerClient::FormatVariable(iter.second(), -1);
if (fullvalue.find(m_filter, 0, false) < 0) {
continue;
}
}
ret.add(name, iter.second());
}
m_variables = ret.toArray();
}
}
return proxy.sendToClient(this);
}
+9 -3
Ver Arquivo
@@ -28,10 +28,14 @@ public:
static Array GetGlobalVariables();
static void PrintVariable(DebuggerClient &client, const String& varName);
static void PrintVariables(DebuggerClient &client, CArrRef variables,
bool global, const String& text);
int frame, const String& text, int version);
public:
CmdVariable() : DebuggerCommand(KindOfVariable) {}
CmdVariable() : DebuggerCommand(KindOfVariable) {
m_frame = 0;
m_version = 1;
m_global = false;
}
virtual void help(DebuggerClient &client);
@@ -45,7 +49,9 @@ protected:
private:
int m_frame;
Array m_variables;
bool m_global;
bool m_global; // Set true by onServer if it used g_vmContext->m_globalVarEnv
String m_varName;
String m_filter;
};
///////////////////////////////////////////////////////////////////////////////
+2 -2
Ver Arquivo
@@ -189,7 +189,7 @@ const StaticString
// Add location information for the given continuation to the given frame.
void addContinuationLocation(Array& frameData,
c_AsyncFunctionWaitHandle& contWh) {
c_ContinuationWaitHandle& contWh) {
// A running continuation is active on the normal stack, and we
// cannot compute the location just by inspecting the continuation
// alone.
@@ -226,7 +226,7 @@ Array createAsyncStacktrace() {
frameData.set(s_id, wh->t_getid(), true);
frameData.set(s_ancestors, ancestors, true);
// Continuation wait handles may have a source location to add.
auto contWh = dynamic_cast<c_AsyncFunctionWaitHandle*>(wh);
auto contWh = dynamic_cast<c_ContinuationWaitHandle*>(wh);
if (contWh != nullptr) addContinuationLocation(frameData, *contWh);
trace.append(frameData);
}
+4
Ver Arquivo
@@ -490,6 +490,10 @@ static void object_set(Variant &var, const String& key, CVarRef value,
static void attach_zval(json_parser *json, const String& key,
int assoc) {
if (json->the_top < 1) {
return;
}
Variant &root = json->the_zstack[json->the_top - 1];
Variant &child = json->the_zstack[json->the_top];
int up_mode = json->the_stack[json->the_top - 1];
+1 -1
Ver Arquivo
@@ -57,7 +57,7 @@ void AsioContext::exit(context_idx_t ctx_idx) {
}
}
void AsioContext::schedule(c_AsyncFunctionWaitHandle* wait_handle) {
void AsioContext::schedule(c_ContinuationWaitHandle* wait_handle) {
m_runnableQueue.push(wait_handle);
wait_handle->incRefCount();
}
+6 -6
Ver Arquivo
@@ -27,7 +27,7 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
FORWARD_DECLARE_CLASS(WaitableWaitHandle);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
FORWARD_DECLARE_CLASS(ContinuationWaitHandle);
FORWARD_DECLARE_CLASS(RescheduleWaitHandle);
FORWARD_DECLARE_CLASS(ExternalThreadEventWaitHandle);
@@ -42,9 +42,9 @@ class AsioContext {
void exit(context_idx_t ctx_idx);
bool isRunning() { return m_current; }
c_AsyncFunctionWaitHandle* getCurrent() { return m_current; }
c_ContinuationWaitHandle* getCurrent() { return m_current; }
void schedule(c_AsyncFunctionWaitHandle* wait_handle);
void schedule(c_ContinuationWaitHandle* wait_handle);
void schedule(c_RescheduleWaitHandle* wait_handle, uint32_t queue, uint32_t priority);
uint32_t registerExternalThreadEvent(c_ExternalThreadEventWaitHandle* wait_handle);
void unregisterExternalThreadEvent(uint32_t ete_idx);
@@ -59,10 +59,10 @@ class AsioContext {
bool runSingle(reschedule_priority_queue_t& queue);
c_AsyncFunctionWaitHandle* m_current;
c_ContinuationWaitHandle* m_current;
// queue of AsyncFunctionWaitHandles ready for immediate execution
smart::queue<c_AsyncFunctionWaitHandle*> m_runnableQueue;
// queue of ContinuationWaitHandles ready for immediate execution
smart::queue<c_ContinuationWaitHandle*> m_runnableQueue;
// queue of RescheduleWaitHandles scheduled in default mode
reschedule_priority_queue_t m_priorityQueueDefault;
+26 -16
Ver Arquivo
@@ -74,47 +74,57 @@ void AsioSession::initAbruptInterruptException() {
"The request was abruptly interrupted.");
}
void AsioSession::onAsyncFunctionCreate(c_AsyncFunctionWaitHandle* cont) {
assert(m_onAsyncFunctionCreateCallback.get());
void AsioSession::onFailed(CObjRef exception) {
if (m_onFailedCallback.get()) {
try {
vm_call_user_func(m_onFailedCallback, Array::Create(exception));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by onFailed callback");
}
}
}
void AsioSession::onContinuationCreate(c_ContinuationWaitHandle* cont) {
assert(m_onContinuationCreateCallback.get());
try {
vm_call_user_func(
m_onAsyncFunctionCreateCallback,
m_onContinuationCreateCallback,
Array::Create(cont));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onCreate callback");
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onCreate callback");
}
}
void AsioSession::onAsyncFunctionAwait(c_AsyncFunctionWaitHandle* cont, c_WaitHandle* child) {
assert(m_onAsyncFunctionAwaitCallback.get());
void AsioSession::onContinuationYield(c_ContinuationWaitHandle* cont, c_WaitHandle* child) {
assert(m_onContinuationYieldCallback.get());
try {
vm_call_user_func(
m_onAsyncFunctionAwaitCallback,
m_onContinuationYieldCallback,
make_packed_array(cont, child));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onAwait callback");
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onYield callback");
}
}
void AsioSession::onAsyncFunctionSuccess(c_AsyncFunctionWaitHandle* cont, CVarRef result) {
assert(m_onAsyncFunctionSuccessCallback.get());
void AsioSession::onContinuationSuccess(c_ContinuationWaitHandle* cont, CVarRef result) {
assert(m_onContinuationSuccessCallback.get());
try {
vm_call_user_func(
m_onAsyncFunctionSuccessCallback,
m_onContinuationSuccessCallback,
make_packed_array(cont, result));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onSuccess callback");
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onSuccess callback");
}
}
void AsioSession::onAsyncFunctionFail(c_AsyncFunctionWaitHandle* cont, CObjRef exception) {
assert(m_onAsyncFunctionFailCallback.get());
void AsioSession::onContinuationFail(c_ContinuationWaitHandle* cont, CObjRef exception) {
assert(m_onContinuationFailCallback.get());
try {
vm_call_user_func(
m_onAsyncFunctionFailCallback,
m_onContinuationFailCallback,
make_packed_array(cont, exception));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onFail callback");
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onFail callback");
}
}
+34 -24
Ver Arquivo
@@ -31,7 +31,7 @@ FORWARD_DECLARE_CLASS(GenArrayWaitHandle);
FORWARD_DECLARE_CLASS(GenMapWaitHandle);
FORWARD_DECLARE_CLASS(GenVectorWaitHandle);
FORWARD_DECLARE_CLASS(SetResultToRefWaitHandle);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
FORWARD_DECLARE_CLASS(ContinuationWaitHandle);
class AsioSession {
public:
@@ -64,7 +64,7 @@ class AsioSession {
return static_cast<context_idx_t>(m_contexts.size());
}
c_AsyncFunctionWaitHandle* getCurrentWaitHandle() {
c_ContinuationWaitHandle* getCurrentWaitHandle() {
assert(!isInContext() || getCurrentContext()->isRunning());
return isInContext() ? getCurrentContext()->getCurrent() : nullptr;
}
@@ -87,31 +87,38 @@ class AsioSession {
void initAbruptInterruptException();
// AsyncFunctionWaitHandle callbacks:
void setOnAsyncFunctionCreateCallback(ObjectData* on_start) {
// callback: on failed
void setOnFailedCallback(ObjectData* on_failed_callback) {
assert(!on_failed_callback || on_failed_callback->instanceof(c_Closure::classof()));
m_onFailedCallback = on_failed_callback;
}
void onFailed(CObjRef exception);
// ContinuationWaitHandle callbacks:
void setOnContinuationCreateCallback(ObjectData* on_start) {
assert(!on_start || on_start->instanceof(c_Closure::classof()));
m_onAsyncFunctionCreateCallback = on_start;
m_onContinuationCreateCallback = on_start;
}
void setOnAsyncFunctionAwaitCallback(ObjectData* on_await) {
assert(!on_await || on_await->instanceof(c_Closure::classof()));
m_onAsyncFunctionAwaitCallback = on_await;
void setOnContinuationYieldCallback(ObjectData* on_yield) {
assert(!on_yield || on_yield->instanceof(c_Closure::classof()));
m_onContinuationYieldCallback = on_yield;
}
void setOnAsyncFunctionSuccessCallback(ObjectData* on_success) {
void setOnContinuationSuccessCallback(ObjectData* on_success) {
assert(!on_success || on_success->instanceof(c_Closure::classof()));
m_onAsyncFunctionSuccessCallback = on_success;
m_onContinuationSuccessCallback = on_success;
}
void setOnAsyncFunctionFailCallback(ObjectData* on_fail) {
void setOnContinuationFailCallback(ObjectData* on_fail) {
assert(!on_fail || on_fail->instanceof(c_Closure::classof()));
m_onAsyncFunctionFailCallback = on_fail;
m_onContinuationFailCallback = on_fail;
}
bool hasOnAsyncFunctionCreateCallback() { return m_onAsyncFunctionCreateCallback.get(); }
bool hasOnAsyncFunctionAwaitCallback() { return m_onAsyncFunctionAwaitCallback.get(); }
bool hasOnAsyncFunctionSuccessCallback() { return m_onAsyncFunctionSuccessCallback.get(); }
bool hasOnAsyncFunctionFailCallback() { return m_onAsyncFunctionFailCallback.get(); }
void onAsyncFunctionCreate(c_AsyncFunctionWaitHandle* cont);
void onAsyncFunctionAwait(c_AsyncFunctionWaitHandle* cont, c_WaitHandle* child);
void onAsyncFunctionSuccess(c_AsyncFunctionWaitHandle* cont, CVarRef result);
void onAsyncFunctionFail(c_AsyncFunctionWaitHandle* cont, CObjRef exception);
bool hasOnContinuationCreateCallback() { return m_onContinuationCreateCallback.get(); }
bool hasOnContinuationYieldCallback() { return m_onContinuationYieldCallback.get(); }
bool hasOnContinuationSuccessCallback() { return m_onContinuationSuccessCallback.get(); }
bool hasOnContinuationFailCallback() { return m_onContinuationFailCallback.get(); }
void onContinuationCreate(c_ContinuationWaitHandle* cont);
void onContinuationYield(c_ContinuationWaitHandle* cont, c_WaitHandle* child);
void onContinuationSuccess(c_ContinuationWaitHandle* cont, CVarRef result);
void onContinuationFail(c_ContinuationWaitHandle* cont, CObjRef exception);
// WaitHandle callbacks:
void setOnJoinCallback(ObjectData* on_join) {
@@ -165,15 +172,18 @@ class AsioSession {
Object m_abruptInterruptException;
Object m_onAsyncFunctionCreateCallback;
Object m_onAsyncFunctionAwaitCallback;
Object m_onAsyncFunctionSuccessCallback;
Object m_onAsyncFunctionFailCallback;
Object m_onContinuationCreateCallback;
Object m_onContinuationYieldCallback;
Object m_onContinuationSuccessCallback;
Object m_onContinuationFailCallback;
Object m_onGenArrayCreateCallback;
Object m_onGenMapCreateCallback;
Object m_onGenVectorCreateCallback;
Object m_onSetResultToRefCreateCallback;
Object m_onJoinCallback;
// Legacy callback for backwards compatibility.
Object m_onFailedCallback;
};
///////////////////////////////////////////////////////////////////////////////
@@ -34,57 +34,57 @@ namespace {
StaticString s_continuation("Continuation");
}
c_AsyncFunctionWaitHandle::c_AsyncFunctionWaitHandle(Class* cb)
c_ContinuationWaitHandle::c_ContinuationWaitHandle(Class* cb)
: c_BlockableWaitHandle(cb), m_continuation(), m_child(), m_privData(),
m_depth(0) {
}
c_AsyncFunctionWaitHandle::~c_AsyncFunctionWaitHandle() {
c_ContinuationWaitHandle::~c_ContinuationWaitHandle() {
}
void c_AsyncFunctionWaitHandle::t___construct() {
void c_ContinuationWaitHandle::t___construct() {
Object e(SystemLib::AllocInvalidOperationExceptionObject(
"Use $continuation->getWaitHandle() instead of constructor"));
throw e;
}
void c_AsyncFunctionWaitHandle::ti_setoncreatecallback(CVarRef callback) {
void c_ContinuationWaitHandle::ti_setoncreatecallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set AsyncFunctionWaitHandle::onStart: on_start_cb not a closure"));
"Unable to set ContinuationWaitHandle::onStart: on_start_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnAsyncFunctionCreateCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnContinuationCreateCallback(callback.getObjectDataOrNull());
}
void c_AsyncFunctionWaitHandle::ti_setonawaitcallback(CVarRef callback) {
void c_ContinuationWaitHandle::ti_setonyieldcallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set AsyncFunctionWaitHandle::onAwait: on_await_cb not a closure"));
"Unable to set ContinuationWaitHandle::onYield: on_yield_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnAsyncFunctionAwaitCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnContinuationYieldCallback(callback.getObjectDataOrNull());
}
void c_AsyncFunctionWaitHandle::ti_setonsuccesscallback(CVarRef callback) {
void c_ContinuationWaitHandle::ti_setonsuccesscallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set AsyncFunctionWaitHandle::onSuccess: on_success_cb not a closure"));
"Unable to set ContinuationWaitHandle::onSuccess: on_success_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnAsyncFunctionSuccessCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnContinuationSuccessCallback(callback.getObjectDataOrNull());
}
void c_AsyncFunctionWaitHandle::ti_setonfailcallback(CVarRef callback) {
void c_ContinuationWaitHandle::ti_setonfailcallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set AsyncFunctionWaitHandle::onFail: on_fail_cb not a closure"));
"Unable to set ContinuationWaitHandle::onFail: on_fail_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnAsyncFunctionFailCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnContinuationFailCallback(callback.getObjectDataOrNull());
}
void c_AsyncFunctionWaitHandle::Create(c_Continuation* continuation) {
void c_ContinuationWaitHandle::Create(c_Continuation* continuation) {
assert(continuation);
assert(continuation->m_waitHandle.isNull());
@@ -104,24 +104,24 @@ void c_AsyncFunctionWaitHandle::Create(c_Continuation* continuation) {
throw e;
}
continuation->m_waitHandle = NEWOBJ(c_AsyncFunctionWaitHandle)();
continuation->m_waitHandle = NEWOBJ(c_ContinuationWaitHandle)();
continuation->m_waitHandle->initialize(continuation, depth + 1);
// needs to be called after continuation->m_waitHandle is set
if (UNLIKELY(session->hasOnAsyncFunctionCreateCallback())) {
session->onAsyncFunctionCreate(continuation->m_waitHandle.get());
if (UNLIKELY(session->hasOnContinuationCreateCallback())) {
session->onContinuationCreate(continuation->m_waitHandle.get());
}
}
Object c_AsyncFunctionWaitHandle::t_getprivdata() {
Object c_ContinuationWaitHandle::t_getprivdata() {
return m_privData;
}
void c_AsyncFunctionWaitHandle::t_setprivdata(CObjRef data) {
void c_ContinuationWaitHandle::t_setprivdata(CObjRef data) {
m_privData = data;
}
void c_AsyncFunctionWaitHandle::initialize(c_Continuation* continuation, uint16_t depth) {
void c_ContinuationWaitHandle::initialize(c_Continuation* continuation, uint16_t depth) {
m_continuation = continuation;
m_child = nullptr;
m_privData = nullptr;
@@ -146,7 +146,7 @@ void c_AsyncFunctionWaitHandle::initialize(c_Continuation* continuation, uint16_
}
}
void c_AsyncFunctionWaitHandle::run() {
void c_ContinuationWaitHandle::run() {
// may happen if scheduled in multiple contexts
if (getState() != STATE_SCHEDULED) {
return;
@@ -186,13 +186,13 @@ void c_AsyncFunctionWaitHandle::run() {
c_WaitHandle* child = c_WaitHandle::fromCell(value);
if (UNLIKELY(!child)) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Expected await argument to be an instance of Awaitable"));
"Expected yield argument to be an instance of WaitHandle"));
throw e;
}
AsioSession* session = AsioSession::Get();
if (UNLIKELY(session->hasOnAsyncFunctionAwaitCallback())) {
session->onAsyncFunctionAwait(this, child);
if (UNLIKELY(session->hasOnContinuationYieldCallback())) {
session->onContinuationYield(this, child);
}
m_child = child;
@@ -212,17 +212,17 @@ void c_AsyncFunctionWaitHandle::run() {
}
}
void c_AsyncFunctionWaitHandle::onUnblocked() {
void c_ContinuationWaitHandle::onUnblocked() {
setState(STATE_SCHEDULED);
if (isInContext()) {
getContext()->schedule(this);
}
}
void c_AsyncFunctionWaitHandle::markAsSucceeded(const Cell& result) {
void c_ContinuationWaitHandle::markAsSucceeded(const Cell& result) {
AsioSession* session = AsioSession::Get();
if (UNLIKELY(session->hasOnAsyncFunctionSuccessCallback())) {
session->onAsyncFunctionSuccess(this, cellAsCVarRef(result));
if (UNLIKELY(session->hasOnContinuationSuccessCallback())) {
session->onContinuationSuccess(this, cellAsCVarRef(result));
}
setResult(result);
@@ -232,19 +232,19 @@ void c_AsyncFunctionWaitHandle::markAsSucceeded(const Cell& result) {
m_child = nullptr;
}
void c_AsyncFunctionWaitHandle::markAsFailed(CObjRef exception) {
void c_ContinuationWaitHandle::markAsFailed(CObjRef exception) {
AsioSession* session = AsioSession::Get();
if (UNLIKELY(session->hasOnAsyncFunctionFailCallback())) {
session->onAsyncFunctionFail(this, exception);
session->onFailed(exception);
if (UNLIKELY(session->hasOnContinuationFailCallback())) {
session->onContinuationFail(this, exception);
}
setException(exception.get());
m_continuation = nullptr;
m_child = nullptr;
}
String c_AsyncFunctionWaitHandle::getName() {
String c_ContinuationWaitHandle::getName() {
switch (getState()) {
case STATE_SUCCEEDED:
return s_continuationResult;
@@ -269,7 +269,7 @@ String c_AsyncFunctionWaitHandle::getName() {
}
}
c_WaitableWaitHandle* c_AsyncFunctionWaitHandle::getChild() {
c_WaitableWaitHandle* c_ContinuationWaitHandle::getChild() {
if (getState() == STATE_BLOCKED) {
assert(dynamic_cast<c_WaitableWaitHandle*>(m_child.get()));
return static_cast<c_WaitableWaitHandle*>(m_child.get());
@@ -279,7 +279,7 @@ c_WaitableWaitHandle* c_AsyncFunctionWaitHandle::getChild() {
}
}
void c_AsyncFunctionWaitHandle::enterContext(context_idx_t ctx_idx) {
void c_ContinuationWaitHandle::enterContext(context_idx_t ctx_idx) {
assert(AsioSession::Get()->getContext(ctx_idx));
// stop before corrupting unioned data
@@ -318,7 +318,7 @@ void c_AsyncFunctionWaitHandle::enterContext(context_idx_t ctx_idx) {
}
}
void c_AsyncFunctionWaitHandle::exitContext(context_idx_t ctx_idx) {
void c_ContinuationWaitHandle::exitContext(context_idx_t ctx_idx) {
assert(AsioSession::Get()->getContext(ctx_idx));
// stop before corrupting unioned data
@@ -361,7 +361,7 @@ void c_AsyncFunctionWaitHandle::exitContext(context_idx_t ctx_idx) {
}
// Get the filename in which execution will proceed when execution resumes.
String c_AsyncFunctionWaitHandle::getFileName() {
String c_ContinuationWaitHandle::getFileName() {
if (m_continuation.isNull()) return empty_string;
auto ar = m_continuation->actRec();
auto file = ar->m_func->unit()->filepath()->data();
@@ -372,7 +372,7 @@ String c_AsyncFunctionWaitHandle::getFileName() {
}
// Get the line number on which execution will proceed when execution resumes.
int c_AsyncFunctionWaitHandle::getLineNumber() {
int c_ContinuationWaitHandle::getLineNumber() {
if (m_continuation.isNull()) return -1;
auto const unit = m_continuation->actRec()->m_func->unit();
return unit->getLineNumber(m_continuation->getNextExecutionOffset());
+2 -2
Ver Arquivo
@@ -85,7 +85,7 @@ Object c_GenArrayWaitHandle::ti_create(CArrRef dependencies) {
Cell* current = tvAssertCell(deps->nvGetValueRef(iter_pos));
if (IS_NULL_TYPE(current->m_type)) {
// {uninit,null} gives null
// {uninit,null} yields null
tvWriteNull(current);
continue;
}
@@ -141,7 +141,7 @@ void c_GenArrayWaitHandle::onUnblocked() {
Cell* current = tvAssertCell(m_deps->nvGetValueRef(m_iterPos));
if (IS_NULL_TYPE(current->m_type)) {
// {uninit,null} gives null
// {uninit,null} yields null
tvWriteNull(current);
continue;
}
+13
Ver Arquivo
@@ -51,5 +51,18 @@ Object f_asio_get_running() {
return AsioSession::Get()->getCurrentWaitHandle();
}
void f_asio_set_on_failed_callback(CVarRef on_failed_cb) {
if (!on_failed_cb.isNull() && !on_failed_cb.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set asio on failed callback: on_failed_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnFailedCallback(on_failed_cb.getObjectDataOrNull());
}
void f_asio_set_on_started_callback(CVarRef on_started_cb) {
c_ContinuationWaitHandle::ti_setoncreatecallback(on_started_cb);
}
///////////////////////////////////////////////////////////////////////////////
}
+13 -11
Ver Arquivo
@@ -27,6 +27,8 @@ namespace HPHP {
int f_asio_get_current_context_idx();
Object f_asio_get_running_in_context(int ctx_idx);
Object f_asio_get_running();
void f_asio_set_on_failed_callback(CVarRef on_failed_cb);
void f_asio_set_on_started_callback(CVarRef on_started_cb);
///////////////////////////////////////////////////////////////////////////////
// class WaitHandle
@@ -42,7 +44,7 @@ Object f_asio_get_running();
* StaticExceptionWaitHandle - statically failed wait handle with exception
* WaitableWaitHandle - wait handle that can be waited for
* BlockableWaitHandle - wait handle that can be blocked by other WH
* AsyncFunctionWaitHandle - async function-based asynchronous execution
* ContinuationWaitHandle - Continuation-powered asynchronous execution
* GenArrayWaitHandle - wait handle representing an array of WHs
* GenMapWaitHandle - wait handle representing an Map of WHs
* GenVectorWaitHandle - wait handle representing an Vector of WHs
@@ -51,7 +53,7 @@ Object f_asio_get_running();
*
* A wait handle can be either synchronously joined (waited for the operation
* to finish) or passed in various contexts as a dependency and waited for
* asynchronously (such as using await mechanism of async function or
* asynchronously (such as using yield mechanism of ContinuationWaitHandle or
* passed as an array member of GenArrayWaitHandle).
*/
FORWARD_DECLARE_CLASS(WaitHandle);
@@ -222,7 +224,7 @@ class c_WaitableWaitHandle : public c_WaitHandle {
static const int8_t STATE_NEW = 2;
private:
c_AsyncFunctionWaitHandle* m_creator;
c_ContinuationWaitHandle* m_creator;
c_BlockableWaitHandle* m_firstParent;
};
@@ -265,26 +267,26 @@ class c_BlockableWaitHandle : public c_WaitableWaitHandle {
};
///////////////////////////////////////////////////////////////////////////////
// class AsyncFunctionWaitHandle
// class ContinuationWaitHandle
/**
* A continuation wait handle represents a basic unit of asynchronous execution
* powered by continuation object. An asynchronous program can be written using
* continuations; a dependency on another wait handle is set up by awaiting such
* continuations; a dependency on another wait handle is set up by yielding such
* wait handle, giving control of the execution back to the asio framework.
*/
FORWARD_DECLARE_CLASS(Continuation);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
class c_AsyncFunctionWaitHandle : public c_BlockableWaitHandle {
FORWARD_DECLARE_CLASS(ContinuationWaitHandle);
class c_ContinuationWaitHandle : public c_BlockableWaitHandle {
public:
DECLARE_CLASS_NO_SWEEP(AsyncFunctionWaitHandle)
DECLARE_CLASS_NO_SWEEP(ContinuationWaitHandle)
// need to implement
public: c_AsyncFunctionWaitHandle(Class* cls = c_AsyncFunctionWaitHandle::classof());
public: ~c_AsyncFunctionWaitHandle();
public: c_ContinuationWaitHandle(Class* cls = c_ContinuationWaitHandle::classof());
public: ~c_ContinuationWaitHandle();
public: void t___construct();
public: static void ti_setoncreatecallback(CVarRef callback);
public: static void ti_setonawaitcallback(CVarRef callback);
public: static void ti_setonyieldcallback(CVarRef callback);
public: static void ti_setonsuccesscallback(CVarRef callback);
public: static void ti_setonfailcallback(CVarRef callback);
public: Object t_getprivdata();
+1 -1
Ver Arquivo
@@ -87,7 +87,7 @@ void c_Continuation::t_update_key(int64_t label, CVarRef key, CVarRef value) {
Object c_Continuation::t_getwaithandle() {
if (m_waitHandle.isNull()) {
c_AsyncFunctionWaitHandle::Create(this);
c_ContinuationWaitHandle::Create(this);
assert(!m_waitHandle.isNull());
}
return m_waitHandle;
+2 -2
Ver Arquivo
@@ -26,7 +26,7 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
FORWARD_DECLARE_CLASS(Continuation);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
FORWARD_DECLARE_CLASS(ContinuationWaitHandle);
Object f_hphp_create_continuation(const String& clsname, const String& funcname,
const String& origFuncName,
CArrRef args = null_array);
@@ -159,7 +159,7 @@ public:
/* ActRec for continuation (does not live on stack) */
ActRec* m_arPtr;
p_AsyncFunctionWaitHandle m_waitHandle;
p_ContinuationWaitHandle m_waitHandle;
/* temporary storage used to save the SP when inlining into a continuation */
void* m_stashedSP;
+14 -11
Ver Arquivo
@@ -51,48 +51,51 @@ static bool ctype(CVarRef v, int (*iswhat)(int)) {
return false;
}
// Use lambdas wrapping the ctype.h functions because of linker weirdness on
// OS X Mavericks.
bool f_ctype_alnum(CVarRef text) {
return ctype(text, isalnum);
return ctype(text, [] (int i) -> int { return isalnum(i); });
}
bool f_ctype_alpha(CVarRef text) {
return ctype(text, isalpha);
return ctype(text, [] (int i) -> int { return isalpha(i); });
}
bool f_ctype_cntrl(CVarRef text) {
return ctype(text, iscntrl);
return ctype(text, [] (int i) -> int { return iscntrl(i); });
}
bool f_ctype_digit(CVarRef text) {
return ctype(text, isdigit);
return ctype(text, [] (int i) -> int { return isdigit(i); });
}
bool f_ctype_graph(CVarRef text) {
return ctype(text, isgraph);
return ctype(text, [] (int i) -> int { return isgraph(i); });
}
bool f_ctype_lower(CVarRef text) {
return ctype(text, islower);
return ctype(text, [] (int i) -> int { return islower(i); });
}
bool f_ctype_print(CVarRef text) {
return ctype(text, isprint);
return ctype(text, [] (int i) -> int { return isprint(i); });
}
bool f_ctype_punct(CVarRef text) {
return ctype(text, ispunct);
return ctype(text, [] (int i) -> int { return ispunct(i); });
}
bool f_ctype_space(CVarRef text) {
return ctype(text, isspace);
return ctype(text, [] (int i) -> int { return isspace(i); });
}
bool f_ctype_upper(CVarRef text) {
return ctype(text, isupper);
return ctype(text, [] (int i) -> int { return isupper(i); });
}
bool f_ctype_xdigit(CVarRef text) {
return ctype(text, isxdigit);
return ctype(text, [] (int i) -> int { return isxdigit(i); });
}
///////////////////////////////////////////////////////////////////////////////
+5 -4
Ver Arquivo
@@ -1162,9 +1162,8 @@ static void appendOrphan(XmlNodeSet &orphans, xmlNodePtr node) {
}
}
static void removeOrphan(XmlNodeSet &orphans, xmlNodePtr node) {
static void removeOrphanIfNeeded(XmlNodeSet &orphans, xmlNodePtr node) {
if (node) {
assert(orphans.find(node) != orphans.end());
orphans.erase(node);
}
}
@@ -1954,6 +1953,7 @@ Variant c_DOMNode::t_appendchild(CObjRef newnode) {
c_DOMNode *newdomnode = newnode.getTyped<c_DOMNode>();
xmlNodePtr child = newdomnode->m_node;
xmlNodePtr new_child = NULL;
if (dom_node_is_read_only(nodep) ||
(child->parent != NULL && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, doc()->m_stricterror);
@@ -2014,7 +2014,7 @@ Variant c_DOMNode::t_appendchild(CObjRef newnode) {
}
}
if (newdomnode->doc().get()) {
removeOrphan(*newdomnode->doc()->m_orphans, newdomnode->m_node);
removeOrphanIfNeeded(*newdomnode->doc()->m_orphans, newdomnode->m_node);
}
dom_reconcile_ns(nodep->doc, new_child);
return create_node_object(new_child, doc(), false);
@@ -2094,6 +2094,7 @@ Variant c_DOMNode::t_insertbefore(CObjRef newnode,
xmlNodePtr child = domchildnode->m_node;
xmlNodePtr new_child = NULL;
int stricterror = doc()->m_stricterror;
if (dom_node_is_read_only(parentp) ||
(child->parent != NULL && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
@@ -2207,7 +2208,7 @@ Variant c_DOMNode::t_insertbefore(CObjRef newnode,
return false;
}
if (domchildnode->doc().get()) {
removeOrphan(*domchildnode->doc()->m_orphans, domchildnode->m_node);
removeOrphanIfNeeded(*domchildnode->doc()->m_orphans, domchildnode->m_node);
}
dom_reconcile_ns(parentp->doc, new_child);
return create_node_object(new_child, doc(), false);
+3 -1
Ver Arquivo
@@ -7111,6 +7111,8 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo,
}
if (exif_file_sections_realloc(ImageInfo, sn, ifd_size)) {
return 0;
} else {
end = (char*)ImageInfo->file.list[sn].data + dir_size;
}
/* read values not stored in directory itself */
snData = ImageInfo->infile->read(ifd_size-dir_size);
@@ -7175,8 +7177,8 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo,
}
} else {
if (!exif_process_IFD_TAG(ImageInfo, (char*)dir_entry,
(char*)ImageInfo->file.list[sn].data + ifd_size,
(char*)(ImageInfo->file.list[sn].data-dir_offset),
(char*)(ImageInfo->file.list[sn].data + ifd_size),
ifd_size, 0, section_index, 0, tag_table)) {
return 0;
}
+3 -11
Ver Arquivo
@@ -932,21 +932,14 @@ void f_header(const String& str, bool replace /* = true */,
// handle single line of status code
if (header->size() >= 5 && strncasecmp(header_line, "HTTP/", 5) == 0) {
int code = 200;
const char *reason = nullptr;
for (const char *ptr = header_line; *ptr; ptr++) {
if (*ptr == ' ' && *(ptr + 1) != ' ') {
code = atoi(ptr + 1);
for (ptr++; *ptr; ptr++) {
if (*ptr == ' ' && *(ptr + 1) != ' ') {
reason = ptr + 1;
break;
}
}
break;
}
}
if (code) {
transport->setResponse(code, reason);
transport->setResponse(code, "explicit_header");
}
return;
}
@@ -998,10 +991,10 @@ Variant f_http_response_code(int response_code /* = 0 */) {
Array f_headers_list() {
Transport *transport = g_context->getTransport();
Array ret = Array::Create();
if (transport) {
HeaderMap headers;
transport->getResponseHeaders(headers);
Array ret;
for (HeaderMap::const_iterator iter = headers.begin();
iter != headers.end(); ++iter) {
const vector<string> &values = iter->second;
@@ -1009,9 +1002,8 @@ Array f_headers_list() {
ret.append(String(iter->first + ": " + values[i]));
}
}
return ret;
}
return Array();
return ret;
}
bool f_headers_sent(VRefParam file /* = null */, VRefParam line /* = null */) {
-14
Ver Arquivo
@@ -1482,20 +1482,6 @@ Variant c_PDO::t_quote(const String& str, int64_t paramtype /* = q_PDO$$PARAM_ST
return false;
}
bool c_PDO::t_sqlitecreatefunction(const String& name,
CVarRef callback,
int64_t argcount /* = -1 */) {
raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
return false;
}
bool c_PDO::t_sqlitecreateaggregate(const String& name,
CVarRef step, CVarRef final,
int64_t argcount /* = -1 */) {
raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
return false;
}
Variant c_PDO::t___wakeup() {
throw_pdo_exception(uninit_null(), uninit_null(),
"You cannot serialize or unserialize PDO instances");
+8 -26
Ver Arquivo
@@ -135,12 +135,7 @@ class c_PDO : public ExtObjectData, public Sweepable {
public: Variant t_errorcode();
public: Array t_errorinfo();
public: Variant t_query(const String& sql);
public: Variant t_quote(const String& str,
int64_t paramtype = q_PDO$$PARAM_STR);
public: bool t_sqlitecreatefunction(const String& name, CVarRef callback,
int64_t argcount = -1);
public: bool t_sqlitecreateaggregate(const String& name, CVarRef step,
CVarRef final, int64_t argcount = -1);
public: Variant t_quote(const String& str, int64_t paramtype = q_PDO$$PARAM_STR);
public: Variant t___wakeup();
public: Variant t___sleep();
public: static Array ti_getavailabledrivers();
@@ -161,25 +156,13 @@ class c_PDOStatement : public ExtObjectData, public Sweepable {
public: ~c_PDOStatement();
public: void t___construct();
public: Variant t_execute(CArrRef params = null_array);
public: Variant t_fetch(int64_t how = 0,
int64_t orientation = q_PDO$$FETCH_ORI_NEXT,
int64_t offset = 0);
public: Variant t_fetchobject(const String& class_name = null_string,
CVarRef ctor_args = uninit_null());
public: Variant t_fetch(int64_t how = 0, int64_t orientation = q_PDO$$FETCH_ORI_NEXT, int64_t offset = 0);
public: Variant t_fetchobject(const String& class_name = null_string, CVarRef ctor_args = uninit_null());
public: Variant t_fetchcolumn(int64_t column_numner = 0);
public: Variant t_fetchall(int64_t how = 0,
CVarRef class_name = uninit_null(),
CVarRef ctor_args = uninit_null());
public: bool t_bindvalue(CVarRef paramno, CVarRef param,
int64_t type = q_PDO$$PARAM_STR);
public: bool t_bindparam(CVarRef paramno, VRefParam param,
int64_t type = q_PDO$$PARAM_STR,
int64_t max_value_len = 0,
CVarRef driver_params = uninit_null());
public: bool t_bindcolumn(CVarRef paramno, VRefParam param,
int64_t type = q_PDO$$PARAM_STR,
int64_t max_value_len = 0,
CVarRef driver_params = uninit_null());
public: Variant t_fetchall(int64_t how = 0, CVarRef class_name = uninit_null(), CVarRef ctor_args = uninit_null());
public: bool t_bindvalue(CVarRef paramno, CVarRef param, int64_t type = q_PDO$$PARAM_STR);
public: bool t_bindparam(CVarRef paramno, VRefParam param, int64_t type = q_PDO$$PARAM_STR, int64_t max_value_len = 0, CVarRef driver_params = uninit_null());
public: bool t_bindcolumn(CVarRef paramno, VRefParam param, int64_t type = q_PDO$$PARAM_STR, int64_t max_value_len = 0, CVarRef driver_params = uninit_null());
public: int64_t t_rowcount();
public: Variant t_errorcode();
public: Array t_errorinfo();
@@ -187,8 +170,7 @@ class c_PDOStatement : public ExtObjectData, public Sweepable {
public: Variant t_getattribute(int64_t attribute);
public: int64_t t_columncount();
public: Variant t_getcolumnmeta(int64_t column);
public: bool t_setfetchmode(int _argc, int64_t mode,
CArrRef _argv = null_array);
public: bool t_setfetchmode(int _argc, int64_t mode, CArrRef _argv = null_array);
public: bool t_nextrowset();
public: bool t_closecursor();
public: Variant t_debugdumpparams();
+1 -1
Ver Arquivo
@@ -445,7 +445,7 @@ static void set_function_info(Array &ret, const Func* func) {
param.set(s_class, VarNR(func->cls() ? func->cls()->name() :
func->preClass()->name()));
}
if (!nonExtendedConstraint || fpi.typeConstraint().isNullable()) {
if (!nonExtendedConstraint || fpi.typeConstraint().nullable()) {
param.set(s_nullable, true_varNR);
}
+1 -1
Ver Arquivo
@@ -1231,7 +1231,7 @@ new_session:
/* Unconditionally destroy existing arrays -- possible dirty data */
GlobalVariables *g = get_global_variables();
g->add(s__SESSION, Array::Create(), false);
g->set(s__SESSION, Array::Create(), false);
PS(invalid_session_id) = false;
String value;
+2 -1
Ver Arquivo
@@ -18,13 +18,14 @@
#define incl_HPHP_ICU_MATCHER_H_
#include <boost/scoped_ptr.hpp>
#include <unicode/regex.h>
// Avoid dragging in the icu namespace.
#ifndef U_USING_ICU_NAMESPACE
#define U_USING_ICU_NAMESPACE 0
#endif
#include <unicode/regex.h>
namespace HPHP {
// Wrapper class around icu::RegexMatcher that provides a default constructor
// so that it can be used in thread-local storage.
+2 -1
Ver Arquivo
@@ -18,13 +18,14 @@
#define incl_HPHP_ICU_TRANSLITERATOR_H_
#include <boost/scoped_ptr.hpp>
#include <unicode/translit.h>
// Avoid dragging in the icu namespace.
#ifndef U_USING_ICU_NAMESPACE
#define U_USING_ICU_NAMESPACE 0
#endif
#include <unicode/translit.h>
namespace HPHP {
// Wrapper class around icu::Transliterator that provides a default constructor
// so that it can be used in thread-local storage.
+2 -2
Ver Arquivo
@@ -22,13 +22,13 @@
#include <vector>
#include <boost/scoped_ptr.hpp>
#include <unicode/rbbi.h> // icu
// Avoid dragging in the icu namespace.
#ifndef U_USING_ICU_NAMESPACE
#define U_USING_ICU_NAMESPACE 0
#endif
#include <unicode/rbbi.h> // icu
namespace HPHP {
typedef icu::RuleBasedBreakIterator BreakIterator;
+5 -2
Ver Arquivo
@@ -919,8 +919,8 @@ bool PDOMySqlStatement::executer() {
return false;
}
my_ulonglong row_count = mysql_affected_rows(m_server);
if (row_count == (my_ulonglong)-1) {
my_ulonglong affected_count = mysql_affected_rows(m_server);
if (affected_count == (my_ulonglong)-1) {
/* we either have a query that returned a result set or an error occured
lets see if we have access to a result set */
if (!m_conn->buffered()) {
@@ -938,6 +938,9 @@ bool PDOMySqlStatement::executer() {
m_fields = mysql_fetch_fields(m_result);
}
else {
row_count = affected_count;
}
return true;
}
+48 -44
Ver Arquivo
@@ -99,6 +99,7 @@ const StaticString
s__GET("_GET"),
s__POST("_POST"),
s__REQUEST("_REQUEST"),
s__SESSION("_SESSION"),
s__ENV("_ENV"),
s__COOKIE("_COOKIE"),
s_HTTP_RAW_POST_DATA("HTTP_RAW_POST_DATA"),
@@ -127,6 +128,17 @@ const StaticString
s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
s_THREAD_TYPE("THREAD_TYPE");
static auto const s_arraysToClear = {
s__SERVER,
s__GET,
s__POST,
s__FILES,
s__REQUEST,
s__SESSION,
s__ENV,
s__COOKIE,
};
/**
* PHP has "EGPCS" processing order of these global variables, and this
* order is important in preparing $_REQUEST that needs to know which to
@@ -135,9 +147,16 @@ const StaticString
void HttpProtocol::PrepareSystemVariables(Transport *transport,
const RequestURI &r,
const SourceRootInfo &sri) {
GlobalVariables *g = get_global_variables();
const VirtualHost *vhost = VirtualHost::GetCurrent();
GlobalVariables* g = get_global_variables();
Variant emptyArr(HphpArray::GetStaticEmptyArray());
for (auto& key : s_arraysToClear) {
g->remove(key.get(), false);
g->set(key.get(), emptyArr, false);
}
g->set(s_HTTP_RAW_POST_DATA, empty_string, false);
Variant& server = g->getRef(s__SERVER);
server.set(s_REQUEST_START_TIME, time(nullptr));
@@ -253,53 +272,38 @@ void HttpProtocol::PrepareSystemVariables(Transport *transport,
HeaderMap headers;
transport->getHeaders(headers);
static std::atomic<int> badRequests(-1);
static int bad_request_count = -1;
for (HeaderMap::const_iterator iter = headers.begin();
iter != headers.end(); ++iter) {
const vector<string> &values = iter->second;
std::vector<std::string> badHeaders;
for (auto const& header : headers) {
auto const& key = header.first;
auto const& values = header.second;
auto normalizedKey = String("HTTP_") + f_strtoupper(key).replace("-", "_");
// Detect suspicious headers. We are about to modify header names for
// the SERVER variable. This means that it is possible to deliberately
// cause a header collision, which an attacker could use to sneak a
// header past a proxy that would either overwrite or filter it
// otherwise. Client code should use apache_request_headers() to
// retrieve the original headers if they are security-critical.
if (RuntimeOption::LogHeaderMangle != 0 &&
server.asArrRef().exists(normalizedKey)) {
badHeaders.push_back(key);
}
if (!values.empty()) {
// When a header has multiple values, we always take the last one.
server.set(normalizedKey, String(values.back()));
}
}
if (!badHeaders.empty()) {
auto reqId = badRequests.fetch_add(1, std::memory_order_acq_rel) + 1;
if (!(reqId % RuntimeOption::LogHeaderMangle)) {
std::string badNames = folly::join(", ", badHeaders);
std::string allHeaders;
const char* separator = "";
for (auto const& header : headers) {
for (auto const& value : header.second) {
folly::toAppend(separator, header.first, ": ", value,
&allHeaders);
separator = "\n";
// Detect suspicious headers. We are about to modify header names
// for the SERVER variable. This means that it is possible to
// deliberately cause a header collision, which an attacker could
// use to sneak a header past a proxy that would either overwrite
// or filter it otherwise. Client code should use
// apache_request_headers() to retrieve the original headers if
// they are security-critical.
if (RuntimeOption::LogHeaderMangle != 0) {
String key = "HTTP_";
key += f_strtoupper(iter->first).replace("-","_");
if (server.asArrRef().exists(key)) {
if (!(++bad_request_count % RuntimeOption::LogHeaderMangle)) {
Logger::Warning(
"HeaderMangle warning: "
"The header %s overwrote another header which mapped to the same "
"key. This happens because PHP normalises - to _, ie AN_EXAMPLE "
"and AN-EXAMPLE are equivalent. You should treat this as "
"malicious.",
iter->first.c_str());
}
}
}
Logger::Warning(
"HeaderMangle warning: "
"The header(s) [%s] overwrote other headers which mapped to the same "
"key. This happens because PHP normalises - to _, ie AN_EXAMPLE "
"and AN-EXAMPLE are equivalent. You should treat this as "
"malicious. All headers from this request:\n%s",
badNames.c_str(), allHeaders.c_str());
for (unsigned int i = 0; i < values.size(); i++) {
String key = "HTTP_";
key += f_strtoupper(iter->first).replace("-", "_");
server.set(key, String(values[i]));
}
}
+27 -8
Ver Arquivo
@@ -208,7 +208,8 @@ void HttpServer::onServerShutdown() {
}
}
void HttpServer::takeoverShutdown() {
void HttpServer::takeoverShutdown(HPHP::Server* server) {
assert(server == m_pageServer.get());
// We want to synchronously shut down our satellite servers to free up ports,
// then asynchronously shut down everything else.
onServerShutdown();
@@ -284,14 +285,15 @@ void HttpServer::run() {
if (m_stopReason) {
Logger::Warning("Server stopping with reason: %s\n", m_stopReason);
}
removePid();
Logger::Info("page server stopped");
}
onServerShutdown(); // dangling server already started here
time_t t0 = time(0);
if (RuntimeOption::ServerPort) {
m_pageServer->stop();
m_pageServer->waitForJobs();
removePid();
m_pageServer->closePort();
}
time_t t1 = time(0);
if (!m_danglings.empty() && RuntimeOption::ServerDanglingWait > 0) {
@@ -471,14 +473,31 @@ bool HttpServer::startServer(bool pageServer) {
HttpClient http;
string url = "http://";
url += RuntimeOption::ServerIP;
if (!RuntimeOption::ServerIP.empty()) {
url += RuntimeOption::ServerIP;
} else {
url += "localhost";
}
url += ":";
url += lexical_cast<string>(RuntimeOption::AdminServerPort);
url += "/stop";
StringBuffer response;
http.get(url.c_str(), response);
sleep(1);
if (!RuntimeOption::AdminPasswords.empty()) {
for (auto it = RuntimeOption::AdminPasswords.begin();
it != RuntimeOption::AdminPasswords.end();
++it) {
string passUrl = url + "?auth=" + *it;
StringBuffer response;
http.get(passUrl.c_str(), response);
sleep(1);
}
} else {
if (!RuntimeOption::AdminPassword.empty()) {
url += "?auth=" + RuntimeOption::AdminPassword;
}
StringBuffer response;
http.get(url.c_str(), response);
sleep(1);
}
}
}
+1 -1
Ver Arquivo
@@ -46,7 +46,7 @@ public:
void flushLog();
void watchDog();
void takeoverShutdown();
void takeoverShutdown(HPHP::Server* server);
ServerPtr getPageServer() { return m_pageServer;}
void getSatelliteStats(vector<std::pair<std::string, int>> *stats);
+19 -1
Ver Arquivo
@@ -15,6 +15,8 @@
*/
#include "hphp/runtime/server/libevent-server.h"
#include "hphp/runtime/server/libevent-server-with-fd.h"
#include "hphp/runtime/server/libevent-server-with-takeover.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
@@ -27,7 +29,23 @@ public:
};
ServerPtr LibEventServerFactory::createServer(const ServerOptions& options) {
return std::make_shared<LibEventServer>(options);
if (options.m_serverFD != -1 || options.m_sslFD != -1) {
auto const server = std::make_shared<LibEventServerWithFd>
(options.m_address, options.m_port, options.m_numThreads);
server->setServerSocketFd(options.m_serverFD);
server->setSSLSocketFd(options.m_sslFD);
return server;
}
if (!options.m_takeoverFilename.empty()) {
auto const server = std::make_shared<LibEventServerWithTakeover>
(options.m_address, options.m_port, options.m_numThreads);
server->setTransferFilename(options.m_takeoverFilename);
return server;
}
return std::make_shared<LibEventServer>(options.m_address, options.m_port,
options.m_numThreads);
}
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -26,7 +26,7 @@ namespace HPHP {
LibEventServerWithFd::LibEventServerWithFd
(const std::string &address, int port, int thread)
: LibEventServer(ServerOptions(address, port, thread))
: LibEventServer(address, port, thread)
{
}
@@ -14,7 +14,7 @@
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/server/takeover-agent.h"
#include "hphp/runtime/server/libevent-server-with-takeover.h"
#include "hphp/util/logger.h"
#include "hphp/runtime/base/string-util.h"
#include "hphp/runtime/ext/ext_string.h"
@@ -22,12 +22,12 @@
#include <afdt.h>
/*
TakeoverAgent provides the ability
LibEventServerWithTakeover extends LibEventServer with the ability
to transfer the accept socket (the file descriptor used to accept
new connections) from an older instance of the server to a new one
that has just been brought up.
In takeover, the agent will attempt to
In getAcceptSocket, if binding fails, the server will attempt to
use libafdt to transfer a file descriptor from an existing process
(which should exist, since we couldn't bind to the socket).
The transfer is performed in a separate event loop that we wait on,
@@ -74,23 +74,22 @@ static int fd_transfer_request_handler(
uint8_t* response,
uint32_t* response_length,
void* userdata) {
TakeoverAgent* agent = (TakeoverAgent*)userdata;
LibEventServerWithTakeover* server = (LibEventServerWithTakeover*)userdata;
String req((const char*)request, request_length, CopyString);
String resp;
int fd = agent->afdtRequest(req, &resp);
int fd = server->afdtRequest(req, &resp);
assert(resp.size() <= (int)*response_length);
memcpy(response, resp.data(), resp.size());
*response_length = resp.size();
return fd;
}
TakeoverAgent::TakeoverAgent(const std::string &fname)
: m_delete_handle(nullptr),
m_transfer_fname(fname),
LibEventServerWithTakeover::LibEventServerWithTakeover
(const std::string &address, int port, int thread)
: LibEventServer(address, port, thread),
m_delete_handle(nullptr),
m_took_over(false),
m_takeover_state(TakeoverState::NotStarted),
m_sock(-1),
m_callback(nullptr)
m_takeover_state(TakeoverState::NotStarted)
{
}
@@ -99,14 +98,25 @@ const StaticString
s_ver_C_TERM_REQ(P_VERSION C_TERM_REQ),
s_ver_C_TERM_OK(P_VERSION C_TERM_OK);
int TakeoverAgent::afdtRequest(String request, String* response) {
int LibEventServerWithTakeover::afdtRequest(String request, String* response) {
Logger::Info("takeover: received request");
if (request == s_ver_C_FD_REQ) {
Logger::Info("takeover: request is a listen socket request");
int ret;
*response = P_VERSION C_FD_RESP;
// Make evhttp forget our copy of the accept socket so we don't accept any
// more connections and drop them. Keep the socket open until we get the
// shutdown request so that we can still serve AFDT requests (if the new
// server crashes or something). The downside is that it will take the LB
// longer to figure out that we are broken.
ret = evhttp_del_accept_socket(m_server, m_accept_sock);
if (ret < 0) {
// This will fail if we get a second AFDT request, but the spurious
// log message is not too harmful.
Logger::Error("Unable to delete accept socket");
}
m_takeover_state = TakeoverState::Started;
(void)m_callback->onTakeoverRequest(RequestType::LISTEN_SOCKET);
return m_sock;
return m_accept_sock;
} else if (request == s_ver_C_TERM_REQ) {
Logger::Info("takeover: request is a terminate request");
// It is a little bit of a hack to use an AFDT request/response
@@ -114,9 +124,28 @@ int TakeoverAgent::afdtRequest(String request, String* response) {
// within the main libevent thread.
int ret;
*response = P_VERSION C_TERM_BAD;
if (m_callback->onTakeoverRequest(RequestType::TERMINATE) != 0) {
ret = close(m_accept_sock);
if (ret < 0) {
Logger::Error("Unable to close accept socket");
return -1;
}
m_accept_sock = -1;
// Close SSL server
if (m_server_ssl) {
assert(m_accept_sock_ssl > 0);
ret = evhttp_del_accept_socket(m_server_ssl, m_accept_sock_ssl);
if (ret < 0) {
Logger::Error("Unable to delete accept socket for SSL in evhttp");
return -1;
}
ret = close(m_accept_sock_ssl);
if (ret < 0) {
Logger::Error("Unable to close accept socket for SSL");
return -1;
}
}
ret = afdt_close_server(m_delete_handle);
if (ret < 0) {
Logger::Error("Unable to close afdt server");
@@ -130,7 +159,7 @@ int TakeoverAgent::afdtRequest(String request, String* response) {
for (std::set<TakeoverListener*>::iterator it =
m_takeover_listeners.begin();
it != m_takeover_listeners.end(); ++it) {
(*it)->takeoverShutdown();
(*it)->takeoverShutdown(this);
}
Logger::Info("takeover: notification complete");
return -1;
@@ -141,21 +170,18 @@ int TakeoverAgent::afdtRequest(String request, String* response) {
}
}
int TakeoverAgent::setupFdServer(event_base *eventBase, int sock,
Callback *callback) {
void LibEventServerWithTakeover::setupFdServer() {
int ret;
m_sock = sock;
m_callback = callback;
ret = unlink(m_transfer_fname.c_str());
if (ret < 0 && errno != ENOENT) {
Logger::Error("Unalbe to unlink '%s': %s",
m_transfer_fname.c_str(), folly::errnoStr(errno).c_str());
return -1;
return;
}
ret = afdt_create_server(
m_transfer_fname.c_str(),
eventBase,
m_eventBase,
fd_transfer_request_handler,
afdt_no_post,
fd_transfer_error_hander,
@@ -167,11 +193,31 @@ int TakeoverAgent::setupFdServer(event_base *eventBase, int sock,
if (ret >= 0) {
Logger::Info("takeover: fd server set up successfully");
}
return ret;
}
int TakeoverAgent::takeover(std::chrono::seconds timeoutSec) {
int LibEventServerWithTakeover::getAcceptSocket() {
int ret;
const char *address = m_address.empty() ? nullptr : m_address.c_str();
if (m_accept_sock != -1) {
Logger::Warning("LibEventServerWithTakeover trying to get a socket, "
"but m_accept_sock is not -1. Possibly leaking file descriptors.");
m_accept_sock = -1;
}
ret = evhttp_bind_socket_backlog_fd(m_server, address,
m_port, RuntimeOption::ServerBacklog);
if (ret >= 0) {
Logger::Info("takeover: bound directly to port %d", m_port);
m_accept_sock = ret;
return 0;
} else if (errno != EADDRINUSE) {
return -1;
}
if (m_transfer_fname.empty()) {
return -1;
}
Logger::Info("takeover: beginning listen socket acquisition");
uint8_t fd_request[3] = P_VERSION C_FD_REQ;
@@ -179,21 +225,21 @@ int TakeoverAgent::takeover(std::chrono::seconds timeoutSec) {
uint32_t response_len = sizeof(fd_response);
afdt_error_t err = AFDT_ERROR_T_INIT;
// TODO(dreiss): Make this timeout configurable.
struct timeval timeout = { timeoutSec.count() , 0 };
struct timeval timeout = { 2 , 0 };
ret = afdt_sync_client(
m_transfer_fname.c_str(),
fd_request,
sizeof(fd_request) - 1,
fd_response,
&response_len,
&m_sock,
&m_accept_sock,
&timeout,
&err);
if (ret < 0) {
fd_transfer_error_hander(&err, nullptr);
errno = EADDRINUSE;
return -1;
} else if (m_sock < 0) {
} else if (m_accept_sock < 0) {
String resp((const char*)fd_response, response_len, CopyString);
Logger::Error(
"AFDT did not receive a file descriptor: "
@@ -206,10 +252,30 @@ int TakeoverAgent::takeover(std::chrono::seconds timeoutSec) {
Logger::Info("takeover: acquired listen socket");
m_took_over = true;
return m_sock;
ret = evhttp_accept_socket(m_server, m_accept_sock);
if (ret < 0) {
Logger::Error("evhttp_accept_socket: %s",
folly::errnoStr(errno).c_str());
int errno_save = errno;
close(m_accept_sock);
m_accept_sock = -1;
errno = errno_save;
return -1;
}
return 0;
}
void TakeoverAgent::requestShutdown() {
void LibEventServerWithTakeover::start() {
if (m_server_ssl) {
// Set a flag to prevent parent class from trying to listen to ssl
// before the old server releases the port
m_accept_sock_ssl = -2;
}
LibEventServer::start();
if (m_took_over) {
Logger::Info("takeover: requesting shutdown of satellites");
// Use AFDT to synchronously shut down the old server's satellites
@@ -249,9 +315,19 @@ void TakeoverAgent::requestShutdown() {
Logger::Info("takeover: old satellites have shut down");
}
}
if (m_server_ssl) {
if (getAcceptSocketSSL() != 0) {
Logger::Error("Fail to listen on ssl port %d", m_port_ssl);
throw FailedToListenException(m_address, m_port_ssl);
}
Logger::Info("Listen on ssl port %d",m_port_ssl);
}
setupFdServer();
}
void TakeoverAgent::stop() {
void LibEventServerWithTakeover::stop() {
if (m_delete_handle != nullptr) {
afdt_close_server(m_delete_handle);
}
@@ -264,9 +340,13 @@ void TakeoverAgent::stop() {
// be safe we close the socket so that if nobody else is listening
// the OS starts rejecting requests but if somebody is listening we
// let them receive the requests.
if (m_takeover_state != TakeoverState::NotStarted) {
m_callback->takeoverAborted();
if (m_takeover_state != TakeoverState::NotStarted &&
m_accept_sock != -1) {
close(m_accept_sock);
m_accept_sock = -1;
}
LibEventServer::stop();
}
TakeoverListener::~TakeoverListener() {
@@ -14,73 +14,36 @@
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
#define incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
#ifndef incl_HPHP_HTTP_SERVER_LIB_EVENT_SERVER_WITH_TAKEOVER_H_
#define incl_HPHP_HTTP_SERVER_LIB_EVENT_SERVER_WITH_TAKEOVER_H_
#include "hphp/runtime/base/complex-types.h"
#include <event.h>
#include <chrono>
#include <set>
#include <string>
#include "hphp/runtime/server/libevent-server.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
/**
* A callback to be informed when a server is shutting down because its socket
* has been taken over by a new process.
*/
class TakeoverListener {
public:
virtual ~TakeoverListener();
virtual void takeoverShutdown() = 0;
};
/**
* Agent with the ability to take over an accept socket
* LibEventServer that adds the ability to take over an accept socket
* from another process, and give its accept socket up.
*/
class TakeoverAgent {
class LibEventServerWithTakeover : public LibEventServer {
public:
enum class RequestType {
LISTEN_SOCKET,
TERMINATE,
};
LibEventServerWithTakeover(const std::string &address, int port, int thread);
class Callback {
public:
virtual ~Callback() {}
// Called by the TakeoverAgent when it receives a request for takeover
// Returns non zero on error, which terminates the takeover action
virtual int onTakeoverRequest(RequestType type) = 0;
virtual void stop();
// Called by the TakeoverAgent when it is shutdown mid-way through a
// takeover.
virtual void takeoverAborted() = 0;
};
explicit TakeoverAgent(const std::string &fname);
// execute a takeover and return the fd. -1 if an fd could not be acquired
int takeover(std::chrono::seconds timeout = std::chrono::seconds(2));
// instruct the old server to shutdown
void requestShutdown();
// setup a server to listen for takeover requests
int setupFdServer(event_base *eventBase, int sock, Callback *callback);
// stop the takeover agent, including in-progress takeovers
void stop();
void addTakeoverListener(TakeoverListener* listener) {
m_takeover_listeners.insert(listener);
// Set the name of the file to be used for a Unix domain socket
// over which to transfer the accept socket.
void setTransferFilename(const std::string &fname) {
assert(m_transfer_fname.empty());
m_transfer_fname = fname;
}
void removeTakeoverListener(TakeoverListener* listener) {
m_takeover_listeners.erase(listener);
virtual void addTakeoverListener(TakeoverListener* lisener) {
m_takeover_listeners.insert(lisener);
}
virtual void removeTakeoverListener(TakeoverListener* lisener) {
m_takeover_listeners.erase(lisener);
}
// These are public so they can be called from a C-style callback.
@@ -96,6 +59,10 @@ protected:
Complete,
};
virtual void start();
virtual int getAcceptSocket();
void setupFdServer();
void notifyTakeoverComplete();
void* m_delete_handle;
@@ -107,15 +74,9 @@ protected:
// The state of taking over this server's socket
TakeoverState m_takeover_state;
// Target socket
int m_sock;
// User callback for events
Callback *m_callback;
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
#endif // incl_HPHP_HTTP_SERVER_LIB_EVENT_SERVER_H_
+29 -144
Ver Arquivo
@@ -78,11 +78,12 @@ LibEventTransportTraits::LibEventTransportTraits(LibEventJobPtr job,
///////////////////////////////////////////////////////////////////////////////
// constructor and destructor
LibEventServer::LibEventServer(const ServerOptions &options)
: Server(options.m_address, options.m_port, options.m_numThreads),
m_accept_sock(options.m_serverFD),
m_accept_sock_ssl(options.m_sslFD),
m_dispatcher(options.m_numThreads, RuntimeOption::ServerThreadRoundRobin,
LibEventServer::LibEventServer(const std::string &address, int port,
int thread)
: Server(address, port, thread),
m_accept_sock(-1),
m_accept_sock_ssl(-1),
m_dispatcher(thread, RuntimeOption::ServerThreadRoundRobin,
RuntimeOption::ServerThreadDropCacheTimeoutSeconds,
RuntimeOption::ServerThreadDropStack,
this, RuntimeOption::ServerThreadJobLIFOSwitchThreshold,
@@ -98,10 +99,6 @@ LibEventServer::LibEventServer(const ServerOptions &options)
evhttp_set_read_limit(m_server, RuntimeOption::RequestBodyReadLimit);
#endif
m_responseQueue.create(m_eventBase);
if (!options.m_takeoverFilename.empty()) {
m_takeover_agent.reset(new TakeoverAgent(options.m_takeoverFilename));
}
}
LibEventServer::~LibEventServer() {
@@ -119,129 +116,19 @@ LibEventServer::~LibEventServer() {
///////////////////////////////////////////////////////////////////////////////
// implementing HttpServer
void LibEventServer::addTakeoverListener(TakeoverListener* listener) {
if (m_takeover_agent) {
m_takeover_agent->addTakeoverListener(listener);
}
}
void LibEventServer::removeTakeoverListener(TakeoverListener* listener) {
if (m_takeover_agent) {
m_takeover_agent->removeTakeoverListener(listener);
}
}
int LibEventServer::useExistingFd(evhttp *server, int fd, bool needListen) {
Logger::Info("inheritfd: using inherited fd %d for server", fd);
int ret = -1;
if (needListen) {
ret = listen(fd, RuntimeOption::ServerBacklog);
if (ret != 0) {
Logger::Error("inheritfd: listen() failed: %s",
folly::errnoStr(errno).c_str());
return -1;
}
}
ret = evhttp_accept_socket(server, fd);
int LibEventServer::getAcceptSocket() {
int ret;
const char *address = m_address.empty() ? nullptr : m_address.c_str();
ret = evhttp_bind_socket_backlog_fd(m_server, address,
m_port, RuntimeOption::ServerBacklog);
if (ret < 0) {
Logger::Error("evhttp_accept_socket: %s",
folly::errnoStr(errno).c_str());
int errno_save = errno;
close(fd);
errno = errno_save;
Logger::Error("Fail to bind port %d", m_port);
return -1;
}
return 0;
}
int LibEventServer::getAcceptSocket() {
if (m_accept_sock >= 0) {
if (useExistingFd(m_server, m_accept_sock, true /* listen */) != 0) {
m_accept_sock = -1;
return -1;
}
return 0;
}
const char *address = m_address.empty() ? nullptr : m_address.c_str();
int ret = evhttp_bind_socket_backlog_fd(m_server, address,
m_port, RuntimeOption::ServerBacklog);
if (ret < 0) {
if (errno == EADDRINUSE && m_takeover_agent) {
m_accept_sock = m_takeover_agent->takeover();
if (m_accept_sock < 0) {
return -1;
}
if (useExistingFd(m_server, m_accept_sock, false /* no listen */) != 0) {
m_accept_sock = -1;
return -1;
}
} else {
Logger::Error("Fail to bind port %d", m_port);
return -1;
}
}
m_accept_sock = ret;
if (m_takeover_agent) {
m_takeover_agent->requestShutdown();
m_takeover_agent->setupFdServer(m_eventBase, m_accept_sock, this);
}
return 0;
}
int LibEventServer::onTakeoverRequest(TakeoverAgent::RequestType type) {
int ret = -1;
if (type == TakeoverAgent::RequestType::LISTEN_SOCKET) {
// TODO: This is broken-sauce. We should continue serving
// requests from this process until shutdown.
// Make evhttp forget our copy of the accept socket so we don't accept any
// more connections and drop them. Keep the socket open until we get the
// shutdown request so that we can still serve AFDT requests (if the new
// server crashes or something). The downside is that it will take the LB
// longer to figure out that we are broken.
ret = evhttp_del_accept_socket(m_server, m_accept_sock);
if (ret < 0) {
// This will fail if we get a second AFDT request, but the spurious
// log message is not too harmful.
Logger::Error("Unable to delete accept socket");
}
} else if (type == TakeoverAgent::RequestType::TERMINATE) {
ret = close(m_accept_sock);
if (ret < 0) {
Logger::Error("Unable to close accept socket");
return -1;
}
m_accept_sock = -1;
// Close SSL server
if (m_server_ssl) {
assert(m_accept_sock_ssl > 0);
ret = evhttp_del_accept_socket(m_server_ssl, m_accept_sock_ssl);
if (ret < 0) {
Logger::Error("Unable to delete accept socket for SSL in evhttp");
return -1;
}
ret = close(m_accept_sock_ssl);
if (ret < 0) {
Logger::Error("Unable to close accept socket for SSL");
return -1;
}
}
}
return 0;
}
void LibEventServer::takeoverAborted() {
if (m_accept_sock >= 0) {
close(m_accept_sock);
m_accept_sock = -1;
}
}
int LibEventServer::getLibEventConnectionCount() {
return evhttp_get_connection_count(m_server);
}
@@ -249,11 +136,16 @@ int LibEventServer::getLibEventConnectionCount() {
void LibEventServer::start() {
if (getStatus() == RunStatus::RUNNING) return;
if (getAcceptSocket() != 0) {
throw FailedToListenException(m_address, m_port);
if (m_server != nullptr) {
if (getAcceptSocket() != 0) {
throw FailedToListenException(m_address, m_port);
}
}
if (m_server_ssl != nullptr) {
if (m_server_ssl != nullptr && m_accept_sock_ssl != -2) {
// m_accept_sock_ssl here serves as a flag to indicate whether it is
// called from subclass (LibEventServerWithTakeover). If it is (==-2)
// we delay the getAcceptSocketSSL();
if (getAcceptSocketSSL() != 0) {
Logger::Error("Fail to listen on ssl port %d", m_port_ssl);
throw FailedToListenException(m_address, m_port_ssl);
@@ -304,17 +196,13 @@ void LibEventServer::dispatch() {
}
m_responseQueue.close();
if (m_takeover_agent) {
m_takeover_agent->stop();
}
// flushing all remaining events
if (RuntimeOption::ServerGracefulShutdownWait) {
dispatchWithTimeout(RuntimeOption::ServerGracefulShutdownWait);
}
}
void LibEventServer::stop() {
void LibEventServer::waitForJobs() {
Lock lock(m_mutex);
if (getStatus() != RunStatus::RUNNING || m_server == nullptr) return;
@@ -363,11 +251,18 @@ void LibEventServer::stop() {
// an error occured but we're in shutdown already, so ignore
}
m_dispatcherThread.waitForEnd();
}
void LibEventServer::closePort() {
evhttp_free(m_server);
m_server = nullptr;
}
void LibEventServer::stop() {
waitForJobs();
closePort();
}
///////////////////////////////////////////////////////////////////////////////
// SSL handling
@@ -429,14 +324,6 @@ bool LibEventServer::enableSSL(int port) {
}
int LibEventServer::getAcceptSocketSSL() {
if (m_accept_sock_ssl >= 0) {
if (useExistingFd(m_server_ssl, m_accept_sock_ssl, true /*listen*/) != 0) {
m_accept_sock_ssl = -1;
return -1;
}
return 0;
}
const char *address = m_address.empty() ? nullptr : m_address.c_str();
int ret = evhttp_bind_socket_backlog_fd(m_server_ssl, address,
m_port_ssl, RuntimeOption::ServerBacklog);
@@ -504,9 +391,7 @@ void LibEventServer::onResponse(int worker, evhttp_request *request,
int totalSize = 0;
if (RuntimeOption::LibEventSyncSend && !skip_sync) {
auto const& reasonStr = transport->getResponseInfo();
const char* reason = reasonStr.empty() ? HttpProtocol::GetReasonString(code)
: reasonStr.c_str();
const char *reason = HttpProtocol::GetReasonString(code);
timespec begin, end;
Timer::GetMonotonicTime(begin);
#ifdef EVHTTP_SYNC_SEND_REPORT_TOTAL_LEN
+4 -17
Ver Arquivo
@@ -22,7 +22,6 @@
#include "hphp/runtime/server/job-queue-vm-stack.h"
//#include "hphp/util/job-queue.h"
//#include "hphp/util/service-data.h"
#include "hphp/runtime/server/takeover-agent.h"
#include "hphp/runtime/server/server-worker.h"
#include "hphp/util/process.h"
@@ -97,17 +96,19 @@ private:
* Implementing an evhttp based HTTP server with JobQueueDispatcher. This
* server will have one dispather thread and multiple worker threads.
*/
class LibEventServer : public Server, public TakeoverAgent::Callback {
class LibEventServer : public Server {
public:
/**
* Constructor and destructor.
*/
explicit LibEventServer(const ServerOptions &options);
LibEventServer(const std::string &address, int port, int thread);
~LibEventServer();
// implementing Server
virtual void start();
virtual void waitForEnd();
virtual void waitForJobs();
virtual void closePort();
virtual void stop();
virtual int getActiveWorker() {
return m_dispatcher.getActiveWorker();
@@ -120,9 +121,6 @@ public:
}
int getLibEventConnectionCount();
void addTakeoverListener(TakeoverListener* listener);
void removeTakeoverListener(TakeoverListener* listener);
/**
* Request handler called by evhttp library.
*/
@@ -145,13 +143,6 @@ public:
*/
virtual bool enableSSL(int port);
/**
* TakeoverAgent::Callback
*/
int onTakeoverRequest(TakeoverAgent::RequestType type);
void takeoverAborted();
protected:
virtual int getAcceptSocket();
virtual int getAcceptSocketSSL();
@@ -165,8 +156,6 @@ protected:
evhttp *m_server_ssl;
int m_port_ssl;
std::unique_ptr<TakeoverAgent> m_takeover_agent;
// signal to stop the thread
event m_eventStop;
CPipe m_pipeStop;
@@ -179,8 +168,6 @@ private:
};
RequestPriority getRequestPriority(struct evhttp_request* request);
int useExistingFd(evhttp *server, int fd, bool listen);
static bool certHandler(const std::string &server_name,
const std::string& key_file,
const std::string& cert_file);
+22 -3
Ver Arquivo
@@ -17,7 +17,6 @@
#ifndef incl_HPHP_HTTP_SERVER_SERVER_H_
#define incl_HPHP_HTTP_SERVER_SERVER_H_
#include "hphp/runtime/server/takeover-agent.h"
#include "hphp/runtime/server/transport.h"
#include "hphp/util/exception.h"
#include "hphp/util/lock.h"
@@ -92,6 +91,16 @@ private:
int m_timeout;
};
/**
* A callback to be informed when a server is shutting down because its socket
* has been taken over by a new process.
*/
class TakeoverListener {
public:
virtual ~TakeoverListener();
virtual void takeoverShutdown(Server* server) = 0;
};
typedef std::function<std::unique_ptr<RequestHandler>()> RequestHandlerFactory;
typedef std::function<bool(const std::string&)> URLChecker;
@@ -158,8 +167,8 @@ public:
*
* This is a no-op for servers that do not support socket takeover.
*/
virtual void addTakeoverListener(TakeoverListener* listener) {}
virtual void removeTakeoverListener(TakeoverListener* listener) {}
virtual void addTakeoverListener(TakeoverListener* lisener) {}
virtual void removeTakeoverListener(TakeoverListener* lisener) {}
/**
* Add additional worker threads
@@ -195,6 +204,16 @@ public:
*/
virtual void waitForEnd() = 0;
/*
* Stop accepting new connections and finish ongoing requests.
*/
virtual void waitForJobs() = 0;
/*
* Close the port this server is listening on.
*/
virtual void closePort() = 0;
/**
* Gracefully stop this web server. We will stop accepting new connections
* and finish ongoing requests without being interrupted in the middle of
+1 -1
Ver Arquivo
@@ -797,7 +797,7 @@ void Transport::onSendEnd() {
}
auto httpResponseStats = ServiceData::createTimeseries(
folly::to<string>(HTTP_RESPONSE_STATS_PREFIX, getResponseCode()),
{ServiceData::StatsType::SUM});
{ServiceData::StatsType::RATE});
httpResponseStats->addValue(1);
onSendEndImpl();
}
+8 -7
Ver Arquivo
@@ -118,6 +118,12 @@ void VirtualHost::initRuntimeOption(Hdf overwrite) {
m_runtimeOption.requestTimeoutSeconds = requestTimeoutSeconds;
m_runtimeOption.maxPostSize = maxPostSize;
m_runtimeOption.uploadMaxFileSize = uploadMaxFileSize;
m_documentRoot = RuntimeOption::SourceRoot + m_pathTranslation;
if (!m_documentRoot.empty() &&
m_documentRoot[m_documentRoot.length() - 1] == '/') {
m_documentRoot = m_documentRoot.substr(0, m_documentRoot.length() - 1);
}
}
void VirtualHost::addAllowedDirectories(const std::vector<std::string>& dirs) {
@@ -150,7 +156,6 @@ void VirtualHost::init(Hdf vh) {
const char *pattern = vh["Pattern"].get("");
const char *pathTranslation = vh["PathTranslation"].get("");
Hdf overwrite = vh["overwrite"];
initRuntimeOption(overwrite);
if (prefix) m_prefix = prefix;
if (pattern) {
@@ -166,13 +171,9 @@ void VirtualHost::init(Hdf vh) {
m_pathTranslation += '/';
}
}
m_disabled = vh["Disabled"].getBool(false);
initRuntimeOption(overwrite);
m_documentRoot = RuntimeOption::SourceRoot + m_pathTranslation;
if (!m_documentRoot.empty() &&
m_documentRoot[m_documentRoot.length() - 1] == '/') {
m_documentRoot = m_documentRoot.substr(0, m_documentRoot.length() - 1);
}
m_disabled = vh["Disabled"].getBool(false);
Hdf rewriteRules = vh["RewriteRules"];
for (Hdf hdf = rewriteRules.firstChild(); hdf.exists(); hdf = hdf.next()) {
+10 -24
Ver Arquivo
@@ -782,15 +782,6 @@ template<class Target> Target read_opcode_arg(AsmState& as) {
}
}
uint8_t read_AssertT_arg(AsmState& as) {
auto const str = read_opcode_arg<std::string>(as);
#define ASSERTT_OP(x) if (str == #x) return static_cast<uint8_t>(AssertTOp::x);
ASSERTT_OPS
#undef ASSERTT_OP
as.error("unknown AssertT operand");
NOT_REACHED();
}
const StringData* read_litstr(AsmState& as) {
as.in.skipSpaceTab();
std::string strVal;
@@ -1011,11 +1002,8 @@ OpcodeParserMap opcode_parsers;
read_opcode_arg<std::string>(as)))
#define IMM_IA as.ue->emitIVA(as.getIterId( \
read_opcode_arg<int32_t>(as)))
#define IMM_OA as.ue->emitByte( \
(thisOpcode == Op::AssertTL || \
thisOpcode == Op::AssertTStk) ? read_AssertT_arg(as) \
: uint8_t(read_opcode_arg<int32_t>(as)))
// TODO more subop names
#define IMM_OA as.ue->emitByte( \
uint8_t(read_opcode_arg<int32_t>(as))) // TODO op names
#define IMM_AA as.ue->emitInt32(as.ue->mergeArray(read_litarray(as)))
/*
@@ -1080,13 +1068,12 @@ OpcodeParserMap opcode_parsers;
#define NUM_POP_ONE(a) 1
#define NUM_POP_TWO(a,b) 2
#define NUM_POP_THREE(a,b,c) 3
#define NUM_POP_MMANY vecImmStackValues
#define NUM_POP_V_MMANY (1 + vecImmStackValues)
#define NUM_POP_R_MMANY (1 + vecImmStackValues)
#define NUM_POP_C_MMANY (1 + vecImmStackValues)
#define NUM_POP_LMANY vecImmStackValues
#define NUM_POP_V_LMANY (1 + vecImmStackValues)
#define NUM_POP_R_LMANY (1 + vecImmStackValues)
#define NUM_POP_C_LMANY (1 + vecImmStackValues)
#define NUM_POP_FMANY immIVA /* number of arguments */
#define NUM_POP_CVMANY immIVA /* number of arguments */
#define NUM_POP_CVUMANY immIVA /* number of arguments */
#define NUM_POP_CMANY immIVA /* number of arguments */
#define O(name, imm, pop, push, flags) \
@@ -1160,13 +1147,12 @@ OPCODES
#undef NUM_POP_TWO
#undef NUM_POP_THREE
#undef NUM_POP_POS_N
#undef NUM_POP_MMANY
#undef NUM_POP_V_MMANY
#undef NUM_POP_R_MMANY
#undef NUM_POP_C_MMANY
#undef NUM_POP_LMANY
#undef NUM_POP_V_LMANY
#undef NUM_POP_R_LMANY
#undef NUM_POP_C_LMANY
#undef NUM_POP_FMANY
#undef NUM_POP_CVMANY
#undef NUM_POP_CVUMANY
#undef NUM_POP_CMANY
void initialize_opcode_map() {
+16 -50
Ver Arquivo
@@ -1260,10 +1260,14 @@ Array VMExecutionContext::getCallerInfo() {
return result;
}
VarEnv* VMExecutionContext::getVarEnv() {
VarEnv* VMExecutionContext::getVarEnv(int frame) {
VMRegAnchor _;
ActRec* fp = getFP();
for (; frame > 0; --frame) {
if (!fp) break;
fp = getPrevVMState(fp);
}
if (UNLIKELY(!fp)) return NULL;
if (fp->skipFrame()) {
fp = getPrevVMState(fp);
@@ -4624,44 +4628,6 @@ IOP_TYPE_CHECK_INSTR(true, Double, is_double)
IOP_TYPE_CHECK_INSTR(true, Bool, is_bool)
#undef IOP_TYPE_CHECK_INSTR
OPTBLD_INLINE static void implAssertT(TypedValue* tv, AssertTOp op) {
switch (op) {
case AssertTOp::Uninit: assert(tv->m_type == KindOfUninit); break;
case AssertTOp::InitNull: assert(tv->m_type == KindOfNull); break;
case AssertTOp::Int: assert(tv->m_type == KindOfInt64); break;
case AssertTOp::Dbl: assert(tv->m_type == KindOfDouble); break;
case AssertTOp::Res: assert(tv->m_type == KindOfResource); break;
case AssertTOp::Null: assert(IS_NULL_TYPE(tv->m_type)); break;
case AssertTOp::Bool: assert(tv->m_type == KindOfBoolean); break;
case AssertTOp::Str: assert(IS_STRING_TYPE(tv->m_type)); break;
case AssertTOp::Arr: assert(tv->m_type == KindOfArray); break;
case AssertTOp::Obj: assert(tv->m_type == KindOfObject); break;
case AssertTOp::InitUnc: assert(tv->m_type != KindOfUninit);
/* fallthrough */
case AssertTOp::Unc: assert(!IS_REFCOUNTED_TYPE(tv->m_type) ||
(tv->m_type == KindOfString && tv->m_data.pstr->isStatic()) ||
(tv->m_type == KindOfArray && tv->m_data.parr->isStatic())); break;
case AssertTOp::InitCell: assert(tv->m_type != KindOfUninit &&
tv->m_type != KindOfRef); break;
}
}
OPTBLD_INLINE void VMExecutionContext::iopAssertTL(PC& pc) {
NEXT();
DECODE_LA(localId);
DECODE_OA(op);
implAssertT(frame_local(m_fp, localId), static_cast<AssertTOp>(op));
}
OPTBLD_INLINE void VMExecutionContext::iopAssertTStk(PC& pc) {
NEXT();
DECODE_IVA(stkSlot);
DECODE_OA(op);
implAssertT(m_stack.indTV(stkSlot), static_cast<AssertTOp>(op));
}
OPTBLD_INLINE void VMExecutionContext::iopEmptyL(PC& pc) {
NEXT();
DECODE_LA(local);
@@ -4877,7 +4843,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetWithRefRM(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpL(PC& pc) {
NEXT();
DECODE_LA(local);
DECODE_OA(op);
DECODE(unsigned char, op);
Cell* fr = m_stack.topC();
Cell* to = tvToCell(frame_local(m_fp, local));
SETOP_BODY_CELL(to, op, fr);
@@ -4887,7 +4853,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpL(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpN(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
StringData* name;
Cell* fr = m_stack.topC();
TypedValue* tv2 = m_stack.indTV(1);
@@ -4905,7 +4871,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpN(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpG(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
StringData* name;
Cell* fr = m_stack.topC();
TypedValue* tv2 = m_stack.indTV(1);
@@ -4923,7 +4889,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpG(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpS(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
Cell* fr = m_stack.topC();
TypedValue* classref = m_stack.indTV(1);
TypedValue* propn = m_stack.indTV(2);
@@ -4947,7 +4913,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpS(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpM(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
DECLARE_SETHELPER_ARGS
if (!setHelperPre<MoreWarnings, true, false, false, 1,
VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
@@ -4988,7 +4954,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpM(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecL(PC& pc) {
NEXT();
DECODE_LA(local);
DECODE_OA(op);
DECODE(unsigned char, op);
TypedValue* to = m_stack.allocTV();
tvWriteUninit(to);
TypedValue* fr = frame_local(m_fp, local);
@@ -4997,7 +4963,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecL(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecN(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
StringData* name;
TypedValue* nameCell = m_stack.topTV();
TypedValue* local = nullptr;
@@ -5010,7 +4976,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecN(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecG(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
StringData* name;
TypedValue* nameCell = m_stack.topTV();
TypedValue* gbl = nullptr;
@@ -5024,7 +4990,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecG(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecS(PC& pc) {
StringData* name;
SPROP_OP_PRELUDE
DECODE_OA(op);
DECODE(unsigned char, op);
if (!(visible && accessible)) {
raise_error("Invalid static property access: %s::%s",
clsref->m_data.pcls->name()->data(),
@@ -5038,7 +5004,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecS(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecM(PC& pc) {
NEXT();
DECODE_OA(op);
DECODE(unsigned char, op);
DECLARE_SETHELPER_ARGS
TypedValue to;
tvWriteUninit(&to);
@@ -6414,7 +6380,7 @@ OPTBLD_INLINE void VMExecutionContext::iopThis(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopBareThis(PC& pc) {
NEXT();
DECODE_OA(notice);
DECODE(unsigned char, notice);
if (m_fp->hasThis()) {
ObjectData* this_ = m_fp->getThis();
m_stack.pushObject(this_);
+1 -1
Ver Arquivo
@@ -45,6 +45,6 @@ extern int register_gdb_hook(char *symfile_addr,
uint64_t symfile_size, DwarfChunk* d);
extern void unregister_gdb_chunk(DwarfChunk* d);
extern void __jit_debug_register_code();
extern "C" void __jit_debug_register_code();
#endif
+2
Ver Arquivo
@@ -14,6 +14,8 @@
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/vm/debug/gdb-jit.h"
/* __jit_debug_regiser_code() needs to be defined in a separate file
* from the one it is called from. Otherwise gcc notices that it is empty,
* and optimizes away the call. This prevents gdb from trapping updates to
-14
Ver Arquivo
@@ -751,20 +751,6 @@ int Func::getDVEntryNumParams(Offset offset) const {
return -1;
}
bool Func::shouldPGO() const {
if (!RuntimeOption::EvalJitPGO) return false;
// Cloned closures use the func prologue tables to hold the
// addresses of the DV funclets, and not real prologues. The
// mechanism to retranslate prologues currently assumes that the
// prologue tables contain real prologues, so it doesn't properly
// handle cloned closures for now. So don't profile & retranslate
// them for now.
if (isClonedClosure()) return false;
if (!RuntimeOption::EvalJitPGOHotOnly) return true;
return attrs() & AttrHot;
}
//=============================================================================
// FuncEmitter.
-2
Ver Arquivo
@@ -369,8 +369,6 @@ struct Func {
return shared()->m_userAttributes;
}
bool shouldPGO() const;
/**
* Closure's __invoke()s have an extra pointer used to keep cloned versions
* of themselves with different contexts.
+16 -33
Ver Arquivo
@@ -362,13 +362,12 @@ int instrNumPops(const Op* opcode) {
#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 LMANY -1
#define C_LMANY -2
#define V_LMANY -2
#define R_LMANY -2
#define FMANY -3
#define CVMANY -3
#define CVUMANY -3
#define CMANY -3
#define O(name, imm, pop, push, flags) pop,
OPCODES
@@ -377,13 +376,12 @@ int instrNumPops(const Op* opcode) {
#undef TWO
#undef THREE
#undef FOUR
#undef MMANY
#undef C_MMANY
#undef V_MMANY
#undef R_MMANY
#undef LMANY
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef FMANY
#undef CVMANY
#undef CVUMANY
#undef CMANY
#undef O
};
@@ -502,13 +500,12 @@ FlavorDesc instrInputFlavor(const Op* op, uint32_t idx) {
#define TWO(f1, f2) return doFlavor(idx, f1, f2);
#define THREE(f1, f2, f3) return doFlavor(idx, f1, f2, f3);
#define FOUR(f1, f2, f3, f4) return doFlavor(idx, f1, f2, f3, f4);
#define MMANY return minstrFlavor(op, idx, nov);
#define C_MMANY return minstrFlavor(op, idx, CV);
#define V_MMANY return minstrFlavor(op, idx, VV);
#define R_MMANY return minstrFlavor(op, idx, RV);
#define LMANY return minstrFlavor(op, idx, nov);
#define C_LMANY return minstrFlavor(op, idx, CV);
#define V_LMANY return minstrFlavor(op, idx, VV);
#define R_LMANY return minstrFlavor(op, idx, RV);
#define FMANY return manyFlavor(op, idx, FV);
#define CVMANY return manyFlavor(op, idx, CVV);
#define CVUMANY return manyFlavor(op, idx, CVUV);
#define CMANY return manyFlavor(op, idx, CV);
#define O(name, imm, pop, push, flags) case Op::name: pop
switch (*op) {
@@ -520,13 +517,12 @@ FlavorDesc instrInputFlavor(const Op* op, uint32_t idx) {
#undef TWO
#undef THREE
#undef FOUR
#undef MMANY
#undef C_MMANY
#undef V_MMANY
#undef R_MMANY
#undef LMANY
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef FMANY
#undef CVMANY
#undef CVUMANY
#undef CMANY
#undef O
}
@@ -789,16 +785,6 @@ std::string instrToString(const Op* it, const Unit* u /* = NULL */) {
static const int setopNamesCount =
(int)(sizeof(setopNames)/sizeof(const char*));
auto assertTName = [&] (int immVal) {
# define ASSERTT_OP(x) case AssertTOp::x: return #x;
switch (static_cast<AssertTOp>(immVal)) {
ASSERTT_OPS
}
# undef ASSERTT_OP
assert(false);
return "<" "?" ">";
};
std::stringstream out;
const Op* iStart = it;
Op op = *it;
@@ -844,9 +830,6 @@ std::string instrToString(const Op* it, const Unit* u /* = NULL */) {
out << ((immVal >=0 && immVal < setopNamesCount) ? \
setopNames[immVal] : "?"); \
break; \
case Op::AssertTL: case Op::AssertTStk: \
out << assertTName(immVal); \
break; \
default: \
out << immVal; \
break; \
+21 -46
Ver Arquivo
@@ -68,15 +68,13 @@ union ArgUnion {
const Offset InvalidAbsoluteOffset = -1;
enum FlavorDesc {
NOV, // None
CV, // Cell
VV, // Var
AV, // Classref
RV, // Return value (cell or var)
FV, // Function parameter (cell or var)
UV, // Uninit
CVV, // Cell or Var argument
CVUV, // Cell, Var, or Uninit argument
NOV, // None
CV, // Cell
VV, // Var
AV, // Classref
RV, // Return value (cell or var)
FV, // Function parameter (cell or var)
CVV, // Cell or Var argument
};
enum InstrFlags {
@@ -307,27 +305,6 @@ enum IncDecOp {
IncDec_invalid
};
#define ASSERTT_OPS \
ASSERTT_OP(Uninit) \
ASSERTT_OP(InitNull) \
ASSERTT_OP(Int) \
ASSERTT_OP(Dbl) \
ASSERTT_OP(Res) \
ASSERTT_OP(Null) \
ASSERTT_OP(Bool) \
ASSERTT_OP(Str) \
ASSERTT_OP(Arr) \
ASSERTT_OP(Obj) \
ASSERTT_OP(InitUnc) \
ASSERTT_OP(Unc) \
ASSERTT_OP(InitCell)
enum class AssertTOp : uint8_t {
#define ASSERTT_OP(op) op,
ASSERTT_OPS
#undef ASSERTT_OP
};
enum IterKind {
KindOfIter = 0,
KindOfMIter = 1,
@@ -372,7 +349,7 @@ enum SetOpOp {
O(BoxR, NA, ONE(RV), ONE(VV), NF) \
O(UnboxR, NA, ONE(RV), ONE(CV), NF) \
O(Null, NA, NOV, ONE(CV), NF) \
O(NullUninit, NA, NOV, ONE(UV), NF) \
O(NullUninit, NA, NOV, ONE(CV), NF) \
O(True, NA, NOV, ONE(CV), NF) \
O(False, NA, NOV, ONE(CV), NF) \
O(Int, ONE(I64A), NOV, ONE(CV), NF) \
@@ -446,12 +423,12 @@ enum SetOpOp {
O(CGetN, NA, ONE(CV), ONE(CV), NF) \
O(CGetG, NA, ONE(CV), ONE(CV), NF) \
O(CGetS, NA, TWO(AV,CV), ONE(CV), NF) \
O(CGetM, ONE(MA), MMANY, ONE(CV), NF) \
O(CGetM, ONE(MA), LMANY, ONE(CV), NF) \
O(VGetL, ONE(LA), NOV, ONE(VV), NF) \
O(VGetN, NA, ONE(CV), ONE(VV), NF) \
O(VGetG, NA, ONE(CV), ONE(VV), NF) \
O(VGetS, NA, TWO(AV,CV), ONE(VV), NF) \
O(VGetM, ONE(MA), MMANY, ONE(VV), NF) \
O(VGetM, ONE(MA), LMANY, ONE(VV), NF) \
O(AGetC, NA, ONE(CV), ONE(AV), NF) \
O(AGetL, ONE(LA), NOV, ONE(AV), NF) \
O(AKExists, NA, TWO(CV,CV), ONE(CV), NF) \
@@ -459,12 +436,12 @@ enum SetOpOp {
O(IssetN, NA, ONE(CV), ONE(CV), NF) \
O(IssetG, NA, ONE(CV), ONE(CV), NF) \
O(IssetS, NA, TWO(AV,CV), ONE(CV), NF) \
O(IssetM, ONE(MA), MMANY, ONE(CV), NF) \
O(IssetM, ONE(MA), LMANY, ONE(CV), NF) \
O(EmptyL, ONE(LA), NOV, ONE(CV), NF) \
O(EmptyN, NA, ONE(CV), ONE(CV), NF) \
O(EmptyG, NA, ONE(CV), ONE(CV), NF) \
O(EmptyS, NA, TWO(AV,CV), ONE(CV), NF) \
O(EmptyM, ONE(MA), MMANY, ONE(CV), NF) \
O(EmptyM, ONE(MA), LMANY, ONE(CV), NF) \
/* NB: isTypePred depends on this ordering. */ \
O(IsNullC, NA, ONE(CV), ONE(CV), NF) \
O(IsBoolC, NA, ONE(CV), ONE(CV), NF) \
@@ -480,34 +457,32 @@ enum SetOpOp {
O(IsStringL, ONE(LA), NOV, ONE(CV), NF) \
O(IsArrayL, ONE(LA), NOV, ONE(CV), NF) \
O(IsObjectL, ONE(LA), NOV, ONE(CV), NF) \
O(AssertTL, TWO(LA,OA), NOV, NOV, NF) \
O(AssertTStk, TWO(IVA,OA), NOV, NOV, NF) \
O(SetL, ONE(LA), ONE(CV), ONE(CV), NF) \
O(SetN, NA, TWO(CV,CV), ONE(CV), NF) \
O(SetG, NA, TWO(CV,CV), ONE(CV), NF) \
O(SetS, NA, THREE(CV,AV,CV), ONE(CV), NF) \
O(SetM, ONE(MA), C_MMANY, ONE(CV), NF) \
O(SetWithRefLM, TWO(MA,LA), MMANY, NOV, NF) \
O(SetWithRefRM, ONE(MA), R_MMANY, NOV, NF) \
O(SetM, ONE(MA), C_LMANY, ONE(CV), NF) \
O(SetWithRefLM, TWO(MA,LA), LMANY, NOV, NF) \
O(SetWithRefRM, ONE(MA), R_LMANY, NOV, NF) \
O(SetOpL, TWO(LA,OA), ONE(CV), ONE(CV), NF) \
O(SetOpN, ONE(OA), TWO(CV,CV), ONE(CV), NF) \
O(SetOpG, ONE(OA), TWO(CV,CV), ONE(CV), NF) \
O(SetOpS, ONE(OA), THREE(CV,AV,CV), ONE(CV), NF) \
O(SetOpM, TWO(OA,MA), C_MMANY, ONE(CV), NF) \
O(SetOpM, TWO(OA,MA), C_LMANY, ONE(CV), NF) \
O(IncDecL, TWO(LA,OA), NOV, ONE(CV), NF) \
O(IncDecN, ONE(OA), ONE(CV), ONE(CV), NF) \
O(IncDecG, ONE(OA), ONE(CV), ONE(CV), NF) \
O(IncDecS, ONE(OA), TWO(AV,CV), ONE(CV), NF) \
O(IncDecM, TWO(OA,MA), MMANY, ONE(CV), NF) \
O(IncDecM, TWO(OA,MA), LMANY, ONE(CV), NF) \
O(BindL, ONE(LA), ONE(VV), ONE(VV), NF) \
O(BindN, NA, TWO(VV,CV), ONE(VV), NF) \
O(BindG, NA, TWO(VV,CV), ONE(VV), NF) \
O(BindS, NA, THREE(VV,AV,CV), ONE(VV), NF) \
O(BindM, ONE(MA), V_MMANY, ONE(VV), NF) \
O(BindM, ONE(MA), V_LMANY, ONE(VV), NF) \
O(UnsetL, ONE(LA), NOV, NOV, NF) \
O(UnsetN, NA, ONE(CV), NOV, NF) \
O(UnsetG, NA, ONE(CV), NOV, NF) \
O(UnsetM, ONE(MA), MMANY, NOV, NF) \
O(UnsetM, ONE(MA), LMANY, NOV, NF) \
/* NOTE: isFPush below relies on the grouping of FPush* here */ \
O(FPushFunc, ONE(IVA), ONE(CV), NOV, NF) \
O(FPushFuncD, TWO(IVA,SA), NOV, NOV, NF) \
@@ -532,10 +507,10 @@ enum SetOpOp {
O(FPassN, ONE(IVA), ONE(CV), ONE(FV), FF) \
O(FPassG, ONE(IVA), ONE(CV), ONE(FV), FF) \
O(FPassS, ONE(IVA), TWO(AV,CV), ONE(FV), FF) \
O(FPassM, TWO(IVA,MA), MMANY, ONE(FV), FF) \
O(FPassM, TWO(IVA,MA), LMANY, ONE(FV), FF) \
O(FCall, ONE(IVA), FMANY, ONE(RV), CF_FF) \
O(FCallArray, NA, ONE(FV), ONE(RV), CF_FF) \
O(FCallBuiltin, THREE(IVA,IVA,SA),CVUMANY, ONE(RV), CF) \
O(FCallBuiltin, THREE(IVA,IVA,SA),CVMANY, ONE(RV), CF) \
O(CufSafeArray, NA, THREE(RV,CV,CV), ONE(CV), NF) \
O(CufSafeReturn, NA, THREE(RV,CV,CV), ONE(RV), NF) \
O(IterInit, THREE(IA,BA,LA), ONE(CV), NOV, CF) \
+1 -1
Ver Arquivo
@@ -219,7 +219,7 @@ template<typename L> inline
void Block::forEachSrc(unsigned i, L body) {
for (Edge& e : m_preds) {
IRInstruction* jmp = e.from()->back();
assert(jmp->op() == Jmp && jmp->taken() == this);
assert(jmp->op() == Jmp_ && jmp->taken() == this);
body(jmp, jmp->src(i));
}
}
+3 -3
Ver Arquivo
@@ -90,7 +90,7 @@ DEBUG_ONLY static int numBlockParams(Block* b) {
* 6. If the DefLabel produces a value, all of its incoming edges must be from
* blocks listed in the block list for this block's Trace.
* 7. Any path from this block to a Block that expects values must be
* from a Jmp instruciton.
* from a Jmp_ instruciton.
* 8. Every instruction's BCMarker must point to a valid bytecode instruction.
* 9. If this block is a catch block, it must have at most one predecessor
* and the trace containing it must contain exactly this block.
@@ -129,9 +129,9 @@ bool checkBlock(Block* b) {
// Invariant #7
if (b->taken()) {
// only Jmp can branch to a join block expecting values.
// only Jmp_ can branch to a join block expecting values.
DEBUG_ONLY IRInstruction* branch = b->back();
DEBUG_ONLY auto numArgs = branch->op() == Jmp ? branch->numSrcs() : 0;
DEBUG_ONLY auto numArgs = branch->op() == Jmp_ ? branch->numSrcs() : 0;
assert(numBlockParams(b->taken()) == numArgs);
}
+6 -7
Ver Arquivo
@@ -156,7 +156,7 @@ PUNT_OPCODE(JmpIsType)
PUNT_OPCODE(JmpIsNType)
PUNT_OPCODE(JmpZero)
PUNT_OPCODE(JmpNZero)
PUNT_OPCODE(Jmp)
PUNT_OPCODE(Jmp_)
PUNT_OPCODE(ReqBindJmpGt)
PUNT_OPCODE(ReqBindJmpGte)
PUNT_OPCODE(ReqBindJmpLt)
@@ -458,10 +458,10 @@ void CodeGenerator::emitTypeTest(Type type, Loc typeSrc, Loc dataSrc,
// Note: ARM can actually do better here; it has a fused test-and-branch
// instruction. The way this code is factored makes it difficult to use,
// though; the jump instruction will be written by some other code.
m_as. Tst (rAsm.W(), KindOfStringBit);
m_as. Cmp (rAsm.W(), KindOfStringBit);
cc = CC_NE;
} else if (type.equals(Type::UncountedInit)) {
m_as. Tst (rAsm.W(), KindOfUncountedInitBit);
m_as. Cmp (rAsm.W(), KindOfUncountedInitBit);
cc = CC_NE;
} else if (type.equals(Type::Uncounted)) {
m_as. Cmp (rAsm.W(), KindOfRefCountThreshold);
@@ -486,8 +486,8 @@ void CodeGenerator::emitTypeTest(Type type, Loc typeSrc, Loc dataSrc,
doJcc(CC_E);
} else if (type.subtypeOf(Type::Arr) && type.hasArrayKind()) {
m_as. Ldr (rAsm, dataSrc);
m_as. Ldrb (rAsm.W(), rAsm[ArrayData::offsetofKind()]);
m_as. Cmp (rAsm.W(), type.getArrayKind());
m_as. Ldr (rAsm, rAsm[ArrayData::offsetofKind()]);
m_as. Cmp (rAsm, type.getArrayKind());
doJcc(CC_E);
}
}
@@ -840,8 +840,7 @@ void CodeGenerator::cgBlock(Block* block, vector<TransBCMapping>* bcMap) {
// marker since the last instruction, update the bc mapping.
if ((!prevMarker.valid() || inst->marker() != prevMarker) &&
m_tx64->isTransDBEnabled() && bcMap) {
bcMap->push_back(TransBCMapping{inst->marker().func->unit()->md5(),
inst->marker().bcOff,
bcMap->push_back(TransBCMapping{inst->marker().bcOff,
m_as.frontier(),
m_astubs.frontier()});
prevMarker = inst->marker();
@@ -170,15 +170,6 @@ struct IfCountNotStatic {
}
};
void emitTransCounterInc(Asm& a) {
if (!tx64->isTransDBEnabled()) return;
a. movq (tx64->getTransCounterAddr(), rAsm);
a. lock ();
a. incq (*rAsm);
}
void emitIncRef(Asm& as, PhysReg base) {
if (RuntimeOption::EvalHHIRGenerateAsserts) {
emitAssertRefCount(as, base);
-2
Ver Arquivo
@@ -49,8 +49,6 @@ void emitEagerSyncPoint(Asm& as, const HPHP::Opcode* pc,
void emitEagerVMRegSave(Asm& as, RegSaveFlags flags);
void emitGetGContext(Asm& as, PhysReg dest);
void emitTransCounterInc(Asm& a);
void emitIncRef(Asm& as, PhysReg base);
void emitIncRefCheckNonStatic(Asm& as, PhysReg base, DataType dtype);
void emitIncRefGenericRegSafe(Asm& as, PhysReg base, int disp, PhysReg tmpReg);
+42 -54
Ver Arquivo
@@ -709,41 +709,42 @@ static int64_t shuffleArgs(Asm& a, ArgGroup& args) {
argDescs[int(dstReg)] = &args[i];
}
}
auto const howTo = doRegMoves(moves, int(rCgGP));
std::vector<MoveInfo> howTo;
doRegMoves(moves, int(rCgGP), howTo);
// Execute the plan
for (auto& how : howTo) {
if (how.m_kind == MoveInfo::Kind::Move) {
if (how.m_reg2 == rCgGP) {
emitMovRegReg(a, how.m_reg1, how.m_reg2);
for (size_t i = 0; i < howTo.size(); ++i) {
if (howTo[i].m_kind == MoveInfo::Kind::Move) {
if (howTo[i].m_reg2 == rCgGP) {
emitMovRegReg(a, howTo[i].m_reg1, howTo[i].m_reg2);
} else {
ArgDesc* argDesc = argDescs[int(how.m_reg2)];
ArgDesc* argDesc = argDescs[int(howTo[i].m_reg2)];
ArgDesc::Kind kind = argDesc->kind();
if (kind == ArgDesc::Kind::Reg || kind == ArgDesc::Kind::TypeReg) {
if (argDesc->isZeroExtend()) {
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. movzbl (rbyte(how.m_reg1), r32(how.m_reg2));
assert(howTo[i].m_reg1.isGP());
assert(howTo[i].m_reg2.isGP());
a. movzbl (rbyte(howTo[i].m_reg1), r32(howTo[i].m_reg2));
} else {
emitMovRegReg(a, how.m_reg1, how.m_reg2);
emitMovRegReg(a, howTo[i].m_reg1, howTo[i].m_reg2);
}
} else {
assert(kind == ArgDesc::Kind::Addr);
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. lea (how.m_reg1[argDesc->imm().q()], how.m_reg2);
assert(howTo[i].m_reg1.isGP());
assert(howTo[i].m_reg2.isGP());
a. lea (howTo[i].m_reg1[argDesc->imm().q()],
howTo[i].m_reg2);
}
if (kind != ArgDesc::Kind::TypeReg) {
argDesc->markDone();
}
}
} else {
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. xchgq (how.m_reg1, how.m_reg2);
assert(howTo[i].m_reg1.isGP());
assert(howTo[i].m_reg2.isGP());
a. xchgq (howTo[i].m_reg1, howTo[i].m_reg2);
}
}
// Handle const-to-register moves, type shifting,
// load-effective address and zero extending for bools.
// Ignore args that have been handled by the
@@ -4377,7 +4378,6 @@ void CodeGenerator::cgCheckBounds(IRInstruction* inst) {
kVoidDest, SyncOptions::kSyncPoint, args);
};
if (idx->isConst()) {
int64_t idxVal = idx->getValInt();
assert(idxVal >= 0); // we would have punted otherwise
@@ -4385,17 +4385,18 @@ void CodeGenerator::cgCheckBounds(IRInstruction* inst) {
unlikelyIfBlock(CC_LE, throwHelper);
return;
}
auto idxReg = m_regs[idx].reg();
m_as.cmpq(0, idxReg);
unlikelyIfBlock(CC_L, throwHelper);
if (size->isConst()) {
int64_t sizeVal = size->getValInt();
assert(sizeVal >= 0);
m_as.cmpq(sizeVal, idxReg);
m_as.cmpq(sizeVal, m_regs[idx].reg());
} else {
auto sizeReg = m_regs[size].reg();
m_as.cmpq(sizeReg, idxReg);
}
unlikelyIfBlock(CC_AE, throwHelper);
unlikelyIfBlock(CC_GE, throwHelper);
}
void CodeGenerator::cgLdVectorSize(IRInstruction* inst) {
@@ -5088,8 +5089,7 @@ void CodeGenerator::cgLdClsCached(IRInstruction* inst) {
CppCall(func),
callDest(inst->dst()),
SyncOptions::kSyncPoint,
ArgGroup(m_regs).addr(rVmTl, intptr_t(ch))
.ssa(inst->src(0)));
ArgGroup(m_regs).addr(rVmTl, intptr_t(ch)).ssas(inst, 0));
});
}
@@ -5281,7 +5281,7 @@ void CodeGenerator::cgReqBindJmpNZero(IRInstruction* inst) {
emitReqBindJcc(CC_NZ, inst->extra<ReqBindJmpNZero>());
}
void CodeGenerator::cgJmp(IRInstruction* inst) {
void CodeGenerator::cgJmp_(IRInstruction* inst) {
Block* target = inst->taken();
if (unsigned n = inst->numSrcs()) {
// Parallel-copy sources to the label's destination registers.
@@ -5294,7 +5294,7 @@ void CodeGenerator::cgJmp(IRInstruction* inst) {
auto dst = &dsts[i];
auto src = srcs[i];
// Currently, full XMM registers cannot be assigned to SSATmps
// passed from to Jmp to DefLabel. If this changes, it'll require
// passed from to Jmp_ to DefLabel. If this changes, it'll require
// teaching shuffleArgs() how to handle full XMM values.
assert(!m_regs[src].isFullXMM() && !m_regs[dst].isFullXMM());
if (m_regs[dst].reg(0) == InvalidReg) continue; // dst is unused.
@@ -5316,10 +5316,10 @@ void CodeGenerator::cgJmp(IRInstruction* inst) {
}
}
assert(args.numStackArgs() == 0 &&
"Jmp doesn't support passing arguments on the stack yet.");
"Jmp_ doesn't support passing arguments on the stack yet.");
shuffleArgs(m_as, args);
}
if (!m_state.noTerminalJmp) {
if (!m_state.noTerminalJmp_) {
emitFwdJmp(m_as, target, m_state);
}
}
@@ -5874,7 +5874,7 @@ void CodeGenerator::cgIncStat(IRInstruction *inst) {
}
void CodeGenerator::cgIncTransCounter(IRInstruction* inst) {
emitTransCounterInc(m_as);
m_tx64->emitTransCounterInc(m_as);
}
void CodeGenerator::cgDbgAssertRefCount(IRInstruction* inst) {
@@ -5933,7 +5933,8 @@ void CodeGenerator::cgRBTrace(IRInstruction* inst) {
}
void CodeGenerator::print() const {
JIT::print(std::cout, m_unit, &m_state.regs, nullptr, m_state.asmInfo);
JIT::print(std::cout, m_unit,
&m_state.regs, m_state.lifetime, m_state.asmInfo);
}
static void patchJumps(CodeBlock& cb, CodegenState& state, Block* block) {
@@ -5960,8 +5961,7 @@ void CodeGenerator::cgBlock(Block* block, vector<TransBCMapping>* bcMap) {
// marker since the last instruction, update the bc mapping.
if ((!prevMarker.valid() || inst->marker() != prevMarker) &&
m_tx64->isTransDBEnabled() && bcMap) {
bcMap->push_back(TransBCMapping{inst->marker().func->unit()->md5(),
inst->marker().bcOff,
bcMap->push_back(TransBCMapping{inst->marker().bcOff,
m_as.frontier(),
m_astubs.frontier()});
prevMarker = inst->marker();
@@ -6004,15 +6004,16 @@ LiveRegs computeLiveRegs(const IRUnit& unit, const RegAllocInfo& regs,
return live_regs;
}
void genCodeImpl(CodeBlock& mainCode,
CodeBlock& stubsCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
Transl::TranslatorX64* tx64,
const RegAllocInfo& regs,
AsmInfo* asmInfo) {
void genCode(CodeBlock& mainCode,
CodeBlock& stubsCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
Transl::TranslatorX64* tx64,
const RegAllocInfo& regs,
const LifetimeInfo* lifetime,
AsmInfo* asmInfo) {
LiveRegs live_regs = computeLiveRegs(unit, regs, unit.entry());
CodegenState state(unit, regs, live_regs, asmInfo);
CodegenState state(unit, regs, live_regs, lifetime, asmInfo);
// Returns: whether a block has already been emitted.
DEBUG_ONLY auto isEmitted = [&](Block* block) {
@@ -6036,10 +6037,10 @@ void genCodeImpl(CodeBlock& mainCode,
patchJumps(cb, state, block);
state.addresses[block] = aStart;
// If the block ends with a Jmp and the next block is going to be
// If the block ends with a Jmp_ and the next block is going to be
// its target, we don't need to actually emit it.
IRInstruction* last = block->back();
state.noTerminalJmp = last->op() == Jmp && nextBlock == last->taken();
state.noTerminalJmp_ = last->op() == Jmp_ && nextBlock == last->taken();
if (state.asmInfo) {
state.asmInfo->asmRanges[block] = TcaRange(aStart, cb.frontier());
@@ -6095,17 +6096,4 @@ void genCodeImpl(CodeBlock& mainCode,
}
}
void genCode(CodeBlock& main, CodeBlock& stubs, IRUnit& unit,
vector<TransBCMapping>* bcMap,
Transl::TranslatorX64* tx64,
const RegAllocInfo& regs) {
if (dumpIREnabled()) {
AsmInfo ai(unit);
genCodeImpl(main, stubs, unit, bcMap, tx64, regs, &ai);
dumpTrace(kCodeGenLevel, unit, " after code gen ", &regs, nullptr, &ai);
} else {
genCodeImpl(main, stubs, unit, bcMap, tx64, regs, nullptr);
}
}
}}
+20 -5
Ver Arquivo
@@ -82,11 +82,13 @@ typedef StateVector<IRInstruction, RegSet> LiveRegs;
// and address information produced during codegen.
struct CodegenState {
CodegenState(const IRUnit& unit, const RegAllocInfo& regs,
const LiveRegs& liveRegs, AsmInfo* asmInfo)
const LiveRegs& liveRegs, const LifetimeInfo* lifetime,
AsmInfo* asmInfo)
: patches(unit, nullptr)
, addresses(unit, nullptr)
, regs(regs)
, liveRegs(liveRegs)
, lifetime(lifetime)
, asmInfo(asmInfo)
, catches(unit, CatchInfo())
{}
@@ -96,9 +98,9 @@ struct CodegenState {
StateVector<Block,void*> patches;
StateVector<Block,TCA> addresses;
// True if this block's terminal Jmp has a desination equal to the
// True if this block's terminal Jmp_ has a desination equal to the
// next block in the same assmbler.
bool noTerminalJmp;
bool noTerminalJmp_;
// output from register allocator
const RegAllocInfo& regs;
@@ -108,6 +110,10 @@ struct CodegenState {
// registers.
const LiveRegs& liveRegs;
// Optional information used when pretty-printing code after codegen.
// when not available, these are nullptrs.
const LifetimeInfo* lifetime;
// Output: start/end ranges of machine code addresses of each instruction.
AsmInfo* asmInfo;
@@ -472,7 +478,7 @@ private:
* assert(args.size() == 3);
*/
struct ArgGroup {
typedef smart::vector<ArgDesc> ArgVec;
typedef std::vector<ArgDesc> ArgVec;
explicit ArgGroup(const RegAllocInfo& regs)
: m_regs(regs), m_override(nullptr)
@@ -519,6 +525,13 @@ struct ArgGroup {
return *this;
}
ArgGroup& ssas(IRInstruction* inst, unsigned begin, unsigned count = 1) {
for (SSATmp* s : inst->srcs().subpiece(begin, count)) {
push_arg(ArgDesc(s, m_regs[s]));
}
return *this;
}
/*
* Pass tmp as a TypedValue passed by value.
*/
@@ -588,7 +601,9 @@ void genCode(CodeBlock& mainCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
TranslatorX64* tx64,
const RegAllocInfo& regs);
const RegAllocInfo& regs,
const LifetimeInfo* lifetime = nullptr,
AsmInfo* asmInfo = nullptr);
}}
+12 -14
Ver Arquivo
@@ -189,14 +189,14 @@ BlockList removeUnreachable(IRTrace* trace, IRUnit& unit) {
continue;
}
FTRACE(5, "erasing block {}\n", (*bit)->id());
if ((*bit)->taken() && (*bit)->back()->op() == Jmp) {
if ((*bit)->taken() && (*bit)->back()->op() == Jmp_) {
needsReflow = true;
}
bit = t->erase(bit);
}
});
// 3. if we removed any whole blocks that ended in Jmp
// 3. if we removed any whole blocks that ended in Jmp_
// instructions, reflow all types in case they change the
// incoming types of DefLabel instructions.
if (needsReflow) reflowTypes(blocks.front(), blocks);
@@ -215,7 +215,7 @@ initInstructions(const BlockList& blocks, DceState& state) {
state[inst].setLive();
wl.push_back(&inst);
}
if (RuntimeOption::EvalHHIREnableRefCountOpt && inst.op() == DecRefNZ) {
if (inst.op() == DecRefNZ) {
auto* srcInst = inst.src(0)->inst();
Opcode srcOpc = srcInst->op();
if (srcOpc != DefConst) {
@@ -314,7 +314,7 @@ void optimizeRefCount(IRTrace* trace, IRTrace* main, DceState& state,
void sinkIncRefs(IRTrace* trace, IRUnit& unit, DceState& state) {
assert(trace->isMain());
assert(trace->back()->back()->op() != Jmp);
assert(trace->back()->back()->op() != Jmp_);
auto copyPropTrace = [] (IRTrace* trace) {
forEachInst(trace->blocks(), copyProp);
@@ -373,7 +373,7 @@ void sinkIncRefs(IRTrace* trace, IRUnit& unit, DceState& state) {
assert(state[inst].countConsumed());
}
}
if (inst->op() == DecRefNZ) {
if (inst->op() == DecRefNZ && !state[inst].isDead()) {
IRInstruction* srcInst = inst->src(0)->inst();
if (state[srcInst].isDead()) {
state[inst].setDead();
@@ -681,22 +681,20 @@ void eliminateDeadCode(IRUnit& unit) {
// If inst consumes this source, find the true source instruction and
// mark it as consumed if it's an IncRef.
if (RuntimeOption::EvalHHIREnableRefCountOpt && inst->consumesReference(i)) {
if (inst->consumesReference(i)) {
consumeIncRef(inst, src, state);
}
}
}
// Optimize IncRefs and DecRefs.
if (RuntimeOption::EvalHHIREnableRefCountOpt) {
forEachTrace(unit, [&](IRTrace* t) {
optimizeRefCount(t, unit.main(), state, uses);
});
forEachTrace(unit, [&](IRTrace* t) {
optimizeRefCount(t, unit.main(), state, uses);
});
if (RuntimeOption::EvalHHIREnableSinking) {
// Sink IncRefs consumed off trace.
sinkIncRefs(trace, unit, state);
}
if (RuntimeOption::EvalHHIREnableSinking) {
// Sink IncRefs consumed off trace.
sinkIncRefs(trace, unit, state);
}
// Optimize unused inlined activation records. It's not necessary
-428
Ver Arquivo
@@ -1,428 +0,0 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/vm/jit/func-prologues-x64.h"
#include "hphp/util/asm-x64.h"
#include "hphp/runtime/ext/ext_closure.h"
#include "hphp/runtime/vm/func.h"
#include "hphp/runtime/vm/srckey.h"
#include "hphp/runtime/vm/jit/code-gen-helpers-x64.h"
#include "hphp/runtime/vm/jit/translator-x64.h"
#include "hphp/runtime/vm/jit/translator-x64-internal.h"
#include "hphp/runtime/vm/jit/write-lease.h"
namespace HPHP { namespace JIT { namespace X64 {
//////////////////////////////////////////////////////////////////////
using Transl::TCA;
TRACE_SET_MOD(tx64);
constexpr auto kJcc8Len = 3;
//////////////////////////////////////////////////////////////////////
namespace {
void emitStackCheck(int funcDepth, Offset pc) {
using namespace reg;
Asm a { tx64->mainCode };
funcDepth += kStackCheckPadding * sizeof(Cell);
uint64_t stackMask = cellsToBytes(RuntimeOption::EvalVMStackElms) - 1;
a. mov_reg64_reg64(rVmSp, rAsm); // copy to destroy
a. and_imm64_reg64(stackMask, rAsm);
a. sub_imm64_reg64(funcDepth + Stack::sSurprisePageSize, rAsm);
// Unlikely branch to failure.
a. jl(tx64->uniqueStubs.stackOverflowHelper);
// Success.
}
TCA emitFuncGuard(X64Assembler& a, const Func* func) {
using namespace reg;
assert(kScratchCrossTraceRegs.contains(rax));
assert(kScratchCrossTraceRegs.contains(rdx));
const int kAlign = kX64CacheLineSize;
const int kAlignMask = kAlign - 1;
int loBits = uintptr_t(a.frontier()) & kAlignMask;
int delta, size;
// Ensure the immediate is safely smashable
// the immediate must not cross a qword boundary,
if (!deltaFits((intptr_t)func, sz::dword)) {
size = 8;
delta = loBits + kFuncMovImm;
} else {
size = 4;
delta = loBits + kFuncCmpImm;
}
delta = (delta + size - 1) & kAlignMask;
if (delta < size - 1) {
a.emitNop(size - 1 - delta);
}
TCA aStart DEBUG_ONLY = a.frontier();
if (!deltaFits((intptr_t)func, sz::dword)) {
a. load_reg64_disp_reg64(rStashedAR, AROFF(m_func), rax);
/*
Although func doesnt fit in a signed 32-bit immediate, it may still
fit in an unsigned one. Rather than deal with yet another case
(which only happens when we disable jemalloc) just force it to
be an 8-byte immediate, and patch it up afterwards.
*/
a. mov_imm64_reg(0xdeadbeeffeedface, rdx);
assert(((uint64_t*)a.frontier())[-1] == 0xdeadbeeffeedface);
((uint64_t*)a.frontier())[-1] = uintptr_t(func);
a. cmp_reg64_reg64(rax, rdx);
} else {
a. cmp_imm32_disp_reg32(uint64_t(func), AROFF(m_func), rStashedAR);
}
assert(tx64->uniqueStubs.funcPrologueRedispatch);
a. jnz(tx64->uniqueStubs.funcPrologueRedispatch);
assert(funcPrologueToGuard(a.frontier(), func) == aStart);
assert(funcPrologueHasGuard(a.frontier(), func));
return a.frontier();
}
// Initialize at most this many locals inline in function body prologue; more
// than this, and emitting a loop is more compact. To be precise, the actual
// crossover point in terms of code size is 6; 9 was determined by experiment to
// be the optimal point in certain benchmarks. #microoptimization
constexpr auto kLocalsToInitializeInline = 9;
SrcKey emitPrologueWork(Func* func, int nPassed) {
using Transl::Location;
using namespace reg;
int numParams = func->numParams();
const Func::ParamInfoVec& paramInfo = func->params();
Offset dvInitializer = InvalidAbsoluteOffset;
assert(IMPLIES(func->isGenerator(), nPassed == numParams));
Asm a { tx64->mainCode };
if (tx64->mode() == TransProflogue) {
assert(func->shouldPGO());
TransID transId = tx64->profData()->curTransID();
auto counterAddr = tx64->profData()->transCounterAddr(transId);
a.movq(counterAddr, rAsm);
a.decq(rAsm[0]);
}
if (nPassed > numParams) {
// Too many args; a weird case, so just callout. Stash ar
// somewhere callee-saved.
if (false) { // typecheck
Transl::trimExtraArgs((ActRec*)nullptr);
}
a. mov_reg64_reg64(rStashedAR, argNumToRegName[0]);
emitCall(a, TCA(Transl::trimExtraArgs));
// We'll fix rVmSp below.
} else if (nPassed < numParams) {
// Figure out which, if any, default value initializer to go to
for (int i = nPassed; i < numParams; ++i) {
const Func::ParamInfo& pi = paramInfo[i];
if (pi.hasDefaultValue()) {
dvInitializer = pi.funcletOff();
break;
}
}
TRACE(1, "Only have %d of %d args; getting dvFunclet\n",
nPassed, numParams);
a. emitImmReg(nPassed, rax);
// do { *(--rVmSp) = NULL; nPassed++; } while (nPassed < numParams);
// This should be an unusual case, so optimize for code density
// rather than execution speed; i.e., don't unroll the loop.
TCA loopTop = a.frontier();
a. sub_imm32_reg64(sizeof(Cell), rVmSp);
a. incl(eax);
emitStoreUninitNull(a, 0, rVmSp);
a. cmp_imm32_reg32(numParams, rax);
a. jcc8(CC_L, loopTop);
}
// Entry point for numParams == nPassed is here.
// Args are kosher. Frame linkage: set fp = ar.
a. mov_reg64_reg64(rStashedAR, rVmFp);
int numLocals = numParams;
if (func->isClosureBody()) {
// Closure object properties are the use vars followed by the
// static locals (which are per-instance).
int numUseVars = func->cls()->numDeclProperties() -
func->numStaticLocals();
emitLea(a, rVmFp[-cellsToBytes(numParams)], rVmSp);
PhysReg rClosure = rcx;
a. loadq(rVmFp[AROFF(m_this)], rClosure);
// Swap in the $this or late bound class
a. loadq(rClosure[c_Closure::ctxOffset()], rAsm);
a. storeq(rAsm, rVmFp[AROFF(m_this)]);
if (!(func->attrs() & AttrStatic)) {
a.shrq(1, rAsm);
JccBlock<CC_BE> ifRealThis(a);
a.shlq(1, rAsm);
// XXX can objects be static?
emitIncRefCheckNonStatic(a, rAsm, KindOfObject);
}
// Put in the correct context
a. loadq(rClosure[c_Closure::funcOffset()], rAsm);
a. storeq(rAsm, rVmFp[AROFF(m_func)]);
// Copy in all the use vars
int baseUVOffset = sizeof(ObjectData) + func->cls()->builtinPropSize();
for (int i = 0; i < numUseVars + 1; i++) {
int spOffset = -cellsToBytes(i+1);
if (i == 0) {
// The closure is the first local.
// We don't incref because it used to be $this
// and now it is a local, so they cancel out
emitStoreTypedValue(a, KindOfObject, rClosure, spOffset, rVmSp);
continue;
}
int uvOffset = baseUVOffset + cellsToBytes(i-1);
emitCopyTo(a, rClosure, uvOffset, rVmSp, spOffset, rAsm);
emitIncRefGenericRegSafe(a, rVmSp, spOffset, rAsm);
}
numLocals += numUseVars + 1;
}
// We're in the callee frame; initialize locals. Unroll the loop all
// the way if there are a modest number of locals to update;
// otherwise, do it in a compact loop. If we're in a generator body,
// named locals will be initialized by UnpackCont so we can leave
// them alone here.
int numUninitLocals = func->numLocals() - numLocals;
assert(numUninitLocals >= 0);
if (numUninitLocals > 0 && !func->isGenerator()) {
// If there are too many locals, then emitting a loop to initialize locals
// is more compact, rather than emitting a slew of movs inline.
if (numUninitLocals > kLocalsToInitializeInline) {
PhysReg loopReg = rcx;
// rVmFp + rcx points to the count/type fields of the TypedValue we're
// about to write to.
int loopStart = -func->numLocals() * sizeof(TypedValue) + TVOFF(m_type);
int loopEnd = -numLocals * sizeof(TypedValue) + TVOFF(m_type);
a. emitImmReg(loopStart, loopReg);
a. emitImmReg(KindOfUninit, rdx);
TCA topOfLoop = a.frontier();
// do {
// rVmFp[loopReg].m_type = KindOfUninit;
// } while(++loopReg != loopEnd);
emitStoreTVType(a, edx, rVmFp[loopReg]);
a. addq (sizeof(Cell), loopReg);
a. cmpq (loopEnd, loopReg);
a. jcc8 (CC_NE, topOfLoop);
} else {
PhysReg base;
int disp, k;
static_assert(KindOfUninit == 0, "");
if (numParams < func->numLocals()) {
a.xorl (eax, eax);
}
for (k = numLocals; k < func->numLocals(); ++k) {
locToRegDisp(Location(Location::Local, k), &base, &disp, func);
emitStoreTVType(a, eax, base[disp + TVOFF(m_type)]);
}
}
}
const HPHP::Opcode* destPC = func->unit()->entry() + func->base();
if (dvInitializer != InvalidAbsoluteOffset) {
// dispatch to funclet.
destPC = func->unit()->entry() + dvInitializer;
}
SrcKey funcBody(func, destPC);
// Move rVmSp to the right place: just past all locals
int frameCells = func->numSlotsInFrame();
if (func->isGenerator()) {
frameCells = 1;
} else {
emitLea(a, rVmFp[-cellsToBytes(frameCells)], rVmSp);
}
Fixup fixup(funcBody.offset() - func->base(), frameCells);
// Emit warnings for any missing arguments
if (!func->info() && !(func->attrs() & AttrNative)) {
for (int i = nPassed; i < numParams; ++i) {
if (paramInfo[i].funcletOff() == InvalidAbsoluteOffset) {
a. emitImmReg((intptr_t)func->name()->data(), argNumToRegName[0]);
a. emitImmReg(numParams, argNumToRegName[1]);
a. emitImmReg(i, argNumToRegName[2]);
emitCall(a, (TCA)raiseMissingArgument);
tx64->fixupMap().recordFixup(a.frontier(), fixup);
break;
}
}
}
// Check surprise flags in the same place as the interpreter: after
// setting up the callee's frame but before executing any of its
// code
emitCheckSurpriseFlagsEnter(tx64->mainCode, tx64->stubsCode, false,
tx64->fixupMap(), fixup);
if (func->isClosureBody() && func->cls()) {
int entry = nPassed <= numParams ? nPassed : numParams + 1;
// Relying on rStashedAR == rVmFp here
a. loadq (rStashedAR[AROFF(m_func)], rax);
a. loadq (rax[Func::prologueTableOff() + sizeof(TCA)*entry],
rax);
a. jmp (rax);
} else {
emitBindJmp(tx64->mainCode, tx64->stubsCode, funcBody);
}
return funcBody;
}
} // anonymous namespace
//////////////////////////////////////////////////////////////////////
TCA emitCallArrayPrologue(Func* func, DVFuncletsVec& dvs) {
auto& mainCode = tx64->mainCode;
auto& stubsCode = tx64->stubsCode;
Asm a { mainCode };
Asm astubs { stubsCode };
TCA start = mainCode.frontier();
if (dvs.size() == 1) {
a. cmp_imm32_disp_reg32(dvs[0].first,
AROFF(m_numArgsAndCtorFlag), rVmFp);
emitBindJcc(mainCode, stubsCode, CC_LE, SrcKey(func, dvs[0].second));
emitBindJmp(mainCode, stubsCode, SrcKey(func, func->base()));
} else {
a. load_reg64_disp_reg32(rVmFp, AROFF(m_numArgsAndCtorFlag), reg::rax);
for (unsigned i = 0; i < dvs.size(); i++) {
a. cmp_imm32_reg32(dvs[i].first, reg::rax);
emitBindJcc(mainCode, stubsCode, CC_LE, SrcKey(func, dvs[i].second));
}
emitBindJmp(mainCode, stubsCode, SrcKey(func, func->base()));
}
return start;
}
SrcKey emitFuncPrologue(CodeBlock& mainCode, CodeBlock& stubsCode,
Func* func, bool funcIsMagic, int nPassed,
TCA& start, TCA& aStart) {
Asm a { mainCode };
// Guard: we're in the right callee. This happens in magicStart for
// magic callees.
if (!funcIsMagic) {
start = aStart = emitFuncGuard(a, func);
}
emitRB(a, Trace::RBTypeFuncPrologueTry, func->fullName()->data());
// NB: We have most of the register file to play with, since we know
// we're between BB's. So, we hardcode some registers here rather
// than using the scratch allocator.
TRACE(2, "funcPrologue: user function: %s\n", func->name()->data());
// Add a counter for the translation if requested
if (RuntimeOption::EvalJitTransCounters) {
emitTransCounterInc(a);
}
if (!funcIsMagic) {
emitPopRetIntoActRec(a);
// entry point for magic methods comes later
emitRB(a, Trace::RBTypeFuncEntry, func->fullName()->data());
/*
* Guard: we have stack enough stack space to complete this
* function. We omit overflow checks if it is a leaf function
* that can't use more than kStackCheckLeafPadding cells.
*/
auto const needStackCheck =
!(func->attrs() & AttrPhpLeafFn) ||
func->maxStackCells() >= kStackCheckLeafPadding;
if (needStackCheck) {
emitStackCheck(cellsToBytes(func->maxStackCells()), func->base());
}
}
SrcKey skFuncBody = emitPrologueWork(func, nPassed);
if (funcIsMagic) {
// entry points for magic methods is here
TCA magicStart = emitFuncGuard(a, func);
emitPopRetIntoActRec(a);
emitRB(a, Trace::RBTypeFuncEntry, func->fullName()->data());
// Guard: we have stack enough stack space to complete this function.
emitStackCheck(cellsToBytes(func->maxStackCells()), func->base());
assert(func->numParams() == 2);
// Special __call prologue
a. mov_reg64_reg64(rStashedAR, argNumToRegName[0]);
emitCall(a, TCA(Transl::shuffleArgsForMagicCall));
if (memory_profiling) {
tx64->fixupMap().recordFixup(
a.frontier(),
Fixup(skFuncBody.offset() - func->base(), func->numSlotsInFrame())
);
}
// if shuffleArgs returns 0, that means this was not a magic call
// and we should proceed to a prologue specialized for nPassed;
// otherwise, proceed to a prologue specialized for nPassed==numParams (2).
if (nPassed == 2) {
a.jmp(start);
} else {
a.test_reg64_reg64(reg::rax, reg::rax);
// z ==> not a magic call, go to prologue for nPassed
if (deltaFits(start - (a.frontier() + kJcc8Len), sz::byte)) {
a.jcc8(CC_Z, start);
} else {
a.jcc(CC_Z, start);
}
// this was a magic call
// nPassed == 2
// Fix up hardware stack pointer
nPassed = 2;
emitLea(a, rStashedAR[-cellsToBytes(nPassed)], rVmSp);
// Optimization TODO: Reuse the prologue for args == 2
emitPrologueWork(func, nPassed);
}
start = magicStart;
}
return skFuncBody;
}
}}}
-92
Ver Arquivo
@@ -1,92 +0,0 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_JIT_FUNC_PROLOGUES_X64_H
#define incl_HPHP_JIT_FUNC_PROLOGUES_X64_H
#include "hphp/util/asm-x64.h"
#include "hphp/runtime/vm/jit/translator-x64.h"
#include "hphp/runtime/vm/jit/types.h"
namespace HPHP {
struct ActRec;
class Func;
namespace JIT { namespace X64 {
//////////////////////////////////////////////////////////////////////
// The funcGuard gets skipped and patched by other code, so we have some
// magic offsets.
constexpr auto kFuncMovImm = 6; // Offset to the immediate for 8 byte Func*
constexpr auto kFuncCmpImm = 4; // Offset to the immediate for 4 byte Func*
constexpr auto kFuncGuardLen = 23;
constexpr auto kFuncGuardShortLen = 14;
template<typename T>
T* funcPrologueToGuardImm(Transl::TCA prologue) {
assert(arch() == Arch::X64);
assert(sizeof(T) == 4 || sizeof(T) == 8);
T* retval = (T*)(prologue - (sizeof(T) == 8 ?
kFuncGuardLen - kFuncMovImm :
kFuncGuardShortLen - kFuncCmpImm));
// We padded these so the immediate would fit inside a cache line
assert(((uintptr_t(retval) ^ (uintptr_t(retval + 1) - 1)) &
~(kX64CacheLineSize - 1)) == 0);
return retval;
}
inline bool funcPrologueHasGuard(Transl::TCA prologue, const Func* func) {
assert(arch() == Arch::X64);
intptr_t iptr = uintptr_t(func);
if (deltaFits(iptr, sz::dword)) {
return *funcPrologueToGuardImm<int32_t>(prologue) == iptr;
}
return *funcPrologueToGuardImm<int64_t>(prologue) == iptr;
}
inline TCA funcPrologueToGuard(TCA prologue, const Func* func) {
assert(arch() == Arch::X64);
if (!prologue || prologue == tx64->uniqueStubs.fcallHelperThunk) {
return prologue;
}
return prologue -
(deltaFits(uintptr_t(func), sz::dword) ?
kFuncGuardShortLen :
kFuncGuardLen);
}
inline void funcPrologueSmashGuard(Transl::TCA prologue, const Func* func) {
intptr_t iptr = uintptr_t(func);
if (deltaFits(iptr, sz::dword)) {
*funcPrologueToGuardImm<int32_t>(prologue) = 0;
return;
}
*funcPrologueToGuardImm<int64_t>(prologue) = 0;
}
//////////////////////////////////////////////////////////////////////
Transl::TCA emitCallArrayPrologue(Func* func, DVFuncletsVec& dvs);
SrcKey emitFuncPrologue(CodeBlock& mainCode, CodeBlock& stubsCode,
Func* func, bool funcIsMagic, int nPassed,
TCA& start, TCA& aStart);
}}}
#endif
@@ -87,7 +87,7 @@ static void setupAfterPrologue(ActRec* fp) {
TCA fcallHelper(ActRec* ar) {
try {
TCA tca =
tx64->getFuncPrologue((Func*)ar->m_func, ar->numArgs(), ar);
Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs(), ar);
if (tca) {
return tca;
}
+6 -33
Ver Arquivo
@@ -1327,7 +1327,7 @@ void HhbcTranslator::emitIterBreak(const ImmVector& iv,
}
if (!breakTracelet) return;
gen(Jmp, makeExit(offset));
gen(Jmp_, makeExit(offset));
}
void HhbcTranslator::emitCreateCont(Id funNameStrId) {
@@ -1885,7 +1885,7 @@ void HhbcTranslator::emitJmp(int32_t offset,
emitJmpSurpriseCheck();
}
if (!breakTracelet) return;
gen(Jmp, makeExit(offset));
gen(Jmp_, makeExit(offset));
}
SSATmp* HhbcTranslator::emitJmpCondHelper(int32_t offset,
@@ -2852,13 +2852,13 @@ void HhbcTranslator::emitSwitch(const ImmVector& iv,
}
if (type.subtypeOf(Type::Null)) {
gen(Jmp, makeExit(zeroOff));
gen(Jmp_, makeExit(zeroOff));
return;
}
if (type.subtypeOf(Type::Bool)) {
Offset nonZeroOff = bcOff() + iv.vec32()[iv.size() - 2];
gen(JmpNZero, makeExit(nonZeroOff), switchVal);
gen(Jmp, makeExit(zeroOff));
gen(Jmp_, makeExit(zeroOff));
return;
}
@@ -2884,7 +2884,7 @@ void HhbcTranslator::emitSwitch(const ImmVector& iv,
index = gen(LdSwitchObjIndex, catchBlock, switchVal, ssabase, ssatargets);
} else if (type.subtypeOf(Type::Arr)) {
gen(DecRef, switchVal);
gen(Jmp, makeExit(defaultOff));
gen(Jmp_, makeExit(defaultOff));
return;
} else {
PUNT(Switch-UnknownType);
@@ -3215,7 +3215,7 @@ void HhbcTranslator::emitVerifyParamType(int32_t paramId) {
if (tc.isTypeVar()) {
return;
}
if (tc.isNullable() && locType.isNull()) {
if (tc.nullable() && locType.isNull()) {
return;
}
if (tc.isCallable()) {
@@ -3734,33 +3734,6 @@ void HhbcTranslator::emitCeil() {
push(gen(Ceil, dblVal));
}
static Type assertOpToType(AssertTOp op) {
switch (op) {
case AssertTOp::Uninit: return Type::Uninit;
case AssertTOp::InitNull: return Type::InitNull;
case AssertTOp::Int: return Type::Int;
case AssertTOp::Dbl: return Type::Dbl;
case AssertTOp::Res: return Type::Res;
case AssertTOp::Null: return Type::Null;
case AssertTOp::Bool: return Type::Bool;
case AssertTOp::Str: return Type::Str;
case AssertTOp::Arr: return Type::Arr;
case AssertTOp::Obj: return Type::Obj;
case AssertTOp::InitUnc: return Type::UncountedInit;
case AssertTOp::Unc: return Type::Uncounted;
case AssertTOp::InitCell: return Type::Cell - Type::Uninit;
}
not_reached();
}
void HhbcTranslator::emitAssertTL(int32_t id, AssertTOp op) {
assertTypeLocal(id, assertOpToType(op));
}
void HhbcTranslator::emitAssertTStk(int32_t offset, AssertTOp op) {
assertTypeStack(offset, assertOpToType(op));
}
void HhbcTranslator::emitAbs() {
auto value = popC();
-3
Ver Arquivo
@@ -343,11 +343,8 @@ struct HhbcTranslator {
void emitRetC(bool freeInline);
void emitRetV(bool freeInline);
// miscelaneous ops
void emitFloor();
void emitCeil();
void emitAssertTL(int32_t id, AssertTOp op);
void emitAssertTStk(int32_t offset, AssertTOp op);
// binary arithmetic ops
void emitAdd();
+8 -7
Ver Arquivo
@@ -141,14 +141,15 @@ bool IRInstruction::mayRaiseError() const {
bool IRInstruction::isEssential() const {
Opcode opc = op();
if (is(IncRef, IncRefCtx, DecRefNZ, DecRefNZOrBranch)) {
// If the ref count optimization is turned off, mark all refcounting
// operations as essential.
if (!RuntimeOption::EvalHHIREnableRefCountOpt) return true;
if (opc == DecRefNZ) {
// If the source of a DecRefNZ is not an IncRef, mark it as essential
// because we won't remove its source as well as itself.
if (is(DecRefNZ) && !src(0)->inst()->is(IncRef)) return true;
// If the ref count optimization is turned off, mark all DecRefNZ as
// essential.
if (!RuntimeOption::EvalHHIREnableRefCountOpt ||
src(0)->inst()->op() != IncRef) {
return true;
}
}
return isControlFlow() ||
opcodeHasFlags(opc, Essential) ||
@@ -334,7 +335,7 @@ void IRInstruction::convertToNop() {
void IRInstruction::convertToJmp() {
assert(isControlFlow());
assert(IMPLIES(block(), block()->back() == this));
m_op = Jmp;
m_op = Jmp_;
m_typeParam = Type::None;
m_numSrcs = 0;
m_numDsts = 0;
+4 -5
Ver Arquivo
@@ -57,11 +57,6 @@ struct BCMarker {
std::string show() const;
bool valid() const;
SrcKey sk() const {
assert(valid());
return SrcKey { func, bcOff };
}
};
/*
@@ -222,6 +217,10 @@ struct IRInstruction {
Type typeParam() const { return m_typeParam; }
void setTypeParam(Type t) { m_typeParam = t; }
uint32_t numSrcs() const { return m_numSrcs; }
void setNumSrcs(uint32_t i) {
assert(i <= m_numSrcs);
m_numSrcs = i;
}
SSATmp* src(uint32_t i) const;
void setSrc(uint32_t i, SSATmp* newSrc);
SrcRange srcs() const {
+3 -12
Ver Arquivo
@@ -244,7 +244,7 @@ IRTranslator::translateLtGtOp(const NormalizedInstruction& i) {
DataType leftType = i.inputs[0]->outerType();
DataType rightType = i.inputs[1]->outerType();
bool ok = equivDataTypes(leftType, rightType) &&
bool ok = HPHP::TypeConstraint::equivDataTypes(leftType, rightType) &&
(i.inputs[0]->isNull() ||
leftType == KindOfBoolean ||
i.inputs[0]->isInt());
@@ -463,14 +463,6 @@ IRTranslator::translateCeil(const NormalizedInstruction& i) {
HHIR_EMIT(Ceil);
}
void IRTranslator::translateAssertTL(const NormalizedInstruction& i) {
HHIR_EMIT(AssertTL, i.imm[0].u_LA, static_cast<AssertTOp>(i.imm[1].u_OA));
}
void IRTranslator::translateAssertTStk(const NormalizedInstruction& i) {
HHIR_EMIT(AssertTStk, i.imm[0].u_IVA, static_cast<AssertTOp>(i.imm[1].u_OA));
}
void
IRTranslator::translateAddNewElemC(const NormalizedInstruction& i) {
assert(i.inputs.size() == 2);
@@ -1660,11 +1652,10 @@ static Type flavorToType(FlavorDesc f) {
switch (f) {
case NOV: not_reached();
case CV: return Type::Cell; // TODO(#3029148) this could be Cell - Uninit
case UV: return Type::Uninit;
case CV: return Type::Cell;
case VV: return Type::BoxedCell;
case AV: return Type::Cls;
case RV: case FV: case CVV: case CVUV: return Type::Gen;
case RV: case FV: case CVV: return Type::Gen;
}
not_reached();
}
+4 -7
Ver Arquivo
@@ -300,7 +300,7 @@ O(JmpIsNType, D(None), SUnk, E) \
/* name dstinfo srcinfo flags */ \
O(JmpZero, D(None), SNum, E) \
O(JmpNZero, D(None), SNum, E) \
O(Jmp, D(None), SUnk, T|E) \
O(Jmp_, D(None), SUnk, T|E) \
O(ReqBindJmpGt, ND, S(Gen) S(Gen), T|E) \
O(ReqBindJmpGte, ND, S(Gen) S(Gen), T|E) \
O(ReqBindJmpLt, ND, S(Gen) S(Gen), T|E) \
@@ -889,6 +889,7 @@ typename std::enable_if<
return std::is_same<T,bool>::value ? Type::Bool : Type::Int;
}
inline Type typeForConst(const StringData*) { return Type::StaticStr; }
inline Type typeForConst(const NamedEntity*) { return Type::NamedEntity; }
inline Type typeForConst(const Func*) { return Type::Func; }
inline Type typeForConst(const Class*) { return Type::Cls; }
@@ -898,14 +899,10 @@ inline Type typeForConst(double) { return Type::Dbl; }
inline Type typeForConst(SetOpOp) { return Type::Int; }
inline Type typeForConst(IncDecOp) { return Type::Int; }
inline Type typeForConst(std::nullptr_t) { return Type::Nullptr; }
inline Type typeForConst(const StringData* sd) {
assert(sd->isStatic());
return Type::StaticStr;
}
inline Type typeForConst(const ArrayData* ad) {
assert(ad->isStatic());
return Type::StaticArr;
// TODO: Task #2124292, Reintroduce StaticArr
return Type::Arr;
}
/*
+1 -1
Ver Arquivo
@@ -208,7 +208,7 @@ void eliminateUnconditionalJump(IRUnit& unit) {
Block* lastBlock = trace->back();
auto lastInst = lastBlock->backIter(); // iterator to last instruction
IRInstruction& jmp = *lastInst;
if (jmp.op() == Jmp && jmp.taken()->numPreds() == 1) {
if (jmp.op() == Jmp_ && jmp.taken()->numPreds() == 1) {
Block* target = jmp.taken();
lastBlock->splice(lastInst, target, target->skipHeader(), target->end(),
lastInst->marker());
+17 -16
Ver Arquivo
@@ -153,10 +153,9 @@ void smashJcc(TCA jccAddr, TCA newDest) {
- X64::kJmpImmBytes);
*deltaAddr = newDelta;
} else {
// This offset is asserted in emitSmashableJump. We wrote three
// instructions. Then the jump destination was written at the next 8-byte
// boundary.
auto dataPtr = jccAddr + 12;
// This offset is asserted in emitSmashableJump. We wrote four instructions.
// Then the jump destination was written at the next 8-byte boundary.
auto dataPtr = jccAddr + 16;
if ((uintptr_t(dataPtr) & 7) != 0) {
dataPtr += 4;
assert((uintptr_t(dataPtr) & 7) == 0);
@@ -190,7 +189,8 @@ void emitSmashableJump(CodeBlock& cb, Transl::TCA dest,
// 26-bit jump offsets (not big enough). It does, however, entail an
// indirect jump.
if (cc == CC_None) {
a. Ldr (ARM::rAsm, &targetData);
a. Adr (ARM::rAsm, &targetData);
a. Ldr (ARM::rAsm, ARM::rAsm[0]);
a. Br (ARM::rAsm);
if (!cb.isFrontierAligned(8)) {
a. Nop ();
@@ -200,11 +200,12 @@ void emitSmashableJump(CodeBlock& cb, Transl::TCA dest,
a. dc64 (reinterpret_cast<int64_t>(dest));
// If this assert breaks, you need to change smashJmp
assert(targetData.target() == start + 8 ||
targetData.target() == start + 12);
assert(targetData.target() == start + 12 ||
targetData.target() == start + 16);
} else {
a. B (&afterData, InvertCondition(ARM::convertCC(cc)));
a. Ldr (ARM::rAsm, &targetData);
a. Adr (ARM::rAsm, &targetData);
a. Ldr (ARM::rAsm, ARM::rAsm[0]);
a. Br (ARM::rAsm);
if (!cb.isFrontierAligned(8)) {
a. Nop ();
@@ -215,8 +216,8 @@ void emitSmashableJump(CodeBlock& cb, Transl::TCA dest,
a. bind (&afterData);
// If this assert breaks, you need to change smashJcc
assert(targetData.target() == start + 12 ||
targetData.target() == start + 16);
assert(targetData.target() == start + 16 ||
targetData.target() == start + 20);
}
}
}
@@ -228,13 +229,13 @@ Transl::TCA jmpTarget(Transl::TCA jmp) {
if (jmp[0] != 0xe9) return nullptr;
return jmp + 5 + ((int32_t*)(jmp + 5))[-1];
} else if (arch() == Arch::ARM) {
// This doesn't verify that each of the two or three instructions that make
// This doesn't verify that each of the three or four instructions that make
// up this sequence matches; just the first one and the indirect jump.
using namespace vixl;
Instruction* ldr = Instruction::Cast(jmp);
if (ldr->Bits(31, 24) != 0x58) return nullptr;
Instruction* adr = Instruction::Cast(jmp);
if (adr->Bit(31) != 0 || adr->Bits(28, 24) != 0x10) return nullptr;
Instruction* br = Instruction::Cast(jmp + 4);
Instruction* br = Instruction::Cast(jmp + 8);
if (br->Bits(31, 10) != 0x3587C0 || br->Bits(5, 0) != 0) return nullptr;
uintptr_t dest = reinterpret_cast<uintptr_t>(jmp + 8);
@@ -257,10 +258,10 @@ Transl::TCA jccTarget(Transl::TCA jmp) {
Instruction* b = Instruction::Cast(jmp);
if (b->Bits(31, 24) != 0x54 || b->Bit(4) != 0) return nullptr;
Instruction* br = Instruction::Cast(jmp + 8);
Instruction* br = Instruction::Cast(jmp + 12);
if (br->Bits(31, 10) != 0x3587C0 || br->Bits(5, 0) != 0) return nullptr;
uintptr_t dest = reinterpret_cast<uintptr_t>(jmp + 8);
uintptr_t dest = reinterpret_cast<uintptr_t>(jmp + 12);
if ((dest & 7) != 0) {
dest += 4;
assert((dest & 7) == 0);
+1
Ver Arquivo
@@ -27,6 +27,7 @@ constexpr int kJmpLen = 5;
constexpr int kCallLen = 5;
constexpr int kJmpccLen = 6;
constexpr int kJmpImmBytes = 4;
constexpr int kJcc8Len = 3;
}
/*
+17 -17
Ver Arquivo
@@ -60,7 +60,7 @@ struct LinearScan : private boost::noncopyable {
static const int NumRegs = kNumRegs;
explicit LinearScan(IRUnit&);
RegAllocInfo allocRegs();
RegAllocInfo allocRegs(LifetimeInfo*);
private:
class RegState {
@@ -204,7 +204,7 @@ private:
// stores pre-coloring hints
PreColoringHint m_preColoringHint;
// a map from SSATmp* to a list of Jmp instructions that have it as
// a map from SSATmp* to a list of Jmp_ instructions that have it as
// a source.
typedef smart::vector<IRInstruction*> JmpList;
StateVector<SSATmp, JmpList> m_jmps;
@@ -757,7 +757,7 @@ void LinearScan::collectInfo(BlockList::iterator it, IRTrace* trace) {
}
IRInstruction* jmp = block->back();
if (jmp->op() == Jmp && jmp->numSrcs() != 0) {
if (jmp->op() == Jmp_ && jmp->numSrcs() != 0) {
for (SSATmp* src : jmp->srcs()) {
m_jmps[src].push_back(jmp);
}
@@ -872,7 +872,7 @@ void LinearScan::computePreColoringHint() {
}
// Given a label, dest index for that label, and register index, scan
// the sources of all incoming Jmps to see if any have a register
// the sources of all incoming Jmp_s to see if any have a register
// allocated at the specified index.
static RegNumber findLabelSrcReg(const RegAllocInfo& regs, IRInstruction* label,
unsigned dstIdx, uint32_t regIndex) {
@@ -886,8 +886,8 @@ static RegNumber findLabelSrcReg(const RegAllocInfo& regs, IRInstruction* label,
// This function attempts to find a pre-coloring hint from two
// different sources: If tmp comes from a DefLabel, it will scan up to
// the SSATmps providing values to incoming Jmps to look for a
// hint. If tmp is consumed by a Jmp, look for other incoming Jmps
// the SSATmps providing values to incoming Jmp_s to look for a
// hint. If tmp is consumed by a Jmp_, look for other incoming Jmp_s
// to its destination and see if any of them have already been given a
// register. If all of these fail, let normal register allocation
// proceed unhinted.
@@ -911,7 +911,7 @@ RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex,
auto reg = findLabelSrcReg(m_allocInfo, srcInst, i, regIndex);
// Until we handle loops, it's a bug to try and allocate a
// register to a DefLabel's dest before all of its incoming
// Jmps have had their srcs allocated, unless the incoming
// Jmp_s have had their srcs allocated, unless the incoming
// block is unreachable.
const DEBUG_ONLY bool unreachable =
std::find(m_blocks.begin(), m_blocks.end(),
@@ -923,19 +923,19 @@ RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex,
not_reached();
}
// If srcInst wasn't a label, check if tmp is used by any Jmp
// instructions. If it is, trace to the Jmp's label and use the
// If srcInst wasn't a label, check if tmp is used by any Jmp_
// instructions. If it is, trace to the Jmp_'s label and use the
// same procedure as above.
for (unsigned ji = 0, jn = jmps.size(); ji < jn; ++ji) {
IRInstruction* jmp = jmps[ji];
IRInstruction* label = jmp->taken()->front();
// Figure out which src of the Jmp is tmp
// Figure out which src of the Jmp_ is tmp
for (unsigned si = 0, sn = jmp->numSrcs(); si < sn; ++si) {
SSATmp* src = jmp->src(si);
if (tmp == src) {
// For now, a DefLabel should never have a register assigned
// to it before any of its incoming Jmp instructions.
// to it before any of its incoming Jmp_ instructions.
always_assert(m_allocInfo[label->dst(si)].reg(regIndex) ==
reg::noreg);
auto reg = findLabelSrcReg(m_allocInfo, label, si, regIndex);
@@ -1075,7 +1075,7 @@ void LinearScan::findFullXMMCandidates() {
m_fullXMMCandidates -= notCandidates;
}
RegAllocInfo LinearScan::allocRegs() {
RegAllocInfo LinearScan::allocRegs(LifetimeInfo* lifetime) {
if (RuntimeOption::EvalHHIREnableCoalescing) {
// <coalesce> doesn't need instruction numbering.
coalesce();
@@ -1104,9 +1104,9 @@ RegAllocInfo LinearScan::allocRegs() {
if (m_slots.size()) genSpillStats(numSpillLocs);
if (dumpIREnabled()) {
dumpTrace(kRegAllocLevel, m_unit, " after reg alloc ", &m_allocInfo,
&m_lifetime, nullptr, nullptr);
if (lifetime) {
lifetime->linear = std::move(m_linear);
lifetime->uses = std::move(m_uses);
}
return m_allocInfo;
}
@@ -1451,8 +1451,8 @@ void LinearScan::PreColoringHint::add(SSATmp* tmp, uint32_t index, int argNum) {
//////////////////////////////////////////////////////////////////////
RegAllocInfo allocRegsForUnit(IRUnit& unit) {
return LinearScan(unit).allocRegs();
RegAllocInfo allocRegsForUnit(IRUnit& unit, LifetimeInfo* lifetime) {
return LinearScan(unit).allocRegs(lifetime);
}
}} // HPHP::JIT
+1 -1
Ver Arquivo
@@ -206,7 +206,7 @@ inline std::ostream& operator<<(std::ostream& os, SpillInfo si) {
* The main entry point for register allocation. Called prior to code
* generation.
*/
RegAllocInfo allocRegsForUnit(IRUnit&);
RegAllocInfo allocRegsForUnit(IRUnit&, LifetimeInfo* = nullptr);
// Native stack layout:
// | |
+11 -16
Ver Arquivo
@@ -683,32 +683,27 @@ HhbcTranslator::MInstrTranslator::simpleCollectionOp() {
}
} else if (baseType.strictSubtypeOf(Type::Obj)) {
const Class* klass = baseType.getClass();
auto const isVector = klass == c_Vector::classof();
auto const isPair = klass == c_Pair::classof();
auto const isMap = klass == c_Map::classof();
auto const isStableMap = klass == c_StableMap::classof();
if (isVector || isPair) {
if (klass == c_Vector::classof() ||
klass == c_Pair::classof()) {
if (mcodeMaybeVectorKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1, DataTypeGeneric);
if (key->isA(Type::Int)) {
// We don't specialize setting pair elements.
if (isPair && op == OpSetM) return SimpleOp::None;
return isVector ? SimpleOp::Vector : SimpleOp::Pair;
return (klass == c_Vector::classof()) ?
SimpleOp::Vector : SimpleOp::Pair;
}
}
} else if (isMap || isStableMap) {
} else if (klass == c_Map::classof() ||
klass == c_StableMap::classof()) {
if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1, DataTypeGeneric);
if (key->isA(Type::Int) || key->isA(Type::Str)) {
return isMap ? SimpleOp::Map : SimpleOp::StableMap;
return (klass == c_Map::classof()) ?
SimpleOp::Map : SimpleOp::StableMap;
}
}
}
}
}
return SimpleOp::None;
}
@@ -1726,7 +1721,7 @@ void HhbcTranslator::MInstrTranslator::emitVectorGet(SSATmp* key) {
PUNT(emitVectorGet);
}
SSATmp* size = gen(LdVectorSize, m_base);
gen(CheckBounds, makeCatch(), key, size);
gen(CheckBounds, key, size);
SSATmp* base = gen(LdVectorBase, m_base);
static_assert(sizeof(TypedValue) == 16,
"TypedValue size expected to be 16 bytes");
@@ -1750,7 +1745,7 @@ void HhbcTranslator::MInstrTranslator::emitPairGet(SSATmp* key) {
auto index = cns(key->getValInt() << 4);
value = gen(LdElem, base, index);
} else {
gen(CheckBounds, makeCatch(), key, cns(1));
gen(CheckBounds, key, cns(1));
SSATmp* base = gen(LdPairBase, m_base);
auto idx = gen(Shl, key, cns(4));
value = gen(LdElem, base, idx);
@@ -2315,7 +2310,7 @@ void HhbcTranslator::MInstrTranslator::emitVectorSet(
PUNT(emitVectorSet); // will throw
}
SSATmp* size = gen(LdVectorSize, m_base);
gen(CheckBounds, makeCatch(), key, size);
gen(CheckBounds, key, size);
SSATmp* increffed = gen(IncRef, value);
SSATmp* vecBase = gen(LdVectorBase, m_base);
+2 -3
Ver Arquivo
@@ -50,8 +50,8 @@ bool cycleHasXMMReg(const CycleInfo& cycle, const int (&moves)[N]) {
}
template <int N>
smart::vector<MoveInfo> doRegMoves(int (&moves)[N], int rTmp) {
smart::vector<MoveInfo> howTo;
void doRegMoves(int (&moves)[N], int rTmp, std::vector<MoveInfo>& howTo) {
assert(howTo.empty());
int outDegree[N];
CycleInfo cycles[N];
int numCycles = 0;
@@ -149,7 +149,6 @@ pathloop:
howTo.push_back(MoveInfo(MoveInfo::Kind::Move, rTmp, w));
}
}
return howTo;
}
}}
+1 -1
Ver Arquivo
@@ -462,7 +462,7 @@ RegionDescPtr selectHotRegion(TransID transId,
std::string dotFileName = string("/tmp/trans-cfg-") +
lexical_cast<std::string>(transId) + ".dot";
cfg.print(dotFileName, funcId, profData, &selectedTIDs);
cfg.print(dotFileName, profData, &selectedTIDs);
FTRACE(5, "selectHotRegion: New Translation {} (file: {}) {}\n",
tx64->profData()->curTransID(), dotFileName,
region ? show(*region) : std::string("empty region"));
+9 -10
Ver Arquivo
@@ -170,11 +170,6 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
if (index == kNumActRecCells) return StackValueInfo { inst, Type::Bool };
if (index == kNumActRecCells + 1) return getStackValue(prevSp, 0);
break;
case Op::FPushCtor:
case Op::FPushCtorD:
if (index == kNumActRecCells) return StackValueInfo { inst, Type::Obj };
if (index == kNumActRecCells + 1) return getStackValue(prevSp, 0);
break;
default:
if (index == 0 && !resultType.equals(Type::None)) {
@@ -370,7 +365,8 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) {
case UnboxPtr: return simplifyUnboxPtr(inst);
case IsType:
case IsNType: return simplifyIsType(inst);
case CheckInit: return simplifyCheckInit(inst);
case CheckInit:
case CheckInitMem: return simplifyCheckInit(inst);
case JmpZero:
case JmpNZero:
@@ -1395,7 +1391,7 @@ SSATmp* Simplifier::simplifyJmpIsType(IRInstruction* inst) {
assert(res->isConst());
if (res->getValBool()) {
// Taken jump
return gen(Jmp, inst->taken());
return gen(Jmp_, inst->taken());
} else {
// Not taken jump; turn jump into a nop
inst->convertToNop();
@@ -1787,10 +1783,13 @@ SSATmp* Simplifier::simplifyUnboxPtr(IRInstruction* inst) {
}
SSATmp* Simplifier::simplifyCheckInit(IRInstruction* inst) {
auto const srcType = inst->src(0)->type();
Type srcType = inst->src(0)->type();
srcType = inst->op() == CheckInitMem ? srcType.deref() : srcType;
assert(srcType.notPtr());
assert(inst->taken());
if (srcType.isInit()) inst->convertToNop();
if (srcType.isInit()) {
inst->convertToNop();
}
return nullptr;
}
@@ -1842,7 +1841,7 @@ SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) {
val = !val;
}
if (val) {
return gen(Jmp, inst->taken());
return gen(Jmp_, inst->taken());
}
inst->convertToNop();
return nullptr;
+24 -9
Ver Arquivo
@@ -26,12 +26,10 @@
#include "hphp/runtime/base/execution-context.h"
#include "hphp/runtime/base/runtime-error.h"
#include "hphp/runtime/vm/jit/translator-inline.h"
#include "hphp/runtime/vm/jit/translator-runtime.h"
namespace HPHP { namespace JIT {
using namespace HPHP::MethodLookup;
using namespace HPHP::Transl;
TRACE_SET_MOD(targetcache);
@@ -254,14 +252,30 @@ void methodCacheSlowPath(MethodCache* mce,
assert(name->isStatic()); // No incRef needed.
}
} catch (...) {
// This is extreme shadiness. See the comments of
// arPreliveOverwriteCells() for more info on how this code gets the
// unwinder to restore the pre-FPushObjMethodD state, including decref
// of the ar->getThis() object.
/*
* Barf.
*
* If the slow lookup fails, we're going to rewind to the state
* before the FPushObjMethodD that dumped us here. In this state,
* the object is still on the stack, but for efficiency reasons,
* we've smashed this TypedValue* with the ActRec we were trying
* to push.
*
* Reconstitute the virtual object before rethrowing.
*/
TypedValue* shouldBeObj = reinterpret_cast<TypedValue*>(ar) +
kNumActRecCells - 1;
ObjectData* arThis = ar->getThis();
auto firstActRecCell = arPreliveOverwriteCells(ar);
firstActRecCell->m_type = KindOfObject;
firstActRecCell->m_data.pobj = arThis;
shouldBeObj->m_type = KindOfObject;
shouldBeObj->m_data.pobj = arThis;
// There used to be a half-built ActRec on the stack that we need the
// unwinder to ignore. We overwrote 1/3 of it with the code above, but
// because of the emitMarker() in LdObjMethod we need the other two slots
// to not have any TypedValues.
tvWriteNull(shouldBeObj - 1);
tvWriteNull(shouldBeObj - 2);
throw;
}
}
@@ -513,3 +527,4 @@ StaticMethodFCache::lookupIR(RDS::Handle handle, const Class* cls,
//////////////////////////////////////////////////////////////////////
}}
+1 -1
Ver Arquivo
@@ -202,7 +202,7 @@ SSATmp* TraceBuilder::preOptimizeCheckLoc(IRInstruction* inst) {
if (!typeParam.isBoxed() || !prevType.isBoxed()) {
if ((typeParam & prevType) == Type::Bottom) {
assert(RuntimeOption::EvalJitPGO);
return gen(Jmp, inst->taken());
return gen(Jmp_, inst->taken());
}
}
}
+2 -2
Ver Arquivo
@@ -218,10 +218,10 @@ struct TraceBuilder {
DisableCseGuard guard(*this);
branch(taken_block);
SSATmp* v1 = next();
gen(Jmp, done_block, v1);
gen(Jmp_, done_block, v1);
appendBlock(taken_block);
SSATmp* v2 = taken();
gen(Jmp, done_block, v2);
gen(Jmp_, done_block, v2);
appendBlock(done_block);
SSATmp* result = label->dst(0);
result->setType(Type::unionOf(v1->type(), v2->type()));

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais