60 Commits

Autor SHA1 Mensagem Data
Paul Tarjan 5848896a92 Allow default .hdf to be compiled in to php binary
Reviewed By: @swtaarrs

Differential Revision: D1013374
2013-10-20 21:02:26 -07:00
Jordan DeLong 2f4aa50bc5 Memoize getNativeFunctionName
Makes doing IR dumps faster.  (Ideally we'd combine this with
the one for Disasm but it's probably not a big difference and this
helped for now.)

Reviewed By: @swtaarrs

Differential Revision: D1013823
2013-10-20 21:02:22 -07:00
Edwin Smith e23001357e Update terminology in ir.specification to refer to blocks as blocks.
The IR is block-based now, and labels are optional.  Updated wording.

Reviewed By: @jdelong

Differential Revision: D1019285
2013-10-20 21:02:18 -07:00
Jordan DeLong 179439a9e4 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 21:02:13 -07:00
bsimmers 6d7e68a5cc tc-print improvements and fixes
tc-print used to assume that all the bytecodes in a translation were
in the same function. This was a fine assumption, but now we have inlining so
that's often not true. TransBCMapping now holds an MD5 to identify the unit it
came from. This is then used while printing bytecodes to look in the correct
unit. I included a few other small tweaks:

- Better alignment for some output, and replace a lot of snprintf with
  folly::format
- systemlib is embedded in the tc-print binary so it can be loaded at startup
  (it's not in production repos)
- The native func and class units are generated at startup
- Function names are printed in translation summaries
- Bytecode offsets are printed in decimal instead of hex, to match conventions
  elsewhere

Reviewed By: @ottoni

Differential Revision: D1016351
2013-10-20 21:02:09 -07:00
Bert Maher 88a2da1408 Describe the global litstr table in bytecode.specification
Also spellchecked, because OCD

Reviewed By: @jdelong

Differential Revision: D1017649
2013-10-18 22:18:48 -07:00
Alex Malyshev 7ae34b0022 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-18 22:18:48 -07:00
Owen Yamauchi 4915bdf7fd Add support for emitting PC-relative loads to vixl assembler
The lower-level assembler infra is there, but this addressing mode
wasn't exposed in the public-facing API. This will allow us to generate
better code for stuff like smashable jumps, which I've included in this
diff. (Previously, it was one instruction to compute an address and
another one to load; now it's just a load.)

Reviewed By: @jdelong

Differential Revision: D1017834
2013-10-18 22:18:47 -07:00
Joel Marcey 9e1f0cd094 Stub out two sqlite PDO methods.
Doctrine unit tests calls one of these two methods (sqliteCreateFunction). We fatal because we do not support it. The PHP docs says these functions are experimental, but let's not fatal if we don't have to.

http://www.php.net/manual/en/pdo.sqlitecreatefunction.php

With this diff (and a Doctrine patch that fixes some interface issues it was having), we no longer fatal in Doctrine! We do, however, fail a lot of tests.

Reviewed By: @ptarjan

Differential Revision: D1017386
2013-10-18 22:18:47 -07:00
Joel Marcey 79db99c827 Add Pear and Magento to our test script
Pear and Magento now run correctly with unit testing. I had to do an additional download for PEAR via the new
pull request mechanism, but it works out well. Neither fatal! :)

Reviewed By: @ptarjan

Differential Revision: D1018560
2013-10-18 22:18:47 -07:00
Joel Marcey 952163b9ae Add the pull request that fixes the interface problem with Doctrine
The interface problem ended up being a problem with Dbal (a dependency). This was fixed via a fork from the main dbal repo, but hasn't been merged into the main repo yet.

The script is updated to support pull requests.

Reviewed By: @ptarjan

Differential Revision: D1017683
2013-10-18 22:18:46 -07:00
James Miller 8e87aaa617 update .gitignore for HHVM
Adds a few more filetypes to the HHVM .gitignore

Reviewed By: @ptarjan

Differential Revision: D1015499

Pulled By: @scannell
2013-10-18 22:18:40 -07:00
aravind 7f6e7e9cbe 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-18 22:12:21 -07:00
Brett Simmers 6a41c10cdf Don't truncate HphpArray's allocation size to 32 bits
For arrays that hold ~150 million or more elements, we need to
allocate more than 4GB of ram. computeAllocBytes used to return a uint32_t so
we were truncating the real size and allocating a lot less memory than we
needed.

Reviewed By: @jdelong

Differential Revision: D1016732
2013-10-18 22:12:12 -07:00
Owen Yamauchi df4a7e74da Move function prologues out of tx64
This is the last big chunk of code emission logic in tx64. The path to
getting stuff working in ARM is now blocked by function prologues, so
this needs to happen now.

Function prologues are delicate and messy, so this change isn't 100%
straight code motion.

- The call-array prologue stuff was quite smooth, and all the changes
  are mechanical.

- funcPrologue() turned out to have a pretty big and easily separable
  chunk of x64-specific code in the middle, sandwiched between big
  chunks of platform-agnostic code. I pulled out the platform-specific
  part into the new module I created.

- All the function-guard smashing stuff is very platform-specific so got
  pulled out as well.

Reviewed By: @ottoni

Differential Revision: D1013868
2013-10-18 22:12:08 -07:00
Sean Cannella 12a756085f Better symbol resolution for pprof
Using names more in line with Func::prettyPrint and using full
names for builtins makes it a lot more clear what is going on.

Reviewed By: @hermanventer

Differential Revision: D1016225
2013-10-18 22:12:04 -07:00
Yuval Hager 4b33353789 Support custom reason for status header
Support custom reason for status header

Closes #967
Closes #1183

Reviewed By: afrind

Differential Revision: D1015633

Pulled By: @scannell
2013-10-18 22:11:59 -07:00
Edwin Smith df1ca4c26c Clean up shuffleArgs.
Use smart container, return the move schedule by value,
and do some manual CSE for clarity.

Reviewed By: @jdelong

Differential Revision: D1016082
2013-10-18 22:11:54 -07:00
Eugene Letuchy 0a6adf3bf4 introduce a helper for overwriting an ActRec as cells
... as discussed.

Reviewed By: @jdelong

Differential Revision: D1013186
2013-10-18 22:11:50 -07:00
Jan Oravec b5972e789b Rename ContinuationWaitHandle to AsyncFunctionWaitHandle
- rename ContinuationWaitHandle to AsyncFunctionWaitHandle
- rename onYield hook to onAwait hook

Reviewed By: @billf

Differential Revision: D1016901
2013-10-18 22:11:41 -07:00
Jan Oravec 37ba715ec4 Removed legacy unused callbacks
Remove unused asio_set_on_{started,failed}_callback.

Reviewed By: alexsuhan

Differential Revision: D1016805
2013-10-18 22:11:37 -07:00
seanc 22bc89ab93 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-18 20:01:30 -07:00
Jordan DeLong 094ba8ed5b Initialize m_type when ignoring isTypeVar TypeConstraints
This diff shouldn't change behavior at all; it just
initializes these values so that if you call fullName() on a
TypeConstraint where isTypeVar is true you get a predicatable result.

Reviewed By: @dariorussi

Differential Revision: D1015647
2013-10-17 20:27:25 -07:00
Jordan DeLong be934dc214 Modify test/quick/verify-param-type.php to fail in repo mode; disable it
This test is testing that we don't assume that a successful
parameter type hint implies anything about the type of the parameter,
but we explicitly do this in repo mode.

Reviewed By: @dariorussi

Differential Revision: D1015116
2013-10-17 20:27:25 -07:00
Jordan DeLong 250a811e2b Fix bytecode spec for BareThis
It can push null.

Reviewed By: @swtaarrs

Differential Revision: D1015095
2013-10-17 20:27:25 -07:00
Jordan DeLong 94a523a232 Tweak the format of TypeConstraint::fullName() for !isExtended()
This is currently only used to print the name of a type
constraint when an extended type hint is failing.  I'm using it for
tracing in another context, so I want to it to print different things
for "?Foo" vs "Foo $x = null" hints.

Reviewed By: @dariorussi

Differential Revision: D1014903
2013-10-17 20:27:24 -07:00
Jordan DeLong 1db1c1c0b9 Generate StaticArr for constant arrays
Adding Type::StaticArr was breaking the simplification logic for
OpSame, since it was checking for precise equality of types.

Reviewed By: @edwinsmith

Differential Revision: D719215
2013-10-17 20:27:24 -07:00
Jordan DeLong e7ab1d6c48 Some cleanup to TypeConstraint, expose the DataType/MetaType fields
I need access to the DataType field, and accessing metaType()
directly is more convenient than going through the isFoo functions
(and if you use switch it also means we can get warnings when people
add new metatypes).  Some cleanup along the way (e.g. pull
equivDataTypes out of there, group related members a little more,
etc).

Reviewed By: @dariorussi

Differential Revision: D1014535
2013-10-17 20:27:24 -07:00
Jordan DeLong 72a86a583a 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 20:27:24 -07:00
Eugene Letuchy bbc51c4a89 make 'abstract async' syntax error only behind !(whole program)
... so that production builds (perflab) work for a bit.
 Reverts commit ab87fc08420d56555ade1c5eec1ff4411edf5804.

Reviewed By: @swtaarrs

Differential Revision: D1013669
2013-10-17 20:27:23 -07:00
Andrey Bannikov 7a6a9aa1eb when performing 'out' command, step over next popr
When the 'out' command is issued and flow is inside a called function that will not return value, debugging should continue from the line following the call. We do that by stepping over a PopR if we encounter one after performing 'out'.

Reviewed By: @hermanventer

Differential Revision: D1016061
2013-10-17 20:27:23 -07:00
bsimmers 345aad19ea Fix catch trace issue with inline collection ops
The CheckBounds instruction used to emit two calls to the throw
helper, and we'd try to reference the same catch trace twice. I considered
supporting that but it's faster to just do a single unsigned comparison
anyway. This diff also fixes a case where we'd incorrectly think we were
specializing SetM with a Pair base.

Reviewed By: @jdelong

Differential Revision: D1013820
2013-10-17 20:27:23 -07:00
Stephen Chen dbd5b4d531 instead of rate, use sum to track hhvm's response code
We added rate for tracking hhvm's response code. But this is not a good stats to
use. Rate is defined as (sum / time period). So that means, if we have 59 500s
in the last 1 minute, the rate will still be 0.

Switching to sum will give us better precision and better metric.

This will make it easier for us to monitor the RC roll out.

Reviewed By: jmarch

Differential Revision: D1015363
2013-10-17 20:27:22 -07:00
Brett Simmers 25c9bf57ef Clean up http header initialization code
This diff fixes a few things I ran into while debugging an unrelated
issue:
- The request counter was global and not synchronized. It's not critical for
  there to be no races on this counter but it's not a perf-sensitive path so I
  can't see any reason to not fix it.
- The HeaderMangle warning is now more informative, including the entire set of
  received headers. It also prints at most one warning per request, though that
  warning may have multiple lines.
- The code to actually put the headers in $_SERVER['HTTP_*'] was looping over
  the vector of values for each header, assigning each one to the same key in
  $_SERVER. Unless I'm missing something really subtle, this is equivalent to
  just assigning the final element of the vector to the key, so I changed it to
  do that.

Reviewed By: @markw65

Differential Revision: D1010458
2013-10-17 20:27:22 -07:00
Edwin Smith ba6bc49e63 Remove dead method IRInstruction::setNumSrcs()
Reviewed By: @ottoni

Differential Revision: D1015523
2013-10-17 20:27:22 -07:00
Edwin Smith 1649218428 Rename Jmp_ to Jmp
Reviewed By: @ottoni

Differential Revision: D1015484
2013-10-17 20:27:22 -07:00
bsimmers ccb3ac68b0 Clean up the runtime option to disable refcount opts
It wasn't disabling everything it should.

Reviewed By: @jdelong

Differential Revision: D1013355
2013-10-17 20:27:21 -07:00
Alex Malyshev 73dfe7a892 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 20:27:21 -07:00
Edwin Smith 2a5577435f Don't print lifetime in codegen
This removes some coupling between linear-scan.cpp and codegen.
The lifetime information is based on the linear numbering that
only matters to linear scan.

Reviewed By: @swtaarrs

Differential Revision: D1013874
2013-10-17 20:27:21 -07:00
Jordan DeLong d2762c0a48 Rename LMANY pop descriptor to MMANY
I think this dates back to a time when member instructions
had arg types called "LA", which eventually got renamed, and now "LA"
means local argument.  Rename this.

Reviewed By: @markw65

Differential Revision: D1011765
2013-10-17 20:27:20 -07:00
Jordan DeLong 1d445d8ed7 Add a U stack flavor, for Uninit nulls on the eval stack
The NullUninit opcode implies that cells on the stack can be
uninit pretty much anywhere, but in practice we only need this for
default arguments to FCallBuiltin.  Make that a bytecode invariant
using stack flavors.

Reviewed By: @markw65

Differential Revision: D1011749
2013-10-17 20:27:20 -07:00
Jordan DeLong c4d323fac8 Add AssertT{L,Stk} opcodes, currently for debugging purposes
They assert in debug builds in the interpreter, and cause the
JIT to assume the types are as specified.  I only added a few types
based on my current needs; we can add more as needed, and I didn't
hook it up to any of our region selection or tracelet analyzer stuff.

Reviewed By: @markw65

Differential Revision: D1011114
2013-10-17 20:27:14 -07:00
Bert Maher 60d0e7dad9 Print function in dot output of CFG trace
It's nice to look at profiled CFG's with TRACE=pgo:5.  This
makes it easier to find which one you want to look at.

Reviewed By: @jdelong

Differential Revision: D1013062
2013-10-17 20:27:13 -07:00
Alan Frindell f68fad38d2 make test_server generic
I had previously copied this file for ProxygenServer.  Instead, make it generic so it can test any server.  The "TestServer" test is now "TestLibEventServer".  The new proxygen test includes this source file (as well as main.cpp), so they could perhaps be a library.  I made two tests virtual because the current proxygen instantiation can't pass them.

Reviewed By: @markw65

Differential Revision: D1004360
2013-10-17 20:27:13 -07:00
Alan Frindell ebe1575c98 refactor socket takeover using compostion
Refactor the takeover logic to not be libevent specific, so it can be reused for ProxygenServer.  Creates a takeover agent which handles the socket takeover procedure and ultimately hands off an fd for the caller to use.

Note: it seems to me that the old LibEventServerWithTakeover::stop() had a concurrency bug, because it accessed the eventBase outside of the dispatcher thread.

Reviewed By: @markw65

Differential Revision: D1004326
2013-10-17 20:27:03 -07:00
Alan Frindell d27ddec787 Refactor server fd inheritance using composition
Collapsing the *WithFd functionality directly into LibEventServer.  ProxygenServer will mirror this capability.  Also changed the LibEventServer ctor to take a ServerOptions parameter.

Reviewed By: @markw65

Differential Revision: D1004307
2013-10-17 18:50:03 -07:00
mwilliams 8ea8e65c21 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 18:49:54 -07:00
Jordan DeLong 964a37d43f Remove simplifier case for CheckInitMem
This shouldn't be here, since we don't know what can change
memory locations.

Reviewed By: @swtaarrs

Differential Revision: D1012639
2013-10-17 18:49:42 -07:00
Owen Yamauchi 3680a85130 Miscellaneous ARM fixes to whittle down failure count
- Fix some of the tracelet-guard tests to actually be testing/comparing
  the right thing. I wasn't paying enough attention to byte vs. word
  loads before.

- Don't call EmitLiteralPool unless there are literals. Even if there
  are no literals, if you call that function, vixl emits a "marker"
  instruction so it knows how big the pool is, but that's not needed.

- Add support for stack-chasing through interp-ones of FPushCtor and
  FPushCtorD. Without this, we assert-fail when anything consumes the
  output type of one of these bytecodes, because we've forgotten that
  the output type is Obj.

With this, 180 tests in test/quick fail. The lion's share of them are
due to unimplemented service requests, but there are a few crashes too.
I'll probably go after the unimplemented service requests next.

Reviewed By: @jdelong

Differential Revision: D1009911
2013-10-17 18:49:37 -07:00
Paul Tarjan 905f7e5386 log on invalid php.ini
If you screw up and put a bad config, it should tell you.

Reviewed By: @markw65

Differential Revision: D1013397
2013-10-17 18:49:11 -07:00
Paul Tarjan e3c32c5f8e change test to allow newer libxml2
In versions after 2.7.8 (the one we use) if you parse a poorly formatted xml document, it leaves the bad namespace as part of the node name. In this test, that means it was looking for a function named `ns1:sum`.

I'm making the xml document better formatted. As far as I can tell (from reading their code), zend has this same behavior. They don't have a test with a poorly formatted input.

Here is my cpp test the behaves differently on 2.7.8 and 2.8.0

  #include <libxml/parserInternals.h>
  #include <string.h>
  int main() {
    const char* buf = "<ns1:sum>1</ns1:sum>";
    xmlParserCtxtPtr ctxt = xmlCreateMemoryParserCtxt(buf, strlen(buf));
    ctxt->sax->error = NULL;
    xmlParseDocument(ctxt);
    printf("%s\n", ctxt->myDoc->children->name);
    xmlFreeParserCtxt(ctxt);
  }

compiled with

  /usr/include/libxml2 a.cpp -lxml2 && ./a.out

Reviewed By: @markw65

Differential Revision: D1013435
2013-10-17 18:49:07 -07:00
Paul Tarjan 283579ef6a better empty file warning
Many people have asked what the message you get when you run `hhvm` is. I was thinking of making a real usage thing. Anyone have an idea for the most common use cases? What I have here is better than nothing.

I'm sure there is some fancy C++-ism so I don't need that stupid extra boolean constructor. Input welcome.

Reviewed By: @markw65

Differential Revision: D1013406
2013-10-17 18:49:02 -07:00
bsimmers 2533a2193a Revert "[hphp] easy: parse error for "abstract async""
This reverts commit 061bcc66ac0710fc639cc8c936b9cf68ce45cdf6.

Reviewed By: @elgenie
2013-10-17 18:48:58 -07:00
Paul Tarjan 45fa5837fc don't hardcode name in test
this test has nothing to do with the stderr, so lets not require that we can write to some random file

Reviewed By: @markw65

Differential Revision: D1013443
2013-10-17 18:48:45 -07:00
James Miller 467c1a8024 Make the embedded systemlib a dependency of the binary 2013-10-17 15:54:38 -07:00
Sara Golemon b04bae25f3 Merge pull request #1184 from PocketRent/fix-install
Fix broken make install behaviour
2013-10-17 15:36:08 -07:00
Sean Cannella 8f574018aa Merge pull request #1185 from kandy/patch-2
Travis CI banner url update due to GitHub project name change
2013-10-17 07:32:20 -07:00
Andrii Kasian cae20c2e28 Travis CI banner path update 2013-10-17 08:40:19 +03:00
James Miller 4f3652034d 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 16:31:38 +13:00
Sara Golemon 75a3806bae Master is now working towards 2.3.0 2013-10-16 11:19:10 -07:00
189 arquivos alterados com 19525 adições e 2506 exclusões
+1 -1
Ver Arquivo
@@ -1,4 +1,4 @@
# 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 for PHP [![Build Status](https://travis-ci.org/facebook/hhvm.png?branch=master)](https://travis-ci.org/facebook/hhvm)
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
@@ -0,0 +1,26 @@
# 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
+28 -17
Ver Arquivo
@@ -118,6 +118,12 @@ 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
@@ -287,12 +293,13 @@ 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_LMANY 0
#define COUNT_C_LMANY 0
#define COUNT_R_LMANY 0
#define COUNT_V_LMANY 0
#define COUNT_MMANY 0
#define COUNT_C_MMANY 0
#define COUNT_R_MMANY 0
#define COUNT_V_MMANY 0
#define COUNT_FMANY 0
#define COUNT_CVMANY 0
#define COUNT_CVUMANY 0
#define COUNT_CMANY 0
#define ONE(t) \
@@ -333,21 +340,22 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
POP_##t2(1); \
POP_##t3(2); \
POP_##t4(3)
#define POP_LMANY \
getEmitterVisitor().popEvalStackLMany()
#define POP_C_LMANY \
#define POP_MMANY \
getEmitterVisitor().popEvalStackMMany()
#define POP_C_MMANY \
getEmitterVisitor().popEvalStack(StackSym::C); \
getEmitterVisitor().popEvalStackLMany()
#define POP_V_LMANY \
getEmitterVisitor().popEvalStackMMany()
#define POP_V_MMANY \
getEmitterVisitor().popEvalStack(StackSym::V); \
getEmitterVisitor().popEvalStackLMany()
#define POP_R_LMANY \
getEmitterVisitor().popEvalStackMMany()
#define POP_R_MMANY \
getEmitterVisitor().popEvalStack(StackSym::R); \
getEmitterVisitor().popEvalStackLMany()
getEmitterVisitor().popEvalStackMMany()
#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)
@@ -409,6 +417,7 @@ 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)
@@ -574,10 +583,10 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#undef POP_TWO
#undef POP_THREE
#undef POP_FOUR
#undef POP_LMANY
#undef POP_C_LMANY
#undef POP_V_LMANY
#undef POP_R_LMANY
#undef POP_MMANY
#undef POP_C_MMANY
#undef POP_V_MMANY
#undef POP_R_MMANY
#undef POP_CV
#undef POP_VV
#undef POP_HV
@@ -587,6 +596,7 @@ 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
@@ -609,6 +619,7 @@ 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
@@ -1277,7 +1288,7 @@ void EmitterVisitor::popSymbolicLocal(Op op, int arg, int pos) {
}
}
void EmitterVisitor::popEvalStackLMany() {
void EmitterVisitor::popEvalStackMMany() {
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 popEvalStackLMany();
void popEvalStackMMany();
void popEvalStackMany(int len, char symFlavor);
void popEvalStackCVMany(int len);
void pushEvalStack(char symFlavor);
+4 -2
Ver Arquivo
@@ -220,7 +220,8 @@ void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
"Access type for interface method %s::%s() must be omitted",
classScope->getOriginalName().c_str(), getOriginalName().c_str());
}
if (m_modifiers->isAsync()) {
// FIXME: WholeProgram check is temporary (t3044335)
if (!Option::WholeProgram && m_modifiers->isAsync()) {
m_modifiers->parseTimeFatal(
Compiler::InvalidAttribute,
Strings::ASYNC_WITHOUT_BODY,
@@ -253,7 +254,8 @@ void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
classScope->getOriginalName().c_str(),
getOriginalName().c_str());
}
if (m_modifiers->isAsync()) {
// FIXME: WholeProgram check is temporary (t3044335)
if (!Option::WholeProgram && m_modifiers->isAsync()) {
m_modifiers->parseTimeFatal(
Compiler::InvalidAttribute,
Strings::ASYNC_WITHOUT_BODY,
+42 -27
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 expressesion and statements,
using simpler constructs to encode more complex expressions 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 comprimising PHP 5.4 compatibility, run-time efficiency,
or simplified without compromising 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 containers's refcount logic. When the container
responsible for honoring the container'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 provdes essential information about the function, such as the
metadata that provides 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 parmeters with
default values are called "required parameters", while formal parameters 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 continguous range of bytecode.
and each fault funclet must be a contiguous 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 constitues the protected region, and an offset of a fault
of bytecode that constitutes 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,8 +270,13 @@ 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. Litstr ids are signed
32-bit integer values. Each litstr id must be between 0 and 2^31 - 2 inclusive.
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 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
@@ -320,7 +325,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 pusing an entry on the FPI
Calls to builtin functions may be optimized to avoid pushing an entry on the FPI
stack if it is known that the builtin function does not need access to the 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.
@@ -359,7 +364,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 definitons of all of that class's ancestors.
by that class's definition and the definitions 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
@@ -387,13 +392,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 overriden by the "public"
from the original declaration is effectively overridden 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 visibile in all contexts, but only accessible
of class E the property P will be visible 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
@@ -454,7 +459,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 definitons of all of that class's
determined by that class's definition and the definitions 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
@@ -513,7 +518,7 @@ not declared in the class definition.
FPI regions
-----------
An FPI region is a continguous range of bytecode that constitutes a call site.
An FPI region is a contiguous 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
@@ -552,6 +557,8 @@ 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
@@ -564,7 +571,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
@@ -760,7 +767,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). Instuctions accepting an immediate vector
input to BaseSC comes first though). Instructions 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
@@ -836,9 +843,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 [] -> [C:Uninit]
NullUninit [] -> [U]
Push Uninit Null Cell onto stack.
Push an uninitialized null on the stack.
Int <signed 64-bit integer value> [] -> [C:Int]
Double <double value> [] -> [C:Dbl]
@@ -1087,7 +1094,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]
@@ -1847,7 +1854,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..C|V] -> [R]
FCallBuiltin <total params> <passed params> <litstr id> [C|V|U..C|V|U] -> [R]
Optimized builtin call without an ActRec. This instruction attempts to
lookup a builtin function named %3. If no function named %3 is defined,
@@ -3525,8 +3532,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.
@@ -3628,7 +3635,7 @@ This [] -> [C:Obj]
instruction throws a fatal error. Next, this instruction pushes the current
instance onto the stack.
BareThis <notice> [] -> [C:Obj]
BareThis <notice> [] -> [C:Obj|Null]
This. This instruction pushes the current instance onto the stack. If %1 is
not zero, and the current instance is null, emits a notice.
@@ -3743,6 +3750,14 @@ 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
---------------------------------------
@@ -3755,9 +3770,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] -> []
+81 -90
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 -> L
D:T = CheckType<T> S0:Gen -> B
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 the label L.
S0 is not type T, branch to block B.
CheckNullptr S0:{CountedStr|NullPtr} -> L
CheckNullptr S0:{CountedStr|NullPtr} -> B
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*.
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*.
D:T = AssertType<T> S0:{Gen|Nullptr}
Assert that the type of S0 is T, copying it to D.
CheckTypeMem<T> S0:PtrToGen -> L
CheckTypeMem<T> S0:PtrToGen -> B
If the value pointed to by S0 is not type T, branch to the label L.
If the value pointed to by S0 is not type T, branch to the block B.
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 -> L
D:FramePtr = CheckLoc<T,localId> S0:FramePtr -> B
Check that type of the given localId on the frame S0 is T; if not,
branch to the label L.
branch to block B.
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 -> L
D:StkPtr = CheckStk<T,offset> S0:StkPtr -> B
Check that the type of the cell on the stack pointed to by S0 at
offset (in cells) is T; if not, branch to the label L.
offset (in cells) is T; if not, branch to block B.
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 -> L
D:StkPtr = CoerceStk<T,offset> S0:StkPtr -> B
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 label L.
can't be done then branches to block B.
CheckInit S0:Gen -> L
CheckInit S0:Gen -> B
If S0's type is Uninit, branch to label L.
If S0's type is Uninit, branch to block B.
CheckInitMem S0:PtrToGen S1:ConstInt -> L
CheckInitMem S0:PtrToGen S1:ConstInt -> B
If the value at S0 + S1 (in bytes) has type Uninit, branch to L.
If the value at S0 + S1 (in bytes) has type Uninit, branch to block B.
CheckDefinedClsEq<className,classPtr> -> L
CheckDefinedClsEq<className,classPtr> -> B
Compare the currently defined class of name `className' with
`classPtr'; if they aren't equal or if `className' is not defined,
branch to L.
branch to block B.
CheckCold<TransID> -> L
CheckCold<TransID> -> B
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 label L.
has reached the "hotness threshold"), then branch to block B.
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 -> L
CheckStaticLocInit S0:BoxedCell -> B
Check if the static local (RDS) RefData represented by S0 is
initialized, and if not branch to L.
initialized, and if not branch to block B.
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 -> L
D:Dbl = OpDivDbl S0:Dbl S1:Dbl -> B
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 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.
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.
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 -> L
CheckSurpriseFlags -> B
Tests the implementation-specific surprise flags. If they're true, branches
to L.
to block B.
SurpriseHook
@@ -680,33 +680,26 @@ 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 -> L
ExitOnVarEnv S0:FramePtr -> B
Loads the VarEnv slot off the ActRec pointed to by S0. If it is
non-zero, jumps to the exit-trace label L.
non-zero, jumps to the exit-trace block B.
Jmp_ [S:T ...] -> L
Jmp -> B
Jmp [S:T ...] -> B: DefLabel
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_.
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.
DefLabel
D:T ... = 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.
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.
6. Reference manipulation
@@ -781,38 +774,37 @@ 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 [ -> L ]
D:T = LdMem<T> S0:PtrToGen S1:ConstInt [ -> B ]
Loads from S0 + S1 (in bytes) 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 transfers control
to L.
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.
D:T = LdProp<T> S0:Obj S1:ConstInt [ -> L ]
D:T = LdProp<T> S0:Obj S1:ConstInt [ -> B ]
Loads a property from the object referenced by S0 at the offset
given by S1 and puts the value in D. If the optional label L is
given by S1 and puts the value in D. If the optional target B is
specified and the loaded value's type does not match T, this
instruction does not load into D and transfers control to L.
instruction does not load into D and branches to block B.
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& [ -> L ]
D:T = LdRef<T> S0:Cell& [ -> B ]
Loads the value held in the box referenced by S0 and puts the value
in D. If the optional label L is specified and the loaded value's
in D. If the optional target B is specified and the loaded value's
type does not match T, this instruction does not load into D and
transfers control to L.
branches to block B.
D:Obj = LdThis S0:FramePtr [ -> L ]
D:Obj = LdThis S0:FramePtr [ -> B ]
Loads the this pointer out of the ActRec pointed to by S0, and puts
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.
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.
D:Ctx = LdCtx<func> S0:FramePtr
@@ -873,20 +865,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 [ -> L ]
D:Cls = LdClsCachedSafe S0:ConstStr [ -> B ]
Loads the class whose name is S0 out of the RDS. If the class is not
defined, returns null and optionally branches to L.
defined, returns null and optionally branches to block B.
D:T = LdClsCns<T,className,constName> [ -> L ]
D:T = LdClsCns<T,className,constName> [ -> B ]
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 label L is specified and the loaded value's type
If the optional block B is specified and the loaded value's type
does not match T, this instruction does not load into D and
transfers control to L.
branches to block B.
The result may be uninitialized if the class is not defined. Note
that no decref of the result is necessary because class constants
@@ -950,9 +942,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 -> L
D:PtrToGen = LdGblAddr S0:Str -> B
Loads a pointer to a global. S0 is the global's name. Branches to L
Loads a pointer to a global. S0 is the global's name. Branches to B
if the global is not defined.
D:PtrToGen = LdGblAddrDef S0:Str
@@ -961,24 +953,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 [ -> L ]
D:PtrToGen = LdClsPropAddr S0:Cls S1:Str S2:ConstCls [ -> B ]
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 L if
static property named S1, then this instruction will either (1) jump to B 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 [ -> L ]
S3:ConstCls [ -> B ]
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 label L is not present, or (2)
jump to L if it is present.
throw a fatal error if the optional block B is not given, or (2)
jump to B if it is present.
LdObjMethod S0:Cls S1:ConstStr S2:StkPtr
@@ -988,10 +980,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 -> L
D:Func = LdObjInvoke S0:Cls -> B
Try to load a cached non-static __invoke Func from the Class in S0,
or branch to L if it is not present.
or branch to block B if it is not present.
D:Cls = LdObjClass S0:Obj
@@ -1008,10 +1000,10 @@ D:Func = LdFuncCached<funcName>
autoload if it not defined yet. Fatal if function autoloader fails
to define it.
D:Func = LdFuncCachedSafe<funcName> [ -> L ]
D:Func = LdFuncCachedSafe<funcName> [ -> B ]
Try to load the Func named funcName from the RDS. If the function
is not defined, returns null and optionally branches to L.
is not defined, returns null and optionally branches to B.
D:Func = LdFuncCachedU<funcName,fallbackName>
@@ -1199,11 +1191,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 -> L
ReleaseVVOrExit S0:FramePtr -> B
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 the exit-trace label L.
ExtraArgs structure. Otherwise jumps to block B.
D:StkPtr = GenericRetDecRefs S0:FramePtr S1:ConstInt
@@ -1652,7 +1644,6 @@ D:T = Reload S0:T
Loads from a spilled temporary S0, and stores the result in D.
16. Continuations & Closures
@@ -1694,16 +1685,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 -> L
ContPreNext S0:Obj -> B
Performs operations needed for the Continuation::next() method. This
includes checking m_done and m_running---if either is not the case
branches to the label L.
branches to block B.
ContStartedCheck S0:Obj -> L
ContStartedCheck S0:Obj -> B
Checks if the continuation object S0 has started, and if not
branches to the label L.
branches to block B.
ContSetRunning S0:Obj S1:ConstBool
+2 -2
Ver Arquivo
@@ -22,7 +22,7 @@ namespace HPHP {
// ArrayInit
HOT_FUNC
ArrayInit::ArrayInit(size_t n)
ArrayInit::ArrayInit(ssize_t n)
#ifdef DEBUG
: m_addCount(0)
, m_expectedCount(n)
@@ -37,7 +37,7 @@ ArrayInit::ArrayInit(size_t n)
}
HOT_FUNC
ArrayInit::ArrayInit(size_t n, MapInit)
ArrayInit::ArrayInit(ssize_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(size_t n);
explicit ArrayInit(ssize_t n);
ArrayInit(size_t n, MapInit);
ArrayInit(ssize_t n, MapInit);
ArrayInit(ArrayInit&& other)
: m_data(other.m_data)
-5
Ver Arquivo
@@ -1198,11 +1198,6 @@ 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);
+5 -2
Ver Arquivo
@@ -176,8 +176,11 @@ inline Variant throw_missing_class(const char *cls) {
throw ClassNotFoundException((std::string("unknown class ") + cls).c_str());
}
inline Variant throw_missing_file(const char *cls) {
throw PhpFileDoesNotExistException(cls);
inline Variant throw_missing_file(const char *file) {
if (file[0] == '\0') {
throw NoFileSpecifiedException();
}
throw PhpFileDoesNotExistException(file);
}
void throw_instance_method_fatal(const char *name);
+16 -1
Ver Arquivo
@@ -25,7 +25,7 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// definitions
enum DataType: int8_t {
enum DataType : int8_t {
KindOfClass = -13,
MinDataType = -13,
@@ -277,6 +277,21 @@ 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,6 +45,10 @@ 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,10 +300,25 @@ 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(int frame = 0);
VarEnv* getVarEnv();
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
uint32_t computeAllocBytes(uint32_t cap, uint32_t mask) {
size_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);
+5 -2
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;
std::string RuntimeOption::IniFile = "/etc/hhvm/php.ini";
int RuntimeOption::HttpDefaultTimeout = 30;
int RuntimeOption::HttpSlowQueryThreshold = 5000; // ms
@@ -849,8 +849,11 @@ void RuntimeOption::Load(Hdf &config, StringVec *overwrites /* = NULL */,
UseDirectCopy = server["UseDirectCopy"].getBool(false);
AlwaysUseRelativePath = server["AlwaysUseRelativePath"].getBool(false);
IniFile = server["IniFile"].getString("/etc/hhvm/php.ini");
IniFile = server["IniFile"].getString(IniFile);
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();
}
+6 -15
Ver Arquivo
@@ -118,22 +118,13 @@ 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));
// Use lambdas wrapping the ctype.h functions because of linker weirdness on
// OS X Mavericks.
#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)
#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); })
#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)
/**
+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
#error Exactly one of IEEE_LITTLE_ENDIAN, IEEE_BIG_ENDIAN, VAX, or IBM \
should be defined.
Exactly one of IEEE_LITTLE_ENDIAN IEEE_BIG_ENDIAN, VAX, or
IBM should be defined.
#endif
typedef union {
+2 -13
Ver Arquivo
@@ -16,7 +16,6 @@
#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 {
///////////////////////////////////////////////////////////////////////////////
@@ -31,7 +30,6 @@ 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) {
@@ -64,22 +62,13 @@ void CmdGlobal::onClient(DebuggerClient &client) {
if (cmd->m_globals.empty()) {
client.info("(no global variable was found)");
} else {
CmdVariable::PrintVariables(client, cmd->m_globals, -1,
text, cmd->m_version);
m_globals = cmd->m_globals;
CmdVariable::PrintVariables(client, cmd->m_globals, true, text);
}
}
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);
}
+1 -3
Ver Arquivo
@@ -25,9 +25,7 @@ namespace HPHP { namespace Eval {
DECLARE_BOOST_TYPES(CmdGlobal);
class CmdGlobal : public DebuggerCommand {
public:
CmdGlobal() : DebuggerCommand(KindOfGlobal) {
m_version = 1;
}
CmdGlobal() : DebuggerCommand(KindOfGlobal) {}
virtual void help(DebuggerClient &client);
+8 -9
Ver Arquivo
@@ -158,7 +158,7 @@ bool CmdList::listFileRange(DebuggerClient &client,
const StaticString
s_methods("methods"),
s_file("file"),
s_line1("line1"),
s_line1("line2"),
s_line2("line2");
// Sends an Info command to the server to retrieve source location
@@ -278,9 +278,6 @@ 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;
@@ -323,9 +320,12 @@ void CmdList::onClient(DebuggerClient &client) {
}
}
if (!listFileRange(client, line, charFocus0, lineFocus1, charFocus1)) {
client.error(
"Unable to read specified function, class or source file location.");
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;
}
}
@@ -350,8 +350,7 @@ bool CmdList::onServer(DebuggerProxy &proxy) {
}
}
RuntimeOption::WarningFrequency = savedWarningFrequency;
if (!m_code.toBoolean() &&
m_file.find("systemlib.php") == m_file.length() - 13) {
if (!m_code.toBoolean() && m_file == "systemlib.php") {
m_code = SystemLib::s_source;
}
return proxy.sendToClient((DebuggerCommand*)this);
+19 -2
Ver Arquivo
@@ -15,6 +15,7 @@
*/
#include "hphp/runtime/debugger/cmd/cmd_out.h"
#include "hphp/runtime/vm/hhbc.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
@@ -48,6 +49,12 @@ 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();
@@ -70,8 +77,18 @@ void CmdOut::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) {
TRACE(2, "CmdOut: shallower stack depth, done.\n");
cleanupStepOuts();
m_complete = (decCount() == 0);
if (!m_complete) {
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 {
TRACE(2, "CmdOut: not complete, step out again.\n");
onSetup(proxy, interrupt);
}
+3 -1
Ver Arquivo
@@ -25,11 +25,13 @@ namespace HPHP { namespace Eval {
DECLARE_BOOST_TYPES(CmdOut);
class CmdOut : public CmdFlowControl {
public:
CmdOut() : CmdFlowControl(KindOfOut) {}
CmdOut() : CmdFlowControl(KindOfOut), m_skippingOverPopR(false) {}
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,8 +43,6 @@ 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());
+13 -125
Ver Arquivo
@@ -31,10 +31,6 @@ 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) {
@@ -43,20 +39,13 @@ void CmdVariable::recvImpl(DebuggerThriftBuffer &thrift) {
{
String sdata;
thrift.read(sdata);
auto error = DebuggerWireHelpers::WireUnserialize(sdata, m_variables);
if (error != DebuggerWireHelpers::NoError) {
if (DebuggerWireHelpers::WireUnserialize(sdata, m_variables) !=
DebuggerWireHelpers::NoError) {
m_variables = null_array;
if (error != DebuggerWireHelpers::HitLimit || m_version == 0) {
// Unexpected error. Log it.
m_wireError = sdata;
}
m_wireError = sdata;
}
}
thrift.read(m_global);
if (m_version == 2) {
thrift.read(m_varName);
thrift.read(m_filter);
}
}
void CmdVariable::help(DebuggerClient &client) {
@@ -83,26 +72,6 @@ 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();
@@ -125,61 +94,21 @@ 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,
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 global, const String& text) {
bool system = true;
int i = 0;
bool found = false;
for (ArrayIter iter(variables); iter; ++iter) {
String name = iter.first().toString();
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) {
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) {
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) {
@@ -188,7 +117,7 @@ void CmdVariable::PrintVariables(DebuggerClient &client, CArrRef variables,
client.output("$%s = %s", name.data(), value.data());
}
// we know s_http_response_header is the last system global
// we knew this is the last system global
if (global && name == s_http_response_header) {
client.output("%s", "");
system = false;
@@ -224,8 +153,8 @@ void CmdVariable::onClient(DebuggerClient &client) {
if (cmd->m_variables.empty()) {
client.info("(no variable was defined)");
} else {
PrintVariables(client, cmd->m_variables, cmd->m_global ? -1 : m_frame,
text, cmd->m_version);
m_variables = cmd->m_variables;
PrintVariables(client, cmd->m_variables, cmd->m_global, text);
}
}
@@ -238,48 +167,7 @@ Array CmdVariable::GetGlobalVariables() {
}
bool CmdVariable::onServer(DebuggerProxy &proxy) {
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();
}
}
m_variables = g_vmContext->getLocalDefinedVariables(m_frame);
return proxy.sendToClient(this);
}
+3 -9
Ver Arquivo
@@ -28,14 +28,10 @@ public:
static Array GetGlobalVariables();
static void PrintVariable(DebuggerClient &client, const String& varName);
static void PrintVariables(DebuggerClient &client, CArrRef variables,
int frame, const String& text, int version);
bool global, const String& text);
public:
CmdVariable() : DebuggerCommand(KindOfVariable) {
m_frame = 0;
m_version = 1;
m_global = false;
}
CmdVariable() : DebuggerCommand(KindOfVariable) {}
virtual void help(DebuggerClient &client);
@@ -49,9 +45,7 @@ protected:
private:
int m_frame;
Array m_variables;
bool m_global; // Set true by onServer if it used g_vmContext->m_globalVarEnv
String m_varName;
String m_filter;
bool m_global;
};
///////////////////////////////////////////////////////////////////////////////
+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_ContinuationWaitHandle& contWh) {
c_AsyncFunctionWaitHandle& 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_ContinuationWaitHandle*>(wh);
auto contWh = dynamic_cast<c_AsyncFunctionWaitHandle*>(wh);
if (contWh != nullptr) addContinuationLocation(frameData, *contWh);
trace.append(frameData);
}
-4
Ver Arquivo
@@ -490,10 +490,6 @@ 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_ContinuationWaitHandle* wait_handle) {
void AsioContext::schedule(c_AsyncFunctionWaitHandle* 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(ContinuationWaitHandle);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
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_ContinuationWaitHandle* getCurrent() { return m_current; }
c_AsyncFunctionWaitHandle* getCurrent() { return m_current; }
void schedule(c_ContinuationWaitHandle* wait_handle);
void schedule(c_AsyncFunctionWaitHandle* 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_ContinuationWaitHandle* m_current;
c_AsyncFunctionWaitHandle* m_current;
// queue of ContinuationWaitHandles ready for immediate execution
smart::queue<c_ContinuationWaitHandle*> m_runnableQueue;
// queue of AsyncFunctionWaitHandles ready for immediate execution
smart::queue<c_AsyncFunctionWaitHandle*> m_runnableQueue;
// queue of RescheduleWaitHandles scheduled in default mode
reschedule_priority_queue_t m_priorityQueueDefault;
+16 -26
Ver Arquivo
@@ -74,57 +74,47 @@ void AsioSession::initAbruptInterruptException() {
"The request was abruptly interrupted.");
}
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());
void AsioSession::onAsyncFunctionCreate(c_AsyncFunctionWaitHandle* cont) {
assert(m_onAsyncFunctionCreateCallback.get());
try {
vm_call_user_func(
m_onContinuationCreateCallback,
m_onAsyncFunctionCreateCallback,
Array::Create(cont));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onCreate callback");
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onCreate callback");
}
}
void AsioSession::onContinuationYield(c_ContinuationWaitHandle* cont, c_WaitHandle* child) {
assert(m_onContinuationYieldCallback.get());
void AsioSession::onAsyncFunctionAwait(c_AsyncFunctionWaitHandle* cont, c_WaitHandle* child) {
assert(m_onAsyncFunctionAwaitCallback.get());
try {
vm_call_user_func(
m_onContinuationYieldCallback,
m_onAsyncFunctionAwaitCallback,
make_packed_array(cont, child));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onYield callback");
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onAwait callback");
}
}
void AsioSession::onContinuationSuccess(c_ContinuationWaitHandle* cont, CVarRef result) {
assert(m_onContinuationSuccessCallback.get());
void AsioSession::onAsyncFunctionSuccess(c_AsyncFunctionWaitHandle* cont, CVarRef result) {
assert(m_onAsyncFunctionSuccessCallback.get());
try {
vm_call_user_func(
m_onContinuationSuccessCallback,
m_onAsyncFunctionSuccessCallback,
make_packed_array(cont, result));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onSuccess callback");
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onSuccess callback");
}
}
void AsioSession::onContinuationFail(c_ContinuationWaitHandle* cont, CObjRef exception) {
assert(m_onContinuationFailCallback.get());
void AsioSession::onAsyncFunctionFail(c_AsyncFunctionWaitHandle* cont, CObjRef exception) {
assert(m_onAsyncFunctionFailCallback.get());
try {
vm_call_user_func(
m_onContinuationFailCallback,
m_onAsyncFunctionFailCallback,
make_packed_array(cont, exception));
} catch (const Object& callback_exception) {
raise_warning("[asio] Ignoring exception thrown by ContinuationWaitHandle::onFail callback");
raise_warning("[asio] Ignoring exception thrown by AsyncFunctionWaitHandle::onFail callback");
}
}
+24 -34
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(ContinuationWaitHandle);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
class AsioSession {
public:
@@ -64,7 +64,7 @@ class AsioSession {
return static_cast<context_idx_t>(m_contexts.size());
}
c_ContinuationWaitHandle* getCurrentWaitHandle() {
c_AsyncFunctionWaitHandle* getCurrentWaitHandle() {
assert(!isInContext() || getCurrentContext()->isRunning());
return isInContext() ? getCurrentContext()->getCurrent() : nullptr;
}
@@ -87,38 +87,31 @@ class AsioSession {
void initAbruptInterruptException();
// 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) {
// AsyncFunctionWaitHandle callbacks:
void setOnAsyncFunctionCreateCallback(ObjectData* on_start) {
assert(!on_start || on_start->instanceof(c_Closure::classof()));
m_onContinuationCreateCallback = on_start;
m_onAsyncFunctionCreateCallback = on_start;
}
void setOnContinuationYieldCallback(ObjectData* on_yield) {
assert(!on_yield || on_yield->instanceof(c_Closure::classof()));
m_onContinuationYieldCallback = on_yield;
void setOnAsyncFunctionAwaitCallback(ObjectData* on_await) {
assert(!on_await || on_await->instanceof(c_Closure::classof()));
m_onAsyncFunctionAwaitCallback = on_await;
}
void setOnContinuationSuccessCallback(ObjectData* on_success) {
void setOnAsyncFunctionSuccessCallback(ObjectData* on_success) {
assert(!on_success || on_success->instanceof(c_Closure::classof()));
m_onContinuationSuccessCallback = on_success;
m_onAsyncFunctionSuccessCallback = on_success;
}
void setOnContinuationFailCallback(ObjectData* on_fail) {
void setOnAsyncFunctionFailCallback(ObjectData* on_fail) {
assert(!on_fail || on_fail->instanceof(c_Closure::classof()));
m_onContinuationFailCallback = on_fail;
m_onAsyncFunctionFailCallback = on_fail;
}
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);
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);
// WaitHandle callbacks:
void setOnJoinCallback(ObjectData* on_join) {
@@ -172,18 +165,15 @@ class AsioSession {
Object m_abruptInterruptException;
Object m_onContinuationCreateCallback;
Object m_onContinuationYieldCallback;
Object m_onContinuationSuccessCallback;
Object m_onContinuationFailCallback;
Object m_onAsyncFunctionCreateCallback;
Object m_onAsyncFunctionAwaitCallback;
Object m_onAsyncFunctionSuccessCallback;
Object m_onAsyncFunctionFailCallback;
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_ContinuationWaitHandle::c_ContinuationWaitHandle(Class* cb)
c_AsyncFunctionWaitHandle::c_AsyncFunctionWaitHandle(Class* cb)
: c_BlockableWaitHandle(cb), m_continuation(), m_child(), m_privData(),
m_depth(0) {
}
c_ContinuationWaitHandle::~c_ContinuationWaitHandle() {
c_AsyncFunctionWaitHandle::~c_AsyncFunctionWaitHandle() {
}
void c_ContinuationWaitHandle::t___construct() {
void c_AsyncFunctionWaitHandle::t___construct() {
Object e(SystemLib::AllocInvalidOperationExceptionObject(
"Use $continuation->getWaitHandle() instead of constructor"));
throw e;
}
void c_ContinuationWaitHandle::ti_setoncreatecallback(CVarRef callback) {
void c_AsyncFunctionWaitHandle::ti_setoncreatecallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set ContinuationWaitHandle::onStart: on_start_cb not a closure"));
"Unable to set AsyncFunctionWaitHandle::onStart: on_start_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnContinuationCreateCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnAsyncFunctionCreateCallback(callback.getObjectDataOrNull());
}
void c_ContinuationWaitHandle::ti_setonyieldcallback(CVarRef callback) {
void c_AsyncFunctionWaitHandle::ti_setonawaitcallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set ContinuationWaitHandle::onYield: on_yield_cb not a closure"));
"Unable to set AsyncFunctionWaitHandle::onAwait: on_await_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnContinuationYieldCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnAsyncFunctionAwaitCallback(callback.getObjectDataOrNull());
}
void c_ContinuationWaitHandle::ti_setonsuccesscallback(CVarRef callback) {
void c_AsyncFunctionWaitHandle::ti_setonsuccesscallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set ContinuationWaitHandle::onSuccess: on_success_cb not a closure"));
"Unable to set AsyncFunctionWaitHandle::onSuccess: on_success_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnContinuationSuccessCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnAsyncFunctionSuccessCallback(callback.getObjectDataOrNull());
}
void c_ContinuationWaitHandle::ti_setonfailcallback(CVarRef callback) {
void c_AsyncFunctionWaitHandle::ti_setonfailcallback(CVarRef callback) {
if (!callback.isNull() && !callback.instanceof(c_Closure::classof())) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Unable to set ContinuationWaitHandle::onFail: on_fail_cb not a closure"));
"Unable to set AsyncFunctionWaitHandle::onFail: on_fail_cb not a closure"));
throw e;
}
AsioSession::Get()->setOnContinuationFailCallback(callback.getObjectDataOrNull());
AsioSession::Get()->setOnAsyncFunctionFailCallback(callback.getObjectDataOrNull());
}
void c_ContinuationWaitHandle::Create(c_Continuation* continuation) {
void c_AsyncFunctionWaitHandle::Create(c_Continuation* continuation) {
assert(continuation);
assert(continuation->m_waitHandle.isNull());
@@ -104,24 +104,24 @@ void c_ContinuationWaitHandle::Create(c_Continuation* continuation) {
throw e;
}
continuation->m_waitHandle = NEWOBJ(c_ContinuationWaitHandle)();
continuation->m_waitHandle = NEWOBJ(c_AsyncFunctionWaitHandle)();
continuation->m_waitHandle->initialize(continuation, depth + 1);
// needs to be called after continuation->m_waitHandle is set
if (UNLIKELY(session->hasOnContinuationCreateCallback())) {
session->onContinuationCreate(continuation->m_waitHandle.get());
if (UNLIKELY(session->hasOnAsyncFunctionCreateCallback())) {
session->onAsyncFunctionCreate(continuation->m_waitHandle.get());
}
}
Object c_ContinuationWaitHandle::t_getprivdata() {
Object c_AsyncFunctionWaitHandle::t_getprivdata() {
return m_privData;
}
void c_ContinuationWaitHandle::t_setprivdata(CObjRef data) {
void c_AsyncFunctionWaitHandle::t_setprivdata(CObjRef data) {
m_privData = data;
}
void c_ContinuationWaitHandle::initialize(c_Continuation* continuation, uint16_t depth) {
void c_AsyncFunctionWaitHandle::initialize(c_Continuation* continuation, uint16_t depth) {
m_continuation = continuation;
m_child = nullptr;
m_privData = nullptr;
@@ -146,7 +146,7 @@ void c_ContinuationWaitHandle::initialize(c_Continuation* continuation, uint16_t
}
}
void c_ContinuationWaitHandle::run() {
void c_AsyncFunctionWaitHandle::run() {
// may happen if scheduled in multiple contexts
if (getState() != STATE_SCHEDULED) {
return;
@@ -186,13 +186,13 @@ void c_ContinuationWaitHandle::run() {
c_WaitHandle* child = c_WaitHandle::fromCell(value);
if (UNLIKELY(!child)) {
Object e(SystemLib::AllocInvalidArgumentExceptionObject(
"Expected yield argument to be an instance of WaitHandle"));
"Expected await argument to be an instance of Awaitable"));
throw e;
}
AsioSession* session = AsioSession::Get();
if (UNLIKELY(session->hasOnContinuationYieldCallback())) {
session->onContinuationYield(this, child);
if (UNLIKELY(session->hasOnAsyncFunctionAwaitCallback())) {
session->onAsyncFunctionAwait(this, child);
}
m_child = child;
@@ -212,17 +212,17 @@ void c_ContinuationWaitHandle::run() {
}
}
void c_ContinuationWaitHandle::onUnblocked() {
void c_AsyncFunctionWaitHandle::onUnblocked() {
setState(STATE_SCHEDULED);
if (isInContext()) {
getContext()->schedule(this);
}
}
void c_ContinuationWaitHandle::markAsSucceeded(const Cell& result) {
void c_AsyncFunctionWaitHandle::markAsSucceeded(const Cell& result) {
AsioSession* session = AsioSession::Get();
if (UNLIKELY(session->hasOnContinuationSuccessCallback())) {
session->onContinuationSuccess(this, cellAsCVarRef(result));
if (UNLIKELY(session->hasOnAsyncFunctionSuccessCallback())) {
session->onAsyncFunctionSuccess(this, cellAsCVarRef(result));
}
setResult(result);
@@ -232,19 +232,19 @@ void c_ContinuationWaitHandle::markAsSucceeded(const Cell& result) {
m_child = nullptr;
}
void c_ContinuationWaitHandle::markAsFailed(CObjRef exception) {
void c_AsyncFunctionWaitHandle::markAsFailed(CObjRef exception) {
AsioSession* session = AsioSession::Get();
session->onFailed(exception);
if (UNLIKELY(session->hasOnContinuationFailCallback())) {
session->onContinuationFail(this, exception);
if (UNLIKELY(session->hasOnAsyncFunctionFailCallback())) {
session->onAsyncFunctionFail(this, exception);
}
setException(exception.get());
m_continuation = nullptr;
m_child = nullptr;
}
String c_ContinuationWaitHandle::getName() {
String c_AsyncFunctionWaitHandle::getName() {
switch (getState()) {
case STATE_SUCCEEDED:
return s_continuationResult;
@@ -269,7 +269,7 @@ String c_ContinuationWaitHandle::getName() {
}
}
c_WaitableWaitHandle* c_ContinuationWaitHandle::getChild() {
c_WaitableWaitHandle* c_AsyncFunctionWaitHandle::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_ContinuationWaitHandle::getChild() {
}
}
void c_ContinuationWaitHandle::enterContext(context_idx_t ctx_idx) {
void c_AsyncFunctionWaitHandle::enterContext(context_idx_t ctx_idx) {
assert(AsioSession::Get()->getContext(ctx_idx));
// stop before corrupting unioned data
@@ -318,7 +318,7 @@ void c_ContinuationWaitHandle::enterContext(context_idx_t ctx_idx) {
}
}
void c_ContinuationWaitHandle::exitContext(context_idx_t ctx_idx) {
void c_AsyncFunctionWaitHandle::exitContext(context_idx_t ctx_idx) {
assert(AsioSession::Get()->getContext(ctx_idx));
// stop before corrupting unioned data
@@ -361,7 +361,7 @@ void c_ContinuationWaitHandle::exitContext(context_idx_t ctx_idx) {
}
// Get the filename in which execution will proceed when execution resumes.
String c_ContinuationWaitHandle::getFileName() {
String c_AsyncFunctionWaitHandle::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_ContinuationWaitHandle::getFileName() {
}
// Get the line number on which execution will proceed when execution resumes.
int c_ContinuationWaitHandle::getLineNumber() {
int c_AsyncFunctionWaitHandle::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} yields null
// {uninit,null} gives 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} yields null
// {uninit,null} gives null
tvWriteNull(current);
continue;
}
-13
Ver Arquivo
@@ -51,18 +51,5 @@ 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);
}
///////////////////////////////////////////////////////////////////////////////
}
+11 -13
Ver Arquivo
@@ -27,8 +27,6 @@ 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
@@ -44,7 +42,7 @@ void f_asio_set_on_started_callback(CVarRef on_started_cb);
* 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
* ContinuationWaitHandle - Continuation-powered asynchronous execution
* AsyncFunctionWaitHandle - async function-based 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
@@ -53,7 +51,7 @@ void f_asio_set_on_started_callback(CVarRef on_started_cb);
*
* 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 yield mechanism of ContinuationWaitHandle or
* asynchronously (such as using await mechanism of async function or
* passed as an array member of GenArrayWaitHandle).
*/
FORWARD_DECLARE_CLASS(WaitHandle);
@@ -224,7 +222,7 @@ class c_WaitableWaitHandle : public c_WaitHandle {
static const int8_t STATE_NEW = 2;
private:
c_ContinuationWaitHandle* m_creator;
c_AsyncFunctionWaitHandle* m_creator;
c_BlockableWaitHandle* m_firstParent;
};
@@ -267,26 +265,26 @@ class c_BlockableWaitHandle : public c_WaitableWaitHandle {
};
///////////////////////////////////////////////////////////////////////////////
// class ContinuationWaitHandle
// class AsyncFunctionWaitHandle
/**
* 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 yielding such
* continuations; a dependency on another wait handle is set up by awaiting such
* wait handle, giving control of the execution back to the asio framework.
*/
FORWARD_DECLARE_CLASS(Continuation);
FORWARD_DECLARE_CLASS(ContinuationWaitHandle);
class c_ContinuationWaitHandle : public c_BlockableWaitHandle {
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
class c_AsyncFunctionWaitHandle : public c_BlockableWaitHandle {
public:
DECLARE_CLASS_NO_SWEEP(ContinuationWaitHandle)
DECLARE_CLASS_NO_SWEEP(AsyncFunctionWaitHandle)
// need to implement
public: c_ContinuationWaitHandle(Class* cls = c_ContinuationWaitHandle::classof());
public: ~c_ContinuationWaitHandle();
public: c_AsyncFunctionWaitHandle(Class* cls = c_AsyncFunctionWaitHandle::classof());
public: ~c_AsyncFunctionWaitHandle();
public: void t___construct();
public: static void ti_setoncreatecallback(CVarRef callback);
public: static void ti_setonyieldcallback(CVarRef callback);
public: static void ti_setonawaitcallback(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_ContinuationWaitHandle::Create(this);
c_AsyncFunctionWaitHandle::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(ContinuationWaitHandle);
FORWARD_DECLARE_CLASS(AsyncFunctionWaitHandle);
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_ContinuationWaitHandle m_waitHandle;
p_AsyncFunctionWaitHandle m_waitHandle;
/* temporary storage used to save the SP when inlining into a continuation */
void* m_stashedSP;
+11 -14
Ver Arquivo
@@ -51,51 +51,48 @@ 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, [] (int i) -> int { return isalnum(i); });
return ctype(text, isalnum);
}
bool f_ctype_alpha(CVarRef text) {
return ctype(text, [] (int i) -> int { return isalpha(i); });
return ctype(text, isalpha);
}
bool f_ctype_cntrl(CVarRef text) {
return ctype(text, [] (int i) -> int { return iscntrl(i); });
return ctype(text, iscntrl);
}
bool f_ctype_digit(CVarRef text) {
return ctype(text, [] (int i) -> int { return isdigit(i); });
return ctype(text, isdigit);
}
bool f_ctype_graph(CVarRef text) {
return ctype(text, [] (int i) -> int { return isgraph(i); });
return ctype(text, isgraph);
}
bool f_ctype_lower(CVarRef text) {
return ctype(text, [] (int i) -> int { return islower(i); });
return ctype(text, islower);
}
bool f_ctype_print(CVarRef text) {
return ctype(text, [] (int i) -> int { return isprint(i); });
return ctype(text, isprint);
}
bool f_ctype_punct(CVarRef text) {
return ctype(text, [] (int i) -> int { return ispunct(i); });
return ctype(text, ispunct);
}
bool f_ctype_space(CVarRef text) {
return ctype(text, [] (int i) -> int { return isspace(i); });
return ctype(text, isspace);
}
bool f_ctype_upper(CVarRef text) {
return ctype(text, [] (int i) -> int { return isupper(i); });
return ctype(text, isupper);
}
bool f_ctype_xdigit(CVarRef text) {
return ctype(text, [] (int i) -> int { return isxdigit(i); });
return ctype(text, isxdigit);
}
///////////////////////////////////////////////////////////////////////////////
+4 -5
Ver Arquivo
@@ -1162,8 +1162,9 @@ static void appendOrphan(XmlNodeSet &orphans, xmlNodePtr node) {
}
}
static void removeOrphanIfNeeded(XmlNodeSet &orphans, xmlNodePtr node) {
static void removeOrphan(XmlNodeSet &orphans, xmlNodePtr node) {
if (node) {
assert(orphans.find(node) != orphans.end());
orphans.erase(node);
}
}
@@ -1953,7 +1954,6 @@ 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()) {
removeOrphanIfNeeded(*newdomnode->doc()->m_orphans, newdomnode->m_node);
removeOrphan(*newdomnode->doc()->m_orphans, newdomnode->m_node);
}
dom_reconcile_ns(nodep->doc, new_child);
return create_node_object(new_child, doc(), false);
@@ -2094,7 +2094,6 @@ 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);
@@ -2208,7 +2207,7 @@ Variant c_DOMNode::t_insertbefore(CObjRef newnode,
return false;
}
if (domchildnode->doc().get()) {
removeOrphanIfNeeded(*domchildnode->doc()->m_orphans, domchildnode->m_node);
removeOrphan(*domchildnode->doc()->m_orphans, domchildnode->m_node);
}
dom_reconcile_ns(parentp->doc, new_child);
return create_node_object(new_child, doc(), false);
+1 -3
Ver Arquivo
@@ -7111,8 +7111,6 @@ 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);
@@ -7177,8 +7175,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;
}
+11 -3
Ver Arquivo
@@ -932,14 +932,21 @@ 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, "explicit_header");
transport->setResponse(code, reason);
}
return;
}
@@ -991,10 +998,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;
@@ -1002,8 +1009,9 @@ Array f_headers_list() {
ret.append(String(iter->first + ": " + values[i]));
}
}
return ret;
}
return ret;
return Array();
}
bool f_headers_sent(VRefParam file /* = null */, VRefParam line /* = null */) {
+14
Ver Arquivo
@@ -1482,6 +1482,20 @@ 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");
+26 -8
Ver Arquivo
@@ -135,7 +135,12 @@ 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: 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___wakeup();
public: Variant t___sleep();
public: static Array ti_getavailabledrivers();
@@ -156,13 +161,25 @@ 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();
@@ -170,7 +187,8 @@ 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().nullable()) {
if (!nonExtendedConstraint || fpi.typeConstraint().isNullable()) {
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->set(s__SESSION, Array::Create(), false);
g->add(s__SESSION, Array::Create(), false);
PS(invalid_session_id) = false;
String value;
+1 -2
Ver Arquivo
@@ -18,14 +18,13 @@
#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.
+1 -2
Ver Arquivo
@@ -18,14 +18,13 @@
#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;
+2 -5
Ver Arquivo
@@ -919,8 +919,8 @@ bool PDOMySqlStatement::executer() {
return false;
}
my_ulonglong affected_count = mysql_affected_rows(m_server);
if (affected_count == (my_ulonglong)-1) {
my_ulonglong row_count = mysql_affected_rows(m_server);
if (row_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,9 +938,6 @@ bool PDOMySqlStatement::executer() {
m_fields = mysql_fetch_fields(m_result);
}
else {
row_count = affected_count;
}
return true;
}
+45 -49
Ver Arquivo
@@ -99,7 +99,6 @@ 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"),
@@ -128,17 +127,6 @@ 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
@@ -147,16 +135,9 @@ static auto const s_arraysToClear = {
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));
@@ -272,38 +253,53 @@ void HttpProtocol::PrepareSystemVariables(Transport *transport,
HeaderMap headers;
transport->getHeaders(headers);
static int bad_request_count = -1;
for (HeaderMap::const_iterator iter = headers.begin();
iter != headers.end(); ++iter) {
const vector<string> &values = iter->second;
static std::atomic<int> badRequests(-1);
// 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());
}
}
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);
}
for (unsigned int i = 0; i < values.size(); i++) {
String key = "HTTP_";
key += f_strtoupper(iter->first).replace("-", "_");
server.set(key, String(values[i]));
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";
}
}
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());
}
}
+8 -27
Ver Arquivo
@@ -208,8 +208,7 @@ void HttpServer::onServerShutdown() {
}
}
void HttpServer::takeoverShutdown(HPHP::Server* server) {
assert(server == m_pageServer.get());
void HttpServer::takeoverShutdown() {
// We want to synchronously shut down our satellite servers to free up ports,
// then asynchronously shut down everything else.
onServerShutdown();
@@ -285,15 +284,14 @@ 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->waitForJobs();
removePid();
m_pageServer->closePort();
m_pageServer->stop();
}
time_t t1 = time(0);
if (!m_danglings.empty() && RuntimeOption::ServerDanglingWait > 0) {
@@ -473,31 +471,14 @@ bool HttpServer::startServer(bool pageServer) {
HttpClient http;
string url = "http://";
if (!RuntimeOption::ServerIP.empty()) {
url += RuntimeOption::ServerIP;
} else {
url += "localhost";
}
url += RuntimeOption::ServerIP;
url += ":";
url += lexical_cast<string>(RuntimeOption::AdminServerPort);
url += "/stop";
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);
}
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(HPHP::Server* server);
void takeoverShutdown();
ServerPtr getPageServer() { return m_pageServer;}
void getSatelliteStats(vector<std::pair<std::string, int>> *stats);
+1 -19
Ver Arquivo
@@ -15,8 +15,6 @@
*/
#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 {
///////////////////////////////////////////////////////////////////////////////
@@ -29,23 +27,7 @@ public:
};
ServerPtr LibEventServerFactory::createServer(const ServerOptions& 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);
return std::make_shared<LibEventServer>(options);
}
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -26,7 +26,7 @@ namespace HPHP {
LibEventServerWithFd::LibEventServerWithFd
(const std::string &address, int port, int thread)
: LibEventServer(address, port, thread)
: LibEventServer(ServerOptions(address, port, thread))
{
}
+145 -30
Ver Arquivo
@@ -78,12 +78,11 @@ LibEventTransportTraits::LibEventTransportTraits(LibEventJobPtr job,
///////////////////////////////////////////////////////////////////////////////
// constructor and destructor
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,
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,
RuntimeOption::ServerThreadDropCacheTimeoutSeconds,
RuntimeOption::ServerThreadDropStack,
this, RuntimeOption::ServerThreadJobLIFOSwitchThreshold,
@@ -99,6 +98,10 @@ LibEventServer::LibEventServer(const std::string &address, int port,
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() {
@@ -116,19 +119,129 @@ LibEventServer::~LibEventServer() {
///////////////////////////////////////////////////////////////////////////////
// implementing HttpServer
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);
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);
if (ret < 0) {
Logger::Error("Fail to bind port %d", m_port);
Logger::Error("evhttp_accept_socket: %s",
folly::errnoStr(errno).c_str());
int errno_save = errno;
close(fd);
errno = errno_save;
return -1;
}
m_accept_sock = ret;
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);
}
@@ -136,16 +249,11 @@ int LibEventServer::getLibEventConnectionCount() {
void LibEventServer::start() {
if (getStatus() == RunStatus::RUNNING) return;
if (m_server != nullptr) {
if (getAcceptSocket() != 0) {
throw FailedToListenException(m_address, m_port);
}
if (getAcceptSocket() != 0) {
throw FailedToListenException(m_address, m_port);
}
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 (m_server_ssl != nullptr) {
if (getAcceptSocketSSL() != 0) {
Logger::Error("Fail to listen on ssl port %d", m_port_ssl);
throw FailedToListenException(m_address, m_port_ssl);
@@ -196,13 +304,17 @@ 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::waitForJobs() {
void LibEventServer::stop() {
Lock lock(m_mutex);
if (getStatus() != RunStatus::RUNNING || m_server == nullptr) return;
@@ -251,18 +363,11 @@ void LibEventServer::waitForJobs() {
// 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
@@ -324,6 +429,14 @@ 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);
@@ -391,7 +504,9 @@ void LibEventServer::onResponse(int worker, evhttp_request *request,
int totalSize = 0;
if (RuntimeOption::LibEventSyncSend && !skip_sync) {
const char *reason = HttpProtocol::GetReasonString(code);
auto const& reasonStr = transport->getResponseInfo();
const char* reason = reasonStr.empty() ? HttpProtocol::GetReasonString(code)
: reasonStr.c_str();
timespec begin, end;
Timer::GetMonotonicTime(begin);
#ifdef EVHTTP_SYNC_SEND_REPORT_TOTAL_LEN
+17 -4
Ver Arquivo
@@ -22,6 +22,7 @@
#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"
@@ -96,19 +97,17 @@ private:
* Implementing an evhttp based HTTP server with JobQueueDispatcher. This
* server will have one dispather thread and multiple worker threads.
*/
class LibEventServer : public Server {
class LibEventServer : public Server, public TakeoverAgent::Callback {
public:
/**
* Constructor and destructor.
*/
LibEventServer(const std::string &address, int port, int thread);
explicit LibEventServer(const ServerOptions &options);
~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();
@@ -121,6 +120,9 @@ public:
}
int getLibEventConnectionCount();
void addTakeoverListener(TakeoverListener* listener);
void removeTakeoverListener(TakeoverListener* listener);
/**
* Request handler called by evhttp library.
*/
@@ -143,6 +145,13 @@ public:
*/
virtual bool enableSSL(int port);
/**
* TakeoverAgent::Callback
*/
int onTakeoverRequest(TakeoverAgent::RequestType type);
void takeoverAborted();
protected:
virtual int getAcceptSocket();
virtual int getAcceptSocketSSL();
@@ -156,6 +165,8 @@ 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;
@@ -168,6 +179,8 @@ 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);
+3 -22
Ver Arquivo
@@ -17,6 +17,7 @@
#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"
@@ -91,16 +92,6 @@ 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;
@@ -167,8 +158,8 @@ public:
*
* This is a no-op for servers that do not support socket takeover.
*/
virtual void addTakeoverListener(TakeoverListener* lisener) {}
virtual void removeTakeoverListener(TakeoverListener* lisener) {}
virtual void addTakeoverListener(TakeoverListener* listener) {}
virtual void removeTakeoverListener(TakeoverListener* listener) {}
/**
* Add additional worker threads
@@ -204,16 +195,6 @@ 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
@@ -14,7 +14,7 @@
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/server/libevent-server-with-takeover.h"
#include "hphp/runtime/server/takeover-agent.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>
/*
LibEventServerWithTakeover extends LibEventServer with the ability
TakeoverAgent provides 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 getAcceptSocket, if binding fails, the server will attempt to
In takeover, the agent 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,22 +74,23 @@ static int fd_transfer_request_handler(
uint8_t* response,
uint32_t* response_length,
void* userdata) {
LibEventServerWithTakeover* server = (LibEventServerWithTakeover*)userdata;
TakeoverAgent* agent = (TakeoverAgent*)userdata;
String req((const char*)request, request_length, CopyString);
String resp;
int fd = server->afdtRequest(req, &resp);
int fd = agent->afdtRequest(req, &resp);
assert(resp.size() <= (int)*response_length);
memcpy(response, resp.data(), resp.size());
*response_length = resp.size();
return fd;
}
LibEventServerWithTakeover::LibEventServerWithTakeover
(const std::string &address, int port, int thread)
: LibEventServer(address, port, thread),
m_delete_handle(nullptr),
TakeoverAgent::TakeoverAgent(const std::string &fname)
: m_delete_handle(nullptr),
m_transfer_fname(fname),
m_took_over(false),
m_takeover_state(TakeoverState::NotStarted)
m_takeover_state(TakeoverState::NotStarted),
m_sock(-1),
m_callback(nullptr)
{
}
@@ -98,25 +99,14 @@ const StaticString
s_ver_C_TERM_REQ(P_VERSION C_TERM_REQ),
s_ver_C_TERM_OK(P_VERSION C_TERM_OK);
int LibEventServerWithTakeover::afdtRequest(String request, String* response) {
int TakeoverAgent::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;
return m_accept_sock;
(void)m_callback->onTakeoverRequest(RequestType::LISTEN_SOCKET);
return m_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
@@ -124,28 +114,9 @@ int LibEventServerWithTakeover::afdtRequest(String request, String* response) {
// within the main libevent thread.
int ret;
*response = P_VERSION C_TERM_BAD;
ret = close(m_accept_sock);
if (ret < 0) {
Logger::Error("Unable to close accept socket");
if (m_callback->onTakeoverRequest(RequestType::TERMINATE) != 0) {
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");
@@ -159,7 +130,7 @@ int LibEventServerWithTakeover::afdtRequest(String request, String* response) {
for (std::set<TakeoverListener*>::iterator it =
m_takeover_listeners.begin();
it != m_takeover_listeners.end(); ++it) {
(*it)->takeoverShutdown(this);
(*it)->takeoverShutdown();
}
Logger::Info("takeover: notification complete");
return -1;
@@ -170,18 +141,21 @@ int LibEventServerWithTakeover::afdtRequest(String request, String* response) {
}
}
void LibEventServerWithTakeover::setupFdServer() {
int TakeoverAgent::setupFdServer(event_base *eventBase, int sock,
Callback *callback) {
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;
return -1;
}
ret = afdt_create_server(
m_transfer_fname.c_str(),
m_eventBase,
eventBase,
fd_transfer_request_handler,
afdt_no_post,
fd_transfer_error_hander,
@@ -193,31 +167,11 @@ void LibEventServerWithTakeover::setupFdServer() {
if (ret >= 0) {
Logger::Info("takeover: fd server set up successfully");
}
return ret;
}
int LibEventServerWithTakeover::getAcceptSocket() {
int TakeoverAgent::takeover(std::chrono::seconds timeoutSec) {
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;
@@ -225,21 +179,21 @@ int LibEventServerWithTakeover::getAcceptSocket() {
uint32_t response_len = sizeof(fd_response);
afdt_error_t err = AFDT_ERROR_T_INIT;
// TODO(dreiss): Make this timeout configurable.
struct timeval timeout = { 2 , 0 };
struct timeval timeout = { timeoutSec.count() , 0 };
ret = afdt_sync_client(
m_transfer_fname.c_str(),
fd_request,
sizeof(fd_request) - 1,
fd_response,
&response_len,
&m_accept_sock,
&m_sock,
&timeout,
&err);
if (ret < 0) {
fd_transfer_error_hander(&err, nullptr);
errno = EADDRINUSE;
return -1;
} else if (m_accept_sock < 0) {
} else if (m_sock < 0) {
String resp((const char*)fd_response, response_len, CopyString);
Logger::Error(
"AFDT did not receive a file descriptor: "
@@ -252,30 +206,10 @@ int LibEventServerWithTakeover::getAcceptSocket() {
Logger::Info("takeover: acquired listen socket");
m_took_over = true;
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;
return m_sock;
}
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();
void TakeoverAgent::requestShutdown() {
if (m_took_over) {
Logger::Info("takeover: requesting shutdown of satellites");
// Use AFDT to synchronously shut down the old server's satellites
@@ -315,19 +249,9 @@ void LibEventServerWithTakeover::start() {
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 LibEventServerWithTakeover::stop() {
void TakeoverAgent::stop() {
if (m_delete_handle != nullptr) {
afdt_close_server(m_delete_handle);
}
@@ -340,13 +264,9 @@ void LibEventServerWithTakeover::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_accept_sock != -1) {
close(m_accept_sock);
m_accept_sock = -1;
if (m_takeover_state != TakeoverState::NotStarted) {
m_callback->takeoverAborted();
}
LibEventServer::stop();
}
TakeoverListener::~TakeoverListener() {
@@ -14,36 +14,73 @@
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_HTTP_SERVER_LIB_EVENT_SERVER_WITH_TAKEOVER_H_
#define incl_HPHP_HTTP_SERVER_LIB_EVENT_SERVER_WITH_TAKEOVER_H_
#ifndef incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
#define incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
#include "hphp/runtime/server/libevent-server.h"
#include "hphp/runtime/base/complex-types.h"
#include <event.h>
#include <chrono>
#include <set>
#include <string>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
/**
* LibEventServer that adds the ability to take over an accept socket
* 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
* from another process, and give its accept socket up.
*/
class LibEventServerWithTakeover : public LibEventServer {
class TakeoverAgent {
public:
LibEventServerWithTakeover(const std::string &address, int port, int thread);
enum class RequestType {
LISTEN_SOCKET,
TERMINATE,
};
virtual void stop();
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;
// 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;
// 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);
}
virtual void addTakeoverListener(TakeoverListener* lisener) {
m_takeover_listeners.insert(lisener);
}
virtual void removeTakeoverListener(TakeoverListener* lisener) {
m_takeover_listeners.erase(lisener);
void removeTakeoverListener(TakeoverListener* listener) {
m_takeover_listeners.erase(listener);
}
// These are public so they can be called from a C-style callback.
@@ -59,10 +96,6 @@ protected:
Complete,
};
virtual void start();
virtual int getAcceptSocket();
void setupFdServer();
void notifyTakeoverComplete();
void* m_delete_handle;
@@ -74,9 +107,15 @@ 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_LIB_EVENT_SERVER_H_
#endif // incl_HPHP_HTTP_SERVER_TAKEOVER_AGENT_H_
+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::RATE});
{ServiceData::StatsType::SUM});
httpResponseStats->addValue(1);
onSendEndImpl();
}
+7 -8
Ver Arquivo
@@ -118,12 +118,6 @@ 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) {
@@ -156,6 +150,7 @@ 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) {
@@ -171,10 +166,14 @@ void VirtualHost::init(Hdf vh) {
m_pathTranslation += '/';
}
}
initRuntimeOption(overwrite);
m_disabled = vh["Disabled"].getBool(false);
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);
}
Hdf rewriteRules = vh["RewriteRules"];
for (Hdf hdf = rewriteRules.firstChild(); hdf.exists(); hdf = hdf.next()) {
RewriteRule dummy;
+24 -10
Ver Arquivo
@@ -782,6 +782,15 @@ 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;
@@ -1002,8 +1011,11 @@ 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( \
uint8_t(read_opcode_arg<int32_t>(as))) // TODO op names
#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_AA as.ue->emitInt32(as.ue->mergeArray(read_litarray(as)))
/*
@@ -1068,12 +1080,13 @@ 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_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_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_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) \
@@ -1147,12 +1160,13 @@ OPCODES
#undef NUM_POP_TWO
#undef NUM_POP_THREE
#undef NUM_POP_POS_N
#undef NUM_POP_LMANY
#undef NUM_POP_V_LMANY
#undef NUM_POP_R_LMANY
#undef NUM_POP_C_LMANY
#undef NUM_POP_MMANY
#undef NUM_POP_V_MMANY
#undef NUM_POP_R_MMANY
#undef NUM_POP_C_MMANY
#undef NUM_POP_FMANY
#undef NUM_POP_CVMANY
#undef NUM_POP_CVUMANY
#undef NUM_POP_CMANY
void initialize_opcode_map() {
+50 -16
Ver Arquivo
@@ -1260,14 +1260,10 @@ Array VMExecutionContext::getCallerInfo() {
return result;
}
VarEnv* VMExecutionContext::getVarEnv(int frame) {
VarEnv* VMExecutionContext::getVarEnv() {
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);
@@ -4628,6 +4624,44 @@ 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);
@@ -4843,7 +4877,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetWithRefRM(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpL(PC& pc) {
NEXT();
DECODE_LA(local);
DECODE(unsigned char, op);
DECODE_OA(op);
Cell* fr = m_stack.topC();
Cell* to = tvToCell(frame_local(m_fp, local));
SETOP_BODY_CELL(to, op, fr);
@@ -4853,7 +4887,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpL(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpN(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
StringData* name;
Cell* fr = m_stack.topC();
TypedValue* tv2 = m_stack.indTV(1);
@@ -4871,7 +4905,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpN(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpG(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
StringData* name;
Cell* fr = m_stack.topC();
TypedValue* tv2 = m_stack.indTV(1);
@@ -4889,7 +4923,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpG(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpS(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
Cell* fr = m_stack.topC();
TypedValue* classref = m_stack.indTV(1);
TypedValue* propn = m_stack.indTV(2);
@@ -4913,7 +4947,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpS(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopSetOpM(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
DECLARE_SETHELPER_ARGS
if (!setHelperPre<MoreWarnings, true, false, false, 1,
VectorLeaveCode::LeaveLast>(MEMBERHELPERPRE_ARGS)) {
@@ -4954,7 +4988,7 @@ OPTBLD_INLINE void VMExecutionContext::iopSetOpM(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecL(PC& pc) {
NEXT();
DECODE_LA(local);
DECODE(unsigned char, op);
DECODE_OA(op);
TypedValue* to = m_stack.allocTV();
tvWriteUninit(to);
TypedValue* fr = frame_local(m_fp, local);
@@ -4963,7 +4997,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecL(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecN(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
StringData* name;
TypedValue* nameCell = m_stack.topTV();
TypedValue* local = nullptr;
@@ -4976,7 +5010,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecN(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecG(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
StringData* name;
TypedValue* nameCell = m_stack.topTV();
TypedValue* gbl = nullptr;
@@ -4990,7 +5024,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecG(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecS(PC& pc) {
StringData* name;
SPROP_OP_PRELUDE
DECODE(unsigned char, op);
DECODE_OA(op);
if (!(visible && accessible)) {
raise_error("Invalid static property access: %s::%s",
clsref->m_data.pcls->name()->data(),
@@ -5004,7 +5038,7 @@ OPTBLD_INLINE void VMExecutionContext::iopIncDecS(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopIncDecM(PC& pc) {
NEXT();
DECODE(unsigned char, op);
DECODE_OA(op);
DECLARE_SETHELPER_ARGS
TypedValue to;
tvWriteUninit(&to);
@@ -6380,7 +6414,7 @@ OPTBLD_INLINE void VMExecutionContext::iopThis(PC& pc) {
OPTBLD_INLINE void VMExecutionContext::iopBareThis(PC& pc) {
NEXT();
DECODE(unsigned char, notice);
DECODE_OA(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 "C" void __jit_debug_register_code();
extern void __jit_debug_register_code();
#endif
-2
Ver Arquivo
@@ -14,8 +14,6 @@
+----------------------------------------------------------------------+
*/
#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,6 +751,20 @@ 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,6 +369,8 @@ 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.
+33 -16
Ver Arquivo
@@ -362,12 +362,13 @@ int instrNumPops(const Op* opcode) {
#define TWO(...) 2
#define THREE(...) 3
#define FOUR(...) 4
#define LMANY -1
#define C_LMANY -2
#define V_LMANY -2
#define R_LMANY -2
#define MMANY -1
#define C_MMANY -2
#define V_MMANY -2
#define R_MMANY -2
#define FMANY -3
#define CVMANY -3
#define CVUMANY -3
#define CMANY -3
#define O(name, imm, pop, push, flags) pop,
OPCODES
@@ -376,12 +377,13 @@ int instrNumPops(const Op* opcode) {
#undef TWO
#undef THREE
#undef FOUR
#undef LMANY
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef MMANY
#undef C_MMANY
#undef V_MMANY
#undef R_MMANY
#undef FMANY
#undef CVMANY
#undef CVUMANY
#undef CMANY
#undef O
};
@@ -500,12 +502,13 @@ 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 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 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 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) {
@@ -517,12 +520,13 @@ FlavorDesc instrInputFlavor(const Op* op, uint32_t idx) {
#undef TWO
#undef THREE
#undef FOUR
#undef LMANY
#undef C_LMANY
#undef V_LMANY
#undef R_LMANY
#undef MMANY
#undef C_MMANY
#undef V_MMANY
#undef R_MMANY
#undef FMANY
#undef CVMANY
#undef CVUMANY
#undef CMANY
#undef O
}
@@ -785,6 +789,16 @@ 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;
@@ -830,6 +844,9 @@ 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; \
+46 -21
Ver Arquivo
@@ -68,13 +68,15 @@ 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)
CVV, // Cell or Var argument
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
};
enum InstrFlags {
@@ -305,6 +307,27 @@ 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,
@@ -349,7 +372,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(CV), NF) \
O(NullUninit, NA, NOV, ONE(UV), NF) \
O(True, NA, NOV, ONE(CV), NF) \
O(False, NA, NOV, ONE(CV), NF) \
O(Int, ONE(I64A), NOV, ONE(CV), NF) \
@@ -423,12 +446,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), LMANY, ONE(CV), NF) \
O(CGetM, ONE(MA), MMANY, 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), LMANY, ONE(VV), NF) \
O(VGetM, ONE(MA), MMANY, 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) \
@@ -436,12 +459,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), LMANY, ONE(CV), NF) \
O(IssetM, ONE(MA), MMANY, 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), LMANY, ONE(CV), NF) \
O(EmptyM, ONE(MA), MMANY, 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) \
@@ -457,32 +480,34 @@ 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_LMANY, ONE(CV), NF) \
O(SetWithRefLM, TWO(MA,LA), LMANY, NOV, NF) \
O(SetWithRefRM, ONE(MA), R_LMANY, NOV, 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(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_LMANY, ONE(CV), NF) \
O(SetOpM, TWO(OA,MA), C_MMANY, 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), LMANY, ONE(CV), NF) \
O(IncDecM, TWO(OA,MA), MMANY, 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_LMANY, ONE(VV), NF) \
O(BindM, ONE(MA), V_MMANY, 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), LMANY, NOV, NF) \
O(UnsetM, ONE(MA), MMANY, 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) \
@@ -507,10 +532,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), LMANY, ONE(FV), FF) \
O(FPassM, TWO(IVA,MA), MMANY, 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),CVMANY, ONE(RV), CF) \
O(FCallBuiltin, THREE(IVA,IVA,SA),CVUMANY, 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);
}
+7 -6
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. Cmp (rAsm.W(), KindOfStringBit);
m_as. Tst (rAsm.W(), KindOfStringBit);
cc = CC_NE;
} else if (type.equals(Type::UncountedInit)) {
m_as. Cmp (rAsm.W(), KindOfUncountedInitBit);
m_as. Tst (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. Ldr (rAsm, rAsm[ArrayData::offsetofKind()]);
m_as. Cmp (rAsm, type.getArrayKind());
m_as. Ldrb (rAsm.W(), rAsm[ArrayData::offsetofKind()]);
m_as. Cmp (rAsm.W(), type.getArrayKind());
doJcc(CC_E);
}
}
@@ -840,7 +840,8 @@ 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().bcOff,
bcMap->push_back(TransBCMapping{inst->marker().func->unit()->md5(),
inst->marker().bcOff,
m_as.frontier(),
m_astubs.frontier()});
prevMarker = inst->marker();
@@ -170,6 +170,15 @@ 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,6 +49,8 @@ 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);
+54 -42
Ver Arquivo
@@ -709,42 +709,41 @@ static int64_t shuffleArgs(Asm& a, ArgGroup& args) {
argDescs[int(dstReg)] = &args[i];
}
}
std::vector<MoveInfo> howTo;
doRegMoves(moves, int(rCgGP), howTo);
auto const howTo = doRegMoves(moves, int(rCgGP));
// Execute the plan
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);
for (auto& how : howTo) {
if (how.m_kind == MoveInfo::Kind::Move) {
if (how.m_reg2 == rCgGP) {
emitMovRegReg(a, how.m_reg1, how.m_reg2);
} else {
ArgDesc* argDesc = argDescs[int(howTo[i].m_reg2)];
ArgDesc* argDesc = argDescs[int(how.m_reg2)];
ArgDesc::Kind kind = argDesc->kind();
if (kind == ArgDesc::Kind::Reg || kind == ArgDesc::Kind::TypeReg) {
if (argDesc->isZeroExtend()) {
assert(howTo[i].m_reg1.isGP());
assert(howTo[i].m_reg2.isGP());
a. movzbl (rbyte(howTo[i].m_reg1), r32(howTo[i].m_reg2));
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. movzbl (rbyte(how.m_reg1), r32(how.m_reg2));
} else {
emitMovRegReg(a, howTo[i].m_reg1, howTo[i].m_reg2);
emitMovRegReg(a, how.m_reg1, how.m_reg2);
}
} else {
assert(kind == ArgDesc::Kind::Addr);
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);
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. lea (how.m_reg1[argDesc->imm().q()], how.m_reg2);
}
if (kind != ArgDesc::Kind::TypeReg) {
argDesc->markDone();
}
}
} else {
assert(howTo[i].m_reg1.isGP());
assert(howTo[i].m_reg2.isGP());
a. xchgq (howTo[i].m_reg1, howTo[i].m_reg2);
assert(how.m_reg1.isGP());
assert(how.m_reg2.isGP());
a. xchgq (how.m_reg1, how.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
@@ -4378,6 +4377,7 @@ 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,18 +4385,17 @@ 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, m_regs[idx].reg());
m_as.cmpq(sizeVal, idxReg);
} else {
auto sizeReg = m_regs[size].reg();
m_as.cmpq(sizeReg, idxReg);
}
unlikelyIfBlock(CC_GE, throwHelper);
unlikelyIfBlock(CC_AE, throwHelper);
}
void CodeGenerator::cgLdVectorSize(IRInstruction* inst) {
@@ -5089,7 +5088,8 @@ void CodeGenerator::cgLdClsCached(IRInstruction* inst) {
CppCall(func),
callDest(inst->dst()),
SyncOptions::kSyncPoint,
ArgGroup(m_regs).addr(rVmTl, intptr_t(ch)).ssas(inst, 0));
ArgGroup(m_regs).addr(rVmTl, intptr_t(ch))
.ssa(inst->src(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) {
m_tx64->emitTransCounterInc(m_as);
emitTransCounterInc(m_as);
}
void CodeGenerator::cgDbgAssertRefCount(IRInstruction* inst) {
@@ -5933,8 +5933,7 @@ void CodeGenerator::cgRBTrace(IRInstruction* inst) {
}
void CodeGenerator::print() const {
JIT::print(std::cout, m_unit,
&m_state.regs, m_state.lifetime, m_state.asmInfo);
JIT::print(std::cout, m_unit, &m_state.regs, nullptr, m_state.asmInfo);
}
static void patchJumps(CodeBlock& cb, CodegenState& state, Block* block) {
@@ -5961,7 +5960,8 @@ 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().bcOff,
bcMap->push_back(TransBCMapping{inst->marker().func->unit()->md5(),
inst->marker().bcOff,
m_as.frontier(),
m_astubs.frontier()});
prevMarker = inst->marker();
@@ -6004,16 +6004,15 @@ LiveRegs computeLiveRegs(const IRUnit& unit, const RegAllocInfo& regs,
return live_regs;
}
void genCode(CodeBlock& mainCode,
CodeBlock& stubsCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
Transl::TranslatorX64* tx64,
const RegAllocInfo& regs,
const LifetimeInfo* lifetime,
AsmInfo* asmInfo) {
void genCodeImpl(CodeBlock& mainCode,
CodeBlock& stubsCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
Transl::TranslatorX64* tx64,
const RegAllocInfo& regs,
AsmInfo* asmInfo) {
LiveRegs live_regs = computeLiveRegs(unit, regs, unit.entry());
CodegenState state(unit, regs, live_regs, lifetime, asmInfo);
CodegenState state(unit, regs, live_regs, asmInfo);
// Returns: whether a block has already been emitted.
DEBUG_ONLY auto isEmitted = [&](Block* block) {
@@ -6037,10 +6036,10 @@ void genCode(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());
@@ -6096,4 +6095,17 @@ void genCode(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);
}
}
}}
+5 -20
Ver Arquivo
@@ -82,13 +82,11 @@ typedef StateVector<IRInstruction, RegSet> LiveRegs;
// and address information produced during codegen.
struct CodegenState {
CodegenState(const IRUnit& unit, const RegAllocInfo& regs,
const LiveRegs& liveRegs, const LifetimeInfo* lifetime,
AsmInfo* asmInfo)
const LiveRegs& liveRegs, AsmInfo* asmInfo)
: patches(unit, nullptr)
, addresses(unit, nullptr)
, regs(regs)
, liveRegs(liveRegs)
, lifetime(lifetime)
, asmInfo(asmInfo)
, catches(unit, CatchInfo())
{}
@@ -98,9 +96,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;
@@ -110,10 +108,6 @@ 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;
@@ -478,7 +472,7 @@ private:
* assert(args.size() == 3);
*/
struct ArgGroup {
typedef std::vector<ArgDesc> ArgVec;
typedef smart::vector<ArgDesc> ArgVec;
explicit ArgGroup(const RegAllocInfo& regs)
: m_regs(regs), m_override(nullptr)
@@ -525,13 +519,6 @@ 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.
*/
@@ -601,9 +588,7 @@ void genCode(CodeBlock& mainCode,
IRUnit& unit,
vector<TransBCMapping>* bcMap,
TranslatorX64* tx64,
const RegAllocInfo& regs,
const LifetimeInfo* lifetime = nullptr,
AsmInfo* asmInfo = nullptr);
const RegAllocInfo& regs);
}}
+14 -12
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 (inst.op() == DecRefNZ) {
if (RuntimeOption::EvalHHIREnableRefCountOpt && 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 && !state[inst].isDead()) {
if (inst->op() == DecRefNZ) {
IRInstruction* srcInst = inst->src(0)->inst();
if (state[srcInst].isDead()) {
state[inst].setDead();
@@ -681,20 +681,22 @@ 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 (inst->consumesReference(i)) {
if (RuntimeOption::EvalHHIREnableRefCountOpt && inst->consumesReference(i)) {
consumeIncRef(inst, src, state);
}
}
}
// Optimize IncRefs and DecRefs.
forEachTrace(unit, [&](IRTrace* t) {
optimizeRefCount(t, unit.main(), state, uses);
});
if (RuntimeOption::EvalHHIREnableRefCountOpt) {
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
@@ -0,0 +1,428 @@
/*
+----------------------------------------------------------------------+
| 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
@@ -0,0 +1,92 @@
/*
+----------------------------------------------------------------------+
| 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 =
Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs(), ar);
tx64->getFuncPrologue((Func*)ar->m_func, ar->numArgs(), ar);
if (tca) {
return tca;
}
+33 -6
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.nullable() && locType.isNull()) {
if (tc.isNullable() && locType.isNull()) {
return;
}
if (tc.isCallable()) {
@@ -3734,6 +3734,33 @@ 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,8 +343,11 @@ 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();
+7 -8
Ver Arquivo
@@ -141,15 +141,14 @@ bool IRInstruction::mayRaiseError() const {
bool IRInstruction::isEssential() const {
Opcode opc = op();
if (opc == DecRefNZ) {
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 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 the ref count optimization is turned off, mark all DecRefNZ as
// essential.
if (!RuntimeOption::EvalHHIREnableRefCountOpt ||
src(0)->inst()->op() != IncRef) {
return true;
}
if (is(DecRefNZ) && !src(0)->inst()->is(IncRef)) return true;
}
return isControlFlow() ||
opcodeHasFlags(opc, Essential) ||
@@ -335,7 +334,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;
+5 -4
Ver Arquivo
@@ -57,6 +57,11 @@ struct BCMarker {
std::string show() const;
bool valid() const;
SrcKey sk() const {
assert(valid());
return SrcKey { func, bcOff };
}
};
/*
@@ -217,10 +222,6 @@ 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 {
+12 -3
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 = HPHP::TypeConstraint::equivDataTypes(leftType, rightType) &&
bool ok = equivDataTypes(leftType, rightType) &&
(i.inputs[0]->isNull() ||
leftType == KindOfBoolean ||
i.inputs[0]->isInt());
@@ -463,6 +463,14 @@ 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);
@@ -1652,10 +1660,11 @@ static Type flavorToType(FlavorDesc f) {
switch (f) {
case NOV: not_reached();
case CV: return Type::Cell;
case CV: return Type::Cell; // TODO(#3029148) this could be Cell - Uninit
case UV: return Type::Uninit;
case VV: return Type::BoxedCell;
case AV: return Type::Cls;
case RV: case FV: case CVV: return Type::Gen;
case RV: case FV: case CVV: case CVUV: return Type::Gen;
}
not_reached();
}
+7 -4
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,7 +889,6 @@ 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; }
@@ -899,10 +898,14 @@ 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());
// TODO: Task #2124292, Reintroduce StaticArr
return Type::Arr;
return Type::StaticArr;
}
/*
+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());
+16 -17
Ver Arquivo
@@ -153,9 +153,10 @@ void smashJcc(TCA jccAddr, TCA newDest) {
- X64::kJmpImmBytes);
*deltaAddr = newDelta;
} else {
// 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;
// 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;
if ((uintptr_t(dataPtr) & 7) != 0) {
dataPtr += 4;
assert((uintptr_t(dataPtr) & 7) == 0);
@@ -189,8 +190,7 @@ 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. Adr (ARM::rAsm, &targetData);
a. Ldr (ARM::rAsm, ARM::rAsm[0]);
a. Ldr (ARM::rAsm, &targetData);
a. Br (ARM::rAsm);
if (!cb.isFrontierAligned(8)) {
a. Nop ();
@@ -200,12 +200,11 @@ 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 + 12 ||
targetData.target() == start + 16);
assert(targetData.target() == start + 8 ||
targetData.target() == start + 12);
} else {
a. B (&afterData, InvertCondition(ARM::convertCC(cc)));
a. Adr (ARM::rAsm, &targetData);
a. Ldr (ARM::rAsm, ARM::rAsm[0]);
a. Ldr (ARM::rAsm, &targetData);
a. Br (ARM::rAsm);
if (!cb.isFrontierAligned(8)) {
a. Nop ();
@@ -216,8 +215,8 @@ void emitSmashableJump(CodeBlock& cb, Transl::TCA dest,
a. bind (&afterData);
// If this assert breaks, you need to change smashJcc
assert(targetData.target() == start + 16 ||
targetData.target() == start + 20);
assert(targetData.target() == start + 12 ||
targetData.target() == start + 16);
}
}
}
@@ -229,13 +228,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 three or four instructions that make
// This doesn't verify that each of the two or three instructions that make
// up this sequence matches; just the first one and the indirect jump.
using namespace vixl;
Instruction* adr = Instruction::Cast(jmp);
if (adr->Bit(31) != 0 || adr->Bits(28, 24) != 0x10) return nullptr;
Instruction* ldr = Instruction::Cast(jmp);
if (ldr->Bits(31, 24) != 0x58) return nullptr;
Instruction* br = Instruction::Cast(jmp + 8);
Instruction* br = Instruction::Cast(jmp + 4);
if (br->Bits(31, 10) != 0x3587C0 || br->Bits(5, 0) != 0) return nullptr;
uintptr_t dest = reinterpret_cast<uintptr_t>(jmp + 8);
@@ -258,10 +257,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 + 12);
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 + 12);
uintptr_t dest = reinterpret_cast<uintptr_t>(jmp + 8);
if ((dest & 7) != 0) {
dest += 4;
assert((dest & 7) == 0);
-1
Ver Arquivo
@@ -27,7 +27,6 @@ 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(LifetimeInfo*);
RegAllocInfo allocRegs();
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 Jmp_s to see if any have a register
// the sources of all incoming Jmps 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 Jmp_s to look for a
// hint. If tmp is consumed by a Jmp_, look for other incoming Jmp_s
// the SSATmps providing values to incoming Jmps to look for a
// hint. If tmp is consumed by a Jmp, look for other incoming Jmps
// 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
// Jmp_s have had their srcs allocated, unless the incoming
// Jmps 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(LifetimeInfo* lifetime) {
RegAllocInfo LinearScan::allocRegs() {
if (RuntimeOption::EvalHHIREnableCoalescing) {
// <coalesce> doesn't need instruction numbering.
coalesce();
@@ -1104,9 +1104,9 @@ RegAllocInfo LinearScan::allocRegs(LifetimeInfo* lifetime) {
if (m_slots.size()) genSpillStats(numSpillLocs);
if (lifetime) {
lifetime->linear = std::move(m_linear);
lifetime->uses = std::move(m_uses);
if (dumpIREnabled()) {
dumpTrace(kRegAllocLevel, m_unit, " after reg alloc ", &m_allocInfo,
&m_lifetime, nullptr, nullptr);
}
return m_allocInfo;
}
@@ -1451,8 +1451,8 @@ void LinearScan::PreColoringHint::add(SSATmp* tmp, uint32_t index, int argNum) {
//////////////////////////////////////////////////////////////////////
RegAllocInfo allocRegsForUnit(IRUnit& unit, LifetimeInfo* lifetime) {
return LinearScan(unit).allocRegs(lifetime);
RegAllocInfo allocRegsForUnit(IRUnit& unit) {
return LinearScan(unit).allocRegs();
}
}} // 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&, LifetimeInfo* = nullptr);
RegAllocInfo allocRegsForUnit(IRUnit&);
// Native stack layout:
// | |
+16 -11
Ver Arquivo
@@ -683,27 +683,32 @@ HhbcTranslator::MInstrTranslator::simpleCollectionOp() {
}
} else if (baseType.strictSubtypeOf(Type::Obj)) {
const Class* klass = baseType.getClass();
if (klass == c_Vector::classof() ||
klass == c_Pair::classof()) {
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 (mcodeMaybeVectorKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1, DataTypeGeneric);
if (key->isA(Type::Int)) {
return (klass == c_Vector::classof()) ?
SimpleOp::Vector : SimpleOp::Pair;
// We don't specialize setting pair elements.
if (isPair && op == OpSetM) return SimpleOp::None;
return isVector ? SimpleOp::Vector : SimpleOp::Pair;
}
}
} else if (klass == c_Map::classof() ||
klass == c_StableMap::classof()) {
} else if (isMap || isStableMap) {
if (mcodeMaybeArrayOrMapKey(m_ni.immVecM[0])) {
SSATmp* key = getInput(m_mii.valCount() + 1, DataTypeGeneric);
if (key->isA(Type::Int) || key->isA(Type::Str)) {
return (klass == c_Map::classof()) ?
SimpleOp::Map : SimpleOp::StableMap;
return isMap ? SimpleOp::Map : SimpleOp::StableMap;
}
}
}
}
}
return SimpleOp::None;
}
@@ -1721,7 +1726,7 @@ void HhbcTranslator::MInstrTranslator::emitVectorGet(SSATmp* key) {
PUNT(emitVectorGet);
}
SSATmp* size = gen(LdVectorSize, m_base);
gen(CheckBounds, key, size);
gen(CheckBounds, makeCatch(), key, size);
SSATmp* base = gen(LdVectorBase, m_base);
static_assert(sizeof(TypedValue) == 16,
"TypedValue size expected to be 16 bytes");
@@ -1745,7 +1750,7 @@ void HhbcTranslator::MInstrTranslator::emitPairGet(SSATmp* key) {
auto index = cns(key->getValInt() << 4);
value = gen(LdElem, base, index);
} else {
gen(CheckBounds, key, cns(1));
gen(CheckBounds, makeCatch(), key, cns(1));
SSATmp* base = gen(LdPairBase, m_base);
auto idx = gen(Shl, key, cns(4));
value = gen(LdElem, base, idx);
@@ -2310,7 +2315,7 @@ void HhbcTranslator::MInstrTranslator::emitVectorSet(
PUNT(emitVectorSet); // will throw
}
SSATmp* size = gen(LdVectorSize, m_base);
gen(CheckBounds, key, size);
gen(CheckBounds, makeCatch(), key, size);
SSATmp* increffed = gen(IncRef, value);
SSATmp* vecBase = gen(LdVectorBase, m_base);
+3 -2
Ver Arquivo
@@ -50,8 +50,8 @@ bool cycleHasXMMReg(const CycleInfo& cycle, const int (&moves)[N]) {
}
template <int N>
void doRegMoves(int (&moves)[N], int rTmp, std::vector<MoveInfo>& howTo) {
assert(howTo.empty());
smart::vector<MoveInfo> doRegMoves(int (&moves)[N], int rTmp) {
smart::vector<MoveInfo> howTo;
int outDegree[N];
CycleInfo cycles[N];
int numCycles = 0;
@@ -149,6 +149,7 @@ 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, profData, &selectedTIDs);
cfg.print(dotFileName, funcId, profData, &selectedTIDs);
FTRACE(5, "selectHotRegion: New Translation {} (file: {}) {}\n",
tx64->profData()->curTransID(), dotFileName,
region ? show(*region) : std::string("empty region"));
+10 -9
Ver Arquivo
@@ -170,6 +170,11 @@ 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)) {
@@ -365,8 +370,7 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) {
case UnboxPtr: return simplifyUnboxPtr(inst);
case IsType:
case IsNType: return simplifyIsType(inst);
case CheckInit:
case CheckInitMem: return simplifyCheckInit(inst);
case CheckInit: return simplifyCheckInit(inst);
case JmpZero:
case JmpNZero:
@@ -1391,7 +1395,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();
@@ -1783,13 +1787,10 @@ SSATmp* Simplifier::simplifyUnboxPtr(IRInstruction* inst) {
}
SSATmp* Simplifier::simplifyCheckInit(IRInstruction* inst) {
Type srcType = inst->src(0)->type();
srcType = inst->op() == CheckInitMem ? srcType.deref() : srcType;
auto const srcType = inst->src(0)->type();
assert(srcType.notPtr());
assert(inst->taken());
if (srcType.isInit()) {
inst->convertToNop();
}
if (srcType.isInit()) inst->convertToNop();
return nullptr;
}
@@ -1841,7 +1842,7 @@ SSATmp* Simplifier::simplifyCondJmp(IRInstruction* inst) {
val = !val;
}
if (val) {
return gen(Jmp_, inst->taken());
return gen(Jmp, inst->taken());
}
inst->convertToNop();
return nullptr;
+9 -24
Ver Arquivo
@@ -26,10 +26,12 @@
#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);
@@ -252,30 +254,14 @@ void methodCacheSlowPath(MethodCache* mce,
assert(name->isStatic()); // No incRef needed.
}
} catch (...) {
/*
* 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;
// 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.
ObjectData* arThis = ar->getThis();
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);
auto firstActRecCell = arPreliveOverwriteCells(ar);
firstActRecCell->m_type = KindOfObject;
firstActRecCell->m_data.pobj = arThis;
throw;
}
}
@@ -527,4 +513,3 @@ 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