Reland D23318594 and D23318592 add recordbasenativesp instr
[hiphop-php.git] / hphp / runtime / vm / member-operations.cpp
blob922594cad9bea890223178a40a9cdf8ecf5064d4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/member-operations.h"
19 #include "hphp/runtime/base/array-iterator.h"
20 #include "hphp/runtime/base/packed-array.h"
21 #include "hphp/runtime/base/packed-array-defs.h"
22 #include "hphp/runtime/base/tv-refcount.h"
23 #include "hphp/runtime/base/type-string.h"
25 #include "hphp/system/systemlib.h"
27 #include <type_traits>
29 namespace HPHP {
31 StringData* prepareAnyKey(TypedValue* tv) {
32 if (isStringType(tv->m_type)) {
33 StringData* str = tv->m_data.pstr;
34 str->incRefCount();
35 return str;
36 } else {
37 return tvAsCVarRef(tv).toString().detach();
41 void unknownBaseType(DataType type) {
42 always_assert_flog(
43 false,
44 "Unknown KindOf: {} in member operation base",
45 static_cast<uint8_t>(type)
49 // Mutable collections support appending new elements using [] without a key
50 // like so: "$vector[] = 123;". However, collections do not support using []
51 // without a key to implicitly create a new element without supplying assigning
52 // an initial value (ex "$vector[]['a'] = 73;").
53 void throw_cannot_use_newelem_for_lval_read_col() {
54 SystemLib::throwInvalidOperationExceptionObject(
55 "Cannot use [] with collections for reading in an lvalue context");
58 void throw_cannot_use_newelem_for_lval_read_vec() {
59 SystemLib::throwInvalidOperationExceptionObject(
60 "Cannot use [] with vecs for reading in an lvalue context"
64 void throw_cannot_use_newelem_for_lval_read_dict() {
65 SystemLib::throwInvalidOperationExceptionObject(
66 "Cannot use [] with dicts for reading in an lvalue context"
70 void throw_cannot_use_newelem_for_lval_read_keyset() {
71 SystemLib::throwInvalidOperationExceptionObject(
72 "Cannot use [] with keysets for reading in an lvalue context"
76 void throw_cannot_use_newelem_for_lval_read_clsmeth() {
77 SystemLib::throwInvalidOperationExceptionObject(
78 "Cannot use [] with clsmeth for reading in an lvalue context"
82 void throw_cannot_use_newelem_for_lval_read_record() {
83 SystemLib::throwInvalidOperationExceptionObject(
84 "Cannot use [] with record for reading in an lvalue context"
88 TypedValue incDecBodySlow(IncDecOp op, tv_lval fr) {
89 assertx(tvIsPlausible(*fr));
90 assertx(type(fr) != KindOfUninit);
92 auto dup = [&]() { tvIncRefGen(*fr); return *fr; };
94 switch (op) {
95 case IncDecOp::PreInc:
96 tvInc(fr);
97 return dup();
98 case IncDecOp::PostInc: {
99 auto const tmp = dup();
100 tvInc(fr);
101 return tmp;
103 case IncDecOp::PreDec:
104 tvDec(fr);
105 return dup();
106 case IncDecOp::PostDec: {
107 auto const tmp = dup();
108 tvDec(fr);
109 return tmp;
111 case IncDecOp::PreIncO:
112 tvIncO(fr);
113 return dup();
114 case IncDecOp::PostIncO: {
115 auto const tmp = dup();
116 tvIncO(fr);
117 return tmp;
119 case IncDecOp::PreDecO:
120 tvDecO(fr);
121 return dup();
122 case IncDecOp::PostDecO: {
123 auto const tmp = dup();
124 tvDecO(fr);
125 return tmp;
128 not_reached();
131 namespace {
132 ALWAYS_INLINE
133 void copy_int(void* dest, int64_t src, size_t size) {
134 if (size == 1) {
135 uint8_t i = src;
136 memcpy(dest, &i, 1);
137 } else if (size == 2) {
138 uint16_t i = src;
139 memcpy(dest, &i, 2);
140 } else if (size == 4) {
141 uint32_t i = src;
142 memcpy(dest, &i, 4);
143 } else {
144 assertx(size == 8);
145 uint64_t i = src;
146 memcpy(dest, &i, 8);
150 template<typename... Args>
151 [[noreturn]] NEVER_INLINE
152 void fail_oob(folly::StringPiece fmt, Args&&... args) {
153 SystemLib::throwOutOfBoundsExceptionObject(
154 folly::sformat(fmt, std::forward<Args>(args)...)
158 template<typename... Args>
159 [[noreturn]] NEVER_INLINE
160 void fail_invalid(folly::StringPiece fmt, Args&&... args) {
161 SystemLib::throwInvalidOperationExceptionObject(
162 folly::sformat(fmt, std::forward<Args>(args)...)
166 template<bool reverse, typename F>
167 void setRangeString(
168 char* dest, TypedValue src, int64_t count, int64_t size, F range_check
170 auto const src_str = val(src).pstr;
171 if (count == -1) {
172 count = src_str->size();
173 } else if (count < 0 || count > src_str->size()) {
174 fail_oob("Cannot read {}-byte range from {}-byte source string",
175 count, src_str->size());
177 if (size != 1) {
178 fail_invalid("Invalid size {} for string source", size);
181 range_check(count);
182 auto const data = src_str->data();
183 if (reverse) {
184 auto end = data + count;
185 for (int64_t i = 0; i < count % 8; ++i) {
186 *dest++ = *--end;
188 for (int64_t i = 0; i < count / 8; ++i) {
189 end -= 8;
190 uint64_t tmp;
191 memcpy(&tmp, end, 8);
192 tmp = __builtin_bswap64(tmp);
193 memcpy(dest, &tmp, 8);
194 dest += 8;
196 } else {
197 memcpy(dest, data, count);
202 * The main vec loop is a template to avoid calling memcpy() with a
203 * non-constant size, and to avoid the check for KindOfDouble in the more
204 * common int case.
206 template<bool reverse, int64_t size, DataType elem_type>
207 void setRangeVecLoop(char* dest, const ArrayData* vec, int64_t count) {
208 static_assert(
209 (isBoolType(elem_type) && size == 1) ||
210 (isIntType(elem_type) &&
211 (size == 1 || size == 2 || size == 4 || size == 8)) ||
212 (isDoubleType(elem_type) && (size == 4 || size == 8)),
213 "Unsupported elem_type and/or size"
216 auto i = 0;
217 if (reverse) dest += (count - 1) * size;
218 IterateV(vec, [&](TypedValue elem) {
219 if (UNLIKELY(type(elem) != elem_type)) {
220 fail_invalid(
221 "Multiple types in vec source: {} at index 0; {} at index {}",
222 getDataTypeString(elem_type).data(),
223 getDataTypeString(type(elem)).data(),
228 if (isDoubleType(elem_type)) {
229 std::conditional_t<size == 4, float, double> f = val(elem).dbl;
230 memcpy(dest, &f, size);
231 } else {
232 copy_int(dest, val(elem).num, size);
234 dest += reverse ? -size : size;
235 return (++i) == count;
239 template<bool reverse, typename F>
240 void setRangeVec(
241 char* dest, const ArrayData* vec, int64_t count, int64_t size, F range_check
243 auto const vec_size = vec->size();
244 if (count == -1) {
245 count = vec_size;
246 } else if (count < 0 || count > vec_size) {
247 fail_oob("Cannot read {} elements from vec of size {}", count, vec_size);
250 range_check(count * size);
251 auto const elem_type = type(vec->at(int64_t{0}));
252 assertx(elem_type != KindOfUninit);
253 auto bad_type = [&]() {
254 fail_invalid(
255 "Bad type ({}) and element size ({}) combination in vec source",
256 getDataTypeString(elem_type).data(), size
260 switch (size) {
261 case 1:
262 if (isIntType(elem_type)) {
263 setRangeVecLoop<reverse, 1, KindOfInt64>(dest, vec, count);
264 } else if (isBoolType(elem_type)) {
265 setRangeVecLoop<reverse, 1, KindOfBoolean>(dest, vec, count);
266 } else {
267 bad_type();
269 break;
271 case 2:
272 if (isIntType(elem_type)) {
273 setRangeVecLoop<reverse, 2, KindOfInt64>(dest, vec, count);
274 } else {
275 bad_type();
277 break;
279 case 4:
280 if (isIntType(elem_type)) {
281 setRangeVecLoop<reverse, 4, KindOfInt64>(dest, vec, count);
282 } else if (isDoubleType(elem_type)) {
283 setRangeVecLoop<reverse, 4, KindOfDouble>(dest, vec, count);
284 } else {
285 bad_type();
287 break;
289 case 8:
290 if (isIntType(elem_type)) {
291 setRangeVecLoop<reverse, 8, KindOfInt64>(dest, vec, count);
292 } else if (isDoubleType(elem_type)) {
293 setRangeVecLoop<reverse, 8, KindOfDouble>(dest, vec, count);
294 } else {
295 bad_type();
297 break;
299 default:
300 not_reached();
305 template<bool reverse>
306 void SetRange(
307 tv_lval base, int64_t offset, TypedValue src, int64_t count, int64_t size
309 if (!tvIsString(base)) {
310 fail_invalid("Invalid base type {} for range set operation",
311 getDataTypeString(type(base)).data());
313 if (count == 0) return;
314 type(base) = KindOfString;
316 auto& base_str = val(base).pstr;
317 if (base_str->cowCheck()) {
318 auto const old_str = base_str;
319 base_str = StringData::Make(old_str, CopyStringMode{});
320 FOLLY_SDT(hhvm, hhvm_cow_setrange, base_str->size(), size*count);
321 decRefStr(old_str);
322 } else {
323 FOLLY_SDT(hhvm, hhvm_mut_setrange, base_str->size(), size*count);
326 auto range_check = [&](size_t data_len) {
327 if (offset < 0 || offset + data_len > base_str->size()) {
328 fail_oob(
329 "Cannot set {}-byte range at offset {} in string of length {}",
330 data_len,
331 offset,
332 base_str->size()
337 auto dest = base_str->mutableData() + offset;
339 auto fail_if_reverse = [&]() {
340 if (reverse) {
341 fail_invalid("Cannot set reverse range for primitive type {}",
342 getDataTypeString(type(src)).data());
346 if (tvIsBool(src)) {
347 fail_if_reverse();
349 if (size != 1) {
350 fail_invalid("Invalid size {} for bool source", size);
353 range_check(1);
354 copy_int(dest, val(src).num, 1);
355 } else if (tvIsInt(src)) {
356 fail_if_reverse();
358 range_check(size);
359 copy_int(dest, val(src).num, size);
360 } else if (tvIsDouble(src)) {
361 fail_if_reverse();
363 if (size == 4) {
364 float f = val(src).dbl;
365 range_check(4);
366 memcpy(dest, &f, 4);
367 } else if (size == 8) {
368 range_check(8);
369 memcpy(dest, &val(src).dbl, 8);
370 } else {
371 fail_invalid("Invalid size {} for double source", size);
373 } else if (tvIsString(src)) {
374 setRangeString<reverse>(dest, src, count, size, range_check);
375 } else if (tvIsVec(src)) {
376 setRangeVec<reverse>(dest, val(src).parr, count, size, range_check);
377 } else {
378 fail_invalid("Invalid source type %s for range set operation",
379 tname(type(src)));
383 template void SetRange<true>(tv_lval, int64_t, TypedValue, int64_t, int64_t);
384 template void SetRange<false>(tv_lval, int64_t, TypedValue, int64_t, int64_t);
386 ///////////////////////////////////////////////////////////////////////////////